feat: add metric history bulk fetch endpoint (#1535)
This adds a new endpoint to fetch metric history items for multiple experiment runs at once: GET /experiment_runs/metric_history Without arguments this fetches metric history for every experiment run. To fetch metric history for a specific set of experiment runs, the `filterQuery` param can be used with `experimentRunId IN (1,2,3)`: GET /experiment_runs/metric_history?filterQuery=experimentRunId+IN+(1,2,3) AI-assisted: Claude Code 1.0.63 (claude-sonnet-4@20250514) Signed-off-by: Jon Burdo <jon@jonburdo.com>
This commit is contained in:
parent
af2e09940a
commit
ec49073cff
|
|
@ -253,6 +253,35 @@ paths:
|
|||
operationId: createExperimentRun
|
||||
summary: Create an ExperimentRun
|
||||
description: Creates a new instance of an `ExperimentRun`.
|
||||
"/api/model_registry/v1alpha3/experiment_runs/metric_history":
|
||||
summary: Path used to get metric history for multiple experiment runs.
|
||||
description: >-
|
||||
The REST endpoint/path used to get metric history for multiple `ExperimentRun` entities. This path contains a `GET` operation to retrieve metrics with optional filtering.
|
||||
get:
|
||||
tags:
|
||||
- ModelRegistryService
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/filterQuery"
|
||||
- $ref: "#/components/parameters/name"
|
||||
- $ref: "#/components/parameters/stepIds"
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
- $ref: "#/components/parameters/orderBy"
|
||||
- $ref: "#/components/parameters/sortOrder"
|
||||
- $ref: "#/components/parameters/nextPageToken"
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/MetricListResponse"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"503":
|
||||
$ref: "#/components/responses/ServiceUnavailable"
|
||||
operationId: getExperimentRunsMetricHistory
|
||||
summary: Get metric history for multiple ExperimentRuns
|
||||
description: Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
"/api/model_registry/v1alpha3/experiment_runs/{experimentrunId}":
|
||||
summary: Path used to manage a single ExperimentRun.
|
||||
description: >-
|
||||
|
|
|
|||
|
|
@ -1520,6 +1520,35 @@ paths:
|
|||
type: string
|
||||
in: path
|
||||
required: true
|
||||
"/api/model_registry/v1alpha3/experiment_runs/metric_history":
|
||||
summary: Path used to get metric history for multiple experiment runs.
|
||||
description: >-
|
||||
The REST endpoint/path used to get metric history for multiple `ExperimentRun` entities. This path contains a `GET` operation to retrieve metrics with optional filtering.
|
||||
get:
|
||||
tags:
|
||||
- ModelRegistryService
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/filterQuery"
|
||||
- $ref: "#/components/parameters/name"
|
||||
- $ref: "#/components/parameters/stepIds"
|
||||
- $ref: "#/components/parameters/pageSize"
|
||||
- $ref: "#/components/parameters/orderBy"
|
||||
- $ref: "#/components/parameters/sortOrder"
|
||||
- $ref: "#/components/parameters/nextPageToken"
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/MetricListResponse"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalServerError"
|
||||
"503":
|
||||
$ref: "#/components/responses/ServiceUnavailable"
|
||||
operationId: getExperimentRunsMetricHistory
|
||||
summary: Get metric history for multiple ExperimentRuns
|
||||
description: Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
"/api/model_registry/v1alpha3/experiment_runs/{experimentrunId}/metric_history":
|
||||
summary: Path used to get metric history for an experiment run.
|
||||
description: >-
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ Class | Method | HTTP request | Description
|
|||
*ModelRegistryServiceApi* | [**get_experiment_run_artifacts**](mr_openapi/docs/ModelRegistryServiceApi.md#get_experiment_run_artifacts) | **GET** /api/model_registry/v1alpha3/experiment_runs/{experimentrunId}/artifacts | List all artifacts associated with the `ExperimentRun`
|
||||
*ModelRegistryServiceApi* | [**get_experiment_run_metric_history**](mr_openapi/docs/ModelRegistryServiceApi.md#get_experiment_run_metric_history) | **GET** /api/model_registry/v1alpha3/experiment_runs/{experimentrunId}/metric_history | Get metric history for an ExperimentRun
|
||||
*ModelRegistryServiceApi* | [**get_experiment_runs**](mr_openapi/docs/ModelRegistryServiceApi.md#get_experiment_runs) | **GET** /api/model_registry/v1alpha3/experiment_runs | List All ExperimentRuns
|
||||
*ModelRegistryServiceApi* | [**get_experiment_runs_metric_history**](mr_openapi/docs/ModelRegistryServiceApi.md#get_experiment_runs_metric_history) | **GET** /api/model_registry/v1alpha3/experiment_runs/metric_history | Get metric history for multiple ExperimentRuns
|
||||
*ModelRegistryServiceApi* | [**get_experiments**](mr_openapi/docs/ModelRegistryServiceApi.md#get_experiments) | **GET** /api/model_registry/v1alpha3/experiments | List All Experiments
|
||||
*ModelRegistryServiceApi* | [**get_inference_service**](mr_openapi/docs/ModelRegistryServiceApi.md#get_inference_service) | **GET** /api/model_registry/v1alpha3/inference_services/{inferenceserviceId} | Get a InferenceService
|
||||
*ModelRegistryServiceApi* | [**get_inference_service_model**](mr_openapi/docs/ModelRegistryServiceApi.md#get_inference_service_model) | **GET** /api/model_registry/v1alpha3/inference_services/{inferenceserviceId}/model | Get InferenceService's RegisteredModel
|
||||
|
|
|
|||
|
|
@ -8251,6 +8251,383 @@ class ModelRegistryServiceApi:
|
|||
_request_auth=_request_auth,
|
||||
)
|
||||
|
||||
@validate_call
|
||||
async def get_experiment_runs_metric_history(
|
||||
self,
|
||||
filter_query: Annotated[
|
||||
Optional[StrictStr],
|
||||
Field(
|
||||
description='A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `"value"` or `\'value\'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = "my-model"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE "%tensorflow%"` - Complex: `(name = "model-a" OR name = "model-b") AND state = "LIVE"` - Custom property: `framework.string_value = "pytorch"` - Escaped property: `` `mlflow.source.type` = "notebook" `` '
|
||||
),
|
||||
] = None,
|
||||
name: Annotated[Optional[StrictStr], Field(description="Name of entity to search.")] = None,
|
||||
step_ids: Annotated[
|
||||
Optional[StrictStr], Field(description="Comma-separated list of step IDs to filter metrics by.")
|
||||
] = None,
|
||||
page_size: Annotated[Optional[StrictStr], Field(description="Number of entities in each page.")] = None,
|
||||
order_by: Annotated[
|
||||
Optional[OrderByField], Field(description="Specifies the order by criteria for listing entities.")
|
||||
] = None,
|
||||
sort_order: Annotated[
|
||||
Optional[SortOrder], Field(description="Specifies the sort order for listing entities, defaults to ASC.")
|
||||
] = None,
|
||||
next_page_token: Annotated[
|
||||
Optional[StrictStr], Field(description="Token to use to retrieve next page of results.")
|
||||
] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]],
|
||||
] = None,
|
||||
_request_auth: Optional[dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> MetricList:
|
||||
r"""Get metric history for multiple ExperimentRuns.
|
||||
|
||||
Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
|
||||
:param filter_query: A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `\"value\"` or `'value'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = \"my-model\"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE \"%tensorflow%\"` - Complex: `(name = \"model-a\" OR name = \"model-b\") AND state = \"LIVE\"` - Custom property: `framework.string_value = \"pytorch\"` - Escaped property: `` `mlflow.source.type` = \"notebook\" ``
|
||||
:type filter_query: str
|
||||
:param name: Name of entity to search.
|
||||
:type name: str
|
||||
:param step_ids: Comma-separated list of step IDs to filter metrics by.
|
||||
:type step_ids: str
|
||||
:param page_size: Number of entities in each page.
|
||||
:type page_size: str
|
||||
:param order_by: Specifies the order by criteria for listing entities.
|
||||
:type order_by: OrderByField
|
||||
:param sort_order: Specifies the sort order for listing entities, defaults to ASC.
|
||||
:type sort_order: SortOrder
|
||||
:param next_page_token: Token to use to retrieve next page of results.
|
||||
:type next_page_token: str
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
_param = self._get_experiment_runs_metric_history_serialize(
|
||||
filter_query=filter_query,
|
||||
name=name,
|
||||
step_ids=step_ids,
|
||||
page_size=page_size,
|
||||
order_by=order_by,
|
||||
sort_order=sort_order,
|
||||
next_page_token=next_page_token,
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index,
|
||||
)
|
||||
|
||||
_response_types_map: dict[str, Optional[str]] = {
|
||||
"200": "MetricList",
|
||||
"401": "Error",
|
||||
"404": "Error",
|
||||
"500": "Error",
|
||||
"503": "Error",
|
||||
}
|
||||
response_data = await self.api_client.call_api(*_param, _request_timeout=_request_timeout)
|
||||
await response_data.read()
|
||||
return self.api_client.response_deserialize(
|
||||
response_data=response_data,
|
||||
response_types_map=_response_types_map,
|
||||
).data
|
||||
|
||||
@validate_call
|
||||
async def get_experiment_runs_metric_history_with_http_info(
|
||||
self,
|
||||
filter_query: Annotated[
|
||||
Optional[StrictStr],
|
||||
Field(
|
||||
description='A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `"value"` or `\'value\'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = "my-model"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE "%tensorflow%"` - Complex: `(name = "model-a" OR name = "model-b") AND state = "LIVE"` - Custom property: `framework.string_value = "pytorch"` - Escaped property: `` `mlflow.source.type` = "notebook" `` '
|
||||
),
|
||||
] = None,
|
||||
name: Annotated[Optional[StrictStr], Field(description="Name of entity to search.")] = None,
|
||||
step_ids: Annotated[
|
||||
Optional[StrictStr], Field(description="Comma-separated list of step IDs to filter metrics by.")
|
||||
] = None,
|
||||
page_size: Annotated[Optional[StrictStr], Field(description="Number of entities in each page.")] = None,
|
||||
order_by: Annotated[
|
||||
Optional[OrderByField], Field(description="Specifies the order by criteria for listing entities.")
|
||||
] = None,
|
||||
sort_order: Annotated[
|
||||
Optional[SortOrder], Field(description="Specifies the sort order for listing entities, defaults to ASC.")
|
||||
] = None,
|
||||
next_page_token: Annotated[
|
||||
Optional[StrictStr], Field(description="Token to use to retrieve next page of results.")
|
||||
] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]],
|
||||
] = None,
|
||||
_request_auth: Optional[dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> ApiResponse[MetricList]:
|
||||
r"""Get metric history for multiple ExperimentRuns.
|
||||
|
||||
Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
|
||||
:param filter_query: A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `\"value\"` or `'value'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = \"my-model\"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE \"%tensorflow%\"` - Complex: `(name = \"model-a\" OR name = \"model-b\") AND state = \"LIVE\"` - Custom property: `framework.string_value = \"pytorch\"` - Escaped property: `` `mlflow.source.type` = \"notebook\" ``
|
||||
:type filter_query: str
|
||||
:param name: Name of entity to search.
|
||||
:type name: str
|
||||
:param step_ids: Comma-separated list of step IDs to filter metrics by.
|
||||
:type step_ids: str
|
||||
:param page_size: Number of entities in each page.
|
||||
:type page_size: str
|
||||
:param order_by: Specifies the order by criteria for listing entities.
|
||||
:type order_by: OrderByField
|
||||
:param sort_order: Specifies the sort order for listing entities, defaults to ASC.
|
||||
:type sort_order: SortOrder
|
||||
:param next_page_token: Token to use to retrieve next page of results.
|
||||
:type next_page_token: str
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
_param = self._get_experiment_runs_metric_history_serialize(
|
||||
filter_query=filter_query,
|
||||
name=name,
|
||||
step_ids=step_ids,
|
||||
page_size=page_size,
|
||||
order_by=order_by,
|
||||
sort_order=sort_order,
|
||||
next_page_token=next_page_token,
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index,
|
||||
)
|
||||
|
||||
_response_types_map: dict[str, Optional[str]] = {
|
||||
"200": "MetricList",
|
||||
"401": "Error",
|
||||
"404": "Error",
|
||||
"500": "Error",
|
||||
"503": "Error",
|
||||
}
|
||||
response_data = await self.api_client.call_api(*_param, _request_timeout=_request_timeout)
|
||||
await response_data.read()
|
||||
return self.api_client.response_deserialize(
|
||||
response_data=response_data,
|
||||
response_types_map=_response_types_map,
|
||||
)
|
||||
|
||||
@validate_call
|
||||
async def get_experiment_runs_metric_history_without_preload_content(
|
||||
self,
|
||||
filter_query: Annotated[
|
||||
Optional[StrictStr],
|
||||
Field(
|
||||
description='A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `"value"` or `\'value\'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = "my-model"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE "%tensorflow%"` - Complex: `(name = "model-a" OR name = "model-b") AND state = "LIVE"` - Custom property: `framework.string_value = "pytorch"` - Escaped property: `` `mlflow.source.type` = "notebook" `` '
|
||||
),
|
||||
] = None,
|
||||
name: Annotated[Optional[StrictStr], Field(description="Name of entity to search.")] = None,
|
||||
step_ids: Annotated[
|
||||
Optional[StrictStr], Field(description="Comma-separated list of step IDs to filter metrics by.")
|
||||
] = None,
|
||||
page_size: Annotated[Optional[StrictStr], Field(description="Number of entities in each page.")] = None,
|
||||
order_by: Annotated[
|
||||
Optional[OrderByField], Field(description="Specifies the order by criteria for listing entities.")
|
||||
] = None,
|
||||
sort_order: Annotated[
|
||||
Optional[SortOrder], Field(description="Specifies the sort order for listing entities, defaults to ASC.")
|
||||
] = None,
|
||||
next_page_token: Annotated[
|
||||
Optional[StrictStr], Field(description="Token to use to retrieve next page of results.")
|
||||
] = None,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
tuple[Annotated[StrictFloat, Field(gt=0)], Annotated[StrictFloat, Field(gt=0)]],
|
||||
] = None,
|
||||
_request_auth: Optional[dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> RESTResponseType:
|
||||
r"""Get metric history for multiple ExperimentRuns.
|
||||
|
||||
Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
|
||||
:param filter_query: A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `\"value\"` or `'value'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = \"my-model\"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE \"%tensorflow%\"` - Complex: `(name = \"model-a\" OR name = \"model-b\") AND state = \"LIVE\"` - Custom property: `framework.string_value = \"pytorch\"` - Escaped property: `` `mlflow.source.type` = \"notebook\" ``
|
||||
:type filter_query: str
|
||||
:param name: Name of entity to search.
|
||||
:type name: str
|
||||
:param step_ids: Comma-separated list of step IDs to filter metrics by.
|
||||
:type step_ids: str
|
||||
:param page_size: Number of entities in each page.
|
||||
:type page_size: str
|
||||
:param order_by: Specifies the order by criteria for listing entities.
|
||||
:type order_by: OrderByField
|
||||
:param sort_order: Specifies the sort order for listing entities, defaults to ASC.
|
||||
:type sort_order: SortOrder
|
||||
:param next_page_token: Token to use to retrieve next page of results.
|
||||
:type next_page_token: str
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
_param = self._get_experiment_runs_metric_history_serialize(
|
||||
filter_query=filter_query,
|
||||
name=name,
|
||||
step_ids=step_ids,
|
||||
page_size=page_size,
|
||||
order_by=order_by,
|
||||
sort_order=sort_order,
|
||||
next_page_token=next_page_token,
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index,
|
||||
)
|
||||
|
||||
_response_types_map: dict[str, Optional[str]] = {
|
||||
"200": "MetricList",
|
||||
"401": "Error",
|
||||
"404": "Error",
|
||||
"500": "Error",
|
||||
"503": "Error",
|
||||
}
|
||||
response_data = await self.api_client.call_api(*_param, _request_timeout=_request_timeout)
|
||||
return response_data.response
|
||||
|
||||
def _get_experiment_runs_metric_history_serialize(
|
||||
self,
|
||||
filter_query,
|
||||
name,
|
||||
step_ids,
|
||||
page_size,
|
||||
order_by,
|
||||
sort_order,
|
||||
next_page_token,
|
||||
_request_auth,
|
||||
_content_type,
|
||||
_headers,
|
||||
_host_index,
|
||||
) -> RequestSerialized:
|
||||
|
||||
_host = None
|
||||
|
||||
_collection_formats: dict[str, str] = {}
|
||||
|
||||
_path_params: dict[str, str] = {}
|
||||
_query_params: list[tuple[str, str]] = []
|
||||
_header_params: dict[str, Optional[str]] = _headers or {}
|
||||
_form_params: list[tuple[str, str]] = []
|
||||
_files: dict[str, Union[str, bytes]] = {}
|
||||
_body_params: Optional[bytes] = None
|
||||
|
||||
# process the path parameters
|
||||
# process the query parameters
|
||||
if filter_query is not None:
|
||||
|
||||
_query_params.append(("filterQuery", filter_query))
|
||||
|
||||
if name is not None:
|
||||
|
||||
_query_params.append(("name", name))
|
||||
|
||||
if step_ids is not None:
|
||||
|
||||
_query_params.append(("stepIds", step_ids))
|
||||
|
||||
if page_size is not None:
|
||||
|
||||
_query_params.append(("pageSize", page_size))
|
||||
|
||||
if order_by is not None:
|
||||
|
||||
_query_params.append(("orderBy", order_by.value))
|
||||
|
||||
if sort_order is not None:
|
||||
|
||||
_query_params.append(("sortOrder", sort_order.value))
|
||||
|
||||
if next_page_token is not None:
|
||||
|
||||
_query_params.append(("nextPageToken", next_page_token))
|
||||
|
||||
# process the header parameters
|
||||
# process the form parameters
|
||||
# process the body parameter
|
||||
|
||||
# set the HTTP header `Accept`
|
||||
_header_params["Accept"] = self.api_client.select_header_accept(["application/json"])
|
||||
|
||||
# authentication setting
|
||||
_auth_settings: list[str] = ["Bearer"]
|
||||
|
||||
return self.api_client.param_serialize(
|
||||
method="GET",
|
||||
resource_path="/api/model_registry/v1alpha3/experiment_runs/metric_history",
|
||||
path_params=_path_params,
|
||||
query_params=_query_params,
|
||||
header_params=_header_params,
|
||||
body=_body_params,
|
||||
post_params=_form_params,
|
||||
files=_files,
|
||||
auth_settings=_auth_settings,
|
||||
collection_formats=_collection_formats,
|
||||
_host=_host,
|
||||
_request_auth=_request_auth,
|
||||
)
|
||||
|
||||
@validate_call
|
||||
async def get_experiments(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -239,22 +239,24 @@ func (b *ModelRegistryService) GetExperimentRunArtifacts(artifactType openapi.Ar
|
|||
|
||||
func (b *ModelRegistryService) GetExperimentRunMetricHistory(name *string, stepIds *string, listOptions api.ListOptions, experimentRunId *string) (*openapi.MetricList, error) {
|
||||
|
||||
// Validate experiment run exists
|
||||
if experimentRunId == nil {
|
||||
return nil, fmt.Errorf("experiment run ID is required: %w", api.ErrBadRequest)
|
||||
}
|
||||
var experimentRunIdInt32Ptr *int32
|
||||
|
||||
// Validate experiment run exists
|
||||
_, err := b.GetExperimentRunById(*experimentRunId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("experiment run not found: %w", err)
|
||||
}
|
||||
// If experimentRunId is provided, validate it exists and convert to int32
|
||||
if experimentRunId != nil {
|
||||
// Validate experiment run exists
|
||||
_, err := b.GetExperimentRunById(*experimentRunId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("experiment run not found: %w", err)
|
||||
}
|
||||
|
||||
// Convert experiment run ID to int32 for repository queries
|
||||
experimentRunIdInt32, err := apiutils.ValidateIDAsInt32(*experimentRunId, "experiment run")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Convert experiment run ID to int32 for repository queries
|
||||
experimentRunIdInt32, err := apiutils.ValidateIDAsInt32(*experimentRunId, "experiment run")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
experimentRunIdInt32Ptr = &experimentRunIdInt32
|
||||
}
|
||||
// If experimentRunId is nil, experimentRunIdInt32Ptr remains nil for bulk query
|
||||
|
||||
listOptsCopy := models.MetricHistoryListOptions{
|
||||
Pagination: models.Pagination{
|
||||
|
|
@ -264,7 +266,7 @@ func (b *ModelRegistryService) GetExperimentRunMetricHistory(name *string, stepI
|
|||
NextPageToken: listOptions.NextPageToken,
|
||||
FilterQuery: listOptions.FilterQuery,
|
||||
},
|
||||
ExperimentRunID: &experimentRunIdInt32,
|
||||
ExperimentRunID: experimentRunIdInt32Ptr,
|
||||
}
|
||||
|
||||
// Add name filter if provided
|
||||
|
|
@ -354,6 +356,17 @@ func (b *ModelRegistryService) InsertMetricHistory(metric *openapi.Metric, exper
|
|||
// Clear the external ID to avoid duplicate key error
|
||||
metricHistory.ExternalId = nil
|
||||
|
||||
// Set experiment properties on the metric before converting
|
||||
// This ensures the experiment context is available as custom properties for the converter
|
||||
experimentRun, err := b.GetExperimentRunById(experimentRunId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get experiment run: %w", err)
|
||||
}
|
||||
|
||||
// Create a temporary artifact to use the existing helper function
|
||||
tempArtifact := &openapi.Artifact{Metric: &metricHistory}
|
||||
b.setExperimentPropertiesOnArtifact(tempArtifact, experimentRun.ExperimentId, experimentRunId)
|
||||
|
||||
// Create the MetricHistory entity with the correct TypeID
|
||||
// Get the metric history type ID from the types map
|
||||
metricHistoryTypeID, exists := b.typesMap[defaults.MetricHistoryTypeName]
|
||||
|
|
@ -373,15 +386,15 @@ func (b *ModelRegistryService) InsertMetricHistory(metric *openapi.Metric, exper
|
|||
}
|
||||
|
||||
// Map properties from metric to metric history using the converter
|
||||
metricProperties, err := converter.MapMetricPropertiesEmbedMD(metric)
|
||||
metricProperties, err := converter.MapMetricPropertiesEmbedMD(&metricHistory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to map metric properties: %w", err)
|
||||
}
|
||||
metricHistoryEntity.Properties = metricProperties
|
||||
|
||||
// Handle custom properties using the converter
|
||||
if metric.CustomProperties != nil {
|
||||
customProps, err := converter.MapOpenAPICustomPropertiesEmbedMD(metric.CustomProperties)
|
||||
// Handle custom properties using the converter - use the updated custom properties from tempArtifact
|
||||
if tempArtifact.Metric.CustomProperties != nil {
|
||||
customProps, err := converter.MapOpenAPICustomPropertiesEmbedMD(tempArtifact.Metric.CustomProperties)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to map custom properties: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -381,18 +381,23 @@ func TestGetExperimentRunMetricHistoryWithPagination(t *testing.T) {
|
|||
assert.Equal(t, pageSize, result.PageSize, "should have correct page size")
|
||||
}
|
||||
|
||||
func TestGetExperimentRunMetricHistoryBulkQuery(t *testing.T) {
|
||||
service, cleanup := SetupModelRegistryService(t)
|
||||
defer cleanup()
|
||||
|
||||
// Test with nil experiment run ID should work (bulk query)
|
||||
result, err := service.GetExperimentRunMetricHistory(nil, nil, api.ListOptions{}, nil)
|
||||
assert.NoError(t, err, "should not return error for nil experiment run ID (bulk query)")
|
||||
assert.NotNil(t, result, "should return result for bulk query")
|
||||
}
|
||||
|
||||
func TestGetExperimentRunMetricHistoryWithInvalidExperimentRunId(t *testing.T) {
|
||||
service, cleanup := SetupModelRegistryService(t)
|
||||
defer cleanup()
|
||||
|
||||
// Test with nil experiment run ID
|
||||
_, err := service.GetExperimentRunMetricHistory(nil, nil, api.ListOptions{}, nil)
|
||||
assert.Error(t, err, "should return error for nil experiment run ID")
|
||||
assert.Contains(t, err.Error(), "experiment run ID is required")
|
||||
|
||||
// Test with non-existent experiment run ID
|
||||
nonExistentId := "999999"
|
||||
_, err = service.GetExperimentRunMetricHistory(nil, nil, api.ListOptions{}, &nonExistentId)
|
||||
_, err := service.GetExperimentRunMetricHistory(nil, nil, api.ListOptions{}, &nonExistentId)
|
||||
assert.Error(t, err, "should return error for non-existent experiment run ID")
|
||||
assert.Contains(t, err.Error(), "experiment run not found")
|
||||
}
|
||||
|
|
@ -603,6 +608,137 @@ func TestGetExperimentRunMetricHistoryWithStepIdsFilter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGetExperimentRunsMetricHistoryBulk(t *testing.T) {
|
||||
service, cleanup := SetupModelRegistryService(t)
|
||||
defer cleanup()
|
||||
|
||||
// Create experiment
|
||||
experiment := &openapi.Experiment{
|
||||
Name: "test-experiment-bulk",
|
||||
Description: apiutils.Of("Test experiment for bulk metric history"),
|
||||
}
|
||||
savedExperiment, err := service.UpsertExperiment(experiment)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create two experiment runs
|
||||
experimentRun1 := &openapi.ExperimentRun{
|
||||
Name: apiutils.Of("test-experiment-run-1"),
|
||||
Description: apiutils.Of("Test experiment run 1 for bulk metric history"),
|
||||
}
|
||||
savedExperimentRun1, err := service.UpsertExperimentRun(experimentRun1, savedExperiment.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
experimentRun2 := &openapi.ExperimentRun{
|
||||
Name: apiutils.Of("test-experiment-run-2"),
|
||||
Description: apiutils.Of("Test experiment run 2 for bulk metric history"),
|
||||
}
|
||||
savedExperimentRun2, err := service.UpsertExperimentRun(experimentRun2, savedExperiment.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create metrics for first experiment run
|
||||
metric1Run1 := &openapi.Metric{
|
||||
Name: apiutils.Of("accuracy"),
|
||||
Value: apiutils.Of(0.85),
|
||||
Timestamp: apiutils.Of("1234567890"),
|
||||
Step: apiutils.Of(int64(1)),
|
||||
}
|
||||
metric2Run1 := &openapi.Metric{
|
||||
Name: apiutils.Of("loss"),
|
||||
Value: apiutils.Of(0.15),
|
||||
Timestamp: apiutils.Of("1234567891"),
|
||||
Step: apiutils.Of(int64(1)),
|
||||
}
|
||||
|
||||
// Create metrics for second experiment run
|
||||
metric1Run2 := &openapi.Metric{
|
||||
Name: apiutils.Of("accuracy"),
|
||||
Value: apiutils.Of(0.90),
|
||||
Timestamp: apiutils.Of("1234567892"),
|
||||
Step: apiutils.Of(int64(2)),
|
||||
}
|
||||
metric2Run2 := &openapi.Metric{
|
||||
Name: apiutils.Of("loss"),
|
||||
Value: apiutils.Of(0.10),
|
||||
Timestamp: apiutils.Of("1234567893"),
|
||||
Step: apiutils.Of(int64(2)),
|
||||
}
|
||||
|
||||
// Insert metric history for both runs
|
||||
err = service.InsertMetricHistory(metric1Run1, *savedExperimentRun1.Id)
|
||||
require.NoError(t, err, "error inserting metric 1 for run 1")
|
||||
err = service.InsertMetricHistory(metric2Run1, *savedExperimentRun1.Id)
|
||||
require.NoError(t, err, "error inserting metric 2 for run 1")
|
||||
err = service.InsertMetricHistory(metric1Run2, *savedExperimentRun2.Id)
|
||||
require.NoError(t, err, "error inserting metric 1 for run 2")
|
||||
err = service.InsertMetricHistory(metric2Run2, *savedExperimentRun2.Id)
|
||||
require.NoError(t, err, "error inserting metric 2 for run 2")
|
||||
|
||||
// Test getting all metric history for all experiment runs (bulk endpoint)
|
||||
result, err := service.GetExperimentRunMetricHistory(nil, nil, api.ListOptions{}, nil)
|
||||
require.NoError(t, err, "error getting bulk metric history")
|
||||
assert.GreaterOrEqual(t, int(result.Size), 4, "should return at least 4 metric history records")
|
||||
assert.GreaterOrEqual(t, len(result.Items), 4, "should have at least 4 items in the result")
|
||||
|
||||
// Debug: print information about returned metrics
|
||||
t.Logf("Found %d total metrics in bulk query", len(result.Items))
|
||||
for i, item := range result.Items {
|
||||
nameStr := "<nil>"
|
||||
if item.Name != nil {
|
||||
nameStr = *item.Name
|
||||
}
|
||||
expRunIdStr := "<nil>"
|
||||
if item.ExperimentRunId != nil {
|
||||
expRunIdStr = *item.ExperimentRunId
|
||||
}
|
||||
expIdStr := "<nil>"
|
||||
if item.ExperimentId != nil {
|
||||
expIdStr = *item.ExperimentId
|
||||
}
|
||||
t.Logf("Metric %d: name=%s, experimentRunId=%s, experimentId=%s",
|
||||
i, nameStr, expRunIdStr, expIdStr)
|
||||
}
|
||||
|
||||
// Verify we have metrics from both experiment runs
|
||||
run1MetricsFound := 0
|
||||
run2MetricsFound := 0
|
||||
for _, item := range result.Items {
|
||||
// For metrics we inserted, experiment fields should be populated
|
||||
// But bulk query might return other metrics too, so we only check our specific ones
|
||||
if item.ExperimentRunId != nil {
|
||||
switch *item.ExperimentRunId {
|
||||
case *savedExperimentRun1.Id:
|
||||
run1MetricsFound++
|
||||
// Verify experiment fields are populated for our metrics
|
||||
assert.Equal(t, savedExperiment.Id, item.ExperimentId, "experimentId should be populated for run 1 metrics")
|
||||
case *savedExperimentRun2.Id:
|
||||
run2MetricsFound++
|
||||
// Verify experiment fields are populated for our metrics
|
||||
assert.Equal(t, savedExperiment.Id, item.ExperimentId, "experimentId should be populated for run 2 metrics")
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.GreaterOrEqual(t, run1MetricsFound, 2, "should find at least 2 metrics from experiment run 1")
|
||||
assert.GreaterOrEqual(t, run2MetricsFound, 2, "should find at least 2 metrics from experiment run 2")
|
||||
|
||||
// Test filtering by name in bulk mode
|
||||
accuracyName := "accuracy"
|
||||
result, err = service.GetExperimentRunMetricHistory(&accuracyName, nil, api.ListOptions{}, nil)
|
||||
require.NoError(t, err, "error getting bulk metric history with name filter")
|
||||
assert.GreaterOrEqual(t, int(result.Size), 2, "should return at least 2 accuracy metrics")
|
||||
for _, item := range result.Items {
|
||||
assert.Equal(t, "accuracy", *item.Name, "all returned metrics should be accuracy metrics")
|
||||
}
|
||||
|
||||
// Test filtering by step IDs in bulk mode
|
||||
stepIds := "1"
|
||||
result, err = service.GetExperimentRunMetricHistory(nil, &stepIds, api.ListOptions{}, nil)
|
||||
require.NoError(t, err, "error getting bulk metric history with step filter")
|
||||
assert.GreaterOrEqual(t, int(result.Size), 2, "should return at least 2 metrics from step 1")
|
||||
for _, item := range result.Items {
|
||||
assert.Equal(t, int64(1), *item.Step, "all returned metrics should be from step 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExperimentRunMetricHistoryExperimentFields(t *testing.T) {
|
||||
service, cleanup := SetupModelRegistryService(t)
|
||||
defer cleanup()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package models
|
||||
|
||||
import "github.com/kubeflow/model-registry/internal/db/filter"
|
||||
|
||||
const MetricHistoryType = "metric-history"
|
||||
|
||||
type MetricHistoryListOptions struct {
|
||||
|
|
@ -10,6 +12,11 @@ type MetricHistoryListOptions struct {
|
|||
StepIds *string
|
||||
}
|
||||
|
||||
// GetRestEntityType implements the FilterApplier interface
|
||||
func (m *MetricHistoryListOptions) GetRestEntityType() filter.RestEntityType {
|
||||
return filter.RestEntityMetric // Metric history uses the same filtering rules as metrics
|
||||
}
|
||||
|
||||
type MetricHistoryAttributes struct {
|
||||
Name *string
|
||||
URI *string
|
||||
|
|
|
|||
|
|
@ -48,13 +48,14 @@ func applyMetricHistoryListFilters(query *gorm.DB, listOptions *models.MetricHis
|
|||
query = query.Where("Artifact.external_id = ?", listOptions.ExternalID)
|
||||
}
|
||||
|
||||
// Add step IDs filter if provided
|
||||
// Add step IDs filter if provided - use unique alias to avoid conflicts with filterQuery joins
|
||||
if listOptions.StepIds != nil && *listOptions.StepIds != "" {
|
||||
query = query.Joins("JOIN ArtifactProperty ON ArtifactProperty.artifact_id = Artifact.id").
|
||||
Where("ArtifactProperty.name = ? AND ArtifactProperty.int_value IN (?)",
|
||||
query = query.Joins("JOIN ArtifactProperty AS step_props ON step_props.artifact_id = Artifact.id").
|
||||
Where("step_props.name = ? AND step_props.int_value IN (?)",
|
||||
"step", strings.Split(*listOptions.StepIds, ","))
|
||||
}
|
||||
|
||||
// Join with Attribution table only when filtering by experiment run ID
|
||||
if listOptions.ExperimentRunID != nil {
|
||||
query = query.Joins("JOIN Attribution ON Attribution.artifact_id = Artifact.id").
|
||||
Where("Attribution.context_id = ?", listOptions.ExperimentRunID)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ type ModelRegistryServiceAPIRouter interface {
|
|||
GetExperimentRunArtifacts(http.ResponseWriter, *http.Request)
|
||||
GetExperimentRunMetricHistory(http.ResponseWriter, *http.Request)
|
||||
GetExperimentRuns(http.ResponseWriter, *http.Request)
|
||||
GetExperimentRunsMetricHistory(http.ResponseWriter, *http.Request)
|
||||
GetExperiments(http.ResponseWriter, *http.Request)
|
||||
GetInferenceService(http.ResponseWriter, *http.Request)
|
||||
GetInferenceServiceModel(http.ResponseWriter, *http.Request)
|
||||
|
|
@ -112,6 +113,7 @@ type ModelRegistryServiceAPIServicer interface {
|
|||
GetExperimentRunArtifacts(context.Context, string, string, string, string, model.ArtifactTypeQueryParam, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetExperimentRunMetricHistory(context.Context, string, string, string, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetExperimentRuns(context.Context, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetExperimentRunsMetricHistory(context.Context, string, string, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetExperiments(context.Context, string, string, model.OrderByField, model.SortOrder, string) (ImplResponse, error)
|
||||
GetInferenceService(context.Context, string) (ImplResponse, error)
|
||||
GetInferenceServiceModel(context.Context, string) (ImplResponse, error)
|
||||
|
|
|
|||
|
|
@ -198,6 +198,11 @@ func (c *ModelRegistryServiceAPIController) Routes() Routes {
|
|||
"/api/model_registry/v1alpha3/experiment_runs",
|
||||
c.GetExperimentRuns,
|
||||
},
|
||||
"GetExperimentRunsMetricHistory": Route{
|
||||
strings.ToUpper("Get"),
|
||||
"/api/model_registry/v1alpha3/experiment_runs/metric_history",
|
||||
c.GetExperimentRunsMetricHistory,
|
||||
},
|
||||
"GetExperiments": Route{
|
||||
strings.ToUpper("Get"),
|
||||
"/api/model_registry/v1alpha3/experiments",
|
||||
|
|
@ -945,6 +950,26 @@ func (c *ModelRegistryServiceAPIController) GetExperimentRuns(w http.ResponseWri
|
|||
EncodeJSONResponse(result.Body, &result.Code, w)
|
||||
}
|
||||
|
||||
// GetExperimentRunsMetricHistory - Get metric history for multiple ExperimentRuns
|
||||
func (c *ModelRegistryServiceAPIController) GetExperimentRunsMetricHistory(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
filterQueryParam := query.Get("filterQuery")
|
||||
nameParam := query.Get("name")
|
||||
stepIdsParam := query.Get("stepIds")
|
||||
pageSizeParam := query.Get("pageSize")
|
||||
orderByParam := query.Get("orderBy")
|
||||
sortOrderParam := query.Get("sortOrder")
|
||||
nextPageTokenParam := query.Get("nextPageToken")
|
||||
result, err := c.service.GetExperimentRunsMetricHistory(r.Context(), filterQueryParam, nameParam, stepIdsParam, pageSizeParam, model.OrderByField(orderByParam), model.SortOrder(sortOrderParam), nextPageTokenParam)
|
||||
// If an error occurred, encode the error with the status code
|
||||
if err != nil {
|
||||
c.errorHandler(w, r, err, &result)
|
||||
return
|
||||
}
|
||||
// If no error, encode the body and the result code
|
||||
EncodeJSONResponse(result.Body, &result.Code, w)
|
||||
}
|
||||
|
||||
// GetExperiments - List All Experiments
|
||||
func (c *ModelRegistryServiceAPIController) GetExperiments(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
|
|
|
|||
|
|
@ -791,6 +791,19 @@ func (s *ModelRegistryServiceAPIService) UpsertExperimentRunArtifact(ctx context
|
|||
|
||||
// GetExperimentRunMetricHistory - Get metric history for an ExperimentRun
|
||||
func (s *ModelRegistryServiceAPIService) GetExperimentRunMetricHistory(ctx context.Context, experimentrunId string,
|
||||
filterQuery string, name string, stepIds string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
return s.getMetricHistoryHelper(ctx, apiutils.StrPtr(experimentrunId), filterQuery, name, stepIds, pageSize, orderBy, sortOrder, nextPageToken)
|
||||
}
|
||||
|
||||
// GetExperimentRunsMetricHistory - Get metric history for multiple ExperimentRuns
|
||||
func (s *ModelRegistryServiceAPIService) GetExperimentRunsMetricHistory(ctx context.Context,
|
||||
filterQuery string, name string, stepIds string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
// Pass nil for experimentRunId to get metrics for all experiment runs
|
||||
return s.getMetricHistoryHelper(ctx, nil, filterQuery, name, stepIds, pageSize, orderBy, sortOrder, nextPageToken)
|
||||
}
|
||||
|
||||
// getMetricHistoryHelper handles the common logic for getting metric history
|
||||
func (s *ModelRegistryServiceAPIService) getMetricHistoryHelper(ctx context.Context, experimentRunId *string,
|
||||
filterQuery string, name string, stepIds string, pageSize string, orderBy model.OrderByField, sortOrder model.SortOrder, nextPageToken string) (ImplResponse, error) {
|
||||
listOpts, err := s.buildListOption(filterQuery, pageSize, orderBy, sortOrder, nextPageToken)
|
||||
if err != nil {
|
||||
|
|
@ -807,7 +820,7 @@ func (s *ModelRegistryServiceAPIService) GetExperimentRunMetricHistory(ctx conte
|
|||
stepIdsPtr = &stepIds
|
||||
}
|
||||
|
||||
result, err := s.coreApi.GetExperimentRunMetricHistory(namePtr, stepIdsPtr, listOpts, apiutils.StrPtr(experimentrunId))
|
||||
result, err := s.coreApi.GetExperimentRunMetricHistory(namePtr, stepIdsPtr, listOpts, experimentRunId)
|
||||
if err != nil {
|
||||
return ErrorResponse(api.ErrToStatus(err), err), err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5229,6 +5229,219 @@ func (a *ModelRegistryServiceAPIService) GetExperimentRunsExecute(r ApiGetExperi
|
|||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiGetExperimentRunsMetricHistoryRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ModelRegistryServiceAPIService
|
||||
filterQuery *string
|
||||
name *string
|
||||
stepIds *string
|
||||
pageSize *string
|
||||
orderBy *OrderByField
|
||||
sortOrder *SortOrder
|
||||
nextPageToken *string
|
||||
}
|
||||
|
||||
// A SQL-like query string to filter the list of entities. The query supports rich filtering capabilities with automatic type inference. **Supported Operators:** - Comparison: `=`, `!=`, `<>`, `>`, `<`, `>=`, `<=` - Pattern matching: `LIKE`, `ILIKE` (case-insensitive) - Set membership: `IN` - Logical: `AND`, `OR` - Grouping: `()` for complex expressions **Data Types:** - Strings: `\"value\"` or `'value'` - Numbers: `42`, `3.14`, `1e-5` - Booleans: `true`, `false` (case-insensitive) **Property Access:** - Standard properties: `name`, `id`, `state`, `createTimeSinceEpoch` - Custom properties: Any user-defined property name - Escaped properties: Use backticks for special characters: `` `custom-property` `` - Type-specific access: `property.string_value`, `property.double_value`, `property.int_value`, `property.bool_value` **Examples:** - Basic: `name = \"my-model\"` - Comparison: `accuracy > 0.95` - Pattern: `name LIKE \"%tensorflow%\"` - Complex: `(name = \"model-a\" OR name = \"model-b\") AND state = \"LIVE\"` - Custom property: `framework.string_value = \"pytorch\"` - Escaped property: `` `mlflow.source.type` = \"notebook\" ``
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) FilterQuery(filterQuery string) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.filterQuery = &filterQuery
|
||||
return r
|
||||
}
|
||||
|
||||
// Name of entity to search.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) Name(name string) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.name = &name
|
||||
return r
|
||||
}
|
||||
|
||||
// Comma-separated list of step IDs to filter metrics by.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) StepIds(stepIds string) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.stepIds = &stepIds
|
||||
return r
|
||||
}
|
||||
|
||||
// Number of entities in each page.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) PageSize(pageSize string) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.pageSize = &pageSize
|
||||
return r
|
||||
}
|
||||
|
||||
// Specifies the order by criteria for listing entities.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) OrderBy(orderBy OrderByField) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.orderBy = &orderBy
|
||||
return r
|
||||
}
|
||||
|
||||
// Specifies the sort order for listing entities, defaults to ASC.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) SortOrder(sortOrder SortOrder) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.sortOrder = &sortOrder
|
||||
return r
|
||||
}
|
||||
|
||||
// Token to use to retrieve next page of results.
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) NextPageToken(nextPageToken string) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
r.nextPageToken = &nextPageToken
|
||||
return r
|
||||
}
|
||||
|
||||
func (r ApiGetExperimentRunsMetricHistoryRequest) Execute() (*MetricList, *http.Response, error) {
|
||||
return r.ApiService.GetExperimentRunsMetricHistoryExecute(r)
|
||||
}
|
||||
|
||||
/*
|
||||
GetExperimentRunsMetricHistory Get metric history for multiple ExperimentRuns
|
||||
|
||||
Gets the metric history for multiple `ExperimentRun` entities with optional filtering by metric name, step IDs, and experiment run IDs.
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiGetExperimentRunsMetricHistoryRequest
|
||||
*/
|
||||
func (a *ModelRegistryServiceAPIService) GetExperimentRunsMetricHistory(ctx context.Context) ApiGetExperimentRunsMetricHistoryRequest {
|
||||
return ApiGetExperimentRunsMetricHistoryRequest{
|
||||
ApiService: a,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the request
|
||||
//
|
||||
// @return MetricList
|
||||
func (a *ModelRegistryServiceAPIService) GetExperimentRunsMetricHistoryExecute(r ApiGetExperimentRunsMetricHistoryRequest) (*MetricList, *http.Response, error) {
|
||||
var (
|
||||
localVarHTTPMethod = http.MethodGet
|
||||
localVarPostBody interface{}
|
||||
formFiles []formFile
|
||||
localVarReturnValue *MetricList
|
||||
)
|
||||
|
||||
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ModelRegistryServiceAPIService.GetExperimentRunsMetricHistory")
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()}
|
||||
}
|
||||
|
||||
localVarPath := localBasePath + "/api/model_registry/v1alpha3/experiment_runs/metric_history"
|
||||
|
||||
localVarHeaderParams := make(map[string]string)
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.filterQuery != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "filterQuery", r.filterQuery, "")
|
||||
}
|
||||
if r.name != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "name", r.name, "")
|
||||
}
|
||||
if r.stepIds != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "stepIds", r.stepIds, "")
|
||||
}
|
||||
if r.pageSize != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "pageSize", r.pageSize, "")
|
||||
}
|
||||
if r.orderBy != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "orderBy", r.orderBy, "")
|
||||
}
|
||||
if r.sortOrder != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "sortOrder", r.sortOrder, "")
|
||||
}
|
||||
if r.nextPageToken != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "nextPageToken", r.nextPageToken, "")
|
||||
}
|
||||
// to determine the Content-Type header
|
||||
localVarHTTPContentTypes := []string{}
|
||||
|
||||
// set Content-Type header
|
||||
localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes)
|
||||
if localVarHTTPContentType != "" {
|
||||
localVarHeaderParams["Content-Type"] = localVarHTTPContentType
|
||||
}
|
||||
|
||||
// to determine the Accept header
|
||||
localVarHTTPHeaderAccepts := []string{"application/json"}
|
||||
|
||||
// set Accept header
|
||||
localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts)
|
||||
if localVarHTTPHeaderAccept != "" {
|
||||
localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept
|
||||
}
|
||||
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles)
|
||||
if err != nil {
|
||||
return localVarReturnValue, nil, err
|
||||
}
|
||||
|
||||
localVarHTTPResponse, err := a.client.callAPI(req)
|
||||
if err != nil || localVarHTTPResponse == nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
localVarBody, err := io.ReadAll(localVarHTTPResponse.Body)
|
||||
localVarHTTPResponse.Body.Close()
|
||||
localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody))
|
||||
if err != nil {
|
||||
return localVarReturnValue, localVarHTTPResponse, err
|
||||
}
|
||||
|
||||
if localVarHTTPResponse.StatusCode >= 300 {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: localVarHTTPResponse.Status,
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 401 {
|
||||
var v Error
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 404 {
|
||||
var v Error
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 500 {
|
||||
var v Error
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
if localVarHTTPResponse.StatusCode == 503 {
|
||||
var v Error
|
||||
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr.error = err.Error()
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v)
|
||||
newErr.model = v
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
|
||||
if err != nil {
|
||||
newErr := &GenericOpenAPIError{
|
||||
body: localVarBody,
|
||||
error: err.Error(),
|
||||
}
|
||||
return localVarReturnValue, localVarHTTPResponse, newErr
|
||||
}
|
||||
|
||||
return localVarReturnValue, localVarHTTPResponse, nil
|
||||
}
|
||||
|
||||
type ApiGetExperimentsRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *ModelRegistryServiceAPIService
|
||||
|
|
|
|||
Loading…
Reference in New Issue