From 83d8a4732f6f7e4647539b5636eab533c1414850 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Wed, 6 May 2026 19:44:35 -0700 Subject: [PATCH 1/4] Emit SDK from TypeSpec and apply post-emitter fixes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../SKILL.md | 2 +- sdk/ai/azure-ai-projects/CHANGELOG.md | 4 + .../azure-ai-projects/apiview-properties.json | 4 +- .../azure/ai/projects/_utils/utils.py | 1 + .../ai/projects/aio/operations/_operations.py | 426 +++++++++++++- .../ai/projects/aio/operations/_patch.py | 2 +- .../azure/ai/projects/models/__init__.py | 8 +- .../azure/ai/projects/models/_enums.py | 15 +- .../azure/ai/projects/models/_models.py | 174 ++++-- .../ai/projects/operations/_operations.py | 527 +++++++++++++++++- sdk/ai/azure-ai-projects/tsp-location.yaml | 2 +- 11 files changed, 1100 insertions(+), 65 deletions(-) diff --git a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md index e69970823d4a..4f95c93e92bd 100644 --- a/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md +++ b/sdk/ai/azure-ai-projects/.github/skills/azure-ai-projects-emit-from-typespec/SKILL.md @@ -30,7 +30,7 @@ Ask the user the following questions **one at a time**, waiting for each answer Ask the user to choose **one** of the following two options for the target topic branch: -1. **Create a new topic branch (with default branch name)** – Create a new topic branch for the emitted changes. If selected, this default branch name will be used "/", where `github-userid` is the user's GitHub ID and `DD-MM-HHMM` is the current date-time using date, month, hour and minute. For example, if the GitHub ID is "dargilco" and the current date and time is May 1st, 2026 at 8:13am, the default branch name would be `dargilco/emit-from-typespec-01-05-0813`. This should be the default option. If you press enter without typing anything, this option will be selected. +1. **Create a new topic branch (with default branch name)** – Create a new topic branch for the emitted changes. If selected, this default branch name will be used "/", where `github-userid` is the user's GitHub ID and `DD-MM-HHMM` is the current date-time using date, month, hour and minute. For example, if the GitHub ID is "dargilco" and the current date and time is May 1st, 2026 at 8:13am, the default branch name would be `dargilco/emit-from-typespec-01-05-0813`. This should be the default option, and the default branch name should be displayed. If you press enter without typing anything, this option will be selected. 2. **Create a new topic branch (branch name given by user)** - Ask the user for the branch name. Mention that a common format is "/". If the user enters a branch name `feature/azure-ai-projects/2.2.0` then stop and report that they cannot emit directly to the current feature branch. diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 3864773b2f2a..68dc0d1999be 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -10,6 +10,8 @@ * New `.beta.datasets` sub-client with data generation job operations: `create_generation_job`, `get_generation_job`, `list_generation_jobs`, `cancel_generation_job`, `delete_generation_job`. * New read-only property `content_hash` on `CodeConfiguration`, returning the SHA-256 hex digest of the uploaded code zip. * New evaluator generation job operations on `.beta.evaluators`: `create_generation_job`, `get_generation_job`, `list_generation_jobs`, `cancel_generation_job`, `delete_generation_job`. +* New methods on `.beta.agents` sub-client for code-based hosted agents: `update_agent_from_code()`, `create_agent_version_from_code()`, `download_agent_version_code()`, `download_agent_code()`. +* New enum `CodeDependencyResolution` with values `BUNDLED` and `REMOTE_BUILD`, controlling how package dependencies are resolved at deployment time. ### Breaking Changes @@ -18,6 +20,8 @@ Breaking changes in beta operations: * Required property `isolation_key_source` removed from class `EntraAuthorizationScheme`. * Required keyword argument `isolation_key` removed from `.beta.agents.create_session()` and `.beta.agents.delete_session()` methods. * Argument `body` in methods `.beta.evaluation_taxonomies.create()` and `.beta.evaluation_taxonomies.update()` renamed to `taxonomy`. +* Removed enum value `DataGenerationJobType.TASK`. +* Removed model `TaskDataGenerationJobOptions`. ### Bugs Fixed diff --git a/sdk/ai/azure-ai-projects/apiview-properties.json b/sdk/ai/azure-ai-projects/apiview-properties.json index c9c61ccf5141..0700d1af4fcc 100644 --- a/sdk/ai/azure-ai-projects/apiview-properties.json +++ b/sdk/ai/azure-ai-projects/apiview-properties.json @@ -85,6 +85,8 @@ "azure.ai.projects.models.EvaluationRuleAction": "Azure.AI.Projects.EvaluationRuleAction", "azure.ai.projects.models.ContinuousEvaluationRuleAction": "Azure.AI.Projects.ContinuousEvaluationRuleAction", "azure.ai.projects.models.CosmosDBIndex": "Azure.AI.Projects.CosmosDBIndex", + "azure.ai.projects.models.CreateAgentVersionFromCodeContent": "Azure.AI.Projects.CreateAgentVersionFromCodeContent", + "azure.ai.projects.models.CreateAgentVersionFromCodeRequest": "Azure.AI.Projects.CreateAgentVersionFromCodeRequest", "azure.ai.projects.models.Trigger": "Azure.AI.Projects.Trigger", "azure.ai.projects.models.CronTrigger": "Azure.AI.Projects.CronTrigger", "azure.ai.projects.models.CustomCredential": "Azure.AI.Projects.CustomCredential", @@ -239,7 +241,6 @@ "azure.ai.projects.models.SpecificFunctionShellParam": "OpenAI.SpecificFunctionShellParam", "azure.ai.projects.models.StructuredInputDefinition": "Azure.AI.Projects.StructuredInputDefinition", "azure.ai.projects.models.StructuredOutputDefinition": "Azure.AI.Projects.StructuredOutputDefinition", - "azure.ai.projects.models.TaskDataGenerationJobOptions": "Azure.AI.Projects.TaskDataGenerationJobOptions", "azure.ai.projects.models.TaxonomyCategory": "Azure.AI.Projects.TaxonomyCategory", "azure.ai.projects.models.TaxonomySubCategory": "Azure.AI.Projects.TaxonomySubCategory", "azure.ai.projects.models.TelemetryConfig": "Azure.AI.Projects.TelemetryConfig", @@ -298,6 +299,7 @@ "azure.ai.projects.models.ContainerSkillType": "OpenAI.ContainerSkillType", "azure.ai.projects.models.SearchContextSize": "OpenAI.SearchContextSize", "azure.ai.projects.models.AgentProtocol": "Azure.AI.Projects.AgentProtocol", + "azure.ai.projects.models.CodeDependencyResolution": "Azure.AI.Projects.CodeDependencyResolution", "azure.ai.projects.models.TelemetryEndpointKind": "Azure.AI.Projects.TelemetryEndpointKind", "azure.ai.projects.models.TelemetryDataKind": "Azure.AI.Projects.TelemetryDataKind", "azure.ai.projects.models.TelemetryEndpointAuthType": "Azure.AI.Projects.TelemetryEndpointAuthType", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py index 707b7d8fac75..bd821750f4c6 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/_utils/utils.py @@ -10,6 +10,7 @@ from .._utils.model_base import Model, SdkJSONEncoder + # file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)` FileContent = Union[str, bytes, IO[str], IO[bytes]] diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py index 92542b738815..699644e8a17d 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py @@ -33,8 +33,9 @@ from azure.core.utils import case_insensitive_dict from ... import models as _models -from ..._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from ..._utils.model_base import Model as _Model, SdkJSONEncoder, _deserialize, _failsafe_deserialize from ..._utils.serialization import Deserializer, Serializer +from ..._utils.utils import prepare_multipart_form_data from ...operations._operations import ( build_agents_create_version_from_manifest_request, build_agents_create_version_request, @@ -44,15 +45,19 @@ build_agents_get_version_request, build_agents_list_request, build_agents_list_versions_request, + build_beta_agents_create_agent_version_from_code_request, build_beta_agents_create_session_request, build_beta_agents_delete_session_file_request, build_beta_agents_delete_session_request, + build_beta_agents_download_agent_code_request, + build_beta_agents_download_agent_version_code_request, build_beta_agents_download_session_file_request, build_beta_agents_get_session_files_request, build_beta_agents_get_session_log_stream_request, build_beta_agents_get_session_request, build_beta_agents_list_sessions_request, build_beta_agents_patch_agent_details_request, + build_beta_agents_update_agent_from_code_request, build_beta_agents_upload_session_file_request, build_beta_datasets_cancel_generation_job_request, build_beta_datasets_create_generation_job_request, @@ -3023,6 +3028,150 @@ def __init__(self, *args, **kwargs) -> None: self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @overload + async def update_agent_from_code( + self, agent_name: str, body: _models.CreateAgentVersionFromCodeContent, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update_agent_from_code( + self, agent_name: str, body: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def update_agent_from_code( + self, + agent_name: str, + body: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + + _body = body.as_dict() if isinstance(body, _Model) else body + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_update_agent_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + @overload async def patch_agent_details( self, @@ -3177,6 +3326,281 @@ async def patch_agent_details( return deserialized # type: ignore + @overload + async def create_agent_version_from_code( + self, agent_name: str, body: _models.CreateAgentVersionFromCodeContent, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_agent_version_from_code( + self, agent_name: str, body: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_agent_version_from_code( + self, + agent_name: str, + body: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + _body = body.as_dict() if isinstance(body, _Model) else body + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_create_agent_version_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def download_agent_version_code( + self, agent_name: str, agent_version: str, **kwargs: Any + ) -> AsyncIterator[bytes]: + """Download the code zip for a specific version of a code-based hosted agent. Returns the + previously-uploaded zip (``application/zip``). The SHA-256 digest of the returned bytes matches + the ``content_hash`` on the agent version's ``code_configuration``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_version: The version of the agent whose code zip should be downloaded. Required. + :type agent_version: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_agent_version_code_request( + agent_name=agent_name, + agent_version=agent_version, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def download_agent_code(self, agent_name: str, **kwargs: Any) -> AsyncIterator[bytes]: + """Download the code zip for the latest version of a code-based hosted agent. Returns the + previously-uploaded zip (``application/zip``). The SHA-256 digest of the returned bytes matches + the ``content_hash`` on the latest version's ``code_configuration``. + + :param agent_name: The name of the agent whose latest-version code zip should be downloaded. + Required. + :type agent_name: str + :return: AsyncIterator[bytes] + :rtype: AsyncIterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[AsyncIterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_agent_code_request( + agent_name=agent_name, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = await self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + await response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + @overload async def create_session( self, diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py index 48459efd771e..3c3e527771b4 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch.py @@ -59,7 +59,7 @@ class BetaOperations(GeneratedBetaOperations): skills: BetaSkillsOperations """:class:`~azure.ai.projects.aio.operations.BetaSkillsOperations` operations""" datasets: BetaDatasetsOperations - """:class:`~azure.ai.projects.aio.operations.BetaDatasetsOperations` operations""" + """:class:`~azure.ai.projects.aio.operations.BetaDatasetsOperations` operations""" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py index b4e40ca68195..e2766099b650 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py @@ -85,6 +85,8 @@ ContainerSkill, ContinuousEvaluationRuleAction, CosmosDBIndex, + CreateAgentVersionFromCodeContent, + CreateAgentVersionFromCodeRequest, CronTrigger, CustomCredential, CustomGrammarFormatParam, @@ -246,7 +248,6 @@ StructuredOutputDefinition, Target, TargetConfig, - TaskDataGenerationJobOptions, TaxonomyCategory, TaxonomySubCategory, TelemetryConfig, @@ -307,6 +308,7 @@ AgentVersionStatus, AttackStrategy, AzureAISearchQueryType, + CodeDependencyResolution, ComputerEnvironment, ConnectionType, ContainerMemoryLimit, @@ -443,6 +445,8 @@ "ContainerSkill", "ContinuousEvaluationRuleAction", "CosmosDBIndex", + "CreateAgentVersionFromCodeContent", + "CreateAgentVersionFromCodeRequest", "CronTrigger", "CustomCredential", "CustomGrammarFormatParam", @@ -604,7 +608,6 @@ "StructuredOutputDefinition", "Target", "TargetConfig", - "TaskDataGenerationJobOptions", "TaxonomyCategory", "TaxonomySubCategory", "TelemetryConfig", @@ -662,6 +665,7 @@ "AgentVersionStatus", "AttackStrategy", "AzureAISearchQueryType", + "CodeDependencyResolution", "ComputerEnvironment", "ConnectionType", "ContainerMemoryLimit", diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py index a709458309bf..40648951bbda 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py @@ -21,6 +21,8 @@ class _AgentDefinitionOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): """CONTAINER_AGENTS_V1_PREVIEW.""" AGENT_ENDPOINT_V1_PREVIEW = "AgentEndpoints=V1Preview" """AGENT_ENDPOINT_V1_PREVIEW.""" + CODE_AGENTS_V1_PREVIEW = "CodeAgents=V1Preview" + """CODE_AGENTS_V1_PREVIEW.""" class _FoundryFeaturesOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): @@ -234,6 +236,16 @@ class AzureAISearchQueryType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Query type ``vector_semantic_hybrid``.""" +class CodeDependencyResolution(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """How package dependencies are resolved at deployment time for a code-based hosted agent.""" + + BUNDLED = "bundled" + """The caller has bundled all dependencies into the uploaded zip; the service performs no remote + build.""" + REMOTE_BUILD = "remote_build" + """The service builds dependencies remotely from the manifest included in the uploaded zip.""" + + class ComputerEnvironment(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Type of ComputerEnvironment.""" @@ -375,9 +387,6 @@ class DataGenerationJobType(str, Enum, metaclass=CaseInsensitiveEnumMeta): """Single turn query and response from agent traces.""" TOOL_USE = "tool_use" """Tool calling conversation between user and agent.""" - TASK = "task" - """Task helps in providing a scenario description for generating multi turn conversation between - user and agent.""" class DatasetType(str, Enum, metaclass=CaseInsensitiveEnumMeta): diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py index f0544466410e..ee95d39675aa 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py @@ -12,6 +12,7 @@ from typing import Any, Literal, Mapping, Optional, TYPE_CHECKING, Union, overload from .._utils.model_base import Model as _Model, rest_discriminator, rest_field +from .._utils.utils import FileType from ._enums import ( AgentBlueprintReferenceType, AgentEndpointAuthorizationSchemeType, @@ -2926,6 +2927,12 @@ class CodeConfiguration(_Model): :vartype runtime: str :ivar entry_point: The entry point command and arguments for the code execution. Required. :vartype entry_point: list[str] + :ivar dependency_resolution: How package dependencies are resolved at deployment time. Defaults + to ``bundled``, where the caller bundles all dependencies into the uploaded zip and the service + performs no remote build. ``remote_build`` instructs the service to build dependencies remotely + from the manifest included in the uploaded zip. Required. Known values are: "bundled" and + "remote_build". + :vartype dependency_resolution: str or ~azure.ai.projects.models.CodeDependencyResolution :ivar content_hash: The SHA-256 hex digest of the uploaded code zip. Set by the service from the ``x-ms-code-zip-sha256`` request header; read-only in responses and never accepted in request payloads. @@ -2937,6 +2944,13 @@ class CodeConfiguration(_Model): Required.""" entry_point: list[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) """The entry point command and arguments for the code execution. Required.""" + dependency_resolution: Union[str, "_models.CodeDependencyResolution"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """How package dependencies are resolved at deployment time. Defaults to ``bundled``, where the + caller bundles all dependencies into the uploaded zip and the service performs no remote build. + ``remote_build`` instructs the service to build dependencies remotely from the manifest + included in the uploaded zip. Required. Known values are: \"bundled\" and \"remote_build\".""" content_hash: Optional[str] = rest_field(visibility=["read"]) """The SHA-256 hex digest of the uploaded code zip. Set by the service from the ``x-ms-code-zip-sha256`` request header; read-only in responses and never accepted in request @@ -2948,6 +2962,7 @@ def __init__( *, runtime: str, entry_point: list[str], + dependency_resolution: Union[str, "_models.CodeDependencyResolution"], ) -> None: ... @overload @@ -3633,6 +3648,97 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.type = IndexType.COSMOS_DB # type: ignore +class CreateAgentVersionFromCodeContent(_Model): + """Multipart request body for updating or versioning a code-based agent (POST /agents/{name} and + POST /agents/{name}/versions). + + :ivar metadata: JSON metadata including description and hosted definition. Required. + :vartype metadata: ~azure.ai.projects.models.CreateAgentVersionFromCodeRequest + :ivar code: The code zip file (max 250 MB). Required. + :vartype code: ~azure.ai.projects._utils.utils.FileType + """ + + metadata: "_models.CreateAgentVersionFromCodeRequest" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """JSON metadata including description and hosted definition. Required.""" + code: FileType = rest_field( + visibility=["read", "create", "update", "delete", "query"], is_multipart_file_input=True + ) + """The code zip file (max 250 MB). Required.""" + + @overload + def __init__( + self, + *, + metadata: "_models.CreateAgentVersionFromCodeRequest", + code: FileType, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + +class CreateAgentVersionFromCodeRequest(_Model): + """JSON metadata for code-based agent operations (create, update, create version). The agent name + comes from the URL path parameter or the ``x-ms-agent-name`` header, so it is not included in + this model. The content hash (SHA-256 of the zip) is carried in the ``x-ms-code-zip-sha256`` + header. + + :ivar description: A human-readable description of the agent. + :vartype description: str + :ivar metadata: Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters. + :vartype metadata: dict[str, str] + :ivar definition: The hosted agent definition including code_configuration (runtime, + entry_point), cpu, memory, and protocol_versions. Required. + :vartype definition: ~azure.ai.projects.models.HostedAgentDefinition + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the agent.""" + metadata: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Set of 16 key-value pairs that can be attached to an object. This can be + useful for storing additional information about the object in a structured + format, and querying for objects via API or the dashboard. + + Keys are strings with a maximum length of 64 characters. Values are strings + with a maximum length of 512 characters.""" + definition: "_models.HostedAgentDefinition" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The hosted agent definition including code_configuration (runtime, entry_point), cpu, memory, + and protocol_versions. Required.""" + + @overload + def __init__( + self, + *, + definition: "_models.HostedAgentDefinition", + description: Optional[str] = None, + metadata: Optional[dict[str, str]] = None, + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: + """ + :param mapping: raw JSON to initialize the model. + :type mapping: Mapping[str, Any] + """ + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + + class Trigger(_Model): """Base model for Trigger of the schedule. @@ -4073,11 +4179,11 @@ class DataGenerationJobOptions(_Model): """Options for managing data generation jobs. You probably want to use the sub-classes and not this class directly. Known sub-classes are: - SimpleQnADataGenerationJobOptions, TaskDataGenerationJobOptions, - ToolUseFineTuningDataGenerationJobOptions, TracesDataGenerationJobOptions + SimpleQnADataGenerationJobOptions, ToolUseFineTuningDataGenerationJobOptions, + TracesDataGenerationJobOptions :ivar type: The data generation job type. Required. Known values are: "simple_qna", "traces", - "tool_use", and "task". + and "tool_use". :vartype type: str or ~azure.ai.projects.models.DataGenerationJobType :ivar max_samples: Maximum number of samples to generate. Required. :vartype max_samples: int @@ -4090,8 +4196,8 @@ class DataGenerationJobOptions(_Model): __mapping__: dict[str, _Model] = {} type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) - """The data generation job type. Required. Known values are: \"simple_qna\", \"traces\", - \"tool_use\", and \"task\".""" + """The data generation job type. Required. Known values are: \"simple_qna\", \"traces\", and + \"tool_use\".""" max_samples: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) """Maximum number of samples to generate. Required.""" train_split: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) @@ -4229,20 +4335,20 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: class DataGenerationTokenUsage(_Model): """Token usage information for a data generation job. - :ivar prompt_tokens: The number of prompt tokens used. + :ivar prompt_tokens: The number of prompt tokens used. Required. :vartype prompt_tokens: int - :ivar completion_tokens: The number of completion tokens generated. + :ivar completion_tokens: The number of completion tokens generated. Required. :vartype completion_tokens: int - :ivar total_tokens: Total number of tokens used. + :ivar total_tokens: Total number of tokens used. Required. :vartype total_tokens: int """ - prompt_tokens: Optional[int] = rest_field(visibility=["read"]) - """The number of prompt tokens used.""" - completion_tokens: Optional[int] = rest_field(visibility=["read"]) - """The number of completion tokens generated.""" - total_tokens: Optional[int] = rest_field(visibility=["read"]) - """Total number of tokens used.""" + prompt_tokens: int = rest_field(visibility=["read"]) + """The number of prompt tokens used. Required.""" + completion_tokens: int = rest_field(visibility=["read"]) + """The number of completion tokens generated. Required.""" + total_tokens: int = rest_field(visibility=["read"]) + """Total number of tokens used. Required.""" class DatasetCredential(_Model): @@ -10571,46 +10677,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) -class TaskDataGenerationJobOptions(DataGenerationJobOptions, discriminator="task"): - """The options for a data generation job with Task type. - - :ivar max_samples: Maximum number of samples to generate. Required. - :vartype max_samples: int - :ivar train_split: The proportion of the generated data to be used for training when the data - is used for fine-tuning. The rest will be used for validation. Value should be between 0 and 1. - :vartype train_split: float - :ivar model_options: The LLM model options. - :vartype model_options: ~azure.ai.projects.models.DataGenerationModelOptions - :ivar type: The data generation job type, which is Task for this model. Required. Task helps in - providing a scenario description for generating multi turn conversation between user and agent. - :vartype type: str or ~azure.ai.projects.models.TASK - """ - - type: Literal[DataGenerationJobType.TASK] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore - """The data generation job type, which is Task for this model. Required. Task helps in providing a - scenario description for generating multi turn conversation between user and agent.""" - - @overload - def __init__( - self, - *, - max_samples: int, - train_split: Optional[float] = None, - model_options: Optional["_models.DataGenerationModelOptions"] = None, - ) -> None: ... - - @overload - def __init__(self, mapping: Mapping[str, Any]) -> None: - """ - :param mapping: raw JSON to initialize the model. - :type mapping: Mapping[str, Any] - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self.type = DataGenerationJobType.TASK # type: ignore - - class TaxonomyCategory(_Model): """Taxonomy category definition. diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py index edcfcf622ab1..a0c8c986ba56 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/operations/_operations.py @@ -35,8 +35,9 @@ from .. import models as _models from .._configuration import AIProjectClientConfiguration -from .._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from .._utils.model_base import Model as _Model, SdkJSONEncoder, _deserialize, _failsafe_deserialize from .._utils.serialization import Deserializer, Serializer +from .._utils.utils import prepare_multipart_form_data JSON = MutableMapping[str, Any] _Unset: Any = object() @@ -797,6 +798,33 @@ def build_indexes_create_or_update_request(name: str, version: str, **kwargs: An return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) +def build_beta_agents_update_agent_from_code_request( # pylint: disable=name-too-long + agent_name: str, *, code_zip_sha256: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/agents/{agent_name}" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["x-ms-code-zip-sha256"] = _SERIALIZER.header("code_zip_sha256", code_zip_sha256, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + def build_beta_agents_patch_agent_details_request( # pylint: disable=name-too-long agent_name: str, **kwargs: Any ) -> HttpRequest: @@ -826,6 +854,86 @@ def build_beta_agents_patch_agent_details_request( # pylint: disable=name-too-l return HttpRequest(method="PATCH", url=_url, params=_params, headers=_headers, **kwargs) +def build_beta_agents_create_agent_version_from_code_request( # pylint: disable=name-too-long + agent_name: str, *, code_zip_sha256: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/agents/{agent_name}/versions" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["x-ms-code-zip-sha256"] = _SERIALIZER.header("code_zip_sha256", code_zip_sha256, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_agents_download_agent_version_code_request( # pylint: disable=name-too-long + agent_name: str, agent_version: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/zip") + + # Construct URL + _url = "/agents/{agent_name}/versions/{agent_version}/code:download" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "agent_version": _SERIALIZER.url("agent_version", agent_version, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_beta_agents_download_agent_code_request( # pylint: disable=name-too-long + agent_name: str, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/zip") + + # Construct URL + _url = "/agents/{agent_name}/code:download" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + def build_beta_agents_create_session_request(agent_name: str, **kwargs: Any) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) @@ -5451,6 +5559,150 @@ def __init__(self, *args, **kwargs) -> None: self._serialize: Serializer = input_args.pop(0) if input_args else kwargs.pop("serializer") self._deserialize: Deserializer = input_args.pop(0) if input_args else kwargs.pop("deserializer") + @overload + def update_agent_from_code( + self, agent_name: str, body: _models.CreateAgentVersionFromCodeContent, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update_agent_from_code( + self, agent_name: str, body: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def update_agent_from_code( + self, + agent_name: str, + body: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentDetails: + """Updates a code-based agent by uploading new code and creating a new version. If the code and + definition are unchanged (matched by x-ms-code-zip-sha256 header), returns the existing + version. The request body is multipart/form-data with a JSON metadata part and a binary code + part (part order is irrelevant). Maximum upload size is 250 MB. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentDetails. The AgentDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentDetails] = kwargs.pop("cls", None) + + _body = body.as_dict() if isinstance(body, _Model) else body + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_update_agent_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + @overload def patch_agent_details( self, @@ -5605,6 +5857,279 @@ def patch_agent_details( return deserialized # type: ignore + @overload + def create_agent_version_from_code( + self, agent_name: str, body: _models.CreateAgentVersionFromCodeContent, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_agent_version_from_code( + self, agent_name: str, body: JSON, *, code_zip_sha256: str, **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Required. + :type body: JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_agent_version_from_code( + self, + agent_name: str, + body: Union[_models.CreateAgentVersionFromCodeContent, JSON], + *, + code_zip_sha256: str, + **kwargs: Any + ) -> _models.AgentVersionDetails: + """create_agent_version_from_code. + + :param agent_name: The unique name that identifies the agent. Name can be used to + retrieve/update/delete the agent. + + * Must start and end with alphanumeric characters, + * Can contain hyphens in the middle + * Must not exceed 63 characters. Required. + :type agent_name: str + :param body: Is either a CreateAgentVersionFromCodeContent type or a JSON type. Required. + :type body: ~azure.ai.projects.models.CreateAgentVersionFromCodeContent or JSON + :keyword code_zip_sha256: SHA-256 hex digest of the uploaded code zip. Used for change + detection (dedup) and integrity verification. Required. + :paramtype code_zip_sha256: str + :return: AgentVersionDetails. The AgentVersionDetails is compatible with MutableMapping + :rtype: ~azure.ai.projects.models.AgentVersionDetails + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[_models.AgentVersionDetails] = kwargs.pop("cls", None) + + _body = body.as_dict() if isinstance(body, _Model) else body + _file_fields: list[str] = ["code"] + _data_fields: list[str] = ["metadata"] + _files = prepare_multipart_form_data(_body, _file_fields, _data_fields) + + _request = build_beta_agents_create_agent_version_from_code_request( + agent_name=agent_name, + code_zip_sha256=code_zip_sha256, + api_version=self._config.api_version, + files=_files, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", False) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + if _stream: + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + else: + deserialized = _deserialize(_models.AgentVersionDetails, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def download_agent_version_code(self, agent_name: str, agent_version: str, **kwargs: Any) -> Iterator[bytes]: + """Download the code zip for a specific version of a code-based hosted agent. Returns the + previously-uploaded zip (``application/zip``). The SHA-256 digest of the returned bytes matches + the ``content_hash`` on the agent version's ``code_configuration``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param agent_version: The version of the agent whose code zip should be downloaded. Required. + :type agent_version: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_agent_version_code_request( + agent_name=agent_name, + agent_version=agent_version, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def download_agent_code(self, agent_name: str, **kwargs: Any) -> Iterator[bytes]: + """Download the code zip for the latest version of a code-based hosted agent. Returns the + previously-uploaded zip (``application/zip``). The SHA-256 digest of the returned bytes matches + the ``content_hash`` on the latest version's ``code_configuration``. + + :param agent_name: The name of the agent whose latest-version code zip should be downloaded. + Required. + :type agent_name: str + :return: Iterator[bytes] + :rtype: Iterator[bytes] + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Iterator[bytes]] = kwargs.pop("cls", None) + + _request = build_beta_agents_download_agent_code_request( + agent_name=agent_name, + api_version=self._config.api_version, + headers=_headers, + params=_params, + ) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + _request.url = self._client.format_url(_request.url, **path_format_arguments) + + _decompress = kwargs.pop("decompress", True) + _stream = kwargs.pop("stream", True) + pipeline_response: PipelineResponse = self._client._pipeline.run( # pylint: disable=protected-access + _request, stream=_stream, **kwargs + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + if _stream: + try: + response.read() # Load the body in memory and close the socket + except (StreamConsumedError, StreamClosedError): + pass + map_error(status_code=response.status_code, response=response, error_map=error_map) + error = _failsafe_deserialize( + _models.ApiErrorResponse, + response, + ) + raise HttpResponseError(response=response, model=error) + + response_headers = {} + response_headers["Content-Type"] = self._deserialize("str", response.headers.get("Content-Type")) + + deserialized = response.iter_bytes() if _decompress else response.iter_raw() + + if cls: + return cls(pipeline_response, deserialized, response_headers) # type: ignore + + return deserialized # type: ignore + @overload def create_session( self, diff --git a/sdk/ai/azure-ai-projects/tsp-location.yaml b/sdk/ai/azure-ai-projects/tsp-location.yaml index 9fe8f05d118a..d6d08d8ea372 100644 --- a/sdk/ai/azure-ai-projects/tsp-location.yaml +++ b/sdk/ai/azure-ai-projects/tsp-location.yaml @@ -1,4 +1,4 @@ directory: specification/ai-foundry/data-plane/Foundry -commit: 146c19ec2408517a8216df2b6403446a82813056 +commit: 4de5d57b5542c40135a5c4b9246cc7915d01f117 repo: Azure/azure-rest-api-specs additionalDirectories: From 917827c2f85c2570579c35abe78827028ee2c296 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Wed, 6 May 2026 20:09:22 -0700 Subject: [PATCH 2/4] more updates --- sdk/ai/azure-ai-projects/CHANGELOG.md | 1 - sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py | 1 + .../foundry_features_header_test_base.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 68dc0d1999be..203d3a9e6096 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -11,7 +11,6 @@ * New read-only property `content_hash` on `CodeConfiguration`, returning the SHA-256 hex digest of the uploaded code zip. * New evaluator generation job operations on `.beta.evaluators`: `create_generation_job`, `get_generation_job`, `list_generation_jobs`, `cancel_generation_job`, `delete_generation_job`. * New methods on `.beta.agents` sub-client for code-based hosted agents: `update_agent_from_code()`, `create_agent_version_from_code()`, `download_agent_version_code()`, `download_agent_code()`. -* New enum `CodeDependencyResolution` with values `BUNDLED` and `REMOTE_BUILD`, controlling how package dependencies are resolved at deployment time. ### Breaking Changes diff --git a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py index 4c9c649d30f1..03b2e81ab05d 100644 --- a/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py +++ b/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py @@ -53,6 +53,7 @@ [ _AgentDefinitionOptInKeys.HOSTED_AGENTS_V1_PREVIEW.value, _AgentDefinitionOptInKeys.AGENT_ENDPOINT_V1_PREVIEW.value, + _AgentDefinitionOptInKeys.CODE_AGENTS_V1_PREVIEW.value, ] ), } diff --git a/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py b/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py index 9584e2193b7a..c0c242ee5723 100644 --- a/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py +++ b/sdk/ai/azure-ai-projects/tests/foundry_features_header/foundry_features_header_test_base.py @@ -44,7 +44,7 @@ "toolboxes": "Toolboxes=V1Preview", "skills": "Skills=V1Preview", "datasets": "DataGenerationJobs=V1Preview", - "agents": "HostedAgents=V1Preview,AgentEndpoints=V1Preview", + "agents": "HostedAgents=V1Preview,AgentEndpoints=V1Preview,CodeAgents=V1Preview", } # Shared test cases for non-beta methods that optionally send the Foundry-Features header. From 66f488cc8f732cba213ae8dee36acc0104084ad1 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Wed, 6 May 2026 20:20:53 -0700 Subject: [PATCH 3/4] Fix wrong entries in changelog --- sdk/ai/azure-ai-projects/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/ai/azure-ai-projects/CHANGELOG.md b/sdk/ai/azure-ai-projects/CHANGELOG.md index 203d3a9e6096..32fca73275fb 100644 --- a/sdk/ai/azure-ai-projects/CHANGELOG.md +++ b/sdk/ai/azure-ai-projects/CHANGELOG.md @@ -19,8 +19,6 @@ Breaking changes in beta operations: * Required property `isolation_key_source` removed from class `EntraAuthorizationScheme`. * Required keyword argument `isolation_key` removed from `.beta.agents.create_session()` and `.beta.agents.delete_session()` methods. * Argument `body` in methods `.beta.evaluation_taxonomies.create()` and `.beta.evaluation_taxonomies.update()` renamed to `taxonomy`. -* Removed enum value `DataGenerationJobType.TASK`. -* Removed model `TaskDataGenerationJobOptions`. ### Bugs Fixed From 3e69f163eb0a24f3c1ffe03856974fd4bc38c9b1 Mon Sep 17 00:00:00 2001 From: Darren Cohen <39422044+dargilco@users.noreply.github.com> Date: Wed, 6 May 2026 21:30:15 -0700 Subject: [PATCH 4/4] Update recordings with added preview flag for .beta.agents operations --- sdk/ai/azure-ai-projects/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/ai/azure-ai-projects/assets.json b/sdk/ai/azure-ai-projects/assets.json index ba6a1bdf610c..59aa2d33f39a 100644 --- a/sdk/ai/azure-ai-projects/assets.json +++ b/sdk/ai/azure-ai-projects/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/ai/azure-ai-projects", - "Tag": "python/ai/azure-ai-projects_33354b389b" + "Tag": "python/ai/azure-ai-projects_f15b61b44c" }