diff --git a/.github/workflows/test-spec.yml b/.github/workflows/test-spec.yml index 1d915bbc7..dad66e2f1 100644 --- a/.github/workflows/test-spec.yml +++ b/.github/workflows/test-spec.yml @@ -207,4 +207,4 @@ jobs: if: github.event_name == 'pull_request' with: name: pr-comment - path: pr-comment.json + path: pr-comment.json \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ea5d565b5..4b9cb13ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added `aggs` property as an alias to `aggregations` in requestBody of `search` [#774](https://github.com/opensearch-project/opensearch-api-specification/issues/774) - Added `POST /_plugins/_ml/memory`, `POST /_plugins/_ml/memory/_search`, `{memory_id}/_search`, `{memory_id}/messages`, `PUT /_plugins/_ml/memory/{memory_id}`, `message/{message_id}`, `GET /_plugins/_ml/memory`, `GET /_plugins/_ml/memory/{memory_id}`, `_search`, `message/{message_id}`, `{memory_id}/messages`, `{memory_id}/_search`, `message/{message_id}/traces`, and `DELETE /_plugins/_ml/memory/{memory_id}` ([#771](https://github.com/opensearch-project/opensearch-api-specification/pull/771)) - Added support for evaluating response payloads in prologues and epilogues ([#772](https://github.com/opensearch-project/opensearch-api-specification/pull/772)) +- Added `GET /_plugins/_ml/models/{model_id}`, `POST /_plugins/_ml/models/_search`, `POST /_plugins/_ml/models/_unload`, `_undeploy`, `_upload`, `meta`, `_register_meta`, `POST /_plugins/_ml/models/{model_id}/_load`, `_predict`, `_unload`, `chunk/{chunk_number}`, `upload_chunk/{chunk_number}`, and `PUT /_plugins/_ml/models/{model_id}` ([#733](https://github.com/opensearch-project/opensearch-api-specification/pull/733)) ### Removed - Removed unsupported `_common.mapping:SourceField`'s `mode` field and associated `_common.mapping:SourceFieldMode` enum ([#652](https://github.com/opensearch-project/opensearch-api-specification/pull/652)) diff --git a/json_schemas/test_story.schema.yaml b/json_schemas/test_story.schema.yaml index 333f24987..01b742b51 100644 --- a/json_schemas/test_story.schema.yaml +++ b/json_schemas/test_story.schema.yaml @@ -105,6 +105,8 @@ definitions: $ref: '#/definitions/Distributions' retry: $ref: '#/definitions/Retry' + response: + $ref: '#/definitions/ExpectedResponse' required: [method, path] Output: diff --git a/spec/namespaces/ml.yaml b/spec/namespaces/ml.yaml index f4cef1311..bae59a079 100644 --- a/spec/namespaces/ml.yaml +++ b/spec/namespaces/ml.yaml @@ -70,16 +70,107 @@ paths: post: operationId: ml.register_model.0 x-operation-group: ml.register_model + x-version-added: '2.7' description: Registers a model. requestBody: $ref: '#/components/requestBodies/ml.register_model' responses: '200': $ref: '#/components/responses/ml.register_model@200' + /_plugins/_ml/models/meta: + post: + operationId: ml.create_model_meta.0 + x-operation-group: ml.create_model_meta + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `_register_meta` instead. + description: Registers model metadata. + requestBody: + $ref: '#/components/requestBodies/ml.create_model_meta' + responses: + '200': + $ref: '#/components/responses/ml.create_model_meta@200' + /_plugins/_ml/models/_register_meta: + post: + operationId: ml.register_model_meta.0 + x-operation-group: ml.register_model_meta + x-version-added: '2.7' + description: Registers model metadata. + requestBody: + $ref: '#/components/requestBodies/ml.register_model_meta' + responses: + '200': + $ref: '#/components/responses/ml.register_model_meta@200' + /_plugins/_ml/models/{model_id}/chunk/{chunk_number}: + post: + operationId: ml.chunk_model.0 + x-operation-group: ml.chunk_model + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `upload_chunk` instead. + description: Uploads model chunk. + parameters: + - $ref: '#/components/parameters/ml.chunk_model::path.chunk_number' + - $ref: '#/components/parameters/ml.chunk_model::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.chunk_model' + responses: + '200': + $ref: '#/components/responses/ml.chunk_model@200' + /_plugins/_ml/models/{model_id}/upload_chunk/{chunk_number}: + post: + operationId: ml.upload_chunk.0 + x-operation-group: ml.upload_chunk + x-version-added: '2.7' + description: Uploads model chunk. + parameters: + - $ref: '#/components/parameters/ml.upload_chunk::path.chunk_number' + - $ref: '#/components/parameters/ml.upload_chunk::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.upload_chunk' + responses: + '200': + $ref: '#/components/responses/ml.upload_chunk@200' + /_plugins/_ml/models/_upload: + post: + operationId: ml.upload_model.0 + x-operation-group: ml.upload_model + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `register_model` instead. + description: Registers a model. + requestBody: + $ref: '#/components/requestBodies/ml.upload_model' + responses: + '200': + $ref: '#/components/responses/ml.upload_model@200' /_plugins/_ml/models/{model_id}: + get: + operationId: ml.get_model.0 + x-operation-group: ml.get_model + x-version-added: '1.3' + description: Retrieves a model. + parameters: + - $ref: '#/components/parameters/ml.get_model::path.model_id' + responses: + '200': + $ref: '#/components/responses/ml.get_model@200' + put: + operationId: ml.update_model.0 + x-operation-group: ml.update_model + x-version-added: '2.12' + description: Updates a model. + parameters: + - $ref: '#/components/parameters/ml.update_model::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.update_model' + responses: + '200': + $ref: '#/components/responses/ml.update_model@200' delete: operationId: ml.delete_model.0 x-operation-group: ml.delete_model + x-version-added: '1.3' description: Deletes a model. parameters: - $ref: '#/components/parameters/ml.delete_model::path.model_id' @@ -90,22 +181,91 @@ paths: post: operationId: ml.deploy_model.0 x-operation-group: ml.deploy_model + x-version-added: '2.7' description: Deploys a model. parameters: - $ref: '#/components/parameters/ml.deploy_model::path.model_id' responses: '200': $ref: '#/components/responses/ml.deploy_model@200' + /_plugins/_ml/models/{model_id}/_load: + post: + operationId: ml.load_model.0 + x-operation-group: ml.load_model + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `deploy_model` instead. + description: Deploys a model. + parameters: + - $ref: '#/components/parameters/ml.load_model::path.model_id' + responses: + '200': + $ref: '#/components/responses/ml.load_model@200' /_plugins/_ml/models/{model_id}/_undeploy: post: operationId: ml.undeploy_model.0 x-operation-group: ml.undeploy_model + x-version-added: '2.7' description: Undeploys a model. parameters: - $ref: '#/components/parameters/ml.undeploy_model::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.undeploy_model' responses: '200': $ref: '#/components/responses/ml.undeploy_model@200' + /_plugins/_ml/models/_undeploy: + post: + operationId: ml.undeploy_model.1 + x-operation-group: ml.undeploy_model + x-version-added: '2.7' + description: Undeploys a model. + requestBody: + $ref: '#/components/requestBodies/ml.undeploy_model' + responses: + '200': + $ref: '#/components/responses/ml.undeploy_model@200' + /_plugins/_ml/models/{model_id}/_unload: + post: + operationId: ml.unload_model.0 + x-operation-group: ml.unload_model + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `undeploy_model` instead. + description: Unloads a model. + parameters: + - $ref: '#/components/parameters/ml.unload_model::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.unload_model' + responses: + '200': + $ref: '#/components/responses/ml.unload_model@200' + /_plugins/_ml/models/_unload: + post: + operationId: ml.unload_model.1 + x-operation-group: ml.unload_model + deprecated: true + x-version-deprecated: '2.7' + x-deprecation-message: Use `undeploy_model` instead. + description: Unloads a model. + requestBody: + $ref: '#/components/requestBodies/ml.unload_model' + responses: + '200': + $ref: '#/components/responses/ml.unload_model@200' + /_plugins/_ml/models/{model_id}/_predict: + post: + operationId: ml.predict_model.0 + x-operation-group: ml.predict_model + x-version-added: '2.12' + description: Predicts a model. + parameters: + - $ref: '#/components/parameters/ml.predict_model::path.model_id' + requestBody: + $ref: '#/components/requestBodies/ml.predict_model' + responses: + '200': + $ref: '#/components/responses/ml.predict_model@200' /_plugins/_ml/tasks/{task_id}: get: operationId: ml.get_task.0 @@ -129,6 +289,17 @@ paths: get: operationId: ml.search_models.0 x-operation-group: ml.search_models + x-version-added: '1.3' + description: Searches for models. + requestBody: + $ref: '#/components/requestBodies/ml.search_models' + responses: + '200': + $ref: '#/components/responses/ml.search_models@200' + post: + operationId: ml.search_models.1 + x-operation-group: ml.search_models + x-version-added: '1.3' description: Searches for models. requestBody: $ref: '#/components/requestBodies/ml.search_models' @@ -497,20 +668,132 @@ components: type: string description: The model name. version: + $ref: '../schemas/_common.yaml#/components/schemas/VersionString' + model_format: + $ref: '../schemas/_common.yaml#/components/schemas/ModelFormat' + description: type: string - description: The model version. + description: The model description. + model_group_id: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + required: + - name + - version + ml.create_model_meta: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: The model name. + version: + $ref: '../schemas/_common.yaml#/components/schemas/VersionString' model_format: + $ref: '../schemas/_common.yaml#/components/schemas/ModelFormat' + model_group_id: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + model_content_hash_value: type: string - description: The portable format of the model file. - enum: [ONNX, TORCH_SCRIPT] + description: The model content hash value. + model_config: + $ref: '../schemas/ml._common.yaml#/components/schemas/ModelConfig' + total_chunks: + type: integer + format: int64 + description: Number of chunks the model is split into. + url: + type: string + description: The model URL. description: type: string description: The model description. + required: + - model_config + - model_content_hash_value + - model_format + - name + - total_chunks + - version + ml.register_model_meta: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: The model name. + version: + $ref: '../schemas/_common.yaml#/components/schemas/VersionString' + model_format: + $ref: '../schemas/_common.yaml#/components/schemas/ModelFormat' model_group_id: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + model_content_hash_value: + type: string + description: The model content hash value. + model_config: + $ref: '../schemas/ml._common.yaml#/components/schemas/ModelConfig' + total_chunks: + type: integer + format: int64 + description: Number of chunks the model is split into. + url: type: string - description: The ID of the model group to which to register the model. + description: The model URL. + description: + type: string + description: The model description. required: + - model_config + - model_content_hash_value - model_format + - name + - total_chunks + - version + ml.chunk_model: + content: + application/json: + schema: + type: object + properties: + chunk: + type: string + description: The model chunk. + required: + - chunk + ml.upload_chunk: + content: + application/json: + schema: + type: object + properties: + chunk: + type: string + description: The model chunk. + required: + - chunk + ml.upload_model: + content: + application/json: + schema: + type: object + properties: + name: + type: string + description: The model name. + version: + $ref: '../schemas/_common.yaml#/components/schemas/VersionString' + model_format: + $ref: '../schemas/_common.yaml#/components/schemas/ModelFormat' + description: + type: string + description: The model description. + model_group_id: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + required: - name - version ml.search_models: @@ -531,6 +814,84 @@ components: items: $ref: '../schemas/ml._common.yaml#/components/schemas/Sort' description: The sort order. + ml.update_model: + content: + application/json: + schema: + type: object + properties: + connector: + type: object + description: The connector to use for the model. + connector_id: + type: string + description: The connector ID. + is_enabled: + type: boolean + description: Whether the model is enabled. + description: + type: string + description: The model description. + model_config: + $ref: '../schemas/ml._common.yaml#/components/schemas/ModelConfig' + name: + type: string + description: The model name. + rate_limiter: + $ref: '../schemas/ml._common.yaml#/components/schemas/RateLimiter' + guardrails: + $ref: '../schemas/ml._common.yaml#/components/schemas/Guardrails' + interface: + type: object + description: The model interface. + ml.undeploy_model: + content: + application/json: + schema: + type: object + properties: + node_ids: + type: array + items: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + model_ids: + type: array + items: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + required: + - model_ids + ml.unload_model: + content: + application/json: + schema: + type: object + properties: + node_ids: + type: array + items: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + model_ids: + type: array + items: + $ref: '../schemas/_common.yaml#/components/schemas/Id' + required: + - model_ids + ml.predict_model: + content: + application/json: + schema: + type: object + properties: + query_text: + type: string + description: The query text. + text_docs: + type: array + items: + type: string + description: The text documents. + required: + - text_docs ml.predict: content: application/json: @@ -849,6 +1210,67 @@ components: required: - status - task_id + ml.create_model_meta@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + model_id: + type: string + required: + - model_id + - status + ml.register_model_meta@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + model_id: + type: string + required: + - model_id + - status + ml.chunk_model@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + description: The status of the chunk upload operation. + required: + - status + ml.upload_chunk@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + description: The status of the chunk upload operation. + required: + - status + ml.upload_model@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + task_id: + type: string + required: + - status + - task_id ml.deploy_model@200: content: application/json: @@ -864,16 +1286,61 @@ components: required: - status - task_id + ml.load_model@200: + content: + application/json: + schema: + type: object + properties: + status: + type: string + task_id: + type: string + task_type: + type: string + enum: + - BATCH_INGEST + - BATCH_PREDICTION + - DEPLOY_MODEL + - EXECUTION + - PREDICTION + - REGISTER_MODEL + - TRAINING + - TRAINING_AND_PREDICTION + required: + - status + - task_id + - task_type ml.undeploy_model@200: content: application/json: schema: $ref: '../schemas/ml._common.yaml#/components/schemas/UndeployModelResponse' + ml.unload_model@200: + content: + application/json: + schema: + $ref: '../schemas/ml._common.yaml#/components/schemas/UnloadModelResponse' + ml.predict_model@200: + content: + application/json: + schema: + $ref: '../schemas/ml._common.yaml#/components/schemas/PredictModelResponse' ml.delete_model@200: content: application/json: schema: $ref: '../schemas/_common.yaml#/components/schemas/WriteResponseBase' + ml.get_model@200: + content: + application/json: + schema: + $ref: '../schemas/ml._common.yaml#/components/schemas/Model' + ml.update_model@200: + content: + application/json: + schema: + $ref: '../schemas/ml._common.yaml#/components/schemas/UpdateModelResponse' ml.delete_task@200: content: application/json: @@ -1036,18 +1503,74 @@ components: required: true schema: type: string + ml.get_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string + ml.update_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string ml.deploy_model::path.model_id: name: model_id in: path required: true schema: type: string + ml.load_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string ml.undeploy_model::path.model_id: name: model_id in: path required: true schema: type: string + ml.unload_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string + ml.predict_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string + ml.chunk_model::path.chunk_number: + name: chunk_number + in: path + required: true + schema: + type: integer + format: int64 + ml.chunk_model::path.model_id: + name: model_id + in: path + required: true + schema: + type: string + ml.upload_chunk::path.chunk_number: + name: chunk_number + in: path + required: true + schema: + type: integer + format: int64 + ml.upload_chunk::path.model_id: + name: model_id + in: path + required: true + schema: + type: string ml.delete_task::path.task_id: name: task_id in: path diff --git a/spec/schemas/_common.yaml b/spec/schemas/_common.yaml index 139e8290f..6491758db 100644 --- a/spec/schemas/_common.yaml +++ b/spec/schemas/_common.yaml @@ -2475,4 +2475,9 @@ components: type: number required: - active_threads - - thread_executions \ No newline at end of file + - thread_executions + ModelFormat: + type: string + enum: + - ONNX + - TORCH_SCRIPT \ No newline at end of file diff --git a/spec/schemas/ml._common.yaml b/spec/schemas/ml._common.yaml index 9c416c9a2..281fe255d 100644 --- a/spec/schemas/ml._common.yaml +++ b/spec/schemas/ml._common.yaml @@ -136,8 +136,7 @@ components: model_config: $ref: '#/components/schemas/ModelConfig' model_format: - type: string - description: The model format. + $ref: '_common.yaml#/components/schemas/ModelFormat' model_task_type: type: string description: The model task type. @@ -273,7 +272,6 @@ components: type: object properties: inference_results: - # Update this once the PR for ML Model APIs (#733) has been merged type: array items: $ref: '#/components/schemas/InferenceResults' @@ -781,6 +779,149 @@ components: description: The status. SearchModelGroupsResponse: $ref: '#/components/schemas/SearchResponse' + Model: + type: object + properties: + name: + type: string + description: The model name. + model_group_id: + type: string + description: The model group ID. + algorithm: + type: string + description: The algorithm. + model_version: + type: string + description: The model version. + model_format: + $ref: '_common.yaml#/components/schemas/ModelFormat' + model_state: + type: string + description: The model state. + enum: + - DEPLOYED + - DEPLOYING + - DEPLOY_FAILED + - PARTIALLY_DEPLOYED + - REGISTERED + - REGISTERING + - UNDEPLOYED + model_content_size_in_bytes: + type: integer + format: int64 + description: The model content size in bytes. + model_content_hash_value: + type: string + description: The model content hash value. + model_config: + $ref: '#/components/schemas/ModelConfig' + created_time: + type: integer + format: int64 + description: The created time. + last_updated_time: + type: integer + format: int64 + description: The last updated time. + last_registered_time: + type: integer + format: int64 + description: The last registered time. + total_chunks: + type: integer + format: int64 + description: The total chunks. + is_hidden: + type: boolean + description: Whether the model is hidden. + required: + - model_state + RateLimiter: + type: object + properties: + limit: + type: integer + format: int64 + description: The maximum limit. + unit: + type: string + description: The unit of time. + enum: + - DAYS + - HOURS + - MICROSECONDS + - MILLISECONDS + - MINUTES + - NANOSECONDS + - SECONDS + required: + - limit + - unit + Guardrails: + type: object + properties: + type: + type: string + description: The guardrails type. + enum: + - local_regex + - model + input_guardrail: + $ref: '#/components/schemas/GuardrailsInputOutput' + output_guardrail: + $ref: '#/components/schemas/GuardrailsInputOutput' + stop_words: + $ref: '_common.analysis.yaml#/components/schemas/StopWords' + index_name: + $ref: '_common.yaml#/components/schemas/IndexName' + source_fields: + $ref: '_common.yaml#/components/schemas/Fields' + regex: + type: object + description: The regex used for input/output validation. + model_id: + type: string + description: The model ID. + response_filter: + type: string + description: The response filter. + response_validation_regex: + type: string + description: The response validation regex. + GuardrailsInputOutput: + type: object + properties: + model_id: + type: string + description: The model ID. + response_validation_regex: + type: string + description: The response validation regex. + UpdateModelResponse: + type: object + properties: + _index: + $ref: '_common.yaml#/components/schemas/IndexName' + _id: + $ref: '_common.yaml#/components/schemas/Id' + _version: + $ref: '_common.yaml#/components/schemas/VersionNumber' + result: + $ref: '_common.yaml#/components/schemas/Result' + forced_refresh: + type: boolean + description: Whether the model is forced to refresh. + _shards: + $ref: '_common.yaml#/components/schemas/ShardStatistics' + _seq_no: + $ref: '_common.yaml#/components/schemas/SequenceNumber' + _primary_term: + type: integer + format: int64 + description: The primary term. + required: + - _index Task: type: object properties: @@ -836,6 +977,69 @@ components: UndeployModelNodeStats: type: object additionalProperties: true + UnloadModelResponse: + type: object + additionalProperties: + title: nodes + $ref: '#/components/schemas/UnloadModelNode' + UnloadModelNode: + type: object + properties: + stats: + $ref: '#/components/schemas/UnloadModelNodeStats' + UnloadModelNodeStats: + type: object + additionalProperties: true + PredictModelResponse: + type: object + properties: + inference_results: + type: array + items: + $ref: '#/components/schemas/PredictModelResult' + PredictModelResult: + type: object + properties: + output: + type: array + items: + $ref: '#/components/schemas/PredictModelOutput' + PredictModelOutput: + type: object + properties: + name: + type: string + description: The output name. + data_type: + type: string + description: The output data type. + enum: + - BOOLEAN + - FLOAT16 + - FLOAT32 + - FLOAT64 + - INT32 + - INT64 + - INT8 + - STRING + - UINT8 + - UNKNOWN + shape: + type: array + items: + type: integer + format: int64 + description: The output shape. + data: + type: array + items: + type: number + format: double + description: The output data. + byte_buffer: + $ref: '#/components/schemas/ByteBuffer' + required: + - data Credential: type: object properties: diff --git a/tests/default/_core/search/scroll.yaml b/tests/default/_core/search/scroll.yaml index 6ed9e10ff..fb4baccd1 100644 --- a/tests/default/_core/search/scroll.yaml +++ b/tests/default/_core/search/scroll.yaml @@ -105,11 +105,3 @@ chapters: path: /_search/scroll/{scroll_id} parameters: scroll_id: ${scroll.scroll_id} - - synopsis: Deleting a non-existent scroll. - method: DELETE - path: /_search/scroll - request: - payload: - scroll_id: ${scroll.scroll_id} - response: - status: 404 diff --git a/tests/plugins/ml/ml/models.yaml b/tests/plugins/ml/ml/models.yaml index 92cac3ca3..7258c5d07 100644 --- a/tests/plugins/ml/ml/models.yaml +++ b/tests/plugins/ml/ml/models.yaml @@ -51,6 +51,23 @@ chapters: hits: hits: - _score: 1 + - synopsis: Get model. + path: /_plugins/_ml/models/{model_id} + method: GET + parameters: + model_id: ${get_completed_task.model_id} + response: + status: 200 + - synopsis: Update model. + path: /_plugins/_ml/models/{model_id} + method: PUT + parameters: + model_id: ${get_completed_task.model_id} + request: + payload: + name: updated + response: + status: 200 - synopsis: Delete model. path: /_plugins/_ml/models/{model_id} parameters: diff --git a/tests/plugins/ml/ml/models/chunk.yaml b/tests/plugins/ml/ml/models/chunk.yaml new file mode 100644 index 000000000..f3be9a004 --- /dev/null +++ b/tests/plugins/ml/ml/models/chunk.yaml @@ -0,0 +1,74 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test deprecated chunk API for chunk upload. +version: '>= 2.7' +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_registering_model_via_local_file: true + - path: /_plugins/_ml/models/meta + id: model_metadata + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + model_content_hash_value: 123abc + total_chunks: 3 + model_config: + all_config: path + model_type: bert + embedding_dimension: 384 + framework_type: sentence_transformers + output: + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${model_metadata.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Upload chunk 0. + path: /_plugins/_ml/models/{model_id}/chunk/{chunk_number} + method: POST + parameters: + chunk_number: 0 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk0 + response: + status: 200 + payload: + status: Uploaded + - synopsis: Upload chunk 1. + path: /_plugins/_ml/models/{model_id}/chunk/{chunk_number} + method: POST + parameters: + chunk_number: 1 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk1 + response: + status: 200 + payload: + status: Uploaded + - synopsis: Upload chunk 2. + path: /_plugins/_ml/models/{model_id}/chunk/{chunk_number} + method: POST + parameters: + chunk_number: 2 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk2 + response: + status: 200 + payload: + status: Uploaded \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/load.yaml b/tests/plugins/ml/ml/models/load.yaml new file mode 100644 index 000000000..e3d56d11c --- /dev/null +++ b/tests/plugins/ml/ml/models/load.yaml @@ -0,0 +1,60 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the deployment of a model using deprecated load API. +prologues: + - path: /_plugins/_ml/models/_register + id: register_model + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_register_model_task + method: GET + parameters: + task_id: ${register_model.task_id} + retry: + count: 3 + wait: 10000 + output: + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_load_model_task + method: GET + parameters: + task_id: ${load_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + model_id: payload.model_id + - path: /_plugins/_ml/models/{model_id}/_undeploy + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + status: [200, 404] + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${get_completed_register_model_task.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Deploy a model. + id: load_model + path: /_plugins/_ml/models/{model_id}/_load + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + output: + task_id: payload.task_id + response: + status: 200 \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/meta.yaml b/tests/plugins/ml/ml/models/meta.yaml new file mode 100644 index 000000000..2dc9d1d90 --- /dev/null +++ b/tests/plugins/ml/ml/models/meta.yaml @@ -0,0 +1,37 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test deprecated meta API for model creation. +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_registering_model_via_local_file: true +epilogues: + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${model_metadata.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Create model metadata. + id: model_metadata + path: /_plugins/_ml/models/meta + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + model_content_hash_value: 123abc + total_chunks: 3 + model_config: + all_config: path + model_type: bert + embedding_dimension: 384 + framework_type: sentence_transformers + response: + status: 200 + output: + model_id: payload.model_id \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/predict.yaml b/tests/plugins/ml/ml/models/predict.yaml new file mode 100644 index 000000000..1a2cd5471 --- /dev/null +++ b/tests/plugins/ml/ml/models/predict.yaml @@ -0,0 +1,105 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the prediction of new data. +version: '>= 2.12' +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.only_run_on_ml_node: false + plugins.ml_commons.model_access_control_enabled: true + plugins.ml_commons.native_memory_threshold: 99 + - path: /_plugins/_ml/model_groups/_register + id: create_model_group + method: POST + request: + payload: + name: NLP_Group + description: Model group for NLP models. + output: + test_model_group_id: payload.model_group_id + - path: /_plugins/_ml/models/_register + id: register_model + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_register_model_task + method: GET + parameters: + task_id: ${register_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + model_id: payload.model_id + - path: /_plugins/_ml/models/{model_id}/_deploy + id: deploy_model + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_deploy_model_task + method: GET + parameters: + task_id: ${deploy_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/models/{model_id}/_undeploy + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + status: [200, 404] + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${get_completed_register_model_task.model_id} + method: DELETE + status: [200, 404] + - path: /_plugins/_ml/model_groups/{model_group_id} + method: DELETE + status: [200, 404] + parameters: + model_group_id: ${create_model_group.test_model_group_id} +chapters: + - synopsis: Predict new data. + id: predict_data + path: /_plugins/_ml/models/{model_id}/_predict + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + request: + payload: + query_text: The best selling book series in history is Harry Potter + text_docs: + - Harry Potter is written by J.K. Rowling + - The Great Gatsby is a story of wealth and tragedy + - The Lord of the Rings is an epic high fantasy novel + - The best selling book series in history is Harry Potter + response: + status: 200 + payload: + inference_results: + - output: + - data: + - 101 \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/register_meta.yaml b/tests/plugins/ml/ml/models/register_meta.yaml new file mode 100644 index 000000000..ae4f1b8bb --- /dev/null +++ b/tests/plugins/ml/ml/models/register_meta.yaml @@ -0,0 +1,38 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the creation of model metadata. +version: '>= 2.7' +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_registering_model_via_local_file: true +epilogues: + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${model_metadata.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Register model metadata. + id: model_metadata + path: /_plugins/_ml/models/_register_meta + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + model_content_hash_value: 123abc + total_chunks: 3 + model_config: + all_config: path + model_type: bert + embedding_dimension: 384 + framework_type: sentence_transformers + response: + status: 200 + output: + model_id: payload.model_id \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/search.yaml b/tests/plugins/ml/ml/models/search.yaml new file mode 100644 index 000000000..db1e84686 --- /dev/null +++ b/tests/plugins/ml/ml/models/search.yaml @@ -0,0 +1,30 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the search of models. +version: '>= 2.7' +prologues: + - path: /_plugins/_ml/models/_register + id: register_model + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + output: + task_id: payload.task_id +chapters: + - synopsis: Search model. + path: /_plugins/_ml/models/_search + method: POST + request: + payload: + query: + match_all: {} + size: 1000 + response: + status: 200 + payload: + hits: + hits: + - _score: 1 \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/undeploy.yaml b/tests/plugins/ml/ml/models/undeploy.yaml new file mode 100644 index 000000000..b59da6fd2 --- /dev/null +++ b/tests/plugins/ml/ml/models/undeploy.yaml @@ -0,0 +1,87 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the undeployment of specific models from specific nodes and a model from all nodes. +version: '>= 2.7' +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_custom_deployment_plan: true + - path: /_plugins/_ml/models/_register + id: register_model + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_register_model_task + method: GET + parameters: + task_id: ${register_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + model_id: payload.model_id + - path: /_plugins/_ml/models/{model_id}/_deploy + id: deploy_model + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_deploy_model_task + method: GET + parameters: + task_id: ${deploy_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + node_id: payload.worker_node[0] + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/models/{model_id}/_undeploy + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + status: [200, 404] + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${get_completed_register_model_task.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Undeploy specific models from specific nodes. + path: /_plugins/_ml/models/_undeploy + method: POST + request: + payload: + node_ids: + - ${get_completed_deploy_model_task.node_id} + model_ids: + - ${get_completed_register_model_task.model_id} + response: + status: 200 + - synopsis: Undeploy a model from all nodes. + path: /_plugins/_ml/models/{model_id}/_undeploy + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + response: + status: 200 \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/unload.yaml b/tests/plugins/ml/ml/models/unload.yaml new file mode 100644 index 000000000..d94b29114 --- /dev/null +++ b/tests/plugins/ml/ml/models/unload.yaml @@ -0,0 +1,86 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the unload (deprecated) of specific models from specific nodes and a model from all nodes. +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_custom_deployment_plan: true + - path: /_plugins/_ml/models/_register + id: register_model + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_register_model_task + method: GET + parameters: + task_id: ${register_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + model_id: payload.model_id + - path: /_plugins/_ml/models/{model_id}/_deploy + id: deploy_model + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + output: + task_id: payload.task_id + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_deploy_model_task + method: GET + parameters: + task_id: ${deploy_model.task_id} + retry: + count: 3 + wait: 10000 + response: + status: 200 + payload: + state: COMPLETED + output: + node_id: payload.worker_node[0] + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/models/{model_id}/_unload + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + status: [200, 404] + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${get_completed_register_model_task.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Unload specific models from specific nodes. + path: /_plugins/_ml/models/_unload + method: POST + request: + payload: + node_ids: + - ${get_completed_deploy_model_task.node_id} + model_ids: + - ${get_completed_register_model_task.model_id} + response: + status: 200 + - synopsis: Unload a model from all nodes. + path: /_plugins/_ml/models/{model_id}/_unload + method: POST + parameters: + model_id: ${get_completed_register_model_task.model_id} + response: + status: 200 \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/upload.yaml b/tests/plugins/ml/ml/models/upload.yaml new file mode 100644 index 000000000..f1ba30223 --- /dev/null +++ b/tests/plugins/ml/ml/models/upload.yaml @@ -0,0 +1,33 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test the creation of models using upload API (deprecated). +epilogues: + - path: /_plugins/_ml/tasks/{task_id} + id: get_completed_task + method: GET + parameters: + task_id: ${upload_model.task_id} + retry: + count: 3 + wait: 10000 + output: + model_id: payload.model_id + - path: /_plugins/_ml/models/{model_id} + method: DELETE + status: [200, 404] + parameters: + model_id: ${get_completed_task.model_id} +chapters: + - synopsis: Upload model. + id: upload_model + path: /_plugins/_ml/models/_upload + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + response: + status: 200 + output: + task_id: payload.task_id \ No newline at end of file diff --git a/tests/plugins/ml/ml/models/upload_chunk.yaml b/tests/plugins/ml/ml/models/upload_chunk.yaml new file mode 100644 index 000000000..213eb5ccb --- /dev/null +++ b/tests/plugins/ml/ml/models/upload_chunk.yaml @@ -0,0 +1,74 @@ +$schema: ../../../../../json_schemas/test_story.schema.yaml + +description: Test uploading model chunk. +version: '>= 2.7' +prologues: + - path: /_cluster/settings + method: PUT + request: + payload: + persistent: + plugins.ml_commons.allow_registering_model_via_local_file: true + - path: /_plugins/_ml/models/_register_meta + id: model_metadata + method: POST + request: + payload: + name: huggingface/sentence-transformers/msmarco-distilbert-base-tas-b + version: 1.0.1 + model_format: TORCH_SCRIPT + model_content_hash_value: 123abc + total_chunks: 3 + model_config: + all_config: path + model_type: bert + embedding_dimension: 384 + framework_type: sentence_transformers + output: + model_id: payload.model_id +epilogues: + - path: /_plugins/_ml/models/{model_id} + parameters: + model_id: ${model_metadata.model_id} + method: DELETE + status: [200, 404] +chapters: + - synopsis: Upload chunk 0. + path: /_plugins/_ml/models/{model_id}/upload_chunk/{chunk_number} + method: POST + parameters: + chunk_number: 0 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk0 + response: + status: 200 + payload: + status: Uploaded + - synopsis: Upload chunk 1. + path: /_plugins/_ml/models/{model_id}/upload_chunk/{chunk_number} + method: POST + parameters: + chunk_number: 1 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk1 + response: + status: 200 + payload: + status: Uploaded + - synopsis: Upload chunk 2. + path: /_plugins/_ml/models/{model_id}/upload_chunk/{chunk_number} + method: POST + parameters: + chunk_number: 2 + model_id: ${model_metadata.model_id} + request: + payload: + chunk: chunk2 + response: + status: 200 + payload: + status: Uploaded \ No newline at end of file diff --git a/tests/plugins/security/api/nodesdn.yaml b/tests/plugins/security/api/nodesdn.yaml index 6a4611ef3..1f5091445 100644 --- a/tests/plugins/security/api/nodesdn.yaml +++ b/tests/plugins/security/api/nodesdn.yaml @@ -63,4 +63,4 @@ chapters: response: status: 200 payload: - status: OK + status: OK \ No newline at end of file