1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
# frozen_string_literal: true
module API
class MlModelPackages < ::API::Base
include APIGuard
include ::API::Helpers::Authentication
ML_MODEL_PACKAGES_REQUIREMENTS = {
model_name: API::NO_SLASH_URL_PART_REGEX,
file_name: API::NO_SLASH_URL_PART_REGEX
}.freeze
FAILURES = [
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not Found' }
].freeze
ALLOWED_STATUSES = %w[default hidden].freeze
CANDIDATE_PREFIX = 'candidate:'
feature_category :mlops
urgency :low
after_validation do
require_packages_enabled!
authenticate_non_get!
not_found! unless can?(current_user, :read_model_registry, user_project)
end
authenticate_with do |accept|
accept.token_types(:personal_access_token, :job_token)
.sent_through(:http_bearer_token)
end
helpers do
include ::API::Helpers::PackagesHelpers
include ::API::Helpers::Packages::BasicAuthHelpers
def project
authorized_user_project
end
def max_file_size_exceeded?
project.actual_limits.exceeded?(:ml_model_max_file_size, params[:file].size)
end
def find_model_version!
::Ml::ModelVersion.by_project_id_and_id(user_project.id, params[:model_version_id]) || not_found!
end
def find_candidate!
candidate_iid = params[:model_version_id].delete_prefix(CANDIDATE_PREFIX)
candidate = ::Ml::Candidate.with_project_id_and_iid(user_project.id, candidate_iid)
candidate || not_found!
end
def model_version
@model_version ||= find_model_version!
end
def candidate
@candidate ||= find_candidate!
end
def candidate_package
::Packages::MlModel::PackageForCandidateService
.new(candidate.project, current_user, { candidate: candidate })
.execute
end
def package
return candidate_package if params[:model_version_id].starts_with?(CANDIDATE_PREFIX)
model_version.package
end
end
params do
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
params do
requires :file_name, type: String, desc: 'File name', file_path: true,
regexp: Gitlab::Regex.ml_model_file_name_regex
optional :path, type: String, desc: 'File directory path'
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
requires :model_version_id, type: String, desc: 'Model version id'
end
namespace ':id/packages/ml_models/:model_version_id/files/(*path/):file_name',
requirements: ML_MODEL_PACKAGES_REQUIREMENTS do
desc 'Workhorse authorize model package file' do
detail 'Introduced in GitLab 16.8'
success code: 200
failure FAILURES
tags %w[ml_model_registry]
end
put 'authorize' do
authorize_workhorse!(subject: project, maximum_size: project.actual_limits.ml_model_max_file_size)
end
desc 'Workhorse upload model package file' do
detail 'Introduced in GitLab 16.8'
success code: 201
failure FAILURES
tags %w[ml_model_registry]
end
params do
requires :file,
type: ::API::Validations::Types::WorkhorseFile,
desc: 'The package file to be published (generated by Multipart middleware)',
documentation: { type: 'file' }
end
put do
authorize_upload!(project)
not_found! unless can?(current_user, :write_model_registry, project)
bad_request!(s_('MlModelRegistry|Artifact file is too large')) if max_file_size_exceeded?
bad_request!(s_('MlModelRegistry|Package creation failed')) unless package
create_package_file_params = declared(params).merge(
package: package,
build: current_authenticated_job,
package_name: package.name,
package_version: package.version,
file_name: [params[:path], params[:file_name]].compact.join('/')
)
package_file = ::Packages::MlModel::CreatePackageFileService
.new(project, current_user, create_package_file_params)
.execute
bad_request!(s_('MlModelRegistry|Artifact file creation failed')) unless package_file
track_package_event('push_package', :ml_model, project: project, namespace: project.namespace)
created!
rescue ObjectStorage::RemoteStoreError => e
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id })
forbidden!
end
desc 'Download an ml_model package file' do
detail 'This feature was introduced in GitLab 16.8'
success code: 200
failure FAILURES
tags %w[ml_model_registry]
end
get do
authorize_read_package!(project)
file_name = URI.encode_uri_component([params[:path], params[:file_name]].compact.join('/'))
package_file = ::Packages::PackageFileFinder.new(package, file_name).execute!
track_package_event('pull_package', :ml_model, project: project, namespace: project.namespace)
present_package_file!(package_file)
end
end
end
end
end
|