From c88e0ba175677f1167cd322845561addb74edaa2 Mon Sep 17 00:00:00 2001 From: Eduardo Bonet <ebonet@gitlab.com> Date: Mon, 8 Apr 2024 16:58:31 +0200 Subject: [PATCH] Maps addition Ml:: graphql types - Adds CandidateParamType, CandidateMetricType, CandidateMetadataType - Adds params, metrics and fields to CandidateType - Adds packageId field to ModelVersionType --- .../types/ml/candidate_metadata_type.rb | 24 +++ app/graphql/types/ml/candidate_metric_type.rb | 28 +++ app/graphql/types/ml/candidate_param_type.rb | 24 +++ app/graphql/types/ml/candidate_type.rb | 12 ++ app/graphql/types/ml/model_version_type.rb | 4 + doc/api/graphql/reference/index.md | 170 ++++++++++++++++++ .../types/ml/candidate_metadata_type_spec.rb | 11 ++ .../types/ml/candidate_metric_type_spec.rb | 11 ++ .../types/ml/candidate_param_type_spec.rb | 11 ++ spec/graphql/types/ml/candidate_type_spec.rb | 39 ++++ .../types/ml/model_version_type_spec.rb | 4 +- 11 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 app/graphql/types/ml/candidate_metadata_type.rb create mode 100644 app/graphql/types/ml/candidate_metric_type.rb create mode 100644 app/graphql/types/ml/candidate_param_type.rb create mode 100644 spec/graphql/types/ml/candidate_metadata_type_spec.rb create mode 100644 spec/graphql/types/ml/candidate_metric_type_spec.rb create mode 100644 spec/graphql/types/ml/candidate_param_type_spec.rb diff --git a/app/graphql/types/ml/candidate_metadata_type.rb b/app/graphql/types/ml/candidate_metadata_type.rb new file mode 100644 index 000000000000..b2cccb47bc56 --- /dev/null +++ b/app/graphql/types/ml/candidate_metadata_type.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Types + module Ml + # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver + class CandidateMetadataType < ::Types::BaseObject + graphql_name 'MlCandidateMetadata' + description 'Metadata for a candidate in the model registry' + + connection_type_class Types::LimitedCountableConnectionType + + field :id, ::Types::GlobalIDType[::Ml::CandidateMetadata], null: false, description: 'ID of the metadata.' + + field :name, ::GraphQL::Types::String, + null: true, + description: 'Name of the metadata entry.' + + field :value, ::GraphQL::Types::String, + null: false, + description: 'Value set for the metadata entry.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/ml/candidate_metric_type.rb b/app/graphql/types/ml/candidate_metric_type.rb new file mode 100644 index 000000000000..b0cb9ca18e80 --- /dev/null +++ b/app/graphql/types/ml/candidate_metric_type.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Types + module Ml + # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver + class CandidateMetricType < ::Types::BaseObject + graphql_name 'MlCandidateMetric' + description 'Metric for a candidate in the model registry' + + connection_type_class Types::LimitedCountableConnectionType + + field :id, ::Types::GlobalIDType[::Ml::CandidateMetric], null: false, description: 'ID of the metric.' + + field :name, ::GraphQL::Types::String, + null: true, + description: 'Name of the metric.' + + field :step, ::GraphQL::Types::Int, + null: false, + description: 'Step at which the metric was measured.' + + field :value, ::GraphQL::Types::Float, + null: false, + description: 'Value set for the metric.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/ml/candidate_param_type.rb b/app/graphql/types/ml/candidate_param_type.rb new file mode 100644 index 000000000000..e73e176c4e5e --- /dev/null +++ b/app/graphql/types/ml/candidate_param_type.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Types + module Ml + # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver + class CandidateParamType < ::Types::BaseObject + graphql_name 'MlCandidateParam' + description 'Parameter for a candidate in the model registry' + + connection_type_class Types::LimitedCountableConnectionType + + field :id, ::Types::GlobalIDType[::Ml::CandidateParam], null: false, description: 'ID of the parameter.' + + field :name, ::GraphQL::Types::String, + null: true, + description: 'Name of the parameter.' + + field :value, ::GraphQL::Types::String, + null: false, + description: 'Value set for the parameter.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/ml/candidate_type.rb b/app/graphql/types/ml/candidate_type.rb index 1c6ce26b9cee..9367d0995d31 100644 --- a/app/graphql/types/ml/candidate_type.rb +++ b/app/graphql/types/ml/candidate_type.rb @@ -31,6 +31,18 @@ class CandidateType < ::Types::BaseObject field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + field :params, ::Types::Ml::CandidateParamType.connection_type, + null: false, + description: 'Parameters for the candidate.' + + field :metrics, ::Types::Ml::CandidateMetricType.connection_type, + null: false, + description: 'Metrics for the candidate.' + + field :metadata, ::Types::Ml::CandidateMetadataType.connection_type, + null: false, + description: 'Metadata entries for the candidate.' + field :ci_job, ::Types::Ci::JobType, null: true, description: 'CI information about the job that created the candidate.' diff --git a/app/graphql/types/ml/model_version_type.rb b/app/graphql/types/ml/model_version_type.rb index 771ff2491887..30443f537ac8 100644 --- a/app/graphql/types/ml/model_version_type.rb +++ b/app/graphql/types/ml/model_version_type.rb @@ -14,6 +14,10 @@ class ModelVersionType < ::Types::BaseObject field :created_at, Types::TimeType, null: false, description: 'Date of creation.' field :version, ::GraphQL::Types::String, null: false, description: 'Name of the version.' + field :package_id, ::Types::GlobalIDType[::Packages::Package], + null: false, + description: 'Package for model version artifacts.' + field :candidate, ::Types::Ml::CandidateType, null: false, description: 'Metrics, params and metadata for the model version.' diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 53d7c081ad8a..e6c66c6cdc4d 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12547,6 +12547,117 @@ The edge type for [`MlCandidate`](#mlcandidate). | <a id="mlcandidateedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | | <a id="mlcandidateedgenode"></a>`node` | [`MlCandidate`](#mlcandidate) | The item at the end of the edge. | +#### `MlCandidateMetadataConnection` + +The connection type for [`MlCandidateMetadata`](#mlcandidatemetadata). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetadataconnectionedges"></a>`edges` | [`[MlCandidateMetadataEdge]`](#mlcandidatemetadataedge) | A list of edges. | +| <a id="mlcandidatemetadataconnectionnodes"></a>`nodes` | [`[MlCandidateMetadata]`](#mlcandidatemetadata) | A list of nodes. | +| <a id="mlcandidatemetadataconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +##### Fields with arguments + +###### `MlCandidateMetadataConnection.count` + +Limited count of collection. Returns limit + 1 for counts greater than the limit. + +Returns [`Int!`](#int). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetadataconnectioncountlimit"></a>`limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. | + +#### `MlCandidateMetadataEdge` + +The edge type for [`MlCandidateMetadata`](#mlcandidatemetadata). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetadataedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="mlcandidatemetadataedgenode"></a>`node` | [`MlCandidateMetadata`](#mlcandidatemetadata) | The item at the end of the edge. | + +#### `MlCandidateMetricConnection` + +The connection type for [`MlCandidateMetric`](#mlcandidatemetric). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetricconnectionedges"></a>`edges` | [`[MlCandidateMetricEdge]`](#mlcandidatemetricedge) | A list of edges. | +| <a id="mlcandidatemetricconnectionnodes"></a>`nodes` | [`[MlCandidateMetric]`](#mlcandidatemetric) | A list of nodes. | +| <a id="mlcandidatemetricconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +##### Fields with arguments + +###### `MlCandidateMetricConnection.count` + +Limited count of collection. Returns limit + 1 for counts greater than the limit. + +Returns [`Int!`](#int). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetricconnectioncountlimit"></a>`limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. | + +#### `MlCandidateMetricEdge` + +The edge type for [`MlCandidateMetric`](#mlcandidatemetric). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetricedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="mlcandidatemetricedgenode"></a>`node` | [`MlCandidateMetric`](#mlcandidatemetric) | The item at the end of the edge. | + +#### `MlCandidateParamConnection` + +The connection type for [`MlCandidateParam`](#mlcandidateparam). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidateparamconnectionedges"></a>`edges` | [`[MlCandidateParamEdge]`](#mlcandidateparamedge) | A list of edges. | +| <a id="mlcandidateparamconnectionnodes"></a>`nodes` | [`[MlCandidateParam]`](#mlcandidateparam) | A list of nodes. | +| <a id="mlcandidateparamconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. | + +##### Fields with arguments + +###### `MlCandidateParamConnection.count` + +Limited count of collection. Returns limit + 1 for counts greater than the limit. + +Returns [`Int!`](#int). + +####### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidateparamconnectioncountlimit"></a>`limit` | [`Int`](#int) | Limit value to be applied to the count query. Default is 1000. | + +#### `MlCandidateParamEdge` + +The edge type for [`MlCandidateParam`](#mlcandidateparam). + +##### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidateparamedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. | +| <a id="mlcandidateparamedgenode"></a>`node` | [`MlCandidateParam`](#mlcandidateparam) | The item at the end of the edge. | + #### `MlModelConnection` The connection type for [`MlModel`](#mlmodel). @@ -24257,9 +24368,49 @@ Candidate for a model version in the model registry. | <a id="mlcandidateeid"></a>`eid` | [`String!`](#string) | MLflow uuid for the candidate. | | <a id="mlcandidateid"></a>`id` | [`MlCandidateID!`](#mlcandidateid) | ID of the candidate. | | <a id="mlcandidateiid"></a>`iid` | [`Int!`](#int) | IID of the candidate scoped to project. | +| <a id="mlcandidatemetadata"></a>`metadata` | [`MlCandidateMetadataConnection!`](#mlcandidatemetadataconnection) | Metadata entries for the candidate. (see [Connections](#connections)) | +| <a id="mlcandidatemetrics"></a>`metrics` | [`MlCandidateMetricConnection!`](#mlcandidatemetricconnection) | Metrics for the candidate. (see [Connections](#connections)) | | <a id="mlcandidatename"></a>`name` | [`String`](#string) | Name of the candidate. | +| <a id="mlcandidateparams"></a>`params` | [`MlCandidateParamConnection!`](#mlcandidateparamconnection) | Parameters for the candidate. (see [Connections](#connections)) | | <a id="mlcandidatestatus"></a>`status` | [`String`](#string) | Candidate status. | +### `MlCandidateMetadata` + +Metadata for a candidate in the model registry. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetadataid"></a>`id` | [`MlCandidateMetadataID!`](#mlcandidatemetadataid) | ID of the metadata. | +| <a id="mlcandidatemetadataname"></a>`name` | [`String`](#string) | Name of the metadata entry. | +| <a id="mlcandidatemetadatavalue"></a>`value` | [`String!`](#string) | Value set for the metadata entry. | + +### `MlCandidateMetric` + +Metric for a candidate in the model registry. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidatemetricid"></a>`id` | [`MlCandidateMetricID!`](#mlcandidatemetricid) | ID of the metric. | +| <a id="mlcandidatemetricname"></a>`name` | [`String`](#string) | Name of the metric. | +| <a id="mlcandidatemetricstep"></a>`step` | [`Int!`](#int) | Step at which the metric was measured. | +| <a id="mlcandidatemetricvalue"></a>`value` | [`Float!`](#float) | Value set for the metric. | + +### `MlCandidateParam` + +Parameter for a candidate in the model registry. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="mlcandidateparamid"></a>`id` | [`MlCandidateParamID!`](#mlcandidateparamid) | ID of the parameter. | +| <a id="mlcandidateparamname"></a>`name` | [`String`](#string) | Name of the parameter. | +| <a id="mlcandidateparamvalue"></a>`value` | [`String!`](#string) | Value set for the parameter. | + ### `MlModel` Machine learning model in the model registry. @@ -24310,6 +24461,7 @@ Version of a machine learning model. | <a id="mlmodelversioncandidate"></a>`candidate` | [`MlCandidate!`](#mlcandidate) | Metrics, params and metadata for the model version. | | <a id="mlmodelversioncreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. | | <a id="mlmodelversionid"></a>`id` | [`MlModelVersionID!`](#mlmodelversionid) | ID of the model version. | +| <a id="mlmodelversionpackageid"></a>`packageId` | [`PackagesPackageID!`](#packagespackageid) | Package for model version artifacts. | | <a id="mlmodelversionversion"></a>`version` | [`String!`](#string) | Name of the version. | ### `MonthlyUsage` @@ -34424,6 +34576,24 @@ A `MlCandidateID` is a global ID. It is encoded as a string. An example `MlCandidateID` is: `"gid://gitlab/Ml::Candidate/1"`. +### `MlCandidateMetadataID` + +A `MlCandidateMetadataID` is a global ID. It is encoded as a string. + +An example `MlCandidateMetadataID` is: `"gid://gitlab/Ml::CandidateMetadata/1"`. + +### `MlCandidateMetricID` + +A `MlCandidateMetricID` is a global ID. It is encoded as a string. + +An example `MlCandidateMetricID` is: `"gid://gitlab/Ml::CandidateMetric/1"`. + +### `MlCandidateParamID` + +A `MlCandidateParamID` is a global ID. It is encoded as a string. + +An example `MlCandidateParamID` is: `"gid://gitlab/Ml::CandidateParam/1"`. + ### `MlModelID` A `MlModelID` is a global ID. It is encoded as a string. diff --git a/spec/graphql/types/ml/candidate_metadata_type_spec.rb b/spec/graphql/types/ml/candidate_metadata_type_spec.rb new file mode 100644 index 000000000000..719739d76f69 --- /dev/null +++ b/spec/graphql/types/ml/candidate_metadata_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MlCandidateMetadata'], feature_category: :mlops do + it 'has the expected fields' do + expected_fields = %w[id name value] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ml/candidate_metric_type_spec.rb b/spec/graphql/types/ml/candidate_metric_type_spec.rb new file mode 100644 index 000000000000..f5c345b819b9 --- /dev/null +++ b/spec/graphql/types/ml/candidate_metric_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MlCandidateMetric'], feature_category: :mlops do + it 'has the expected fields' do + expected_fields = %w[id name value step] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ml/candidate_param_type_spec.rb b/spec/graphql/types/ml/candidate_param_type_spec.rb new file mode 100644 index 000000000000..a16b6a6428bf --- /dev/null +++ b/spec/graphql/types/ml/candidate_param_type_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['MlCandidateParam'], feature_category: :mlops do + it 'has the expected fields' do + expected_fields = %w[id name value] + + expect(described_class).to include_graphql_fields(*expected_fields) + end +end diff --git a/spec/graphql/types/ml/candidate_type_spec.rb b/spec/graphql/types/ml/candidate_type_spec.rb index 11a3e46ecc86..41219b0b09f6 100644 --- a/spec/graphql/types/ml/candidate_type_spec.rb +++ b/spec/graphql/types/ml/candidate_type_spec.rb @@ -10,6 +10,9 @@ let_it_be(:candidate) do model_version.candidate.tap do |c| c.update!(ci_build: create(:ci_build, pipeline: pipeline, user: current_user)) + c.metrics = [create(:ml_candidate_metrics, candidate: c)] + c.params = [create(:ml_candidate_params, candidate: c)] + c.metadata = [create(:ml_candidate_metadata, candidate: c)] end end @@ -33,6 +36,21 @@ showPath artifactPath } + metrics { + nodes { + id + } + } + params { + nodes { + id + } + } + metadata { + nodes { + id + } + } } } } @@ -59,6 +77,27 @@ '_links' => { 'showPath' => "/#{project.full_path}/-/ml/candidates/#{model_version.candidate.iid}", 'artifactPath' => "/#{project.full_path}/-/packages/#{model_version.package_id}" + }, + 'metrics' => { + 'nodes' => [ + { + 'id' => "gid://gitlab/Ml::CandidateMetric/#{candidate.metrics.first.id}" + } + ] + }, + 'params' => { + 'nodes' => [ + { + 'id' => "gid://gitlab/Ml::CandidateParam/#{candidate.params.first.id}" + } + ] + }, + 'metadata' => { + 'nodes' => [ + { + 'id' => "gid://gitlab/Ml::CandidateMetadata/#{candidate.metadata.first.id}" + } + ] } }) end diff --git a/spec/graphql/types/ml/model_version_type_spec.rb b/spec/graphql/types/ml/model_version_type_spec.rb index 16d4e2350a45..b77c4d77791f 100644 --- a/spec/graphql/types/ml/model_version_type_spec.rb +++ b/spec/graphql/types/ml/model_version_type_spec.rb @@ -15,6 +15,7 @@ latestVersion { id version + packageId candidate { id } @@ -33,7 +34,7 @@ subject(:data) { GitlabSchema.execute(query, context: { current_user: project.owner }).as_json } it 'includes all fields' do - expected_fields = %w[id version created_at _links candidate] + expected_fields = %w[id version created_at _links candidate package_id] expect(described_class).to include_graphql_fields(*expected_fields) end @@ -44,6 +45,7 @@ expect(version_data).to eq({ 'id' => "gid://gitlab/Ml::ModelVersion/#{model_version.id}", 'version' => model_version.version, + 'packageId' => "gid://gitlab/Packages::Package/#{model_version.package_id}", 'candidate' => { 'id' => "gid://gitlab/Ml::Candidate/#{model_version.candidate.id}" }, -- GitLab