diff --git a/Makefile b/Makefile index c90107fe5..7b3256c5d 100644 --- a/Makefile +++ b/Makefile @@ -152,8 +152,8 @@ dist_native: # Project specific targets ## codegen codegen: - # Download openapi.json from https://platform.aignostics.com/api/v1/openapi.json, - # format via jq, and save as codegen/in/openapi_$version.json, with the + # Download openapi.json from https://platform.aignostics.com/api/v1/openapi.json, + # format via jq, and save as codegen/in/openapi_$version.json, with the # version extracted from the info.version field in the JSON mkdir -p codegen/in/archive # curl -s https://platform.aignostics.com/api/v1/openapi.json | jq . > codegen/in/openapi.json @@ -176,9 +176,9 @@ codegen: find codegen/out/aignx/codegen/models/ -name "[a-z]*.py" -type f | sed 's|.*/\(.*\)\.py|\1|' | xargs -I{} echo "from .{} import *" > codegen/out/aignx/codegen/models/__init__.py # fix resource patch # in codegen/out/public_api.py replace all occurrences of resource_path='/v1 with resource_path='/api/v1 - # Use portable sed syntax: -i'' works on both macOS and Linux - sed -i"" "s|resource_path='/v1|resource_path='/api/v1|g" codegen/out/aignx/codegen/api/public_api.py - + # Use portable sed syntax: try GNU-style -i'' first, then BSD/macOS-style -i '' + sed -i'' "s|resource_path='/v1|resource_path='/api/v1|g" codegen/out/aignx/codegen/api/public_api.py || sed -i '' "s|resource_path='/v1|resource_path='/api/v1|g" codegen/out/aignx/codegen/api/public_api.py + # Special rule to catch any arguments (like patch, minor, major, pdf, Python versions, or x.y.z) # This prevents "No rule to make target" errors when passing arguments to make commands .PHONY: % diff --git a/codegen/in/archive/openapi_1.4.0.json b/codegen/in/archive/openapi_1.4.0.json new file mode 100644 index 000000000..cae9e0200 --- /dev/null +++ b/codegen/in/archive/openapi_1.4.0.json @@ -0,0 +1,2970 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Aignostics Platform API", + "description": "\nThe Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. \n\nTo begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. \n\nMore information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com).\n\n**How to authorize and test API endpoints:**\n\n1. Click the \"Authorize\" button in the right corner below\n3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials\n4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint\n\n**Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized.\n\n", + "version": "1.4.0" + }, + "servers": [ + { + "url": "/api" + } + ], + "paths": { + "/v1/applications": { + "get": { + "tags": [ + "Public" + ], + "summary": "List available applications", + "description": "Returns the list of the applications, available to the caller.\n\nThe application is available if any of the versions of the application is assigned to the caller's organization.\nThe response is paginated and sorted according to the provided parameters.", + "operationId": "list_applications_v1_applications_get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Page" + } + }, + { + "name": "page-size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 5, + "default": 50, + "title": "Page-Size" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `application_id`\n- `name`\n- `description`\n- `regulatory_classes`\n\n**Examples:**\n- `?sort=application_id` - Sort by application_id ascending\n- `?sort=-name` - Sort by name descending\n- `?sort=+description&sort=name` - Sort by description ascending, then name descending", + "title": "Sort" + }, + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `application_id`\n- `name`\n- `description`\n- `regulatory_classes`\n\n**Examples:**\n- `?sort=application_id` - Sort by application_id ascending\n- `?sort=-name` - Sort by name descending\n- `?sort=+description&sort=name` - Sort by description ascending, then name descending" + } + ], + "responses": { + "200": { + "description": "A list of applications available to the caller", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationReadShortResponse" + }, + "title": "Response List Applications V1 Applications Get" + }, + "example": [ + { + "application_id": "he-tme", + "name": "Atlas H&E-TME", + "regulatory_classes": [ + "RUO" + ], + "description": "The Atlas H&E TME is an AI application designed to examine FFPE (formalin-fixed, paraffin-embedded) tissues stained with H&E (hematoxylin and eosin), delivering comprehensive insights into the tumor microenvironment.", + "latest_version": { + "number": "1.0.0", + "released_at": "2025-09-01T19:01:05.401Z" + } + }, + { + "application_id": "test-app", + "name": "Test Application", + "regulatory_classes": [ + "RUO" + ], + "description": "This is the test application with two algorithms: TissueQc and Tissue Segmentation", + "latest_version": { + "number": "2.0.0", + "released_at": "2025-09-02T19:01:05.401Z" + } + } + ] + } + } + }, + "401": { + "description": "Unauthorized - Invalid or missing authentication" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/applications/{application_id}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Read Application By Id", + "description": "Retrieve details of a specific application by its ID.", + "operationId": "read_application_by_id_v1_applications__application_id__get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Application Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplicationReadResponse" + } + } + } + }, + "403": { + "description": "Forbidden - You don't have permission to see this application" + }, + "404": { + "description": "Not Found - Application with the given ID does not exist" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/applications/{application_id}/versions/{version}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Application Version Details", + "description": "Get the application version details.\n\nAllows caller to retrieve information about application version based on provided application version ID.", + "operationId": "application_version_details_v1_applications__application_id__versions__version__get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "application_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Application Id" + } + }, + { + "name": "version", + "in": "path", + "required": true, + "schema": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VersionReadResponse" + }, + "example": { + "version_number": "0.4.4", + "changelog": "New deployment", + "input_artifacts": [ + { + "name": "whole_slide_image", + "mime_type": "image/tiff", + "metadata_schema": { + "type": "object", + "$defs": { + "LungCancerMetadata": { + "type": "object", + "title": "LungCancerMetadata", + "required": [ + "type", + "tissue" + ], + "properties": { + "type": { + "enum": [ + "lung" + ], + "type": "string", + "const": "lung", + "title": "Type" + }, + "tissue": { + "enum": [ + "lung", + "lymph node", + "liver", + "adrenal gland", + "bone", + "brain" + ], + "type": "string", + "title": "Tissue" + } + }, + "additionalProperties": false + } + }, + "title": "ExternalImageMetadata", + "$schema": "http://json-schema.org/draft-07/schema#", + "required": [ + "checksum_crc32c", + "base_mpp", + "width", + "height", + "cancer" + ], + "properties": { + "stain": { + "enum": [ + "H&E" + ], + "type": "string", + "const": "H&E", + "title": "Stain", + "default": "H&E" + }, + "width": { + "type": "integer", + "title": "Width", + "maximum": 150000, + "minimum": 1 + }, + "cancer": { + "anyOf": [ + { + "$ref": "#/$defs/LungCancerMetadata" + } + ], + "title": "Cancer" + }, + "height": { + "type": "integer", + "title": "Height", + "maximum": 150000, + "minimum": 1 + }, + "base_mpp": { + "type": "number", + "title": "Base Mpp", + "maximum": 0.5, + "minimum": 0.125 + }, + "mime_type": { + "enum": [ + "application/dicom", + "image/tiff" + ], + "type": "string", + "title": "Mime Type", + "default": "image/tiff" + }, + "checksum_crc32c": { + "type": "string", + "title": "Checksum Crc32C" + } + }, + "description": "Metadata corresponding to an external image.", + "additionalProperties": false + } + } + ], + "output_artifacts": [ + { + "name": "tissue_qc:tiff_heatmap", + "mime_type": "image/tiff", + "metadata_schema": { + "type": "object", + "title": "HeatmapMetadata", + "$schema": "http://json-schema.org/draft-07/schema#", + "required": [ + "checksum_crc32c", + "width", + "height", + "class_colors" + ], + "properties": { + "width": { + "type": "integer", + "title": "Width" + }, + "height": { + "type": "integer", + "title": "Height" + }, + "base_mpp": { + "type": "number", + "title": "Base Mpp", + "maximum": 0.5, + "minimum": 0.125 + }, + "mime_type": { + "enum": [ + "image/tiff" + ], + "type": "string", + "const": "image/tiff", + "title": "Mime Type", + "default": "image/tiff" + }, + "class_colors": { + "type": "object", + "title": "Class Colors", + "additionalProperties": { + "type": "array", + "maxItems": 3, + "minItems": 3, + "prefixItems": [ + { + "type": "integer", + "maximum": 255, + "minimum": 0 + }, + { + "type": "integer", + "maximum": 255, + "minimum": 0 + }, + { + "type": "integer", + "maximum": 255, + "minimum": 0 + } + ] + } + }, + "checksum_crc32c": { + "type": "string", + "title": "Checksum Crc32C" + } + }, + "description": "Metadata corresponding to a segmentation heatmap file.", + "additionalProperties": false + }, + "scope": "ITEM", + "visibility": "EXTERNAL" + } + ], + "released_at": "2025-04-16T08:45:20.655972Z" + } + } + } + }, + "403": { + "description": "Forbidden - You don't have permission to see this version" + }, + "404": { + "description": "Not Found - Application version with given ID is not available to you or does not exist" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs": { + "get": { + "tags": [ + "Public" + ], + "summary": "List Runs", + "description": "List runs with filtering, sorting, and pagination capabilities.\n\nReturns paginated runs that were submitted by the user.", + "operationId": "list_runs_v1_runs_get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "application_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional application ID filter", + "examples": [ + "tissue-segmentation", + "heta" + ], + "title": "Application Id" + }, + "description": "Optional application ID filter" + }, + { + "name": "application_version", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optional Version Name", + "examples": [ + "1.0.2", + "1.0.1-beta2" + ], + "title": "Application Version" + }, + "description": "Optional Version Name" + }, + { + "name": "external_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Optionally filter runs by items with this external ID", + "examples": [ + "slide_001", + "patient_12345_sample_A" + ], + "title": "External Id" + }, + "description": "Optionally filter runs by items with this external ID" + }, + { + "name": "custom_metadata", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ], + "description": "Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata.\n#### URL Encoding Required\n**Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding.\n\n#### Examples (Clear Format):\n- **Field existence**: `$.study` - Runs that have a study field defined\n- **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value\n- **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75\n- **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\"\n- **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements\n\n#### Examples (URL-Encoded Format):\n- **Field existence**: `%24.study`\n- **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)`\n- **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)`\n- **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)`\n- **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)`\n\n#### Notes\n- JSONPath expressions are evaluated using PostgreSQL's `@?` operator\n- The `$.` prefix is automatically added to root-level field references if missing\n- String values in conditions must be enclosed in double quotes\n- Use `&&` for AND operations and `||` for OR operations\n- Regular expressions use `like_regex` with standard regex syntax\n- **Please remember to URL-encode the entire JSONPath expression when making HTTP requests**\n\n ", + "title": "Custom Metadata" + }, + "description": "Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata.\n#### URL Encoding Required\n**Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding.\n\n#### Examples (Clear Format):\n- **Field existence**: `$.study` - Runs that have a study field defined\n- **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value\n- **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75\n- **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\"\n- **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements\n\n#### Examples (URL-Encoded Format):\n- **Field existence**: `%24.study`\n- **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)`\n- **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)`\n- **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)`\n- **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)`\n\n#### Notes\n- JSONPath expressions are evaluated using PostgreSQL's `@?` operator\n- The `$.` prefix is automatically added to root-level field references if missing\n- String values in conditions must be enclosed in double quotes\n- Use `&&` for AND operations and `||` for OR operations\n- Regular expressions use `like_regex` with standard regex syntax\n- **Please remember to URL-encode the entire JSONPath expression when making HTTP requests**\n\n ", + "examples": { + "no_filter": { + "summary": "No filter (returns all)", + "description": "Returns all items without filtering by custom metadata", + "value": "$" + }, + "field_exists": { + "summary": "Check if field exists", + "description": "Find applications that have a project field defined", + "value": "$.study" + }, + "field_has_value": { + "summary": "Check if field has a certain value", + "description": "Compare a field value against a certain value", + "value": "$.study ? (@ == \"abc-1\")" + }, + "numeric_comparisons": { + "summary": "Compare to a numeric value of a field", + "description": "Compare a field value against a numeric value of a field", + "value": "$.confidence_score ? (@ > 0.75)" + }, + "array_operations": { + "summary": "Check if an array contains a certain value", + "description": "Check if an array contains a certain value", + "value": "$.tags[*] ? (@ == \"draft\")" + }, + "complex_filters": { + "summary": "Combine multiple checks", + "description": "Combine multiple checks", + "value": "$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)" + } + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Page" + } + }, + { + "name": "page_size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 5, + "default": 50, + "title": "Page Size" + } + }, + { + "name": "for_organization", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.", + "title": "For Organization" + }, + "description": "Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs." + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n", + "title": "Sort" + }, + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RunReadResponse" + }, + "title": "Response List Runs V1 Runs Get" + } + } + } + }, + "404": { + "description": "Run not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "Public" + ], + "summary": "Initiate Run", + "description": "This endpoint initiates a processing run for a selected application and version, and returns a `run_id` for tracking purposes.\n\nSlide processing occurs asynchronously, allowing you to retrieve results for individual slides as soon as they\ncomplete processing. The system typically processes slides in batches.\nBelow is an example of the required payload for initiating an Atlas H&E TME processing run.\n\n\n### Payload\n\nThe payload includes `application_id`, optional `version_number`, and `items` base fields.\n\n`application_id` is the unique identifier for the application.\n`version_number` is the semantic version to use. If not provided, the latest available version will be used.\n\n`items` includes the list of the items to process (slides, in case of HETA application).\nEvery item has a set of standard fields defined by the API, plus the custom_metadata, specific to the\nchosen application.\n\nExample payload structure with the comments:\n```\n{\n application_id: \"he-tme\",\n version_number: \"1.0.0-beta\",\n items: [{\n \"external_id\": \"slide_1\",\n \"custom_metadata\": {\"project\": \"sample-study\"},\n \"input_artifacts\": [{\n \"name\": \"user_slide\",\n \"download_url\": \"https://...\",\n \"metadata\": {\n \"specimen\": {\n \"disease\": \"LUNG_CANCER\",\n \"tissue\": \"LUNG\"\n },\n \"staining_method\": \"H&E\",\n \"width_px\": 136223,\n \"height_px\": 87761,\n \"resolution_mpp\": 0.2628238,\n \"media-type\":\"image/tiff\",\n \"checksum_base64_crc32c\": \"64RKKA==\"\n }\n }]\n }]\n}\n```\n\n| Parameter | Description |\n| :---- | :---- |\n| `application_id` required | Unique ID for the application |\n| `version_number` optional | Semantic version of the application. If not provided, the latest available version will be used |\n| `items` required | List of submitted items i.e. whole slide images (WSIs) with parameters described below. |\n| `external_id` required | Unique WSI name or ID for easy reference to items, provided by the caller. The `external_id` should be unique across all items of the run. |\n| `input_artifacts` required | List of provided artifacts for a WSI; at the moment Atlas H&E-TME receives only 1 artifact per slide (the slide itself), but for some other applications this can be a slide and a segmentation map |\n| `name` required | Type of artifact; Atlas H&E-TME supports only `\"input_slide\"` |\n| `download_url` required | Signed URL to the input file in the S3 or GCS; Should be valid for at least 6 days |\n| `specimen: disease` required | Supported cancer types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) |\n| `specimen: tissue` required | Supported tissue types for Atlas H&E-TME (see full list in Atlas H&E-TME manual) |\n| `staining_method` required | WSI stain bio-marker; Atlas H&E-TME supports only `\"H&E\"` |\n| `width_px` required | Integer value. Number of pixels of the WSI in the X dimension. |\n| `height_px` required | Integer value. Number of pixels of the WSI in the Y dimension. |\n| `resolution_mpp` required | Resolution of WSI in micrometers per pixel; check allowed range in Atlas H&E-TME manual |\n| `media-type` required | Supported media formats; available values are: image/tiff (for .tiff or .tif WSI), application/dicom (for DICOM ), application/zip (for zipped DICOM), and application/octet-stream (for .svs WSI) |\n| `checksum_base64_crc32c` required | Base64-encoded big-endian CRC32C checksum of the WSI image |\n\n\n\n### Response\n\nThe endpoint returns the run UUID. After that, the job is scheduled for the execution in the background.\n\nTo check the status of the run, call `GET v1/runs/{run_id}` endpoint with the returned run UUID.\n\n### Rejection\n\nApart from the authentication, authorization, and malformed input error, the request can be\nrejected when specific quota limit is exceeded. More details on quotas is described in the\ndocumentation", + "operationId": "create_run_v1_runs_post", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunCreationRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunCreationResponse" + } + } + } + }, + "404": { + "description": "Application version not found" + }, + "403": { + "description": "Forbidden - You don't have permission to create this run" + }, + "400": { + "description": "Bad Request - Input validation failed" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get run details", + "description": "This endpoint allows the caller to retrieve the current status of a run along with other relevant run details.\n A run becomes available immediately after it is created through the `POST /v1/runs/` endpoint.\n\n To download the output results, use `GET /v1/runs/{run_id}/` items to get outputs for all slides.\nAccess to a run is restricted to the user who created it.", + "operationId": "get_run_v1_runs__run_id__get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /v1/runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /v1/runs/` endpoint" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RunReadResponse" + } + } + } + }, + "404": { + "description": "Run not found because it was deleted." + }, + "403": { + "description": "Forbidden - You don't have permission to see this run" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/cancel": { + "post": { + "tags": [ + "Public" + ], + "summary": "Cancel Run", + "description": "The run can be canceled by the user who created the run.\n\nThe execution can be canceled any time while the run is not in the terminated state. The\npending items of a canceled run will not be processed and will not add to the cost.\n\nWhen the run is canceled, the already completed items remain available for download.", + "operationId": "cancel_run_v1_runs__run_id__cancel_post", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /runs/` endpoint" + } + ], + "responses": { + "202": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "description": "Run not found" + }, + "403": { + "description": "Forbidden - You don't have permission to cancel this run" + }, + "409": { + "description": "Conflict - The Run is already cancelled" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/items": { + "get": { + "tags": [ + "Public" + ], + "summary": "List Run Items", + "description": "List items in a run with filtering, sorting, and pagination capabilities.\n\nReturns paginated items within a specific run. Results can be filtered\nby `item_id`, `external_ids`, `custom_metadata`, `terminated_at`, and `termination_reason` using JSONPath expressions.\n\n## JSONPath Metadata Filtering\nUse PostgreSQL JSONPath expressions to filter items using their custom_metadata.\n\n### Examples:\n- **Field existence**: `$.case_id` - Results that have a case_id field defined\n- **Exact value match**: `$.priority ? (@ == \"high\")` - Results with high priority\n- **Numeric comparison**: `$.confidence_score ? (@ > 0.95)` - Results with high confidence\n- **Array operations**: `$.flags[*] ? (@ == \"reviewed\")` - Results flagged as reviewed\n- **Complex conditions**: `$.metrics ? (@.accuracy > 0.9 && @.recall > 0.8)` - Results meeting performance thresholds\n\n## Notes\n- JSONPath expressions are evaluated using PostgreSQL's `@?` operator\n- The `$.` prefix is automatically added to root-level field references if missing\n- String values in conditions must be enclosed in double quotes\n- Use `&&` for AND operations and `||` for OR operations", + "operationId": "list_run_items_v1_runs__run_id__items_get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /v1/runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /v1/runs/` endpoint" + }, + { + "name": "item_id__in", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + { + "type": "null" + } + ], + "description": "Filter for item ids", + "title": "Item Id In" + }, + "description": "Filter for item ids" + }, + { + "name": "external_id__in", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Filter for items by their external_id from the input payload", + "title": "External Id In" + }, + "description": "Filter for items by their external_id from the input payload" + }, + { + "name": "state", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ItemState" + }, + { + "type": "null" + } + ], + "description": "Filter items by their state", + "title": "State" + }, + "description": "Filter items by their state" + }, + { + "name": "termination_reason", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ItemTerminationReason" + }, + { + "type": "null" + } + ], + "description": "Filter items by their termination reason. Only applies to TERMINATED items.", + "title": "Termination Reason" + }, + "description": "Filter items by their termination reason. Only applies to TERMINATED items." + }, + { + "name": "custom_metadata", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "maxLength": 1000 + }, + { + "type": "null" + } + ], + "description": "JSONPath expression to filter items by their custom_metadata", + "title": "Custom Metadata" + }, + "description": "JSONPath expression to filter items by their custom_metadata", + "examples": { + "no_filter": { + "summary": "No filter (returns all)", + "description": "Returns all items without filtering by custom metadata", + "value": "$" + }, + "field_exists": { + "summary": "Check if field exists", + "description": "Find items that have a project field defined", + "value": "$.project" + }, + "field_has_value": { + "summary": "Check if field has a certain value", + "description": "Compare a field value against a certain value", + "value": "$.project ? (@ == \"cancer-research\")" + }, + "numeric_comparisons": { + "summary": "Compare to a numeric value of a field", + "description": "Compare a field value against a numeric value of a field", + "value": "$.duration_hours ? (@ < 2)" + }, + "array_operations": { + "summary": "Check if an array contains a certain value", + "description": "Check if an array contains a certain value", + "value": "$.tags[*] ? (@ == \"production\")" + }, + "complex_filters": { + "summary": "Combine multiple checks", + "description": "Combine multiple checks", + "value": "$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)" + } + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "default": 1, + "title": "Page" + } + }, + { + "name": "page_size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 5, + "default": 50, + "title": "Page Size" + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "Sort the items by one or more fields. Use `+` for ascending and `-` for descending order.\n **Available fields:**\n- `item_id`\n- `external_id`\n- `custom_metadata`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=item_id` - Sort by id of the item (ascending)\n- `?sort=-external_id` - Sort by external ID (descending)\n- `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending)", + "title": "Sort" + }, + "description": "Sort the items by one or more fields. Use `+` for ascending and `-` for descending order.\n **Available fields:**\n- `item_id`\n- `external_id`\n- `custom_metadata`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=item_id` - Sort by id of the item (ascending)\n- `?sort=-external_id` - Sort by external ID (descending)\n- `?sort=custom_metadata&sort=-external_id` - Sort by metadata, then by external ID (descending)" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ItemResultReadResponse" + }, + "title": "Response List Run Items V1 Runs Run Id Items Get" + } + } + } + }, + "404": { + "description": "Run not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/items/{external_id}": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get Item By Run", + "description": "Retrieve details of a specific item (slide) by its external ID and the run ID.", + "operationId": "get_item_by_run_v1_runs__run_id__items__external_id__get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "The run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "The run id, returned by `POST /runs/` endpoint" + }, + { + "name": "external_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The `external_id` that was defined for the item by the customer that triggered the run.", + "title": "External Id" + }, + "description": "The `external_id` that was defined for the item by the customer that triggered the run." + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ItemResultReadResponse" + } + } + } + }, + "404": { + "description": "Not Found - Item with given ID does not exist" + }, + "403": { + "description": "Forbidden - You don't have permission to see this item" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/artifacts/{artifact_id}/file": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get Artifact Url", + "description": "Download the artifact file with the specified artifact_id, belonging to the specified run.\nThe artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also\nbe retrieved via `GET /v1/runs/{run_id}/items/{external_id}`.\n\nThe endpoint may return a redirect response with a presigned URL to download the artifact file from the storage\nbucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response.", + "operationId": "get_artifact_url_v1_runs__run_id__artifacts__artifact_id__file_get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /runs/` endpoint" + }, + { + "name": "artifact_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "The artifact id to download", + "title": "Artifact Id" + }, + "description": "The artifact id to download" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "description": "Not Found - Artifact not found for the specified run" + }, + "307": { + "description": "Temporary Redirect - Redirect to the artifact file URL" + }, + "403": { + "description": "Forbidden - You don't have permission to download this artifact" + }, + "410": { + "description": "Gone - Artifact has been deleted" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/artifacts": { + "delete": { + "tags": [ + "Public" + ], + "summary": "Delete Run Items", + "description": "This endpoint allows the caller to explicitly delete artifacts generated by a run.\nIt can only be invoked when the run has reached a final state, i.e.\n`PROCESSED`, `CANCELED_SYSTEM`, or `CANCELED_USER`.\nNote that by default, all artifacts are automatically deleted 30 days after the run finishes,\nregardless of whether the caller explicitly requests such deletion.", + "operationId": "delete_run_items_v1_runs__run_id__artifacts_delete", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /runs/` endpoint" + } + ], + "responses": { + "200": { + "description": "Run artifacts deleted", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "description": "Run not found" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/custom-metadata": { + "put": { + "tags": [ + "Public" + ], + "summary": "Put Run Custom Metadata", + "description": "Update the custom metadata of a run with the specified `run_id`.\n\nOptionally, a checksum may be provided along the custom metadata JSON.\nIt can be used to verify if the custom metadata was updated since the last time it was accessed.\nIf the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current\ncustom metadata value to be overwritten is acknowledged by the user.\nIf no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks.\n\nThe latest custom metadata and checksum can be retrieved for the run via the `GET /v1/runs/{run_id}` endpoint.\n\n**Note on deadlines:** Run deadlines must be set during run creation and cannot be modified afterward.\nAny deadline changes in custom metadata will be ignored by the system.", + "operationId": "put_run_custom_metadata_v1_runs__run_id__custom_metadata_put", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /runs/` endpoint" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomMetadataUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Custom metadata successfully updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomMetadataUpdateResponse" + } + } + } + }, + "404": { + "description": "Run not found" + }, + "403": { + "description": "Forbidden - You don't have permission to update this run" + }, + "412": { + "description": "Precondition Failed - Checksum mismatch, resource has been modified" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/runs/{run_id}/items/{external_id}/custom-metadata": { + "put": { + "tags": [ + "Public" + ], + "summary": "Put Item Custom Metadata By Run", + "description": "Update the custom metadata of the item with the specified `external_id`, belonging to the specified run.\n\nOptionally, a checksum may be provided along the custom metadata JSON.\nIt can be used to verify if the custom metadata was updated since the last time it was accessed.\nIf the checksum is provided, it must match the existing custom metadata in the system, ensuring that the current\ncustom metadata value to be overwritten is acknowledged by the user.\nIf no checksum is provided, submitted metadata directly overwrites the existing metadata, without any checks.\n\nThe latest custom metadata and checksum can be retrieved\n for individual items via `GET /v1/runs/{run_id}/items/{external_id}`,\n and for all items of a run via `GET /v1/runs/{run_id}/items`.", + "operationId": "put_item_custom_metadata_by_run_v1_runs__run_id__items__external_id__custom_metadata_put", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "The run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "The run id, returned by `POST /runs/` endpoint" + }, + { + "name": "external_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "The `external_id` that was defined for the item by the customer that triggered the run.", + "title": "External Id" + }, + "description": "The `external_id` that was defined for the item by the customer that triggered the run." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomMetadataUpdateRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Custom metadata successfully updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomMetadataUpdateResponse" + } + } + } + }, + "403": { + "description": "Forbidden - You don't have permission to update this item" + }, + "404": { + "description": "Item not found" + }, + "412": { + "description": "Precondition Failed - Checksum mismatch" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/v1/me": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get current user", + "description": "Retrieves your identity details, including name, email, and organization.\nThis is useful for verifying that the request is being made under the correct user profile\nand organization context, as well as confirming that the expected environment variables are correctly set\n(in case you are using Python SDK)", + "operationId": "get_me_v1_me_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MeReadResponse" + } + } + } + } + }, + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ] + } + } + }, + "components": { + "schemas": { + "ApplicationReadResponse": { + "properties": { + "application_id": { + "type": "string", + "title": "Application Id", + "description": "Application ID", + "examples": [ + "he-tme" + ] + }, + "name": { + "type": "string", + "title": "Name", + "description": "Application display name", + "examples": [ + "Atlas H&E-TME" + ] + }, + "regulatory_classes": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Regulatory Classes", + "description": "Regulatory classes, to which the applications comply with. Possible values include: RUO, IVDR, FDA.", + "examples": [ + [ + "RUO" + ] + ] + }, + "description": { + "type": "string", + "title": "Description", + "description": "Describing what the application can do ", + "examples": [ + "The Atlas H&E TME is an AI application designed to examine FFPE (formalin-fixed, paraffin-embedded) tissues stained with H&E (hematoxylin and eosin), delivering comprehensive insights into the tumor microenvironment." + ] + }, + "versions": { + "items": { + "$ref": "#/components/schemas/ApplicationVersion" + }, + "type": "array", + "title": "Versions", + "description": "All version numbers available to the user" + } + }, + "type": "object", + "required": [ + "application_id", + "name", + "regulatory_classes", + "description", + "versions" + ], + "title": "ApplicationReadResponse", + "description": "Response schema for `List available applications` and `Read Application by Id` endpoints" + }, + "ApplicationReadShortResponse": { + "properties": { + "application_id": { + "type": "string", + "title": "Application Id", + "description": "Application ID", + "examples": [ + "he-tme" + ] + }, + "name": { + "type": "string", + "title": "Name", + "description": "Application display name", + "examples": [ + "Atlas H&E-TME" + ] + }, + "regulatory_classes": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Regulatory Classes", + "description": "Regulatory classes, to which the applications comply with. Possible values include: RUO, IVDR, FDA.", + "examples": [ + [ + "RUO" + ] + ] + }, + "description": { + "type": "string", + "title": "Description", + "description": "Describing what the application can do ", + "examples": [ + "The Atlas H&E TME is an AI application designed to examine FFPE (formalin-fixed, paraffin-embedded) tissues stained with H&E (hematoxylin and eosin), delivering comprehensive insights into the tumor microenvironment." + ] + }, + "latest_version": { + "anyOf": [ + { + "$ref": "#/components/schemas/ApplicationVersion" + }, + { + "type": "null" + } + ], + "description": "The version with highest version number available to the user" + } + }, + "type": "object", + "required": [ + "application_id", + "name", + "regulatory_classes", + "description" + ], + "title": "ApplicationReadShortResponse", + "description": "Response schema for `List available applications` and `Read Application by Id` endpoints" + }, + "ApplicationVersion": { + "properties": { + "number": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Number", + "description": "The number of the latest version", + "examples": [ + "1.0.0" + ] + }, + "released_at": { + "type": "string", + "format": "date-time", + "title": "Released At", + "description": "The timestamp for when the application version was made available in the Platform", + "examples": [ + "2025-09-15T10:30:45.123Z" + ] + } + }, + "type": "object", + "required": [ + "number", + "released_at" + ], + "title": "ApplicationVersion" + }, + "ArtifactOutput": { + "type": "string", + "enum": [ + "NONE", + "AVAILABLE", + "DELETED_BY_USER", + "DELETED_BY_SYSTEM" + ], + "title": "ArtifactOutput" + }, + "ArtifactState": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "TERMINATED" + ], + "title": "ArtifactState" + }, + "ArtifactTerminationReason": { + "type": "string", + "enum": [ + "SUCCEEDED", + "USER_ERROR", + "SYSTEM_ERROR", + "SKIPPED" + ], + "title": "ArtifactTerminationReason" + }, + "CustomMetadataUpdateRequest": { + "properties": { + "custom_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata", + "description": "JSON metadata that should be set for the run", + "examples": [ + { + "department": "D1", + "study": "abc-1" + } + ] + }, + "custom_metadata_checksum": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata Checksum", + "description": "Optional field to verify that the latest custom metadata was known. If set to the checksum retrieved via the /runs endpoint, it must match the checksum of the current value in the database.", + "examples": [ + "f54fe109" + ] + } + }, + "type": "object", + "title": "CustomMetadataUpdateRequest" + }, + "CustomMetadataUpdateResponse": { + "properties": { + "custom_metadata_checksum": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata Checksum", + "description": "The checksum of the updated custom metadata. If the `custom_metadata` is None,\nthe checksum also None.", + "readOnly": true + } + }, + "type": "object", + "required": [ + "custom_metadata_checksum" + ], + "title": "CustomMetadataUpdateResponse" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "InputArtifact": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "mime_type": { + "type": "string", + "pattern": "^\\w+\\/\\w+[-+.|\\w+]+\\w+$", + "title": "Mime Type", + "examples": [ + "image/tiff" + ] + }, + "metadata_schema": { + "additionalProperties": true, + "type": "object", + "title": "Metadata Schema" + } + }, + "type": "object", + "required": [ + "name", + "mime_type", + "metadata_schema" + ], + "title": "InputArtifact" + }, + "InputArtifactCreationRequest": { + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "Type of artifact. For Atlas H&E-TME, use \"input_slide\"", + "examples": [ + "input_slide" + ] + }, + "download_url": { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri", + "title": "Download Url", + "description": "[Signed URL](https://cloud.google.com/cdn/docs/using-signed-urls) to the input artifact file. The URL should be valid for at least 6 days from the payload submission time.", + "examples": [ + "https://example.com/case-no-1-slide.tiff" + ] + }, + "metadata": { + "additionalProperties": true, + "type": "object", + "title": "Metadata", + "description": "The metadata of the artifact, required by the application version. The JSON schema of the metadata can be requested by `/v1/versions/{application_version_id}`. The schema is located in `input_artifacts.[].metadata_schema`", + "examples": [ + { + "checksum_base64_crc32c": "752f9554", + "height": 2000, + "height_mpp": 0.5, + "width": 10000, + "width_mpp": 0.5 + } + ] + } + }, + "type": "object", + "required": [ + "name", + "download_url", + "metadata" + ], + "title": "InputArtifactCreationRequest", + "description": "Input artifact containing the slide image and associated metadata." + }, + "ItemCreationRequest": { + "properties": { + "external_id": { + "type": "string", + "maxLength": 255, + "title": "External Id", + "description": "Unique identifier for this item within the run. Used for referencing items. Must be unique across all items in the same run", + "examples": [ + "slide_1", + "patient_001_slide_A", + "sample_12345" + ] + }, + "custom_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata", + "description": "Optional JSON custom_metadata to store additional information alongside an item.", + "examples": [ + { + "case": "abc" + } + ] + }, + "input_artifacts": { + "items": { + "$ref": "#/components/schemas/InputArtifactCreationRequest" + }, + "type": "array", + "title": "Input Artifacts", + "description": "List of input artifacts for this item. For Atlas H&E-TME, typically contains one artifact (the slide image)", + "examples": [ + [ + { + "download_url": "https://example-bucket.s3.amazonaws.com/slide1.tiff", + "metadata": { + "checksum_base64_crc32c": "64RKKA==", + "height_px": 87761, + "media-type": "image/tiff", + "resolution_mpp": 0.2628238, + "specimen": { + "disease": "LUNG_CANCER", + "tissue": "LUNG" + }, + "staining_method": "H&E", + "width_px": 136223 + }, + "name": "input_slide" + } + ] + ] + } + }, + "type": "object", + "required": [ + "external_id", + "input_artifacts" + ], + "title": "ItemCreationRequest", + "description": "Individual item (slide) to be processed in a run." + }, + "ItemOutput": { + "type": "string", + "enum": [ + "NONE", + "FULL" + ], + "title": "ItemOutput" + }, + "ItemResultReadResponse": { + "properties": { + "item_id": { + "type": "string", + "format": "uuid", + "title": "Item Id", + "description": "Item UUID generated by the Platform" + }, + "external_id": { + "type": "string", + "title": "External Id", + "description": "The external_id of the item from the user payload", + "examples": [ + "slide_1" + ] + }, + "custom_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata", + "description": "The custom_metadata of the item that has been provided by the user on run creation." + }, + "custom_metadata_checksum": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata Checksum", + "description": "The checksum of the `custom_metadata` field.\nCan be used in the `PUT /runs/{run-id}/items/{external_id}/custom_metadata`\nrequest to avoid unwanted override of the values in concurrent requests.", + "examples": [ + "f54fe109" + ] + }, + "queue_position_org": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Queue Position Org", + "description": "The position of the item in the organization's queue." + }, + "queue_position_platform": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Queue Position Platform", + "description": "The position of the item in the platform's queue." + }, + "state": { + "$ref": "#/components/schemas/ItemState", + "description": "\nThe item moves from `PENDING` to `PROCESSING` to `TERMINATED` state.\nWhen terminated, consult the `termination_reason` property to see whether it was successful.\n " + }, + "output": { + "$ref": "#/components/schemas/ItemOutput", + "description": "The output status of the item (NONE, FULL)" + }, + "termination_reason": { + "anyOf": [ + { + "$ref": "#/components/schemas/ItemTerminationReason" + }, + { + "type": "null" + } + ], + "description": "\nWhen the `state` is `TERMINATED` this will explain why\n`SUCCEEDED` -> Successful processing.\n`USER_ERROR` -> Failed because the provided input was invalid.\n`SYSTEM_ERROR` -> There was an error in the model or platform.\n`SKIPPED` -> Was cancelled\n" + }, + "error_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Code" + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Message", + "description": "\n The error message in case the `termination_reason` is in `USER_ERROR` or `SYSTEM_ERROR`\n ", + "examples": [ + "This item was not processed because the threshold of 3 items finishing in error state (user or system error) was reached before the item was processed.", + "The item was not processed because the run was cancelled by the user before the item was processed.", + "User error raised by Application because the input data provided by the user cannot be processed:\nThe image width is 123000 px, but the maximum width is 100000 px", + "A system error occurred during the item execution:\n System went out of memory in cell classification", + "An unknown system error occurred during the item execution" + ] + }, + "terminated_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Terminated At", + "description": "Timestamp showing when the item reached a terminal state.", + "examples": [ + "2024-01-15T10:30:45.123Z" + ] + }, + "output_artifacts": { + "items": { + "$ref": "#/components/schemas/OutputArtifactResultReadResponse" + }, + "type": "array", + "title": "Output Artifacts", + "description": "\nThe list of the results generated by the application algorithm. The number of files and their\ntypes depend on the particular application version, call `/v1/versions/{version_id}` to get\nthe details.\n " + } + }, + "type": "object", + "required": [ + "item_id", + "external_id", + "custom_metadata", + "state", + "output", + "output_artifacts" + ], + "title": "ItemResultReadResponse", + "description": "Response schema for items in `List Run Items` endpoint" + }, + "ItemState": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "TERMINATED" + ], + "title": "ItemState" + }, + "ItemTerminationReason": { + "type": "string", + "enum": [ + "SUCCEEDED", + "USER_ERROR", + "SYSTEM_ERROR", + "SKIPPED" + ], + "title": "ItemTerminationReason" + }, + "MeReadResponse": { + "properties": { + "user": { + "$ref": "#/components/schemas/UserReadResponse" + }, + "organization": { + "$ref": "#/components/schemas/OrganizationReadResponse" + } + }, + "type": "object", + "required": [ + "user", + "organization" + ], + "title": "MeReadResponse", + "description": "Response schema for `Get current user` endpoint" + }, + "OrganizationReadResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique organization identifier", + "examples": [ + "org_123456" + ] + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name", + "description": "Organization name (E.g. “aignx”)", + "examples": [ + "aignx" + ] + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name", + "description": "Public organization name (E.g. “Aignostics GmbH”)", + "examples": [ + "Aignostics GmbH" + ] + }, + "aignostics_bucket_hmac_access_key_id": { + "type": "string", + "title": "Aignostics Bucket Hmac Access Key Id", + "description": "HMAC access key ID for the Aignostics-provided storage bucket. Used to authenticate requests for uploading files and generating signed URLs", + "examples": [ + "YOUR_HMAC_ACCESS_KEY_ID" + ] + }, + "aignostics_bucket_hmac_secret_access_key": { + "type": "string", + "title": "Aignostics Bucket Hmac Secret Access Key", + "description": "HMAC secret access key paired with the access key ID. Keep this credential secure.", + "examples": [ + "YOUR/HMAC/SECRET_ACCESS_KEY" + ] + }, + "aignostics_bucket_name": { + "type": "string", + "title": "Aignostics Bucket Name", + "description": "Name of the bucket provided by Aignostics for storing input artifacts (slide images)", + "examples": [ + "aignostics-platform-bucket" + ] + }, + "aignostics_bucket_protocol": { + "type": "string", + "title": "Aignostics Bucket Protocol", + "description": "Protocol to use for bucket access. Defines the URL scheme for connecting to the storage service", + "examples": [ + "gs" + ] + }, + "aignostics_logfire_token": { + "type": "string", + "title": "Aignostics Logfire Token", + "description": "Authentication token for Logfire observability service. Enables sending application logs and performance metrics to Aignostics for monitoring and support", + "examples": [ + "your-logfire-token" + ] + }, + "aignostics_sentry_dsn": { + "type": "string", + "title": "Aignostics Sentry Dsn", + "description": "Data Source Name (DSN) for Sentry error tracking service. Allows automatic reporting of errors and exceptions to Aignostics support team", + "examples": [ + "https://2354s3#ewsha@o44.ingest.us.sentry.io/34345123432" + ] + } + }, + "type": "object", + "required": [ + "id", + "aignostics_bucket_hmac_access_key_id", + "aignostics_bucket_hmac_secret_access_key", + "aignostics_bucket_name", + "aignostics_bucket_protocol", + "aignostics_logfire_token", + "aignostics_sentry_dsn" + ], + "title": "OrganizationReadResponse", + "description": "Part of response schema for Organization object in `Get current user` endpoint.\nThis model corresponds to the response schema returned from\nAuth0 GET /v2/organizations/{id} endpoint, flattens out the metadata out\nand doesn't return branding or token_quota objects.\nFor details, see:\nhttps://auth0.com/docs/api/management/v2/organizations/get-organizations-by-id\n\n#### Configuration for integrating with Aignostics Platform services.\n\nThe Aignostics Platform API requires signed URLs for input artifacts (slide images). To simplify this process,\nAignostics provides a dedicated storage bucket. The HMAC credentials below grant read and write\naccess to this bucket, allowing you to upload files and generate the signed URLs needed for API calls.\n\nAdditionally, logging and error reporting tokens enable Aignostics to provide better support and monitor\nsystem performance for your integration." + }, + "OutputArtifact": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "mime_type": { + "type": "string", + "pattern": "^\\w+\\/\\w+[-+.|\\w+]+\\w+$", + "title": "Mime Type", + "examples": [ + "application/vnd.apache.parquet" + ] + }, + "metadata_schema": { + "additionalProperties": true, + "type": "object", + "title": "Metadata Schema" + }, + "scope": { + "$ref": "#/components/schemas/OutputArtifactScope" + }, + "visibility": { + "$ref": "#/components/schemas/OutputArtifactVisibility" + } + }, + "type": "object", + "required": [ + "name", + "mime_type", + "metadata_schema", + "scope", + "visibility" + ], + "title": "OutputArtifact" + }, + "OutputArtifactResultReadResponse": { + "properties": { + "output_artifact_id": { + "type": "string", + "format": "uuid", + "title": "Output Artifact Id", + "description": "The Id of the artifact. Used internally" + }, + "name": { + "type": "string", + "title": "Name", + "description": "\nName of the output from the output schema from the `/v1/versions/{version_id}` endpoint.\n ", + "examples": [ + "tissue_qc:tiff_heatmap" + ] + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Metadata", + "description": "The metadata of the output artifact, provided by the application. Can only be None if the artifact itself was deleted." + }, + "state": { + "$ref": "#/components/schemas/ArtifactState", + "description": "The current state of the artifact (PENDING, PROCESSING, TERMINATED)" + }, + "termination_reason": { + "anyOf": [ + { + "$ref": "#/components/schemas/ArtifactTerminationReason" + }, + { + "type": "null" + } + ], + "description": "The reason for termination when state is TERMINATED" + }, + "output": { + "$ref": "#/components/schemas/ArtifactOutput", + "description": "The output status of the artifact (NONE, FULL)" + }, + "error_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Code" + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Message" + }, + "download_url": { + "anyOf": [ + { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" + }, + { + "type": "null" + } + ], + "title": "Download Url", + "description": "\nThe download URL to the output file. The URL is valid for 1 hour after the endpoint is called.\nA new URL is generated every time the endpoint is called.\n ", + "deprecated": true + } + }, + "type": "object", + "required": [ + "output_artifact_id", + "name", + "state", + "output" + ], + "title": "OutputArtifactResultReadResponse" + }, + "OutputArtifactScope": { + "type": "string", + "enum": [ + "ITEM", + "GLOBAL" + ], + "title": "OutputArtifactScope" + }, + "OutputArtifactVisibility": { + "type": "string", + "enum": [ + "INTERNAL", + "EXTERNAL" + ], + "title": "OutputArtifactVisibility" + }, + "RunCreationRequest": { + "properties": { + "application_id": { + "type": "string", + "title": "Application Id", + "description": "Unique ID for the application to use for processing", + "examples": [ + "he-tme" + ] + }, + "version_number": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version Number", + "description": "Semantic version of the application to use for processing. If not provided, the latest available version will be used", + "examples": [ + "1.0.0-beta1" + ] + }, + "custom_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata", + "description": "Optional JSON metadata to store additional information alongside the run", + "examples": [ + { + "department": "D1", + "study": "abc-1" + } + ] + }, + "scheduling": { + "anyOf": [ + { + "$ref": "#/components/schemas/SchedulingRequest" + }, + { + "type": "null" + } + ], + "description": "Optional scheduling constraints for this run.", + "examples": [ + { + "deadline": "2026-03-05T23:59:59Z", + "due_date": "2026-03-04T23:59:59Z" + } + ] + }, + "items": { + "items": { + "$ref": "#/components/schemas/ItemCreationRequest" + }, + "type": "array", + "minItems": 1, + "title": "Items", + "description": "List of items (slides) to process. Each item represents a whole slide image (WSI) with its associated metadata and artifacts", + "examples": [ + [ + { + "external_id": "slide_1", + "input_artifacts": [ + { + "download_url": "https://example-bucket.s3.amazonaws.com/slide1.tiff?signature=...", + "metadata": { + "checksum_base64_crc32c": "64RKKA==", + "height_px": 87761, + "media-type": "image/tiff", + "resolution_mpp": 0.2628238, + "specimen": { + "disease": "LUNG_CANCER", + "tissue": "LUNG" + }, + "staining_method": "H&E", + "width_px": 136223 + }, + "name": "input_slide" + } + ] + } + ] + ] + } + }, + "type": "object", + "required": [ + "application_id", + "items" + ], + "title": "RunCreationRequest", + "description": "Request schema for `Initiate Run` endpoint.\nIt describes which application version is chosen, and which user data should be processed." + }, + "RunCreationResponse": { + "properties": { + "run_id": { + "type": "string", + "format": "uuid", + "title": "Run Id", + "examples": [ + "3fa85f64-5717-4562-b3fc-2c963f66afa6" + ] + } + }, + "type": "object", + "required": [ + "run_id" + ], + "title": "RunCreationResponse" + }, + "RunItemStatistics": { + "properties": { + "item_count": { + "type": "integer", + "title": "Item Count", + "description": "Total number of the items in the run" + }, + "item_pending_count": { + "type": "integer", + "title": "Item Pending Count", + "description": "The number of items in `PENDING` state" + }, + "item_processing_count": { + "type": "integer", + "title": "Item Processing Count", + "description": "The number of items in `PROCESSING` state" + }, + "item_user_error_count": { + "type": "integer", + "title": "Item User Error Count", + "description": "The number of items in `TERMINATED` state, and the item termination reason is `USER_ERROR`" + }, + "item_system_error_count": { + "type": "integer", + "title": "Item System Error Count", + "description": "The number of items in `TERMINATED` state, and the item termination reason is `SYSTEM_ERROR`" + }, + "item_skipped_count": { + "type": "integer", + "title": "Item Skipped Count", + "description": "The number of items in `TERMINATED` state, and the item termination reason is `SKIPPED`" + }, + "item_succeeded_count": { + "type": "integer", + "title": "Item Succeeded Count", + "description": "The number of items in `TERMINATED` state, and the item termination reason is `SUCCEEDED`" + } + }, + "type": "object", + "required": [ + "item_count", + "item_pending_count", + "item_processing_count", + "item_user_error_count", + "item_system_error_count", + "item_skipped_count", + "item_succeeded_count" + ], + "title": "RunItemStatistics" + }, + "RunOutput": { + "type": "string", + "enum": [ + "NONE", + "PARTIAL", + "FULL" + ], + "title": "RunOutput" + }, + "RunReadResponse": { + "properties": { + "run_id": { + "type": "string", + "format": "uuid", + "title": "Run Id", + "description": "UUID of the application" + }, + "application_id": { + "type": "string", + "title": "Application Id", + "description": "Application id", + "examples": [ + "he-tme" + ] + }, + "version_number": { + "type": "string", + "title": "Version Number", + "description": "Application version number", + "examples": [ + "0.4.4" + ] + }, + "state": { + "$ref": "#/components/schemas/RunState", + "description": "When the run request is received by the Platform, the `state` of it is set to\n`PENDING`. The state changes to `PROCESSING` when at least one item is being processed. After `PROCESSING`, the\nstate of the run can switch back to `PENDING` if there are no processing items, or to `TERMINATED` when the run\nfinished processing." + }, + "output": { + "$ref": "#/components/schemas/RunOutput", + "description": "The status of the output of the run. When 0 items are successfully processed the output is\n`NONE`, after one item is successfully processed, the value is set to `PARTIAL`. When all items of the run are\nsuccessfully processed, the output is set to `FULL`." + }, + "termination_reason": { + "anyOf": [ + { + "$ref": "#/components/schemas/RunTerminationReason" + }, + { + "type": "null" + } + ], + "description": "The termination reason of the run. When the run is not in `TERMINATED` state, the\n termination_reason is `null`. If all items of of the run are processed (successfully or with an error), then\n termination_reason is set to `ALL_ITEMS_PROCESSED`. If the run is cancelled by the user, the value is set to\n `CANCELED_BY_USER`. If the run reaches the threshold of number of failed items, the Platform cancels the run\n and sets the termination_reason to `CANCELED_BY_SYSTEM`.\n " + }, + "error_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Code", + "description": "When the termination_reason is set to CANCELED_BY_SYSTEM, the error_code is set to define the\n structured description of the error.", + "examples": [ + "SCHEDULER.ITEMS_WITH_ERROR_THRESHOLD_REACHED" + ] + }, + "error_message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Message", + "description": "When the termination_reason is set to CANCELED_BY_SYSTEM, the error_message is set to provide\n more insights to the error cause.", + "examples": [ + "Run canceled given errors on more than 10 items." + ] + }, + "statistics": { + "$ref": "#/components/schemas/RunItemStatistics", + "description": "Aggregated statistics of the run execution" + }, + "custom_metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata", + "description": "Optional JSON metadata that was stored in alongside the run by the user", + "examples": [ + { + "department": "D1", + "study": "abc-1" + } + ] + }, + "custom_metadata_checksum": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Custom Metadata Checksum", + "description": "The checksum of the `custom_metadata` field. Can be used in the `PUT /runs/{run-id}/custom_metadata`\nrequest to avoid unwanted override of the values in concurrent requests.", + "examples": [ + "f54fe109" + ] + }, + "submitted_at": { + "type": "string", + "format": "date-time", + "title": "Submitted At", + "description": "Timestamp showing when the run was triggered" + }, + "submitted_by": { + "type": "string", + "title": "Submitted By", + "description": "Id of the user who triggered the run", + "examples": [ + "auth0|123456" + ] + }, + "terminated_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Terminated At", + "description": "Timestamp showing when the run reached a terminal state.", + "examples": [ + "2024-01-15T10:30:45.123Z" + ] + }, + "num_preceding_items_org": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Num Preceding Items Org", + "description": "How many Items from other Runs in the same Organization are due to begin processing before this Run's next Item does." + }, + "num_preceding_items_platform": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Num Preceding Items Platform", + "description": "How many Items from other Runs are due to begin processing before this Run's next Item does." + }, + "scheduling": { + "anyOf": [ + { + "$ref": "#/components/schemas/SchedulingResponse" + }, + { + "type": "null" + } + ], + "description": "Scheduling constraints set for this run." + } + }, + "type": "object", + "required": [ + "run_id", + "application_id", + "version_number", + "state", + "output", + "termination_reason", + "error_code", + "error_message", + "statistics", + "submitted_at", + "submitted_by" + ], + "title": "RunReadResponse", + "description": "Response schema for `Get run details` endpoint" + }, + "RunState": { + "type": "string", + "enum": [ + "PENDING", + "PROCESSING", + "TERMINATED" + ], + "title": "RunState" + }, + "RunTerminationReason": { + "type": "string", + "enum": [ + "ALL_ITEMS_PROCESSED", + "CANCELED_BY_SYSTEM", + "CANCELED_BY_USER" + ], + "title": "RunTerminationReason" + }, + "SchedulingRequest": { + "properties": { + "due_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Due Date", + "description": "Requested completion time. Items are prioritized to meet this target.", + "examples": [ + "2026-03-04T23:59:59Z" + ] + }, + "deadline": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deadline", + "description": "Hard deadline. The run will be cancelled if not completed by this time.", + "examples": [ + "2026-03-05T23:59:59Z" + ] + } + }, + "type": "object", + "title": "SchedulingRequest", + "description": "Scheduling constraints for a run." + }, + "SchedulingResponse": { + "properties": { + "due_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Due Date" + }, + "deadline": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deadline" + } + }, + "type": "object", + "title": "SchedulingResponse", + "description": "Scheduling fields returned in run responses." + }, + "UserReadResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Unique user identifier", + "examples": [ + "auth0|123456" + ] + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Email", + "description": "User email", + "examples": [ + "user@domain.com" + ] + }, + "email_verified": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Email Verified", + "examples": [ + true + ] + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name", + "description": "First and last name of the user", + "examples": [ + "Jane Doe" + ] + }, + "given_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Given Name", + "examples": [ + "Jane" + ] + }, + "family_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Family Name", + "examples": [ + "Doe" + ] + }, + "nickname": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Nickname", + "examples": [ + "jdoe" + ] + }, + "picture": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Picture", + "examples": [ + "https://example.com/jdoe.jpg" + ] + }, + "updated_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Updated At", + "examples": [ + "2023-10-05T14:48:00.000Z" + ] + } + }, + "type": "object", + "required": [ + "id" + ], + "title": "UserReadResponse", + "description": "Part of response schema for User object in `Get current user` endpoint.\nThis model corresponds to the response schema returned from\nAuth0 GET /v2/users/{id} endpoint.\nFor details, see:\nhttps://auth0.com/docs/api/management/v2/users/get-users-by-id" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + }, + "input": { + "title": "Input" + }, + "ctx": { + "type": "object", + "title": "Context" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "VersionReadResponse": { + "properties": { + "version_number": { + "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + "title": "Version Number", + "description": "Semantic version of the application" + }, + "changelog": { + "type": "string", + "title": "Changelog", + "description": "Description of the changes relative to the previous version" + }, + "input_artifacts": { + "items": { + "$ref": "#/components/schemas/InputArtifact" + }, + "type": "array", + "title": "Input Artifacts", + "description": "List of the input fields, provided by the User" + }, + "output_artifacts": { + "items": { + "$ref": "#/components/schemas/OutputArtifact" + }, + "type": "array", + "title": "Output Artifacts", + "description": "List of the output fields, generated by the application" + }, + "released_at": { + "type": "string", + "format": "date-time", + "title": "Released At", + "description": "The timestamp when the application version was registered" + } + }, + "type": "object", + "required": [ + "version_number", + "changelog", + "input_artifacts", + "output_artifacts", + "released_at" + ], + "title": "VersionReadResponse", + "description": "Base Response schema for the `Application Version Details` endpoint" + } + }, + "securitySchemes": { + "OAuth2AuthorizationCodeBearer": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "scopes": {}, + "authorizationUrl": "https://aignostics-platform-staging.eu.auth0.com/authorize", + "tokenUrl": "https://aignostics-platform-staging.eu.auth0.com/oauth/token" + } + } + } + } + } +} diff --git a/codegen/in/openapi.json b/codegen/in/openapi.json index e3ee044a4..cae9e0200 100644 --- a/codegen/in/openapi.json +++ b/codegen/in/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Aignostics Platform API", "description": "\nThe Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. \n\nTo begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. \n\nMore information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com).\n\n**How to authorize and test API endpoints:**\n\n1. Click the \"Authorize\" button in the right corner below\n3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials\n4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint\n\n**Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized.\n\n", - "version": "1.0.0-ga" + "version": "1.4.0" }, "servers": [ { @@ -17,7 +17,7 @@ "Public" ], "summary": "List available applications", - "description": "Returns the list of the applications, available to the caller.\n\nThe application is available if any of the versions of the application is assigned to the caller’s organization.\nThe response is paginated and sorted according to the provided parameters.", + "description": "Returns the list of the applications, available to the caller.\n\nThe application is available if any of the versions of the application is assigned to the caller's organization.\nThe response is paginated and sorted according to the provided parameters.", "operationId": "list_applications_v1_applications_get", "security": [ { @@ -210,6 +210,7 @@ "required": true, "schema": { "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "title": "Version" } } @@ -582,6 +583,24 @@ "title": "Page Size" } }, + { + "name": "for_organization", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.", + "title": "For Organization" + }, + "description": "Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs." + }, { "name": "sort", "in": "query", @@ -598,10 +617,10 @@ "type": "null" } ], - "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `statistics`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n", + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n", "title": "Sort" }, - "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `statistics`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n" + "description": "Sort the results by one or more fields. Use `+` for ascending and `-` for descending order.\n\n**Available fields:**\n- `run_id`\n- `application_id`\n- `version_number`\n- `custom_metadata`\n- `submitted_at`\n- `submitted_by`\n- `terminated_at`\n- `termination_reason`\n\n**Examples:**\n- `?sort=submitted_at` - Sort by creation time (ascending)\n- `?sort=-submitted_at` - Sort by creation time (descending)\n- `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending)\n" } ], "responses": { @@ -1104,6 +1123,79 @@ } } }, + "/v1/runs/{run_id}/artifacts/{artifact_id}/file": { + "get": { + "tags": [ + "Public" + ], + "summary": "Get Artifact Url", + "description": "Download the artifact file with the specified artifact_id, belonging to the specified run.\nThe artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also\nbe retrieved via `GET /v1/runs/{run_id}/items/{external_id}`.\n\nThe endpoint may return a redirect response with a presigned URL to download the artifact file from the storage\nbucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response.", + "operationId": "get_artifact_url_v1_runs__run_id__artifacts__artifact_id__file_get", + "security": [ + { + "OAuth2AuthorizationCodeBearer": [] + } + ], + "parameters": [ + { + "name": "run_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "Run id, returned by `POST /runs/` endpoint", + "title": "Run Id" + }, + "description": "Run id, returned by `POST /runs/` endpoint" + }, + { + "name": "artifact_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "description": "The artifact id to download", + "title": "Artifact Id" + }, + "description": "The artifact id to download" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "description": "Not Found - Artifact not found for the specified run" + }, + "307": { + "description": "Temporary Redirect - Redirect to the artifact file URL" + }, + "403": { + "description": "Forbidden - You don't have permission to download this artifact" + }, + "410": { + "description": "Gone - Artifact has been deleted" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, "/v1/runs/{run_id}/artifacts": { "delete": { "tags": [ @@ -1461,6 +1553,7 @@ "properties": { "number": { "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "title": "Number", "description": "The number of the latest version", "examples": [ @@ -1518,6 +1611,7 @@ "custom_metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -1602,6 +1696,7 @@ ] }, "metadata_schema": { + "additionalProperties": true, "type": "object", "title": "Metadata Schema" } @@ -1636,6 +1731,7 @@ ] }, "metadata": { + "additionalProperties": true, "type": "object", "title": "Metadata", "description": "The metadata of the artifact, required by the application version. The JSON schema of the metadata can be requested by `/v1/versions/{application_version_id}`. The schema is located in `input_artifacts.[].metadata_schema`", @@ -1675,6 +1771,7 @@ "custom_metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -1753,6 +1850,7 @@ "custom_metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -1820,6 +1918,17 @@ ], "description": "\nWhen the `state` is `TERMINATED` this will explain why\n`SUCCEEDED` -> Successful processing.\n`USER_ERROR` -> Failed because the provided input was invalid.\n`SYSTEM_ERROR` -> There was an error in the model or platform.\n`SKIPPED` -> Was cancelled\n" }, + "error_code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error Code" + }, "error_message": { "anyOf": [ { @@ -1833,7 +1942,8 @@ "description": "\n The error message in case the `termination_reason` is in `USER_ERROR` or `SYSTEM_ERROR`\n ", "examples": [ "This item was not processed because the threshold of 3 items finishing in error state (user or system error) was reached before the item was processed.", - "The item was not processed because the run was cancelled by the user before the item was processed.User error raised by Application because the input data provided by the user cannot be processed:\nThe image width is 123000 px, but the maximum width is 100000 px", + "The item was not processed because the run was cancelled by the user before the item was processed.", + "User error raised by Application because the input data provided by the user cannot be processed:\nThe image width is 123000 px, but the maximum width is 100000 px", "A system error occurred during the item execution:\n System went out of memory in cell classification", "An unknown system error occurred during the item execution" ] @@ -1861,19 +1971,6 @@ "type": "array", "title": "Output Artifacts", "description": "\nThe list of the results generated by the application algorithm. The number of files and their\ntypes depend on the particular application version, call `/v1/versions/{version_id}` to get\nthe details.\n " - }, - "error_code": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Error Code", - "description": "Error code describing the error that occurred during item processing.", - "readOnly": true } }, "type": "object", @@ -1883,8 +1980,7 @@ "custom_metadata", "state", "output", - "output_artifacts", - "error_code" + "output_artifacts" ], "title": "ItemResultReadResponse", "description": "Response schema for items in `List Run Items` endpoint" @@ -2042,6 +2138,7 @@ ] }, "metadata_schema": { + "additionalProperties": true, "type": "object", "title": "Metadata Schema" }, @@ -2081,6 +2178,7 @@ "metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -2109,7 +2207,7 @@ "$ref": "#/components/schemas/ArtifactOutput", "description": "The output status of the artifact (NONE, FULL)" }, - "error_message": { + "error_code": { "anyOf": [ { "type": "string" @@ -2118,36 +2216,34 @@ "type": "null" } ], - "title": "Error Message", - "description": "Error message when artifact is in error state" + "title": "Error Code" }, - "download_url": { + "error_message": { "anyOf": [ { - "type": "string", - "maxLength": 2083, - "minLength": 1, - "format": "uri" + "type": "string" }, { "type": "null" } ], - "title": "Download Url", - "description": "\nThe download URL to the output file. The URL is valid for 1 hour after the endpoint is called.\nA new URL is generated every time the endpoint is called.\n " + "title": "Error Message" }, - "error_code": { + "download_url": { "anyOf": [ { - "type": "string" + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri" }, { "type": "null" } ], - "title": "Error Code", - "description": "Error code describing the error that occurred during artifact processing.", - "readOnly": true + "title": "Download Url", + "description": "\nThe download URL to the output file. The URL is valid for 1 hour after the endpoint is called.\nA new URL is generated every time the endpoint is called.\n ", + "deprecated": true } }, "type": "object", @@ -2155,9 +2251,7 @@ "output_artifact_id", "name", "state", - "output", - "download_url", - "error_code" + "output" ], "title": "OutputArtifactResultReadResponse" }, @@ -2205,6 +2299,7 @@ "custom_metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -2220,6 +2315,23 @@ } ] }, + "scheduling": { + "anyOf": [ + { + "$ref": "#/components/schemas/SchedulingRequest" + }, + { + "type": "null" + } + ], + "description": "Optional scheduling constraints for this run.", + "examples": [ + { + "deadline": "2026-03-05T23:59:59Z", + "due_date": "2026-03-04T23:59:59Z" + } + ] + }, "items": { "items": { "$ref": "#/components/schemas/ItemCreationRequest" @@ -2269,13 +2381,15 @@ "type": "string", "format": "uuid", "title": "Run Id", - "default": "Run id", "examples": [ "3fa85f64-5717-4562-b3fc-2c963f66afa6" ] } }, "type": "object", + "required": [ + "run_id" + ], "title": "RunCreationResponse" }, "RunItemStatistics": { @@ -2417,6 +2531,7 @@ "custom_metadata": { "anyOf": [ { + "additionalProperties": true, "type": "object" }, { @@ -2500,6 +2615,17 @@ ], "title": "Num Preceding Items Platform", "description": "How many Items from other Runs are due to begin processing before this Run's next Item does." + }, + "scheduling": { + "anyOf": [ + { + "$ref": "#/components/schemas/SchedulingResponse" + }, + { + "type": "null" + } + ], + "description": "Scheduling constraints set for this run." } }, "type": "object", @@ -2537,6 +2663,76 @@ ], "title": "RunTerminationReason" }, + "SchedulingRequest": { + "properties": { + "due_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Due Date", + "description": "Requested completion time. Items are prioritized to meet this target.", + "examples": [ + "2026-03-04T23:59:59Z" + ] + }, + "deadline": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deadline", + "description": "Hard deadline. The run will be cancelled if not completed by this time.", + "examples": [ + "2026-03-05T23:59:59Z" + ] + } + }, + "type": "object", + "title": "SchedulingRequest", + "description": "Scheduling constraints for a run." + }, + "SchedulingResponse": { + "properties": { + "due_date": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Due Date" + }, + "deadline": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deadline" + } + }, + "type": "object", + "title": "SchedulingResponse", + "description": "Scheduling fields returned in run responses." + }, "UserReadResponse": { "properties": { "id": { @@ -2693,6 +2889,13 @@ "type": { "type": "string", "title": "Error Type" + }, + "input": { + "title": "Input" + }, + "ctx": { + "type": "object", + "title": "Context" } }, "type": "object", @@ -2707,6 +2910,7 @@ "properties": { "version_number": { "type": "string", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", "title": "Version Number", "description": "Semantic version of the application" }, diff --git a/codegen/out/.openapi-generator/FILES b/codegen/out/.openapi-generator/FILES index f28d03b19..ee99c756f 100644 --- a/codegen/out/.openapi-generator/FILES +++ b/codegen/out/.openapi-generator/FILES @@ -32,6 +32,8 @@ aignx/codegen/models/run_output.py aignx/codegen/models/run_read_response.py aignx/codegen/models/run_state.py aignx/codegen/models/run_termination_reason.py +aignx/codegen/models/scheduling_request.py +aignx/codegen/models/scheduling_response.py aignx/codegen/models/user_read_response.py aignx/codegen/models/validation_error.py aignx/codegen/models/validation_error_loc_inner.py diff --git a/codegen/out/aignx/codegen/api/public_api.py b/codegen/out/aignx/codegen/api/public_api.py index 8351bc614..fdaaedae8 100644 --- a/codegen/out/aignx/codegen/api/public_api.py +++ b/codegen/out/aignx/codegen/api/public_api.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -16,7 +16,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from typing_extensions import Annotated -from pydantic import Field, StrictStr +from pydantic import Field, StrictStr, field_validator from typing import Any, List, Optional from typing_extensions import Annotated from aignx.codegen.models.application_read_response import ApplicationReadResponse @@ -54,7 +54,7 @@ def __init__(self, api_client=None) -> None: def application_version_details_v1_applications_application_id_versions_version_get( self, application_id: StrictStr, - version: StrictStr, + version: Annotated[str, Field(strict=True)], _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -128,7 +128,7 @@ def application_version_details_v1_applications_application_id_versions_version_ def application_version_details_v1_applications_application_id_versions_version_get_with_http_info( self, application_id: StrictStr, - version: StrictStr, + version: Annotated[str, Field(strict=True)], _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -202,7 +202,7 @@ def application_version_details_v1_applications_application_id_versions_version_ def application_version_details_v1_applications_application_id_versions_version_get_without_preload_content( self, application_id: StrictStr, - version: StrictStr, + version: Annotated[str, Field(strict=True)], _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -1161,6 +1161,297 @@ def _delete_run_items_v1_runs_run_id_artifacts_delete_serialize( + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _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, + ) -> object: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: 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_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + '404': None, + '307': None, + '403': None, + '410': None, + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_with_http_info( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _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[object]: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: 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_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + '404': None, + '307': None, + '403': None, + '410': None, + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_without_preload_content( + self, + run_id: Annotated[StrictStr, Field(description="Run id, returned by `POST /runs/` endpoint")], + artifact_id: Annotated[StrictStr, Field(description="The artifact id to download")], + _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: + """Get Artifact Url + + Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + + :param run_id: Run id, returned by `POST /runs/` endpoint (required) + :type run_id: str + :param artifact_id: The artifact id to download (required) + :type artifact_id: 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_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + run_id=run_id, + artifact_id=artifact_id, + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '200': "object", + '404': None, + '307': None, + '403': None, + '410': None, + '422': "HTTPValidationError", + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get_serialize( + self, + run_id, + artifact_id, + _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, List[str], List[bytes], List[Tuple[str, bytes]]] + ] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + if run_id is not None: + _path_params['run_id'] = run_id + if artifact_id is not None: + _path_params['artifact_id'] = artifact_id + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + # set the HTTP header `Accept` + if 'Accept' not in _header_params: + _header_params['Accept'] = self.api_client.select_header_accept( + [ + 'application/json' + ] + ) + + + # authentication setting + _auth_settings: List[str] = [ + 'OAuth2AuthorizationCodeBearer' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/runs/{run_id}/artifacts/{artifact_id}/file', + 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 def get_item_by_run_v1_runs_run_id_items_external_id_get( self, @@ -1983,7 +2274,7 @@ def list_applications_v1_applications_get( ) -> List[ApplicationReadShortResponse]: """List available applications - Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller’s organization. The response is paginated and sorted according to the provided parameters. + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. :param page: :type page: int @@ -2060,7 +2351,7 @@ def list_applications_v1_applications_get_with_http_info( ) -> ApiResponse[List[ApplicationReadShortResponse]]: """List available applications - Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller’s organization. The response is paginated and sorted according to the provided parameters. + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. :param page: :type page: int @@ -2137,7 +2428,7 @@ def list_applications_v1_applications_get_without_preload_content( ) -> RESTResponseType: """List available applications - Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller’s organization. The response is paginated and sorted according to the provided parameters. + Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. :param page: :type page: int @@ -2681,7 +2972,8 @@ def list_runs_v1_runs_get( custom_metadata: Annotated[Optional[Annotated[str, Field(strict=True, max_length=1000)]], Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, page: Optional[Annotated[int, Field(strict=True, ge=1)]] = None, page_size: Optional[Annotated[int, Field(le=100, strict=True, ge=5)]] = None, - sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + for_organization: Annotated[Optional[StrictStr], Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -2711,7 +3003,9 @@ def list_runs_v1_runs_get( :type page: int :param page_size: :type page_size: int - :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) :type sort: List[str] :param _request_timeout: timeout setting for this request. If one number provided, it will be total request @@ -2742,6 +3036,7 @@ def list_runs_v1_runs_get( custom_metadata=custom_metadata, page=page, page_size=page_size, + for_organization=for_organization, sort=sort, _request_auth=_request_auth, _content_type=_content_type, @@ -2774,7 +3069,8 @@ def list_runs_v1_runs_get_with_http_info( custom_metadata: Annotated[Optional[Annotated[str, Field(strict=True, max_length=1000)]], Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, page: Optional[Annotated[int, Field(strict=True, ge=1)]] = None, page_size: Optional[Annotated[int, Field(le=100, strict=True, ge=5)]] = None, - sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + for_organization: Annotated[Optional[StrictStr], Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -2804,7 +3100,9 @@ def list_runs_v1_runs_get_with_http_info( :type page: int :param page_size: :type page_size: int - :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) :type sort: List[str] :param _request_timeout: timeout setting for this request. If one number provided, it will be total request @@ -2835,6 +3133,7 @@ def list_runs_v1_runs_get_with_http_info( custom_metadata=custom_metadata, page=page, page_size=page_size, + for_organization=for_organization, sort=sort, _request_auth=_request_auth, _content_type=_content_type, @@ -2867,7 +3166,8 @@ def list_runs_v1_runs_get_without_preload_content( custom_metadata: Annotated[Optional[Annotated[str, Field(strict=True, max_length=1000)]], Field(description="Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** ")] = None, page: Optional[Annotated[int, Field(strict=True, ge=1)]] = None, page_size: Optional[Annotated[int, Field(le=100, strict=True, ge=5)]] = None, - sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, + for_organization: Annotated[Optional[StrictStr], Field(description="Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs.")] = None, + sort: Annotated[Optional[List[StrictStr]], Field(description="Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) ")] = None, _request_timeout: Union[ None, Annotated[StrictFloat, Field(gt=0)], @@ -2897,7 +3197,9 @@ def list_runs_v1_runs_get_without_preload_content( :type page: int :param page_size: :type page_size: int - :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) + :param for_organization: Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. + :type for_organization: str + :param sort: Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) :type sort: List[str] :param _request_timeout: timeout setting for this request. If one number provided, it will be total request @@ -2928,6 +3230,7 @@ def list_runs_v1_runs_get_without_preload_content( custom_metadata=custom_metadata, page=page, page_size=page_size, + for_organization=for_organization, sort=sort, _request_auth=_request_auth, _content_type=_content_type, @@ -2955,6 +3258,7 @@ def _list_runs_v1_runs_get_serialize( custom_metadata, page, page_size, + for_organization, sort, _request_auth, _content_type, @@ -3003,6 +3307,10 @@ def _list_runs_v1_runs_get_serialize( _query_params.append(('page_size', page_size)) + if for_organization is not None: + + _query_params.append(('for_organization', for_organization)) + if sort is not None: _query_params.append(('sort', sort)) diff --git a/codegen/out/aignx/codegen/api_client.py b/codegen/out/aignx/codegen/api_client.py index ab2292b5e..b56edae2b 100644 --- a/codegen/out/aignx/codegen/api_client.py +++ b/codegen/out/aignx/codegen/api_client.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/configuration.py b/codegen/out/aignx/codegen/configuration.py index aaf8956a4..ac221d92e 100644 --- a/codegen/out/aignx/codegen/configuration.py +++ b/codegen/out/aignx/codegen/configuration.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -502,7 +502,7 @@ def to_debug_report(self) -> str: return "Python SDK Debug Report:\n"\ "OS: {env}\n"\ "Python Version: {pyversion}\n"\ - "Version of the API: 1.0.0-ga\n"\ + "Version of the API: 1.4.0\n"\ "SDK Package Version: 1.0.0".\ format(env=sys.platform, pyversion=sys.version) diff --git a/codegen/out/aignx/codegen/exceptions.py b/codegen/out/aignx/codegen/exceptions.py index d5bf1d3e3..4f7dfe5b4 100644 --- a/codegen/out/aignx/codegen/exceptions.py +++ b/codegen/out/aignx/codegen/exceptions.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/__init__.py b/codegen/out/aignx/codegen/models/__init__.py index b806fec99..382b6ac7b 100644 --- a/codegen/out/aignx/codegen/models/__init__.py +++ b/codegen/out/aignx/codegen/models/__init__.py @@ -1,35 +1,37 @@ -from .output_artifact_visibility import * -from .organization_read_response import * -from .me_read_response import * -from .run_item_statistics import * -from .validation_error import * -from .output_artifact_result_read_response import * -from .version_read_response import * +from .item_result_read_response import * +from .run_output import * +from .artifact_state import * +from .validation_error_loc_inner import * +from .run_state import * from .item_output import * -from .run_termination_reason import * from .run_creation_response import * +from .scheduling_request import * +from .organization_read_response import * +from .validation_error import * +from .application_read_response import * +from .application_read_short_response import * from .output_artifact_scope import * -from .run_output import * -from .item_result_read_response import * -from .application_version import * -from .run_read_response import * -from .run_state import * +from .scheduling_response import * +from .me_read_response import * from .input_artifact_creation_request import * -from .custom_metadata_update_response import * -from .input_artifact import * -from .custom_metadata_update_request import * +from .item_creation_request import * from .item_state import * -from .artifact_state import * -from .application_read_short_response import * -from .output_artifact import * -from .user_read_response import * -from .item_termination_reason import * -from .artifact_output import * from .auth0_organization import * -from .auth0_user import * -from .item_creation_request import * +from .application_version import * from .http_validation_error import * -from .validation_error_loc_inner import * +from .custom_metadata_update_response import * +from .user_read_response import * +from .run_termination_reason import * +from .input_artifact import * +from .output_artifact_result_read_response import * +from .version_read_response import * +from .auth0_user import * +from .run_read_response import * from .artifact_termination_reason import * from .run_creation_request import * -from .application_read_response import * +from .item_termination_reason import * +from .run_item_statistics import * +from .output_artifact_visibility import * +from .custom_metadata_update_request import * +from .artifact_output import * +from .output_artifact import * diff --git a/codegen/out/aignx/codegen/models/application_read_response.py b/codegen/out/aignx/codegen/models/application_read_response.py index 64cd5739a..9fea43961 100644 --- a/codegen/out/aignx/codegen/models/application_read_response.py +++ b/codegen/out/aignx/codegen/models/application_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/application_read_short_response.py b/codegen/out/aignx/codegen/models/application_read_short_response.py index 0302af0b0..da128fa5e 100644 --- a/codegen/out/aignx/codegen/models/application_read_short_response.py +++ b/codegen/out/aignx/codegen/models/application_read_short_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/application_version.py b/codegen/out/aignx/codegen/models/application_version.py index 8ecf00c67..b7c6b700a 100644 --- a/codegen/out/aignx/codegen/models/application_version.py +++ b/codegen/out/aignx/codegen/models/application_version.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,8 +18,9 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictStr +from pydantic import BaseModel, ConfigDict, Field, field_validator from typing import Any, ClassVar, Dict, List +from typing_extensions import Annotated from typing import Optional, Set from typing_extensions import Self @@ -27,10 +28,17 @@ class ApplicationVersion(BaseModel): """ ApplicationVersion """ # noqa: E501 - number: StrictStr = Field(description="The number of the latest version") + number: Annotated[str, Field(strict=True)] = Field(description="The number of the latest version") released_at: datetime = Field(description="The timestamp for when the application version was made available in the Platform") __properties: ClassVar[List[str]] = ["number", "released_at"] + @field_validator('number') + def number_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$", value): + raise ValueError(r"must validate the regular expression /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/") + return value + model_config = ConfigDict( populate_by_name=True, validate_assignment=True, diff --git a/codegen/out/aignx/codegen/models/artifact_output.py b/codegen/out/aignx/codegen/models/artifact_output.py index 5d8a8d9bf..150698d7c 100644 --- a/codegen/out/aignx/codegen/models/artifact_output.py +++ b/codegen/out/aignx/codegen/models/artifact_output.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/artifact_state.py b/codegen/out/aignx/codegen/models/artifact_state.py index aad53f962..b90f2272a 100644 --- a/codegen/out/aignx/codegen/models/artifact_state.py +++ b/codegen/out/aignx/codegen/models/artifact_state.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/artifact_termination_reason.py b/codegen/out/aignx/codegen/models/artifact_termination_reason.py index 018a8fe89..6f06896b0 100644 --- a/codegen/out/aignx/codegen/models/artifact_termination_reason.py +++ b/codegen/out/aignx/codegen/models/artifact_termination_reason.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/custom_metadata_update_request.py b/codegen/out/aignx/codegen/models/custom_metadata_update_request.py index 649eec35e..5cd0650b0 100644 --- a/codegen/out/aignx/codegen/models/custom_metadata_update_request.py +++ b/codegen/out/aignx/codegen/models/custom_metadata_update_request.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/custom_metadata_update_response.py b/codegen/out/aignx/codegen/models/custom_metadata_update_response.py index 01da04914..c1bf3b6db 100644 --- a/codegen/out/aignx/codegen/models/custom_metadata_update_response.py +++ b/codegen/out/aignx/codegen/models/custom_metadata_update_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/http_validation_error.py b/codegen/out/aignx/codegen/models/http_validation_error.py index 0e68b1426..f3834b746 100644 --- a/codegen/out/aignx/codegen/models/http_validation_error.py +++ b/codegen/out/aignx/codegen/models/http_validation_error.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/input_artifact.py b/codegen/out/aignx/codegen/models/input_artifact.py index f4877d5b4..02324f488 100644 --- a/codegen/out/aignx/codegen/models/input_artifact.py +++ b/codegen/out/aignx/codegen/models/input_artifact.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/input_artifact_creation_request.py b/codegen/out/aignx/codegen/models/input_artifact_creation_request.py index 60c6bb9c3..80af637ae 100644 --- a/codegen/out/aignx/codegen/models/input_artifact_creation_request.py +++ b/codegen/out/aignx/codegen/models/input_artifact_creation_request.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/item_creation_request.py b/codegen/out/aignx/codegen/models/item_creation_request.py index 6aee0f1f7..593eff96c 100644 --- a/codegen/out/aignx/codegen/models/item_creation_request.py +++ b/codegen/out/aignx/codegen/models/item_creation_request.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/item_output.py b/codegen/out/aignx/codegen/models/item_output.py index 90e8f9adb..d313e54c9 100644 --- a/codegen/out/aignx/codegen/models/item_output.py +++ b/codegen/out/aignx/codegen/models/item_output.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/item_result_read_response.py b/codegen/out/aignx/codegen/models/item_result_read_response.py index 11aba77d3..f4f334179 100644 --- a/codegen/out/aignx/codegen/models/item_result_read_response.py +++ b/codegen/out/aignx/codegen/models/item_result_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -40,11 +40,11 @@ class ItemResultReadResponse(BaseModel): state: ItemState = Field(description=" The item moves from `PENDING` to `PROCESSING` to `TERMINATED` state. When terminated, consult the `termination_reason` property to see whether it was successful. ") output: ItemOutput = Field(description="The output status of the item (NONE, FULL)") termination_reason: Optional[ItemTerminationReason] = None + error_code: Optional[StrictStr] = None error_message: Optional[StrictStr] = None terminated_at: Optional[datetime] = None output_artifacts: List[OutputArtifactResultReadResponse] = Field(description=" The list of the results generated by the application algorithm. The number of files and their types depend on the particular application version, call `/v1/versions/{version_id}` to get the details. ") - error_code: Optional[StrictStr] - __properties: ClassVar[List[str]] = ["item_id", "external_id", "custom_metadata", "custom_metadata_checksum", "queue_position_org", "queue_position_platform", "state", "output", "termination_reason", "error_message", "terminated_at", "output_artifacts", "error_code"] + __properties: ClassVar[List[str]] = ["item_id", "external_id", "custom_metadata", "custom_metadata_checksum", "queue_position_org", "queue_position_platform", "state", "output", "termination_reason", "error_code", "error_message", "terminated_at", "output_artifacts"] model_config = ConfigDict( populate_by_name=True, @@ -117,6 +117,11 @@ def to_dict(self) -> Dict[str, Any]: if self.termination_reason is None and "termination_reason" in self.model_fields_set: _dict['termination_reason'] = None + # set to None if error_code (nullable) is None + # and model_fields_set contains the field + if self.error_code is None and "error_code" in self.model_fields_set: + _dict['error_code'] = None + # set to None if error_message (nullable) is None # and model_fields_set contains the field if self.error_message is None and "error_message" in self.model_fields_set: @@ -127,11 +132,6 @@ def to_dict(self) -> Dict[str, Any]: if self.terminated_at is None and "terminated_at" in self.model_fields_set: _dict['terminated_at'] = None - # set to None if error_code (nullable) is None - # and model_fields_set contains the field - if self.error_code is None and "error_code" in self.model_fields_set: - _dict['error_code'] = None - return _dict @classmethod @@ -153,10 +153,10 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "state": obj.get("state"), "output": obj.get("output"), "termination_reason": obj.get("termination_reason"), + "error_code": obj.get("error_code"), "error_message": obj.get("error_message"), "terminated_at": obj.get("terminated_at"), - "output_artifacts": [OutputArtifactResultReadResponse.from_dict(_item) for _item in obj["output_artifacts"]] if obj.get("output_artifacts") is not None else None, - "error_code": obj.get("error_code") + "output_artifacts": [OutputArtifactResultReadResponse.from_dict(_item) for _item in obj["output_artifacts"]] if obj.get("output_artifacts") is not None else None }) return _obj diff --git a/codegen/out/aignx/codegen/models/item_state.py b/codegen/out/aignx/codegen/models/item_state.py index 737d8fbe3..8e4928c16 100644 --- a/codegen/out/aignx/codegen/models/item_state.py +++ b/codegen/out/aignx/codegen/models/item_state.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/item_termination_reason.py b/codegen/out/aignx/codegen/models/item_termination_reason.py index 7ffdcb5fa..04070e10b 100644 --- a/codegen/out/aignx/codegen/models/item_termination_reason.py +++ b/codegen/out/aignx/codegen/models/item_termination_reason.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/me_read_response.py b/codegen/out/aignx/codegen/models/me_read_response.py index 45ae03b77..b25865ec2 100644 --- a/codegen/out/aignx/codegen/models/me_read_response.py +++ b/codegen/out/aignx/codegen/models/me_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/organization_read_response.py b/codegen/out/aignx/codegen/models/organization_read_response.py index 34a15cf44..7b3b5c4ee 100644 --- a/codegen/out/aignx/codegen/models/organization_read_response.py +++ b/codegen/out/aignx/codegen/models/organization_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/output_artifact.py b/codegen/out/aignx/codegen/models/output_artifact.py index 837ac7818..dd62295a7 100644 --- a/codegen/out/aignx/codegen/models/output_artifact.py +++ b/codegen/out/aignx/codegen/models/output_artifact.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py b/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py index cb6cb2dae..131c0101f 100644 --- a/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py +++ b/codegen/out/aignx/codegen/models/output_artifact_result_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -36,10 +36,10 @@ class OutputArtifactResultReadResponse(BaseModel): state: ArtifactState = Field(description="The current state of the artifact (PENDING, PROCESSING, TERMINATED)") termination_reason: Optional[ArtifactTerminationReason] = None output: ArtifactOutput = Field(description="The output status of the artifact (NONE, FULL)") + error_code: Optional[StrictStr] = None error_message: Optional[StrictStr] = None - download_url: Optional[Annotated[str, Field(min_length=1, strict=True, max_length=2083)]] - error_code: Optional[StrictStr] - __properties: ClassVar[List[str]] = ["output_artifact_id", "name", "metadata", "state", "termination_reason", "output", "error_message", "download_url", "error_code"] + download_url: Optional[Annotated[str, Field(min_length=1, strict=True, max_length=2083)]] = None + __properties: ClassVar[List[str]] = ["output_artifact_id", "name", "metadata", "state", "termination_reason", "output", "error_code", "error_message", "download_url"] model_config = ConfigDict( populate_by_name=True, @@ -90,6 +90,11 @@ def to_dict(self) -> Dict[str, Any]: if self.termination_reason is None and "termination_reason" in self.model_fields_set: _dict['termination_reason'] = None + # set to None if error_code (nullable) is None + # and model_fields_set contains the field + if self.error_code is None and "error_code" in self.model_fields_set: + _dict['error_code'] = None + # set to None if error_message (nullable) is None # and model_fields_set contains the field if self.error_message is None and "error_message" in self.model_fields_set: @@ -100,11 +105,6 @@ def to_dict(self) -> Dict[str, Any]: if self.download_url is None and "download_url" in self.model_fields_set: _dict['download_url'] = None - # set to None if error_code (nullable) is None - # and model_fields_set contains the field - if self.error_code is None and "error_code" in self.model_fields_set: - _dict['error_code'] = None - return _dict @classmethod @@ -123,9 +123,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "state": obj.get("state"), "termination_reason": obj.get("termination_reason"), "output": obj.get("output"), + "error_code": obj.get("error_code"), "error_message": obj.get("error_message"), - "download_url": obj.get("download_url"), - "error_code": obj.get("error_code") + "download_url": obj.get("download_url") }) return _obj diff --git a/codegen/out/aignx/codegen/models/output_artifact_scope.py b/codegen/out/aignx/codegen/models/output_artifact_scope.py index 0cf11f0cd..d1f927a4c 100644 --- a/codegen/out/aignx/codegen/models/output_artifact_scope.py +++ b/codegen/out/aignx/codegen/models/output_artifact_scope.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/output_artifact_visibility.py b/codegen/out/aignx/codegen/models/output_artifact_visibility.py index 619684588..48a346d0e 100644 --- a/codegen/out/aignx/codegen/models/output_artifact_visibility.py +++ b/codegen/out/aignx/codegen/models/output_artifact_visibility.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/run_creation_request.py b/codegen/out/aignx/codegen/models/run_creation_request.py index 7b004a37d..c6e7bfb95 100644 --- a/codegen/out/aignx/codegen/models/run_creation_request.py +++ b/codegen/out/aignx/codegen/models/run_creation_request.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -21,6 +21,7 @@ from typing import Any, ClassVar, Dict, List, Optional from typing_extensions import Annotated from aignx.codegen.models.item_creation_request import ItemCreationRequest +from aignx.codegen.models.scheduling_request import SchedulingRequest from typing import Optional, Set from typing_extensions import Self @@ -31,8 +32,9 @@ class RunCreationRequest(BaseModel): application_id: StrictStr = Field(description="Unique ID for the application to use for processing") version_number: Optional[StrictStr] = None custom_metadata: Optional[Dict[str, Any]] = None + scheduling: Optional[SchedulingRequest] = None items: Annotated[List[ItemCreationRequest], Field(min_length=1)] = Field(description="List of items (slides) to process. Each item represents a whole slide image (WSI) with its associated metadata and artifacts") - __properties: ClassVar[List[str]] = ["application_id", "version_number", "custom_metadata", "items"] + __properties: ClassVar[List[str]] = ["application_id", "version_number", "custom_metadata", "scheduling", "items"] model_config = ConfigDict( populate_by_name=True, @@ -73,6 +75,9 @@ def to_dict(self) -> Dict[str, Any]: exclude=excluded_fields, exclude_none=True, ) + # override the default output from pydantic by calling `to_dict()` of scheduling + if self.scheduling: + _dict['scheduling'] = self.scheduling.to_dict() # override the default output from pydantic by calling `to_dict()` of each item in items (list) _items = [] if self.items: @@ -90,6 +95,11 @@ def to_dict(self) -> Dict[str, Any]: if self.custom_metadata is None and "custom_metadata" in self.model_fields_set: _dict['custom_metadata'] = None + # set to None if scheduling (nullable) is None + # and model_fields_set contains the field + if self.scheduling is None and "scheduling" in self.model_fields_set: + _dict['scheduling'] = None + return _dict @classmethod @@ -105,6 +115,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "application_id": obj.get("application_id"), "version_number": obj.get("version_number"), "custom_metadata": obj.get("custom_metadata"), + "scheduling": SchedulingRequest.from_dict(obj["scheduling"]) if obj.get("scheduling") is not None else None, "items": [ItemCreationRequest.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None }) return _obj diff --git a/codegen/out/aignx/codegen/models/run_creation_response.py b/codegen/out/aignx/codegen/models/run_creation_response.py index 7419f92e9..7277899ff 100644 --- a/codegen/out/aignx/codegen/models/run_creation_response.py +++ b/codegen/out/aignx/codegen/models/run_creation_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,7 +18,7 @@ import json from pydantic import BaseModel, ConfigDict, StrictStr -from typing import Any, ClassVar, Dict, List, Optional +from typing import Any, ClassVar, Dict, List from typing import Optional, Set from typing_extensions import Self @@ -26,7 +26,7 @@ class RunCreationResponse(BaseModel): """ RunCreationResponse """ # noqa: E501 - run_id: Optional[StrictStr] = 'Run id' + run_id: StrictStr __properties: ClassVar[List[str]] = ["run_id"] model_config = ConfigDict( @@ -80,7 +80,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: return cls.model_validate(obj) _obj = cls.model_validate({ - "run_id": obj.get("run_id") if obj.get("run_id") is not None else 'Run id' + "run_id": obj.get("run_id") }) return _obj diff --git a/codegen/out/aignx/codegen/models/run_item_statistics.py b/codegen/out/aignx/codegen/models/run_item_statistics.py index 33412d18e..f3d24fa07 100644 --- a/codegen/out/aignx/codegen/models/run_item_statistics.py +++ b/codegen/out/aignx/codegen/models/run_item_statistics.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/run_output.py b/codegen/out/aignx/codegen/models/run_output.py index f2af3f3e8..29837ab76 100644 --- a/codegen/out/aignx/codegen/models/run_output.py +++ b/codegen/out/aignx/codegen/models/run_output.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/run_read_response.py b/codegen/out/aignx/codegen/models/run_read_response.py index 53f0f8ca5..0e3ff1a31 100644 --- a/codegen/out/aignx/codegen/models/run_read_response.py +++ b/codegen/out/aignx/codegen/models/run_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -24,6 +24,7 @@ from aignx.codegen.models.run_output import RunOutput from aignx.codegen.models.run_state import RunState from aignx.codegen.models.run_termination_reason import RunTerminationReason +from aignx.codegen.models.scheduling_response import SchedulingResponse from typing import Optional, Set from typing_extensions import Self @@ -47,7 +48,8 @@ class RunReadResponse(BaseModel): terminated_at: Optional[datetime] = None num_preceding_items_org: Optional[StrictInt] = None num_preceding_items_platform: Optional[StrictInt] = None - __properties: ClassVar[List[str]] = ["run_id", "application_id", "version_number", "state", "output", "termination_reason", "error_code", "error_message", "statistics", "custom_metadata", "custom_metadata_checksum", "submitted_at", "submitted_by", "terminated_at", "num_preceding_items_org", "num_preceding_items_platform"] + scheduling: Optional[SchedulingResponse] = None + __properties: ClassVar[List[str]] = ["run_id", "application_id", "version_number", "state", "output", "termination_reason", "error_code", "error_message", "statistics", "custom_metadata", "custom_metadata_checksum", "submitted_at", "submitted_by", "terminated_at", "num_preceding_items_org", "num_preceding_items_platform", "scheduling"] model_config = ConfigDict( populate_by_name=True, @@ -91,6 +93,9 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of statistics if self.statistics: _dict['statistics'] = self.statistics.to_dict() + # override the default output from pydantic by calling `to_dict()` of scheduling + if self.scheduling: + _dict['scheduling'] = self.scheduling.to_dict() # set to None if termination_reason (nullable) is None # and model_fields_set contains the field if self.termination_reason is None and "termination_reason" in self.model_fields_set: @@ -131,6 +136,11 @@ def to_dict(self) -> Dict[str, Any]: if self.num_preceding_items_platform is None and "num_preceding_items_platform" in self.model_fields_set: _dict['num_preceding_items_platform'] = None + # set to None if scheduling (nullable) is None + # and model_fields_set contains the field + if self.scheduling is None and "scheduling" in self.model_fields_set: + _dict['scheduling'] = None + return _dict @classmethod @@ -158,7 +168,8 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: "submitted_by": obj.get("submitted_by"), "terminated_at": obj.get("terminated_at"), "num_preceding_items_org": obj.get("num_preceding_items_org"), - "num_preceding_items_platform": obj.get("num_preceding_items_platform") + "num_preceding_items_platform": obj.get("num_preceding_items_platform"), + "scheduling": SchedulingResponse.from_dict(obj["scheduling"]) if obj.get("scheduling") is not None else None }) return _obj diff --git a/codegen/out/aignx/codegen/models/run_state.py b/codegen/out/aignx/codegen/models/run_state.py index 22f32e7be..734e55890 100644 --- a/codegen/out/aignx/codegen/models/run_state.py +++ b/codegen/out/aignx/codegen/models/run_state.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/run_termination_reason.py b/codegen/out/aignx/codegen/models/run_termination_reason.py index 44b8a5653..5e700de27 100644 --- a/codegen/out/aignx/codegen/models/run_termination_reason.py +++ b/codegen/out/aignx/codegen/models/run_termination_reason.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/scheduling_request.py b/codegen/out/aignx/codegen/models/scheduling_request.py new file mode 100644 index 000000000..6b3be7d0d --- /dev/null +++ b/codegen/out/aignx/codegen/models/scheduling_request.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + Aignostics Platform API + + The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + + The version of the OpenAPI document: 1.4.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from datetime import datetime +from pydantic import BaseModel, ConfigDict +from typing import Any, ClassVar, Dict, List, Optional +from typing import Optional, Set +from typing_extensions import Self + +class SchedulingRequest(BaseModel): + """ + Scheduling constraints for a run. + """ # noqa: E501 + due_date: Optional[datetime] = None + deadline: Optional[datetime] = None + __properties: ClassVar[List[str]] = ["due_date", "deadline"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of SchedulingRequest from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if due_date (nullable) is None + # and model_fields_set contains the field + if self.due_date is None and "due_date" in self.model_fields_set: + _dict['due_date'] = None + + # set to None if deadline (nullable) is None + # and model_fields_set contains the field + if self.deadline is None and "deadline" in self.model_fields_set: + _dict['deadline'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of SchedulingRequest from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "due_date": obj.get("due_date"), + "deadline": obj.get("deadline") + }) + return _obj + + diff --git a/codegen/out/aignx/codegen/models/scheduling_response.py b/codegen/out/aignx/codegen/models/scheduling_response.py new file mode 100644 index 000000000..fd647acce --- /dev/null +++ b/codegen/out/aignx/codegen/models/scheduling_response.py @@ -0,0 +1,100 @@ +# coding: utf-8 + +""" + Aignostics Platform API + + The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. + + The version of the OpenAPI document: 1.4.0 + Generated by OpenAPI Generator (https://openapi-generator.tech) + + Do not edit the class manually. +""" # noqa: E501 + + +from __future__ import annotations +import pprint +import re # noqa: F401 +import json + +from datetime import datetime +from pydantic import BaseModel, ConfigDict +from typing import Any, ClassVar, Dict, List, Optional +from typing import Optional, Set +from typing_extensions import Self + +class SchedulingResponse(BaseModel): + """ + Scheduling fields returned in run responses. + """ # noqa: E501 + due_date: Optional[datetime] = None + deadline: Optional[datetime] = None + __properties: ClassVar[List[str]] = ["due_date", "deadline"] + + model_config = ConfigDict( + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + + def to_str(self) -> str: + """Returns the string representation of the model using alias""" + return pprint.pformat(self.model_dump(by_alias=True)) + + def to_json(self) -> str: + """Returns the JSON representation of the model using alias""" + # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_str: str) -> Optional[Self]: + """Create an instance of SchedulingResponse from a JSON string""" + return cls.from_dict(json.loads(json_str)) + + def to_dict(self) -> Dict[str, Any]: + """Return the dictionary representation of the model using alias. + + This has the following differences from calling pydantic's + `self.model_dump(by_alias=True)`: + + * `None` is only added to the output dict for nullable fields that + were set at model initialization. Other fields with value `None` + are ignored. + """ + excluded_fields: Set[str] = set([ + ]) + + _dict = self.model_dump( + by_alias=True, + exclude=excluded_fields, + exclude_none=True, + ) + # set to None if due_date (nullable) is None + # and model_fields_set contains the field + if self.due_date is None and "due_date" in self.model_fields_set: + _dict['due_date'] = None + + # set to None if deadline (nullable) is None + # and model_fields_set contains the field + if self.deadline is None and "deadline" in self.model_fields_set: + _dict['deadline'] = None + + return _dict + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: + """Create an instance of SchedulingResponse from a dict""" + if obj is None: + return None + + if not isinstance(obj, dict): + return cls.model_validate(obj) + + _obj = cls.model_validate({ + "due_date": obj.get("due_date"), + "deadline": obj.get("deadline") + }) + return _obj + + diff --git a/codegen/out/aignx/codegen/models/user_read_response.py b/codegen/out/aignx/codegen/models/user_read_response.py index b82796ccd..542122424 100644 --- a/codegen/out/aignx/codegen/models/user_read_response.py +++ b/codegen/out/aignx/codegen/models/user_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/validation_error.py b/codegen/out/aignx/codegen/models/validation_error.py index 0f0574bdb..17fa7ef39 100644 --- a/codegen/out/aignx/codegen/models/validation_error.py +++ b/codegen/out/aignx/codegen/models/validation_error.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,7 +18,7 @@ import json from pydantic import BaseModel, ConfigDict, StrictStr -from typing import Any, ClassVar, Dict, List +from typing import Any, ClassVar, Dict, List, Optional from aignx.codegen.models.validation_error_loc_inner import ValidationErrorLocInner from typing import Optional, Set from typing_extensions import Self @@ -30,7 +30,9 @@ class ValidationError(BaseModel): loc: List[ValidationErrorLocInner] msg: StrictStr type: StrictStr - __properties: ClassVar[List[str]] = ["loc", "msg", "type"] + input: Optional[Any] = None + ctx: Optional[Dict[str, Any]] = None + __properties: ClassVar[List[str]] = ["loc", "msg", "type", "input", "ctx"] model_config = ConfigDict( populate_by_name=True, @@ -78,6 +80,11 @@ def to_dict(self) -> Dict[str, Any]: if _item_loc: _items.append(_item_loc.to_dict()) _dict['loc'] = _items + # set to None if input (nullable) is None + # and model_fields_set contains the field + if self.input is None and "input" in self.model_fields_set: + _dict['input'] = None + return _dict @classmethod @@ -92,7 +99,9 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "loc": [ValidationErrorLocInner.from_dict(_item) for _item in obj["loc"]] if obj.get("loc") is not None else None, "msg": obj.get("msg"), - "type": obj.get("type") + "type": obj.get("type"), + "input": obj.get("input"), + "ctx": obj.get("ctx") }) return _obj diff --git a/codegen/out/aignx/codegen/models/validation_error_loc_inner.py b/codegen/out/aignx/codegen/models/validation_error_loc_inner.py index b65d82b77..8f81623fe 100644 --- a/codegen/out/aignx/codegen/models/validation_error_loc_inner.py +++ b/codegen/out/aignx/codegen/models/validation_error_loc_inner.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/aignx/codegen/models/version_read_response.py b/codegen/out/aignx/codegen/models/version_read_response.py index e56dcd7d6..55e4cf4c4 100644 --- a/codegen/out/aignx/codegen/models/version_read_response.py +++ b/codegen/out/aignx/codegen/models/version_read_response.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. @@ -18,8 +18,9 @@ import json from datetime import datetime -from pydantic import BaseModel, ConfigDict, Field, StrictStr +from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator from typing import Any, ClassVar, Dict, List +from typing_extensions import Annotated from aignx.codegen.models.input_artifact import InputArtifact from aignx.codegen.models.output_artifact import OutputArtifact from typing import Optional, Set @@ -29,13 +30,20 @@ class VersionReadResponse(BaseModel): """ Base Response schema for the `Application Version Details` endpoint """ # noqa: E501 - version_number: StrictStr = Field(description="Semantic version of the application") + version_number: Annotated[str, Field(strict=True)] = Field(description="Semantic version of the application") changelog: StrictStr = Field(description="Description of the changes relative to the previous version") input_artifacts: List[InputArtifact] = Field(description="List of the input fields, provided by the User") output_artifacts: List[OutputArtifact] = Field(description="List of the output fields, generated by the application") released_at: datetime = Field(description="The timestamp when the application version was registered") __properties: ClassVar[List[str]] = ["version_number", "changelog", "input_artifacts", "output_artifacts", "released_at"] + @field_validator('version_number') + def version_number_validate_regular_expression(cls, value): + """Validates the regular expression""" + if not re.match(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$", value): + raise ValueError(r"must validate the regular expression /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/") + return value + model_config = ConfigDict( populate_by_name=True, validate_assignment=True, diff --git a/codegen/out/aignx/codegen/rest.py b/codegen/out/aignx/codegen/rest.py index 972b94da5..824e7f3d3 100644 --- a/codegen/out/aignx/codegen/rest.py +++ b/codegen/out/aignx/codegen/rest.py @@ -5,7 +5,7 @@ The Aignostics Platform is a cloud-based service that enables organizations to access advanced computational pathology applications through a secure API. The platform provides standardized access to Aignostics' portfolio of computational pathology solutions, with Atlas H&E-TME serving as an example of the available API endpoints. To begin using the platform, your organization must first be registered by our business support team. If you don't have an account yet, please contact your account manager or email support@aignostics.com to get started. More information about our applications can be found on [https://platform.aignostics.com](https://platform.aignostics.com). **How to authorize and test API endpoints:** 1. Click the \"Authorize\" button in the right corner below 3. Click \"Authorize\" button in the dialog to log in with your Aignostics Platform credentials 4. After successful login, you'll be redirected back and can use \"Try it out\" on any endpoint **Note**: You only need to authorize once per session. The lock icons next to endpoints will show green when authorized. - The version of the OpenAPI document: 1.0.0-ga + The version of the OpenAPI document: 1.4.0 Generated by OpenAPI Generator (https://openapi-generator.tech) Do not edit the class manually. diff --git a/codegen/out/docs/PublicApi.md b/codegen/out/docs/PublicApi.md index 965f56879..82fdb6bd2 100644 --- a/codegen/out/docs/PublicApi.md +++ b/codegen/out/docs/PublicApi.md @@ -8,6 +8,7 @@ Method | HTTP request | Description [**cancel_run_v1_runs_run_id_cancel_post**](PublicApi.md#cancel_run_v1_runs_run_id_cancel_post) | **POST** /v1/runs/{run_id}/cancel | Cancel Run [**create_run_v1_runs_post**](PublicApi.md#create_run_v1_runs_post) | **POST** /v1/runs | Initiate Run [**delete_run_items_v1_runs_run_id_artifacts_delete**](PublicApi.md#delete_run_items_v1_runs_run_id_artifacts_delete) | **DELETE** /v1/runs/{run_id}/artifacts | Delete Run Items +[**get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get**](PublicApi.md#get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get) | **GET** /v1/runs/{run_id}/artifacts/{artifact_id}/file | Get Artifact Url [**get_item_by_run_v1_runs_run_id_items_external_id_get**](PublicApi.md#get_item_by_run_v1_runs_run_id_items_external_id_get) | **GET** /v1/runs/{run_id}/items/{external_id} | Get Item By Run [**get_me_v1_me_get**](PublicApi.md#get_me_v1_me_get) | **GET** /v1/me | Get current user [**get_run_v1_runs_run_id_get**](PublicApi.md#get_run_v1_runs_run_id_get) | **GET** /v1/runs/{run_id} | Get run details @@ -333,6 +334,87 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get** +> object get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get(run_id, artifact_id) + +Get Artifact Url + +Download the artifact file with the specified artifact_id, belonging to the specified run. The artifact_is is returned by the `GET /v1/runs/{run_id}/items` endpoint as part of the item results, and can also be retrieved via `GET /v1/runs/{run_id}/items/{external_id}`. The endpoint may return a redirect response with a presigned URL to download the artifact file from the storage bucket. The presigned URL is valid for a limited time, so it should be used immediately after receiving the response. + +### Example + +* OAuth Authentication (OAuth2AuthorizationCodeBearer): + +```python +import aignx.codegen +from aignx.codegen.rest import ApiException +from pprint import pprint + +# Defining the host is optional and defaults to /api +# See configuration.py for a list of all supported configuration parameters. +configuration = aignx.codegen.Configuration( + host = "/api" +) + +# The client must configure the authentication and authorization parameters +# in accordance with the API server security policy. +# Examples for each auth method are provided below, use the example that +# satisfies your auth use case. + +configuration.access_token = os.environ["ACCESS_TOKEN"] + +# Enter a context with an instance of the API client +with aignx.codegen.ApiClient(configuration) as api_client: + # Create an instance of the API class + api_instance = aignx.codegen.PublicApi(api_client) + run_id = 'run_id_example' # str | Run id, returned by `POST /runs/` endpoint + artifact_id = 'artifact_id_example' # str | The artifact id to download + + try: + # Get Artifact Url + api_response = api_instance.get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get(run_id, artifact_id) + print("The response of PublicApi->get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get:\n") + pprint(api_response) + except Exception as e: + print("Exception when calling PublicApi->get_artifact_url_v1_runs_run_id_artifacts_artifact_id_file_get: %s\n" % e) +``` + + + +### Parameters + + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **run_id** | **str**| Run id, returned by `POST /runs/` endpoint | + **artifact_id** | **str**| The artifact id to download | + +### Return type + +**object** + +### Authorization + +[OAuth2AuthorizationCodeBearer](../README.md#OAuth2AuthorizationCodeBearer) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + +### HTTP response details + +| Status code | Description | Response headers | +|-------------|-------------|------------------| +**200** | Successful Response | - | +**404** | Not Found - Artifact not found for the specified run | - | +**307** | Temporary Redirect - Redirect to the artifact file URL | - | +**403** | Forbidden - You don't have permission to download this artifact | - | +**410** | Gone - Artifact has been deleted | - | +**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + # **get_item_by_run_v1_runs_run_id_items_external_id_get** > ItemResultReadResponse get_item_by_run_v1_runs_run_id_items_external_id_get(run_id, external_id) @@ -567,7 +649,7 @@ Name | Type | Description | Notes List available applications -Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller’s organization. The response is paginated and sorted according to the provided parameters. +Returns the list of the applications, available to the caller. The application is available if any of the versions of the application is assigned to the caller's organization. The response is paginated and sorted according to the provided parameters. ### Example @@ -739,7 +821,7 @@ Name | Type | Description | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) # **list_runs_v1_runs_get** -> List[RunReadResponse] list_runs_v1_runs_get(application_id=application_id, application_version=application_version, external_id=external_id, custom_metadata=custom_metadata, page=page, page_size=page_size, sort=sort) +> List[RunReadResponse] list_runs_v1_runs_get(application_id=application_id, application_version=application_version, external_id=external_id, custom_metadata=custom_metadata, page=page, page_size=page_size, for_organization=for_organization, sort=sort) List Runs @@ -778,11 +860,12 @@ with aignx.codegen.ApiClient(configuration) as api_client: custom_metadata = '$' # str | Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** (optional) page = 1 # int | (optional) (default to 1) page_size = 50 # int | (optional) (default to 50) - sort = ['sort_example'] # List[str] | Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) (optional) + for_organization = 'for_organization_example' # str | Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. (optional) + sort = ['sort_example'] # List[str] | Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) (optional) try: # List Runs - api_response = api_instance.list_runs_v1_runs_get(application_id=application_id, application_version=application_version, external_id=external_id, custom_metadata=custom_metadata, page=page, page_size=page_size, sort=sort) + api_response = api_instance.list_runs_v1_runs_get(application_id=application_id, application_version=application_version, external_id=external_id, custom_metadata=custom_metadata, page=page, page_size=page_size, for_organization=for_organization, sort=sort) print("The response of PublicApi->list_runs_v1_runs_get:\n") pprint(api_response) except Exception as e: @@ -802,7 +885,8 @@ Name | Type | Description | Notes **custom_metadata** | **str**| Use PostgreSQL JSONPath expressions to filter runs by their custom_metadata. #### URL Encoding Required **Important**: JSONPath expressions contain special characters that must be URL-encoded when used in query parameters. Most HTTP clients handle this automatically, but when constructing URLs manually, please ensure proper encoding. #### Examples (Clear Format): - **Field existence**: `$.study` - Runs that have a study field defined - **Exact value match**: `$.study ? (@ == \"high\")` - Runs with specific study value - **Numeric comparison**: `$.confidence_score ? (@ > 0.75)` - Runs with confidence score greater than 0.75 - **Array operations**: `$.tags[*] ? (@ == \"draft\")` - Runs with tags array containing \"draft\" - **Complex conditions**: `$.resources ? (@.gpu_count > 2 && @.memory_gb >= 16)` - Runs with high resource requirements #### Examples (URL-Encoded Format): - **Field existence**: `%24.study` - **Exact value match**: `%24.study%20%3F%20(%40%20%3D%3D%20%22high%22)` - **Numeric comparison**: `%24.confidence_score%20%3F%20(%40%20%3E%200.75)` - **Array operations**: `%24.tags%5B*%5D%20%3F%20(%40%20%3D%3D%20%22draft%22)` - **Complex conditions**: `%24.resources%20%3F%20(%40.gpu_count%20%3E%202%20%26%26%20%40.memory_gb%20%3E%3D%2016)` #### Notes - JSONPath expressions are evaluated using PostgreSQL's `@?` operator - The `$.` prefix is automatically added to root-level field references if missing - String values in conditions must be enclosed in double quotes - Use `&&` for AND operations and `||` for OR operations - Regular expressions use `like_regex` with standard regex syntax - **Please remember to URL-encode the entire JSONPath expression when making HTTP requests** | [optional] **page** | **int**| | [optional] [default to 1] **page_size** | **int**| | [optional] [default to 50] - **sort** | [**List[str]**](str.md)| Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `statistics` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) | [optional] + **for_organization** | **str**| Filter runs by organization ID. Available for superadmins (any org) and admins (own org only). When provided, returns all runs for the specified organization instead of only the caller's own runs. | [optional] + **sort** | [**List[str]**](str.md)| Sort the results by one or more fields. Use `+` for ascending and `-` for descending order. **Available fields:** - `run_id` - `application_id` - `version_number` - `custom_metadata` - `submitted_at` - `submitted_by` - `terminated_at` - `termination_reason` **Examples:** - `?sort=submitted_at` - Sort by creation time (ascending) - `?sort=-submitted_at` - Sort by creation time (descending) - `?sort=state&sort=-submitted_at` - Sort by state, then by time (descending) | [optional] ### Return type diff --git a/src/aignostics/application/_gui/_page_application_run_describe.py b/src/aignostics/application/_gui/_page_application_run_describe.py index f897ae768..740aae23a 100644 --- a/src/aignostics/application/_gui/_page_application_run_describe.py +++ b/src/aignostics/application/_gui/_page_application_run_describe.py @@ -569,6 +569,24 @@ def open_marimo(results_folder: Path, button: ui.button | None = None) -> None: else: status_str = f"{run_data.state.value}" + # Extract scheduling info from the API response field + scheduling = getattr(run_data, "scheduling", None) + due_date_str = "N/A" + deadline_str = "N/A" + if scheduling is not None: + if getattr(scheduling, "due_date", None) is not None: + due_date_str = ( + scheduling.due_date.astimezone().strftime("%m-%d %H:%M") + if hasattr(scheduling.due_date, "astimezone") + else str(scheduling.due_date) + ) + if getattr(scheduling, "deadline", None) is not None: + deadline_str = ( + scheduling.deadline.astimezone().strftime("%m-%d %H:%M") + if hasattr(scheduling.deadline, "astimezone") + else str(scheduling.deadline) + ) + ui.code( f""" * Run ID: {run_data.run_id} @@ -585,6 +603,9 @@ def open_marimo(results_folder: Path, button: ui.button | None = None) -> None: - {run_data.statistics.item_system_error_count} system errors * Submitted: {submitted_at.strftime("%m-%d %H:%M")} ({run_data.submitted_by}) * Terminated: {terminated_at.strftime("%m-%d %H:%M") if terminated_at else "N/A"} ({duration_str}) + * Scheduling: + - Due Date: {due_date_str} + - Deadline: {deadline_str} * Error: {run_data.error_message or "N/A"} ({run_data.error_code or "N/A"}) """, language="markdown", diff --git a/src/aignostics/application/_service.py b/src/aignostics/application/_service.py index f28a9b284..98c4b930c 100644 --- a/src/aignostics/application/_service.py +++ b/src/aignostics/application/_service.py @@ -4,6 +4,7 @@ import re import time from collections.abc import Callable, Generator +from datetime import datetime from http import HTTPStatus from importlib.util import find_spec from pathlib import Path @@ -46,7 +47,9 @@ get_mime_type_for_artifact, get_supported_extensions_for_application, is_not_terminated_with_deadline_exceeded, + validate_deadline, validate_due_date, + validate_scheduling_constraints, ) has_qupath_extra = find_spec("ijson") @@ -569,7 +572,9 @@ def application_runs_static( # noqa: PLR0913, PLR0917 "item_succeeded_count": run.statistics.item_succeeded_count, "tags": run.custom_metadata.get("sdk", {}).get("tags", []) if run.custom_metadata else [], "is_not_terminated_with_deadline_exceeded": is_not_terminated_with_deadline_exceeded( - run.state, run.custom_metadata + run.state, + scheduling=getattr(run, "scheduling", None), + custom_metadata=run.custom_metadata, ), } for run in Service().application_runs( @@ -1004,10 +1009,15 @@ def application_run_submit( # noqa: PLR0913, PLR0917, PLR0912, C901, PLR0915 the application version ID is invalid or items invalid or due_date not ISO 8601 - or due_date not in the future. + or due_date not in the future + or deadline not ISO 8601 + or deadline not in the future + or due_date not before deadline. RuntimeError: If submitting the run failed unexpectedly. """ validate_due_date(due_date) + validate_deadline(deadline) + validate_scheduling_constraints(due_date, deadline) try: if custom_metadata is None: custom_metadata = {} @@ -1021,12 +1031,20 @@ def application_run_submit( # noqa: PLR0913, PLR0917, PLR0912, C901, PLR0915 sdk_metadata["workflow"] = { "onboard_to_aignostics_portal": onboard_to_aignostics_portal, } + # Build scheduling payload for the top-level request field (not custom_metadata) + scheduling = None if due_date or deadline: - sdk_metadata["scheduling"] = {} - if due_date: - sdk_metadata["scheduling"]["due_date"] = due_date - if deadline: - sdk_metadata["scheduling"]["deadline"] = deadline + from aignx.codegen.models import SchedulingRequest # noqa: PLC0415 + + def _parse_iso(value: str | None) -> datetime | None: + if value is None: + return None + return datetime.fromisoformat(value) + + scheduling = SchedulingRequest( + due_date=_parse_iso(due_date), + deadline=_parse_iso(deadline), + ) has_gpu_config = ( gpu_type or gpu_provisioning_mode or max_gpus_per_slide or flex_start_max_run_duration_minutes @@ -1069,6 +1087,7 @@ def application_run_submit( # noqa: PLR0913, PLR0917, PLR0912, C901, PLR0915 items=items, application_version=application_version, custom_metadata=custom_metadata, + scheduling=scheduling, ) except ValueError as e: message = f"Failed to submit application run for '{application_id}' (version: {application_version}): {e}" diff --git a/src/aignostics/application/_utils.py b/src/aignostics/application/_utils.py index 1538a230e..644f206f8 100644 --- a/src/aignostics/application/_utils.py +++ b/src/aignostics/application/_utils.py @@ -13,7 +13,7 @@ from datetime import UTC, datetime from enum import StrEnum from pathlib import Path -from typing import Any +from typing import Any, Protocol import humanize from loguru import logger @@ -39,39 +39,41 @@ RUN_FAILED_MESSAGE = "Failed to get status for run with ID '%s'" -def validate_due_date(due_date: str | None) -> None: - """Validate that due_date is in ISO 8601 format and in the future. +def _validate_scheduling_datetime(value: str | None, field_name: str) -> None: + """Validate that a scheduling datetime field is in ISO 8601 format and in the future. Args: - due_date (str | None): The datetime string to validate. + value (str | None): The datetime string to validate. + field_name (str): The name of the field (e.g. 'due_date', 'deadline') for error messages. Raises: ValueError: If - the format is invalid - or the due_date is not in the future. + the format is invalid, + the datetime is timezone-naive, + or the datetime is not in the future. """ - if due_date is None: + if value is None: return # Try parsing with fromisoformat (handles most ISO 8601 formats) try: # Handle 'Z' suffix by replacing with '+00:00' - normalized = due_date.replace("Z", "+00:00") + normalized = value.replace("Z", "+00:00") parsed_dt = datetime.fromisoformat(normalized) except (ValueError, TypeError) as e: message = ( - f"Invalid ISO 8601 format for due_date. " + f"Invalid ISO 8601 format for {field_name}. " f"Expected format like '2025-10-19T19:53:00+00:00' or '2025-10-19T19:53:00Z', " - f"but got: '{due_date}' (error: {e})" + f"but got: '{value}' (error: {e})" ) raise ValueError(message) from e # Ensure the datetime is timezone-aware (reject naive datetimes) if parsed_dt.tzinfo is None: message = ( - f"Invalid ISO 8601 format for due_date. " + f"Invalid ISO 8601 format for {field_name}. " f"Expected format with timezone like '2025-10-19T19:53:00+00:00' or '2025-10-19T19:53:00Z', " - f"but got: '{due_date}' (missing timezone information)" + f"but got: '{value}' (missing timezone information)" ) raise ValueError(message) @@ -79,13 +81,81 @@ def validate_due_date(due_date: str | None) -> None: now = datetime.now(UTC) if parsed_dt <= now: message = ( - f"due_date must be in the future. " - f"Got '{due_date}' ({parsed_dt.isoformat()}), " + f"{field_name} must be in the future. " + f"Got '{value}' ({parsed_dt.isoformat()}), " f"but current UTC time is {now.isoformat()}" ) raise ValueError(message) +def validate_due_date(due_date: str | None) -> None: + """Validate that due_date is in ISO 8601 format and in the future. + + Args: + due_date (str | None): The datetime string to validate. + + Raises: + ValueError: If + the format is invalid + or the due_date is not in the future. + """ + _validate_scheduling_datetime(due_date, "due_date") + + +def validate_deadline(deadline: str | None) -> None: + """Validate that deadline is in ISO 8601 format and in the future. + + Args: + deadline (str | None): The datetime string to validate. + + Raises: + ValueError: If + the format is invalid + or the deadline is not in the future. + """ + _validate_scheduling_datetime(deadline, "deadline") + + +def _parse_scheduling_datetime(value: str) -> datetime: + """Parse an ISO 8601 scheduling datetime string, handling 'Z' suffix. + + Args: + value (str): The datetime string to parse. + + Returns: + datetime: The parsed timezone-aware datetime. + """ + normalized = value.replace("Z", "+00:00") + return datetime.fromisoformat(normalized) + + +def validate_scheduling_constraints(due_date: str | None, deadline: str | None) -> None: + """Validate cross-field scheduling constraints. + + When both due_date and deadline are provided, due_date must be before deadline. + + Args: + due_date (str | None): The due date string (already individually validated). + deadline (str | None): The deadline string (already individually validated). + + Raises: + ValueError: If due_date is not before deadline. + """ + if due_date is None or deadline is None: + return + + parsed_due_date = _parse_scheduling_datetime(due_date) + parsed_deadline = _parse_scheduling_datetime(deadline) + + if parsed_due_date >= parsed_deadline: + message = ( + f"due_date must be before deadline. " + f"Got due_date='{due_date}' ({parsed_due_date.isoformat()}) " + f"and deadline='{deadline}' ({parsed_deadline.isoformat()})" + ) + raise ValueError(message) + + def validate_mappings(mappings: list[str] | None) -> None: """Validate mapping format for file metadata amendment. @@ -124,9 +194,16 @@ def validate_mappings(mappings: list[str] | None) -> None: raise ValueError(msg) from e +class _SchedulingLike(Protocol): + """Protocol for objects with an optional deadline attribute.""" + + deadline: datetime | None + + def is_not_terminated_with_deadline_exceeded( run_state: RunState, - custom_metadata: dict[str, Any] | None, + scheduling: _SchedulingLike | None = None, + custom_metadata: dict[str, Any] | None = None, ) -> bool | None: """Check if the run is not terminated and the deadline has been exceeded. @@ -135,7 +212,11 @@ def is_not_terminated_with_deadline_exceeded( Args: run_state (RunState): The current state of the run. - custom_metadata (dict[str, Any] | None): The custom metadata containing optional deadline information. + scheduling: The scheduling response object from the API (has .deadline attribute), + or None if no scheduling constraints were set. + custom_metadata (dict[str, Any] | None): Legacy fallback - the custom metadata containing + optional deadline information in sdk.scheduling.deadline. Used only when scheduling + response field is not available. Returns: bool | None: True if run is not terminated and deadline exceeded, @@ -146,16 +227,29 @@ def is_not_terminated_with_deadline_exceeded( if run_state == RunState.TERMINATED: return None - if not custom_metadata: - return None - - deadline_str = custom_metadata.get("sdk", {}).get("scheduling", {}).get("deadline") - if not deadline_str: + # Try the first-class scheduling response field first + deadline_value = None + if scheduling is not None: + deadline_value = getattr(scheduling, "deadline", None) + + # Fallback to custom_metadata for backward compatibility with older runs + if deadline_value is None and custom_metadata: + deadline_str = custom_metadata.get("sdk", {}).get("scheduling", {}).get("deadline") + if deadline_str: + try: + deadline_value = _parse_scheduling_datetime(deadline_str) + except (ValueError, TypeError, AttributeError): + return None + + if deadline_value is None: return None try: now = datetime.now(tz=UTC) - deadline_dt = datetime.fromisoformat(deadline_str) + if isinstance(deadline_value, datetime): + return now > deadline_value + # Handle string values + deadline_dt = _parse_scheduling_datetime(str(deadline_value)) return now > deadline_dt except (ValueError, TypeError, AttributeError): # Invalid deadline format, return None diff --git a/src/aignostics/platform/resources/runs.py b/src/aignostics/platform/resources/runs.py index 418f9924b..dc6dca005 100644 --- a/src/aignostics/platform/resources/runs.py +++ b/src/aignostics/platform/resources/runs.py @@ -23,6 +23,7 @@ RunCreationRequest, RunCreationResponse, RunState, + SchedulingRequest, ) from aignx.codegen.models import ( ItemResultReadResponse as ItemResultData, @@ -528,6 +529,7 @@ def submit( items: list[ItemCreationRequest], application_version: str | None = None, custom_metadata: dict[str, Any] | None = None, + scheduling: SchedulingRequest | None = None, ) -> Run: """Submit a new application run. @@ -537,6 +539,9 @@ def submit( application_version (str|None): The version of the application to use. If None, the latest version is used. custom_metadata (dict[str, Any] | None): Optional metadata to attach to the run. + scheduling (SchedulingRequest | None): Optional scheduling constraints for the run. + Supports 'due_date' (requested completion time, ISO 8601) and + 'deadline' (hard deadline, ISO 8601). Returns: Run: The submitted application run. @@ -557,6 +562,7 @@ def submit( version_number=application_version, custom_metadata=cast("dict[str, Any]", convert_to_json_serializable(custom_metadata)), items=items, + scheduling=scheduling, ) current_settings = settings() self._validate_input_items(payload) diff --git a/tests/aignostics/application/cli_pipeline_validation_test.py b/tests/aignostics/application/cli_pipeline_validation_test.py index a1863c48e..116b6158e 100644 --- a/tests/aignostics/application/cli_pipeline_validation_test.py +++ b/tests/aignostics/application/cli_pipeline_validation_test.py @@ -202,7 +202,7 @@ def test_cli_run_submit_succeeds_with_valid_pipeline_config(runner: CliRunner, t HETA_APPLICATION_ID, str(csv_path), "--deadline", - (datetime.now(tz=UTC) + timedelta(seconds=0)).isoformat(), + (datetime.now(tz=UTC) + timedelta(seconds=5)).isoformat(), "--gpu-type", "L4", "--gpu-provisioning-mode", diff --git a/tests/aignostics/application/gui_test.py b/tests/aignostics/application/gui_test.py index 5231bc6cd..b66d002a5 100644 --- a/tests/aignostics/application/gui_test.py +++ b/tests/aignostics/application/gui_test.py @@ -280,8 +280,10 @@ async def test_gui_download_dataset_via_application_to_run_cancel_to_find_back( await user.should_see("Hard Deadline") await user.should_see("The platform might cancel the run if not completed by this time.", retries=100) + time_due_date: ui.time = user.find(marker="TIME_DUE_DATE").elements.pop() + time_due_date.value = (datetime.now().astimezone() + timedelta(hours=6)).strftime("%Y-%m-%d %H:%M") time_deadline: ui.time = user.find(marker="TIME_DEADLINE").elements.pop() - time_deadline.value = (datetime.now().astimezone() + timedelta(minutes=10)).strftime("%Y-%m-%d %H:%M") + time_deadline.value = (datetime.now().astimezone() + timedelta(hours=12)).strftime("%Y-%m-%d %H:%M") user.find(marker="BUTTON_SCHEDULING_NEXT").click() await assert_notified(user, "Prepared upload UI.") diff --git a/tests/aignostics/application/service_test.py b/tests/aignostics/application/service_test.py index 6e4fcc210..d358554f9 100644 --- a/tests/aignostics/application/service_test.py +++ b/tests/aignostics/application/service_test.py @@ -7,7 +7,6 @@ from typer.testing import CliRunner from aignostics.application import Service as ApplicationService -from aignostics.application._utils import validate_due_date from aignostics.platform import NotFoundException, RunData, RunOutput from tests.constants_test import ( HETA_APPLICATION_ID, @@ -16,92 +15,6 @@ ) -@pytest.mark.unit -def test_validate_due_date_none() -> None: - """Test that None is accepted (optional parameter).""" - # Should not raise any exception - validate_due_date(None) - - -@pytest.mark.unit -def test_validate_due_date_valid_formats() -> None: - """Test that valid ISO 8601 formats in the future are accepted.""" - # Create a datetime 2 hours in the future - future_time = datetime.now(tz=UTC) + timedelta(hours=2) - - valid_formats = [ - future_time.isoformat(), # With timezone offset like +00:00 - future_time.strftime("%Y-%m-%dT%H:%M:%S") + "Z", # With Z suffix - future_time.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z", # With microseconds and Z - future_time.strftime("%Y-%m-%dT%H:%M:%S.%f%z"), # With microseconds and timezone - ] - - for time_str in valid_formats: - # Should not raise any exception - try: - validate_due_date(time_str) - except ValueError as e: - pytest.fail(f"Valid ISO 8601 format '{time_str}' was rejected: {e}") - - -@pytest.mark.unit -def test_validate_due_date_invalid_format() -> None: - """Test that invalid ISO 8601 formats are rejected.""" - invalid_formats = [ - "2025-10-19", # Date only - "19:53:00", # Time only - "2025/10/19 19:53:00", # Wrong separators - "2025-10-19 19:53:00", # Space instead of T - "not-a-date", # Completely invalid - "2025-13-45T25:70:99Z", # Invalid values - ] - - for time_str in invalid_formats: - with pytest.raises(ValueError, match=r"Invalid ISO 8601 format"): - validate_due_date(time_str) - - -@pytest.mark.unit -def test_validate_due_date_past_datetime() -> None: - """Test that datetimes in the past are rejected.""" - # Create a datetime 2 hours in the past - past_time = datetime.now(tz=UTC) - timedelta(hours=2) - - past_formats = [ - past_time.isoformat(), - past_time.strftime("%Y-%m-%dT%H:%M:%S") + "Z", - ] - - for time_str in past_formats: - with pytest.raises(ValueError, match=r"due_date must be in the future"): - validate_due_date(time_str) - - -@pytest.mark.unit -def test_validate_due_date_current_time() -> None: - """Test that current time (not future) is rejected.""" - # Get current time - should be rejected as it's not in the future - current_time = datetime.now(tz=UTC) - current_time_str = current_time.isoformat() - - with pytest.raises(ValueError, match=r"due_date must be in the future"): - validate_due_date(current_time_str) - - -@pytest.mark.unit -def test_validate_due_date_edge_case_one_second_future() -> None: - """Test that a datetime 1 second in the future is accepted.""" - # Create a datetime 1 second in the future - future_time = datetime.now(tz=UTC) + timedelta(seconds=1) - future_time_str = future_time.isoformat() - - # Should not raise any exception - try: - validate_due_date(future_time_str) - except ValueError as e: - pytest.fail(f"Future datetime '{future_time_str}' was rejected: {e}") - - @pytest.mark.e2e def test_application_version_valid_semver_formats(runner: CliRunner) -> None: """Test that valid semver formats are accepted.""" diff --git a/tests/aignostics/application/utils_test.py b/tests/aignostics/application/utils_test.py index 824dbe657..5466cf9e8 100644 --- a/tests/aignostics/application/utils_test.py +++ b/tests/aignostics/application/utils_test.py @@ -1,6 +1,6 @@ """Tests to verify the utility functions of the application module.""" -from datetime import UTC, datetime +from datetime import UTC, datetime, timedelta from pathlib import Path from typing import Any from unittest.mock import MagicMock, Mock, patch @@ -18,7 +18,10 @@ queue_position_string_from_run, read_metadata_csv_to_dict, retrieve_and_print_run_details, + validate_deadline, + validate_due_date, validate_mappings, + validate_scheduling_constraints, write_metadata_dict_to_csv, ) from aignostics.constants import ( @@ -41,6 +44,7 @@ TEST_MAPPING_TIFF_HE = ".*\\.tiff:staining_method=H&E" SUBMITTED_BY = "user@example.com" +_ISO_8601_FORMAT = "%Y-%m-%dT%H:%M:%S" def _make_statistics( # noqa: PLR0913 @@ -229,26 +233,98 @@ def test_application_run_status_to_str_terminated() -> None: # Tests for is_not_terminated_with_deadline_exceeded +# --- Tests using scheduling response object (new primary path) --- + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_object_terminated_run() -> None: + """Test that terminated runs always return None regardless of scheduling deadline.""" + past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) + scheduling = Mock(deadline=past_deadline) + result = is_not_terminated_with_deadline_exceeded(RunState.TERMINATED, scheduling=scheduling) + assert result is None + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_object_pending_future() -> None: + """Test that a pending run with scheduling deadline in the future returns False.""" + from datetime import timedelta + + future_deadline = datetime.now(tz=UTC) + timedelta(hours=1) + scheduling = Mock(deadline=future_deadline) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, scheduling=scheduling) + assert result is False + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_object_pending_past() -> None: + """Test that a pending run with scheduling deadline in the past returns True.""" + past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) + scheduling = Mock(deadline=past_deadline) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, scheduling=scheduling) + assert result is True + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_object_processing_past() -> None: + """Test that a processing run with scheduling deadline in the past returns True.""" + past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) + scheduling = Mock(deadline=past_deadline) + result = is_not_terminated_with_deadline_exceeded(RunState.PROCESSING, scheduling=scheduling) + assert result is True + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_object_none_deadline() -> None: + """Test that scheduling object with None deadline returns None.""" + scheduling = Mock(deadline=None) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, scheduling=scheduling) + assert result is None + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_none() -> None: + """Test that None scheduling and None metadata returns None.""" + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, scheduling=None, custom_metadata=None) + assert result is None + + +@pytest.mark.unit +def test_is_not_terminated_with_deadline_exceeded_scheduling_takes_precedence() -> None: + """Test that scheduling object takes precedence over custom_metadata.""" + from datetime import timedelta + + # scheduling says future (not exceeded), custom_metadata says past (exceeded) + future_deadline = datetime.now(tz=UTC) + timedelta(hours=1) + scheduling = Mock(deadline=future_deadline) + metadata = {"sdk": {"scheduling": {"deadline": "2020-01-01T12:00:00Z"}}} + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, scheduling=scheduling, custom_metadata=metadata) + assert result is False # scheduling wins + + +# --- Tests using custom_metadata fallback (backward compatibility for older runs) --- + + @pytest.mark.unit def test_is_not_terminated_with_deadline_exceeded_terminated_run() -> None: """Test that terminated runs always return None regardless of deadline.""" past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) metadata = {"sdk": {"scheduling": {"deadline": past_deadline.isoformat()}}} - result = is_not_terminated_with_deadline_exceeded(RunState.TERMINATED, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.TERMINATED, custom_metadata=metadata) assert result is None @pytest.mark.unit def test_is_not_terminated_with_deadline_exceeded_none_metadata() -> None: """Test that None metadata returns None.""" - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, None) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=None) assert result is None @pytest.mark.unit def test_is_not_terminated_with_deadline_exceeded_empty_metadata() -> None: """Test that empty metadata returns None.""" - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, {}) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata={}) assert result is None @@ -256,7 +332,7 @@ def test_is_not_terminated_with_deadline_exceeded_empty_metadata() -> None: def test_is_not_terminated_with_deadline_exceeded_no_sdk_key() -> None: """Test that metadata without 'sdk' key returns None.""" metadata = {"other_key": "other_value"} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -264,7 +340,7 @@ def test_is_not_terminated_with_deadline_exceeded_no_sdk_key() -> None: def test_is_not_terminated_with_deadline_exceeded_no_scheduling_key() -> None: """Test that metadata without 'scheduling' key returns None.""" metadata = {"sdk": {"other_key": "other_value"}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -272,19 +348,18 @@ def test_is_not_terminated_with_deadline_exceeded_no_scheduling_key() -> None: def test_is_not_terminated_with_deadline_exceeded_no_deadline_key() -> None: """Test that metadata without 'deadline' key returns None.""" metadata = {"sdk": {"scheduling": {"other_key": "other_value"}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @pytest.mark.unit def test_is_not_terminated_with_deadline_exceeded_pending_deadline_in_future() -> None: """Test that a pending run with deadline in the future returns False.""" - # Create a deadline 1 hour in the future from datetime import timedelta future_deadline = datetime.now(tz=UTC) + timedelta(hours=1) metadata = {"sdk": {"scheduling": {"deadline": future_deadline.isoformat()}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is False @@ -293,7 +368,7 @@ def test_is_not_terminated_with_deadline_exceeded_pending_deadline_in_past() -> """Test that a pending run with deadline in the past returns True.""" past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) metadata = {"sdk": {"scheduling": {"deadline": past_deadline.isoformat()}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is True @@ -302,7 +377,7 @@ def test_is_not_terminated_with_deadline_exceeded_processing_deadline_in_past() """Test that a processing run with deadline in the past returns True.""" past_deadline = datetime(2020, 1, 1, 12, 0, 0, tzinfo=UTC) metadata = {"sdk": {"scheduling": {"deadline": past_deadline.isoformat()}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PROCESSING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PROCESSING, custom_metadata=metadata) assert result is True @@ -310,7 +385,7 @@ def test_is_not_terminated_with_deadline_exceeded_processing_deadline_in_past() def test_is_not_terminated_with_deadline_exceeded_invalid_datetime_format() -> None: """Test that invalid datetime format returns None.""" metadata = {"sdk": {"scheduling": {"deadline": "not-a-valid-datetime"}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -319,7 +394,7 @@ def test_is_not_terminated_with_deadline_exceeded_deadline_with_z_suffix() -> No """Test that deadline with Z suffix (UTC) is handled correctly for pending run.""" past_deadline = "2020-01-01T12:00:00Z" metadata = {"sdk": {"scheduling": {"deadline": past_deadline}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is True @@ -328,7 +403,7 @@ def test_is_not_terminated_with_deadline_exceeded_deadline_with_timezone_offset( """Test that deadline with timezone offset is handled correctly for pending run.""" past_deadline = "2020-01-01T12:00:00+00:00" metadata = {"sdk": {"scheduling": {"deadline": past_deadline}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is True @@ -336,7 +411,7 @@ def test_is_not_terminated_with_deadline_exceeded_deadline_with_timezone_offset( def test_is_not_terminated_with_deadline_exceeded_deadline_empty_string() -> None: """Test that empty string deadline returns None.""" metadata = {"sdk": {"scheduling": {"deadline": ""}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -344,7 +419,7 @@ def test_is_not_terminated_with_deadline_exceeded_deadline_empty_string() -> Non def test_is_not_terminated_with_deadline_exceeded_deadline_none_value() -> None: """Test that None deadline value returns None.""" metadata = {"sdk": {"scheduling": {"deadline": None}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -352,7 +427,7 @@ def test_is_not_terminated_with_deadline_exceeded_deadline_none_value() -> None: def test_is_not_terminated_with_deadline_exceeded_deadline_numeric_value() -> None: """Test that numeric deadline value returns None.""" metadata = {"sdk": {"scheduling": {"deadline": 123456789}}} - result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, metadata) + result = is_not_terminated_with_deadline_exceeded(RunState.PENDING, custom_metadata=metadata) assert result is None @@ -902,3 +977,208 @@ def test_retrieve_and_print_run_details_default_is_detailed(mock_console: Mock) # Verify artifact details ARE shown in detailed mode assert "Download URL" in all_output assert "Artifact ID" in all_output + + +@pytest.mark.unit +def test_validate_due_date_none() -> None: + """Test that None is accepted (optional parameter).""" + # Should not raise any exception + validate_due_date(None) + + +@pytest.mark.unit +def test_validate_due_date_valid_formats() -> None: + """Test that valid ISO 8601 formats in the future are accepted.""" + # Create a datetime 2 hours in the future + future_time = datetime.now(tz=UTC) + timedelta(hours=2) + + valid_formats = [ + future_time.isoformat(), # With timezone offset like +00:00 + future_time.strftime(_ISO_8601_FORMAT) + "Z", # With Z suffix + future_time.strftime(f"{_ISO_8601_FORMAT}.%f") + "Z", # With microseconds and Z + future_time.strftime(f"{_ISO_8601_FORMAT}.%f") + "+00:00", # With microseconds and colon-separated offset + ] + + for time_str in valid_formats: + # Should not raise any exception + try: + validate_due_date(time_str) + except ValueError as e: + pytest.fail(f"Valid ISO 8601 format '{time_str}' was rejected: {e}") + + +@pytest.mark.unit +def test_validate_due_date_invalid_format() -> None: + """Test that invalid ISO 8601 formats are rejected.""" + invalid_formats = [ + "2025-10-19", # Date only + "19:53:00", # Time only + "2025/10/19 19:53:00", # Wrong separators + "2025-10-19 19:53:00", # Space instead of T + "not-a-date", # Completely invalid + "2025-13-45T25:70:99Z", # Invalid values + ] + + for time_str in invalid_formats: + with pytest.raises(ValueError, match=r"Invalid ISO 8601 format"): + validate_due_date(time_str) + + +@pytest.mark.unit +def test_validate_due_date_past_datetime() -> None: + """Test that datetimes in the past are rejected.""" + # Create a datetime 2 hours in the past + past_time = datetime.now(tz=UTC) - timedelta(hours=2) + + past_formats = [ + past_time.isoformat(), + past_time.strftime(_ISO_8601_FORMAT) + "Z", + ] + + for time_str in past_formats: + with pytest.raises(ValueError, match=r"due_date must be in the future"): + validate_due_date(time_str) + + +@pytest.mark.unit +def test_validate_due_date_current_time() -> None: + """Test that current time (not future) is rejected.""" + # Get current time - should be rejected as it's not in the future + current_time = datetime.now(tz=UTC) + current_time_str = current_time.isoformat() + + with pytest.raises(ValueError, match=r"due_date must be in the future"): + validate_due_date(current_time_str) + + +@pytest.mark.unit +def test_validate_due_date_edge_case_one_second_future() -> None: + """Test that a datetime 1 second in the future is accepted.""" + # Create a datetime 1 second in the future + future_time = datetime.now(tz=UTC) + timedelta(seconds=1) + future_time_str = future_time.isoformat() + + # Should not raise any exception + try: + validate_due_date(future_time_str) + except ValueError as e: + pytest.fail(f"Future datetime '{future_time_str}' was rejected: {e}") + + +@pytest.mark.unit +def test_validate_deadline_none() -> None: + """Test that None is accepted (optional parameter).""" + # Should not raise any exception + validate_deadline(None) + + +@pytest.mark.unit +def test_validate_deadline_valid_formats() -> None: + """Test that valid ISO 8601 formats in the future are accepted.""" + future_time = datetime.now(tz=UTC) + timedelta(hours=2) + + valid_formats = [ + future_time.isoformat(), + future_time.strftime(_ISO_8601_FORMAT) + "Z", + future_time.strftime(f"{_ISO_8601_FORMAT}.%f") + "Z", + future_time.strftime(f"{_ISO_8601_FORMAT}.%f") + "+00:00", # With microseconds and colon-separated offset + ] + + for time_str in valid_formats: + try: + validate_deadline(time_str) + except ValueError as e: + pytest.fail(f"Valid ISO 8601 format '{time_str}' was rejected: {e}") + + +@pytest.mark.unit +def test_validate_deadline_invalid_format() -> None: + """Test that invalid ISO 8601 formats are rejected.""" + invalid_formats = [ + "2025-10-19", + "19:53:00", + "2025/10/19 19:53:00", + "2025-10-19 19:53:00", + "not-a-date", + "2025-13-45T25:70:99Z", + ] + + for time_str in invalid_formats: + with pytest.raises(ValueError, match=r"Invalid ISO 8601 format"): + validate_deadline(time_str) + + +@pytest.mark.unit +def test_validate_deadline_past_datetime() -> None: + """Test that datetimes in the past are rejected.""" + past_time = datetime.now(tz=UTC) - timedelta(hours=2) + + past_formats = [ + past_time.isoformat(), + past_time.strftime(_ISO_8601_FORMAT) + "Z", + ] + + for time_str in past_formats: + with pytest.raises(ValueError, match=r"deadline must be in the future"): + validate_deadline(time_str) + + +@pytest.mark.unit +def test_validate_deadline_current_time() -> None: + """Test that current time (not future) is rejected.""" + current_time = datetime.now(tz=UTC) + current_time_str = current_time.isoformat() + + with pytest.raises(ValueError, match=r"deadline must be in the future"): + validate_deadline(current_time_str) + + +@pytest.mark.unit +def test_validate_deadline_edge_case_one_second_future() -> None: + """Test that a datetime 1 second in the future is accepted.""" + future_time = datetime.now(tz=UTC) + timedelta(seconds=1) + future_time_str = future_time.isoformat() + + try: + validate_deadline(future_time_str) + except ValueError as e: + pytest.fail(f"Future datetime '{future_time_str}' was rejected: {e}") + + +@pytest.mark.unit +def test_validate_scheduling_constraints_both_none() -> None: + """Test that both None values are accepted.""" + validate_scheduling_constraints(None, None) + + +@pytest.mark.unit +def test_validate_scheduling_constraints_one_none() -> None: + """Test that a single None value is accepted (no cross-field check needed).""" + future_time = (datetime.now(tz=UTC) + timedelta(hours=2)).isoformat() + validate_scheduling_constraints(future_time, None) + validate_scheduling_constraints(None, future_time) + + +@pytest.mark.unit +def test_validate_scheduling_constraints_due_date_before_deadline() -> None: + """Test that due_date before deadline is accepted.""" + due_date = (datetime.now(tz=UTC) + timedelta(hours=2)).isoformat() + deadline = (datetime.now(tz=UTC) + timedelta(hours=4)).isoformat() + validate_scheduling_constraints(due_date, deadline) + + +@pytest.mark.unit +def test_validate_scheduling_constraints_due_date_after_deadline() -> None: + """Test that due_date after deadline is rejected.""" + due_date = (datetime.now(tz=UTC) + timedelta(hours=4)).isoformat() + deadline = (datetime.now(tz=UTC) + timedelta(hours=2)).isoformat() + with pytest.raises(ValueError, match=r"due_date must be before deadline"): + validate_scheduling_constraints(due_date, deadline) + + +@pytest.mark.unit +def test_validate_scheduling_constraints_due_date_equal_deadline() -> None: + """Test that due_date equal to deadline is rejected.""" + same_time = (datetime.now(tz=UTC) + timedelta(hours=2)).isoformat() + with pytest.raises(ValueError, match=r"due_date must be before deadline"): + validate_scheduling_constraints(same_time, same_time) diff --git a/tests/aignostics/platform/e2e_test.py b/tests/aignostics/platform/e2e_test.py index 1beda7c0b..8560c42e6 100644 --- a/tests/aignostics/platform/e2e_test.py +++ b/tests/aignostics/platform/e2e_test.py @@ -20,6 +20,7 @@ RunOutput, RunState, ) +from aignx.codegen.models.run_read_response import RunReadResponse from loguru import logger from sentry_sdk import metrics @@ -280,13 +281,13 @@ def _submit_and_validate( # noqa: PLR0913, PLR0917 logger.trace(f"Submitting application run for {application_id} version {application_version}") client = platform.Client() + scheduling = { + "due_date": (datetime.now(tz=UTC) + timedelta(seconds=due_date_seconds)).isoformat(), + "deadline": deadline.isoformat(), + } custom_metadata = { "sdk": { "tags": tags or set(), - "scheduling": { - "due_date": (datetime.now(tz=UTC) + timedelta(seconds=due_date_seconds)).isoformat(), - "deadline": deadline.isoformat(), - }, "pipeline": { "gpu": { "gpu_type": PIPELINE_GPU_TYPE, @@ -310,9 +311,10 @@ def _submit_and_validate( # noqa: PLR0913, PLR0917 application_version=application_version, items=payload, custom_metadata=custom_metadata, + scheduling=scheduling, ) - # Let's validate we can fiond the run by id + # Let's validate we can find the run by id details = run.details() assert details.run_id == run.run_id, "Run ID mismatch after submission" assert details.application_id == application_id, "Application ID mismatch after submission" @@ -381,6 +383,21 @@ def _submit_and_wait( # noqa: PLR0913, PLR0917 _validate_output(run, Path(temp_dir), checksum_attribute_key) +def _resolve_run_deadline(details: RunReadResponse, sdk_metadata: RunSdkMetadata) -> datetime: + """Extract the run deadline from the API scheduling field, falling back to custom_metadata. + + Raises: + ValueError: If no deadline is found in either the API scheduling field or sdk_metadata.scheduling. + """ + if getattr(details, "scheduling", None) is not None and getattr(details.scheduling, "deadline", None) is not None: + deadline = details.scheduling.deadline + return deadline if isinstance(deadline, datetime) else datetime.fromisoformat(str(deadline)) + if getattr(sdk_metadata, "scheduling", None) is None or getattr(sdk_metadata.scheduling, "deadline", None) is None: + msg = "No deadline found in API scheduling field or sdk_metadata.scheduling" + raise ValueError(msg) + return datetime.fromisoformat(str(sdk_metadata.scheduling.deadline)) + + def _find_and_validate( application_id: str, application_version: str, @@ -430,9 +447,8 @@ def _find_and_validate( sdk_metadata = RunSdkMetadata.model_validate(details.custom_metadata.get("sdk", {})) logger.trace(sdk_metadata.model_dump_json(indent=2)) print(sdk_metadata.model_dump_json(indent=2)) - allowed_duration = datetime.fromisoformat(sdk_metadata.scheduling.deadline) - datetime.fromisoformat( - sdk_metadata.submission.date - ) + run_deadline = _resolve_run_deadline(details, sdk_metadata) + allowed_duration = run_deadline - datetime.fromisoformat(sdk_metadata.submission.date) allowed_hours = round(allowed_duration.total_seconds() / (60 * 60)) deadline_met = details.state is RunState.TERMINATED metrics_run_attributes = { @@ -441,7 +457,7 @@ def _find_and_validate( "application_version": application_version, "allowed_hours": allowed_hours, "submitted_at": sdk_metadata.submission.date, - "deadline": sdk_metadata.scheduling.deadline, + "deadline": run_deadline.isoformat(), "state": details.state.value, "error_message": details.error_message, "error_code": details.error_code, @@ -454,13 +470,13 @@ def _find_and_validate( value=1, attributes=metrics_run_attributes, ) - completed_duration_seconds = ( - details.terminated_at - datetime.fromisoformat(sdk_metadata.submission.date) - ).total_seconds() - message = f"Run completed in {completed_duration_seconds} seconds" - logger.trace(message) - print(message) if details.terminated_at: + completed_duration_seconds = ( + details.terminated_at - datetime.fromisoformat(sdk_metadata.submission.date) + ).total_seconds() + message = f"Run completed in {completed_duration_seconds} seconds" + logger.trace(message) + print(message) metrics.distribution( name="aignostics.platform.tests.run.completed.duration", value=completed_duration_seconds,