From d5f65f0e9a8637e4793aa0b57df0d71a417b53db Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 12:37:20 -0700 Subject: [PATCH 01/12] Add azure-ai-voiceagents SDK (1.0.0b1) Initial voice agents client: sync/async operations, models, realtime interface (connect raises NotImplementedError), samples, unit tests, and CI/tests pipelines. --- .../azure-ai-voiceagents/CHANGELOG.md | 7 + sdk/voiceagents/azure-ai-voiceagents/LICENSE | 21 + .../azure-ai-voiceagents/MANIFEST.in | 7 + .../azure-ai-voiceagents/README.md | 82 + .../azure-ai-voiceagents/_metadata.json | 6 + .../apiview-properties.json | 179 + .../azure-ai-voiceagents/azure/__init__.py | 1 + .../azure-ai-voiceagents/azure/ai/__init__.py | 1 + .../azure/ai/voiceagents/__init__.py | 32 + .../azure/ai/voiceagents/_client.py | 117 + .../azure/ai/voiceagents/_configuration.py | 68 + .../azure/ai/voiceagents/_patch.py | 21 + .../azure/ai/voiceagents/_unions.py | 24 + .../azure/ai/voiceagents/_utils/__init__.py | 6 + .../azure/ai/voiceagents/_utils/model_base.py | 1770 +++++++ .../ai/voiceagents/_utils/serialization.py | 2175 ++++++++ .../azure/ai/voiceagents/_version.py | 9 + .../azure/ai/voiceagents/aio/__init__.py | 29 + .../azure/ai/voiceagents/aio/_client.py | 119 + .../ai/voiceagents/aio/_configuration.py | 68 + .../azure/ai/voiceagents/aio/_patch.py | 57 + .../azure/ai/voiceagents/aio/_realtime.py | 417 ++ .../ai/voiceagents/aio/operations/__init__.py | 27 + .../voiceagents/aio/operations/_operations.py | 2816 +++++++++++ .../ai/voiceagents/aio/operations/_patch.py | 21 + .../azure/ai/voiceagents/models/__init__.py | 282 ++ .../azure/ai/voiceagents/models/_enums.py | 396 ++ .../azure/ai/voiceagents/models/_models.py | 4463 +++++++++++++++++ .../azure/ai/voiceagents/models/_patch.py | 21 + .../ai/voiceagents/operations/__init__.py | 27 + .../ai/voiceagents/operations/_operations.py | 3586 +++++++++++++ .../azure/ai/voiceagents/operations/_patch.py | 21 + .../azure/ai/voiceagents/py.typed | 1 + .../azure/ai/voiceagents/types.py | 1082 ++++ .../azure-ai-voiceagents/dev_requirements.txt | 4 + .../azure-ai-voiceagents/pyproject.toml | 61 + .../azure-ai-voiceagents/samples/README.md | 122 + ...ple_create_and_manage_voice_agent_async.py | 76 + .../sample_create_and_manage_voice_agent.py | 110 + .../sample_create_voice_agent_with_tools.py | 159 + .../samples/sample_generate_voice_agent.py | 63 + .../sample_manage_voice_agent_versions.py | 100 + .../samples/sample_read_conversation.py | 102 + .../samples/sample_read_conversation_audio.py | 115 + .../azure-ai-voiceagents/tests/__init__.py | 4 + .../azure-ai-voiceagents/tests/conftest.py | 9 + .../tests/unit/__init__.py | 4 + .../tests/unit/test_unit_client.py | 73 + .../tests/unit/test_unit_enums.py | 45 + .../tests/unit/test_unit_models.py | 128 + .../tests/unit/test_unit_realtime.py | 62 + .../azure-ai-voiceagents/tsp-location.yaml | 13 + sdk/voiceagents/ci.yml | 37 + sdk/voiceagents/tests.yml | 7 + 54 files changed, 19253 insertions(+) create mode 100644 sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md create mode 100644 sdk/voiceagents/azure-ai-voiceagents/LICENSE create mode 100644 sdk/voiceagents/azure-ai-voiceagents/MANIFEST.in create mode 100644 sdk/voiceagents/azure-ai-voiceagents/README.md create mode 100644 sdk/voiceagents/azure-ai-voiceagents/_metadata.json create mode 100644 sdk/voiceagents/azure-ai-voiceagents/apiview-properties.json create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_patch.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/model_base.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/serialization.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_version.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_configuration.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_patch.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_enums.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_models.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_patch.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_patch.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/py.typed create mode 100644 sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/types.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/dev_requirements.txt create mode 100644 sdk/voiceagents/azure-ai-voiceagents/pyproject.toml create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/README.md create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/conftest.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/__init__.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_client.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_enums.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_models.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_realtime.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml create mode 100644 sdk/voiceagents/ci.yml create mode 100644 sdk/voiceagents/tests.yml diff --git a/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md b/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md new file mode 100644 index 000000000000..b957b2575b48 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md @@ -0,0 +1,7 @@ +# Release History + +## 1.0.0b1 (1970-01-01) + +### Other Changes + + - Initial version \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/LICENSE b/sdk/voiceagents/azure-ai-voiceagents/LICENSE new file mode 100644 index 000000000000..63447fd8bbbf --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/MANIFEST.in b/sdk/voiceagents/azure-ai-voiceagents/MANIFEST.in new file mode 100644 index 000000000000..40653212ffad --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/MANIFEST.in @@ -0,0 +1,7 @@ +include *.md +include LICENSE +include azure/ai/voiceagents/py.typed +recursive-include tests *.py +recursive-include samples *.py *.md +include azure/__init__.py +include azure/ai/__init__.py diff --git a/sdk/voiceagents/azure-ai-voiceagents/README.md b/sdk/voiceagents/azure-ai-voiceagents/README.md new file mode 100644 index 000000000000..87782cb70376 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/README.md @@ -0,0 +1,82 @@ +# Azure Ai Voiceagents client library for Python + + +## Getting started + +### Install the package + +```bash +python -m pip install azure-ai-voiceagents +``` + +#### Prequisites + +- Python 3.10 or later is required to use this package. +- You need an [Azure subscription][azure_sub] to use this package. +- An existing Azure Ai Voiceagents instance. + +### Use with AI tools + +AI coding tools such as VS Code and GitHub Copilot can help you write and debug code that uses this library. See [Using the Azure SDK for Python with AI tools](https://aka.ms/azsdk/python/ai) for available integrations. + +#### Create with an Azure Active Directory Credential +To use an [Azure Active Directory (AAD) token credential][authenticate_with_token], +provide an instance of the desired credential type obtained from the +[azure-identity][azure_identity_credentials] library. + +To authenticate with AAD, you must first [pip][pip] install [`azure-identity`][azure_identity_pip] + +After setup, you can choose which type of [credential][azure_identity_credentials] from azure.identity to use. +As an example, [DefaultAzureCredential][default_azure_credential] can be used to authenticate the client: + +Set the values of the client ID, tenant ID, and client secret of the AAD application as environment variables: +`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET` + +Use the returned token credential to authenticate the client: + +```python +>>> from azure.ai.voiceagents import VoiceAgentsClient +>>> from azure.identity import DefaultAzureCredential +>>> client = VoiceAgentsClient(endpoint='', credential=DefaultAzureCredential()) +``` + +## Examples + +```python +>>> from azure.ai.voiceagents import VoiceAgentsClient +>>> from azure.identity import DefaultAzureCredential +>>> from azure.core.exceptions import HttpResponseError + +>>> client = VoiceAgentsClient(endpoint='', credential=DefaultAzureCredential()) +>>> try: + + except HttpResponseError as e: + print('service responds error: {}'.format(e.response.json())) + +``` + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. +For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether +you need to provide a CLA and decorate the PR appropriately (e.g., label, +comment). Simply follow the instructions provided by the bot. You will only +need to do this once across all repos using our CLA. + +This project has adopted the +[Microsoft Open Source Code of Conduct][code_of_conduct]. For more information, +see the Code of Conduct FAQ or contact opencode@microsoft.com with any +additional questions or comments. + + +[code_of_conduct]: https://opensource.microsoft.com/codeofconduct/ +[authenticate_with_token]: https://docs.microsoft.com/azure/cognitive-services/authentication?tabs=powershell#authenticate-with-an-authentication-token +[azure_identity_credentials]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#credentials +[azure_identity_pip]: https://pypi.org/project/azure-identity/ +[default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential +[pip]: https://pypi.org/project/pip/ +[azure_sub]: https://azure.microsoft.com/free/ diff --git a/sdk/voiceagents/azure-ai-voiceagents/_metadata.json b/sdk/voiceagents/azure-ai-voiceagents/_metadata.json new file mode 100644 index 000000000000..3a000fe50d57 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/_metadata.json @@ -0,0 +1,6 @@ +{ + "apiVersion": "v1", + "apiVersions": { + "Azure.AI.Projects": "v1" + } +} \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/apiview-properties.json b/sdk/voiceagents/azure-ai-voiceagents/apiview-properties.json new file mode 100644 index 000000000000..c2729d84edb1 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/apiview-properties.json @@ -0,0 +1,179 @@ +{ + "CrossLanguagePackageId": "Azure.AI.Projects", + "CrossLanguageDefinitionId": { + "azure.ai.voiceagents.models.A2AProtocolConfiguration": "Azure.AI.Projects.A2AProtocolConfiguration", + "azure.ai.voiceagents.models.ActivityProtocolConfiguration": "Azure.AI.Projects.ActivityProtocolConfiguration", + "azure.ai.voiceagents.models.AgentBlueprintReference": "Azure.AI.Projects.AgentBlueprintReference", + "azure.ai.voiceagents.models.AgentCard": "Azure.AI.Projects.AgentCard", + "azure.ai.voiceagents.models.AgentCardSkill": "Azure.AI.Projects.AgentCardSkill", + "azure.ai.voiceagents.models.AgentEndpointAuthorizationScheme": "Azure.AI.Projects.AgentEndpointAuthorizationScheme", + "azure.ai.voiceagents.models.AgentEndpointConfig": "Azure.AI.Projects.AgentEndpointConfig", + "azure.ai.voiceagents.models.AgentIdentity": "Azure.AI.Projects.AgentIdentity", + "azure.ai.voiceagents.models.ApiErrorResponse": "Azure.AI.Projects.ApiErrorResponse", + "azure.ai.voiceagents.models.AzureVoice": "Azure.AI.Projects.AzureVoice", + "azure.ai.voiceagents.models.BotServiceAuthorizationScheme": "Azure.AI.Projects.BotServiceAuthorizationScheme", + "azure.ai.voiceagents.models.BotServiceRbacAuthorizationScheme": "Azure.AI.Projects.BotServiceRbacAuthorizationScheme", + "azure.ai.voiceagents.models.BotServiceTenantAuthorizationScheme": "Azure.AI.Projects.BotServiceTenantAuthorizationScheme", + "azure.ai.voiceagents.models.DeleteAgentResponse": "Azure.AI.Projects.DeleteAgentResponse", + "azure.ai.voiceagents.models.DeleteAgentVersionResponse": "Azure.AI.Projects.DeleteAgentVersionResponse", + "azure.ai.voiceagents.models.EntraAuthorizationScheme": "Azure.AI.Projects.EntraAuthorizationScheme", + "azure.ai.voiceagents.models.Error": "OpenAI.Error", + "azure.ai.voiceagents.models.VersionSelectionRule": "Azure.AI.Projects.VersionSelectionRule", + "azure.ai.voiceagents.models.FixedRatioVersionSelectionRule": "Azure.AI.Projects.FixedRatioVersionSelectionRule", + "azure.ai.voiceagents.models.Tool": "OpenAI.Tool", + "azure.ai.voiceagents.models.FunctionTool": "OpenAI.FunctionTool", + "azure.ai.voiceagents.models.InvocationsProtocolConfiguration": "Azure.AI.Projects.InvocationsProtocolConfiguration", + "azure.ai.voiceagents.models.InvocationsWsProtocolConfiguration": "Azure.AI.Projects.InvocationsWsProtocolConfiguration", + "azure.ai.voiceagents.models.ManagedAgentIdentityBlueprintReference": "Azure.AI.Projects.ManagedAgentIdentityBlueprintReference", + "azure.ai.voiceagents.models.MCPListToolsTool": "OpenAI.MCPListToolsTool", + "azure.ai.voiceagents.models.MCPListToolsToolAnnotations": "OpenAI.MCPListToolsToolAnnotations", + "azure.ai.voiceagents.models.MCPListToolsToolInputSchema": "OpenAI.MCPListToolsToolInputSchema", + "azure.ai.voiceagents.models.McpProtocolConfiguration": "Azure.AI.Projects.McpProtocolConfiguration", + "azure.ai.voiceagents.models.MCPTool": "OpenAI.MCPTool", + "azure.ai.voiceagents.models.MCPToolFilter": "OpenAI.MCPToolFilter", + "azure.ai.voiceagents.models.MCPToolRequireApproval": "OpenAI.MCPToolRequireApproval", + "azure.ai.voiceagents.models.ProtocolConfiguration": "Azure.AI.Projects.ProtocolConfiguration", + "azure.ai.voiceagents.models.RaiConfig": "Azure.AI.Projects.RaiConfig", + "azure.ai.voiceagents.models.RealtimeAudioFormats": "OpenAI.RealtimeAudioFormats", + "azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcm": "OpenAI.RealtimeAudioFormatsAudioPcm", + "azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcma": "OpenAI.RealtimeAudioFormatsAudioPcma", + "azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcmu": "OpenAI.RealtimeAudioFormatsAudioPcmu", + "azure.ai.voiceagents.models.RealtimeConversationItem": "OpenAI.RealtimeConversationItem", + "azure.ai.voiceagents.models.RealtimeConversationItemFunctionCall": "OpenAI.RealtimeConversationItemFunctionCall", + "azure.ai.voiceagents.models.RealtimeConversationItemFunctionCallOutput": "OpenAI.RealtimeConversationItemFunctionCallOutput", + "azure.ai.voiceagents.models.RealtimeConversationItemMessage": "OpenAI.RealtimeConversationItemMessage", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistant": "OpenAI.RealtimeConversationItemMessageAssistant", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistantContent": "OpenAI.RealtimeConversationItemMessageAssistantContent", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageSystem": "OpenAI.RealtimeConversationItemMessageSystem", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageSystemContent": "OpenAI.RealtimeConversationItemMessageSystemContent", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageUser": "OpenAI.RealtimeConversationItemMessageUser", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageUserContent": "OpenAI.RealtimeConversationItemMessageUserContent", + "azure.ai.voiceagents.models.RealtimeMCPApprovalRequest": "OpenAI.RealtimeMCPApprovalRequest", + "azure.ai.voiceagents.models.RealtimeMCPApprovalResponse": "OpenAI.RealtimeMCPApprovalResponse", + "azure.ai.voiceagents.models.RealtimeMCPError": "OpenAI.RealtimeMCPError", + "azure.ai.voiceagents.models.RealtimeMCPHTTPError": "OpenAI.RealtimeMCPHTTPError", + "azure.ai.voiceagents.models.RealtimeMCPListTools": "OpenAI.RealtimeMCPListTools", + "azure.ai.voiceagents.models.RealtimeMCPProtocolError": "OpenAI.RealtimeMCPProtocolError", + "azure.ai.voiceagents.models.RealtimeMCPToolCall": "OpenAI.RealtimeMCPToolCall", + "azure.ai.voiceagents.models.RealtimeMCPToolExecutionError": "OpenAI.RealtimeMCPToolExecutionError", + "azure.ai.voiceagents.models.RealtimeResponseStatusDetails": "OpenAI.RealtimeResponseStatusDetails", + "azure.ai.voiceagents.models.RealtimeResponseStatusDetailsError": "OpenAI.RealtimeResponseStatusDetailsError", + "azure.ai.voiceagents.models.RealtimeResponseUsage": "OpenAI.RealtimeResponseUsage", + "azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetails": "OpenAI.RealtimeResponseUsageInputTokenDetails", + "azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails": "OpenAI.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails", + "azure.ai.voiceagents.models.RealtimeResponseUsageOutputTokenDetails": "OpenAI.RealtimeResponseUsageOutputTokenDetails", + "azure.ai.voiceagents.models.ResponsesProtocolConfiguration": "Azure.AI.Projects.ResponsesProtocolConfiguration", + "azure.ai.voiceagents.models.VoiceTurnDetection": "Azure.AI.Projects.VoiceTurnDetection", + "azure.ai.voiceagents.models.SemanticVadTurnDetection": "Azure.AI.Projects.SemanticVadTurnDetection", + "azure.ai.voiceagents.models.ServerVadTurnDetection": "Azure.AI.Projects.ServerVadTurnDetection", + "azure.ai.voiceagents.models.StructuredInputDefinition": "Azure.AI.Projects.StructuredInputDefinition", + "azure.ai.voiceagents.models.ToolConfig": "Azure.AI.Projects.ToolConfig", + "azure.ai.voiceagents.models.VersionSelector": "Azure.AI.Projects.VersionSelector", + "azure.ai.voiceagents.models.VoiceAgentDefinition": "Azure.AI.Projects.VoiceAgentDefinition", + "azure.ai.voiceagents.models.VoiceAgentObject": "Azure.AI.Projects.VoiceAgentObject", + "azure.ai.voiceagents.models.VoiceAgentObjectVersions": "Azure.AI.Projects.VoiceAgentObject.versions.anonymous", + "azure.ai.voiceagents.models.VoiceAgentVersionObject": "Azure.AI.Projects.VoiceAgentVersionObject", + "azure.ai.voiceagents.models.VoiceAssistantMessageItem": "Azure.AI.Projects.VoiceAssistantMessageItem", + "azure.ai.voiceagents.models.VoiceAudioConfig": "Azure.AI.Projects.VoiceAudioConfig", + "azure.ai.voiceagents.models.VoiceAudioFormat": "Azure.AI.Projects.VoiceAudioFormat", + "azure.ai.voiceagents.models.VoiceAudioInputConfig": "Azure.AI.Projects.VoiceAudioInputConfig", + "azure.ai.voiceagents.models.VoiceAudioOutputConfig": "Azure.AI.Projects.VoiceAudioOutputConfig", + "azure.ai.voiceagents.models.VoiceAvatarConfig": "Azure.AI.Projects.VoiceAvatarConfig", + "azure.ai.voiceagents.models.VoiceConversation": "Azure.AI.Projects.VoiceConversation", + "azure.ai.voiceagents.models.VoiceDeletedConversation": "Azure.AI.Projects.VoiceDeletedConversation", + "azure.ai.voiceagents.models.VoiceFunctionCallItem": "Azure.AI.Projects.VoiceFunctionCallItem", + "azure.ai.voiceagents.models.VoiceFunctionCallOutputItem": "Azure.AI.Projects.VoiceFunctionCallOutputItem", + "azure.ai.voiceagents.models.VoiceInputTranscription": "Azure.AI.Projects.VoiceInputTranscription", + "azure.ai.voiceagents.models.VoiceItemAudioResponse": "Azure.AI.Projects.VoiceItemAudioResponse", + "azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem": "Azure.AI.Projects.VoiceMcpApprovalRequestItem", + "azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem": "Azure.AI.Projects.VoiceMcpApprovalResponseItem", + "azure.ai.voiceagents.models.VoiceMcpCallItem": "Azure.AI.Projects.VoiceMcpCallItem", + "azure.ai.voiceagents.models.VoiceMcpListToolsItem": "Azure.AI.Projects.VoiceMcpListToolsItem", + "azure.ai.voiceagents.models.VoiceNoiseReduction": "Azure.AI.Projects.VoiceNoiseReduction", + "azure.ai.voiceagents.models.VoiceRecordingChannelLayout": "Azure.AI.Projects.VoiceRecordingChannelLayout", + "azure.ai.voiceagents.models.VoiceRecordingResponse": "Azure.AI.Projects.VoiceRecordingResponse", + "azure.ai.voiceagents.models.VoiceResponse": "Azure.AI.Projects.VoiceResponse", + "azure.ai.voiceagents.models.VoiceResponseAudio": "Azure.AI.Projects.VoiceResponseAudio", + "azure.ai.voiceagents.models.VoiceResponseAudioOutput": "Azure.AI.Projects.VoiceResponseAudioOutput", + "azure.ai.voiceagents.models.VoiceResponseVoice": "Azure.AI.Projects.VoiceResponseVoice", + "azure.ai.voiceagents.models.VoiceSystemMessageItem": "Azure.AI.Projects.VoiceSystemMessageItem", + "azure.ai.voiceagents.models.VoiceSystemTool": "Azure.AI.Projects.VoiceSystemTool", + "azure.ai.voiceagents.models.VoiceToolboxTool": "Azure.AI.Projects.VoiceToolboxTool", + "azure.ai.voiceagents.models.VoiceUserMessageItem": "Azure.AI.Projects.VoiceUserMessageItem", + "azure.ai.voiceagents.models.VoiceConversationStatus": "Azure.AI.Projects.VoiceConversationStatus", + "azure.ai.voiceagents.models.AgentDefinitionOptInKeys": "Azure.AI.Projects.AgentDefinitionOptInKeys", + "azure.ai.voiceagents.models.VoiceResponseStatus": "Azure.AI.Projects.VoiceResponseStatus", + "azure.ai.voiceagents.models.RealtimeConversationItemMessageType": "OpenAI.RealtimeConversationItemMessageType", + "azure.ai.voiceagents.models.RealtimeConversationItemType": "OpenAI.RealtimeConversationItemType", + "azure.ai.voiceagents.models.RealtimeMcpErrorType": "OpenAI.RealtimeMcpErrorType", + "azure.ai.voiceagents.models.RealtimeAudioFormatsType": "OpenAI.RealtimeAudioFormatsType", + "azure.ai.voiceagents.models.PageOrder": "Azure.AI.Projects.PageOrder", + "azure.ai.voiceagents.models.AgentObjectType": "Azure.AI.Projects.AgentObjectType", + "azure.ai.voiceagents.models.AgentState": "Azure.AI.Projects.AgentState", + "azure.ai.voiceagents.models.VersionSelectorType": "Azure.AI.Projects.VersionSelectorType", + "azure.ai.voiceagents.models.AgentEndpointAuthorizationSchemeType": "Azure.AI.Projects.AgentEndpointAuthorizationSchemeType", + "azure.ai.voiceagents.models.AgentIdentityStatus": "Azure.AI.Projects.AgentIdentityStatus", + "azure.ai.voiceagents.models.AgentBlueprintReferenceType": "Azure.AI.Projects.AgentBlueprintReferenceType", + "azure.ai.voiceagents.models.AgentVersionStatus": "Azure.AI.Projects.AgentVersionStatus", + "azure.ai.voiceagents.models.VoiceModelType": "Azure.AI.Projects.VoiceModelType", + "azure.ai.voiceagents.models.VoiceAudioFormatType": "Azure.AI.Projects.VoiceAudioFormatType", + "azure.ai.voiceagents.models.VoiceNoiseReductionType": "Azure.AI.Projects.VoiceNoiseReductionType", + "azure.ai.voiceagents.models.VoiceTurnDetectionType": "Azure.AI.Projects.VoiceTurnDetectionType", + "azure.ai.voiceagents.models.VoiceTurnDetectionEagerness": "Azure.AI.Projects.VoiceTurnDetectionEagerness", + "azure.ai.voiceagents.models.AzureVoiceType": "Azure.AI.Projects.AzureVoiceType", + "azure.ai.voiceagents.models.VoiceOutputModality": "Azure.AI.Projects.VoiceOutputModality", + "azure.ai.voiceagents.models.VoiceAvatarType": "Azure.AI.Projects.VoiceAvatarType", + "azure.ai.voiceagents.models.VoiceAvatarOutputProtocol": "Azure.AI.Projects.VoiceAvatarOutputProtocol", + "azure.ai.voiceagents.models.ToolType": "OpenAI.ToolType", + "azure.ai.voiceagents.models.VoiceSystemToolName": "Azure.AI.Projects.VoiceSystemToolName", + "azure.ai.voiceagents.models.VoiceAgentType": "Azure.AI.Projects.VoiceAgentType", + "azure.ai.voiceagents.models.VoiceAgentUseCase": "Azure.AI.Projects.VoiceAgentUseCase", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversation", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversation", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.delete_agent_conversation": "Azure.AI.Projects.AgentEndpointConversations.deleteAgentConversation", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.delete_agent_conversation": "Azure.AI.Projects.AgentEndpointConversations.deleteAgentConversation", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.list_agent_conversation_responses": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationResponses", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.list_agent_conversation_responses": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationResponses", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_response": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationResponse", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_response": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationResponse", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.list_agent_conversation_response_items": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationResponseItems", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.list_agent_conversation_response_items": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationResponseItems", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.list_agent_conversation_items": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationItems", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.list_agent_conversation_items": "Azure.AI.Projects.AgentEndpointConversations.listAgentConversationItems", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_item": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItem", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_item": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItem", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_item_audio": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItemAudio", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_item_audio": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItemAudio", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_item_audio_content": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItemAudioContent", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_item_audio_content": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationItemAudioContent", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_audio": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationAudio", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_audio": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationAudio", + "azure.ai.voiceagents.operations.AgentEndpointConversationsOperations.get_agent_conversation_audio_content": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationAudioContent", + "azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations.get_agent_conversation_audio_content": "Azure.AI.Projects.AgentEndpointConversations.getAgentConversationAudioContent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.create_voice_agent": "Azure.AI.Projects.VoiceAgents.createVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.create_voice_agent": "Azure.AI.Projects.VoiceAgents.createVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.list_voice_agents": "Azure.AI.Projects.VoiceAgents.listVoiceAgents", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.list_voice_agents": "Azure.AI.Projects.VoiceAgents.listVoiceAgents", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.get_voice_agent": "Azure.AI.Projects.VoiceAgents.getVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.get_voice_agent": "Azure.AI.Projects.VoiceAgents.getVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.update_voice_agent": "Azure.AI.Projects.VoiceAgents.updateVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.update_voice_agent": "Azure.AI.Projects.VoiceAgents.updateVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.delete_voice_agent": "Azure.AI.Projects.VoiceAgents.deleteVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.delete_voice_agent": "Azure.AI.Projects.VoiceAgents.deleteVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.enable_voice_agent": "Azure.AI.Projects.VoiceAgents.enableVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.enable_voice_agent": "Azure.AI.Projects.VoiceAgents.enableVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.disable_voice_agent": "Azure.AI.Projects.VoiceAgents.disableVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.disable_voice_agent": "Azure.AI.Projects.VoiceAgents.disableVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.generate_voice_agent": "Azure.AI.Projects.VoiceAgents.generateVoiceAgent", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.generate_voice_agent": "Azure.AI.Projects.VoiceAgents.generateVoiceAgent", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.create_voice_agent_version": "Azure.AI.Projects.VoiceAgents.createVoiceAgentVersion", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.create_voice_agent_version": "Azure.AI.Projects.VoiceAgents.createVoiceAgentVersion", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.list_voice_agent_versions": "Azure.AI.Projects.VoiceAgents.listVoiceAgentVersions", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.list_voice_agent_versions": "Azure.AI.Projects.VoiceAgents.listVoiceAgentVersions", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.get_voice_agent_version": "Azure.AI.Projects.VoiceAgents.getVoiceAgentVersion", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.get_voice_agent_version": "Azure.AI.Projects.VoiceAgents.getVoiceAgentVersion", + "azure.ai.voiceagents.operations.VoiceAgentsOperations.delete_voice_agent_version": "Azure.AI.Projects.VoiceAgents.deleteVoiceAgentVersion", + "azure.ai.voiceagents.aio.operations.VoiceAgentsOperations.delete_voice_agent_version": "Azure.AI.Projects.VoiceAgents.deleteVoiceAgentVersion" + }, + "CrossLanguageVersion": "a44fd0706d53" +} \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/__init__.py new file mode 100644 index 000000000000..d55ccad1f573 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/__init__.py new file mode 100644 index 000000000000..d55ccad1f573 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/__init__.py @@ -0,0 +1 @@ +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/__init__.py new file mode 100644 index 000000000000..99bf20879f5b --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/__init__.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._client import VoiceAgentsClient # type: ignore +from ._version import VERSION + +__version__ = VERSION + +try: + from ._patch import __all__ as _patch_all + from ._patch import * +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk + +__all__ = [ + "VoiceAgentsClient", +] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore + +_patch_sdk() diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py new file mode 100644 index 000000000000..5f3318647d5e --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py @@ -0,0 +1,117 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +import sys +from typing import Any, TYPE_CHECKING + +from azure.core import PipelineClient +from azure.core.pipeline import policies +from azure.core.rest import HttpRequest, HttpResponse + +from ._configuration import VoiceAgentsClientConfiguration +from ._utils.serialization import Deserializer, Serializer +from .operations import AgentEndpointConversationsOperations, VoiceAgentsOperations + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self # type: ignore + +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential + + +class VoiceAgentsClient: + """VoiceAgentsClient. + + :ivar agent_endpoint_conversations: AgentEndpointConversationsOperations operations + :vartype agent_endpoint_conversations: + azure.ai.voiceagents.operations.AgentEndpointConversationsOperations + :ivar voice_agents: VoiceAgentsOperations operations + :vartype voice_agents: azure.ai.voiceagents.operations.VoiceAgentsOperations + :param endpoint: Foundry Project endpoint in the form + "https://{ai-services-account-name}.services.ai.azure.com/api/projects/{project-name}". If you + only have one Project in your Foundry Hub, or to target the default Project in your Hub, use + the form "https://{ai-services-account-name}.services.ai.azure.com/api/projects/_project". + Required. + :type endpoint: str + :param credential: Credential used to authenticate requests to the service. Required. + :type credential: ~azure.core.credentials.TokenCredential + :keyword api_version: The API version to use for this operation. Known values are "v1" and + None. Default value is None. If not set, the operation's default API version will be used. Note + that overriding this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__(self, endpoint: str, credential: "TokenCredential", **kwargs: Any) -> None: + _endpoint = "{endpoint}" + self._config = VoiceAgentsClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) + + _policies = kwargs.pop("policies", None) + if _policies is None: + _policies = [ + policies.RequestIdPolicy(**kwargs), + self._config.headers_policy, + self._config.user_agent_policy, + self._config.proxy_policy, + policies.ContentDecodePolicy(**kwargs), + self._config.redirect_policy, + self._config.retry_policy, + self._config.authentication_policy, + self._config.custom_hook_policy, + self._config.logging_policy, + policies.DistributedTracingPolicy(**kwargs), + policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + self._config.http_logging_policy, + ] + self._client: PipelineClient = PipelineClient(base_url=_endpoint, policies=_policies, **kwargs) + + self._serialize = Serializer() + self._deserialize = Deserializer() + self._serialize.client_side_validation = False + self.agent_endpoint_conversations = AgentEndpointConversationsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.voice_agents = VoiceAgentsOperations(self._client, self._config, self._serialize, self._deserialize) + + def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = client.send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.HttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore + + def close(self) -> None: + self._client.close() + + def __enter__(self) -> Self: + self._client.__enter__() + return self + + def __exit__(self, *exc_details: Any) -> None: + self._client.__exit__(*exc_details) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py new file mode 100644 index 000000000000..aad9f81abdf2 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, TYPE_CHECKING + +from azure.core.pipeline import policies + +from ._version import VERSION + +if TYPE_CHECKING: + from azure.core.credentials import TokenCredential + + +class VoiceAgentsClientConfiguration: # pylint: disable=too-many-instance-attributes + """Configuration for VoiceAgentsClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: Foundry Project endpoint in the form + "https://{ai-services-account-name}.services.ai.azure.com/api/projects/{project-name}". If you + only have one Project in your Foundry Hub, or to target the default Project in your Hub, use + the form "https://{ai-services-account-name}.services.ai.azure.com/api/projects/_project". + Required. + :type endpoint: str + :param credential: Credential used to authenticate requests to the service. Required. + :type credential: ~azure.core.credentials.TokenCredential + :keyword api_version: The API version to use for this operation. Known values are "v1" and + None. Default value is None. If not set, the operation's default API version will be used. Note + that overriding this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__(self, endpoint: str, credential: "TokenCredential", **kwargs: Any) -> None: + api_version: str = kwargs.pop("api_version", "v1") + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + + self.endpoint = endpoint + self.credential = credential + self.api_version = api_version + self.credential_scopes = kwargs.pop("credential_scopes", ["https://ai.azure.com/.default"]) + kwargs.setdefault("sdk_moniker", "ai-voiceagents/{}".format(VERSION)) + self.polling_interval = kwargs.get("polling_interval", 30) + self._configure(**kwargs) + + def _configure(self, **kwargs: Any) -> None: + self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) + self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get("redirect_policy") or policies.RedirectPolicy(**kwargs) + self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs) + self.authentication_policy = kwargs.get("authentication_policy") + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.BearerTokenCredentialPolicy( + self.credential, *self.credential_scopes, **kwargs + ) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_patch.py new file mode 100644 index 000000000000..87676c65a8f0 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_patch.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + + +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level + + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py new file mode 100644 index 000000000000..3fb8f699ba8c --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py @@ -0,0 +1,24 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import TYPE_CHECKING, Union + +if TYPE_CHECKING: + from . import models as _models +VoiceConversationItem = Union[ + "_models.VoiceSystemMessageItem", + "_models.VoiceUserMessageItem", + "_models.VoiceAssistantMessageItem", + "_models.VoiceFunctionCallItem", + "_models.VoiceFunctionCallOutputItem", + "_models.VoiceMcpListToolsItem", + "_models.VoiceMcpCallItem", + "_models.VoiceMcpApprovalRequestItem", + "_models.VoiceMcpApprovalResponseItem", +] +VoiceAgentTool = Union["_models.FunctionTool", "_models.MCPTool", "_models.VoiceSystemTool", "_models.VoiceToolboxTool"] diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/__init__.py new file mode 100644 index 000000000000..8026245c2abc --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/__init__.py @@ -0,0 +1,6 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/model_base.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/model_base.py new file mode 100644 index 000000000000..b93f5120d517 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/model_base.py @@ -0,0 +1,1770 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=protected-access, broad-except + +import copy +import calendar +import decimal +import functools +import sys +import logging +import base64 +import re +import typing +import enum +import email.utils +from datetime import datetime, date, time, timedelta, timezone +from json import JSONEncoder +import xml.etree.ElementTree as ET +from collections.abc import MutableMapping +import isodate +from azure.core.exceptions import DeserializationError +from azure.core import CaseInsensitiveEnumMeta +from azure.core.pipeline import PipelineResponse +from azure.core.serialization import _Null + +from azure.core.rest import HttpResponse + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +_LOGGER = logging.getLogger(__name__) + +__all__ = ["SdkJSONEncoder", "Model", "rest_field", "rest_discriminator"] + +TZ_UTC = timezone.utc +_T = typing.TypeVar("_T") +_NONE_TYPE = type(None) + + +def _timedelta_as_isostr(td: timedelta) -> str: + """Converts a datetime.timedelta object into an ISO 8601 formatted string, e.g. 'P4DT12H30M05S' + + Function adapted from the Tin Can Python project: https://github.com/RusticiSoftware/TinCanPython + + :param timedelta td: The timedelta to convert + :rtype: str + :return: ISO8601 version of this timedelta + """ + + # Split seconds to larger units + seconds = td.total_seconds() + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + + days, hours, minutes = list(map(int, (days, hours, minutes))) + seconds = round(seconds, 6) + + # Build date + date_str = "" + if days: + date_str = "%sD" % days + + if hours or minutes or seconds: + # Build time + time_str = "T" + + # Hours + bigger_exists = date_str or hours + if bigger_exists: + time_str += "{:02}H".format(hours) + + # Minutes + bigger_exists = bigger_exists or minutes + if bigger_exists: + time_str += "{:02}M".format(minutes) + + # Seconds + try: + if seconds.is_integer(): + seconds_string = "{:02}".format(int(seconds)) + else: + # 9 chars long w/ leading 0, 6 digits after decimal + seconds_string = "%09.6f" % seconds + # Remove trailing zeros + seconds_string = seconds_string.rstrip("0") + except AttributeError: # int.is_integer() raises + seconds_string = "{:02}".format(seconds) + + time_str += "{}S".format(seconds_string) + else: + time_str = "" + + return "P" + date_str + time_str + + +def _serialize_bytes(o, format: typing.Optional[str] = None) -> str: + encoded = base64.b64encode(o).decode() + if format == "base64url": + return encoded.strip("=").replace("+", "-").replace("/", "_") + return encoded + + +def _serialize_duration(td: timedelta, format: typing.Optional[str] = None): + """Serialize a timedelta to its wire representation. + + For the ``seconds``/``milliseconds`` encodings the value is converted to a + numeric value, otherwise it falls back to an ISO 8601 duration string. + + :param timedelta td: The timedelta to serialize. + :param str format: The duration encoding format. + :rtype: int or float or str + :return: serialized duration + """ + seconds = td.total_seconds() + if format == "duration-seconds-int": + return int(seconds) + if format == "duration-seconds-float": + return seconds + if format == "duration-milliseconds-int": + return int(seconds * 1000) + if format == "duration-milliseconds-float": + return seconds * 1000 + return _timedelta_as_isostr(td) + + +def _serialize_datetime(o, format: typing.Optional[str] = None): + if hasattr(o, "year") and hasattr(o, "hour"): + if format == "rfc7231": + return email.utils.format_datetime(o, usegmt=True) + if format == "unix-timestamp": + return int(calendar.timegm(o.utctimetuple())) + + # astimezone() fails for naive times in Python 2.7, so make make sure o is aware (tzinfo is set) + if not o.tzinfo: + iso_formatted = o.replace(tzinfo=TZ_UTC).isoformat() + else: + iso_formatted = o.astimezone(TZ_UTC).isoformat() + # Replace the trailing "+00:00" UTC offset with "Z" (RFC 3339: https://www.ietf.org/rfc/rfc3339.txt) + return iso_formatted.replace("+00:00", "Z") + # Next try datetime.date or datetime.time + return o.isoformat() + + +def _is_readonly(p): + try: + return p._visibility == ["read"] + except AttributeError: + return False + + +class SdkJSONEncoder(JSONEncoder): + """A JSON encoder that's capable of serializing datetime objects and bytes.""" + + def __init__(self, *args, exclude_readonly: bool = False, format: typing.Optional[str] = None, **kwargs): + super().__init__(*args, **kwargs) + self.exclude_readonly = exclude_readonly + self.format = format + + def default(self, o): # pylint: disable=too-many-return-statements + if _is_model(o): + if self.exclude_readonly: + readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] + return {k: v for k, v in o.items() if k not in readonly_props} + return dict(o.items()) + try: + return super(SdkJSONEncoder, self).default(o) + except TypeError: + if isinstance(o, _Null): + return None + if isinstance(o, decimal.Decimal): + return float(o) + if isinstance(o, (bytes, bytearray)): + return _serialize_bytes(o, self.format) + try: + # First try datetime.datetime + return _serialize_datetime(o, self.format) + except AttributeError: + pass + # Last, try datetime.timedelta + try: + return _timedelta_as_isostr(o) + except AttributeError: + # This will be raised when it hits value.total_seconds in the method above + pass + return super(SdkJSONEncoder, self).default(o) + + +_VALID_DATE = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}" + r"\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") +_VALID_RFC7231 = re.compile( + r"(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s\d{2}\s" + r"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s\d{4}\s\d{2}:\d{2}:\d{2}\sGMT" +) + +_ARRAY_ENCODE_MAPPING = { + "pipeDelimited": "|", + "spaceDelimited": " ", + "commaDelimited": ",", + "newlineDelimited": "\n", +} + + +def _deserialize_array_encoded(delimit: str, attr): + if isinstance(attr, str): + if attr == "": + return [] + return attr.split(delimit) + return attr + + +def _deserialize_datetime(attr: typing.Union[str, datetime]) -> datetime: + """Deserialize ISO-8601 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + attr = attr.upper() + match = _VALID_DATE.match(attr) + if not match: + raise ValueError("Invalid datetime string: " + attr) + + check_decimal = attr.split(".") + if len(check_decimal) > 1: + decimal_str = "" + for digit in check_decimal[1]: + if digit.isdigit(): + decimal_str += digit + else: + break + if len(decimal_str) > 6: + attr = attr.replace(decimal_str, decimal_str[0:6]) + + date_obj = isodate.parse_datetime(attr) + test_utc = date_obj.utctimetuple() + if test_utc.tm_year > 9999 or test_utc.tm_year < 1: + raise OverflowError("Hit max or min date") + return date_obj # type: ignore[no-any-return] + + +def _deserialize_datetime_rfc7231(attr: typing.Union[str, datetime]) -> datetime: + """Deserialize RFC7231 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + match = _VALID_RFC7231.match(attr) + if not match: + raise ValueError("Invalid datetime string: " + attr) + + return email.utils.parsedate_to_datetime(attr) + + +def _deserialize_datetime_unix_timestamp(attr: typing.Union[float, datetime]) -> datetime: + """Deserialize unix timestamp into Datetime object. + + :param str attr: response string to be deserialized. + :rtype: ~datetime.datetime + :returns: The datetime object from that input + """ + if isinstance(attr, datetime): + # i'm already deserialized + return attr + return datetime.fromtimestamp(attr, TZ_UTC) + + +def _deserialize_date(attr: typing.Union[str, date]) -> date: + """Deserialize ISO-8601 formatted string into Date object. + :param str attr: response string to be deserialized. + :rtype: date + :returns: The date object from that input + """ + # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception. + if isinstance(attr, date): + return attr + return isodate.parse_date(attr, defaultmonth=None, defaultday=None) # type: ignore + + +def _deserialize_time(attr: typing.Union[str, time]) -> time: + """Deserialize ISO-8601 formatted string into time object. + + :param str attr: response string to be deserialized. + :rtype: datetime.time + :returns: The time object from that input + """ + if isinstance(attr, time): + return attr + return isodate.parse_time(attr) # type: ignore[no-any-return] + + +def _deserialize_bytes(attr): + if isinstance(attr, (bytes, bytearray)): + return attr + return bytes(base64.b64decode(attr)) + + +def _deserialize_bytes_base64(attr): + if isinstance(attr, (bytes, bytearray)): + return attr + padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore + attr = attr + padding # type: ignore + encoded = attr.replace("-", "+").replace("_", "/") + return bytes(base64.b64decode(encoded)) + + +def _deserialize_duration(attr): + if isinstance(attr, timedelta): + return attr + return isodate.parse_duration(attr) + + +def _deserialize_duration_numeric(attr, unit): + if isinstance(attr, timedelta): + return attr + return timedelta(**{unit: float(attr)}) + + +def _deserialize_decimal(attr): + if isinstance(attr, decimal.Decimal): + return attr + return decimal.Decimal(str(attr)) + + +def _deserialize_int_as_str(attr): + if isinstance(attr, int): + return attr + return int(attr) + + +_DESERIALIZE_MAPPING = { + datetime: _deserialize_datetime, + date: _deserialize_date, + time: _deserialize_time, + bytes: _deserialize_bytes, + bytearray: _deserialize_bytes, + timedelta: _deserialize_duration, + typing.Any: lambda x: x, + decimal.Decimal: _deserialize_decimal, +} + +_DESERIALIZE_MAPPING_WITHFORMAT = { + "rfc3339": _deserialize_datetime, + "rfc7231": _deserialize_datetime_rfc7231, + "unix-timestamp": _deserialize_datetime_unix_timestamp, + "base64": _deserialize_bytes, + "base64url": _deserialize_bytes_base64, + "duration-seconds-int": functools.partial(_deserialize_duration_numeric, unit="seconds"), + "duration-seconds-float": functools.partial(_deserialize_duration_numeric, unit="seconds"), + "duration-milliseconds-int": functools.partial(_deserialize_duration_numeric, unit="milliseconds"), + "duration-milliseconds-float": functools.partial(_deserialize_duration_numeric, unit="milliseconds"), +} + + +def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None): + if annotation is int and rf and rf._format == "str": + return _deserialize_int_as_str + if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING: + return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format]) + if rf and rf._format: + return _DESERIALIZE_MAPPING_WITHFORMAT.get(rf._format) + return _DESERIALIZE_MAPPING.get(annotation) # pyright: ignore + + +def _get_type_alias_type(module_name: str, alias_name: str): + types = { + k: v + for k, v in sys.modules[module_name].__dict__.items() + if isinstance(v, typing._GenericAlias) # type: ignore + } + if alias_name not in types: + return alias_name + return types[alias_name] + + +def _get_model(module_name: str, model_name: str): + models = {k: v for k, v in sys.modules[module_name].__dict__.items() if isinstance(v, type)} + module_end = module_name.rsplit(".", 1)[0] + models.update({k: v for k, v in sys.modules[module_end].__dict__.items() if isinstance(v, type)}) + if isinstance(model_name, str): + model_name = model_name.split(".")[-1] + if model_name not in models: + return model_name + return models[model_name] + + +_UNSET = object() + + +class _MyMutableMapping(MutableMapping[str, typing.Any]): + def __init__(self, data: dict[str, typing.Any]) -> None: + self._data = data + + def __contains__(self, key: typing.Any) -> bool: + return key in self._data + + def __getitem__(self, key: str) -> typing.Any: + # If this key has been deserialized (for mutable types), we need to handle serialization + if hasattr(self, "_attr_to_rest_field"): + cache_attr = f"_deserialized_{key}" + if hasattr(self, cache_attr): + rf = _get_rest_field(getattr(self, "_attr_to_rest_field"), key) + if rf: + value = self._data.get(key) + if isinstance(value, (dict, list, set)): + # For mutable types, serialize and return + # But also update _data with serialized form and clear flag + # so mutations via this returned value affect _data + serialized = _serialize(value, rf._format) + # If serialized form is same type (no transformation needed), + # return _data directly so mutations work + if isinstance(serialized, type(value)) and serialized == value: + return self._data.get(key) + # Otherwise return serialized copy and clear flag + try: + object.__delattr__(self, cache_attr) + except AttributeError: + pass + # Store serialized form back + self._data[key] = serialized + return serialized + return self._data.__getitem__(key) + + def __setitem__(self, key: str, value: typing.Any) -> None: + # Clear any cached deserialized value when setting through dictionary access + cache_attr = f"_deserialized_{key}" + try: + object.__delattr__(self, cache_attr) + except AttributeError: + pass + self._data.__setitem__(key, value) + + def __delitem__(self, key: str) -> None: + self._data.__delitem__(key) + + def __iter__(self) -> typing.Iterator[typing.Any]: + return self._data.__iter__() + + def __len__(self) -> int: + return self._data.__len__() + + def __ne__(self, other: typing.Any) -> bool: + return not self.__eq__(other) + + def keys(self) -> typing.KeysView[str]: + """ + :returns: a set-like object providing a view on D's keys + :rtype: ~typing.KeysView + """ + return self._data.keys() + + def values(self) -> typing.ValuesView[typing.Any]: + """ + :returns: an object providing a view on D's values + :rtype: ~typing.ValuesView + """ + return self._data.values() + + def items(self) -> typing.ItemsView[str, typing.Any]: + """ + :returns: set-like object providing a view on D's items + :rtype: ~typing.ItemsView + """ + return self._data.items() + + def get(self, key: str, default: typing.Any = None) -> typing.Any: + """ + Get the value for key if key is in the dictionary, else default. + :param str key: The key to look up. + :param any default: The value to return if key is not in the dictionary. Defaults to None + :returns: D[k] if k in D, else d. + :rtype: any + """ + try: + return self[key] + except KeyError: + return default + + @typing.overload + def pop(self, key: str) -> typing.Any: ... # pylint: disable=arguments-differ + + @typing.overload + def pop(self, key: str, default: _T) -> _T: ... # pylint: disable=signature-differs + + @typing.overload + def pop(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs + + def pop(self, key: str, default: typing.Any = _UNSET) -> typing.Any: + """ + Removes specified key and return the corresponding value. + :param str key: The key to pop. + :param any default: The value to return if key is not in the dictionary + :returns: The value corresponding to the key. + :rtype: any + :raises KeyError: If key is not found and default is not given. + """ + if default is _UNSET: + return self._data.pop(key) + return self._data.pop(key, default) + + def popitem(self) -> tuple[str, typing.Any]: + """ + Removes and returns some (key, value) pair + :returns: The (key, value) pair. + :rtype: tuple + :raises KeyError: if D is empty. + """ + return self._data.popitem() + + def clear(self) -> None: + """ + Remove all items from D. + """ + self._data.clear() + + def update(self, *args: typing.Any, **kwargs: typing.Any) -> None: # pylint: disable=arguments-differ + """ + Updates D from mapping/iterable E and F. + :param any args: Either a mapping object or an iterable of key-value pairs. + """ + self._data.update(*args, **kwargs) + + @typing.overload + def setdefault(self, key: str, default: None = None) -> None: ... + + @typing.overload + def setdefault(self, key: str, default: typing.Any) -> typing.Any: ... # pylint: disable=signature-differs + + def setdefault(self, key: str, default: typing.Any = _UNSET) -> typing.Any: + """ + Same as calling D.get(k, d), and setting D[k]=d if k not found + :param str key: The key to look up. + :param any default: The value to set if key is not in the dictionary + :returns: D[k] if k in D, else d. + :rtype: any + """ + if default is _UNSET: + return self._data.setdefault(key) + return self._data.setdefault(key, default) + + def __eq__(self, other: typing.Any) -> bool: + if isinstance(other, _MyMutableMapping): + return self._data == other._data + try: + other_model = self.__class__(other) + except Exception: + return False + return self._data == other_model._data + + def __repr__(self) -> str: + return str(self._data) + + +def _is_model(obj: typing.Any) -> bool: + return getattr(obj, "_is_model", False) + + +def _serialize(o, format: typing.Optional[str] = None): # pylint: disable=too-many-return-statements + if isinstance(o, list): + if format in _ARRAY_ENCODE_MAPPING and all(isinstance(x, str) for x in o): + return _ARRAY_ENCODE_MAPPING[format].join(o) + return [_serialize(x, format) for x in o] + if isinstance(o, dict): + return {k: _serialize(v, format) for k, v in o.items()} + if isinstance(o, set): + return {_serialize(x, format) for x in o} + if isinstance(o, tuple): + return tuple(_serialize(x, format) for x in o) + if isinstance(o, (bytes, bytearray)): + return _serialize_bytes(o, format) + if isinstance(o, decimal.Decimal): + return float(o) + if isinstance(o, enum.Enum): + return o.value + if isinstance(o, int): + if format == "str": + return str(o) + return o + try: + # First try datetime.datetime + return _serialize_datetime(o, format) + except AttributeError: + pass + # Last, try datetime.timedelta + try: + return _serialize_duration(o, format) + except AttributeError: + # This will be raised when it hits value.total_seconds in the method above + pass + return o + + +def _get_rest_field(attr_to_rest_field: dict[str, "_RestField"], rest_name: str) -> typing.Optional["_RestField"]: + try: + return next(rf for rf in attr_to_rest_field.values() if rf._rest_name == rest_name) + except StopIteration: + return None + + +def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any: + if not rf: + return _serialize(value, None) + if rf._is_multipart_file_input: + return value + if rf._is_model: + return _deserialize(rf._type, value) + if isinstance(value, ET.Element): + value = _deserialize(rf._type, value) + return _serialize(value, rf._format) + + +# ============================================================================ +# Fast-path scalar deserializer functions for rest_field(deserializer=...) +# These are referenced from rest_field declarations to bypass the generic +# _deserialize -> _deserialize_with_callable chain. +# Only simple/primitive types — no models or container types. +# ============================================================================ + + +def _xml_deser_str(value): + if isinstance(value, ET.Element): + return value.text or "" + return str(value) if value is not None else None + + +def _xml_deser_int(value): + if isinstance(value, ET.Element): + return int(value.text) if value.text else None + return int(value) if value is not None else None + + +def _xml_deser_float(value): + if isinstance(value, ET.Element): + return float(value.text) if value.text else None + return float(value) if value is not None else None + + +def _xml_deser_bool(value): + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + if text in (True, False): + return text + return text.lower() == "true" + + +# pylint: disable=docstring-missing-param +def _xml_deser_bytes(value): + """Deserialize bytes from XML (base64).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_bytes(text) + + +def _xml_deser_bytes_base64url(value): + """Deserialize bytes from XML (base64url).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_bytes_base64(text) + + +def _xml_deser_datetime(value): + """Deserialize a datetime from XML (ISO 8601 / rfc3339).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_datetime(text) + + +def _xml_deser_datetime_rfc7231(value): + """Deserialize a datetime from XML (RFC7231 format).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_datetime_rfc7231(text) + + +def _xml_deser_datetime_unix_timestamp(value): + """Deserialize a datetime from XML (Unix timestamp).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_datetime_unix_timestamp(float(text)) + + +def _xml_deser_date(value): + """Deserialize a date from XML (ISO 8601).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_date(text) + + +def _xml_deser_time(value): + """Deserialize a time from XML (ISO 8601).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_time(text) + + +def _xml_deser_duration(value): + """Deserialize a timedelta from XML (ISO 8601 duration).""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_duration(text) + + +def _xml_deser_decimal(value): + """Deserialize a Decimal from XML.""" + if isinstance(value, ET.Element): + text = value.text + else: + text = value + if text is None: + return None + return _deserialize_decimal(text) + + +def _xml_deser_enum_or_str(enum_cls, value): + """Deserialize a Union[EnumType, str] from XML.""" + text = value.text if isinstance(value, ET.Element) else value + if text is None: + return None + try: + return enum_cls(text) + except ValueError: + return text + + +def _extract_xml_model_type(rf_type): + """Extract the concrete Model class from a resolved rf._type partial chain. + + Unwraps ``Optional[Model]`` and ``_deserialize_model(Model, ...)`` + wrappers. Only handles Model and Optional[Model] — other composite + types (List, Dict, Union, etc.) return None and fall through to the + generic ``_deserialize`` path at runtime. + """ + if rf_type is None: + return None + if isinstance(rf_type, type) and _is_model(rf_type): + return rf_type + if not isinstance(rf_type, functools.partial): + return None + func = rf_type.func + args = rf_type.args + if func is _deserialize_with_optional and args: + return _extract_xml_model_type(args[0]) + if func is _deserialize_model and args: + cls = args[0] + return cls if isinstance(cls, type) and _is_model(cls) else None + return None + + +def _build_xml_field_plan( # pylint: disable=docstring-missing-return, docstring-missing-rtype, unused-variable + cls, attr_to_rest_field: dict +) -> list: + """Build a precomputed XML field plan for fast _init_from_xml iteration. + + Called once per model class in __new__. Returns a list of tuples: + (rest_name, xml_name, kind, deser, rf_type, is_optional, items_name) + + kind: 0=wrapped, 1=attribute, 2=unwrapped, 3=text + + For Model and Optional[Model] fields that lack a scalar + ``_deserializer``, this function precomputes the Model class as the + deserializer so ``_init_from_xml`` can call ``ModelClass(element)`` + directly instead of going through the expensive + ``_get_deserialize_callable_from_annotation`` chain at runtime. + """ + model_meta = getattr(cls, "_xml", {}) + model_ns = model_meta.get("ns") or model_meta.get("namespace") + plan = [] + + for rf in attr_to_rest_field.values(): + prop_meta = getattr(rf, "_xml", {}) + deser = rf._deserializer + + xml_name = prop_meta.get("name", rf._rest_name) + xml_ns = _resolve_xml_ns(prop_meta, model_meta) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + is_optional = rf._is_optional + + # For Model / Optional[Model] fields without a scalar deserializer, + # precompute the Model class as the deserializer. + if deser is None and rf._type is not None: + model_cls = _extract_xml_model_type(rf._type) + if model_cls is not None: + deser = model_cls + + if prop_meta.get("attribute", False): + plan.append((rf._rest_name, xml_name, 1, deser, rf._type, is_optional, None)) + elif prop_meta.get("unwrapped", False): + items_name = prop_meta.get("itemsName") + if items_name: + items_ns = prop_meta.get("itemsNs") + if items_ns is not None: + xml_ns = items_ns + if xml_ns: + items_name = "{" + xml_ns + "}" + items_name + else: + items_name = xml_name + plan.append((rf._rest_name, xml_name, 2, deser, rf._type, is_optional, items_name)) + elif prop_meta.get("text", False): + plan.append((rf._rest_name, xml_name, 3, deser, rf._type, is_optional, None)) + else: + plan.append((rf._rest_name, xml_name, 0, deser, rf._type, is_optional, None)) + + return plan + + +# pylint: enable=docstring-missing-param +class Model(_MyMutableMapping): + _is_model = True + # label whether current class's _attr_to_rest_field has been calculated + # could not see _attr_to_rest_field directly because subclass inherits it from parent class + _calculated: set[str] = set() + + def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None: + class_name = self.__class__.__name__ + if len(args) > 1: + raise TypeError(f"{class_name}.__init__() takes 2 positional arguments but {len(args) + 1} were given") + dict_to_pass: dict[str, typing.Any] = {} + if args: + if isinstance(args[0], ET.Element): + dict_to_pass.update(self._init_from_xml(args[0])) + else: + dict_to_pass.update( + {k: _create_value(_get_rest_field(self._attr_to_rest_field, k), v) for k, v in args[0].items()} + ) + else: + non_attr_kwargs = [k for k in kwargs if k not in self._attr_to_rest_field] + if non_attr_kwargs: + # actual type errors only throw the first wrong keyword arg they see, so following that. + raise TypeError(f"{class_name}.__init__() got an unexpected keyword argument '{non_attr_kwargs[0]}'") + dict_to_pass.update( + { + self._attr_to_rest_field[k]._rest_name: _create_value(self._attr_to_rest_field[k], v) + for k, v in kwargs.items() + if v is not None + } + ) + # Apply client default values for fields the caller didn't set so that + # defaults are part of `_data` and therefore included during serialization. + for rf in self._attr_to_rest_field.values(): + if rf._default is _UNSET: + continue + if rf._rest_name in dict_to_pass: + continue + dict_to_pass[rf._rest_name] = _create_value(rf, rf._default) + super().__init__(dict_to_pass) + + def _init_from_xml( # pylint: disable=too-many-branches, too-many-statements + self, element: ET.Element + ) -> dict[str, typing.Any]: + """Deserialize an XML element into a dict mapping rest field names to values. + + :param ET.Element element: The XML element to deserialize from. + :returns: A dictionary of rest_name to deserialized value pairs. + :rtype: dict + """ + result: dict[str, typing.Any] = {} + existed_attr_keys: list[str] = [] + + field_plan = getattr(self, "_xml_field_plan", None) + if field_plan: + for rest_name, xml_name, kind, deser, rf_type, is_optional, items_name in field_plan: + if kind == 0: # wrapped element (most common) + item = element.find(xml_name) + if item is not None: + existed_attr_keys.append(xml_name) + if deser: + result[rest_name] = deser(item) + else: + result[rest_name] = _deserialize(rf_type, item) + elif kind == 1: # attribute + attr_val = element.get(xml_name) + if attr_val is not None: + existed_attr_keys.append(xml_name) + if deser: + result[rest_name] = deser(attr_val) + else: + result[rest_name] = attr_val + elif kind == 2: # unwrapped array + items = element.findall(items_name) # pyright: ignore + if len(items) > 0: + existed_attr_keys.append(items_name) + if deser: + result[rest_name] = deser(items) + else: + result[rest_name] = _deserialize(rf_type, items) + elif not is_optional: + existed_attr_keys.append(items_name) + result[rest_name] = [] + elif kind == 3: # text + if element.text is not None: + if deser: + result[rest_name] = deser(element.text) + else: + result[rest_name] = element.text + else: + model_meta = getattr(self, "_xml", {}) + for rf in self._attr_to_rest_field.values(): + prop_meta = getattr(rf, "_xml", {}) + xml_name = prop_meta.get("name", rf._rest_name) + xml_ns = _resolve_xml_ns(prop_meta, model_meta) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + # attribute + if prop_meta.get("attribute", False) and element.get(xml_name) is not None: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, element.get(xml_name)) + continue + + # unwrapped element is array + if prop_meta.get("unwrapped", False): + _items_name = prop_meta.get("itemsName") + if _items_name: + xml_name = _items_name + _items_ns = prop_meta.get("itemsNs") + if _items_ns is not None: + xml_ns = _items_ns + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + items = element.findall(xml_name) # pyright: ignore + if len(items) > 0: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, items) + elif not rf._is_optional: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = [] + continue + + # text element is primitive type + if prop_meta.get("text", False): + if element.text is not None: + result[rf._rest_name] = _deserialize(rf._type, element.text) + continue + + # wrapped element could be normal property or array + item = element.find(xml_name) + if item is not None: + existed_attr_keys.append(xml_name) + result[rf._rest_name] = _deserialize(rf._type, item) + + # rest thing is additional properties + for e in element: + if e.tag not in existed_attr_keys: + result[e.tag] = _convert_element(e) + + return result + + def copy(self) -> "Model": + return Model(self.__dict__) + + def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> Self: + if f"{cls.__module__}.{cls.__qualname__}" not in cls._calculated: + # we know the last nine classes in mro are going to be 'Model', '_MyMutableMapping', 'MutableMapping', + # 'Mapping', 'Collection', 'Sized', 'Iterable', 'Container' and 'object' + mros = cls.__mro__[:-9][::-1] # ignore parents, and reverse the mro order + attr_to_rest_field: dict[str, _RestField] = { # map attribute name to rest_field property + k: v for mro_class in mros for k, v in mro_class.__dict__.items() if k[0] != "_" and hasattr(v, "_type") + } + annotations = { + k: v + for mro_class in mros + if hasattr(mro_class, "__annotations__") + for k, v in mro_class.__annotations__.items() + } + for attr, rf in attr_to_rest_field.items(): + rf._module = cls.__module__ + if not rf._type: + rf._type = rf._get_deserialize_callable_from_annotation(annotations.get(attr, None)) + if not rf._rest_name_input: + rf._rest_name_input = attr + cls._attr_to_rest_field: dict[str, _RestField] = dict(attr_to_rest_field.items()) + # Build XML field plan for fast _init_from_xml (only for XML models) + if getattr(cls, "_xml", None): + cls._xml_field_plan = _build_xml_field_plan(cls, attr_to_rest_field) + cls._calculated.add(f"{cls.__module__}.{cls.__qualname__}") + + return super().__new__(cls) + + def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None: + for base in cls.__bases__: + if hasattr(base, "__mapping__"): + base.__mapping__[discriminator or cls.__name__] = cls # type: ignore + + @classmethod + def _get_discriminator(cls, exist_discriminators) -> typing.Optional["_RestField"]: + for v in cls.__dict__.values(): + if isinstance(v, _RestField) and v._is_discriminator and v._rest_name not in exist_discriminators: + return v + return None + + @classmethod + def _deserialize(cls, data, exist_discriminators): + if not hasattr(cls, "__mapping__"): + return cls(data) + discriminator = cls._get_discriminator(exist_discriminators) + if discriminator is None: + return cls(data) + exist_discriminators.append(discriminator._rest_name) + if isinstance(data, ET.Element): + model_meta = getattr(cls, "_xml", {}) + prop_meta = getattr(discriminator, "_xml", {}) + xml_name = prop_meta.get("name", discriminator._rest_name) + xml_ns = _resolve_xml_ns(prop_meta, model_meta) + if xml_ns: + xml_name = "{" + xml_ns + "}" + xml_name + + if data.get(xml_name) is not None: + discriminator_value = data.get(xml_name) + else: + discriminator_value = data.find(xml_name).text # pyright: ignore + else: + discriminator_value = data.get(discriminator._rest_name) + mapped_cls = cls.__mapping__.get(discriminator_value, cls) # pyright: ignore # pylint: disable=no-member + return mapped_cls._deserialize(data, exist_discriminators) + + def as_dict(self, *, exclude_readonly: bool = False) -> dict[str, typing.Any]: + """Return a dict that can be turned into json using json.dump. + + :keyword bool exclude_readonly: Whether to remove the readonly properties. + :returns: A dict JSON compatible object + :rtype: dict + """ + + result = {} + readonly_props = [] + if exclude_readonly: + readonly_props = [p._rest_name for p in self._attr_to_rest_field.values() if _is_readonly(p)] + for k, v in self.items(): + if exclude_readonly and k in readonly_props: # pyright: ignore + continue + is_multipart_file_input = False + try: + is_multipart_file_input = next( + rf for rf in self._attr_to_rest_field.values() if rf._rest_name == k + )._is_multipart_file_input + except StopIteration: + pass + result[k] = v if is_multipart_file_input else Model._as_dict_value(v, exclude_readonly=exclude_readonly) + return result + + @staticmethod + def _as_dict_value(v: typing.Any, exclude_readonly: bool = False) -> typing.Any: + if v is None or isinstance(v, _Null): + return None + if isinstance(v, (list, tuple, set)): + return type(v)(Model._as_dict_value(x, exclude_readonly=exclude_readonly) for x in v) + if isinstance(v, dict): + return {dk: Model._as_dict_value(dv, exclude_readonly=exclude_readonly) for dk, dv in v.items()} + return v.as_dict(exclude_readonly=exclude_readonly) if hasattr(v, "as_dict") else v + + +def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj): + if _is_model(obj): + return obj + return _deserialize(model_deserializer, obj) + + +def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj): + if obj is None: + return obj + return _deserialize_with_callable(if_obj_deserializer, obj) + + +def _deserialize_with_union(deserializers, obj): + for deserializer in deserializers: + try: + return _deserialize(deserializer, obj) + except DeserializationError: + pass + raise DeserializationError() + + +def _deserialize_dict( + value_deserializer: typing.Optional[typing.Callable], + module: typing.Optional[str], + obj: dict[typing.Any, typing.Any], +): + if obj is None: + return obj + if isinstance(obj, ET.Element): + obj = {child.tag: child for child in obj} + return {k: _deserialize(value_deserializer, v, module) for k, v in obj.items()} + + +def _deserialize_multiple_sequence( + entry_deserializers: list[typing.Optional[typing.Callable]], + module: typing.Optional[str], + obj, +): + if obj is None: + return obj + return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)) + + +def _is_array_encoded_deserializer(deserializer: functools.partial) -> bool: + return ( + isinstance(deserializer, functools.partial) + and isinstance(deserializer.args[0], functools.partial) + and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable + ) + + +def _deserialize_sequence( + deserializer: typing.Optional[typing.Callable], + module: typing.Optional[str], + obj, +): + if obj is None: + return obj + if isinstance(obj, ET.Element): + obj = list(obj) + + # encoded string may be deserialized to sequence + if isinstance(obj, str) and isinstance(deserializer, functools.partial): + # for list[str] + if _is_array_encoded_deserializer(deserializer): + return deserializer(obj) + + # for list[Union[...]] + if isinstance(deserializer.args[0], list): + for sub_deserializer in deserializer.args[0]: + if _is_array_encoded_deserializer(sub_deserializer): + return sub_deserializer(obj) + + return type(obj)(_deserialize(deserializer, entry, module) for entry in obj) + + +def _sorted_annotations(types: list[typing.Any]) -> list[typing.Any]: + return sorted( + types, + key=lambda x: hasattr(x, "__name__") and x.__name__.lower() in ("str", "float", "int", "bool"), + ) + + +def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements, too-many-branches + annotation: typing.Any, + module: typing.Optional[str], + rf: typing.Optional["_RestField"] = None, +) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]: + if not annotation: + return None + + # is it a type alias? + if isinstance(annotation, str): + if module is not None: + annotation = _get_type_alias_type(module, annotation) + + # is it a forward ref / in quotes? + if isinstance(annotation, (str, typing.ForwardRef)): + try: + model_name = annotation.__forward_arg__ # type: ignore + except AttributeError: + model_name = annotation + if module is not None: + annotation = _get_model(module, model_name) # type: ignore + + try: + if module and _is_model(annotation): + if rf: + rf._is_model = True + + return functools.partial(_deserialize_model, annotation) # pyright: ignore + except Exception: + pass + + # is it a literal? + try: + if annotation.__origin__ is typing.Literal: # pyright: ignore + return None + except AttributeError: + pass + + # is it optional? + try: + if any(a is _NONE_TYPE for a in annotation.__args__): # pyright: ignore + if rf: + rf._is_optional = True + if len(annotation.__args__) <= 2: # pyright: ignore + if_obj_deserializer = _get_deserialize_callable_from_annotation( + next(a for a in annotation.__args__ if a is not _NONE_TYPE), module, rf # pyright: ignore + ) + + return functools.partial(_deserialize_with_optional, if_obj_deserializer) + # the type is Optional[Union[...]], we need to remove the None type from the Union + annotation_copy = copy.copy(annotation) + annotation_copy.__args__ = [a for a in annotation_copy.__args__ if a is not _NONE_TYPE] # pyright: ignore + return _get_deserialize_callable_from_annotation(annotation_copy, module, rf) + except AttributeError: + pass + + # is it union? + if getattr(annotation, "__origin__", None) is typing.Union: + # initial ordering is we make `string` the last deserialization option, because it is often them most generic + deserializers = [ + _get_deserialize_callable_from_annotation(arg, module, rf) + for arg in _sorted_annotations(annotation.__args__) # pyright: ignore + ] + + return functools.partial(_deserialize_with_union, deserializers) + + try: + annotation_name = ( + annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore + ) + if annotation_name.lower() == "dict": + value_deserializer = _get_deserialize_callable_from_annotation( + annotation.__args__[1], module, rf # pyright: ignore + ) + + return functools.partial( + _deserialize_dict, + value_deserializer, + module, + ) + except (AttributeError, IndexError): + pass + try: + annotation_name = ( + annotation.__name__ if hasattr(annotation, "__name__") else annotation._name # pyright: ignore + ) + if annotation_name.lower() in ["list", "set", "tuple", "sequence"]: + if len(annotation.__args__) > 1: # pyright: ignore + entry_deserializers = [ + _get_deserialize_callable_from_annotation(dt, module, rf) + for dt in annotation.__args__ # pyright: ignore + ] + return functools.partial(_deserialize_multiple_sequence, entry_deserializers, module) + deserializer = _get_deserialize_callable_from_annotation( + annotation.__args__[0], module, rf # pyright: ignore + ) + + return functools.partial(_deserialize_sequence, deserializer, module) + except (TypeError, IndexError, AttributeError, SyntaxError): + pass + + def _deserialize_default( + deserializer, + obj, + ): + if obj is None: + return obj + try: + return _deserialize_with_callable(deserializer, obj) + except Exception: + pass + return obj + + if get_deserializer(annotation, rf): + return functools.partial(_deserialize_default, get_deserializer(annotation, rf)) + + return functools.partial(_deserialize_default, annotation) + + +def _deserialize_with_callable( + deserializer: typing.Optional[typing.Callable[[typing.Any], typing.Any]], + value: typing.Any, +): # pylint: disable=too-many-return-statements + try: + if value is None or isinstance(value, _Null): + return None + if isinstance(value, ET.Element): + if deserializer is str: + return value.text or "" + if deserializer is int: + return int(value.text) if value.text else None + if deserializer is float: + return float(value.text) if value.text else None + if deserializer is bool: + return value.text == "true" if value.text else None + if deserializer and deserializer in _DESERIALIZE_MAPPING.values(): + return deserializer(value.text) if value.text else None + if deserializer and deserializer in _DESERIALIZE_MAPPING_WITHFORMAT.values(): + return deserializer(value.text) if value.text else None + if deserializer is None: + return value + if deserializer in [int, float, bool]: + return deserializer(value) + if isinstance(deserializer, CaseInsensitiveEnumMeta): + try: + return deserializer(value.text if isinstance(value, ET.Element) else value) + except ValueError: + # for unknown value, return raw value + return value.text if isinstance(value, ET.Element) else value + if isinstance(deserializer, type) and issubclass(deserializer, Model): + return deserializer._deserialize(value, []) + return typing.cast(typing.Callable[[typing.Any], typing.Any], deserializer)(value) + except Exception as e: + raise DeserializationError() from e + + +def _deserialize( + deserializer: typing.Any, + value: typing.Any, + module: typing.Optional[str] = None, + rf: typing.Optional["_RestField"] = None, + format: typing.Optional[str] = None, +) -> typing.Any: + if isinstance(value, PipelineResponse): + value = value.http_response.json() + if rf is None and format: + rf = _RestField(format=format) + if not isinstance(deserializer, functools.partial): + deserializer = _get_deserialize_callable_from_annotation(deserializer, module, rf) + return _deserialize_with_callable(deserializer, value) + + +def _failsafe_deserialize( + deserializer: typing.Any, + response: HttpResponse, + module: typing.Optional[str] = None, + rf: typing.Optional["_RestField"] = None, + format: typing.Optional[str] = None, +) -> typing.Any: + try: + return _deserialize(deserializer, response.json(), module, rf, format) + except Exception: # pylint: disable=broad-except + _LOGGER.warning( + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True + ) + return None + + +def _failsafe_deserialize_xml( + deserializer: typing.Any, + response: HttpResponse, +) -> typing.Any: + try: + return _deserialize_xml(deserializer, response.text()) + except Exception: # pylint: disable=broad-except + _LOGGER.warning( + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True + ) + return None + + +# pylint: disable=too-many-instance-attributes +class _RestField: + def __init__( + self, + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + is_discriminator: bool = False, + visibility: typing.Optional[list[str]] = None, + default: typing.Any = _UNSET, + format: typing.Optional[str] = None, + is_multipart_file_input: bool = False, + xml: typing.Optional[dict[str, typing.Any]] = None, + deserializer: typing.Optional[typing.Callable] = None, + ): + self._type = type + self._rest_name_input = name + self._module: typing.Optional[str] = None + self._is_discriminator = is_discriminator + self._visibility = visibility + self._is_model = False + self._is_optional = False + self._default = default + self._format = format + self._is_multipart_file_input = is_multipart_file_input + self._xml = xml if xml is not None else {} + self._deserializer = deserializer + + @property + def _class_type(self) -> typing.Any: + result = getattr(self._type, "args", [None])[0] + # type may be wrapped by nested functools.partial so we need to check for that + if isinstance(result, functools.partial): + return getattr(result, "args", [None])[0] + return result + + @property + def _rest_name(self) -> str: + if self._rest_name_input is None: + raise ValueError("Rest name was never set") + return self._rest_name_input + + def __get__(self, obj: Model, type=None): # pylint: disable=redefined-builtin + # by this point, type and rest_name will have a value bc we default + # them in __new__ of the Model class + # Use _data.get() directly to avoid triggering __getitem__ which clears the cache + item = obj._data.get(self._rest_name, _UNSET) + if item is _UNSET: + # Field not set by user; return the client default if one exists, otherwise None + return self._default if self._default is not _UNSET else None + if item is None: + return item + if self._is_model: + return item + + # For mutable types, we want mutations to directly affect _data + # Check if we've already deserialized this value + cache_attr = f"_deserialized_{self._rest_name}" + if hasattr(obj, cache_attr): + # Return the value from _data directly (it's been deserialized in place) + return obj._data.get(self._rest_name) + + # Fast path: use _deserializer directly (avoids _serialize/_deserialize chain) + if self._deserializer: + deserialized = self._deserializer(item) + else: + deserialized = _deserialize(self._type, _serialize(item, self._format), rf=self) + + # For mutable types, store the deserialized value back in _data + # so mutations directly affect _data + if isinstance(deserialized, (dict, list, set)): + obj._data[self._rest_name] = deserialized + object.__setattr__(obj, cache_attr, True) # Mark as deserialized + return deserialized + + return deserialized + + def __set__(self, obj: Model, value) -> None: + # Clear the cached deserialized object when setting a new value + cache_attr = f"_deserialized_{self._rest_name}" + if hasattr(obj, cache_attr): + object.__delattr__(obj, cache_attr) + + if value is None: + # we want to wipe out entries if users set attr to None + try: + obj.__delitem__(self._rest_name) + except KeyError: + pass + return + if self._is_model: + if not _is_model(value): + value = _deserialize(self._type, value) + obj.__setitem__(self._rest_name, value) + return + obj.__setitem__(self._rest_name, _serialize(value, self._format)) + + def _get_deserialize_callable_from_annotation( + self, annotation: typing.Any + ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]: + return _get_deserialize_callable_from_annotation(annotation, self._module, self) + + +def rest_field( + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + visibility: typing.Optional[list[str]] = None, + default: typing.Any = _UNSET, + format: typing.Optional[str] = None, + is_multipart_file_input: bool = False, + xml: typing.Optional[dict[str, typing.Any]] = None, + deserializer: typing.Optional[typing.Callable] = None, +) -> typing.Any: + return _RestField( + name=name, + type=type, + visibility=visibility, + default=default, + format=format, + is_multipart_file_input=is_multipart_file_input, + xml=xml, + deserializer=deserializer, + ) + + +def rest_discriminator( + *, + name: typing.Optional[str] = None, + type: typing.Optional[typing.Callable] = None, # pylint: disable=redefined-builtin + visibility: typing.Optional[list[str]] = None, + xml: typing.Optional[dict[str, typing.Any]] = None, +) -> typing.Any: + return _RestField(name=name, type=type, is_discriminator=True, visibility=visibility, xml=xml) + + +def serialize_xml(model: Model, exclude_readonly: bool = False) -> str: + """Serialize a model to XML. + + :param Model model: The model to serialize. + :param bool exclude_readonly: Whether to exclude readonly properties. + :returns: The XML representation of the model. + :rtype: str + """ + return ET.tostring(_get_element(model, exclude_readonly), encoding="unicode") # type: ignore + + +def _get_xml_ns(meta: dict[str, typing.Any]) -> typing.Optional[str]: + """Return the XML namespace from a metadata dict, checking both 'ns' (old-style) and 'namespace' (DPG) keys. + + :param dict meta: The metadata dictionary to extract namespace from. + :returns: The namespace string if 'ns' or 'namespace' key is present, None otherwise. + :rtype: str or None + """ + ns = meta.get("ns") + if ns is None: + ns = meta.get("namespace") + return ns + + +def _resolve_xml_ns( + prop_meta: dict[str, typing.Any], model_meta: typing.Optional[dict[str, typing.Any]] = None +) -> typing.Optional[str]: + """Resolve XML namespace for a property, falling back to model namespace when appropriate. + + Checks the property metadata first; if no namespace is found and the model does not declare + an explicit prefix, falls back to the model-level namespace. + + :param dict prop_meta: The property metadata dictionary. + :param dict model_meta: The model metadata dictionary, used as fallback. + :returns: The resolved namespace string, or None. + :rtype: str or None + """ + ns = _get_xml_ns(prop_meta) + if ns is None and model_meta is not None and not model_meta.get("prefix"): + ns = _get_xml_ns(model_meta) + return ns + + +def _set_xml_attribute(element: ET.Element, name: str, value: typing.Any, prop_meta: dict[str, typing.Any]) -> None: + """Set an XML attribute on an element, handling namespace prefix registration. + + :param ET.Element element: The element to set the attribute on. + :param str name: The default attribute name (wire name). + :param any value: The attribute value. + :param dict prop_meta: The property metadata dictionary. + """ + xml_name = prop_meta.get("name", name) + _attr_ns = _get_xml_ns(prop_meta) + if _attr_ns: + _attr_prefix = prop_meta.get("prefix") + if _attr_prefix: + _safe_register_namespace(_attr_prefix, _attr_ns) + xml_name = "{" + _attr_ns + "}" + xml_name + element.set(xml_name, _get_primitive_type_value(value)) + + +def _get_element( + o: typing.Any, + exclude_readonly: bool = False, + parent_meta: typing.Optional[dict[str, typing.Any]] = None, + wrapped_element: typing.Optional[ET.Element] = None, +) -> typing.Union[ET.Element, list[ET.Element]]: + if _is_model(o): + model_meta = getattr(o, "_xml", {}) + + # if prop is a model, then use the prop element directly, else generate a wrapper of model + if wrapped_element is None: + # When serializing as an array item (parent_meta is set), check if the parent has an + # explicit itemsName. This ensures correct element names for unwrapped arrays (where + # the element tag is the property/items name, not the model type name). + _items_name = parent_meta.get("itemsName") if parent_meta is not None else None + element_name = _items_name if _items_name else (model_meta.get("name") or o.__class__.__name__) + _model_ns = _get_xml_ns(model_meta) + wrapped_element = _create_xml_element( + element_name, + model_meta.get("prefix"), + _model_ns, + ) + + readonly_props = [] + if exclude_readonly: + readonly_props = [p._rest_name for p in o._attr_to_rest_field.values() if _is_readonly(p)] + + for k, v in o.items(): + # do not serialize readonly properties + if exclude_readonly and k in readonly_props: + continue + + prop_rest_field = _get_rest_field(o._attr_to_rest_field, k) + if prop_rest_field: + prop_meta = getattr(prop_rest_field, "_xml").copy() + # use the wire name as xml name if no specific name is set + if prop_meta.get("name") is None: + prop_meta["name"] = k + else: + # additional properties will not have rest field, use the wire name as xml name + prop_meta = {"name": k} + + # Propagate model namespace to properties only for old-style "ns"-keyed models. + # DPG-generated models use the "namespace" key and explicitly declare namespace on + # each property that needs it, so propagation is intentionally skipped for them. + if prop_meta.get("ns") is None and model_meta.get("ns"): + prop_meta["ns"] = model_meta.get("ns") + prop_meta["prefix"] = model_meta.get("prefix") + + if prop_meta.get("unwrapped", False): + # unwrapped could only set on array + wrapped_element.extend(_get_element(v, exclude_readonly, prop_meta)) + elif prop_meta.get("text", False): + # text could only set on primitive type + wrapped_element.text = _get_primitive_type_value(v) + elif prop_meta.get("attribute", False): + _set_xml_attribute(wrapped_element, k, v, prop_meta) + else: + # other wrapped prop element + wrapped_element.append(_get_wrapped_element(v, exclude_readonly, prop_meta)) + return wrapped_element + if isinstance(o, list): + return [_get_element(x, exclude_readonly, parent_meta) for x in o] # type: ignore + if isinstance(o, dict): + result = [] + _dict_ns = _get_xml_ns(parent_meta) if parent_meta else None + for k, v in o.items(): + result.append( + _get_wrapped_element( + v, + exclude_readonly, + { + "name": k, + "ns": _dict_ns, + "prefix": parent_meta.get("prefix") if parent_meta else None, + }, + ) + ) + return result + + # primitive case need to create element based on parent_meta + if parent_meta: + _items_ns = parent_meta.get("itemsNs") + if _items_ns is None: + _items_ns = _get_xml_ns(parent_meta) + return _get_wrapped_element( + o, + exclude_readonly, + { + "name": parent_meta.get("itemsName", parent_meta.get("name")), + "prefix": parent_meta.get("itemsPrefix", parent_meta.get("prefix")), + "ns": _items_ns, + }, + ) + + raise ValueError("Could not serialize value into xml: " + o) + + +def _get_wrapped_element( + v: typing.Any, + exclude_readonly: bool, + meta: typing.Optional[dict[str, typing.Any]], +) -> ET.Element: + _meta_ns = _get_xml_ns(meta) if meta else None + wrapped_element = _create_xml_element( + meta.get("name") if meta else None, meta.get("prefix") if meta else None, _meta_ns + ) + if isinstance(v, (dict, list)): + wrapped_element.extend(_get_element(v, exclude_readonly, meta)) + elif _is_model(v): + _get_element(v, exclude_readonly, meta, wrapped_element) + else: + wrapped_element.text = _get_primitive_type_value(v) + return wrapped_element # type: ignore[no-any-return] + + +def _get_primitive_type_value(v) -> str: + if v is True: + return "true" + if v is False: + return "false" + if isinstance(v, _Null): + return "" + return str(v) + + +def _safe_register_namespace(prefix: str, ns: str) -> None: + """Register an XML namespace prefix, handling reserved prefix patterns. + + Some prefixes (e.g. 'ns2') match Python's reserved 'ns\\d+' pattern used for + auto-generated prefixes, causing register_namespace to raise ValueError. + Falls back to directly registering in the internal namespace map. + + :param str prefix: The namespace prefix to register. + :param str ns: The namespace URI. + """ + try: + ET.register_namespace(prefix, ns) + except ValueError: + _ns_map = getattr(ET, "_namespace_map", None) + if _ns_map is not None: + _ns_map[ns] = prefix + + +def _create_xml_element( + tag: typing.Any, prefix: typing.Optional[str] = None, ns: typing.Optional[str] = None +) -> ET.Element: + if prefix and ns: + _safe_register_namespace(prefix, ns) + if ns: + return ET.Element("{" + ns + "}" + tag) + return ET.Element(tag) + + +def _deserialize_xml( + deserializer: typing.Any, + value: str, +) -> typing.Any: + element = ET.fromstring(value) # nosec + if _is_model(deserializer): + return deserializer._deserialize(element, []) + return _deserialize(deserializer, element) + + +def _convert_element(e: ET.Element): + # dict case + if len(e.attrib) > 0 or len({child.tag for child in e}) > 1: + dict_result: dict[str, typing.Any] = {} + for child in e: + if dict_result.get(child.tag) is not None: + if isinstance(dict_result[child.tag], list): + dict_result[child.tag].append(_convert_element(child)) + else: + dict_result[child.tag] = [dict_result[child.tag], _convert_element(child)] + else: + dict_result[child.tag] = _convert_element(child) + dict_result.update(e.attrib) + return dict_result + # array case + if len(e) > 0: + array_result: list[typing.Any] = [] + for child in e: + array_result.append(_convert_element(child)) + return array_result + # primitive case + return e.text diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/serialization.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/serialization.py new file mode 100644 index 000000000000..75906e2eb77f --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/serialization.py @@ -0,0 +1,2175 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +# pyright: reportUnnecessaryTypeIgnoreComment=false + +from base64 import b64decode, b64encode +import calendar +import datetime +import decimal +import email +from enum import Enum +import json +import logging +import re +import sys +import codecs +from typing import ( + Any, + cast, + Optional, + Union, + AnyStr, + IO, + Mapping, + Callable, + MutableMapping, +) + +try: + from urllib import quote # type: ignore +except ImportError: + from urllib.parse import quote +import xml.etree.ElementTree as ET + +import isodate # type: ignore + +from azure.core.exceptions import DeserializationError, SerializationError +from azure.core.serialization import NULL as CoreNull + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +_BOM = codecs.BOM_UTF8.decode(encoding="utf-8") + +JSON = MutableMapping[str, Any] + + +class RawDeserializer: + + # Accept "text" because we're open minded people... + JSON_REGEXP = re.compile(r"^(application|text)/([a-z+.]+\+)?json$") + + # Name used in context + CONTEXT_NAME = "deserialized_data" + + @classmethod + def deserialize_from_text(cls, data: Optional[Union[AnyStr, IO]], content_type: Optional[str] = None) -> Any: + """Decode data according to content-type. + + Accept a stream of data as well, but will be load at once in memory for now. + + If no content-type, will return the string version (not bytes, not stream) + + :param data: Input, could be bytes or stream (will be decoded with UTF8) or text + :type data: str or bytes or IO + :param str content_type: The content type. + :return: The deserialized data. + :rtype: object + """ + if hasattr(data, "read"): + # Assume a stream + data = cast(IO, data).read() + + if isinstance(data, bytes): + data_as_str = data.decode(encoding="utf-8-sig") + else: + # Explain to mypy the correct type. + data_as_str = cast(str, data) + + # Remove Byte Order Mark if present in string + data_as_str = data_as_str.lstrip(_BOM) + + if content_type is None: + return data + + if cls.JSON_REGEXP.match(content_type): + try: + return json.loads(data_as_str) + except ValueError as err: + raise DeserializationError("JSON is invalid: {}".format(err), err) from err + elif "xml" in (content_type or []): + try: + + try: + if isinstance(data, unicode): # type: ignore + # If I'm Python 2.7 and unicode XML will scream if I try a "fromstring" on unicode string + data_as_str = data_as_str.encode(encoding="utf-8") # type: ignore + except NameError: + pass + + return ET.fromstring(data_as_str) # nosec + except ET.ParseError as err: + # It might be because the server has an issue, and returned JSON with + # content-type XML.... + # So let's try a JSON load, and if it's still broken + # let's flow the initial exception + def _json_attemp(data): + try: + return True, json.loads(data) + except ValueError: + return False, None # Don't care about this one + + success, json_result = _json_attemp(data) + if success: + return json_result + # If i'm here, it's not JSON, it's not XML, let's scream + # and raise the last context in this block (the XML exception) + # The function hack is because Py2.7 messes up with exception + # context otherwise. + _LOGGER.critical("Wasn't XML not JSON, failing") + raise DeserializationError("XML is invalid") from err + elif content_type.startswith("text/"): + return data_as_str + raise DeserializationError("Cannot deserialize content-type: {}".format(content_type)) + + @classmethod + def deserialize_from_http_generics(cls, body_bytes: Optional[Union[AnyStr, IO]], headers: Mapping) -> Any: + """Deserialize from HTTP response. + + Use bytes and headers to NOT use any requests/aiohttp or whatever + specific implementation. + Headers will tested for "content-type" + + :param bytes body_bytes: The body of the response. + :param dict headers: The headers of the response. + :returns: The deserialized data. + :rtype: object + """ + # Try to use content-type from headers if available + content_type = None + if "content-type" in headers: + content_type = headers["content-type"].split(";")[0].strip().lower() + # Ouch, this server did not declare what it sent... + # Let's guess it's JSON... + # Also, since Autorest was considering that an empty body was a valid JSON, + # need that test as well.... + else: + content_type = "application/json" + + if body_bytes: + return cls.deserialize_from_text(body_bytes, content_type) + return None + + +_LOGGER = logging.getLogger(__name__) + +try: + _long_type = long # type: ignore +except NameError: + _long_type = int + +TZ_UTC = datetime.timezone.utc + +_FLATTEN = re.compile(r"(? None: + self.additional_properties: Optional[dict[str, Any]] = {} + for k in kwargs: # pylint: disable=consider-using-dict-items + if k not in self._attribute_map: + _LOGGER.warning("%s is not a known attribute of class %s and will be ignored", k, self.__class__) + elif k in self._validation and self._validation[k].get("readonly", False): + _LOGGER.warning("Readonly attribute %s will be ignored in class %s", k, self.__class__) + else: + setattr(self, k, kwargs[k]) + + def __eq__(self, other: Any) -> bool: + """Compare objects by comparing all attributes. + + :param object other: The object to compare + :returns: True if objects are equal + :rtype: bool + """ + if isinstance(other, self.__class__): + return self.__dict__ == other.__dict__ + return False + + def __ne__(self, other: Any) -> bool: + """Compare objects by comparing all attributes. + + :param object other: The object to compare + :returns: True if objects are not equal + :rtype: bool + """ + return not self.__eq__(other) + + def __str__(self) -> str: + return str(self.__dict__) + + @classmethod + def enable_additional_properties_sending(cls) -> None: + cls._attribute_map["additional_properties"] = {"key": "", "type": "{object}"} + + @classmethod + def is_xml_model(cls) -> bool: + try: + cls._xml_map # type: ignore + except AttributeError: + return False + return True + + @classmethod + def _create_xml_node(cls): + """Create XML node. + + :returns: The XML node + :rtype: xml.etree.ElementTree.Element + """ + try: + xml_map = cls._xml_map # type: ignore + except AttributeError: + xml_map = {} + + return _create_xml_node(xml_map.get("name", cls.__name__), xml_map.get("prefix", None), xml_map.get("ns", None)) + + def serialize(self, keep_readonly: bool = False, **kwargs: Any) -> JSON: + """Return the JSON that would be sent to server from this model. + + This is an alias to `as_dict(full_restapi_key_transformer, keep_readonly=False)`. + + If you want XML serialization, you can pass the kwargs is_xml=True. + + :param bool keep_readonly: If you want to serialize the readonly attributes + :returns: A dict JSON compatible object + :rtype: dict + """ + serializer = Serializer(self._infer_class_models()) + return serializer._serialize( # type: ignore # pylint: disable=protected-access + self, keep_readonly=keep_readonly, **kwargs + ) + + def as_dict( + self, + keep_readonly: bool = True, + key_transformer: Callable[[str, dict[str, Any], Any], Any] = attribute_transformer, + **kwargs: Any + ) -> JSON: + """Return a dict that can be serialized using json.dump. + + Advanced usage might optionally use a callback as parameter: + + .. code::python + + def my_key_transformer(key, attr_desc, value): + return key + + Key is the attribute name used in Python. Attr_desc + is a dict of metadata. Currently contains 'type' with the + msrest type and 'key' with the RestAPI encoded key. + Value is the current value in this object. + + The string returned will be used to serialize the key. + If the return type is a list, this is considered hierarchical + result dict. + + See the three examples in this file: + + - attribute_transformer + - full_restapi_key_transformer + - last_restapi_key_transformer + + If you want XML serialization, you can pass the kwargs is_xml=True. + + :param bool keep_readonly: If you want to serialize the readonly attributes + :param function key_transformer: A key transformer function. + :returns: A dict JSON compatible object + :rtype: dict + """ + serializer = Serializer(self._infer_class_models()) + return serializer._serialize( # type: ignore # pylint: disable=protected-access + self, key_transformer=key_transformer, keep_readonly=keep_readonly, **kwargs + ) + + @classmethod + def _infer_class_models(cls): + try: + str_models = cls.__module__.rsplit(".", 1)[0] + models = sys.modules[str_models] + client_models = {k: v for k, v in models.__dict__.items() if isinstance(v, type)} + if cls.__name__ not in client_models: + raise ValueError("Not Autorest generated code") + except Exception: # pylint: disable=broad-exception-caught + # Assume it's not Autorest generated (tests?). Add ourselves as dependencies. + client_models = {cls.__name__: cls} + return client_models + + @classmethod + def deserialize(cls, data: Any, content_type: Optional[str] = None) -> Self: + """Parse a str using the RestAPI syntax and return a model. + + :param str data: A str using RestAPI structure. JSON by default. + :param str content_type: JSON by default, set application/xml if XML. + :returns: An instance of this model + :raises DeserializationError: if something went wrong + :rtype: Self + """ + deserializer = Deserializer(cls._infer_class_models()) + return deserializer(cls.__name__, data, content_type=content_type) # type: ignore + + @classmethod + def from_dict( + cls, + data: Any, + key_extractors: Optional[Callable[[str, dict[str, Any], Any], Any]] = None, + content_type: Optional[str] = None, + ) -> Self: + """Parse a dict using given key extractor return a model. + + By default consider key + extractors (rest_key_case_insensitive_extractor, attribute_key_case_insensitive_extractor + and last_rest_key_case_insensitive_extractor) + + :param dict data: A dict using RestAPI structure + :param function key_extractors: A key extractor function. + :param str content_type: JSON by default, set application/xml if XML. + :returns: An instance of this model + :raises DeserializationError: if something went wrong + :rtype: Self + """ + deserializer = Deserializer(cls._infer_class_models()) + deserializer.key_extractors = ( # type: ignore + [ # type: ignore + attribute_key_case_insensitive_extractor, + rest_key_case_insensitive_extractor, + last_rest_key_case_insensitive_extractor, + ] + if key_extractors is None + else key_extractors + ) + return deserializer(cls.__name__, data, content_type=content_type) # type: ignore + + @classmethod + def _flatten_subtype(cls, key, objects): + if "_subtype_map" not in cls.__dict__: + return {} + result = dict(cls._subtype_map[key]) + for valuetype in cls._subtype_map[key].values(): + result |= objects[valuetype]._flatten_subtype(key, objects) # pylint: disable=protected-access + return result + + @classmethod + def _classify(cls, response, objects): + """Check the class _subtype_map for any child classes. + We want to ignore any inherited _subtype_maps. + + :param dict response: The initial data + :param dict objects: The class objects + :returns: The class to be used + :rtype: class + """ + for subtype_key in cls.__dict__.get("_subtype_map", {}).keys(): + subtype_value = None + + if not isinstance(response, ET.Element): + rest_api_response_key = cls._get_rest_key_parts(subtype_key)[-1] + subtype_value = response.get(rest_api_response_key, None) or response.get(subtype_key, None) + else: + subtype_value = xml_key_extractor(subtype_key, cls._attribute_map[subtype_key], response) + if subtype_value: + # Try to match base class. Can be class name only + # (bug to fix in Autorest to support x-ms-discriminator-name) + if cls.__name__ == subtype_value: + return cls + flatten_mapping_type = cls._flatten_subtype(subtype_key, objects) + try: + return objects[flatten_mapping_type[subtype_value]] # type: ignore + except KeyError: + _LOGGER.warning( + "Subtype value %s has no mapping, use base class %s.", + subtype_value, + cls.__name__, + ) + break + else: + _LOGGER.warning("Discriminator %s is absent or null, use base class %s.", subtype_key, cls.__name__) + break + return cls + + @classmethod + def _get_rest_key_parts(cls, attr_key): + """Get the RestAPI key of this attr, split it and decode part + :param str attr_key: Attribute key must be in attribute_map. + :returns: A list of RestAPI part + :rtype: list + """ + rest_split_key = _FLATTEN.split(cls._attribute_map[attr_key]["key"]) + return [_decode_attribute_map_key(key_part) for key_part in rest_split_key] + + +def _decode_attribute_map_key(key): + """This decode a key in an _attribute_map to the actual key we want to look at + inside the received data. + + :param str key: A key string from the generated code + :returns: The decoded key + :rtype: str + """ + return key.replace("\\.", ".") + + +class Serializer: # pylint: disable=too-many-public-methods + """Request object model serializer.""" + + basic_types = {str: "str", int: "int", bool: "bool", float: "float"} + + _xml_basic_types_serializers = {"bool": lambda x: str(x).lower()} + days = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri", 5: "Sat", 6: "Sun"} + months = { + 1: "Jan", + 2: "Feb", + 3: "Mar", + 4: "Apr", + 5: "May", + 6: "Jun", + 7: "Jul", + 8: "Aug", + 9: "Sep", + 10: "Oct", + 11: "Nov", + 12: "Dec", + } + validation = { + "min_length": lambda x, y: len(x) < y, + "max_length": lambda x, y: len(x) > y, + "minimum": lambda x, y: x < y, + "maximum": lambda x, y: x > y, + "minimum_ex": lambda x, y: x <= y, + "maximum_ex": lambda x, y: x >= y, + "min_items": lambda x, y: len(x) < y, + "max_items": lambda x, y: len(x) > y, + "pattern": lambda x, y: not re.match(y, x, re.UNICODE), + "unique": lambda x, y: len(x) != len(set(x)), + "multiple": lambda x, y: x % y != 0, + } + + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: + self.serialize_type = { + "iso-8601": Serializer.serialize_iso, + "rfc-1123": Serializer.serialize_rfc, + "unix-time": Serializer.serialize_unix, + "duration": Serializer.serialize_duration, + "duration-seconds-int": Serializer.serialize_duration_seconds_int, + "duration-seconds-float": Serializer.serialize_duration_seconds_float, + "duration-milliseconds-int": Serializer.serialize_duration_milliseconds_int, + "duration-milliseconds-float": Serializer.serialize_duration_milliseconds_float, + "date": Serializer.serialize_date, + "time": Serializer.serialize_time, + "decimal": Serializer.serialize_decimal, + "long": Serializer.serialize_long, + "bytearray": Serializer.serialize_bytearray, + "base64": Serializer.serialize_base64, + "object": self.serialize_object, + "[]": self.serialize_iter, + "{}": self.serialize_dict, + } + self.dependencies: dict[str, type] = dict(classes) if classes else {} + self.key_transformer = full_restapi_key_transformer + self.client_side_validation = True + + def _serialize( # pylint: disable=too-many-nested-blocks, too-many-branches, too-many-statements, too-many-locals + self, target_obj, data_type=None, **kwargs + ): + """Serialize data into a string according to type. + + :param object target_obj: The data to be serialized. + :param str data_type: The type to be serialized from. + :rtype: str, dict + :raises SerializationError: if serialization fails. + :returns: The serialized data. + """ + key_transformer = kwargs.get("key_transformer", self.key_transformer) + keep_readonly = kwargs.get("keep_readonly", False) + if target_obj is None: + return None + + attr_name = None + class_name = target_obj.__class__.__name__ + + if data_type: + return self.serialize_data(target_obj, data_type, **kwargs) + + if not hasattr(target_obj, "_attribute_map"): + data_type = type(target_obj).__name__ + if data_type in self.basic_types.values(): + return self.serialize_data(target_obj, data_type, **kwargs) + + # Force "is_xml" kwargs if we detect a XML model + try: + is_xml_model_serialization = kwargs["is_xml"] + except KeyError: + is_xml_model_serialization = kwargs.setdefault("is_xml", target_obj.is_xml_model()) + + serialized = {} + if is_xml_model_serialization: + serialized = target_obj._create_xml_node() # pylint: disable=protected-access + try: + attributes = target_obj._attribute_map # pylint: disable=protected-access + for attr, attr_desc in attributes.items(): + attr_name = attr + if not keep_readonly and target_obj._validation.get( # pylint: disable=protected-access + attr_name, {} + ).get("readonly", False): + continue + + if attr_name == "additional_properties" and attr_desc["key"] == "": + if target_obj.additional_properties is not None: + serialized |= target_obj.additional_properties + continue + try: + + orig_attr = getattr(target_obj, attr) + if is_xml_model_serialization: + pass # Don't provide "transformer" for XML for now. Keep "orig_attr" + else: # JSON + keys, orig_attr = key_transformer(attr, attr_desc.copy(), orig_attr) + keys = keys if isinstance(keys, list) else [keys] + + kwargs["serialization_ctxt"] = attr_desc + new_attr = self.serialize_data(orig_attr, attr_desc["type"], **kwargs) + + if is_xml_model_serialization: + xml_desc = attr_desc.get("xml", {}) + xml_name = xml_desc.get("name", attr_desc["key"]) + xml_prefix = xml_desc.get("prefix", None) + xml_ns = xml_desc.get("ns", None) + if xml_desc.get("attr", False): + if xml_ns: + ET.register_namespace(xml_prefix, xml_ns) + xml_name = "{{{}}}{}".format(xml_ns, xml_name) + serialized.set(xml_name, new_attr) # type: ignore + continue + if xml_desc.get("text", False): + serialized.text = new_attr # type: ignore + continue + if isinstance(new_attr, list): + serialized.extend(new_attr) # type: ignore + elif isinstance(new_attr, ET.Element): + # If the down XML has no XML/Name, + # we MUST replace the tag with the local tag. But keeping the namespaces. + if "name" not in getattr(orig_attr, "_xml_map", {}): + splitted_tag = new_attr.tag.split("}") + if len(splitted_tag) == 2: # Namespace + new_attr.tag = "}".join([splitted_tag[0], xml_name]) + else: + new_attr.tag = xml_name + serialized.append(new_attr) # type: ignore + else: # That's a basic type + # Integrate namespace if necessary + local_node = _create_xml_node(xml_name, xml_prefix, xml_ns) + local_node.text = str(new_attr) + serialized.append(local_node) # type: ignore + else: # JSON + for k in reversed(keys): # type: ignore + new_attr = {k: new_attr} + + _new_attr = new_attr + _serialized = serialized + for k in keys: # type: ignore + if k not in _serialized: + _serialized.update(_new_attr) # type: ignore + _new_attr = _new_attr[k] # type: ignore + _serialized = _serialized[k] + except ValueError as err: + if isinstance(err, SerializationError): + raise + + except (AttributeError, KeyError, TypeError) as err: + msg = "Attribute {} in object {} cannot be serialized.\n{}".format(attr_name, class_name, str(target_obj)) + raise SerializationError(msg) from err + return serialized + + def body(self, data, data_type, **kwargs): + """Serialize data intended for a request body. + + :param object data: The data to be serialized. + :param str data_type: The type to be serialized from. + :rtype: dict + :raises SerializationError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized request body + """ + + # Just in case this is a dict + internal_data_type_str = data_type.strip("[]{}") + internal_data_type = self.dependencies.get(internal_data_type_str, None) + try: + is_xml_model_serialization = kwargs["is_xml"] + except KeyError: + if internal_data_type and issubclass(internal_data_type, Model): + is_xml_model_serialization = kwargs.setdefault("is_xml", internal_data_type.is_xml_model()) + else: + is_xml_model_serialization = False + if internal_data_type and not isinstance(internal_data_type, Enum): + try: + deserializer = Deserializer(self.dependencies) + # Since it's on serialization, it's almost sure that format is not JSON REST + # We're not able to deal with additional properties for now. + deserializer.additional_properties_detection = False + if is_xml_model_serialization: + deserializer.key_extractors = [ # type: ignore + attribute_key_case_insensitive_extractor, + ] + else: + deserializer.key_extractors = [ + rest_key_case_insensitive_extractor, + attribute_key_case_insensitive_extractor, + last_rest_key_case_insensitive_extractor, + ] + data = deserializer._deserialize(data_type, data) # pylint: disable=protected-access + except DeserializationError as err: + raise SerializationError("Unable to build a model: " + str(err)) from err + + return self._serialize(data, data_type, **kwargs) + + def url(self, name, data, data_type, **kwargs): + """Serialize data intended for a URL path. + + :param str name: The name of the URL path parameter. + :param object data: The data to be serialized. + :param str data_type: The type to be serialized from. + :rtype: str + :returns: The serialized URL path + :raises TypeError: if serialization fails. + :raises ValueError: if data is None + """ + try: + output = self.serialize_data(data, data_type, **kwargs) + if data_type == "bool": + output = json.dumps(output) + + if kwargs.get("skip_quote") is True: + output = str(output) + output = output.replace("{", quote("{")).replace("}", quote("}")) + else: + output = quote(str(output), safe="") + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return output + + def query(self, name, data, data_type, **kwargs): + """Serialize data intended for a URL query. + + :param str name: The name of the query parameter. + :param object data: The data to be serialized. + :param str data_type: The type to be serialized from. + :rtype: str, list + :raises TypeError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized query parameter + """ + try: + # Treat the list aside, since we don't want to encode the div separator + if data_type.startswith("["): + internal_data_type = data_type[1:-1] + do_quote = not kwargs.get("skip_quote", False) + return self.serialize_iter(data, internal_data_type, do_quote=do_quote, **kwargs) + + # Not a list, regular serialization + output = self.serialize_data(data, data_type, **kwargs) + if data_type == "bool": + output = json.dumps(output) + if kwargs.get("skip_quote") is True: + output = str(output) + else: + output = quote(str(output), safe="") + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return str(output) + + def header(self, name, data, data_type, **kwargs): + """Serialize data intended for a request header. + + :param str name: The name of the header. + :param object data: The data to be serialized. + :param str data_type: The type to be serialized from. + :rtype: str + :raises TypeError: if serialization fails. + :raises ValueError: if data is None + :returns: The serialized header + """ + try: + if data_type in ["[str]"]: + data = ["" if d is None else d for d in data] + + output = self.serialize_data(data, data_type, **kwargs) + if data_type == "bool": + output = json.dumps(output) + except SerializationError as exc: + raise TypeError("{} must be type {}.".format(name, data_type)) from exc + return str(output) + + def serialize_data(self, data, data_type, **kwargs): + """Serialize generic data according to supplied data type. + + :param object data: The data to be serialized. + :param str data_type: The type to be serialized from. + :raises AttributeError: if required data is None. + :raises ValueError: if data is None + :raises SerializationError: if serialization fails. + :returns: The serialized data. + :rtype: str, int, float, bool, dict, list + """ + if data is None: + raise ValueError("No value for given attribute") + + try: + if data is CoreNull: + return None + if data_type in self.basic_types.values(): + return self.serialize_basic(data, data_type, **kwargs) + + if data_type in self.serialize_type: + return self.serialize_type[data_type](data, **kwargs) + + # If dependencies is empty, try with current data class + # It has to be a subclass of Enum anyway + enum_type = self.dependencies.get(data_type, cast(type, data.__class__)) + if issubclass(enum_type, Enum): + return Serializer.serialize_enum(data, enum_obj=enum_type) + + iter_type = data_type[0] + data_type[-1] + if iter_type in self.serialize_type: + return self.serialize_type[iter_type](data, data_type[1:-1], **kwargs) + + except (ValueError, TypeError) as err: + msg = "Unable to serialize value: {!r} as type: {!r}." + raise SerializationError(msg.format(data, data_type)) from err + return self._serialize(data, **kwargs) + + @classmethod + def _get_custom_serializers(cls, data_type, **kwargs): # pylint: disable=inconsistent-return-statements + custom_serializer = kwargs.get("basic_types_serializers", {}).get(data_type) + if custom_serializer: + return custom_serializer + if kwargs.get("is_xml", False): + return cls._xml_basic_types_serializers.get(data_type) + + @classmethod + def serialize_basic(cls, data, data_type, **kwargs): + """Serialize basic builting data type. + Serializes objects to str, int, float or bool. + + Possible kwargs: + - basic_types_serializers dict[str, callable] : If set, use the callable as serializer + - is_xml bool : If set, use xml_basic_types_serializers + + :param obj data: Object to be serialized. + :param str data_type: Type of object in the iterable. + :rtype: str, int, float, bool + :return: serialized object + :raises TypeError: raise if data_type is not one of str, int, float, bool. + """ + custom_serializer = cls._get_custom_serializers(data_type, **kwargs) + if custom_serializer: + return custom_serializer(data) + if data_type == "str": + return cls.serialize_unicode(data) + if data_type == "int": + return int(data) + if data_type == "float": + return float(data) + if data_type == "bool": + return bool(data) + raise TypeError("Unknown basic data type: {}".format(data_type)) + + @classmethod + def serialize_unicode(cls, data): + """Special handling for serializing unicode strings in Py2. + Encode to UTF-8 if unicode, otherwise handle as a str. + + :param str data: Object to be serialized. + :rtype: str + :return: serialized object + """ + try: # If I received an enum, return its value + return data.value + except AttributeError: + pass + + try: + if isinstance(data, unicode): # type: ignore + # Don't change it, JSON and XML ElementTree are totally able + # to serialize correctly u'' strings + return data + except NameError: + return str(data) + return str(data) + + def serialize_iter(self, data, iter_type, div=None, **kwargs): + """Serialize iterable. + + Supported kwargs: + - serialization_ctxt dict : The current entry of _attribute_map, or same format. + serialization_ctxt['type'] should be same as data_type. + - is_xml bool : If set, serialize as XML + + :param list data: Object to be serialized. + :param str iter_type: Type of object in the iterable. + :param str div: If set, this str will be used to combine the elements + in the iterable into a combined string. Default is 'None'. + Defaults to False. + :rtype: list, str + :return: serialized iterable + """ + if isinstance(data, str): + raise SerializationError("Refuse str type as a valid iter type.") + + serialization_ctxt = kwargs.get("serialization_ctxt", {}) + is_xml = kwargs.get("is_xml", False) + + serialized = [] + for d in data: + try: + serialized.append(self.serialize_data(d, iter_type, **kwargs)) + except ValueError as err: + if isinstance(err, SerializationError): + raise + serialized.append(None) + + if kwargs.get("do_quote", False): + serialized = ["" if s is None else quote(str(s), safe="") for s in serialized] + + if div: + serialized = ["" if s is None else str(s) for s in serialized] + serialized = div.join(serialized) + + if "xml" in serialization_ctxt or is_xml: + # XML serialization is more complicated + xml_desc = serialization_ctxt.get("xml", {}) + xml_name = xml_desc.get("name") + if not xml_name: + xml_name = serialization_ctxt["key"] + + # Create a wrap node if necessary (use the fact that Element and list have "append") + is_wrapped = xml_desc.get("wrapped", False) + node_name = xml_desc.get("itemsName", xml_name) + if is_wrapped: + final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + else: + final_result = [] + # All list elements to "local_node" + for el in serialized: + if isinstance(el, ET.Element): + el_node = el + else: + el_node = _create_xml_node(node_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + if el is not None: # Otherwise it writes "None" :-p + el_node.text = str(el) + final_result.append(el_node) + return final_result + return serialized + + def serialize_dict(self, attr, dict_type, **kwargs): + """Serialize a dictionary of objects. + + :param dict attr: Object to be serialized. + :param str dict_type: Type of object in the dictionary. + :rtype: dict + :return: serialized dictionary + """ + serialization_ctxt = kwargs.get("serialization_ctxt", {}) + serialized = {} + for key, value in attr.items(): + try: + serialized[self.serialize_unicode(key)] = self.serialize_data(value, dict_type, **kwargs) + except ValueError as err: + if isinstance(err, SerializationError): + raise + serialized[self.serialize_unicode(key)] = None + + if "xml" in serialization_ctxt: + # XML serialization is more complicated + xml_desc = serialization_ctxt["xml"] + xml_name = xml_desc["name"] + + final_result = _create_xml_node(xml_name, xml_desc.get("prefix", None), xml_desc.get("ns", None)) + for key, value in serialized.items(): + ET.SubElement(final_result, key).text = value + return final_result + + return serialized + + def serialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements + """Serialize a generic object. + This will be handled as a dictionary. If object passed in is not + a basic type (str, int, float, dict, list) it will simply be + cast to str. + + :param dict attr: Object to be serialized. + :rtype: dict or str + :return: serialized object + """ + if attr is None: + return None + if isinstance(attr, ET.Element): + return attr + obj_type = type(attr) + if obj_type in self.basic_types: + return self.serialize_basic(attr, self.basic_types[obj_type], **kwargs) + if obj_type is _long_type: + return self.serialize_long(attr) + if obj_type is str: + return self.serialize_unicode(attr) + if obj_type is datetime.datetime: + return self.serialize_iso(attr) + if obj_type is datetime.date: + return self.serialize_date(attr) + if obj_type is datetime.time: + return self.serialize_time(attr) + if obj_type is datetime.timedelta: + return self.serialize_duration(attr) + if obj_type is decimal.Decimal: + return self.serialize_decimal(attr) + + # If it's a model or I know this dependency, serialize as a Model + if obj_type in self.dependencies.values() or isinstance(attr, Model): + return self._serialize(attr) + + if obj_type == dict: + serialized = {} + for key, value in attr.items(): + try: + serialized[self.serialize_unicode(key)] = self.serialize_object(value, **kwargs) + except ValueError: + serialized[self.serialize_unicode(key)] = None + return serialized + + if obj_type == list: + serialized = [] + for obj in attr: + try: + serialized.append(self.serialize_object(obj, **kwargs)) + except ValueError: + pass + return serialized + return str(attr) + + @staticmethod + def serialize_enum(attr, enum_obj=None): + try: + result = attr.value + except AttributeError: + result = attr + try: + enum_obj(result) # type: ignore + return result + except ValueError as exc: + for enum_value in enum_obj: # type: ignore + if enum_value.value.lower() == str(attr).lower(): + return enum_value.value + error = "{!r} is not valid value for enum {!r}" + raise SerializationError(error.format(attr, enum_obj)) from exc + + @staticmethod + def serialize_bytearray(attr, **kwargs): # pylint: disable=unused-argument + """Serialize bytearray into base-64 string. + + :param str attr: Object to be serialized. + :rtype: str + :return: serialized base64 + """ + return b64encode(attr).decode() + + @staticmethod + def serialize_base64(attr, **kwargs): # pylint: disable=unused-argument + """Serialize str into base-64 string. + + :param str attr: Object to be serialized. + :rtype: str + :return: serialized base64 + """ + encoded = b64encode(attr).decode("ascii") + return encoded.strip("=").replace("+", "-").replace("/", "_") + + @staticmethod + def serialize_decimal(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Decimal object to float. + + :param decimal attr: Object to be serialized. + :rtype: float + :return: serialized decimal + """ + return float(attr) + + @staticmethod + def serialize_long(attr, **kwargs): # pylint: disable=unused-argument + """Serialize long (Py2) or int (Py3). + + :param int attr: Object to be serialized. + :rtype: int/long + :return: serialized long + """ + return _long_type(attr) + + @staticmethod + def serialize_date(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Date object into ISO-8601 formatted string. + + :param Date attr: Object to be serialized. + :rtype: str + :return: serialized date + """ + if isinstance(attr, str): + attr = isodate.parse_date(attr) + t = "{:04}-{:02}-{:02}".format(attr.year, attr.month, attr.day) + return t + + @staticmethod + def serialize_time(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Time object into ISO-8601 formatted string. + + :param datetime.time attr: Object to be serialized. + :rtype: str + :return: serialized time + """ + if isinstance(attr, str): + attr = isodate.parse_time(attr) + t = "{:02}:{:02}:{:02}".format(attr.hour, attr.minute, attr.second) + if attr.microsecond: + t += ".{:02}".format(attr.microsecond) + return t + + @staticmethod + def serialize_duration(attr, **kwargs): # pylint: disable=unused-argument + """Serialize TimeDelta object into ISO-8601 formatted string. + + :param TimeDelta attr: Object to be serialized. + :rtype: str + :return: serialized duration + """ + if isinstance(attr, str): + attr = isodate.parse_duration(attr) + return isodate.duration_isoformat(attr) + + @staticmethod + def _serialize_duration_numeric(attr, scale, as_int): + """Serialize a TimeDelta into a numeric value scaled to the wire unit. + + :param TimeDelta attr: Object to be serialized. + :param int scale: Multiplier applied to total seconds (1 for seconds, 1000 for milliseconds). + :param bool as_int: Whether to truncate the result to an int. + :rtype: int or float + :return: serialized duration + """ + if isinstance(attr, str): + attr = isodate.parse_duration(attr) + value = attr.total_seconds() * scale if isinstance(attr, datetime.timedelta) else attr + return int(value) if as_int else float(value) + + @staticmethod + def serialize_duration_seconds_int(attr, **kwargs): # pylint: disable=unused-argument + """Serialize TimeDelta object into an integer number of seconds. + + :param TimeDelta attr: Object to be serialized. + :rtype: int + :return: serialized duration + """ + return Serializer._serialize_duration_numeric(attr, 1, True) + + @staticmethod + def serialize_duration_seconds_float(attr, **kwargs): # pylint: disable=unused-argument + """Serialize TimeDelta object into a floating point number of seconds. + + :param TimeDelta attr: Object to be serialized. + :rtype: float + :return: serialized duration + """ + return Serializer._serialize_duration_numeric(attr, 1, False) + + @staticmethod + def serialize_duration_milliseconds_int(attr, **kwargs): # pylint: disable=unused-argument + """Serialize TimeDelta object into an integer number of milliseconds. + + :param TimeDelta attr: Object to be serialized. + :rtype: int + :return: serialized duration + """ + return Serializer._serialize_duration_numeric(attr, 1000, True) + + @staticmethod + def serialize_duration_milliseconds_float(attr, **kwargs): # pylint: disable=unused-argument + """Serialize TimeDelta object into a floating point number of milliseconds. + + :param TimeDelta attr: Object to be serialized. + :rtype: float + :return: serialized duration + """ + return Serializer._serialize_duration_numeric(attr, 1000, False) + + @staticmethod + def serialize_rfc(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Datetime object into RFC-1123 formatted string. + + :param Datetime attr: Object to be serialized. + :rtype: str + :raises TypeError: if format invalid. + :return: serialized rfc + """ + try: + if not attr.tzinfo: + _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + utc = attr.utctimetuple() + except AttributeError as exc: + raise TypeError("RFC1123 object must be valid Datetime object.") from exc + + return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format( + Serializer.days[utc.tm_wday], + utc.tm_mday, + Serializer.months[utc.tm_mon], + utc.tm_year, + utc.tm_hour, + utc.tm_min, + utc.tm_sec, + ) + + @staticmethod + def serialize_iso(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Datetime object into ISO-8601 formatted string. + + :param Datetime attr: Object to be serialized. + :rtype: str + :raises SerializationError: if format invalid. + :return: serialized iso + """ + if isinstance(attr, str): + attr = isodate.parse_datetime(attr) + try: + if not attr.tzinfo: + _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + utc = attr.utctimetuple() + if utc.tm_year > 9999 or utc.tm_year < 1: + raise OverflowError("Hit max or min date") + + microseconds = str(attr.microsecond).rjust(6, "0").rstrip("0").ljust(3, "0") + if microseconds: + microseconds = "." + microseconds + date = "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}".format( + utc.tm_year, utc.tm_mon, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec + ) + return date + microseconds + "Z" + except (ValueError, OverflowError) as err: + msg = "Unable to serialize datetime object." + raise SerializationError(msg) from err + except AttributeError as err: + msg = "ISO-8601 object must be valid Datetime object." + raise TypeError(msg) from err + + @staticmethod + def serialize_unix(attr, **kwargs): # pylint: disable=unused-argument + """Serialize Datetime object into IntTime format. + This is represented as seconds. + + :param Datetime attr: Object to be serialized. + :rtype: int + :raises SerializationError: if format invalid + :return: serialied unix + """ + if isinstance(attr, int): + return attr + try: + if not attr.tzinfo: + _LOGGER.warning("Datetime with no tzinfo will be considered UTC.") + return int(calendar.timegm(attr.utctimetuple())) + except AttributeError as exc: + raise TypeError("Unix time object must be valid Datetime object.") from exc + + +def rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument + key = attr_desc["key"] + working_data = data + + while "." in key: + # Need the cast, as for some reasons "split" is typed as list[str | Any] + dict_keys = cast(list[str], _FLATTEN.split(key)) + if len(dict_keys) == 1: + key = _decode_attribute_map_key(dict_keys[0]) + break + working_key = _decode_attribute_map_key(dict_keys[0]) + working_data = working_data.get(working_key, data) + if working_data is None: + # If at any point while following flatten JSON path see None, it means + # that all properties under are None as well + return None + key = ".".join(dict_keys[1:]) + + return working_data.get(key) + + +def rest_key_case_insensitive_extractor( # pylint: disable=unused-argument, inconsistent-return-statements + attr, attr_desc, data +): + key = attr_desc["key"] + working_data = data + + while "." in key: + dict_keys = _FLATTEN.split(key) + if len(dict_keys) == 1: + key = _decode_attribute_map_key(dict_keys[0]) + break + working_key = _decode_attribute_map_key(dict_keys[0]) + working_data = attribute_key_case_insensitive_extractor(working_key, None, working_data) + if working_data is None: + # If at any point while following flatten JSON path see None, it means + # that all properties under are None as well + return None + key = ".".join(dict_keys[1:]) + + if working_data: + return attribute_key_case_insensitive_extractor(key, None, working_data) + + +def last_rest_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument + """Extract the attribute in "data" based on the last part of the JSON path key. + + :param str attr: The attribute to extract + :param dict attr_desc: The attribute description + :param dict data: The data to extract from + :rtype: object + :returns: The extracted attribute + """ + key = attr_desc["key"] + dict_keys = _FLATTEN.split(key) + return attribute_key_extractor(dict_keys[-1], None, data) + + +def last_rest_key_case_insensitive_extractor(attr, attr_desc, data): # pylint: disable=unused-argument + """Extract the attribute in "data" based on the last part of the JSON path key. + + This is the case insensitive version of "last_rest_key_extractor" + :param str attr: The attribute to extract + :param dict attr_desc: The attribute description + :param dict data: The data to extract from + :rtype: object + :returns: The extracted attribute + """ + key = attr_desc["key"] + dict_keys = _FLATTEN.split(key) + return attribute_key_case_insensitive_extractor(dict_keys[-1], None, data) + + +def attribute_key_extractor(attr, _, data): + return data.get(attr) + + +def attribute_key_case_insensitive_extractor(attr, _, data): + found_key = None + lower_attr = attr.lower() + for key in data: + if lower_attr == key.lower(): + found_key = key + break + + return data.get(found_key) + + +def _extract_name_from_internal_type(internal_type): + """Given an internal type XML description, extract correct XML name with namespace. + + :param dict internal_type: An model type + :rtype: tuple + :returns: A tuple XML name + namespace dict + """ + internal_type_xml_map = getattr(internal_type, "_xml_map", {}) + xml_name = internal_type_xml_map.get("name", internal_type.__name__) + xml_ns = internal_type_xml_map.get("ns", None) + if xml_ns: + xml_name = "{{{}}}{}".format(xml_ns, xml_name) + return xml_name + + +def xml_key_extractor(attr, attr_desc, data): # pylint: disable=unused-argument,too-many-return-statements + if isinstance(data, dict): + return None + + # Test if this model is XML ready first + if not isinstance(data, ET.Element): + return None + + xml_desc = attr_desc.get("xml", {}) + xml_name = xml_desc.get("name", attr_desc["key"]) + + # Look for a children + is_iter_type = attr_desc["type"].startswith("[") + is_wrapped = xml_desc.get("wrapped", False) + internal_type = attr_desc.get("internalType", None) + internal_type_xml_map = getattr(internal_type, "_xml_map", {}) + + # Integrate namespace if necessary + xml_ns = xml_desc.get("ns", internal_type_xml_map.get("ns", None)) + if xml_ns: + xml_name = "{{{}}}{}".format(xml_ns, xml_name) + + # If it's an attribute, that's simple + if xml_desc.get("attr", False): + return data.get(xml_name) + + # If it's x-ms-text, that's simple too + if xml_desc.get("text", False): + return data.text + + # Scenario where I take the local name: + # - Wrapped node + # - Internal type is an enum (considered basic types) + # - Internal type has no XML/Name node + if is_wrapped or (internal_type and (issubclass(internal_type, Enum) or "name" not in internal_type_xml_map)): + children = data.findall(xml_name) + # If internal type has a local name and it's not a list, I use that name + elif not is_iter_type and internal_type and "name" in internal_type_xml_map: + xml_name = _extract_name_from_internal_type(internal_type) + children = data.findall(xml_name) + # That's an array + else: + if internal_type: # Complex type, ignore itemsName and use the complex type name + items_name = _extract_name_from_internal_type(internal_type) + else: + items_name = xml_desc.get("itemsName", xml_name) + children = data.findall(items_name) + + if len(children) == 0: + if is_iter_type: + if is_wrapped: + return None # is_wrapped no node, we want None + return [] # not wrapped, assume empty list + return None # Assume it's not there, maybe an optional node. + + # If is_iter_type and not wrapped, return all found children + if is_iter_type: + if not is_wrapped: + return children + # Iter and wrapped, should have found one node only (the wrap one) + if len(children) != 1: + raise DeserializationError( + "Tried to deserialize an array not wrapped, and found several nodes '{}'. Maybe you should declare this array as wrapped?".format( + xml_name + ) + ) + return list(children[0]) # Might be empty list and that's ok. + + # Here it's not a itertype, we should have found one element only or empty + if len(children) > 1: + raise DeserializationError("Find several XML '{}' where it was not expected".format(xml_name)) + return children[0] + + +class Deserializer: + """Response object model deserializer. + + :param dict classes: Class type dictionary for deserializing complex types. + :ivar list key_extractors: Ordered list of extractors to be used by this deserializer. + """ + + basic_types = {str: "str", int: "int", bool: "bool", float: "float"} + + valid_date = re.compile(r"\d{4}[-]\d{2}[-]\d{2}T\d{2}:\d{2}:\d{2}\.?\d*Z?[-+]?[\d{2}]?:?[\d{2}]?") + + def __init__(self, classes: Optional[Mapping[str, type]] = None) -> None: + self.deserialize_type = { + "iso-8601": Deserializer.deserialize_iso, + "rfc-1123": Deserializer.deserialize_rfc, + "unix-time": Deserializer.deserialize_unix, + "duration": Deserializer.deserialize_duration, + "duration-seconds-int": Deserializer.deserialize_duration_seconds, + "duration-seconds-float": Deserializer.deserialize_duration_seconds, + "duration-milliseconds-int": Deserializer.deserialize_duration_milliseconds, + "duration-milliseconds-float": Deserializer.deserialize_duration_milliseconds, + "date": Deserializer.deserialize_date, + "time": Deserializer.deserialize_time, + "decimal": Deserializer.deserialize_decimal, + "long": Deserializer.deserialize_long, + "bytearray": Deserializer.deserialize_bytearray, + "base64": Deserializer.deserialize_base64, + "object": self.deserialize_object, + "[]": self.deserialize_iter, + "{}": self.deserialize_dict, + } + self.deserialize_expected_types = { + "duration": (isodate.Duration, datetime.timedelta), + "duration-seconds-int": (isodate.Duration, datetime.timedelta), + "duration-seconds-float": (isodate.Duration, datetime.timedelta), + "duration-milliseconds-int": (isodate.Duration, datetime.timedelta), + "duration-milliseconds-float": (isodate.Duration, datetime.timedelta), + "iso-8601": (datetime.datetime), + } + self.dependencies: dict[str, type] = dict(classes) if classes else {} + self.key_extractors = [rest_key_extractor, xml_key_extractor] + # Additional properties only works if the "rest_key_extractor" is used to + # extract the keys. Making it to work whatever the key extractor is too much + # complicated, with no real scenario for now. + # So adding a flag to disable additional properties detection. This flag should be + # used if your expect the deserialization to NOT come from a JSON REST syntax. + # Otherwise, result are unexpected + self.additional_properties_detection = True + + def __call__(self, target_obj, response_data, content_type=None): # pylint: disable=too-many-return-statements + """Call the deserializer to process a REST response. + + :param str target_obj: Target data type to deserialize to. + :param requests.Response response_data: REST response object. + :param str content_type: Swagger "produces" if available. + :raises DeserializationError: if deserialization fails. + :return: Deserialized object. + :rtype: object + """ + # Fast path for header deserialization: response_data is a plain str or None + # and target_obj is a simple scalar type. This avoids the expensive + # _unpack_content → _deserialize → _classify_target → deserialize_data chain. + if response_data is None: + return None + if target_obj == "str" and isinstance(response_data, str): + return response_data + if isinstance(response_data, str): + if target_obj == "int": + return int(response_data) + if target_obj == "bool": + if response_data in ("true", "1", "True"): + return True + if response_data in ("false", "0", "False"): + return False + return bool(response_data) + if target_obj == "rfc-1123": + return Deserializer.deserialize_rfc(response_data) + if target_obj == "bytearray": + return Deserializer.deserialize_bytearray(response_data) + + data = self._unpack_content(response_data, content_type) + return self._deserialize(target_obj, data) + + def _deserialize(self, target_obj, data): # pylint: disable=inconsistent-return-statements + """Call the deserializer on a model. + + Data needs to be already deserialized as JSON or XML ElementTree + + :param str target_obj: Target data type to deserialize to. + :param object data: Object to deserialize. + :raises DeserializationError: if deserialization fails. + :return: Deserialized object. + :rtype: object + """ + # This is already a model, go recursive just in case + if hasattr(data, "_attribute_map"): + constants = [name for name, config in getattr(data, "_validation", {}).items() if config.get("constant")] + try: + for attr, mapconfig in data._attribute_map.items(): # pylint: disable=protected-access + if attr in constants: + continue + value = getattr(data, attr) + if value is None: + continue + local_type = mapconfig["type"] + internal_data_type = local_type.strip("[]{}") + if internal_data_type not in self.dependencies or isinstance(internal_data_type, Enum): + continue + setattr(data, attr, self._deserialize(local_type, value)) + return data + except AttributeError: + return + + response, class_name = self._classify_target(target_obj, data) + + if isinstance(response, str): + return self.deserialize_data(data, response) + if isinstance(response, type) and issubclass(response, Enum): + return self.deserialize_enum(data, response) + + if data is None or data is CoreNull: + return data + try: + attributes = response._attribute_map # type: ignore # pylint: disable=protected-access + d_attrs = {} + for attr, attr_desc in attributes.items(): + # Check empty string. If it's not empty, someone has a real "additionalProperties"... + if attr == "additional_properties" and attr_desc["key"] == "": + continue + raw_value = None + # Enhance attr_desc with some dynamic data + attr_desc = attr_desc.copy() # Do a copy, do not change the real one + internal_data_type = attr_desc["type"].strip("[]{}") + if internal_data_type in self.dependencies: + attr_desc["internalType"] = self.dependencies[internal_data_type] + + for key_extractor in self.key_extractors: + found_value = key_extractor(attr, attr_desc, data) + if found_value is not None: + if raw_value is not None and raw_value != found_value: + msg = ( + "Ignoring extracted value '%s' from %s for key '%s'" + " (duplicate extraction, follow extractors order)" + ) + _LOGGER.warning(msg, found_value, key_extractor, attr) + continue + raw_value = found_value + + value = self.deserialize_data(raw_value, attr_desc["type"]) + d_attrs[attr] = value + except (AttributeError, TypeError, KeyError) as err: + msg = "Unable to deserialize to object: " + class_name # type: ignore + raise DeserializationError(msg) from err + additional_properties = self._build_additional_properties(attributes, data) + return self._instantiate_model(response, d_attrs, additional_properties) + + def _build_additional_properties(self, attribute_map, data): + if not self.additional_properties_detection: + return None + if "additional_properties" in attribute_map and attribute_map.get("additional_properties", {}).get("key") != "": + # Check empty string. If it's not empty, someone has a real "additionalProperties" + return None + if isinstance(data, ET.Element): + data = {el.tag: el.text for el in data} + + known_keys = { + _decode_attribute_map_key(_FLATTEN.split(desc["key"])[0]) + for desc in attribute_map.values() + if desc["key"] != "" + } + present_keys = set(data.keys()) + missing_keys = present_keys - known_keys + return {key: data[key] for key in missing_keys} + + def _classify_target(self, target, data): + """Check to see whether the deserialization target object can + be classified into a subclass. + Once classification has been determined, initialize object. + + :param str target: The target object type to deserialize to. + :param str/dict data: The response data to deserialize. + :return: The classified target object and its class name. + :rtype: tuple + """ + if target is None: + return None, None + + if isinstance(target, str): + try: + target = self.dependencies[target] + except KeyError: + return target, target + + try: + target = target._classify(data, self.dependencies) # type: ignore # pylint: disable=protected-access + except AttributeError: + pass # Target is not a Model, no classify + return target, target.__class__.__name__ # type: ignore + + def failsafe_deserialize(self, target_obj, data, content_type=None): + """Ignores any errors encountered in deserialization, + and falls back to not deserializing the object. Recommended + for use in error deserialization, as we want to return the + HttpResponseError to users, and not have them deal with + a deserialization error. + + :param str target_obj: The target object type to deserialize to. + :param str/dict data: The response data to deserialize. + :param str content_type: Swagger "produces" if available. + :return: Deserialized object. + :rtype: object + """ + try: + return self(target_obj, data, content_type=content_type) + except: # pylint: disable=bare-except + _LOGGER.debug( + "Ran into a deserialization error. Ignoring since this is failsafe deserialization", exc_info=True + ) + return None + + @staticmethod + def _unpack_content(raw_data, content_type=None): + """Extract the correct structure for deserialization. + + If raw_data is a PipelineResponse, try to extract the result of RawDeserializer. + if we can't, raise. Your Pipeline should have a RawDeserializer. + + If not a pipeline response and raw_data is bytes or string, use content-type + to decode it. If no content-type, try JSON. + + If raw_data is something else, bypass all logic and return it directly. + + :param obj raw_data: Data to be processed. + :param str content_type: How to parse if raw_data is a string/bytes. + :raises JSONDecodeError: If JSON is requested and parsing is impossible. + :raises UnicodeDecodeError: If bytes is not UTF8 + :rtype: object + :return: Unpacked content. + """ + # Assume this is enough to detect a Pipeline Response without importing it + context = getattr(raw_data, "context", {}) + if context: + if RawDeserializer.CONTEXT_NAME in context: + return context[RawDeserializer.CONTEXT_NAME] + raise ValueError("This pipeline didn't have the RawDeserializer policy; can't deserialize") + + # Assume this is enough to recognize universal_http.ClientResponse without importing it + if hasattr(raw_data, "body"): + return RawDeserializer.deserialize_from_http_generics(raw_data.text(), raw_data.headers) + + # Assume this enough to recognize requests.Response without importing it. + if hasattr(raw_data, "_content_consumed"): + return RawDeserializer.deserialize_from_http_generics(raw_data.text, raw_data.headers) + + if isinstance(raw_data, (str, bytes)) or hasattr(raw_data, "read"): + return RawDeserializer.deserialize_from_text(raw_data, content_type) # type: ignore + return raw_data + + def _instantiate_model(self, response, attrs, additional_properties=None): + """Instantiate a response model passing in deserialized args. + + :param Response response: The response model class. + :param dict attrs: The deserialized response attributes. + :param dict additional_properties: Additional properties to be set. + :rtype: Response + :return: The instantiated response model. + """ + if callable(response): + subtype = getattr(response, "_subtype_map", {}) + try: + readonly = [ + k + for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore + if v.get("readonly") + ] + const = [ + k + for k, v in response._validation.items() # pylint: disable=protected-access # type: ignore + if v.get("constant") + ] + kwargs = {k: v for k, v in attrs.items() if k not in subtype and k not in readonly + const} + response_obj = response(**kwargs) + for attr in readonly: + setattr(response_obj, attr, attrs.get(attr)) + if additional_properties: + response_obj.additional_properties = additional_properties # type: ignore + return response_obj + except TypeError as err: + msg = "Unable to deserialize {} into model {}. ".format(kwargs, response) # type: ignore + raise DeserializationError(msg + str(err)) from err + else: + try: + for attr, value in attrs.items(): + setattr(response, attr, value) + return response + except Exception as exp: + msg = "Unable to populate response model. " + msg += "Type: {}, Error: {}".format(type(response), exp) + raise DeserializationError(msg) from exp + + def deserialize_data(self, data, data_type): # pylint: disable=too-many-return-statements + """Process data for deserialization according to data type. + + :param str data: The response string to be deserialized. + :param str data_type: The type to deserialize to. + :raises DeserializationError: if deserialization fails. + :return: Deserialized object. + :rtype: object + """ + if data is None: + return data + + try: + if not data_type: + return data + if data_type in self.basic_types.values(): + return self.deserialize_basic(data, data_type) + if data_type in self.deserialize_type: + if isinstance(data, self.deserialize_expected_types.get(data_type, tuple())): + return data + + is_a_text_parsing_type = lambda x: x not in [ # pylint: disable=unnecessary-lambda-assignment + "object", + "[]", + r"{}", + ] + if isinstance(data, ET.Element) and is_a_text_parsing_type(data_type) and not data.text: + return None + data_val = self.deserialize_type[data_type](data) + return data_val + + iter_type = data_type[0] + data_type[-1] + if iter_type in self.deserialize_type: + return self.deserialize_type[iter_type](data, data_type[1:-1]) + + obj_type = self.dependencies[data_type] + if issubclass(obj_type, Enum): + if isinstance(data, ET.Element): + data = data.text + return self.deserialize_enum(data, obj_type) + + except (ValueError, TypeError, AttributeError) as err: + msg = "Unable to deserialize response data." + msg += " Data: {}, {}".format(data, data_type) + raise DeserializationError(msg) from err + return self._deserialize(obj_type, data) + + def deserialize_iter(self, attr, iter_type): + """Deserialize an iterable. + + :param list attr: Iterable to be deserialized. + :param str iter_type: The type of object in the iterable. + :return: Deserialized iterable. + :rtype: list + """ + if attr is None: + return None + if isinstance(attr, ET.Element): # If I receive an element here, get the children + attr = list(attr) + if not isinstance(attr, (list, set)): + raise DeserializationError("Cannot deserialize as [{}] an object of type {}".format(iter_type, type(attr))) + return [self.deserialize_data(a, iter_type) for a in attr] + + def deserialize_dict(self, attr, dict_type): + """Deserialize a dictionary. + + :param dict/list attr: Dictionary to be deserialized. Also accepts + a list of key, value pairs. + :param str dict_type: The object type of the items in the dictionary. + :return: Deserialized dictionary. + :rtype: dict + """ + if isinstance(attr, list): + return {x["key"]: self.deserialize_data(x["value"], dict_type) for x in attr} + + if isinstance(attr, ET.Element): + # Transform value into {"Key": "value"} + attr = {el.tag: el.text for el in attr} + return {k: self.deserialize_data(v, dict_type) for k, v in attr.items()} + + def deserialize_object(self, attr, **kwargs): # pylint: disable=too-many-return-statements + """Deserialize a generic object. + This will be handled as a dictionary. + + :param dict attr: Dictionary to be deserialized. + :return: Deserialized object. + :rtype: dict + :raises TypeError: if non-builtin datatype encountered. + """ + if attr is None: + return None + if isinstance(attr, ET.Element): + # Do no recurse on XML, just return the tree as-is + return attr + if isinstance(attr, str): + return self.deserialize_basic(attr, "str") + obj_type = type(attr) + if obj_type in self.basic_types: + return self.deserialize_basic(attr, self.basic_types[obj_type]) + if obj_type is _long_type: + return self.deserialize_long(attr) + + if obj_type == dict: + deserialized = {} + for key, value in attr.items(): + try: + deserialized[key] = self.deserialize_object(value, **kwargs) + except ValueError: + deserialized[key] = None + return deserialized + + if obj_type == list: + deserialized = [] + for obj in attr: + try: + deserialized.append(self.deserialize_object(obj, **kwargs)) + except ValueError: + pass + return deserialized + + error = "Cannot deserialize generic object with type: " + raise TypeError(error + str(obj_type)) + + def deserialize_basic(self, attr, data_type): # pylint: disable=too-many-return-statements + """Deserialize basic builtin data type from string. + Will attempt to convert to str, int, float and bool. + This function will also accept '1', '0', 'true' and 'false' as + valid bool values. + + :param str attr: response string to be deserialized. + :param str data_type: deserialization data type. + :return: Deserialized basic type. + :rtype: str, int, float or bool + :raises TypeError: if string format is not valid or data_type is not one of str, int, float, bool. + """ + # If we're here, data is supposed to be a basic type. + # If it's still an XML node, take the text + if isinstance(attr, ET.Element): + attr = attr.text + if not attr: + if data_type == "str": + # None or '', node is empty string. + return "" + # None or '', node with a strong type is None. + # Don't try to model "empty bool" or "empty int" + return None + + if data_type == "bool": + if attr in [True, False, 1, 0]: + return bool(attr) + if isinstance(attr, str): + if attr.lower() in ["true", "1"]: + return True + if attr.lower() in ["false", "0"]: + return False + raise TypeError("Invalid boolean value: {}".format(attr)) + + if data_type == "str": + return self.deserialize_unicode(attr) + if data_type == "int": + return int(attr) + if data_type == "float": + return float(attr) + raise TypeError("Unknown basic data type: {}".format(data_type)) + + @staticmethod + def deserialize_unicode(data): + """Preserve unicode objects in Python 2, otherwise return data + as a string. + + :param str data: response string to be deserialized. + :return: Deserialized string. + :rtype: str or unicode + """ + # We might be here because we have an enum modeled as string, + # and we try to deserialize a partial dict with enum inside + if isinstance(data, Enum): + return data + + # Consider this is real string + try: + if isinstance(data, unicode): # type: ignore + return data + except NameError: + return str(data) + return str(data) + + @staticmethod + def deserialize_enum(data, enum_obj): + """Deserialize string into enum object. + + If the string is not a valid enum value it will be returned as-is + and a warning will be logged. + + :param str data: Response string to be deserialized. If this value is + None or invalid it will be returned as-is. + :param Enum enum_obj: Enum object to deserialize to. + :return: Deserialized enum object. + :rtype: Enum + """ + if isinstance(data, enum_obj) or data is None: + return data + if isinstance(data, Enum): + data = data.value + if isinstance(data, int): + # Workaround. We might consider remove it in the future. + try: + return list(enum_obj.__members__.values())[data] + except IndexError as exc: + error = "{!r} is not a valid index for enum {!r}" + raise DeserializationError(error.format(data, enum_obj)) from exc + try: + return enum_obj(str(data)) + except ValueError: + for enum_value in enum_obj: + if enum_value.value.lower() == str(data).lower(): + return enum_value + # We don't fail anymore for unknown value, we deserialize as a string + _LOGGER.warning("Deserializer is not able to find %s as valid enum in %s", data, enum_obj) + return Deserializer.deserialize_unicode(data) + + @staticmethod + def deserialize_bytearray(attr): + """Deserialize string into bytearray. + + :param str attr: response string to be deserialized. + :return: Deserialized bytearray + :rtype: bytearray + :raises TypeError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + return bytearray(b64decode(attr)) # type: ignore + + @staticmethod + def deserialize_base64(attr): + """Deserialize base64 encoded string into string. + + :param str attr: response string to be deserialized. + :return: Deserialized base64 string + :rtype: bytearray + :raises TypeError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + padding = "=" * (3 - (len(attr) + 3) % 4) # type: ignore + attr = attr + padding # type: ignore + encoded = attr.replace("-", "+").replace("_", "/") + return b64decode(encoded) + + @staticmethod + def deserialize_decimal(attr): + """Deserialize string into Decimal object. + + :param str attr: response string to be deserialized. + :return: Deserialized decimal + :raises DeserializationError: if string format invalid. + :rtype: decimal + """ + if isinstance(attr, ET.Element): + attr = attr.text + try: + return decimal.Decimal(str(attr)) # type: ignore + except decimal.DecimalException as err: + msg = "Invalid decimal {}".format(attr) + raise DeserializationError(msg) from err + + @staticmethod + def deserialize_long(attr): + """Deserialize string into long (Py2) or int (Py3). + + :param str attr: response string to be deserialized. + :return: Deserialized int + :rtype: long or int + :raises ValueError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + return _long_type(attr) # type: ignore + + @staticmethod + def deserialize_duration(attr): + """Deserialize ISO-8601 formatted string into TimeDelta object. + + :param str attr: response string to be deserialized. + :return: Deserialized duration + :rtype: TimeDelta + :raises DeserializationError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + try: + duration = isodate.parse_duration(attr) + except (ValueError, OverflowError, AttributeError) as err: + msg = "Cannot deserialize duration object." + raise DeserializationError(msg) from err + return duration + + @staticmethod + def _deserialize_duration_numeric(attr, unit): + """Deserialize a numeric duration value into a TimeDelta object. + + :param float attr: response value to be deserialized. + :param str unit: The wire unit, used as the ``timedelta`` keyword + (``"seconds"`` or ``"milliseconds"``). + :return: Deserialized duration + :rtype: TimeDelta + :raises DeserializationError: if value is invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + try: + duration = datetime.timedelta(**{unit: float(attr)}) # type: ignore + except (ValueError, OverflowError, TypeError) as err: + msg = "Cannot deserialize duration object." + raise DeserializationError(msg) from err + return duration + + @staticmethod + def deserialize_duration_seconds(attr): + """Deserialize a numeric number of seconds into a TimeDelta object. + + :param float attr: response value to be deserialized. + :return: Deserialized duration + :rtype: TimeDelta + :raises DeserializationError: if value is invalid. + """ + return Deserializer._deserialize_duration_numeric(attr, "seconds") + + @staticmethod + def deserialize_duration_milliseconds(attr): + """Deserialize a numeric number of milliseconds into a TimeDelta object. + + :param float attr: response value to be deserialized. + :return: Deserialized duration + :rtype: TimeDelta + :raises DeserializationError: if value is invalid. + """ + return Deserializer._deserialize_duration_numeric(attr, "milliseconds") + + @staticmethod + def deserialize_date(attr): + """Deserialize ISO-8601 formatted string into Date object. + + :param str attr: response string to be deserialized. + :return: Deserialized date + :rtype: Date + :raises DeserializationError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore + raise DeserializationError("Date must have only digits and -. Received: %s" % attr) + # This must NOT use defaultmonth/defaultday. Using None ensure this raises an exception. + return isodate.parse_date(attr, defaultmonth=0, defaultday=0) + + @staticmethod + def deserialize_time(attr): + """Deserialize ISO-8601 formatted string into time object. + + :param str attr: response string to be deserialized. + :return: Deserialized time + :rtype: datetime.time + :raises DeserializationError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + if re.search(r"[^\W\d_]", attr, re.I + re.U): # type: ignore + raise DeserializationError("Date must have only digits and -. Received: %s" % attr) + return isodate.parse_time(attr) + + @staticmethod + def deserialize_rfc(attr): + """Deserialize RFC-1123 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :return: Deserialized RFC datetime + :rtype: Datetime + :raises DeserializationError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + try: + parsed_date = email.utils.parsedate_tz(attr) # type: ignore + date_obj = datetime.datetime( + *parsed_date[:6], tzinfo=datetime.timezone(datetime.timedelta(minutes=(parsed_date[9] or 0) / 60)) + ) + if not date_obj.tzinfo: + date_obj = date_obj.astimezone(tz=TZ_UTC) + except ValueError as err: + msg = "Cannot deserialize to rfc datetime object." + raise DeserializationError(msg) from err + return date_obj + + @staticmethod + def deserialize_iso(attr): + """Deserialize ISO-8601 formatted string into Datetime object. + + :param str attr: response string to be deserialized. + :return: Deserialized ISO datetime + :rtype: Datetime + :raises DeserializationError: if string format invalid. + """ + if isinstance(attr, ET.Element): + attr = attr.text + try: + attr = attr.upper() # type: ignore + match = Deserializer.valid_date.match(attr) + if not match: + raise ValueError("Invalid datetime string: " + attr) + + check_decimal = attr.split(".") + if len(check_decimal) > 1: + decimal_str = "" + for digit in check_decimal[1]: + if digit.isdigit(): + decimal_str += digit + else: + break + if len(decimal_str) > 6: + attr = attr.replace(decimal_str, decimal_str[0:6]) + + date_obj = isodate.parse_datetime(attr) + test_utc = date_obj.utctimetuple() + if test_utc.tm_year > 9999 or test_utc.tm_year < 1: + raise OverflowError("Hit max or min date") + except (ValueError, OverflowError, AttributeError) as err: + msg = "Cannot deserialize datetime object." + raise DeserializationError(msg) from err + return date_obj + + @staticmethod + def deserialize_unix(attr): + """Serialize Datetime object into IntTime format. + This is represented as seconds. + + :param int attr: Object to be serialized. + :return: Deserialized datetime + :rtype: Datetime + :raises DeserializationError: if format invalid + """ + if isinstance(attr, ET.Element): + attr = int(attr.text) # type: ignore + try: + attr = int(attr) + date_obj = datetime.datetime.fromtimestamp(attr, TZ_UTC) + except ValueError as err: + msg = "Cannot deserialize to unix datetime object." + raise DeserializationError(msg) from err + return date_obj diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_version.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_version.py new file mode 100644 index 000000000000..be71c81bd282 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_version.py @@ -0,0 +1,9 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +VERSION = "1.0.0b1" diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/__init__.py new file mode 100644 index 000000000000..e670329dd024 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/__init__.py @@ -0,0 +1,29 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._client import VoiceAgentsClient # type: ignore + +try: + from ._patch import __all__ as _patch_all + from ._patch import * +except ImportError: + _patch_all = [] +from ._patch import patch_sdk as _patch_sdk + +__all__ = [ + "VoiceAgentsClient", +] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore + +_patch_sdk() diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py new file mode 100644 index 000000000000..d513d1a9c270 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py @@ -0,0 +1,119 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from copy import deepcopy +import sys +from typing import Any, Awaitable, TYPE_CHECKING + +from azure.core import AsyncPipelineClient +from azure.core.pipeline import policies +from azure.core.rest import AsyncHttpResponse, HttpRequest + +from .._utils.serialization import Deserializer, Serializer +from ._configuration import VoiceAgentsClientConfiguration +from .operations import AgentEndpointConversationsOperations, VoiceAgentsOperations + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self # type: ignore + +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential + + +class VoiceAgentsClient: + """VoiceAgentsClient. + + :ivar agent_endpoint_conversations: AgentEndpointConversationsOperations operations + :vartype agent_endpoint_conversations: + azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations + :ivar voice_agents: VoiceAgentsOperations operations + :vartype voice_agents: azure.ai.voiceagents.aio.operations.VoiceAgentsOperations + :param endpoint: Foundry Project endpoint in the form + "https://{ai-services-account-name}.services.ai.azure.com/api/projects/{project-name}". If you + only have one Project in your Foundry Hub, or to target the default Project in your Hub, use + the form "https://{ai-services-account-name}.services.ai.azure.com/api/projects/_project". + Required. + :type endpoint: str + :param credential: Credential used to authenticate requests to the service. Required. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :keyword api_version: The API version to use for this operation. Known values are "v1" and + None. Default value is None. If not set, the operation's default API version will be used. Note + that overriding this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__(self, endpoint: str, credential: "AsyncTokenCredential", **kwargs: Any) -> None: + _endpoint = "{endpoint}" + self._config = VoiceAgentsClientConfiguration(endpoint=endpoint, credential=credential, **kwargs) + + _policies = kwargs.pop("policies", None) + if _policies is None: + _policies = [ + policies.RequestIdPolicy(**kwargs), + self._config.headers_policy, + self._config.user_agent_policy, + self._config.proxy_policy, + policies.ContentDecodePolicy(**kwargs), + self._config.redirect_policy, + self._config.retry_policy, + self._config.authentication_policy, + self._config.custom_hook_policy, + self._config.logging_policy, + policies.DistributedTracingPolicy(**kwargs), + policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None, + self._config.http_logging_policy, + ] + self._client: AsyncPipelineClient = AsyncPipelineClient(base_url=_endpoint, policies=_policies, **kwargs) + + self._serialize = Serializer() + self._deserialize = Deserializer() + self._serialize.client_side_validation = False + self.agent_endpoint_conversations = AgentEndpointConversationsOperations( + self._client, self._config, self._serialize, self._deserialize + ) + self.voice_agents = VoiceAgentsOperations(self._client, self._config, self._serialize, self._deserialize) + + def send_request( + self, request: HttpRequest, *, stream: bool = False, **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: + """Runs the network request through the client's chained policies. + + >>> from azure.core.rest import HttpRequest + >>> request = HttpRequest("GET", "https://www.example.org/") + + >>> response = await client.send_request(request) + + + For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request + + :param request: The network request you want to make. Required. + :type request: ~azure.core.rest.HttpRequest + :keyword bool stream: Whether the response payload will be streamed. Defaults to False. + :return: The response of your network call. Does not do error handling on your response. + :rtype: ~azure.core.rest.AsyncHttpResponse + """ + + request_copy = deepcopy(request) + path_format_arguments = { + "endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True), + } + + request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments) + return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore + + async def close(self) -> None: + await self._client.close() + + async def __aenter__(self) -> Self: + await self._client.__aenter__() + return self + + async def __aexit__(self, *exc_details: Any) -> None: + await self._client.__aexit__(*exc_details) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_configuration.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_configuration.py new file mode 100644 index 000000000000..5013da79f3e2 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_configuration.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, TYPE_CHECKING + +from azure.core.pipeline import policies + +from .._version import VERSION + +if TYPE_CHECKING: + from azure.core.credentials_async import AsyncTokenCredential + + +class VoiceAgentsClientConfiguration: # pylint: disable=too-many-instance-attributes + """Configuration for VoiceAgentsClient. + + Note that all parameters used to create this instance are saved as instance + attributes. + + :param endpoint: Foundry Project endpoint in the form + "https://{ai-services-account-name}.services.ai.azure.com/api/projects/{project-name}". If you + only have one Project in your Foundry Hub, or to target the default Project in your Hub, use + the form "https://{ai-services-account-name}.services.ai.azure.com/api/projects/_project". + Required. + :type endpoint: str + :param credential: Credential used to authenticate requests to the service. Required. + :type credential: ~azure.core.credentials_async.AsyncTokenCredential + :keyword api_version: The API version to use for this operation. Known values are "v1" and + None. Default value is None. If not set, the operation's default API version will be used. Note + that overriding this default value may result in unsupported behavior. + :paramtype api_version: str + """ + + def __init__(self, endpoint: str, credential: "AsyncTokenCredential", **kwargs: Any) -> None: + api_version: str = kwargs.pop("api_version", "v1") + + if endpoint is None: + raise ValueError("Parameter 'endpoint' must not be None.") + if credential is None: + raise ValueError("Parameter 'credential' must not be None.") + + self.endpoint = endpoint + self.credential = credential + self.api_version = api_version + self.credential_scopes = kwargs.pop("credential_scopes", ["https://ai.azure.com/.default"]) + kwargs.setdefault("sdk_moniker", "ai-voiceagents/{}".format(VERSION)) + self.polling_interval = kwargs.get("polling_interval", 30) + self._configure(**kwargs) + + def _configure(self, **kwargs: Any) -> None: + self.user_agent_policy = kwargs.get("user_agent_policy") or policies.UserAgentPolicy(**kwargs) + self.headers_policy = kwargs.get("headers_policy") or policies.HeadersPolicy(**kwargs) + self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs) + self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs) + self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs) + self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs) + self.redirect_policy = kwargs.get("redirect_policy") or policies.AsyncRedirectPolicy(**kwargs) + self.retry_policy = kwargs.get("retry_policy") or policies.AsyncRetryPolicy(**kwargs) + self.authentication_policy = kwargs.get("authentication_policy") + if self.credential and not self.authentication_policy: + self.authentication_policy = policies.AsyncBearerTokenCredentialPolicy( + self.credential, *self.credential_scopes, **kwargs + ) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py new file mode 100644 index 000000000000..2d55885f8538 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py @@ -0,0 +1,57 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" +from typing import Any, Optional + +from ._client import VoiceAgentsClient as _GeneratedVoiceAgentsClient +from ._realtime import ( + AsyncRealtime, + AsyncRealtimeConnection, + AsyncRealtimeConnectionManager, +) + + +class VoiceAgentsClient(_GeneratedVoiceAgentsClient): + """VoiceAgentsClient with realtime (WebSocket) streaming support. + + Adds the :attr:`realtime` namespace on top of the generated HTTP client, + following the OpenAI ``client.realtime.connect(...)`` pattern. + """ + + def __init__(self, endpoint: str, credential: Any, **kwargs: Any) -> None: + super().__init__(endpoint, credential, **kwargs) + self._realtime: Optional[AsyncRealtime] = None + + @property + def realtime(self) -> AsyncRealtime: + """Realtime streaming entry point. + + :return: The realtime namespace, exposing ``connect(...)``. + :rtype: ~azure.ai.voiceagents.aio.AsyncRealtime + """ + if self._realtime is None: + self._realtime = AsyncRealtime(self) + return self._realtime + + +__all__: list[str] = [ + "VoiceAgentsClient", + "AsyncRealtime", + "AsyncRealtimeConnection", + "AsyncRealtimeConnectionManager", +] # Add all objects you want publicly available to users at this package level + + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py new file mode 100644 index 000000000000..e04aac4012a0 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py @@ -0,0 +1,417 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Hand-written async realtime (WebSocket) streaming client for voice agents. + +Realtime uses a fundamentally different transport (a persistent WebSocket) +than the request/response HTTP surface, so it is exposed as a **dedicated +standalone client** rather than a member of the HTTP ``VoiceAgentsClient``. +The connection ergonomics follow the OpenAI Python realtime client so that +developers moving between the two libraries get a familiar surface: + +* :meth:`VoiceAgentsRealtimeClient.connect` returns an async context manager. +* Entering the context yields an :class:`AsyncRealtimeConnection`. +* The connection is async-iterable over inbound server events and exposes + sub-namespaces (``session``, ``input_audio_buffer``, ``output_audio_buffer``, + ``conversation``, ``response``) for sending outbound client events. + +The realtime wire schema is owned by Voice Live and is intentionally kept +open here: inbound events are surfaced as plain ``dict`` objects and outbound +events accept either a ready-made mapping or the typed helper methods below. +This keeps the SDK usable today without hard-coupling it to a frame schema +that is still being finalized service-side. + +``aiohttp`` is required for this feature and is *not* a hard dependency of the +package; it is imported lazily so importing the SDK never fails when it is +absent. +""" + +from __future__ import annotations + +import base64 +import json +from typing import Any, AsyncIterator, List, Mapping, Optional, TYPE_CHECKING, Union + +if TYPE_CHECKING: + from aiohttp import ClientSession, ClientWebSocketResponse + from azure.core.credentials_async import AsyncTokenCredential + + +_NOT_IMPLEMENTED_MESSAGE = ( + "Realtime (WebSocket) streaming is not yet available for voice agents. This client is an " + "interface-only preview: the service-side streaming route is not wired up, so no streaming " + "functionality exists today. This surface is provided for early integration only and will " + "begin working in a future release; expect breaking changes until then." +) + + +__all__ = [ + "AsyncRealtime", + "AsyncRealtimeConnection", + "AsyncRealtimeConnectionManager", +] + + +def _to_ws_url(endpoint: str, agent_name: str) -> str: + """Build the realtime WebSocket URL from the HTTP project endpoint. + + :param str endpoint: The Foundry project endpoint (``https://.../api/projects/...``). + :param str agent_name: The name of the voice agent to connect to. + :return: A ``wss://``/``ws://`` URL targeting the deferred realtime route. + :rtype: str + """ + base = endpoint.rstrip("/") + if base.startswith("https://"): + base = "wss://" + base[len("https://") :] + elif base.startswith("http://"): + base = "ws://" + base[len("http://") :] + return f"{base}/agents/{agent_name}/endpoint/protocols/voice" + + +class _BaseResource: + """Base helper that forwards typed helpers to the parent connection.""" + + def __init__(self, connection: "AsyncRealtimeConnection") -> None: + self._connection = connection + + async def _send(self, event: Mapping[str, Any]) -> None: + await self._connection.send(event) + + +class SessionResource(_BaseResource): + """Send ``session.*`` client events.""" + + async def update(self, *, session: Mapping[str, Any], **extra: Any) -> None: + """Update the realtime session configuration. + + :keyword session: The session configuration to apply. + :paramtype session: Mapping[str, Any] + """ + await self._send({"type": "session.update", "session": dict(session), **extra}) + + +class InputAudioBufferResource(_BaseResource): + """Send ``input_audio_buffer.*`` client events.""" + + async def append(self, *, audio: Union[str, bytes], **extra: Any) -> None: + """Append audio bytes to the input buffer. + + :keyword audio: Raw audio bytes, or an already base64-encoded string. + :paramtype audio: str or bytes + """ + if isinstance(audio, (bytes, bytearray)): + audio = base64.b64encode(bytes(audio)).decode("ascii") + await self._send({"type": "input_audio_buffer.append", "audio": audio, **extra}) + + async def commit(self, **extra: Any) -> None: + """Commit the buffered input audio as a user turn.""" + await self._send({"type": "input_audio_buffer.commit", **extra}) + + async def clear(self, **extra: Any) -> None: + """Discard any buffered input audio.""" + await self._send({"type": "input_audio_buffer.clear", **extra}) + + +class OutputAudioBufferResource(_BaseResource): + """Send ``output_audio_buffer.*`` client events.""" + + async def clear(self, **extra: Any) -> None: + """Stop and clear any audio the service is currently playing back.""" + await self._send({"type": "output_audio_buffer.clear", **extra}) + + +class ConversationItemResource(_BaseResource): + """Send ``conversation.item.*`` client events.""" + + async def create(self, *, item: Mapping[str, Any], **extra: Any) -> None: + """Insert an item into the conversation. + + :keyword item: The conversation item to create. + :paramtype item: Mapping[str, Any] + """ + await self._send({"type": "conversation.item.create", "item": dict(item), **extra}) + + async def delete(self, *, item_id: str, **extra: Any) -> None: + """Delete an item from the conversation. + + :keyword str item_id: The id of the item to delete. + """ + await self._send({"type": "conversation.item.delete", "item_id": item_id, **extra}) + + async def truncate(self, *, item_id: str, content_index: int, audio_end_ms: int, **extra: Any) -> None: + """Truncate a previously produced assistant audio item. + + :keyword str item_id: The id of the assistant item to truncate. + :keyword int content_index: The index of the content part to truncate. + :keyword int audio_end_ms: The point, in milliseconds, to truncate the audio to. + """ + await self._send( + { + "type": "conversation.item.truncate", + "item_id": item_id, + "content_index": content_index, + "audio_end_ms": audio_end_ms, + **extra, + } + ) + + +class ConversationResource(_BaseResource): + """Send ``conversation.*`` client events.""" + + def __init__(self, connection: "AsyncRealtimeConnection") -> None: + super().__init__(connection) + self.item = ConversationItemResource(connection) + + +class ResponseResource(_BaseResource): + """Send ``response.*`` client events.""" + + async def create(self, *, response: Optional[Mapping[str, Any]] = None, **extra: Any) -> None: + """Ask the model to generate a response. + + :keyword response: Optional per-response overrides. + :paramtype response: Mapping[str, Any] or None + """ + event: dict[str, Any] = {"type": "response.create", **extra} + if response is not None: + event["response"] = dict(response) + await self._send(event) + + async def cancel(self, *, response_id: Optional[str] = None, **extra: Any) -> None: + """Cancel an in-progress response. + + :keyword response_id: The id of the response to cancel, if targeting a specific one. + :paramtype response_id: str or None + """ + event: dict[str, Any] = {"type": "response.cancel", **extra} + if response_id is not None: + event["response_id"] = response_id + await self._send(event) + + +class AsyncRealtimeConnection: + """An open realtime WebSocket connection to a voice agent. + + Iterate over the connection to receive server events as ``dict`` objects, + and use the sub-namespaces to send client events:: + + async with client.realtime.connect(agent_name="my-agent") as conn: + await conn.session.update(session={"modalities": ["audio", "text"]}) + await conn.input_audio_buffer.append(audio=chunk) + await conn.input_audio_buffer.commit() + await conn.response.create() + async for event in conn: + if event["type"] == "response.done": + break + """ + + def __init__(self, connection: "ClientWebSocketResponse", session: "ClientSession") -> None: + self._connection = connection + self._session = session + self.session = SessionResource(self) + self.input_audio_buffer = InputAudioBufferResource(self) + self.output_audio_buffer = OutputAudioBufferResource(self) + self.conversation = ConversationResource(self) + self.response = ResponseResource(self) + + async def __aenter__(self) -> "AsyncRealtimeConnection": + return self + + async def __aexit__(self, *exc_details: Any) -> None: + await self.close() + + def __aiter__(self) -> AsyncIterator[dict[str, Any]]: + return self._iter() + + async def _iter(self) -> AsyncIterator[dict[str, Any]]: + while True: + try: + yield await self.recv() + except ConnectionResetError: + return + + async def recv(self) -> dict[str, Any]: + """Receive and parse the next JSON server event. + + :return: The parsed server event. + :rtype: dict[str, Any] + :raises ConnectionResetError: If the connection was closed by the server. + """ + import aiohttp + + msg = await self._connection.receive() + if msg.type in (aiohttp.WSMsgType.CLOSE, aiohttp.WSMsgType.CLOSING, aiohttp.WSMsgType.CLOSED): + raise ConnectionResetError("The realtime connection was closed.") + if msg.type == aiohttp.WSMsgType.ERROR: + raise ConnectionResetError( + "The realtime connection encountered an error." + ) from self._connection.exception() + if msg.type == aiohttp.WSMsgType.BINARY: + return json.loads(msg.data.decode("utf-8")) + return json.loads(msg.data) + + async def send(self, event: Union[Mapping[str, Any], str]) -> None: + """Send a client event over the connection. + + :param event: A ready-made event mapping, or a raw JSON string. + :type event: Mapping[str, Any] or str + """ + if isinstance(event, str): + await self._connection.send_str(event) + else: + await self._connection.send_str(json.dumps(dict(event))) + + async def close(self, *, code: int = 1000, reason: str = "") -> None: + """Close the connection and release the underlying HTTP session. + + :keyword int code: The WebSocket close code. + :keyword str reason: The close reason. + """ + try: + await self._connection.close(code=code, message=reason.encode("utf-8")) + finally: + await self._session.close() + + +class AsyncRealtimeConnectionManager: + """Async context manager that opens an :class:`AsyncRealtimeConnection`. + + Returned by :meth:`AsyncRealtime.connect`; you normally use it as + ``async with client.realtime.connect(...) as conn:``. + """ + + def __init__( + self, + *, + endpoint: str, + credential: "AsyncTokenCredential", + credential_scopes: List[str], + api_version: str, + agent_name: str, + foundry_features: Optional[str] = None, + extra_query: Optional[Mapping[str, str]] = None, + extra_headers: Optional[Mapping[str, str]] = None, + **kwargs: Any, + ) -> None: + self._endpoint = endpoint + self._credential = credential + self._credential_scopes = credential_scopes + self._api_version = api_version + self._agent_name = agent_name + self._foundry_features = foundry_features + self._extra_query = dict(extra_query or {}) + self._extra_headers = dict(extra_headers or {}) + self._kwargs = kwargs + self._connection: Optional[AsyncRealtimeConnection] = None + + async def __aenter__(self) -> AsyncRealtimeConnection: + return await self.enter() + + async def enter(self) -> AsyncRealtimeConnection: + """Open the connection. + + :return: The live realtime connection. + :rtype: AsyncRealtimeConnection + """ + try: + import aiohttp + except ImportError as exc: # pragma: no cover - dependency guard + raise RuntimeError( + "The realtime client requires `aiohttp`. Install it with `pip install aiohttp`." + ) from exc + + url = _to_ws_url(self._endpoint, self._agent_name) + + params: dict[str, str] = {"api-version": self._api_version} + params.update(self._extra_query) + + token = await self._credential.get_token(*self._credential_scopes) + headers: dict[str, str] = {"Authorization": f"Bearer {token.token}"} + if self._foundry_features is not None: + headers["Foundry-Features"] = self._foundry_features + headers.update(self._extra_headers) + + session = aiohttp.ClientSession() + try: + connection = await session.ws_connect(url, headers=headers, params=params, **self._kwargs) + except BaseException: + await session.close() + raise + self._connection = AsyncRealtimeConnection(connection, session) + return self._connection + + async def __aexit__(self, *exc_details: Any) -> None: + if self._connection is not None: + await self._connection.close() + self._connection = None + + +class AsyncRealtime: + """Realtime streaming entry point, exposed as ``client.realtime``. + + Follows the OpenAI Python realtime surface: obtain it from the HTTP client + and open a connection with :meth:`connect`:: + + from azure.ai.voiceagents.aio import VoiceAgentsClient + from azure.identity.aio import DefaultAzureCredential + + client = VoiceAgentsClient(endpoint, DefaultAzureCredential()) + async with client.realtime.connect(agent_name="my-agent") as conn: + await conn.session.update(session={"modalities": ["audio", "text"]}) + await conn.input_audio_buffer.append(audio=chunk) + await conn.input_audio_buffer.commit() + await conn.response.create() + async for event in conn: + if event["type"] == "response.done": + break + + :param client: The HTTP client whose endpoint and credential are reused for + the realtime handshake. + :type client: ~azure.ai.voiceagents.aio.VoiceAgentsClient + """ + + def __init__(self, client: Any) -> None: + self._config = client._config # pylint: disable=protected-access + + def connect( + self, + *, + agent_name: str, + foundry_features: Optional[str] = None, + extra_query: Optional[Mapping[str, str]] = None, + extra_headers: Optional[Mapping[str, str]] = None, + **kwargs: Any, + ) -> AsyncRealtimeConnectionManager: + """Open a realtime WebSocket connection to a voice agent. + + .. note:: + Realtime streaming is **not yet implemented**. This method currently + raises :class:`NotImplementedError`; the interface is exposed only for + early integration and will begin working in a future release. + + :keyword str agent_name: The name of the voice agent to connect to. + :keyword foundry_features: Preview opt-in value for the ``Foundry-Features`` header, + e.g. ``"VOICE_AGENTS_V1_PREVIEW"``. Default value is None. + :paramtype foundry_features: str or None + :keyword extra_query: Additional query-string parameters for the handshake. + :paramtype extra_query: Mapping[str, str] or None + :keyword extra_headers: Additional headers for the handshake. + :paramtype extra_headers: Mapping[str, str] or None + :return: An async context manager yielding an :class:`AsyncRealtimeConnection`. + :rtype: AsyncRealtimeConnectionManager + """ + raise NotImplementedError(_NOT_IMPLEMENTED_MESSAGE) + return AsyncRealtimeConnectionManager( # pylint: disable=unreachable + endpoint=self._config.endpoint, + credential=self._config.credential, + credential_scopes=self._config.credential_scopes, + api_version=self._config.api_version, + agent_name=agent_name, + foundry_features=foundry_features, + extra_query=extra_query, + extra_headers=extra_headers, + **kwargs, + ) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/__init__.py new file mode 100644 index 000000000000..0840d3975c41 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/__init__.py @@ -0,0 +1,27 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._operations import AgentEndpointConversationsOperations # type: ignore +from ._operations import VoiceAgentsOperations # type: ignore + +from ._patch import __all__ as _patch_all +from ._patch import * +from ._patch import patch_sdk as _patch_sdk + +__all__ = [ + "AgentEndpointConversationsOperations", + "VoiceAgentsOperations", +] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore +_patch_sdk() diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py new file mode 100644 index 000000000000..818bb52a667c --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py @@ -0,0 +1,2816 @@ +# pylint: disable=too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from collections.abc import MutableMapping +from io import IOBase +import json +from typing import Any, AsyncIterator, Callable, IO, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload +import urllib.parse + +from azure.core import AsyncPipelineClient +from azure.core.async_paging import AsyncItemPaged, AsyncList +from azure.core.exceptions import ( + ClientAuthenticationError, + HttpResponseError, + ResourceExistsError, + ResourceNotFoundError, + ResourceNotModifiedError, + StreamClosedError, + StreamConsumedError, + map_error, +) +from azure.core.pipeline import PipelineResponse +from azure.core.rest import AsyncHttpResponse, HttpRequest +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async +from azure.core.utils import case_insensitive_dict + +from ... import models as _models, types as _types +from ..._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from ..._utils.serialization import Deserializer, Serializer +from ...models._enums import AgentDefinitionOptInKeys +from ...operations._operations import ( + build_agent_endpoint_conversations_delete_agent_conversation_request, + build_agent_endpoint_conversations_get_agent_conversation_audio_content_request, + build_agent_endpoint_conversations_get_agent_conversation_audio_request, + build_agent_endpoint_conversations_get_agent_conversation_item_audio_content_request, + build_agent_endpoint_conversations_get_agent_conversation_item_audio_request, + build_agent_endpoint_conversations_get_agent_conversation_item_request, + build_agent_endpoint_conversations_get_agent_conversation_request, + build_agent_endpoint_conversations_get_agent_conversation_response_request, + build_agent_endpoint_conversations_list_agent_conversation_items_request, + build_agent_endpoint_conversations_list_agent_conversation_response_items_request, + build_agent_endpoint_conversations_list_agent_conversation_responses_request, + build_voice_agents_create_voice_agent_request, + build_voice_agents_create_voice_agent_version_request, + build_voice_agents_delete_voice_agent_request, + build_voice_agents_delete_voice_agent_version_request, + build_voice_agents_disable_voice_agent_request, + build_voice_agents_enable_voice_agent_request, + build_voice_agents_generate_voice_agent_request, + build_voice_agents_get_voice_agent_request, + build_voice_agents_get_voice_agent_version_request, + build_voice_agents_list_voice_agent_versions_request, + build_voice_agents_list_voice_agents_request, + build_voice_agents_update_voice_agent_request, +) +from .._configuration import VoiceAgentsClientConfiguration + +if TYPE_CHECKING: + from ... import _unions +T = TypeVar("T") +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, AsyncHttpResponse], T, dict[str, Any]], Any]] +JSON = MutableMapping[str, Any] +_Unset: Any = object() + + +class AgentEndpointConversationsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.voiceagents.aio.VoiceAgentsClient`'s + :attr:`agent_endpoint_conversations` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: VoiceAgentsClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + 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") + + @distributed_trace_async + async def get_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceConversation: + """Get a voice agent conversation. + + Retrieves a single conversation recorded for the specified voice agent endpoint by its id. + Returns ``404`` when the conversation was not persisted (``store = false``) or does not exist. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation to retrieve. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceConversation. The VoiceConversation is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceConversation + :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.VoiceConversation] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceConversation, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def delete_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceDeletedConversation: + """Delete a voice agent conversation. + + Deletes a conversation and all of its stored data — responses, items, and any audio (cascade). + This is the customer's explicit data-deletion control for voice conversations. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation to delete. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceDeletedConversation. The VoiceDeletedConversation is compatible with + MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceDeletedConversation + :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.VoiceDeletedConversation] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_delete_agent_conversation_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceDeletedConversation, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_agent_conversation_responses( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.VoiceResponse"]: + """List responses in a voice agent conversation. + + Returns a paged collection of the responses (model inference turns) recorded for the specified + conversation. The per-response ``output`` projection may be omitted here; use the + response-items route for the canonical paged output. Returns ``404`` when the conversation was + not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose responses are listed. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceResponse + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.voiceagents.models.VoiceResponse] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceResponse]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_responses_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceResponse], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def get_agent_conversation_response( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceResponse: + """Get a voice agent conversation response. + + Retrieves a single response from the specified conversation by its id, including its ``output`` + items, ``usage``, and status. Returns ``404`` when the conversation or response was not + persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the response. Required. + :type conversation_id: str + :param response_id: The id of the response to retrieve. Required. + :type response_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceResponse. The VoiceResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceResponse + :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.VoiceResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_response_request( + agent_name=agent_name, + conversation_id=conversation_id, + response_id=response_id, + foundry_features=foundry_features, + 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", 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.VoiceResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_agent_conversation_response_items( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_unions.VoiceConversationItem"]: + """List items produced by a voice agent conversation response. + + Returns a paged collection of the output items produced by a specific response (the response's + output projection). For the complete ordered conversation history — including user input and + client-created tool outputs — use the conversation items route instead. Returns ``404`` when + the conversation or response was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the response. Required. + :type conversation_id: str + :param response_id: The id of the response whose output items are listed. Required. + :type response_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceSystemMessageItem or VoiceUserMessageItem or + VoiceAssistantMessageItem or VoiceFunctionCallItem or VoiceFunctionCallOutputItem or + VoiceMcpListToolsItem or VoiceMcpCallItem or VoiceMcpApprovalRequestItem or + VoiceMcpApprovalResponseItem + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list["_unions.VoiceConversationItem"]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_response_items_request( + agent_name=agent_name, + conversation_id=conversation_id, + response_id=response_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list["_unions.VoiceConversationItem"], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace + def list_agent_conversation_items( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_unions.VoiceConversationItem"]: + """List items in a voice agent conversation. + + Returns a paged collection of items — the complete ordered conversation history, including user + input, assistant output, and client-created tool outputs (transcripts + tool events). Returns + ``404`` when the conversation was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose items are listed. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceSystemMessageItem or VoiceUserMessageItem or + VoiceAssistantMessageItem or VoiceFunctionCallItem or VoiceFunctionCallOutputItem or + VoiceMcpListToolsItem or VoiceMcpCallItem or VoiceMcpApprovalRequestItem or + VoiceMcpApprovalResponseItem + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list["_unions.VoiceConversationItem"]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_items_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list["_unions.VoiceConversationItem"], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def get_agent_conversation_item( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> "_unions.VoiceConversationItem": + """Get a voice agent conversation item. + + Retrieves a single item from the specified conversation by its id, including its transcript. An + ``input_audio``/``output_audio`` content part indicates that audio is available for the item; + the canonical per-item audio metadata is the ``/items/{item_id}/audio`` resource, and the bytes + are streamed by ``/items/{item_id}/audio/content``. Returns ``404`` when the conversation or + item was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item to retrieve. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceSystemMessageItem or VoiceUserMessageItem or VoiceAssistantMessageItem or + VoiceFunctionCallItem or VoiceFunctionCallOutputItem or VoiceMcpListToolsItem or + VoiceMcpCallItem or VoiceMcpApprovalRequestItem or VoiceMcpApprovalResponseItem + :rtype: ~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem + :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["_unions.VoiceConversationItem"] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_item_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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", 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("_unions.VoiceConversationItem", response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_agent_conversation_item_audio( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceItemAudioResponse: + """Get a voice agent conversation item's audio metadata. + + Returns metadata for a single conversation item's audio segment, including the common playback + facts (role, format/codec, sample rate, channels, offset, duration) for both Foundry-managed + and bring-your-own-storage (BYOS) recordings; for BYOS the response additionally includes + ``blob_path``, a direct path into the customer's own storage (no SAS) that the customer + downloads with their own credentials. Requires the conversation to have persisted audio + (``store = true``); returns ``404`` when the conversation, item, or its audio was not + persisted. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item whose audio metadata is retrieved. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceItemAudioResponse. The VoiceItemAudioResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceItemAudioResponse + :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.VoiceItemAudioResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_item_audio_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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", 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.VoiceItemAudioResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_agent_conversation_item_audio_content( # pylint: disable=name-too-long + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> AsyncIterator[bytes]: + """Stream a voice agent conversation item's audio. + + Streams a single conversation item's audio as a WAV (``audio/wav``) byte stream through the + service (no SAS URL). This route serves Foundry-managed storage only. For + bring-your-own-storage (BYOS) recordings the bytes are not proxied — the caller must download + directly from customer storage using the ``blob_path`` returned by the item's ``/audio`` + metadata route — so this route returns ``409 Conflict`` for BYOS recordings. Returns ``404`` + when the conversation, item, or its audio was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item whose audio is streamed. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :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_agent_endpoint_conversations_get_agent_conversation_item_audio_content_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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 get_agent_conversation_audio( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceRecordingResponse: + """Get a voice agent conversation's merged recording metadata. + + Returns metadata for the whole-call merged stereo recording (user audio on the left channel, + agent audio on the right). The common metadata (format, sample rate, channels, channel layout, + duration) is returned for both Foundry-managed and bring-your-own-storage (BYOS) recordings; + for BYOS the response additionally includes ``blob_path``, a direct path into the customer's + own storage (no SAS) that the customer downloads with their own credentials. The recording is + built once from the per-turn segments after the session ends; a request against an in-progress + session returns ``409``. Requires the conversation to have persisted audio (``store = true``); + otherwise returns ``404``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose merged recording metadata is + retrieved. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceRecordingResponse. The VoiceRecordingResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceRecordingResponse + :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.VoiceRecordingResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_audio_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceRecordingResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def get_agent_conversation_audio_content( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> AsyncIterator[bytes]: + """Stream a voice agent conversation's merged recording. + + Streams the whole-call merged stereo recording as a WAV (``audio/wav``) byte stream through the + service (no SAS URL). This route serves Foundry-managed storage only. For + bring-your-own-storage (BYOS) recordings the bytes are not proxied — the caller must download + directly from customer storage using the ``blob_path`` returned by the metadata route — so this + route returns ``409 Conflict`` for BYOS recordings. A request against an in-progress session + also returns ``409`` (a distinct condition: session-not-ended versus BYOS-download-required). A + conversation without persisted audio (``store = false``) returns ``404``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose merged recording is streamed. + Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :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_agent_endpoint_conversations_get_agent_conversation_audio_content_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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 + + +class VoiceAgentsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.voiceagents.aio.VoiceAgentsClient`'s + :attr:`voice_agents` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: AsyncPipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: VoiceAgentsClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + 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 create_voice_agent( + self, + *, + name: str, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + state: Optional[Union[str, _models.AgentState]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :keyword 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. + :paramtype name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword state: The initial operational state of the agent. Defaults to 'enabled' if not + specified. Known values are: "enabled" and "disabled". Default value is None. + :paramtype state: str or ~azure.ai.voiceagents.models.AgentState + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :keyword agent_endpoint: An optional endpoint configuration. If not specified, a default + endpoint configuration will be set for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.voiceagents.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.voiceagents.models.AgentCard + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_voice_agent( + self, + body: _types.CreateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Required. + :type body: ~azure.ai.voiceagents.types.CreateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_voice_agent( + self, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_voice_agent( # pylint: disable=too-many-locals + self, + body: Union[JSON, _types.CreateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + name: str = _Unset, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + state: Optional[Union[str, _models.AgentState]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Is one of the following types: JSON, CreateVoiceAgentRequest, IO[bytes] Required. + :type body: JSON or ~azure.ai.voiceagents.types.CreateVoiceAgentRequest or IO[bytes] + :keyword 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. + :paramtype name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword state: The initial operational state of the agent. Defaults to 'enabled' if not + specified. Known values are: "enabled" and "disabled". Default value is None. + :paramtype state: str or ~azure.ai.voiceagents.models.AgentState + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :keyword agent_endpoint: An optional endpoint configuration. If not specified, a default + endpoint configuration will be set for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.voiceagents.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.voiceagents.models.AgentCard + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if name is _Unset: + raise TypeError("missing required argument: name") + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "agent_card": agent_card, + "agent_endpoint": agent_endpoint, + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "draft": draft, + "metadata": metadata, + "name": name, + "state": state, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_create_voice_agent_request( + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_voice_agents( + self, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.VoiceAgentObject"]: + """List voice agents. + + Returns a paged collection of voice agents. + + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceAgentObject + :rtype: ~azure.core.async_paging.AsyncItemPaged[~azure.ai.voiceagents.models.VoiceAgentObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceAgentObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_voice_agents_list_voice_agents_request( + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceAgentObject], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def get_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Get a voice agent. + + Retrieves a voice agent by its unique name. + + :param agent_name: The name of the voice agent to retrieve. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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.VoiceAgentObject] = kwargs.pop("cls", None) + + _request = build_voice_agents_get_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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", 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def update_voice_agent( + self, + agent_name: str, + *, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update_voice_agent( + self, + agent_name: str, + body: _types.UpdateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.voiceagents.types.UpdateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def update_voice_agent( + self, + agent_name: str, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def update_voice_agent( + self, + agent_name: str, + body: Union[JSON, _types.UpdateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Is one of the following types: JSON, UpdateVoiceAgentRequest, IO[bytes] Required. + :type body: JSON or ~azure.ai.voiceagents.types.UpdateVoiceAgentRequest or IO[bytes] + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "metadata": metadata, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_update_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def delete_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.DeleteAgentResponse: + """Delete a voice agent. + + Deletes a voice agent and all of its versions. + + :param agent_name: The name of the voice agent to delete. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: DeleteAgentResponse. The DeleteAgentResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.DeleteAgentResponse + :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.DeleteAgentResponse] = kwargs.pop("cls", None) + + _request = build_voice_agents_delete_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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", 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.DeleteAgentResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def enable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> None: + """Enable a voice agent. + + Enables the specified voice agent, allowing it to accept new requests. This operation is + idempotent — enabling an already-enabled voice agent returns success with no side effects. + + :param agent_name: The name of the voice agent to enable. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: None + :rtype: None + :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[None] = kwargs.pop("cls", None) + + _request = build_voice_agents_enable_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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) + + _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 [204]: + 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 cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace_async + async def disable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> None: + """Disable a voice agent. + + Disables the specified voice agent, preventing it from accepting new requests. This operation + is idempotent — disabling an already-disabled voice agent returns success with no side effects. + + :param agent_name: The name of the voice agent to disable. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: None + :rtype: None + :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[None] = kwargs.pop("cls", None) + + _request = build_voice_agents_disable_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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) + + _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 [204]: + 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 cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + async def generate_voice_agent( + self, + *, + name: str, + model_type: Union[str, _models.VoiceModelType], + model: str, + agent_type: Union[str, _models.VoiceAgentType], + use_case: Union[str, _models.VoiceAgentUseCase], + goal: str, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + description: Optional[str] = None, + tools: Optional[list["_unions.VoiceAgentTool"]] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :keyword name: The unique name for the agent to create. Required. + :paramtype name: str + :keyword model_type: How the model backing the generated agent is served: ``managed`` + (service-managed) or ``self_deployed`` (the customer's own deployment). Carried through to the + generated definition, not generated. Known values are: "managed" and "self_deployed". Required. + :paramtype model_type: str or ~azure.ai.voiceagents.models.VoiceModelType + :keyword model: The model paired with ``model_type``: the service-managed model name when + ``managed``, or the customer's Foundry deployment name when ``self_deployed``. Carried through, + not generated. Required. + :paramtype model: str + :keyword agent_type: The persona/tone to steer generation. Known values are: "personal" and + "business". Required. + :paramtype agent_type: str or ~azure.ai.voiceagents.models.VoiceAgentType + :keyword use_case: The scenario-template catalog entry the generator specializes for. Known + values are: "customer_support", "reception", "sales", "travel_assistant", "outreach", + "personal_assistant", "learning", "call_center", and "in_car". Required. + :paramtype use_case: str or ~azure.ai.voiceagents.models.VoiceAgentUseCase + :keyword goal: A natural-language description of what the agent should do; the seed for the + generated ``instructions``. Required. + :paramtype goal: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword description: An optional description for the agent. Generated from ``goal`` when + omitted. Default value is None. + :paramtype description: str + :keyword tools: Optional tools carried through verbatim onto the generated agent (see + ``VoiceAgentTool``). Default value is None. + :paramtype tools: list[~azure.ai.voiceagents.models.FunctionTool or + ~azure.ai.voiceagents.models.MCPTool or ~azure.ai.voiceagents.models.VoiceSystemTool or + ~azure.ai.voiceagents.models.VoiceToolboxTool] + :keyword draft: (Preview) When ``true``, the generated voice agent is created as a draft — an + editable, unpublished version the caller can review and refine before publishing it via the + standard create/version path. The service defaults to ``false`` if a value is not specified by + the caller, in which case the agent is created and published normally. Default value is None. + :paramtype draft: bool + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def generate_voice_agent( + self, + body: _types.GenerateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Required. + :type body: ~azure.ai.voiceagents.types.GenerateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def generate_voice_agent( + self, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def generate_voice_agent( # pylint: disable=too-many-locals + self, + body: Union[JSON, _types.GenerateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + name: str = _Unset, + model_type: Union[str, _models.VoiceModelType] = _Unset, + model: str = _Unset, + agent_type: Union[str, _models.VoiceAgentType] = _Unset, + use_case: Union[str, _models.VoiceAgentUseCase] = _Unset, + goal: str = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + description: Optional[str] = None, + tools: Optional[list["_unions.VoiceAgentTool"]] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Is one of the following types: JSON, GenerateVoiceAgentRequest, IO[bytes] + Required. + :type body: JSON or ~azure.ai.voiceagents.types.GenerateVoiceAgentRequest or IO[bytes] + :keyword name: The unique name for the agent to create. Required. + :paramtype name: str + :keyword model_type: How the model backing the generated agent is served: ``managed`` + (service-managed) or ``self_deployed`` (the customer's own deployment). Carried through to the + generated definition, not generated. Known values are: "managed" and "self_deployed". Required. + :paramtype model_type: str or ~azure.ai.voiceagents.models.VoiceModelType + :keyword model: The model paired with ``model_type``: the service-managed model name when + ``managed``, or the customer's Foundry deployment name when ``self_deployed``. Carried through, + not generated. Required. + :paramtype model: str + :keyword agent_type: The persona/tone to steer generation. Known values are: "personal" and + "business". Required. + :paramtype agent_type: str or ~azure.ai.voiceagents.models.VoiceAgentType + :keyword use_case: The scenario-template catalog entry the generator specializes for. Known + values are: "customer_support", "reception", "sales", "travel_assistant", "outreach", + "personal_assistant", "learning", "call_center", and "in_car". Required. + :paramtype use_case: str or ~azure.ai.voiceagents.models.VoiceAgentUseCase + :keyword goal: A natural-language description of what the agent should do; the seed for the + generated ``instructions``. Required. + :paramtype goal: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword description: An optional description for the agent. Generated from ``goal`` when + omitted. Default value is None. + :paramtype description: str + :keyword tools: Optional tools carried through verbatim onto the generated agent (see + ``VoiceAgentTool``). Default value is None. + :paramtype tools: list[~azure.ai.voiceagents.models.FunctionTool or + ~azure.ai.voiceagents.models.MCPTool or ~azure.ai.voiceagents.models.VoiceSystemTool or + ~azure.ai.voiceagents.models.VoiceToolboxTool] + :keyword draft: (Preview) When ``true``, the generated voice agent is created as a draft — an + editable, unpublished version the caller can review and refine before publishing it via the + standard create/version path. The service defaults to ``false`` if a value is not specified by + the caller, in which case the agent is created and published normally. Default value is None. + :paramtype draft: bool + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if name is _Unset: + raise TypeError("missing required argument: name") + if model_type is _Unset: + raise TypeError("missing required argument: model_type") + if model is _Unset: + raise TypeError("missing required argument: model") + if agent_type is _Unset: + raise TypeError("missing required argument: agent_type") + if use_case is _Unset: + raise TypeError("missing required argument: use_case") + if goal is _Unset: + raise TypeError("missing required argument: goal") + body = { + "agent_type": agent_type, + "description": description, + "draft": draft, + "goal": goal, + "model": model, + "model_type": model_type, + "name": name, + "tools": tools, + "use_case": use_case, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_generate_voice_agent_request( + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + *, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + body: _types.CreateVoiceAgentVersionRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.voiceagents.types.CreateVoiceAgentVersionRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace_async + async def create_voice_agent_version( + self, + agent_name: str, + body: Union[JSON, _types.CreateVoiceAgentVersionRequest, IO[bytes]] = _Unset, + *, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Is one of the following types: JSON, CreateVoiceAgentVersionRequest, IO[bytes] + Required. + :type body: JSON or ~azure.ai.voiceagents.types.CreateVoiceAgentVersionRequest or IO[bytes] + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentVersionObject] = kwargs.pop("cls", None) + + if body is _Unset: + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "draft": draft, + "metadata": metadata, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_create_voice_agent_version_request( + agent_name=agent_name, + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentVersionObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_voice_agent_versions( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + include_drafts: Optional[bool] = None, + **kwargs: Any + ) -> AsyncItemPaged["_models.VoiceAgentVersionObject"]: + """List voice agent versions. + + Returns a paged collection of versions for the specified voice agent. + + :param agent_name: The name of the voice agent to retrieve versions for. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :keyword include_drafts: (Preview) Whether to include draft versions in the listing. The + service defaults to ``false`` if a value is not specified by the caller (only non-draft + versions are returned). Default value is None. + :paramtype include_drafts: bool + :return: An iterator like instance of VoiceAgentVersionObject + :rtype: + ~azure.core.async_paging.AsyncItemPaged[~azure.ai.voiceagents.models.VoiceAgentVersionObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceAgentVersionObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_voice_agents_list_voice_agent_versions_request( + agent_name=agent_name, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + include_drafts=include_drafts, + 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) + return _request + + async def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceAgentVersionObject], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, AsyncList(list_of_elem) + + async def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return AsyncItemPaged(get_next, extract_data) + + @distributed_trace_async + async def get_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Get a voice agent version. + + Retrieves the specified version of a voice agent by its agent name and version identifier. + + :param agent_name: The name of the voice agent to retrieve. Required. + :type agent_name: str + :param agent_version: The version of the voice agent to retrieve. Required. + :type agent_version: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :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.VoiceAgentVersionObject] = kwargs.pop("cls", None) + + _request = build_voice_agents_get_voice_agent_version_request( + agent_name=agent_name, + agent_version=agent_version, + foundry_features=foundry_features, + 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", 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.VoiceAgentVersionObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace_async + async def delete_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.DeleteAgentVersionResponse: + """Delete a voice agent version. + + Deletes a specific version of a voice agent. + + :param agent_name: The name of the voice agent to delete. Required. + :type agent_name: str + :param agent_version: The version of the voice agent to delete. Required. + :type agent_version: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: DeleteAgentVersionResponse. The DeleteAgentVersionResponse is compatible with + MutableMapping + :rtype: ~azure.ai.voiceagents.models.DeleteAgentVersionResponse + :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.DeleteAgentVersionResponse] = kwargs.pop("cls", None) + + _request = build_voice_agents_delete_voice_agent_version_request( + agent_name=agent_name, + agent_version=agent_version, + foundry_features=foundry_features, + 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", 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.DeleteAgentVersionResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_patch.py new file mode 100644 index 000000000000..87676c65a8f0 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_patch.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + + +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level + + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/__init__.py new file mode 100644 index 000000000000..6dfa600b710b --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/__init__.py @@ -0,0 +1,282 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + + +from ._models import ( # type: ignore + A2AProtocolConfiguration, + ActivityProtocolConfiguration, + AgentBlueprintReference, + AgentCard, + AgentCardSkill, + AgentEndpointAuthorizationScheme, + AgentEndpointConfig, + AgentIdentity, + ApiErrorResponse, + AzureVoice, + BotServiceAuthorizationScheme, + BotServiceRbacAuthorizationScheme, + BotServiceTenantAuthorizationScheme, + DeleteAgentResponse, + DeleteAgentVersionResponse, + EntraAuthorizationScheme, + Error, + FixedRatioVersionSelectionRule, + FunctionTool, + InvocationsProtocolConfiguration, + InvocationsWsProtocolConfiguration, + MCPListToolsTool, + MCPListToolsToolAnnotations, + MCPListToolsToolInputSchema, + MCPTool, + MCPToolFilter, + MCPToolRequireApproval, + ManagedAgentIdentityBlueprintReference, + McpProtocolConfiguration, + ProtocolConfiguration, + RaiConfig, + RealtimeAudioFormats, + RealtimeAudioFormatsAudioPcm, + RealtimeAudioFormatsAudioPcma, + RealtimeAudioFormatsAudioPcmu, + RealtimeConversationItem, + RealtimeConversationItemFunctionCall, + RealtimeConversationItemFunctionCallOutput, + RealtimeConversationItemMessage, + RealtimeConversationItemMessageAssistant, + RealtimeConversationItemMessageAssistantContent, + RealtimeConversationItemMessageSystem, + RealtimeConversationItemMessageSystemContent, + RealtimeConversationItemMessageUser, + RealtimeConversationItemMessageUserContent, + RealtimeMCPApprovalRequest, + RealtimeMCPApprovalResponse, + RealtimeMCPError, + RealtimeMCPHTTPError, + RealtimeMCPListTools, + RealtimeMCPProtocolError, + RealtimeMCPToolCall, + RealtimeMCPToolExecutionError, + RealtimeResponseStatusDetails, + RealtimeResponseStatusDetailsError, + RealtimeResponseUsage, + RealtimeResponseUsageInputTokenDetails, + RealtimeResponseUsageInputTokenDetailsCachedTokensDetails, + RealtimeResponseUsageOutputTokenDetails, + ResponsesProtocolConfiguration, + SemanticVadTurnDetection, + ServerVadTurnDetection, + StructuredInputDefinition, + Tool, + ToolConfig, + VersionSelectionRule, + VersionSelector, + VoiceAgentDefinition, + VoiceAgentObject, + VoiceAgentObjectVersions, + VoiceAgentVersionObject, + VoiceAssistantMessageItem, + VoiceAudioConfig, + VoiceAudioFormat, + VoiceAudioInputConfig, + VoiceAudioOutputConfig, + VoiceAvatarConfig, + VoiceConversation, + VoiceDeletedConversation, + VoiceFunctionCallItem, + VoiceFunctionCallOutputItem, + VoiceInputTranscription, + VoiceItemAudioResponse, + VoiceMcpApprovalRequestItem, + VoiceMcpApprovalResponseItem, + VoiceMcpCallItem, + VoiceMcpListToolsItem, + VoiceNoiseReduction, + VoiceRecordingChannelLayout, + VoiceRecordingResponse, + VoiceResponse, + VoiceResponseAudio, + VoiceResponseAudioOutput, + VoiceResponseVoice, + VoiceSystemMessageItem, + VoiceSystemTool, + VoiceToolboxTool, + VoiceTurnDetection, + VoiceUserMessageItem, +) + +from ._enums import ( # type: ignore + AgentBlueprintReferenceType, + AgentDefinitionOptInKeys, + AgentEndpointAuthorizationSchemeType, + AgentIdentityStatus, + AgentObjectType, + AgentState, + AgentVersionStatus, + AzureVoiceType, + PageOrder, + RealtimeAudioFormatsType, + RealtimeConversationItemMessageType, + RealtimeConversationItemType, + RealtimeMcpErrorType, + ToolType, + VersionSelectorType, + VoiceAgentType, + VoiceAgentUseCase, + VoiceAudioFormatType, + VoiceAvatarOutputProtocol, + VoiceAvatarType, + VoiceConversationStatus, + VoiceModelType, + VoiceNoiseReductionType, + VoiceOutputModality, + VoiceResponseStatus, + VoiceSystemToolName, + VoiceTurnDetectionEagerness, + VoiceTurnDetectionType, +) +from ._patch import __all__ as _patch_all +from ._patch import * +from ._patch import patch_sdk as _patch_sdk + +__all__ = [ + "A2AProtocolConfiguration", + "ActivityProtocolConfiguration", + "AgentBlueprintReference", + "AgentCard", + "AgentCardSkill", + "AgentEndpointAuthorizationScheme", + "AgentEndpointConfig", + "AgentIdentity", + "ApiErrorResponse", + "AzureVoice", + "BotServiceAuthorizationScheme", + "BotServiceRbacAuthorizationScheme", + "BotServiceTenantAuthorizationScheme", + "DeleteAgentResponse", + "DeleteAgentVersionResponse", + "EntraAuthorizationScheme", + "Error", + "FixedRatioVersionSelectionRule", + "FunctionTool", + "InvocationsProtocolConfiguration", + "InvocationsWsProtocolConfiguration", + "MCPListToolsTool", + "MCPListToolsToolAnnotations", + "MCPListToolsToolInputSchema", + "MCPTool", + "MCPToolFilter", + "MCPToolRequireApproval", + "ManagedAgentIdentityBlueprintReference", + "McpProtocolConfiguration", + "ProtocolConfiguration", + "RaiConfig", + "RealtimeAudioFormats", + "RealtimeAudioFormatsAudioPcm", + "RealtimeAudioFormatsAudioPcma", + "RealtimeAudioFormatsAudioPcmu", + "RealtimeConversationItem", + "RealtimeConversationItemFunctionCall", + "RealtimeConversationItemFunctionCallOutput", + "RealtimeConversationItemMessage", + "RealtimeConversationItemMessageAssistant", + "RealtimeConversationItemMessageAssistantContent", + "RealtimeConversationItemMessageSystem", + "RealtimeConversationItemMessageSystemContent", + "RealtimeConversationItemMessageUser", + "RealtimeConversationItemMessageUserContent", + "RealtimeMCPApprovalRequest", + "RealtimeMCPApprovalResponse", + "RealtimeMCPError", + "RealtimeMCPHTTPError", + "RealtimeMCPListTools", + "RealtimeMCPProtocolError", + "RealtimeMCPToolCall", + "RealtimeMCPToolExecutionError", + "RealtimeResponseStatusDetails", + "RealtimeResponseStatusDetailsError", + "RealtimeResponseUsage", + "RealtimeResponseUsageInputTokenDetails", + "RealtimeResponseUsageInputTokenDetailsCachedTokensDetails", + "RealtimeResponseUsageOutputTokenDetails", + "ResponsesProtocolConfiguration", + "SemanticVadTurnDetection", + "ServerVadTurnDetection", + "StructuredInputDefinition", + "Tool", + "ToolConfig", + "VersionSelectionRule", + "VersionSelector", + "VoiceAgentDefinition", + "VoiceAgentObject", + "VoiceAgentObjectVersions", + "VoiceAgentVersionObject", + "VoiceAssistantMessageItem", + "VoiceAudioConfig", + "VoiceAudioFormat", + "VoiceAudioInputConfig", + "VoiceAudioOutputConfig", + "VoiceAvatarConfig", + "VoiceConversation", + "VoiceDeletedConversation", + "VoiceFunctionCallItem", + "VoiceFunctionCallOutputItem", + "VoiceInputTranscription", + "VoiceItemAudioResponse", + "VoiceMcpApprovalRequestItem", + "VoiceMcpApprovalResponseItem", + "VoiceMcpCallItem", + "VoiceMcpListToolsItem", + "VoiceNoiseReduction", + "VoiceRecordingChannelLayout", + "VoiceRecordingResponse", + "VoiceResponse", + "VoiceResponseAudio", + "VoiceResponseAudioOutput", + "VoiceResponseVoice", + "VoiceSystemMessageItem", + "VoiceSystemTool", + "VoiceToolboxTool", + "VoiceTurnDetection", + "VoiceUserMessageItem", + "AgentBlueprintReferenceType", + "AgentDefinitionOptInKeys", + "AgentEndpointAuthorizationSchemeType", + "AgentIdentityStatus", + "AgentObjectType", + "AgentState", + "AgentVersionStatus", + "AzureVoiceType", + "PageOrder", + "RealtimeAudioFormatsType", + "RealtimeConversationItemMessageType", + "RealtimeConversationItemType", + "RealtimeMcpErrorType", + "ToolType", + "VersionSelectorType", + "VoiceAgentType", + "VoiceAgentUseCase", + "VoiceAudioFormatType", + "VoiceAvatarOutputProtocol", + "VoiceAvatarType", + "VoiceConversationStatus", + "VoiceModelType", + "VoiceNoiseReductionType", + "VoiceOutputModality", + "VoiceResponseStatus", + "VoiceSystemToolName", + "VoiceTurnDetectionEagerness", + "VoiceTurnDetectionType", +] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore +_patch_sdk() diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_enums.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_enums.py new file mode 100644 index 000000000000..fc84fda397b3 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_enums.py @@ -0,0 +1,396 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from enum import Enum +from azure.core import CaseInsensitiveEnumMeta + + +class AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of AgentBlueprintReferenceType.""" + + MANAGED_AGENT_IDENTITY_BLUEPRINT = "ManagedAgentIdentityBlueprint" + """MANAGED_AGENT_IDENTITY_BLUEPRINT.""" + + +class AgentDefinitionOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Feature opt-in keys for agent definition operations supporting hosted or workflow agents.""" + + WORKFLOW_AGENTS_V1_PREVIEW = "WorkflowAgents=V1Preview" + """WORKFLOW_AGENTS_V1_PREVIEW.""" + EXTERNAL_AGENTS_V1_PREVIEW = "ExternalAgents=V1Preview" + """EXTERNAL_AGENTS_V1_PREVIEW.""" + DRAFT_AGENTS_V1_PREVIEW = "DraftAgents=V1Preview" + """DRAFT_AGENTS_V1_PREVIEW.""" + VOICE_AGENTS_V1_PREVIEW = "VoiceAgents=V1Preview" + """VOICE_AGENTS_V1_PREVIEW.""" + + +class AgentEndpointAuthorizationSchemeType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of AgentEndpointAuthorizationSchemeType.""" + + ENTRA = "Entra" + """ENTRA.""" + BOT_SERVICE = "BotService" + """BOT_SERVICE.""" + BOT_SERVICE_RBAC = "BotServiceRbac" + """BOT_SERVICE_RBAC.""" + BOT_SERVICE_TENANT = "BotServiceTenant" + """BOT_SERVICE_TENANT.""" + + +class AgentIdentityStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The status of an agent identity, applicable to both the agent instance identity and the agent + blueprint. + """ + + ACTIVE = "active" + """The agent identity is active and can be used to access resources.""" + DISABLED = "disabled" + """The agent identity is disabled and cannot be used to access resources.""" + + +class AgentObjectType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of AgentObjectType.""" + + AGENT = "agent" + """AGENT.""" + AGENT_VERSION = "agent.version" + """AGENT_VERSION.""" + AGENT_DELETED = "agent.deleted" + """AGENT_DELETED.""" + AGENT_VERSION_DELETED = "agent.version.deleted" + """AGENT_VERSION_DELETED.""" + AGENT_CONTAINER = "agent.container" + """AGENT_CONTAINER.""" + + +class AgentState(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The operational state of an agent.""" + + ENABLED = "enabled" + """Agent endpoint accepts requests. This is the default state on creation.""" + DISABLED = "disabled" + """Agent endpoint rejects all requests.""" + + +class AgentVersionStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The provisioning status of an agent version.""" + + CREATING = "creating" + """The agent version is being provisioned.""" + ACTIVE = "active" + """The agent version is active and ready to serve requests.""" + FAILED = "failed" + """The agent version provisioning failed.""" + DELETING = "deleting" + """The agent version is being deleted.""" + DELETED = "deleted" + """The agent version has been deleted.""" + + +class AzureVoiceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The Azure voice kind.""" + + AZURE_STANDARD = "azure-standard" + """AZURE_STANDARD.""" + AZURE_CUSTOM = "azure-custom" + """AZURE_CUSTOM.""" + AZURE_PERSONAL = "azure-personal" + """AZURE_PERSONAL.""" + AVATAR_VOICE_SYNC = "avatar-voice-sync" + """AVATAR_VOICE_SYNC.""" + AZURE_REALTIME_NATIVE = "azure-realtime-native" + """AZURE_REALTIME_NATIVE.""" + + +class PageOrder(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of PageOrder.""" + + ASC = "asc" + """ASC.""" + DESC = "desc" + """DESC.""" + + +class RealtimeAudioFormatsType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of RealtimeAudioFormatsType.""" + + AUDIO_PCM = "audio/pcm" + """AUDIO_PCM.""" + AUDIO_PCMU = "audio/pcmu" + """AUDIO_PCMU.""" + AUDIO_PCMA = "audio/pcma" + """AUDIO_PCMA.""" + + +class RealtimeConversationItemMessageType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of RealtimeConversationItemMessageType.""" + + SYSTEM = "system" + """SYSTEM.""" + USER = "user" + """USER.""" + ASSISTANT = "assistant" + """ASSISTANT.""" + + +class RealtimeConversationItemType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of RealtimeConversationItemType.""" + + FUNCTION_CALL = "function_call" + """FUNCTION_CALL.""" + FUNCTION_CALL_OUTPUT = "function_call_output" + """FUNCTION_CALL_OUTPUT.""" + MCP_APPROVAL_RESPONSE = "mcp_approval_response" + """MCP_APPROVAL_RESPONSE.""" + MCP_LIST_TOOLS = "mcp_list_tools" + """MCP_LIST_TOOLS.""" + MCP_CALL = "mcp_call" + """MCP_CALL.""" + MCP_APPROVAL_REQUEST = "mcp_approval_request" + """MCP_APPROVAL_REQUEST.""" + + +class RealtimeMcpErrorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of RealtimeMcpErrorType.""" + + PROTOCOL_ERROR = "protocol_error" + """PROTOCOL_ERROR.""" + TOOL_EXECUTION_ERROR = "tool_execution_error" + """TOOL_EXECUTION_ERROR.""" + HTTP_ERROR = "http_error" + """HTTP_ERROR.""" + + +class ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of ToolType.""" + + FUNCTION = "function" + """FUNCTION.""" + FILE_SEARCH = "file_search" + """FILE_SEARCH.""" + COMPUTER = "computer" + """COMPUTER.""" + COMPUTER_USE_PREVIEW = "computer_use_preview" + """COMPUTER_USE_PREVIEW.""" + WEB_SEARCH = "web_search" + """WEB_SEARCH.""" + MCP = "mcp" + """MCP.""" + CODE_INTERPRETER = "code_interpreter" + """CODE_INTERPRETER.""" + IMAGE_GENERATION = "image_generation" + """IMAGE_GENERATION.""" + LOCAL_SHELL = "local_shell" + """LOCAL_SHELL.""" + SHELL = "shell" + """SHELL.""" + CUSTOM = "custom" + """CUSTOM.""" + NAMESPACE = "namespace" + """NAMESPACE.""" + TOOL_SEARCH = "tool_search" + """TOOL_SEARCH.""" + WEB_SEARCH_PREVIEW = "web_search_preview" + """WEB_SEARCH_PREVIEW.""" + APPLY_PATCH = "apply_patch" + """APPLY_PATCH.""" + A2_A_PREVIEW = "a2a_preview" + """A2_A_PREVIEW.""" + BING_CUSTOM_SEARCH_PREVIEW = "bing_custom_search_preview" + """BING_CUSTOM_SEARCH_PREVIEW.""" + BROWSER_AUTOMATION_PREVIEW = "browser_automation_preview" + """BROWSER_AUTOMATION_PREVIEW.""" + FABRIC_DATAAGENT_PREVIEW = "fabric_dataagent_preview" + """FABRIC_DATAAGENT_PREVIEW.""" + SHAREPOINT_GROUNDING_PREVIEW = "sharepoint_grounding_preview" + """SHAREPOINT_GROUNDING_PREVIEW.""" + MEMORY_SEARCH_PREVIEW = "memory_search_preview" + """MEMORY_SEARCH_PREVIEW.""" + WORK_IQ_PREVIEW = "work_iq_preview" + """WORK_IQ_PREVIEW.""" + FABRIC_IQ_PREVIEW = "fabric_iq_preview" + """FABRIC_IQ_PREVIEW.""" + WEB_IQ_PREVIEW = "web_iq_preview" + """WEB_IQ_PREVIEW.""" + TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" + """TOOLBOX_SEARCH_PREVIEW.""" + AZURE_AI_SEARCH = "azure_ai_search" + """AZURE_AI_SEARCH.""" + AZURE_FUNCTION = "azure_function" + """AZURE_FUNCTION.""" + BING_GROUNDING = "bing_grounding" + """BING_GROUNDING.""" + CAPTURE_STRUCTURED_OUTPUTS = "capture_structured_outputs" + """CAPTURE_STRUCTURED_OUTPUTS.""" + OPENAPI = "openapi" + """OPENAPI.""" + + +class VersionSelectorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """Type of VersionSelectorType.""" + + FIXED_RATIO = "FixedRatio" + """FIXED_RATIO.""" + + +class VoiceAgentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The persona/tone a guided-authoring request steers the generated voice agent toward.""" + + PERSONAL = "personal" + """A personal-assistant persona.""" + BUSINESS = "business" + """A business / professional persona.""" + + +class VoiceAgentUseCase(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The scenario-template catalog entry a guided-authoring request specializes the generated voice + agent for. Extensible: additional use cases may be added over time. + """ + + CUSTOMER_SUPPORT = "customer_support" + """CUSTOMER_SUPPORT.""" + RECEPTION = "reception" + """RECEPTION.""" + SALES = "sales" + """SALES.""" + TRAVEL_ASSISTANT = "travel_assistant" + """TRAVEL_ASSISTANT.""" + OUTREACH = "outreach" + """OUTREACH.""" + PERSONAL_ASSISTANT = "personal_assistant" + """PERSONAL_ASSISTANT.""" + LEARNING = "learning" + """LEARNING.""" + CALL_CENTER = "call_center" + """CALL_CENTER.""" + IN_CAR = "in_car" + """IN_CAR.""" + + +class VoiceAudioFormatType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The audio format type. Values follow the OpenAI Realtime wire schema and are exempt from the + snake_case enum-value rule. + """ + + PCM = "audio/pcm" + """16-bit PCM.""" + PCMU = "audio/pcmu" + """G.711 mu-law (telephony).""" + PCMA = "audio/pcma" + """G.711 A-law (telephony).""" + + +class VoiceAvatarOutputProtocol(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The transport used to deliver the avatar video stream.""" + + WEBRTC = "webrtc" + """WEBRTC.""" + WEBSOCKET = "websocket" + """WEBSOCKET.""" + + +class VoiceAvatarType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The avatar type.""" + + VIDEO_AVATAR = "video-avatar" + """VIDEO_AVATAR.""" + PHOTO_AVATAR = "photo-avatar" + """PHOTO_AVATAR.""" + + +class VoiceConversationStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The lifecycle status of a persisted voice conversation.""" + + IN_PROGRESS = "in_progress" + """The conversation's live session is still in progress.""" + COMPLETED = "completed" + """The conversation's live session has ended.""" + + +class VoiceModelType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """How the model backing a voice agent is served. This is independent of the architecture + (realtime or cascaded), which the service derives from the selected model. + """ + + MANAGED = "managed" + """The service hosts and manages the named model, for example ``gpt-realtime``.""" + SELF_DEPLOYED = "self_deployed" + """The service uses the customer's own Foundry deployment named by ``model``.""" + + +class VoiceNoiseReductionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The input audio noise reduction mode.""" + + NEAR_FIELD = "near_field" + """NEAR_FIELD.""" + FAR_FIELD = "far_field" + """FAR_FIELD.""" + AZURE_DEEP_NOISE_SUPPRESSION = "azure_deep_noise_suppression" + """Azure deep noise suppression.""" + + +class VoiceOutputModality(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """An output modality the agent may produce. ``animation`` and ``avatar`` are used when an avatar + is configured. + """ + + TEXT = "text" + """TEXT.""" + AUDIO = "audio" + """AUDIO.""" + ANIMATION = "animation" + """ANIMATION.""" + AVATAR = "avatar" + """AVATAR.""" + + +class VoiceResponseStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The status of a voice response.""" + + IN_PROGRESS = "in_progress" + """IN_PROGRESS.""" + COMPLETED = "completed" + """COMPLETED.""" + CANCELLED = "cancelled" + """CANCELLED.""" + INCOMPLETE = "incomplete" + """INCOMPLETE.""" + FAILED = "failed" + """FAILED.""" + + +class VoiceSystemToolName(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """A service-managed voice-session control action. Known values are stable; additional values may + be added over time. + """ + + END_CONVERSATION = "end_conversation" + """Ends the active conversation.""" + + +class VoiceTurnDetectionEagerness(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """How eagerly semantic turn detection ends the user's turn.""" + + AUTO = "auto" + """AUTO.""" + LOW = "low" + """LOW.""" + MEDIUM = "medium" + """MEDIUM.""" + HIGH = "high" + """HIGH.""" + + +class VoiceTurnDetectionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + """The turn detection strategy. Values follow the OpenAI Realtime wire schema.""" + + SERVER_VAD = "server_vad" + """SERVER_VAD.""" + SEMANTIC_VAD = "semantic_vad" + """SEMANTIC_VAD.""" diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_models.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_models.py new file mode 100644 index 000000000000..81eaca88fdc6 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_models.py @@ -0,0 +1,4463 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=useless-super-delegation + +import datetime +from typing import Any, Literal, Mapping, Optional, TYPE_CHECKING, Union, overload + +from .._utils.model_base import Model as _Model, rest_discriminator, rest_field +from ._enums import ( + AgentBlueprintReferenceType, + AgentEndpointAuthorizationSchemeType, + AgentObjectType, + RealtimeAudioFormatsType, + RealtimeConversationItemMessageType, + RealtimeConversationItemType, + RealtimeMcpErrorType, + ToolType, + VersionSelectorType, + VoiceTurnDetectionType, +) + +if TYPE_CHECKING: + from .. import _unions, models as _models + + +class A2AProtocolConfiguration(_Model): + """Configuration specific to the A2A protocol.""" + + +class ActivityProtocolConfiguration(_Model): + """Configuration specific to the activity protocol. + + :ivar enable_m365_public_endpoint: Whether to enable the M365 public endpoint for the activity + protocol. + :vartype enable_m365_public_endpoint: bool + """ + + enable_m365_public_endpoint: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether to enable the M365 public endpoint for the activity protocol.""" + + @overload + def __init__( + self, + *, + enable_m365_public_endpoint: Optional[bool] = 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 AgentBlueprintReference(_Model): + """AgentBlueprintReference. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + ManagedAgentIdentityBlueprintReference + + :ivar type: Required. "ManagedAgentIdentityBlueprint" + :vartype type: str or ~azure.ai.voiceagents.models.AgentBlueprintReferenceType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. \"ManagedAgentIdentityBlueprint\"""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 AgentCard(_Model): + """AgentCard. + + :ivar version: The version of the agent card. Required. + :vartype version: str + :ivar description: The description of the agent card. + :vartype description: str + :ivar skills: The set of skills that an agent can perform. Required. + :vartype skills: list[~azure.ai.voiceagents.models.AgentCardSkill] + """ + + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version of the agent card. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The description of the agent card.""" + skills: list["_models.AgentCardSkill"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The set of skills that an agent can perform. Required.""" + + @overload + def __init__( + self, + *, + version: str, + skills: list["_models.AgentCardSkill"], + description: Optional[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 AgentCardSkill(_Model): + """AgentCardSkill. + + :ivar id: a unique identifier for the skill. Required. + :vartype id: str + :ivar name: The name of the skill. Required. + :vartype name: str + :ivar description: A description of the skill. + :vartype description: str + :ivar tags: set of tagwords describing classes of capabilities for the skill. + :vartype tags: list[str] + :ivar examples: A list of example scenarios that the skill can perform. + :vartype examples: list[str] + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """a unique identifier for the skill. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the skill. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A description of the skill.""" + tags: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """set of tagwords describing classes of capabilities for the skill.""" + examples: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A list of example scenarios that the skill can perform.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + name: str, + description: Optional[str] = None, + tags: Optional[list[str]] = None, + examples: Optional[list[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 AgentEndpointAuthorizationScheme(_Model): + """AgentEndpointAuthorizationScheme. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + BotServiceAuthorizationScheme, BotServiceRbacAuthorizationScheme, + BotServiceTenantAuthorizationScheme, EntraAuthorizationScheme + + :ivar type: Required. Known values are: "Entra", "BotService", "BotServiceRbac", and + "BotServiceTenant". + :vartype type: str or ~azure.ai.voiceagents.models.AgentEndpointAuthorizationSchemeType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"Entra\", \"BotService\", \"BotServiceRbac\", and + \"BotServiceTenant\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 AgentEndpointConfig(_Model): + """AgentEndpointConfig. + + :ivar version_selector: The version selector of the agent endpoint determines how traffic is + routed to different versions of the agent. + :vartype version_selector: ~azure.ai.voiceagents.models.VersionSelector + :ivar protocol_configuration: Per-protocol configuration for the agent endpoint. + :vartype protocol_configuration: ~azure.ai.voiceagents.models.ProtocolConfiguration + :ivar authorization_schemes: The authorization schemes supported by the agent endpoint. + :vartype authorization_schemes: + list[~azure.ai.voiceagents.models.AgentEndpointAuthorizationScheme] + """ + + version_selector: Optional["_models.VersionSelector"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The version selector of the agent endpoint determines how traffic is routed to different + versions of the agent.""" + protocol_configuration: Optional["_models.ProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Per-protocol configuration for the agent endpoint.""" + authorization_schemes: Optional[list["_models.AgentEndpointAuthorizationScheme"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The authorization schemes supported by the agent endpoint.""" + + @overload + def __init__( + self, + *, + version_selector: Optional["_models.VersionSelector"] = None, + protocol_configuration: Optional["_models.ProtocolConfiguration"] = None, + authorization_schemes: Optional[list["_models.AgentEndpointAuthorizationScheme"]] = 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 AgentIdentity(_Model): + """AgentIdentity. + + :ivar principal_id: The principal ID of the agent instance. Required. + :vartype principal_id: str + :ivar client_id: The client ID of the agent instance. Also referred to as the instance ID. + Required. + :vartype client_id: str + :ivar status: The status of the agent identity. Present for both the agent instance identity + and the agent blueprint. Known values are: "active" and "disabled". + :vartype status: str or ~azure.ai.voiceagents.models.AgentIdentityStatus + """ + + principal_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The principal ID of the agent instance. Required.""" + client_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The client ID of the agent instance. Also referred to as the instance ID. Required.""" + status: Optional[Union[str, "_models.AgentIdentityStatus"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the agent identity. Present for both the agent instance identity and the agent + blueprint. Known values are: \"active\" and \"disabled\".""" + + @overload + def __init__( + self, + *, + principal_id: str, + client_id: str, + status: Optional[Union[str, "_models.AgentIdentityStatus"]] = 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 ApiErrorResponse(_Model): + """Error response for API failures. + + :ivar error: Required. + :vartype error: ~azure.ai.voiceagents.models.Error + """ + + error: "_models.Error" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + + @overload + def __init__( + self, + *, + error: "_models.Error", + ) -> 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 AzureVoice(_Model): + """An Azure neural voice with optional prosody. Built-in voices are specified as a bare string + instead. + + :ivar type: The Azure voice kind. Required. Known values are: "azure-standard", "azure-custom", + "azure-personal", "avatar-voice-sync", and "azure-realtime-native". + :vartype type: str or ~azure.ai.voiceagents.models.AzureVoiceType + :ivar name: The Azure neural voice name, e.g. 'en-US-AvaNeural'. Required. + :vartype name: str + :ivar style: Optional speaking style, e.g. 'cheerful'. + :vartype style: str + :ivar pitch: Optional pitch adjustment, e.g. '+5%'. + :vartype pitch: str + :ivar rate: Optional rate adjustment, e.g. '+10%'. + :vartype rate: str + :ivar locale: Optional BCP-47 locale override, e.g. 'en-US'. + :vartype locale: str + """ + + type: Union[str, "_models.AzureVoiceType"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The Azure voice kind. Required. Known values are: \"azure-standard\", \"azure-custom\", + \"azure-personal\", \"avatar-voice-sync\", and \"azure-realtime-native\".""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The Azure neural voice name, e.g. 'en-US-AvaNeural'. Required.""" + style: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional speaking style, e.g. 'cheerful'.""" + pitch: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional pitch adjustment, e.g. '+5%'.""" + rate: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional rate adjustment, e.g. '+10%'.""" + locale: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional BCP-47 locale override, e.g. 'en-US'.""" + + @overload + def __init__( + self, + *, + type: Union[str, "_models.AzureVoiceType"], + name: str, + style: Optional[str] = None, + pitch: Optional[str] = None, + rate: Optional[str] = None, + locale: Optional[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 BotServiceAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator="BotService"): + """BotServiceAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE. + :vartype type: str or ~azure.ai.voiceagents.models.BOT_SERVICE + """ + + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. BOT_SERVICE.""" + + @overload + def __init__( + self, + ) -> 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 = AgentEndpointAuthorizationSchemeType.BOT_SERVICE # type: ignore + + +class BotServiceRbacAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator="BotServiceRbac"): + """BotServiceRbacAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE_RBAC. + :vartype type: str or ~azure.ai.voiceagents.models.BOT_SERVICE_RBAC + """ + + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. BOT_SERVICE_RBAC.""" + + @overload + def __init__( + self, + ) -> 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 = AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC # type: ignore + + +class BotServiceTenantAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator="BotServiceTenant"): + """BotServiceTenantAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE_TENANT. + :vartype type: str or ~azure.ai.voiceagents.models.BOT_SERVICE_TENANT + """ + + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. BOT_SERVICE_TENANT.""" + + @overload + def __init__( + self, + ) -> 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 = AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT # type: ignore + + +class DeleteAgentResponse(_Model): + """A deleted agent Object. + + :ivar object: The object type. Always 'agent.deleted'. Required. AGENT_DELETED. + :vartype object: str or ~azure.ai.voiceagents.models.AGENT_DELETED + :ivar name: The name of the agent. Required. + :vartype name: str + :ivar deleted: Whether the agent was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[AgentObjectType.AGENT_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'agent.deleted'. Required. AGENT_DELETED.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the agent was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[AgentObjectType.AGENT_DELETED], + name: str, + deleted: bool, + ) -> 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 DeleteAgentVersionResponse(_Model): + """A deleted agent version Object. + + :ivar object: The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED. + :vartype object: str or ~azure.ai.voiceagents.models.AGENT_VERSION_DELETED + :ivar name: The name of the agent. Required. + :vartype name: str + :ivar version: The version identifier of the agent. Required. + :vartype version: str + :ivar deleted: Whether the agent was successfully deleted. Required. + :vartype deleted: bool + """ + + object: Literal[AgentObjectType.AGENT_VERSION_DELETED] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always 'agent.version.deleted'. Required. AGENT_VERSION_DELETED.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version identifier of the agent. Required.""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the agent was successfully deleted. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[AgentObjectType.AGENT_VERSION_DELETED], + name: str, + version: str, + deleted: bool, + ) -> 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 EntraAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator="Entra"): + """EntraAuthorizationScheme. + + :ivar type: Required. ENTRA. + :vartype type: str or ~azure.ai.voiceagents.models.ENTRA + """ + + type: Literal[AgentEndpointAuthorizationSchemeType.ENTRA] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. ENTRA.""" + + @overload + def __init__( + self, + ) -> 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 = AgentEndpointAuthorizationSchemeType.ENTRA # type: ignore + + +class Error(_Model): + """Error. + + :ivar code: Required. + :vartype code: str + :ivar message: Required. + :vartype message: str + :ivar param: + :vartype param: str + :ivar type: + :vartype type: str + :ivar details: + :vartype details: list[~azure.ai.voiceagents.models.Error] + :ivar additional_info: + :vartype additional_info: dict[str, any] + :ivar debug_info: + :vartype debug_info: dict[str, any] + """ + + code: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + param: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + type: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + details: Optional[list["_models.Error"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + additional_info: Optional[dict[str, Any]] = rest_field( + name="additionalInfo", visibility=["read", "create", "update", "delete", "query"] + ) + debug_info: Optional[dict[str, Any]] = rest_field( + name="debugInfo", visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + code: str, + message: str, + param: Optional[str] = None, + type: Optional[str] = None, + details: Optional[list["_models.Error"]] = None, + additional_info: Optional[dict[str, Any]] = None, + debug_info: Optional[dict[str, Any]] = 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 VersionSelectionRule(_Model): + """VersionSelectionRule. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + FixedRatioVersionSelectionRule + + :ivar type: Required. "FixedRatio" + :vartype type: str or ~azure.ai.voiceagents.models.VersionSelectorType + :ivar agent_version: The agent version to route traffic to. Required. + :vartype agent_version: str + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. \"FixedRatio\"""" + agent_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The agent version to route traffic to. Required.""" + + @overload + def __init__( + self, + *, + type: str, + agent_version: str, + ) -> 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 FixedRatioVersionSelectionRule(VersionSelectionRule, discriminator="FixedRatio"): + """FixedRatioVersionSelectionRule. + + :ivar agent_version: The agent version to route traffic to. Required. + :vartype agent_version: str + :ivar type: Required. FIXED_RATIO. + :vartype type: str or ~azure.ai.voiceagents.models.FIXED_RATIO + :ivar traffic_percentage: The percentage of traffic to route to the version. Must be between 0 + and 100. Required. + :vartype traffic_percentage: int + """ + + type: Literal[VersionSelectorType.FIXED_RATIO] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. FIXED_RATIO.""" + traffic_percentage: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The percentage of traffic to route to the version. Must be between 0 and 100. Required.""" + + @overload + def __init__( + self, + *, + agent_version: str, + traffic_percentage: int, + ) -> 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 = VersionSelectorType.FIXED_RATIO # type: ignore + + +class Tool(_Model): + """A tool that can be used to generate a response. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + FunctionTool, MCPTool + + :ivar type: Required. Known values are: "function", "file_search", "computer", + "computer_use_preview", "web_search", "mcp", "code_interpreter", "image_generation", + "local_shell", "shell", "custom", "namespace", "tool_search", "web_search_preview", + "apply_patch", "a2a_preview", "bing_custom_search_preview", "browser_automation_preview", + "fabric_dataagent_preview", "sharepoint_grounding_preview", "memory_search_preview", + "work_iq_preview", "fabric_iq_preview", "web_iq_preview", "toolbox_search_preview", + "azure_ai_search", "azure_function", "bing_grounding", "capture_structured_outputs", and + "openapi". + :vartype type: str or ~azure.ai.voiceagents.models.ToolType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"function\", \"file_search\", \"computer\", + \"computer_use_preview\", \"web_search\", \"mcp\", \"code_interpreter\", \"image_generation\", + \"local_shell\", \"shell\", \"custom\", \"namespace\", \"tool_search\", \"web_search_preview\", + \"apply_patch\", \"a2a_preview\", \"bing_custom_search_preview\", + \"browser_automation_preview\", \"fabric_dataagent_preview\", \"sharepoint_grounding_preview\", + \"memory_search_preview\", \"work_iq_preview\", \"fabric_iq_preview\", \"web_iq_preview\", + \"toolbox_search_preview\", \"azure_ai_search\", \"azure_function\", \"bing_grounding\", + \"capture_structured_outputs\", and \"openapi\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 FunctionTool(Tool, discriminator="function"): + """Function. + + :ivar type: The type of the function tool. Always ``function``. Required. FUNCTION. + :vartype type: str or ~azure.ai.voiceagents.models.FUNCTION + :ivar name: The name of the function to call. Required. + :vartype name: str + :ivar description: + :vartype description: str + :ivar parameters: Required. + :vartype parameters: dict[str, any] + :ivar strict: Required. + :vartype strict: bool + :ivar defer_loading: Whether this function is deferred and loaded via tool search. + :vartype defer_loading: bool + """ + + type: Literal[ToolType.FUNCTION] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the function tool. Always ``function``. Required. FUNCTION.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the function to call. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + parameters: dict[str, Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + strict: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this function is deferred and loaded via tool search.""" + + @overload + def __init__( + self, + *, + name: str, + parameters: dict[str, Any], + strict: bool, + description: Optional[str] = None, + defer_loading: Optional[bool] = 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 = ToolType.FUNCTION # type: ignore + + +class InvocationsProtocolConfiguration(_Model): + """Configuration specific to the invocations protocol.""" + + +class InvocationsWsProtocolConfiguration(_Model): + """Configuration specific to the WebSocket-based invocations protocol.""" + + +class ManagedAgentIdentityBlueprintReference(AgentBlueprintReference, discriminator="ManagedAgentIdentityBlueprint"): + """ManagedAgentIdentityBlueprintReference. + + :ivar type: Required. MANAGED_AGENT_IDENTITY_BLUEPRINT. + :vartype type: str or ~azure.ai.voiceagents.models.MANAGED_AGENT_IDENTITY_BLUEPRINT + :ivar blueprint_id: The ID of the managed blueprint. Required. + :vartype blueprint_id: str + """ + + type: Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. MANAGED_AGENT_IDENTITY_BLUEPRINT.""" + blueprint_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the managed blueprint. Required.""" + + @overload + def __init__( + self, + *, + blueprint_id: str, + ) -> 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 = AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT # type: ignore + + +class MCPListToolsTool(_Model): + """MCP list tools tool. + + :ivar name: The name of the tool. Required. + :vartype name: str + :ivar description: + :vartype description: str + :ivar input_schema: The JSON schema describing the tool's input. Required. + :vartype input_schema: ~azure.ai.voiceagents.models.MCPListToolsToolInputSchema + :ivar annotations: + :vartype annotations: ~azure.ai.voiceagents.models.MCPListToolsToolAnnotations + """ + + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the tool. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + input_schema: "_models.MCPListToolsToolInputSchema" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The JSON schema describing the tool's input. Required.""" + annotations: Optional["_models.MCPListToolsToolAnnotations"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + name: str, + input_schema: "_models.MCPListToolsToolInputSchema", + description: Optional[str] = None, + annotations: Optional["_models.MCPListToolsToolAnnotations"] = 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 MCPListToolsToolAnnotations(_Model): + """MCPListToolsToolAnnotations.""" + + +class MCPListToolsToolInputSchema(_Model): + """MCPListToolsToolInputSchema.""" + + +class McpProtocolConfiguration(_Model): + """Configuration specific to the MCP protocol.""" + + +class MCPTool(Tool, discriminator="mcp"): + """MCP tool. + + :ivar type: The type of the MCP tool. Always ``mcp``. Required. MCP. + :vartype type: str or ~azure.ai.voiceagents.models.MCP + :ivar server_label: A label for this MCP server, used to identify it in tool calls. Required. + :vartype server_label: str + :ivar server_url: The URL for the MCP server. One of ``server_url``, ``connector_id``, or + ``tunnel_id`` must be provided. + :vartype server_url: str + :ivar connector_id: Identifier for service connectors, like those available in ChatGPT. One of + ``server_url``, ``connector_id``, or ``tunnel_id`` must be provided. Learn more about service + connectors `here `_. Currently supported + ``connector_id`` values are: + + * Dropbox: `connector_dropbox` + * Gmail: `connector_gmail` + * Google Calendar: `connector_googlecalendar` + * Google Drive: `connector_googledrive` + * Microsoft Teams: `connector_microsoftteams` + * Outlook Calendar: `connector_outlookcalendar` + * Outlook Email: `connector_outlookemail` + * SharePoint: `connector_sharepoint`. Is one of the following types: + Literal["connector_dropbox"], Literal["connector_gmail"], Literal["connector_googlecalendar"], + Literal["connector_googledrive"], Literal["connector_microsoftteams"], + Literal["connector_outlookcalendar"], Literal["connector_outlookemail"], + Literal["connector_sharepoint"] + :vartype connector_id: str or str or str or str or str or str or str or str + :ivar tunnel_id: The Secure MCP Tunnel ID to use instead of a direct server URL. One of + ``server_url``, ``connector_id``, or ``tunnel_id`` must be provided. + :vartype tunnel_id: str + :ivar authorization: An OAuth access token that can be used with a remote MCP server, either + with a custom MCP server URL or a service connector. Your application must handle the OAuth + authorization flow and provide the token here. + :vartype authorization: str + :ivar server_description: Optional description of the MCP server, used to provide more context. + :vartype server_description: str + :ivar headers: + :vartype headers: dict[str, str] + :ivar allowed_tools: Is either a [str] type or a MCPToolFilter type. + :vartype allowed_tools: list[str] or ~azure.ai.voiceagents.models.MCPToolFilter + :ivar require_approval: Is one of the following types: MCPToolRequireApproval, + Literal["always"], Literal["never"] + :vartype require_approval: ~azure.ai.voiceagents.models.MCPToolRequireApproval or str or str + :ivar defer_loading: Whether this MCP tool is deferred and discovered via tool search. + :vartype defer_loading: bool + :ivar project_connection_id: The connection ID in the project for the MCP server. The + connection stores authentication and other connection details needed to connect to the MCP + server. + :vartype project_connection_id: str + :ivar tool_configs: Deprecated. This property is deprecated and will be removed in a future + version. + :vartype tool_configs: dict[str, ~azure.ai.voiceagents.models.ToolConfig] + """ + + type: Literal[ToolType.MCP] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the MCP tool. Always ``mcp``. Required. MCP.""" + server_label: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A label for this MCP server, used to identify it in tool calls. Required.""" + server_url: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The URL for the MCP server. One of ``server_url``, ``connector_id``, or ``tunnel_id`` must be + provided.""" + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for service connectors, like those available in ChatGPT. One of ``server_url``, + ``connector_id``, or ``tunnel_id`` must be provided. Learn more about service connectors `here + `_. Currently supported ``connector_id`` values are: + + * Dropbox: `connector_dropbox` + * Gmail: `connector_gmail` + * Google Calendar: `connector_googlecalendar` + * Google Drive: `connector_googledrive` + * Microsoft Teams: `connector_microsoftteams` + * Outlook Calendar: `connector_outlookcalendar` + * Outlook Email: `connector_outlookemail` + * SharePoint: `connector_sharepoint`. Is one of the following types: + Literal[\"connector_dropbox\"], Literal[\"connector_gmail\"], + Literal[\"connector_googlecalendar\"], Literal[\"connector_googledrive\"], + Literal[\"connector_microsoftteams\"], Literal[\"connector_outlookcalendar\"], + Literal[\"connector_outlookemail\"], Literal[\"connector_sharepoint\"]""" + tunnel_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The Secure MCP Tunnel ID to use instead of a direct server URL. One of ``server_url``, + ``connector_id``, or ``tunnel_id`` must be provided.""" + authorization: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An OAuth access token that can be used with a remote MCP server, either with a custom MCP + server URL or a service connector. Your application must handle the OAuth authorization flow + and provide the token here.""" + server_description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional description of the MCP server, used to provide more context.""" + headers: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + allowed_tools: Optional[Union[list[str], "_models.MCPToolFilter"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is either a [str] type or a MCPToolFilter type.""" + require_approval: Optional[Union["_models.MCPToolRequireApproval", Literal["always"], Literal["never"]]] = ( + rest_field(visibility=["read", "create", "update", "delete", "query"]) + ) + """Is one of the following types: MCPToolRequireApproval, Literal[\"always\"], Literal[\"never\"]""" + defer_loading: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this MCP tool is deferred and discovered via tool search.""" + project_connection_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The connection ID in the project for the MCP server. The connection stores authentication and + other connection details needed to connect to the MCP server.""" + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Deprecated. This property is deprecated and will be removed in a future version.""" + + @overload + def __init__( + self, + *, + server_label: str, + server_url: Optional[str] = None, + connector_id: Optional[ + Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + ] = None, + tunnel_id: Optional[str] = None, + authorization: Optional[str] = None, + server_description: Optional[str] = None, + headers: Optional[dict[str, str]] = None, + allowed_tools: Optional[Union[list[str], "_models.MCPToolFilter"]] = None, + require_approval: Optional[Union["_models.MCPToolRequireApproval", Literal["always"], Literal["never"]]] = None, + defer_loading: Optional[bool] = None, + project_connection_id: Optional[str] = None, + tool_configs: Optional[dict[str, "_models.ToolConfig"]] = 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 = ToolType.MCP # type: ignore + + +class MCPToolFilter(_Model): + """MCP tool filter. + + :ivar tool_names: MCP allowed tools. + :vartype tool_names: list[str] + :ivar read_only: Indicates whether or not a tool modifies data or is read-only. If an MCP + server is `annotated with `readOnlyHint` + `_, + it will match this filter. + :vartype read_only: bool + """ + + tool_names: Optional[list[str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """MCP allowed tools.""" + read_only: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Indicates whether or not a tool modifies data or is read-only. If an MCP server is `annotated + with `readOnlyHint` + `_, + it will match this filter.""" + + @overload + def __init__( + self, + *, + tool_names: Optional[list[str]] = None, + read_only: Optional[bool] = 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 MCPToolRequireApproval(_Model): + """MCPToolRequireApproval. + + :ivar always: + :vartype always: ~azure.ai.voiceagents.models.MCPToolFilter + :ivar never: + :vartype never: ~azure.ai.voiceagents.models.MCPToolFilter + """ + + always: Optional["_models.MCPToolFilter"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + never: Optional["_models.MCPToolFilter"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + always: Optional["_models.MCPToolFilter"] = None, + never: Optional["_models.MCPToolFilter"] = 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 ProtocolConfiguration(_Model): + """Per-protocol configuration for the agent endpoint. + + :ivar activity: Configuration for the activity protocol. + :vartype activity: ~azure.ai.voiceagents.models.ActivityProtocolConfiguration + :ivar responses: Configuration for the responses protocol. + :vartype responses: ~azure.ai.voiceagents.models.ResponsesProtocolConfiguration + :ivar a2_a: Configuration for the A2A protocol. + :vartype a2_a: ~azure.ai.voiceagents.models.A2AProtocolConfiguration + :ivar mcp: Configuration for the MCP protocol. + :vartype mcp: ~azure.ai.voiceagents.models.McpProtocolConfiguration + :ivar invocations: Configuration for the invocations protocol. + :vartype invocations: ~azure.ai.voiceagents.models.InvocationsProtocolConfiguration + :ivar invocations_ws: Configuration for the WebSocket-based invocations protocol. + :vartype invocations_ws: ~azure.ai.voiceagents.models.InvocationsWsProtocolConfiguration + """ + + activity: Optional["_models.ActivityProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the activity protocol.""" + responses: Optional["_models.ResponsesProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the responses protocol.""" + a2_a: Optional["_models.A2AProtocolConfiguration"] = rest_field( + name="a2a", visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the A2A protocol.""" + mcp: Optional["_models.McpProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the MCP protocol.""" + invocations: Optional["_models.InvocationsProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the invocations protocol.""" + invocations_ws: Optional["_models.InvocationsWsProtocolConfiguration"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Configuration for the WebSocket-based invocations protocol.""" + + @overload + def __init__( + self, + *, + activity: Optional["_models.ActivityProtocolConfiguration"] = None, + responses: Optional["_models.ResponsesProtocolConfiguration"] = None, + a2_a: Optional["_models.A2AProtocolConfiguration"] = None, + mcp: Optional["_models.McpProtocolConfiguration"] = None, + invocations: Optional["_models.InvocationsProtocolConfiguration"] = None, + invocations_ws: Optional["_models.InvocationsWsProtocolConfiguration"] = 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 RaiConfig(_Model): + """Configuration for Responsible AI (RAI) content filtering and safety features. + + :ivar rai_policy_name: The name of the RAI policy to apply. Required. + :vartype rai_policy_name: str + """ + + rai_policy_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the RAI policy to apply. Required.""" + + @overload + def __init__( + self, + *, + rai_policy_name: str, + ) -> 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 RealtimeAudioFormats(_Model): + """RealtimeAudioFormats. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + RealtimeAudioFormatsAudioPcm, RealtimeAudioFormatsAudioPcma, RealtimeAudioFormatsAudioPcmu + + :ivar type: Required. Known values are: "audio/pcm", "audio/pcmu", and "audio/pcma". + :vartype type: str or ~azure.ai.voiceagents.models.RealtimeAudioFormatsType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"audio/pcm\", \"audio/pcmu\", and \"audio/pcma\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 RealtimeAudioFormatsAudioPcm(RealtimeAudioFormats, discriminator="audio/pcm"): + """RealtimeAudioFormatsAudioPcm. + + :ivar type: Required. AUDIO_PCM. + :vartype type: str or ~azure.ai.voiceagents.models.AUDIO_PCM + :ivar rate: Default value is 24000. + :vartype rate: int + """ + + type: Literal[RealtimeAudioFormatsType.AUDIO_PCM] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. AUDIO_PCM.""" + rate: Optional[Literal[24000]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Default value is 24000.""" + + @overload + def __init__( + self, + *, + rate: Optional[Literal[24000]] = 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 = RealtimeAudioFormatsType.AUDIO_PCM # type: ignore + + +class RealtimeAudioFormatsAudioPcma(RealtimeAudioFormats, discriminator="audio/pcma"): + """RealtimeAudioFormatsAudioPcma. + + :ivar type: Required. AUDIO_PCMA. + :vartype type: str or ~azure.ai.voiceagents.models.AUDIO_PCMA + """ + + type: Literal[RealtimeAudioFormatsType.AUDIO_PCMA] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. AUDIO_PCMA.""" + + @overload + def __init__( + self, + ) -> 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 = RealtimeAudioFormatsType.AUDIO_PCMA # type: ignore + + +class RealtimeAudioFormatsAudioPcmu(RealtimeAudioFormats, discriminator="audio/pcmu"): + """RealtimeAudioFormatsAudioPcmu. + + :ivar type: Required. AUDIO_PCMU. + :vartype type: str or ~azure.ai.voiceagents.models.AUDIO_PCMU + """ + + type: Literal[RealtimeAudioFormatsType.AUDIO_PCMU] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. AUDIO_PCMU.""" + + @overload + def __init__( + self, + ) -> 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 = RealtimeAudioFormatsType.AUDIO_PCMU # type: ignore + + +class RealtimeConversationItem(_Model): + """A single item within a Realtime conversation. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + RealtimeConversationItemFunctionCall, RealtimeConversationItemFunctionCallOutput, + RealtimeMCPApprovalRequest, RealtimeMCPApprovalResponse, RealtimeMCPToolCall, + RealtimeMCPListTools + + :ivar type: Required. Known values are: "function_call", "function_call_output", + "mcp_approval_response", "mcp_list_tools", "mcp_call", and "mcp_approval_request". + :vartype type: str or ~azure.ai.voiceagents.models.RealtimeConversationItemType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"function_call\", \"function_call_output\", + \"mcp_approval_response\", \"mcp_list_tools\", \"mcp_call\", and \"mcp_approval_request\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 RealtimeConversationItemFunctionCall(RealtimeConversationItem, discriminator="function_call"): + """Realtime function call item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``function_call``. Required. FUNCTION_CALL. + :vartype type: str or ~azure.ai.voiceagents.models.FUNCTION_CALL + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar call_id: The ID of the function call. + :vartype call_id: str + :ivar name: The name of the function being called. Required. + :vartype name: str + :ivar arguments: The arguments of the function call. This is a JSON-encoded string representing + the arguments passed to the function, for example ``{"arg1": "value1", "arg2": 42}``. Required. + :vartype arguments: str + """ + + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the item. This may be provided by the client or generated by the server.""" + object: Optional[Literal["realtime.item"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for the API object being returned - always ``realtime.item``. Optional when creating + a new item. Default value is \"realtime.item\".""" + type: Literal[RealtimeConversationItemType.FUNCTION_CALL] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``function_call``. Required. FUNCTION_CALL.""" + status: Optional[Literal["completed", "incomplete", "in_progress"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the item. Has no effect on the conversation. Is one of the following types: + Literal[\"completed\"], Literal[\"incomplete\"], Literal[\"in_progress\"]""" + call_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the function call.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the function being called. Required.""" + arguments: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The arguments of the function call. This is a JSON-encoded string representing the arguments + passed to the function, for example ``{\"arg1\": \"value1\", \"arg2\": 42}``. Required.""" + + @overload + def __init__( + self, + *, + name: str, + arguments: str, + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + call_id: Optional[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) + self.type = RealtimeConversationItemType.FUNCTION_CALL # type: ignore + + +class RealtimeConversationItemFunctionCallOutput( + RealtimeConversationItem, discriminator="function_call_output" +): # pylint: disable=name-too-long + """Realtime function call output item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``function_call_output``. Required. + FUNCTION_CALL_OUTPUT. + :vartype type: str or ~azure.ai.voiceagents.models.FUNCTION_CALL_OUTPUT + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar call_id: The ID of the function call this output is for. Required. + :vartype call_id: str + :ivar output: The output of the function call, this is free text and can contain any + information or simply be empty. Required. + :vartype output: str + """ + + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the item. This may be provided by the client or generated by the server.""" + object: Optional[Literal["realtime.item"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for the API object being returned - always ``realtime.item``. Optional when creating + a new item. Default value is \"realtime.item\".""" + type: Literal[RealtimeConversationItemType.FUNCTION_CALL_OUTPUT] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``function_call_output``. Required. FUNCTION_CALL_OUTPUT.""" + status: Optional[Literal["completed", "incomplete", "in_progress"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the item. Has no effect on the conversation. Is one of the following types: + Literal[\"completed\"], Literal[\"incomplete\"], Literal[\"in_progress\"]""" + call_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the function call this output is for. Required.""" + output: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The output of the function call, this is free text and can contain any information or simply be + empty. Required.""" + + @overload + def __init__( + self, + *, + call_id: str, + output: str, + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = 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 = RealtimeConversationItemType.FUNCTION_CALL_OUTPUT # type: ignore + + +class RealtimeConversationItemMessage(_Model): + """RealtimeConversationItemMessage. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + RealtimeConversationItemMessageAssistant, RealtimeConversationItemMessageSystem, + RealtimeConversationItemMessageUser + + :ivar role: Required. Known values are: "system", "user", and "assistant". + :vartype role: str or ~azure.ai.voiceagents.models.RealtimeConversationItemMessageType + """ + + __mapping__: dict[str, _Model] = {} + role: str = rest_discriminator(name="role", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"system\", \"user\", and \"assistant\".""" + + @overload + def __init__( + self, + *, + role: str, + ) -> 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 RealtimeConversationItemMessageAssistant(RealtimeConversationItemMessage, discriminator="assistant"): + """Realtime assistant message item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``assistant``. Required. ASSISTANT. + :vartype role: str or ~azure.ai.voiceagents.models.ASSISTANT + :ivar content: The content of the message. Required. + :vartype content: + list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistantContent] + """ + + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the item. This may be provided by the client or generated by the server.""" + object: Optional[Literal["realtime.item"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for the API object being returned - always ``realtime.item``. Optional when creating + a new item. Default value is \"realtime.item\".""" + type: Literal["message"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The type of the item. Always ``message``. Required. Default value is \"message\".""" + status: Optional[Literal["completed", "incomplete", "in_progress"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the item. Has no effect on the conversation. Is one of the following types: + Literal[\"completed\"], Literal[\"incomplete\"], Literal[\"in_progress\"]""" + role: Literal[RealtimeConversationItemMessageType.ASSISTANT] = rest_discriminator(name="role", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The role of the message sender. Always ``assistant``. Required. ASSISTANT.""" + content: list["_models.RealtimeConversationItemMessageAssistantContent"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The content of the message. Required.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageAssistantContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = 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.role = RealtimeConversationItemMessageType.ASSISTANT # type: ignore + self.type: Literal["message"] = "message" + + +class RealtimeConversationItemMessageAssistantContent(_Model): # pylint: disable=name-too-long + """RealtimeConversationItemMessageAssistantContent. + + :ivar type: Is either a Literal["output_text"] type or a Literal["output_audio"] type. + :vartype type: str or str + :ivar text: + :vartype text: str + :ivar audio: + :vartype audio: str + :ivar transcript: + :vartype transcript: str + """ + + type: Optional[Literal["output_text", "output_audio"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is either a Literal[\"output_text\"] type or a Literal[\"output_audio\"] type.""" + text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + audio: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + transcript: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + type: Optional[Literal["output_text", "output_audio"]] = None, + text: Optional[str] = None, + audio: Optional[str] = None, + transcript: Optional[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 RealtimeConversationItemMessageSystem(RealtimeConversationItemMessage, discriminator="system"): + """Realtime system message item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``system``. Required. SYSTEM. + :vartype role: str or ~azure.ai.voiceagents.models.SYSTEM + :ivar content: The content of the message. Required. + :vartype content: + list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageSystemContent] + """ + + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the item. This may be provided by the client or generated by the server.""" + object: Optional[Literal["realtime.item"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for the API object being returned - always ``realtime.item``. Optional when creating + a new item. Default value is \"realtime.item\".""" + type: Literal["message"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The type of the item. Always ``message``. Required. Default value is \"message\".""" + status: Optional[Literal["completed", "incomplete", "in_progress"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the item. Has no effect on the conversation. Is one of the following types: + Literal[\"completed\"], Literal[\"incomplete\"], Literal[\"in_progress\"]""" + role: Literal[RealtimeConversationItemMessageType.SYSTEM] = rest_discriminator(name="role", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The role of the message sender. Always ``system``. Required. SYSTEM.""" + content: list["_models.RealtimeConversationItemMessageSystemContent"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The content of the message. Required.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageSystemContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = 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.role = RealtimeConversationItemMessageType.SYSTEM # type: ignore + self.type: Literal["message"] = "message" + + +class RealtimeConversationItemMessageSystemContent(_Model): # pylint: disable=name-too-long + """RealtimeConversationItemMessageSystemContent. + + :ivar type: Default value is "input_text". + :vartype type: str + :ivar text: + :vartype text: str + """ + + type: Optional[Literal["input_text"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Default value is \"input_text\".""" + text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + type: Optional[Literal["input_text"]] = None, + text: Optional[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 RealtimeConversationItemMessageUser(RealtimeConversationItemMessage, discriminator="user"): + """Realtime user message item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``user``. Required. USER. + :vartype role: str or ~azure.ai.voiceagents.models.USER + :ivar content: The content of the message. Required. + :vartype content: list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageUserContent] + """ + + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the item. This may be provided by the client or generated by the server.""" + object: Optional[Literal["realtime.item"]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Identifier for the API object being returned - always ``realtime.item``. Optional when creating + a new item. Default value is \"realtime.item\".""" + type: Literal["message"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The type of the item. Always ``message``. Required. Default value is \"message\".""" + status: Optional[Literal["completed", "incomplete", "in_progress"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the item. Has no effect on the conversation. Is one of the following types: + Literal[\"completed\"], Literal[\"incomplete\"], Literal[\"in_progress\"]""" + role: Literal[RealtimeConversationItemMessageType.USER] = rest_discriminator(name="role", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The role of the message sender. Always ``user``. Required. USER.""" + content: list["_models.RealtimeConversationItemMessageUserContent"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The content of the message. Required.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageUserContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = 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.role = RealtimeConversationItemMessageType.USER # type: ignore + self.type: Literal["message"] = "message" + + +class RealtimeConversationItemMessageUserContent(_Model): # pylint: disable=name-too-long + """RealtimeConversationItemMessageUserContent. + + :ivar type: Is one of the following types: Literal["input_text"], Literal["input_audio"], + Literal["input_image"] + :vartype type: str or str or str + :ivar text: + :vartype text: str + :ivar audio: + :vartype audio: str + :ivar image_url: + :vartype image_url: str + :ivar detail: Is one of the following types: Literal["auto"], Literal["low"], Literal["high"] + :vartype detail: str or str or str + :ivar transcript: + :vartype transcript: str + """ + + type: Optional[Literal["input_text", "input_audio", "input_image"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is one of the following types: Literal[\"input_text\"], Literal[\"input_audio\"], + Literal[\"input_image\"]""" + text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + audio: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + image_url: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + detail: Optional[Literal["auto", "low", "high"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is one of the following types: Literal[\"auto\"], Literal[\"low\"], Literal[\"high\"]""" + transcript: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + type: Optional[Literal["input_text", "input_audio", "input_image"]] = None, + text: Optional[str] = None, + audio: Optional[str] = None, + image_url: Optional[str] = None, + detail: Optional[Literal["auto", "low", "high"]] = None, + transcript: Optional[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 RealtimeMCPApprovalRequest(RealtimeConversationItem, discriminator="mcp_approval_request"): + """Realtime MCP approval request. + + :ivar type: The type of the item. Always ``mcp_approval_request``. Required. + MCP_APPROVAL_REQUEST. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_APPROVAL_REQUEST + :ivar id: The unique ID of the approval request. Required. + :vartype id: str + :ivar server_label: The label of the MCP server making the request. Required. + :vartype server_label: str + :ivar name: The name of the tool to run. Required. + :vartype name: str + :ivar arguments: A JSON string of arguments for the tool. Required. + :vartype arguments: str + """ + + type: Literal[RealtimeConversationItemType.MCP_APPROVAL_REQUEST] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``mcp_approval_request``. Required. MCP_APPROVAL_REQUEST.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the approval request. Required.""" + server_label: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The label of the MCP server making the request. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the tool to run. Required.""" + arguments: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A JSON string of arguments for the tool. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + server_label: str, + name: str, + arguments: str, + ) -> 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 = RealtimeConversationItemType.MCP_APPROVAL_REQUEST # type: ignore + + +class RealtimeMCPApprovalResponse(RealtimeConversationItem, discriminator="mcp_approval_response"): + """Realtime MCP approval response. + + :ivar type: The type of the item. Always ``mcp_approval_response``. Required. + MCP_APPROVAL_RESPONSE. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_APPROVAL_RESPONSE + :ivar id: The unique ID of the approval response. Required. + :vartype id: str + :ivar approval_request_id: The ID of the approval request being answered. Required. + :vartype approval_request_id: str + :ivar approve: Whether the request was approved. Required. + :vartype approve: bool + :ivar reason: + :vartype reason: str + """ + + type: Literal[RealtimeConversationItemType.MCP_APPROVAL_RESPONSE] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``mcp_approval_response``. Required. MCP_APPROVAL_RESPONSE.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the approval response. Required.""" + approval_request_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The ID of the approval request being answered. Required.""" + approve: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the request was approved. Required.""" + reason: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + approval_request_id: str, + approve: bool, + reason: Optional[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) + self.type = RealtimeConversationItemType.MCP_APPROVAL_RESPONSE # type: ignore + + +class RealtimeMCPError(_Model): + """RealtimeMCPError. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + RealtimeMCPHTTPError, RealtimeMCPProtocolError, RealtimeMCPToolExecutionError + + :ivar type: Required. Known values are: "protocol_error", "tool_execution_error", and + "http_error". + :vartype type: str or ~azure.ai.voiceagents.models.RealtimeMcpErrorType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """Required. Known values are: \"protocol_error\", \"tool_execution_error\", and \"http_error\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 RealtimeMCPHTTPError(RealtimeMCPError, discriminator="http_error"): + """Realtime MCP HTTP error. + + :ivar type: Required. HTTP_ERROR. + :vartype type: str or ~azure.ai.voiceagents.models.HTTP_ERROR + :ivar code: Required. + :vartype code: int + :ivar message: Required. + :vartype message: str + """ + + type: Literal[RealtimeMcpErrorType.HTTP_ERROR] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. HTTP_ERROR.""" + code: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + + @overload + def __init__( + self, + *, + code: int, + message: str, + ) -> 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 = RealtimeMcpErrorType.HTTP_ERROR # type: ignore + + +class RealtimeMCPListTools(RealtimeConversationItem, discriminator="mcp_list_tools"): + """Realtime MCP list tools. + + :ivar type: The type of the item. Always ``mcp_list_tools``. Required. MCP_LIST_TOOLS. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_LIST_TOOLS + :ivar id: The unique ID of the list. + :vartype id: str + :ivar server_label: The label of the MCP server. Required. + :vartype server_label: str + :ivar tools: The tools available on the server. Required. + :vartype tools: list[~azure.ai.voiceagents.models.MCPListToolsTool] + """ + + type: Literal[RealtimeConversationItemType.MCP_LIST_TOOLS] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``mcp_list_tools``. Required. MCP_LIST_TOOLS.""" + id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the list.""" + server_label: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The label of the MCP server. Required.""" + tools: list["_models.MCPListToolsTool"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The tools available on the server. Required.""" + + @overload + def __init__( + self, + *, + server_label: str, + tools: list["_models.MCPListToolsTool"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + ) -> 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 = RealtimeConversationItemType.MCP_LIST_TOOLS # type: ignore + + +class RealtimeMCPProtocolError(RealtimeMCPError, discriminator="protocol_error"): + """Realtime MCP protocol error. + + :ivar type: Required. PROTOCOL_ERROR. + :vartype type: str or ~azure.ai.voiceagents.models.PROTOCOL_ERROR + :ivar code: Required. + :vartype code: int + :ivar message: Required. + :vartype message: str + """ + + type: Literal[RealtimeMcpErrorType.PROTOCOL_ERROR] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. PROTOCOL_ERROR.""" + code: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + + @overload + def __init__( + self, + *, + code: int, + message: str, + ) -> 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 = RealtimeMcpErrorType.PROTOCOL_ERROR # type: ignore + + +class RealtimeMCPToolCall(RealtimeConversationItem, discriminator="mcp_call"): + """Realtime MCP tool call. + + :ivar type: The type of the item. Always ``mcp_call``. Required. MCP_CALL. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_CALL + :ivar id: The unique ID of the tool call. Required. + :vartype id: str + :ivar server_label: The label of the MCP server running the tool. Required. + :vartype server_label: str + :ivar name: The name of the tool that was run. Required. + :vartype name: str + :ivar arguments: A JSON string of the arguments passed to the tool. Required. + :vartype arguments: str + :ivar approval_request_id: + :vartype approval_request_id: str + :ivar output: + :vartype output: str + :ivar error: + :vartype error: ~azure.ai.voiceagents.models.RealtimeMCPError + """ + + type: Literal[RealtimeConversationItemType.MCP_CALL] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """The type of the item. Always ``mcp_call``. Required. MCP_CALL.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique ID of the tool call. Required.""" + server_label: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The label of the MCP server running the tool. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the tool that was run. Required.""" + arguments: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A JSON string of the arguments passed to the tool. Required.""" + approval_request_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + output: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + error: Optional["_models.RealtimeMCPError"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + server_label: str, + name: str, + arguments: str, + approval_request_id: Optional[str] = None, + output: Optional[str] = None, + error: Optional["_models.RealtimeMCPError"] = 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 = RealtimeConversationItemType.MCP_CALL # type: ignore + + +class RealtimeMCPToolExecutionError(RealtimeMCPError, discriminator="tool_execution_error"): + """Realtime MCP tool execution error. + + :ivar type: Required. TOOL_EXECUTION_ERROR. + :vartype type: str or ~azure.ai.voiceagents.models.TOOL_EXECUTION_ERROR + :ivar message: Required. + :vartype message: str + """ + + type: Literal[RealtimeMcpErrorType.TOOL_EXECUTION_ERROR] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. TOOL_EXECUTION_ERROR.""" + message: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + + @overload + def __init__( + self, + *, + message: str, + ) -> 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 = RealtimeMcpErrorType.TOOL_EXECUTION_ERROR # type: ignore + + +class RealtimeResponseStatusDetails(_Model): + """RealtimeResponseStatusDetails. + + :ivar type: Is one of the following types: Literal["completed"], Literal["cancelled"], + Literal["failed"], Literal["incomplete"] + :vartype type: str or str or str or str + :ivar reason: Is one of the following types: Literal["turn_detected"], + Literal["client_cancelled"], Literal["max_output_tokens"], Literal["content_filter"] + :vartype reason: str or str or str or str + :ivar error: + :vartype error: ~azure.ai.voiceagents.models.RealtimeResponseStatusDetailsError + """ + + type: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is one of the following types: Literal[\"completed\"], Literal[\"cancelled\"], + Literal[\"failed\"], Literal[\"incomplete\"]""" + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Is one of the following types: Literal[\"turn_detected\"], Literal[\"client_cancelled\"], + Literal[\"max_output_tokens\"], Literal[\"content_filter\"]""" + error: Optional["_models.RealtimeResponseStatusDetailsError"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + type: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] = None, + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] = None, + error: Optional["_models.RealtimeResponseStatusDetailsError"] = 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 RealtimeResponseStatusDetailsError(_Model): + """RealtimeResponseStatusDetailsError. + + :ivar type: + :vartype type: str + :ivar code: + :vartype code: str + """ + + type: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + code: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + type: Optional[str] = None, + code: Optional[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 RealtimeResponseUsage(_Model): + """RealtimeResponseUsage. + + :ivar total_tokens: + :vartype total_tokens: int + :ivar input_tokens: + :vartype input_tokens: int + :ivar output_tokens: + :vartype output_tokens: int + :ivar input_token_details: + :vartype input_token_details: + ~azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetails + :ivar output_token_details: + :vartype output_token_details: + ~azure.ai.voiceagents.models.RealtimeResponseUsageOutputTokenDetails + """ + + total_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + input_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + output_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + input_token_details: Optional["_models.RealtimeResponseUsageInputTokenDetails"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + output_token_details: Optional["_models.RealtimeResponseUsageOutputTokenDetails"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + total_tokens: Optional[int] = None, + input_tokens: Optional[int] = None, + output_tokens: Optional[int] = None, + input_token_details: Optional["_models.RealtimeResponseUsageInputTokenDetails"] = None, + output_token_details: Optional["_models.RealtimeResponseUsageOutputTokenDetails"] = 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 RealtimeResponseUsageInputTokenDetails(_Model): + """RealtimeResponseUsageInputTokenDetails. + + :ivar cached_tokens: + :vartype cached_tokens: int + :ivar text_tokens: + :vartype text_tokens: int + :ivar image_tokens: + :vartype image_tokens: int + :ivar audio_tokens: + :vartype audio_tokens: int + :ivar cached_tokens_details: + :vartype cached_tokens_details: + ~azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails + """ + + cached_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + text_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + image_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + audio_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + cached_tokens_details: Optional["_models.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + + @overload + def __init__( + self, + *, + cached_tokens: Optional[int] = None, + text_tokens: Optional[int] = None, + image_tokens: Optional[int] = None, + audio_tokens: Optional[int] = None, + cached_tokens_details: Optional["_models.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails"] = 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 RealtimeResponseUsageInputTokenDetailsCachedTokensDetails(_Model): # pylint: disable=name-too-long + """RealtimeResponseUsageInputTokenDetailsCachedTokensDetails. + + :ivar text_tokens: + :vartype text_tokens: int + :ivar image_tokens: + :vartype image_tokens: int + :ivar audio_tokens: + :vartype audio_tokens: int + """ + + text_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + image_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + audio_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + text_tokens: Optional[int] = None, + image_tokens: Optional[int] = None, + audio_tokens: Optional[int] = 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 RealtimeResponseUsageOutputTokenDetails(_Model): + """RealtimeResponseUsageOutputTokenDetails. + + :ivar text_tokens: + :vartype text_tokens: int + :ivar audio_tokens: + :vartype audio_tokens: int + """ + + text_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + audio_tokens: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + + @overload + def __init__( + self, + *, + text_tokens: Optional[int] = None, + audio_tokens: Optional[int] = 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 ResponsesProtocolConfiguration(_Model): + """Configuration specific to the responses protocol.""" + + +class VoiceTurnDetection(_Model): + """Turn (end-of-speech) detection configuration. + + You probably want to use the sub-classes and not this class directly. Known sub-classes are: + SemanticVadTurnDetection, ServerVadTurnDetection + + :ivar type: The turn detection strategy. Required. Known values are: "server_vad" and + "semantic_vad". + :vartype type: str or ~azure.ai.voiceagents.models.VoiceTurnDetectionType + """ + + __mapping__: dict[str, _Model] = {} + type: str = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) + """The turn detection strategy. Required. Known values are: \"server_vad\" and \"semantic_vad\".""" + + @overload + def __init__( + self, + *, + type: str, + ) -> 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 SemanticVadTurnDetection(VoiceTurnDetection, discriminator="semantic_vad"): + """Semantic (model-based) turn detection. + + :ivar type: Required. SEMANTIC_VAD. + :vartype type: str or ~azure.ai.voiceagents.models.SEMANTIC_VAD + :ivar eagerness: How eagerly the model ends the user's turn. Known values are: "auto", "low", + "medium", and "high". + :vartype eagerness: str or ~azure.ai.voiceagents.models.VoiceTurnDetectionEagerness + """ + + type: Literal[VoiceTurnDetectionType.SEMANTIC_VAD] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. SEMANTIC_VAD.""" + eagerness: Optional[Union[str, "_models.VoiceTurnDetectionEagerness"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """How eagerly the model ends the user's turn. Known values are: \"auto\", \"low\", \"medium\", + and \"high\".""" + + @overload + def __init__( + self, + *, + eagerness: Optional[Union[str, "_models.VoiceTurnDetectionEagerness"]] = 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 = VoiceTurnDetectionType.SEMANTIC_VAD # type: ignore + + +class ServerVadTurnDetection(VoiceTurnDetection, discriminator="server_vad"): + """Server-side voice activity detection (silence-based). + + :ivar type: Required. SERVER_VAD. + :vartype type: str or ~azure.ai.voiceagents.models.SERVER_VAD + :ivar threshold: Activation threshold (0-1). Higher requires louder audio to trigger. + :vartype threshold: float + :ivar prefix_padding_ms: Audio (ms) to include before detected speech start. + :vartype prefix_padding_ms: int + :ivar silence_duration_ms: Silence (ms) used to detect speech stop. + :vartype silence_duration_ms: int + :ivar create_response: Whether the server automatically creates a response when a turn ends. + Defaults to true. + :vartype create_response: bool + """ + + type: Literal[VoiceTurnDetectionType.SERVER_VAD] = rest_discriminator(name="type", visibility=["read", "create", "update", "delete", "query"]) # type: ignore + """Required. SERVER_VAD.""" + threshold: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Activation threshold (0-1). Higher requires louder audio to trigger.""" + prefix_padding_ms: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Audio (ms) to include before detected speech start.""" + silence_duration_ms: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Silence (ms) used to detect speech stop.""" + create_response: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the server automatically creates a response when a turn ends. Defaults to true.""" + + @overload + def __init__( + self, + *, + threshold: Optional[float] = None, + prefix_padding_ms: Optional[int] = None, + silence_duration_ms: Optional[int] = None, + create_response: Optional[bool] = 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 = VoiceTurnDetectionType.SERVER_VAD # type: ignore + + +class StructuredInputDefinition(_Model): + """An structured input that can participate in prompt template substitutions and tool argument + binding. + + :ivar description: A human-readable description of the input. + :vartype description: str + :ivar default_value: The default value for the input if no run-time value is provided. + :vartype default_value: any + :ivar schema: The JSON schema for the structured input (optional). + :vartype schema: dict[str, any] + :ivar required: Whether the input property is required when the agent is invoked. The service + defaults to ``false`` if a value is not specified by the caller. + :vartype required: bool + """ + + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the input.""" + default_value: Optional[Any] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The default value for the input if no run-time value is provided.""" + schema: Optional[dict[str, Any]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The JSON schema for the structured input (optional).""" + required: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the input property is required when the agent is invoked. The service defaults to + ``false`` if a value is not specified by the caller.""" + + @overload + def __init__( + self, + *, + description: Optional[str] = None, + default_value: Optional[Any] = None, + schema: Optional[dict[str, Any]] = None, + required: Optional[bool] = 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 ToolConfig(_Model): + """Per-tool configuration that controls tool visibility and search behavior. + + :ivar pin: When true, the tool is always included in agent context and visible in + ``tools/list``. When false (default), the tool is hidden from ``tools/list`` and only + discoverable via ``tool_search``. + :vartype pin: bool + :ivar additional_search_text: Additional text indexed for tool_search. Supplements the native + tool description to improve discoverability. Does not alter ``tools/list`` output. + :vartype additional_search_text: str + """ + + pin: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """When true, the tool is always included in agent context and visible in ``tools/list``. When + false (default), the tool is hidden from ``tools/list`` and only discoverable via + ``tool_search``.""" + additional_search_text: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Additional text indexed for tool_search. Supplements the native tool description to improve + discoverability. Does not alter ``tools/list`` output.""" + + @overload + def __init__( + self, + *, + pin: Optional[bool] = None, + additional_search_text: Optional[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 VersionSelector(_Model): + """VersionSelector. + + :ivar version_selection_rules: Required. + :vartype version_selection_rules: list[~azure.ai.voiceagents.models.VersionSelectionRule] + """ + + version_selection_rules: list["_models.VersionSelectionRule"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Required.""" + + @overload + def __init__( + self, + *, + version_selection_rules: list["_models.VersionSelectionRule"], + ) -> 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 VoiceAgentDefinition(_Model): + """The voice agent definition. Its configuration (model, instructions, audio, tools, and optional + avatar) drives a managed speech-to-speech experience. The realtime voice session is established + through a separate connect operation that is not defined in this specification. Every create or + update produces a new immutable version. + + :ivar kind: The kind discriminator for a voice agent definition. Always ``voice``. Required. + Default value is "voice". + :vartype kind: str + :ivar rai_config: Configuration for Responsible AI (RAI) content filtering and safety features. + :vartype rai_config: ~azure.ai.voiceagents.models.RaiConfig + :ivar model_type: How the model backing this agent is served. Together with ``model``, this + selects the model up front. ``managed`` uses a service-managed model; ``self_deployed`` uses + the customer's own Foundry deployment. This is independent of the architecture (realtime or + cascaded), which the service derives from the selected model. Required. Known values are: + "managed" and "self_deployed". + :vartype model_type: str or ~azure.ai.voiceagents.models.VoiceModelType + :ivar model: The model to use for this agent, paired with ``model_type``: the service-managed + model name when ``model_type`` is ``managed``, or the customer's Foundry deployment name when + ``model_type`` is ``self_deployed``. The model must support realtime or cascaded voice. The + service derives the architecture from the selected model. Required. + :vartype model: str + :ivar instructions: A system (or developer) message inserted into the model's context. Supports + template substitution via ``structured_inputs``, rendered per session before the live session + starts. + :vartype instructions: str + :ivar audio: The audio configuration, including input and output formats, voice, turn + detection, noise reduction, and transcription. These values are session defaults; a client may + override supported fields when connecting. + :vartype audio: ~azure.ai.voiceagents.models.VoiceAudioConfig + :ivar output_modalities: The output modalities the agent produces. Defaults to ``["audio"]``. + ``animation`` and ``avatar`` are available when an avatar is configured. + :vartype output_modalities: list[str or ~azure.ai.voiceagents.models.VoiceOutputModality] + :ivar avatar: Optional avatar configuration. These values are session defaults and may be + overridden when connecting. + :vartype avatar: ~azure.ai.voiceagents.models.VoiceAvatarConfig + :ivar tools: The tools the voice agent may use. Supported tool kinds are ``function`` (executed + by the client), ``mcp``, ``system`` (service-managed session controls), and ``toolbox``. + Server-side tools such as ``web_search``, ``azure_ai_search``, and ``openapi`` are provided + through a toolbox rather than declared directly. + :vartype tools: list[~azure.ai.voiceagents.models.FunctionTool or + ~azure.ai.voiceagents.models.MCPTool or ~azure.ai.voiceagents.models.VoiceSystemTool or + ~azure.ai.voiceagents.models.VoiceToolboxTool] + :ivar structured_inputs: Set of structured inputs that participate in prompt template + substitution, rendered per session before the live session starts. + :vartype structured_inputs: dict[str, ~azure.ai.voiceagents.models.StructuredInputDefinition] + :ivar store: Whether conversations with this agent are persisted. A single, all-or-nothing + persistence switch that defaults to ``false`` (privacy-safe: off by default). When ``true``, + Foundry persists the full conversation — the transcript/event timeline and raw audio. When + ``false``, nothing is persisted and no conversation is surfaced. There is no separate + audio-logging control; audio is persisted only as part of this switch. Latency/performance + telemetry (e.g. time-to-first-audio, inter-token latency, interruption) is observability-only + (customer trace / App Insights) and is not part of the persisted conversation content. + :vartype store: bool + """ + + kind: Literal["voice"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The kind discriminator for a voice agent definition. Always ``voice``. Required. Default value + is \"voice\".""" + rai_config: Optional["_models.RaiConfig"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Configuration for Responsible AI (RAI) content filtering and safety features.""" + model_type: Union[str, "_models.VoiceModelType"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """How the model backing this agent is served. Together with ``model``, this selects the model up + front. ``managed`` uses a service-managed model; ``self_deployed`` uses the customer's own + Foundry deployment. This is independent of the architecture (realtime or cascaded), which the + service derives from the selected model. Required. Known values are: \"managed\" and + \"self_deployed\".""" + model: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The model to use for this agent, paired with ``model_type``: the service-managed model name + when ``model_type`` is ``managed``, or the customer's Foundry deployment name when + ``model_type`` is ``self_deployed``. The model must support realtime or cascaded voice. The + service derives the architecture from the selected model. Required.""" + instructions: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A system (or developer) message inserted into the model's context. Supports template + substitution via ``structured_inputs``, rendered per session before the live session starts.""" + audio: Optional["_models.VoiceAudioConfig"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The audio configuration, including input and output formats, voice, turn detection, noise + reduction, and transcription. These values are session defaults; a client may override + supported fields when connecting.""" + output_modalities: Optional[list[Union[str, "_models.VoiceOutputModality"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The output modalities the agent produces. Defaults to ``[\"audio\"]``. ``animation`` and + ``avatar`` are available when an avatar is configured.""" + avatar: Optional["_models.VoiceAvatarConfig"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Optional avatar configuration. These values are session defaults and may be overridden when + connecting.""" + tools: Optional[list["_unions.VoiceAgentTool"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The tools the voice agent may use. Supported tool kinds are ``function`` (executed by the + client), ``mcp``, ``system`` (service-managed session controls), and ``toolbox``. Server-side + tools such as ``web_search``, ``azure_ai_search``, and ``openapi`` are provided through a + toolbox rather than declared directly.""" + structured_inputs: Optional[dict[str, "_models.StructuredInputDefinition"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Set of structured inputs that participate in prompt template substitution, rendered per session + before the live session starts.""" + store: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether conversations with this agent are persisted. A single, all-or-nothing persistence + switch that defaults to ``false`` (privacy-safe: off by default). When ``true``, Foundry + persists the full conversation — the transcript/event timeline and raw audio. When ``false``, + nothing is persisted and no conversation is surfaced. There is no separate audio-logging + control; audio is persisted only as part of this switch. Latency/performance telemetry (e.g. + time-to-first-audio, inter-token latency, interruption) is observability-only (customer trace / + App Insights) and is not part of the persisted conversation content.""" + + @overload + def __init__( + self, + *, + model_type: Union[str, "_models.VoiceModelType"], + model: str, + rai_config: Optional["_models.RaiConfig"] = None, + instructions: Optional[str] = None, + audio: Optional["_models.VoiceAudioConfig"] = None, + output_modalities: Optional[list[Union[str, "_models.VoiceOutputModality"]]] = None, + avatar: Optional["_models.VoiceAvatarConfig"] = None, + tools: Optional[list["_unions.VoiceAgentTool"]] = None, + structured_inputs: Optional[dict[str, "_models.StructuredInputDefinition"]] = None, + store: Optional[bool] = 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.kind: Literal["voice"] = "voice" + + +class VoiceAgentObject(_Model): + """A voice agent. Mirrors ``AgentObject``, but its latest version is a + ``VoiceAgentVersionObject``. + + :ivar object: The object type, which is always 'agent'. Required. AGENT. + :vartype object: str or ~azure.ai.voiceagents.models.AGENT + :ivar id: The unique identifier of the agent. Required. + :vartype id: str + :ivar name: The name of the agent. Required. + :vartype name: str + :ivar state: The operational state of the agent. Controls whether the agent endpoint accepts or + rejects requests. Required. Known values are: "enabled" and "disabled". + :vartype state: str or ~azure.ai.voiceagents.models.AgentState + :ivar agent_endpoint: The endpoint configuration for the agent. + :vartype agent_endpoint: ~azure.ai.voiceagents.models.AgentEndpointConfig + :ivar instance_identity: The instance identity of the agent. + :vartype instance_identity: ~azure.ai.voiceagents.models.AgentIdentity + :ivar blueprint: The blueprint for the agent. + :vartype blueprint: ~azure.ai.voiceagents.models.AgentIdentity + :ivar blueprint_reference: The blueprint for the agent. + :vartype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :ivar agent_card: + :vartype agent_card: ~azure.ai.voiceagents.models.AgentCard + :ivar versions: The latest version of the voice agent. Required. + :vartype versions: ~azure.ai.voiceagents.models.VoiceAgentObjectVersions + """ + + object: Literal[AgentObjectType.AGENT] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The object type, which is always 'agent'. Required. AGENT.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the agent. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Required.""" + state: Union[str, "_models.AgentState"] = rest_field(visibility=["read"]) + """The operational state of the agent. Controls whether the agent endpoint accepts or rejects + requests. Required. Known values are: \"enabled\" and \"disabled\".""" + agent_endpoint: Optional["_models.AgentEndpointConfig"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The endpoint configuration for the agent.""" + instance_identity: Optional["_models.AgentIdentity"] = rest_field(visibility=["read"]) + """The instance identity of the agent.""" + blueprint: Optional["_models.AgentIdentity"] = rest_field(visibility=["read"]) + """The blueprint for the agent.""" + blueprint_reference: Optional["_models.AgentBlueprintReference"] = rest_field(visibility=["read"]) + """The blueprint for the agent.""" + agent_card: Optional["_models.AgentCard"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + versions: "_models.VoiceAgentObjectVersions" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The latest version of the voice agent. Required.""" + + @overload + def __init__( + self, + *, + object: Literal[AgentObjectType.AGENT], + id: str, # pylint: disable=redefined-builtin + name: str, + versions: "_models.VoiceAgentObjectVersions", + agent_endpoint: Optional["_models.AgentEndpointConfig"] = None, + agent_card: Optional["_models.AgentCard"] = 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 VoiceAgentObjectVersions(_Model): + """VoiceAgentObjectVersions. + + :ivar latest: Required. + :vartype latest: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + """ + + latest: "_models.VoiceAgentVersionObject" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Required.""" + + @overload + def __init__( + self, + *, + latest: "_models.VoiceAgentVersionObject", + ) -> 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 VoiceAgentVersionObject(_Model): + """A voice agent version. Mirrors ``AgentVersionObject``, but its ``definition`` is always a + ``VoiceAgentDefinition``. + + :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. Required. + :vartype metadata: dict[str, str] + :ivar object: The object type, which is always 'agent.version'. Required. AGENT_VERSION. + :vartype object: str or ~azure.ai.voiceagents.models.AGENT_VERSION + :ivar id: The unique identifier of the agent version. Required. + :vartype id: str + :ivar name: The name of the agent. Name can be used to retrieve/update/delete the agent. + Required. + :vartype name: str + :ivar version: The version identifier of the agent. Agents are immutable and every update + creates a new version while keeping the name same. Required. + :vartype version: str + :ivar description: A human-readable description of the agent. + :vartype description: str + :ivar created_at: The Unix timestamp (seconds) when the agent was created. Required. + :vartype created_at: ~datetime.datetime + :ivar draft: Whether this agent version is a draft (candidate) rather than a release. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Defaults to false. + :vartype draft: bool + :ivar status: The provisioning status of the agent version. Defaults to 'active' for non-hosted + agents. For hosted agents, reflects infrastructure readiness. Known values are: "creating", + "active", "failed", "deleting", and "deleted". + :vartype status: str or ~azure.ai.voiceagents.models.AgentVersionStatus + :ivar instance_identity: The instance identity of the agent. + :vartype instance_identity: ~azure.ai.voiceagents.models.AgentIdentity + :ivar blueprint: The blueprint for the agent. + :vartype blueprint: ~azure.ai.voiceagents.models.AgentIdentity + :ivar blueprint_reference: The blueprint for the agent. + :vartype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :ivar agent_guid: The unique GUID identifier of the agent. + :vartype agent_guid: str + :ivar definition: The voice agent definition for this version. Required. + :vartype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + """ + + metadata: 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. Required.""" + object: Literal[AgentObjectType.AGENT_VERSION] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type, which is always 'agent.version'. Required. AGENT_VERSION.""" + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique identifier of the agent version. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the agent. Name can be used to retrieve/update/delete the agent. Required.""" + version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The version identifier of the agent. Agents are immutable and every update creates a new + version while keeping the name same. Required.""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A human-readable description of the agent.""" + created_at: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (seconds) when the agent was created. Required.""" + draft: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether this agent version is a draft (candidate) rather than a release. Draft versions are + recorded but excluded from default 'latest' resolution and are not auto-promoted. Defaults to + false.""" + status: Optional[Union[str, "_models.AgentVersionStatus"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The provisioning status of the agent version. Defaults to 'active' for non-hosted agents. For + hosted agents, reflects infrastructure readiness. Known values are: \"creating\", \"active\", + \"failed\", \"deleting\", and \"deleted\".""" + instance_identity: Optional["_models.AgentIdentity"] = rest_field(visibility=["read"]) + """The instance identity of the agent.""" + blueprint: Optional["_models.AgentIdentity"] = rest_field(visibility=["read"]) + """The blueprint for the agent.""" + blueprint_reference: Optional["_models.AgentBlueprintReference"] = rest_field(visibility=["read"]) + """The blueprint for the agent.""" + agent_guid: Optional[str] = rest_field(visibility=["read"]) + """The unique GUID identifier of the agent.""" + definition: "_models.VoiceAgentDefinition" = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The voice agent definition for this version. Required.""" + + @overload + def __init__( + self, + *, + metadata: dict[str, str], + object: Literal[AgentObjectType.AGENT_VERSION], + id: str, # pylint: disable=redefined-builtin + name: str, + version: str, + created_at: datetime.datetime, + definition: "_models.VoiceAgentDefinition", + description: Optional[str] = None, + draft: Optional[bool] = None, + status: Optional[Union[str, "_models.AgentVersionStatus"]] = 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 VoiceAssistantMessageItem(RealtimeConversationItemMessageAssistant): + """An assistant message item. Only ``output_text`` and ``output_audio`` content are valid for + assistant messages. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``assistant``. Required. ASSISTANT. + :vartype role: str or ~azure.ai.voiceagents.models.ASSISTANT + :ivar content: The content of the message. Required. + :vartype content: + list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistantContent] + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageAssistantContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceAudioConfig(_Model): + """The audio configuration for a voice agent. These values are session defaults and may be + overridden when connecting. + + :ivar input: Input (microphone) audio configuration. + :vartype input: ~azure.ai.voiceagents.models.VoiceAudioInputConfig + :ivar output: Output (agent speech) audio configuration. + :vartype output: ~azure.ai.voiceagents.models.VoiceAudioOutputConfig + """ + + input: Optional["_models.VoiceAudioInputConfig"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Input (microphone) audio configuration.""" + output: Optional["_models.VoiceAudioOutputConfig"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Output (agent speech) audio configuration.""" + + @overload + def __init__( + self, + *, + input: Optional["_models.VoiceAudioInputConfig"] = None, + output: Optional["_models.VoiceAudioOutputConfig"] = 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 VoiceAudioFormat(_Model): + """An audio format. Follows the OpenAI Realtime session schema; ``type`` carries the media + subtype. + + :ivar type: The audio format type, e.g. 'audio/pcm' (16-bit PCM), 'audio/pcmu' (G.711 mu-law), + or 'audio/pcma' (G.711 A-law). Required. Known values are: "audio/pcm", "audio/pcmu", and + "audio/pcma". + :vartype type: str or ~azure.ai.voiceagents.models.VoiceAudioFormatType + :ivar rate: The sample rate in Hz. Applies to 'audio/pcm' (e.g. 24000); omit for telephony + G.711 formats (8 kHz). + :vartype rate: int + """ + + type: Union[str, "_models.VoiceAudioFormatType"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The audio format type, e.g. 'audio/pcm' (16-bit PCM), 'audio/pcmu' (G.711 mu-law), or + 'audio/pcma' (G.711 A-law). Required. Known values are: \"audio/pcm\", \"audio/pcmu\", and + \"audio/pcma\".""" + rate: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The sample rate in Hz. Applies to 'audio/pcm' (e.g. 24000); omit for telephony G.711 formats (8 + kHz).""" + + @overload + def __init__( + self, + *, + type: Union[str, "_models.VoiceAudioFormatType"], + rate: Optional[int] = 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 VoiceAudioInputConfig(_Model): + """Input audio configuration for a voice agent. + + :ivar format: The input audio format. + :vartype format: ~azure.ai.voiceagents.models.VoiceAudioFormat + :ivar noise_reduction: Input noise reduction. Set to null to disable. + :vartype noise_reduction: ~azure.ai.voiceagents.models.VoiceNoiseReduction + :ivar turn_detection: Turn (end-of-speech) detection. Set to null to disable server-side turn + detection. + :vartype turn_detection: ~azure.ai.voiceagents.models.VoiceTurnDetection + :ivar transcription: Optional asynchronous input-audio transcription configuration. + :vartype transcription: ~azure.ai.voiceagents.models.VoiceInputTranscription + """ + + format: Optional["_models.VoiceAudioFormat"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The input audio format.""" + noise_reduction: Optional["_models.VoiceNoiseReduction"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Input noise reduction. Set to null to disable.""" + turn_detection: Optional["_models.VoiceTurnDetection"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Turn (end-of-speech) detection. Set to null to disable server-side turn detection.""" + transcription: Optional["_models.VoiceInputTranscription"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Optional asynchronous input-audio transcription configuration.""" + + @overload + def __init__( + self, + *, + format: Optional["_models.VoiceAudioFormat"] = None, + noise_reduction: Optional["_models.VoiceNoiseReduction"] = None, + turn_detection: Optional["_models.VoiceTurnDetection"] = None, + transcription: Optional["_models.VoiceInputTranscription"] = 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 VoiceAudioOutputConfig(_Model): + """Output audio configuration for a voice agent. + + :ivar format: The output audio format. + :vartype format: ~azure.ai.voiceagents.models.VoiceAudioFormat + :ivar voice: The voice. Either a built-in voice name, such as ``alloy``, or an Azure neural + voice object. Is either a str type or a AzureVoice type. + :vartype voice: str or ~azure.ai.voiceagents.models.AzureVoice + :ivar speed: The speaking speed multiplier, from 0.25 to 1.5. Defaults to 1. + :vartype speed: float + """ + + format: Optional["_models.VoiceAudioFormat"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The output audio format.""" + voice: Optional[Union[str, "_models.AzureVoice"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The voice. Either a built-in voice name, such as ``alloy``, or an Azure neural voice object. Is + either a str type or a AzureVoice type.""" + speed: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The speaking speed multiplier, from 0.25 to 1.5. Defaults to 1.""" + + @overload + def __init__( + self, + *, + format: Optional["_models.VoiceAudioFormat"] = None, + voice: Optional[Union[str, "_models.AzureVoice"]] = None, + speed: Optional[float] = 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 VoiceAvatarConfig(_Model): + """Avatar configuration for a voice agent. These values are session defaults and may be overridden + when connecting. + + :ivar type: The avatar type. Required. Known values are: "video-avatar" and "photo-avatar". + :vartype type: str or ~azure.ai.voiceagents.models.VoiceAvatarType + :ivar character: The avatar character identifier, e.g. 'lisa'. Required. + :vartype character: str + :ivar style: The avatar style, e.g. 'casual-sitting'. + :vartype style: str + :ivar customized: Whether the avatar is a customer-customized avatar. Defaults to false. + :vartype customized: bool + :ivar output_protocol: The transport used to deliver the avatar video stream. Known values are: + "webrtc" and "websocket". + :vartype output_protocol: str or ~azure.ai.voiceagents.models.VoiceAvatarOutputProtocol + """ + + type: Union[str, "_models.VoiceAvatarType"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The avatar type. Required. Known values are: \"video-avatar\" and \"photo-avatar\".""" + character: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The avatar character identifier, e.g. 'lisa'. Required.""" + style: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The avatar style, e.g. 'casual-sitting'.""" + customized: Optional[bool] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Whether the avatar is a customer-customized avatar. Defaults to false.""" + output_protocol: Optional[Union[str, "_models.VoiceAvatarOutputProtocol"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The transport used to deliver the avatar video stream. Known values are: \"webrtc\" and + \"websocket\".""" + + @overload + def __init__( + self, + *, + type: Union[str, "_models.VoiceAvatarType"], + character: str, + style: Optional[str] = None, + customized: Optional[bool] = None, + output_protocol: Optional[Union[str, "_models.VoiceAvatarOutputProtocol"]] = 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 VoiceConversation(_Model): + """A persisted voice conversation. The Foundry envelope that owns a voice agent's stored + transcript, responses, per-turn metrics, and audio. It is the parent, retention, and delete + boundary: deleting it cascades to its responses, items, metrics, and audio. + + :ivar id: The unique id of the conversation. Required. + :vartype id: str + :ivar object: The object type. Always ``voice.conversation``. Required. Default value is + "voice.conversation". + :vartype object: str + :ivar status: The lifecycle status of the conversation. Required. Known values are: + "in_progress" and "completed". + :vartype status: str or ~azure.ai.voiceagents.models.VoiceConversationStatus + :ivar created_at: The Unix timestamp (in seconds) for when the conversation was created. + Required. + :vartype created_at: ~datetime.datetime + :ivar completed_at: The Unix timestamp (in seconds) for when the conversation's session ended. + Absent while in progress. + :vartype completed_at: ~datetime.datetime + :ivar metadata: A set of key-value pairs attached to the conversation. + :vartype metadata: dict[str, str] + :ivar usage: Aggregate token usage totals across all responses in this conversation. + :vartype usage: ~azure.ai.voiceagents.models.RealtimeResponseUsage + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique id of the conversation. Required.""" + object: Literal["voice.conversation"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The object type. Always ``voice.conversation``. Required. Default value is + \"voice.conversation\".""" + status: Union[str, "_models.VoiceConversationStatus"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The lifecycle status of the conversation. Required. Known values are: \"in_progress\" and + \"completed\".""" + created_at: datetime.datetime = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the conversation was created. Required.""" + completed_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the conversation's session ended. Absent while in + progress.""" + metadata: Optional[dict[str, str]] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """A set of key-value pairs attached to the conversation.""" + usage: Optional["_models.RealtimeResponseUsage"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Aggregate token usage totals across all responses in this conversation.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + status: Union[str, "_models.VoiceConversationStatus"], + created_at: datetime.datetime, + completed_at: Optional[datetime.datetime] = None, + metadata: Optional[dict[str, str]] = None, + usage: Optional["_models.RealtimeResponseUsage"] = 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.object: Literal["voice.conversation"] = "voice.conversation" + + +class VoiceDeletedConversation(_Model): + """The response returned when a voice conversation is deleted. + + :ivar id: The id of the deleted conversation. Required. + :vartype id: str + :ivar object: The object type. Always ``voice.conversation.deleted``. Required. Default value + is "voice.conversation.deleted". + :vartype object: str + :ivar deleted: Always ``true`` for a successful delete. Required. + :vartype deleted: bool + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the deleted conversation. Required.""" + object: Literal["voice.conversation.deleted"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The object type. Always ``voice.conversation.deleted``. Required. Default value is + \"voice.conversation.deleted\".""" + deleted: bool = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Always ``true`` for a successful delete. Required.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + deleted: bool, + ) -> 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.object: Literal["voice.conversation.deleted"] = "voice.conversation.deleted" + + +class VoiceFunctionCallItem(RealtimeConversationItemFunctionCall): + """A function call request item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``function_call``. Required. FUNCTION_CALL. + :vartype type: str or ~azure.ai.voiceagents.models.FUNCTION_CALL + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar call_id: The ID of the function call. + :vartype call_id: str + :ivar name: The name of the function being called. Required. + :vartype name: str + :ivar arguments: The arguments of the function call. This is a JSON-encoded string representing + the arguments passed to the function, for example ``{"arg1": "value1", "arg2": 42}``. Required. + :vartype arguments: str + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + name: str, + arguments: str, + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + call_id: Optional[str] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceFunctionCallOutputItem(RealtimeConversationItemFunctionCallOutput): + """A function call output item. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``function_call_output``. Required. + FUNCTION_CALL_OUTPUT. + :vartype type: str or ~azure.ai.voiceagents.models.FUNCTION_CALL_OUTPUT + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar call_id: The ID of the function call this output is for. Required. + :vartype call_id: str + :ivar output: The output of the function call, this is free text and can contain any + information or simply be empty. Required. + :vartype output: str + :ivar name: The name of the function that was called. A Foundry extension: OpenAI's + function_call_output does not carry the function name, only ``call_id``. + :vartype name: str + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + name: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the function that was called. A Foundry extension: OpenAI's function_call_output + does not carry the function name, only ``call_id``.""" + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + call_id: str, + output: str, + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + name: Optional[str] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceInputTranscription(_Model): + """Asynchronous input-audio transcription configuration. + + :ivar model: The transcription model deployment to use. + :vartype model: str + :ivar language: The expected input language as a BCP-47 code (e.g. 'en-US'). Auto-detected when + omitted. + :vartype language: str + :ivar prompt: Optional prompt to bias transcription toward domain terms. + :vartype prompt: str + """ + + model: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The transcription model deployment to use.""" + language: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The expected input language as a BCP-47 code (e.g. 'en-US'). Auto-detected when omitted.""" + prompt: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """Optional prompt to bias transcription toward domain terms.""" + + @overload + def __init__( + self, + *, + model: Optional[str] = None, + language: Optional[str] = None, + prompt: Optional[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 VoiceItemAudioResponse(_Model): + """Metadata for a single conversation item's audio segment. For bring-your-own-storage (BYOS), the + response includes ``blob_path``, a direct customer-storage path or URL without a SAS token, + that the customer accesses with their own credentials. For Foundry-managed storage, + ``blob_path`` is absent and the bytes are streamed through the item's ``/audio/content`` route. + + :ivar conversation_id: The id of the conversation the item belongs to. Required. + :vartype conversation_id: str + :ivar item_id: The id of the item this audio belongs to. Required. + :vartype item_id: str + :ivar role: The role the audio belongs to, e.g. ``user`` or ``agent``. + :vartype role: str + :ivar format: The container format of the audio, e.g. ``wav``. + :vartype format: str + :ivar codec: The audio codec, e.g. ``pcm16``. + :vartype codec: str + :ivar sample_rate: The sample rate in Hz. + :vartype sample_rate: int + :ivar channels: The number of audio channels. + :vartype channels: int + :ivar start_offset_ms: The offset from the session start at which this segment begins, in + milliseconds. + :vartype start_offset_ms: int + :ivar duration_ms: The duration of the audio segment, in milliseconds. + :vartype duration_ms: int + :ivar blob_path: For bring-your-own-storage (BYOS) recordings only: a direct path/URL into the + customer's own storage, without a SAS token — the customer downloads it using their own storage + credentials. Absent for Foundry-managed storage, where the bytes are streamed via the item's + ``/audio/content`` route instead. + :vartype blob_path: str + """ + + conversation_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the conversation the item belongs to. Required.""" + item_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the item this audio belongs to. Required.""" + role: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The role the audio belongs to, e.g. ``user`` or ``agent``.""" + format: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The container format of the audio, e.g. ``wav``.""" + codec: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The audio codec, e.g. ``pcm16``.""" + sample_rate: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The sample rate in Hz.""" + channels: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The number of audio channels.""" + start_offset_ms: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The offset from the session start at which this segment begins, in milliseconds.""" + duration_ms: Optional[int] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The duration of the audio segment, in milliseconds.""" + blob_path: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """For bring-your-own-storage (BYOS) recordings only: a direct path/URL into the customer's own + storage, without a SAS token — the customer downloads it using their own storage credentials. + Absent for Foundry-managed storage, where the bytes are streamed via the item's + ``/audio/content`` route instead.""" + + @overload + def __init__( + self, + *, + conversation_id: str, + item_id: str, + role: Optional[str] = None, + format: Optional[str] = None, + codec: Optional[str] = None, + sample_rate: Optional[int] = None, + channels: Optional[int] = None, + start_offset_ms: Optional[int] = None, + duration_ms: Optional[int] = None, + blob_path: Optional[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 VoiceMcpApprovalRequestItem(RealtimeMCPApprovalRequest): + """An MCP approval request item. + + :ivar type: The type of the item. Always ``mcp_approval_request``. Required. + MCP_APPROVAL_REQUEST. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_APPROVAL_REQUEST + :ivar id: The unique ID of the approval request. Required. + :vartype id: str + :ivar server_label: The label of the MCP server making the request. Required. + :vartype server_label: str + :ivar name: The name of the tool to run. Required. + :vartype name: str + :ivar arguments: A JSON string of arguments for the tool. Required. + :vartype arguments: str + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + server_label: str, + name: str, + arguments: str, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceMcpApprovalResponseItem(RealtimeMCPApprovalResponse): + """An MCP approval response item (client-created). + + :ivar type: The type of the item. Always ``mcp_approval_response``. Required. + MCP_APPROVAL_RESPONSE. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_APPROVAL_RESPONSE + :ivar id: The unique ID of the approval response. Required. + :vartype id: str + :ivar approval_request_id: The ID of the approval request being answered. Required. + :vartype approval_request_id: str + :ivar approve: Whether the request was approved. Required. + :vartype approve: bool + :ivar reason: + :vartype reason: str + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + approval_request_id: str, + approve: bool, + reason: Optional[str] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceMcpCallItem(RealtimeMCPToolCall): + """An MCP call item. + + :ivar type: The type of the item. Always ``mcp_call``. Required. MCP_CALL. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_CALL + :ivar id: The unique ID of the tool call. Required. + :vartype id: str + :ivar server_label: The label of the MCP server running the tool. Required. + :vartype server_label: str + :ivar name: The name of the tool that was run. Required. + :vartype name: str + :ivar arguments: A JSON string of the arguments passed to the tool. Required. + :vartype arguments: str + :ivar approval_request_id: + :vartype approval_request_id: str + :ivar output: + :vartype output: str + :ivar error: + :vartype error: ~azure.ai.voiceagents.models.RealtimeMCPError + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + server_label: str, + name: str, + arguments: str, + approval_request_id: Optional[str] = None, + output: Optional[str] = None, + error: Optional["_models.RealtimeMCPError"] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceMcpListToolsItem(RealtimeMCPListTools): + """An MCP list-tools item. + + :ivar type: The type of the item. Always ``mcp_list_tools``. Required. MCP_LIST_TOOLS. + :vartype type: str or ~azure.ai.voiceagents.models.MCP_LIST_TOOLS + :ivar id: The unique ID of the list. + :vartype id: str + :ivar server_label: The label of the MCP server. Required. + :vartype server_label: str + :ivar tools: The tools available on the server. Required. + :vartype tools: list[~azure.ai.voiceagents.models.MCPListToolsTool] + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + server_label: str, + tools: list["_models.MCPListToolsTool"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceNoiseReduction(_Model): + """Input audio noise reduction configuration. + + :ivar type: The noise reduction mode. Required. Known values are: "near_field", "far_field", + and "azure_deep_noise_suppression". + :vartype type: str or ~azure.ai.voiceagents.models.VoiceNoiseReductionType + """ + + type: Union[str, "_models.VoiceNoiseReductionType"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The noise reduction mode. Required. Known values are: \"near_field\", \"far_field\", and + \"azure_deep_noise_suppression\".""" + + @overload + def __init__( + self, + *, + type: Union[str, "_models.VoiceNoiseReductionType"], + ) -> 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 VoiceRecordingChannelLayout(_Model): + """The role assigned to each channel of a merged stereo voice recording. + + :ivar left: The role carried on the left channel, e.g. ``user``. Required. + :vartype left: str + :ivar right: The role carried on the right channel, e.g. ``agent``. Required. + :vartype right: str + """ + + left: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The role carried on the left channel, e.g. ``user``. Required.""" + right: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The role carried on the right channel, e.g. ``agent``. Required.""" + + @overload + def __init__( + self, + *, + left: str, + right: str, + ) -> 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 VoiceRecordingResponse(_Model): + """Metadata for the merged, whole-call stereo recording of a voice conversation (user audio on the + left channel, agent audio on the right). Built once from the per-turn segments after the + session ends and durably cached. The common metadata (format, sample rate, channels, channel + layout, duration) is returned for both Foundry-managed and bring-your-own-storage (BYOS) + recordings. For BYOS the response also includes ``blob_path``, a direct path/URL into the + customer's own storage (no SAS token) that the customer downloads using their own storage + credentials. For Foundry-managed storage ``blob_path`` is absent and the bytes are streamed via + the ``/audio/content`` route instead. + + :ivar conversation_id: The id of the conversation this recording belongs to. Required. + :vartype conversation_id: str + :ivar format: The container format of the recording, e.g. ``wav``. Required. + :vartype format: str + :ivar sample_rate: The sample rate of the recording in Hz, e.g. 24000. Required. + :vartype sample_rate: int + :ivar channels: The number of audio channels. The merged recording is stereo (``2``). Required. + :vartype channels: int + :ivar channel_layout: The role assigned to each stereo channel. Required. + :vartype channel_layout: ~azure.ai.voiceagents.models.VoiceRecordingChannelLayout + :ivar duration_ms: The total duration of the recording in milliseconds. Required. + :vartype duration_ms: int + :ivar blob_path: For bring-your-own-storage (BYOS) recordings only: a direct path/URL into the + customer's own storage, without a SAS token — the customer downloads it using their own storage + credentials. Absent for Foundry-managed storage, where the bytes are streamed via the + ``/audio/content`` route instead. + :vartype blob_path: str + """ + + conversation_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the conversation this recording belongs to. Required.""" + format: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The container format of the recording, e.g. ``wav``. Required.""" + sample_rate: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The sample rate of the recording in Hz, e.g. 24000. Required.""" + channels: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The number of audio channels. The merged recording is stereo (``2``). Required.""" + channel_layout: "_models.VoiceRecordingChannelLayout" = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The role assigned to each stereo channel. Required.""" + duration_ms: int = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The total duration of the recording in milliseconds. Required.""" + blob_path: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """For bring-your-own-storage (BYOS) recordings only: a direct path/URL into the customer's own + storage, without a SAS token — the customer downloads it using their own storage credentials. + Absent for Foundry-managed storage, where the bytes are streamed via the ``/audio/content`` + route instead.""" + + @overload + def __init__( + self, + *, + conversation_id: str, + format: str, + sample_rate: int, + channels: int, + channel_layout: "_models.VoiceRecordingChannelLayout", + duration_ms: int, + blob_path: Optional[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 VoiceResponse(_Model): + """A persisted voice response representing one model inference turn within a conversation. In list + results the ``output`` projection may be omitted; retrieve the full response (``GET + .../responses/{response_id}``) or the paged response-items route (``GET + .../responses/{response_id}/items``) for its output items. ``created_at``/``completed_at`` are + Foundry durable ordering extensions. + + :ivar id: The unique id of the response. Required. + :vartype id: str + :ivar object: The object type. Always ``realtime.response``. Required. Default value is + "realtime.response". + :vartype object: str + :ivar status: The status of the response. Required. Known values are: "in_progress", + "completed", "cancelled", "incomplete", and "failed". + :vartype status: str or ~azure.ai.voiceagents.models.VoiceResponseStatus + :ivar status_details: Additional detail about a terminal status. + :vartype status_details: ~azure.ai.voiceagents.models.RealtimeResponseStatusDetails + :ivar output: The output items produced by the response. May be omitted in list results; + retrieve the full response (GET .../responses/{response_id}) or use the paged response-items + route (GET .../responses/{response_id}/items) for its output items. Each item's ``response_id`` + also links it back to this response in the conversation-level items list. + :vartype output: list[~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem] + :ivar usage: Token usage statistics for the response. + :vartype usage: ~azure.ai.voiceagents.models.RealtimeResponseUsage + :ivar conversation_id: The id of the conversation this response belongs to. Required. + :vartype conversation_id: str + :ivar audio: The audio configuration used for the response, including the voice and audio + format used for output. + :vartype audio: ~azure.ai.voiceagents.models.VoiceResponseAudio + :ivar output_modalities: The output modalities used for the response, e.g. ``["text", + "audio"]``. Audio output always includes a text transcript. + :vartype output_modalities: list[str or str] + :ivar temperature: The sampling temperature used for the response. + :vartype temperature: float + :ivar max_output_tokens: The maximum number of output tokens allowed for the response; an + integer or the literal ``inf``. Is either a int type or a Literal["inf"] type. + :vartype max_output_tokens: int or str + :ivar created_at: The Unix timestamp (in seconds) for when the response was created. + :vartype created_at: ~datetime.datetime + :ivar completed_at: The Unix timestamp (in seconds) for when the response completed. + :vartype completed_at: ~datetime.datetime + """ + + id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The unique id of the response. Required.""" + object: Literal["realtime.response"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The object type. Always ``realtime.response``. Required. Default value is + \"realtime.response\".""" + status: Union[str, "_models.VoiceResponseStatus"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The status of the response. Required. Known values are: \"in_progress\", \"completed\", + \"cancelled\", \"incomplete\", and \"failed\".""" + status_details: Optional["_models.RealtimeResponseStatusDetails"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Additional detail about a terminal status.""" + output: Optional[list["_unions.VoiceConversationItem"]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The output items produced by the response. May be omitted in list results; retrieve the full + response (GET .../responses/{response_id}) or use the paged response-items route (GET + .../responses/{response_id}/items) for its output items. Each item's ``response_id`` also links + it back to this response in the conversation-level items list.""" + usage: Optional["_models.RealtimeResponseUsage"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """Token usage statistics for the response.""" + conversation_id: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the conversation this response belongs to. Required.""" + audio: Optional["_models.VoiceResponseAudio"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The audio configuration used for the response, including the voice and audio format used for + output.""" + output_modalities: Optional[list[Literal["text", "audio"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The output modalities used for the response, e.g. ``[\"text\", \"audio\"]``. Audio output + always includes a text transcript.""" + temperature: Optional[float] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The sampling temperature used for the response.""" + max_output_tokens: Optional[Union[int, Literal["inf"]]] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The maximum number of output tokens allowed for the response; an integer or the literal + ``inf``. Is either a int type or a Literal[\"inf\"] type.""" + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the response was created.""" + completed_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the response completed.""" + + @overload + def __init__( + self, + *, + id: str, # pylint: disable=redefined-builtin + status: Union[str, "_models.VoiceResponseStatus"], + conversation_id: str, + status_details: Optional["_models.RealtimeResponseStatusDetails"] = None, + output: Optional[list["_unions.VoiceConversationItem"]] = None, + usage: Optional["_models.RealtimeResponseUsage"] = None, + audio: Optional["_models.VoiceResponseAudio"] = None, + output_modalities: Optional[list[Literal["text", "audio"]]] = None, + temperature: Optional[float] = None, + max_output_tokens: Optional[Union[int, Literal["inf"]]] = None, + created_at: Optional[datetime.datetime] = None, + completed_at: Optional[datetime.datetime] = 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.object: Literal["realtime.response"] = "realtime.response" + + +class VoiceResponseAudio(_Model): + """Audio configuration for a response. Follows the OpenAI Realtime GA ``audio`` object shape. + + :ivar output: The audio output configuration used for the response. + :vartype output: ~azure.ai.voiceagents.models.VoiceResponseAudioOutput + """ + + output: Optional["_models.VoiceResponseAudioOutput"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The audio output configuration used for the response.""" + + @overload + def __init__( + self, + *, + output: Optional["_models.VoiceResponseAudioOutput"] = 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 VoiceResponseAudioOutput(_Model): + """The output audio format used for a response. Follows the OpenAI Realtime GA audio format + discriminated union. + + :ivar voice: The voice used for the response's audio output. + :vartype voice: ~azure.ai.voiceagents.models.VoiceResponseVoice + :ivar format: The audio format used for the response's audio output. + :vartype format: ~azure.ai.voiceagents.models.RealtimeAudioFormats + """ + + voice: Optional["_models.VoiceResponseVoice"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The voice used for the response's audio output.""" + format: Optional["_models.RealtimeAudioFormats"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The audio format used for the response's audio output.""" + + @overload + def __init__( + self, + *, + voice: Optional["_models.VoiceResponseVoice"] = None, + format: Optional["_models.RealtimeAudioFormats"] = 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 VoiceResponseVoice(_Model): + """The voice used for a response, for example ``{ "type": "openai", "name": "alloy" }``. + + :ivar type: The voice kind, e.g. ``openai`` or an Azure voice type. Required. + :vartype type: str + :ivar name: The voice name. Required. + :vartype name: str + """ + + type: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The voice kind, e.g. ``openai`` or an Azure voice type. Required.""" + name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The voice name. Required.""" + + @overload + def __init__( + self, + *, + type: str, + name: str, + ) -> 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 VoiceSystemMessageItem(RealtimeConversationItemMessageSystem): + """A system message item. Only ``input_text`` content is valid for system messages. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``system``. Required. SYSTEM. + :vartype role: str or ~azure.ai.voiceagents.models.SYSTEM + :ivar content: The content of the message. Required. + :vartype content: + list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageSystemContent] + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageSystemContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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 VoiceSystemTool(_Model): + """A service-managed control that acts on the active voice session without customer code or + external authentication. + + :ivar type: The type of the tool. Always ``system``. Required. Default value is "system". + :vartype type: str + :ivar name: The service-managed control action. Known values are stable; additional values may + be added over time. Required. "end_conversation" + :vartype name: str or ~azure.ai.voiceagents.models.VoiceSystemToolName + :ivar description: An optional description of the system tool. + :vartype description: str + """ + + type: Literal["system"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The type of the tool. Always ``system``. Required. Default value is \"system\".""" + name: Union[str, "_models.VoiceSystemToolName"] = rest_field( + visibility=["read", "create", "update", "delete", "query"] + ) + """The service-managed control action. Known values are stable; additional values may be added + over time. Required. \"end_conversation\"""" + description: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """An optional description of the system tool.""" + + @overload + def __init__( + self, + *, + name: Union[str, "_models.VoiceSystemToolName"], + description: Optional[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) + self.type: Literal["system"] = "system" + + +class VoiceToolboxTool(_Model): + """A reference to a Foundry toolbox, which is a versioned bundle of tools executed through its MCP + endpoint. + + :ivar type: The type of the tool. Always ``toolbox``. Required. Default value is "toolbox". + :vartype type: str + :ivar toolbox_name: The name of the toolbox to attach. Required. + :vartype toolbox_name: str + :ivar toolbox_version: The immutable version of the toolbox to attach. Required. + :vartype toolbox_version: str + """ + + type: Literal["toolbox"] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The type of the tool. Always ``toolbox``. Required. Default value is \"toolbox\".""" + toolbox_name: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The name of the toolbox to attach. Required.""" + toolbox_version: str = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The immutable version of the toolbox to attach. Required.""" + + @overload + def __init__( + self, + *, + toolbox_name: str, + toolbox_version: str, + ) -> 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: Literal["toolbox"] = "toolbox" + + +class VoiceUserMessageItem(RealtimeConversationItemMessageUser): + """A user message item. ``input_text``, ``input_audio``, and ``input_image`` content are valid for + user messages. + + :ivar id: The unique ID of the item. This may be provided by the client or generated by the + server. + :vartype id: str + :ivar object: Identifier for the API object being returned - always ``realtime.item``. Optional + when creating a new item. Default value is "realtime.item". + :vartype object: str + :ivar type: The type of the item. Always ``message``. Required. Default value is "message". + :vartype type: str + :ivar status: The status of the item. Has no effect on the conversation. Is one of the + following types: Literal["completed"], Literal["incomplete"], Literal["in_progress"] + :vartype status: str or str or str + :ivar role: The role of the message sender. Always ``user``. Required. USER. + :vartype role: str or ~azure.ai.voiceagents.models.USER + :ivar content: The content of the message. Required. + :vartype content: list[~azure.ai.voiceagents.models.RealtimeConversationItemMessageUserContent] + :ivar created_at: The Unix timestamp (in seconds) for when the item was persisted. + :vartype created_at: ~datetime.datetime + :ivar response_id: The id of the response that produced this item, when applicable. + :vartype response_id: str + """ + + created_at: Optional[datetime.datetime] = rest_field( + visibility=["read", "create", "update", "delete", "query"], format="unix-timestamp" + ) + """The Unix timestamp (in seconds) for when the item was persisted.""" + response_id: Optional[str] = rest_field(visibility=["read", "create", "update", "delete", "query"]) + """The id of the response that produced this item, when applicable.""" + + @overload + def __init__( + self, + *, + content: list["_models.RealtimeConversationItemMessageUserContent"], + id: Optional[str] = None, # pylint: disable=redefined-builtin + object: Optional[Literal["realtime.item"]] = None, + status: Optional[Literal["completed", "incomplete", "in_progress"]] = None, + created_at: Optional[datetime.datetime] = None, + response_id: Optional[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) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_patch.py new file mode 100644 index 000000000000..87676c65a8f0 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_patch.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + + +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level + + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/__init__.py new file mode 100644 index 000000000000..0840d3975c41 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/__init__.py @@ -0,0 +1,27 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +# pylint: disable=wrong-import-position + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from ._patch import * # pylint: disable=unused-wildcard-import + +from ._operations import AgentEndpointConversationsOperations # type: ignore +from ._operations import VoiceAgentsOperations # type: ignore + +from ._patch import __all__ as _patch_all +from ._patch import * +from ._patch import patch_sdk as _patch_sdk + +__all__ = [ + "AgentEndpointConversationsOperations", + "VoiceAgentsOperations", +] +__all__.extend([p for p in _patch_all if p not in __all__]) # pyright: ignore +_patch_sdk() diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py new file mode 100644 index 000000000000..8f63643ca4c2 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py @@ -0,0 +1,3586 @@ +# pylint: disable=line-too-long,useless-suppression,too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- +from collections.abc import MutableMapping +from io import IOBase +import json +from typing import Any, Callable, IO, Iterator, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload +import urllib.parse + +from azure.core import PipelineClient +from azure.core.exceptions import ( + ClientAuthenticationError, + HttpResponseError, + ResourceExistsError, + ResourceNotFoundError, + ResourceNotModifiedError, + StreamClosedError, + StreamConsumedError, + map_error, +) +from azure.core.paging import ItemPaged +from azure.core.pipeline import PipelineResponse +from azure.core.rest import HttpRequest, HttpResponse +from azure.core.tracing.decorator import distributed_trace +from azure.core.utils import case_insensitive_dict + +from .. import models as _models, types as _types +from .._configuration import VoiceAgentsClientConfiguration +from .._utils.model_base import SdkJSONEncoder, _deserialize, _failsafe_deserialize +from .._utils.serialization import Deserializer, Serializer +from ..models._enums import AgentDefinitionOptInKeys + +if TYPE_CHECKING: + from .. import _unions +T = TypeVar("T") +ClsType = Optional[Callable[[PipelineResponse[HttpRequest, HttpResponse], T, dict[str, Any]], Any]] +JSON = MutableMapping[str, Any] +_Unset: Any = object() + +_SERIALIZER = Serializer() +_SERIALIZER.client_side_validation = False + + +def build_agent_endpoint_conversations_get_agent_conversation_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_delete_agent_conversation_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_list_agent_conversation_responses_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/responses" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "str") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_response_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/responses/{response_id}" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + "response_id": _SERIALIZER.url("response_id", response_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_list_agent_conversation_response_items_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/responses/{response_id}/items" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + "response_id": _SERIALIZER.url("response_id", response_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "str") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_list_agent_conversation_items_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/items" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "str") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_item_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/items/{item_id}" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + "item_id": _SERIALIZER.url("item_id", item_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_item_audio_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/items/{item_id}/audio" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + "item_id": _SERIALIZER.url("item_id", item_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_item_audio_content_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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", "audio/wav") + + # Construct URL + _url = "/agents/{agent_name}/endpoint/protocols/voice/conversations/{conversation_id}/items/{item_id}/audio/content" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + "item_id": _SERIALIZER.url("item_id", item_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_audio_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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}/endpoint/protocols/voice/conversations/{conversation_id}/audio" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_agent_endpoint_conversations_get_agent_conversation_audio_content_request( # pylint: disable=name-too-long + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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", "audio/wav") + + # Construct URL + _url = "/agents/{agent_name}/endpoint/protocols/voice/conversations/{conversation_id}/audio/content" + path_format_arguments = { + "agent_name": _SERIALIZER.url("agent_name", agent_name, "str"), + "conversation_id": _SERIALIZER.url("conversation_id", conversation_id, "str"), + } + + _url: str = _url.format(**path_format_arguments) # type: ignore + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_create_voice_agent_request( # pylint: disable=name-too-long + *, foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/voice_agents" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_list_voice_agents_request( # pylint: disable=name-too-long + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + **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 = "/voice_agents" + + # Construct parameters + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "str") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_get_voice_agent_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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 = "/voice_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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_update_voice_agent_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/voice_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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_delete_voice_agent_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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 = "/voice_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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_enable_voice_agent_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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")) + # Construct URL + _url = "/voice_agents/{agent_name}:enable" + 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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_disable_voice_agent_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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")) + # Construct URL + _url = "/voice_agents/{agent_name}:disable" + 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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_generate_voice_agent_request( # pylint: disable=name-too-long + *, foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/voice_agents:generate" + + # Construct parameters + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_create_voice_agent_version_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any +) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = case_insensitive_dict(kwargs.pop("params", {}) or {}) + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + api_version: str = kwargs.pop("api_version", _params.pop("api-version", "v1")) + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/voice_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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + if content_type is not None: + _headers["Content-Type"] = _SERIALIZER.header("content_type", content_type, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="POST", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_list_voice_agent_versions_request( # pylint: disable=name-too-long + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + after: Optional[str] = None, + before: Optional[str] = None, + include_drafts: Optional[bool] = None, + **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 = "/voice_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 + if limit is not None: + _params["limit"] = _SERIALIZER.query("limit", limit, "int") + if order is not None: + _params["order"] = _SERIALIZER.query("order", order, "str") + if after is not None: + _params["after"] = _SERIALIZER.query("after", after, "str") + if before is not None: + _params["before"] = _SERIALIZER.query("before", before, "str") + if include_drafts is not None: + _params["include_drafts"] = _SERIALIZER.query("include_drafts", include_drafts, "bool") + _params["api-version"] = _SERIALIZER.query("api_version", api_version, "str") + + # Construct headers + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_get_voice_agent_version_request( # pylint: disable=name-too-long + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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 = "/voice_agents/{agent_name}/versions/{agent_version}" + 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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, params=_params, headers=_headers, **kwargs) + + +def build_voice_agents_delete_voice_agent_version_request( # pylint: disable=name-too-long + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **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 = "/voice_agents/{agent_name}/versions/{agent_version}" + 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 + if foundry_features is not None: + _headers["Foundry-Features"] = _SERIALIZER.header("foundry_features", foundry_features, "str") + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="DELETE", url=_url, params=_params, headers=_headers, **kwargs) + + +class AgentEndpointConversationsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.voiceagents.VoiceAgentsClient`'s + :attr:`agent_endpoint_conversations` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: VoiceAgentsClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + 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") + + @distributed_trace + def get_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceConversation: + """Get a voice agent conversation. + + Retrieves a single conversation recorded for the specified voice agent endpoint by its id. + Returns ``404`` when the conversation was not persisted (``store = false``) or does not exist. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation to retrieve. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceConversation. The VoiceConversation is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceConversation + :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.VoiceConversation] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceConversation, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceDeletedConversation: + """Delete a voice agent conversation. + + Deletes a conversation and all of its stored data — responses, items, and any audio (cascade). + This is the customer's explicit data-deletion control for voice conversations. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation to delete. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceDeletedConversation. The VoiceDeletedConversation is compatible with + MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceDeletedConversation + :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.VoiceDeletedConversation] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_delete_agent_conversation_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceDeletedConversation, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_agent_conversation_responses( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.VoiceResponse"]: + """List responses in a voice agent conversation. + + Returns a paged collection of the responses (model inference turns) recorded for the specified + conversation. The per-response ``output`` projection may be omitted here; use the + response-items route for the canonical paged output. Returns ``404`` when the conversation was + not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose responses are listed. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceResponse + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.voiceagents.models.VoiceResponse] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceResponse]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_responses_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceResponse], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def get_agent_conversation_response( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceResponse: + """Get a voice agent conversation response. + + Retrieves a single response from the specified conversation by its id, including its ``output`` + items, ``usage``, and status. Returns ``404`` when the conversation or response was not + persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the response. Required. + :type conversation_id: str + :param response_id: The id of the response to retrieve. Required. + :type response_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceResponse. The VoiceResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceResponse + :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.VoiceResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_response_request( + agent_name=agent_name, + conversation_id=conversation_id, + response_id=response_id, + foundry_features=foundry_features, + 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", 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.VoiceResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_agent_conversation_response_items( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_unions.VoiceConversationItem"]: + """List items produced by a voice agent conversation response. + + Returns a paged collection of the output items produced by a specific response (the response's + output projection). For the complete ordered conversation history — including user input and + client-created tool outputs — use the conversation items route instead. Returns ``404`` when + the conversation or response was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the response. Required. + :type conversation_id: str + :param response_id: The id of the response whose output items are listed. Required. + :type response_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceSystemMessageItem or VoiceUserMessageItem or + VoiceAssistantMessageItem or VoiceFunctionCallItem or VoiceFunctionCallOutputItem or + VoiceMcpListToolsItem or VoiceMcpCallItem or VoiceMcpApprovalRequestItem or + VoiceMcpApprovalResponseItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list["_unions.VoiceConversationItem"]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_response_items_request( + agent_name=agent_name, + conversation_id=conversation_id, + response_id=response_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list["_unions.VoiceConversationItem"], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def list_agent_conversation_items( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_unions.VoiceConversationItem"]: + """List items in a voice agent conversation. + + Returns a paged collection of items — the complete ordered conversation history, including user + input, assistant output, and client-created tool outputs (transcripts + tool events). Returns + ``404`` when the conversation was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose items are listed. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceSystemMessageItem or VoiceUserMessageItem or + VoiceAssistantMessageItem or VoiceFunctionCallItem or VoiceFunctionCallOutputItem or + VoiceMcpListToolsItem or VoiceMcpCallItem or VoiceMcpApprovalRequestItem or + VoiceMcpApprovalResponseItem + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list["_unions.VoiceConversationItem"]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_agent_endpoint_conversations_list_agent_conversation_items_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list["_unions.VoiceConversationItem"], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def get_agent_conversation_item( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> "_unions.VoiceConversationItem": + """Get a voice agent conversation item. + + Retrieves a single item from the specified conversation by its id, including its transcript. An + ``input_audio``/``output_audio`` content part indicates that audio is available for the item; + the canonical per-item audio metadata is the ``/items/{item_id}/audio`` resource, and the bytes + are streamed by ``/items/{item_id}/audio/content``. Returns ``404`` when the conversation or + item was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item to retrieve. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceSystemMessageItem or VoiceUserMessageItem or VoiceAssistantMessageItem or + VoiceFunctionCallItem or VoiceFunctionCallOutputItem or VoiceMcpListToolsItem or + VoiceMcpCallItem or VoiceMcpApprovalRequestItem or VoiceMcpApprovalResponseItem + :rtype: ~azure.ai.voiceagents.models.VoiceSystemMessageItem or + ~azure.ai.voiceagents.models.VoiceUserMessageItem or + ~azure.ai.voiceagents.models.VoiceAssistantMessageItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallItem or + ~azure.ai.voiceagents.models.VoiceFunctionCallOutputItem or + ~azure.ai.voiceagents.models.VoiceMcpListToolsItem or + ~azure.ai.voiceagents.models.VoiceMcpCallItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem or + ~azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem + :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["_unions.VoiceConversationItem"] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_item_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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", 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("_unions.VoiceConversationItem", response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_agent_conversation_item_audio( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceItemAudioResponse: + """Get a voice agent conversation item's audio metadata. + + Returns metadata for a single conversation item's audio segment, including the common playback + facts (role, format/codec, sample rate, channels, offset, duration) for both Foundry-managed + and bring-your-own-storage (BYOS) recordings; for BYOS the response additionally includes + ``blob_path``, a direct path into the customer's own storage (no SAS) that the customer + downloads with their own credentials. Requires the conversation to have persisted audio + (``store = true``); returns ``404`` when the conversation, item, or its audio was not + persisted. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item whose audio metadata is retrieved. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceItemAudioResponse. The VoiceItemAudioResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceItemAudioResponse + :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.VoiceItemAudioResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_item_audio_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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", 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.VoiceItemAudioResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_agent_conversation_item_audio_content( # pylint: disable=name-too-long + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> Iterator[bytes]: + """Stream a voice agent conversation item's audio. + + Streams a single conversation item's audio as a WAV (``audio/wav``) byte stream through the + service (no SAS URL). This route serves Foundry-managed storage only. For + bring-your-own-storage (BYOS) recordings the bytes are not proxied — the caller must download + directly from customer storage using the ``blob_path`` returned by the item's ``/audio`` + metadata route — so this route returns ``409 Conflict`` for BYOS recordings. Returns ``404`` + when the conversation, item, or its audio was not persisted (``store = false``). + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation that contains the item. Required. + :type conversation_id: str + :param item_id: The id of the conversation item whose audio is streamed. Required. + :type item_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :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_agent_endpoint_conversations_get_agent_conversation_item_audio_content_request( + agent_name=agent_name, + conversation_id=conversation_id, + item_id=item_id, + foundry_features=foundry_features, + 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 get_agent_conversation_audio( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceRecordingResponse: + """Get a voice agent conversation's merged recording metadata. + + Returns metadata for the whole-call merged stereo recording (user audio on the left channel, + agent audio on the right). The common metadata (format, sample rate, channels, channel layout, + duration) is returned for both Foundry-managed and bring-your-own-storage (BYOS) recordings; + for BYOS the response additionally includes ``blob_path``, a direct path into the customer's + own storage (no SAS) that the customer downloads with their own credentials. The recording is + built once from the per-turn segments after the session ends; a request against an in-progress + session returns ``409``. Requires the conversation to have persisted audio (``store = true``); + otherwise returns ``404``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose merged recording metadata is + retrieved. Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceRecordingResponse. The VoiceRecordingResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceRecordingResponse + :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.VoiceRecordingResponse] = kwargs.pop("cls", None) + + _request = build_agent_endpoint_conversations_get_agent_conversation_audio_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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", 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.VoiceRecordingResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def get_agent_conversation_audio_content( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> Iterator[bytes]: + """Stream a voice agent conversation's merged recording. + + Streams the whole-call merged stereo recording as a WAV (``audio/wav``) byte stream through the + service (no SAS URL). This route serves Foundry-managed storage only. For + bring-your-own-storage (BYOS) recordings the bytes are not proxied — the caller must download + directly from customer storage using the ``blob_path`` returned by the metadata route — so this + route returns ``409 Conflict`` for BYOS recordings. A request against an in-progress session + also returns ``409`` (a distinct condition: session-not-ended versus BYOS-download-required). A + conversation without persisted audio (``store = false``) returns ``404``. + + :param agent_name: The name of the agent. Required. + :type agent_name: str + :param conversation_id: The id of the conversation whose merged recording is streamed. + Required. + :type conversation_id: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :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_agent_endpoint_conversations_get_agent_conversation_audio_content_request( + agent_name=agent_name, + conversation_id=conversation_id, + foundry_features=foundry_features, + 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 + + +class VoiceAgentsOperations: + """ + .. warning:: + **DO NOT** instantiate this class directly. + + Instead, you should access the following operations through + :class:`~azure.ai.voiceagents.VoiceAgentsClient`'s + :attr:`voice_agents` attribute. + """ + + def __init__(self, *args, **kwargs) -> None: + input_args = list(args) + self._client: PipelineClient = input_args.pop(0) if input_args else kwargs.pop("client") + self._config: VoiceAgentsClientConfiguration = input_args.pop(0) if input_args else kwargs.pop("config") + 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 create_voice_agent( + self, + *, + name: str, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + state: Optional[Union[str, _models.AgentState]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :keyword 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. + :paramtype name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword state: The initial operational state of the agent. Defaults to 'enabled' if not + specified. Known values are: "enabled" and "disabled". Default value is None. + :paramtype state: str or ~azure.ai.voiceagents.models.AgentState + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :keyword agent_endpoint: An optional endpoint configuration. If not specified, a default + endpoint configuration will be set for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.voiceagents.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.voiceagents.models.AgentCard + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_voice_agent( + self, + body: _types.CreateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Required. + :type body: ~azure.ai.voiceagents.types.CreateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_voice_agent( + self, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_voice_agent( # pylint: disable=too-many-locals + self, + body: Union[JSON, _types.CreateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + name: str = _Unset, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + state: Optional[Union[str, _models.AgentState]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + agent_endpoint: Optional[_models.AgentEndpointConfig] = None, + agent_card: Optional[_models.AgentCard] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Create a voice agent. + + Creates a new voice agent, or a new version of an existing one. + + :param body: Is one of the following types: JSON, CreateVoiceAgentRequest, IO[bytes] Required. + :type body: JSON or ~azure.ai.voiceagents.types.CreateVoiceAgentRequest or IO[bytes] + :keyword 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. + :paramtype name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword state: The initial operational state of the agent. Defaults to 'enabled' if not + specified. Known values are: "enabled" and "disabled". Default value is None. + :paramtype state: str or ~azure.ai.voiceagents.models.AgentState + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :keyword agent_endpoint: An optional endpoint configuration. If not specified, a default + endpoint configuration will be set for the agent. Default value is None. + :paramtype agent_endpoint: ~azure.ai.voiceagents.models.AgentEndpointConfig + :keyword agent_card: Optional agent card for the agent. Default value is None. + :paramtype agent_card: ~azure.ai.voiceagents.models.AgentCard + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if name is _Unset: + raise TypeError("missing required argument: name") + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "agent_card": agent_card, + "agent_endpoint": agent_endpoint, + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "draft": draft, + "metadata": metadata, + "name": name, + "state": state, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_create_voice_agent_request( + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_voice_agents( + self, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + **kwargs: Any + ) -> ItemPaged["_models.VoiceAgentObject"]: + """List voice agents. + + Returns a paged collection of voice agents. + + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :return: An iterator like instance of VoiceAgentObject + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.voiceagents.models.VoiceAgentObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceAgentObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_voice_agents_list_voice_agents_request( + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + 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) + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceAgentObject], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def get_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Get a voice agent. + + Retrieves a voice agent by its unique name. + + :param agent_name: The name of the voice agent to retrieve. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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.VoiceAgentObject] = kwargs.pop("cls", None) + + _request = build_voice_agents_get_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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", 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def update_voice_agent( + self, + agent_name: str, + *, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update_voice_agent( + self, + agent_name: str, + body: _types.UpdateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.voiceagents.types.UpdateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def update_voice_agent( + self, + agent_name: str, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def update_voice_agent( + self, + agent_name: str, + body: Union[JSON, _types.UpdateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Update a voice agent. + + Updates a voice agent by adding a new version if there are any changes to the agent definition. + If no changes, returns the existing agent version. + + :param agent_name: The name of the voice agent to update. Required. + :type agent_name: str + :param body: Is one of the following types: JSON, UpdateVoiceAgentRequest, IO[bytes] Required. + :type body: JSON or ~azure.ai.voiceagents.types.UpdateVoiceAgentRequest or IO[bytes] + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "metadata": metadata, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_update_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.DeleteAgentResponse: + """Delete a voice agent. + + Deletes a voice agent and all of its versions. + + :param agent_name: The name of the voice agent to delete. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: DeleteAgentResponse. The DeleteAgentResponse is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.DeleteAgentResponse + :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.DeleteAgentResponse] = kwargs.pop("cls", None) + + _request = build_voice_agents_delete_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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", 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.DeleteAgentResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def enable_voice_agent( # pylint: disable=inconsistent-return-statements + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> None: + """Enable a voice agent. + + Enables the specified voice agent, allowing it to accept new requests. This operation is + idempotent — enabling an already-enabled voice agent returns success with no side effects. + + :param agent_name: The name of the voice agent to enable. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: None + :rtype: None + :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[None] = kwargs.pop("cls", None) + + _request = build_voice_agents_enable_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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) + + _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 [204]: + 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 cls: + return cls(pipeline_response, None, {}) # type: ignore + + @distributed_trace + def disable_voice_agent( # pylint: disable=inconsistent-return-statements + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> None: + """Disable a voice agent. + + Disables the specified voice agent, preventing it from accepting new requests. This operation + is idempotent — disabling an already-disabled voice agent returns success with no side effects. + + :param agent_name: The name of the voice agent to disable. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: None + :rtype: None + :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[None] = kwargs.pop("cls", None) + + _request = build_voice_agents_disable_voice_agent_request( + agent_name=agent_name, + foundry_features=foundry_features, + 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) + + _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 [204]: + 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 cls: + return cls(pipeline_response, None, {}) # type: ignore + + @overload + def generate_voice_agent( + self, + *, + name: str, + model_type: Union[str, _models.VoiceModelType], + model: str, + agent_type: Union[str, _models.VoiceAgentType], + use_case: Union[str, _models.VoiceAgentUseCase], + goal: str, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + description: Optional[str] = None, + tools: Optional[list["_unions.VoiceAgentTool"]] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :keyword name: The unique name for the agent to create. Required. + :paramtype name: str + :keyword model_type: How the model backing the generated agent is served: ``managed`` + (service-managed) or ``self_deployed`` (the customer's own deployment). Carried through to the + generated definition, not generated. Known values are: "managed" and "self_deployed". Required. + :paramtype model_type: str or ~azure.ai.voiceagents.models.VoiceModelType + :keyword model: The model paired with ``model_type``: the service-managed model name when + ``managed``, or the customer's Foundry deployment name when ``self_deployed``. Carried through, + not generated. Required. + :paramtype model: str + :keyword agent_type: The persona/tone to steer generation. Known values are: "personal" and + "business". Required. + :paramtype agent_type: str or ~azure.ai.voiceagents.models.VoiceAgentType + :keyword use_case: The scenario-template catalog entry the generator specializes for. Known + values are: "customer_support", "reception", "sales", "travel_assistant", "outreach", + "personal_assistant", "learning", "call_center", and "in_car". Required. + :paramtype use_case: str or ~azure.ai.voiceagents.models.VoiceAgentUseCase + :keyword goal: A natural-language description of what the agent should do; the seed for the + generated ``instructions``. Required. + :paramtype goal: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword description: An optional description for the agent. Generated from ``goal`` when + omitted. Default value is None. + :paramtype description: str + :keyword tools: Optional tools carried through verbatim onto the generated agent (see + ``VoiceAgentTool``). Default value is None. + :paramtype tools: list[~azure.ai.voiceagents.models.FunctionTool or + ~azure.ai.voiceagents.models.MCPTool or ~azure.ai.voiceagents.models.VoiceSystemTool or + ~azure.ai.voiceagents.models.VoiceToolboxTool] + :keyword draft: (Preview) When ``true``, the generated voice agent is created as a draft — an + editable, unpublished version the caller can review and refine before publishing it via the + standard create/version path. The service defaults to ``false`` if a value is not specified by + the caller, in which case the agent is created and published normally. Default value is None. + :paramtype draft: bool + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def generate_voice_agent( + self, + body: _types.GenerateVoiceAgentRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Required. + :type body: ~azure.ai.voiceagents.types.GenerateVoiceAgentRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def generate_voice_agent( + self, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def generate_voice_agent( # pylint: disable=too-many-locals + self, + body: Union[JSON, _types.GenerateVoiceAgentRequest, IO[bytes]] = _Unset, + *, + name: str = _Unset, + model_type: Union[str, _models.VoiceModelType] = _Unset, + model: str = _Unset, + agent_type: Union[str, _models.VoiceAgentType] = _Unset, + use_case: Union[str, _models.VoiceAgentUseCase] = _Unset, + goal: str = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + description: Optional[str] = None, + tools: Optional[list["_unions.VoiceAgentTool"]] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentObject: + """Generate a voice agent. + + Generates and creates a voice agent from high-level inputs plus a natural-language goal. The + operation expands the goal into a full, editable definition, creates the agent through the + standard voice create path, and returns the created ``VoiceAgentObject``. The caller can edit + or override the generated fields afterward through normal versioning. + + :param body: Is one of the following types: JSON, GenerateVoiceAgentRequest, IO[bytes] + Required. + :type body: JSON or ~azure.ai.voiceagents.types.GenerateVoiceAgentRequest or IO[bytes] + :keyword name: The unique name for the agent to create. Required. + :paramtype name: str + :keyword model_type: How the model backing the generated agent is served: ``managed`` + (service-managed) or ``self_deployed`` (the customer's own deployment). Carried through to the + generated definition, not generated. Known values are: "managed" and "self_deployed". Required. + :paramtype model_type: str or ~azure.ai.voiceagents.models.VoiceModelType + :keyword model: The model paired with ``model_type``: the service-managed model name when + ``managed``, or the customer's Foundry deployment name when ``self_deployed``. Carried through, + not generated. Required. + :paramtype model: str + :keyword agent_type: The persona/tone to steer generation. Known values are: "personal" and + "business". Required. + :paramtype agent_type: str or ~azure.ai.voiceagents.models.VoiceAgentType + :keyword use_case: The scenario-template catalog entry the generator specializes for. Known + values are: "customer_support", "reception", "sales", "travel_assistant", "outreach", + "personal_assistant", "learning", "call_center", and "in_car". Required. + :paramtype use_case: str or ~azure.ai.voiceagents.models.VoiceAgentUseCase + :keyword goal: A natural-language description of what the agent should do; the seed for the + generated ``instructions``. Required. + :paramtype goal: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword description: An optional description for the agent. Generated from ``goal`` when + omitted. Default value is None. + :paramtype description: str + :keyword tools: Optional tools carried through verbatim onto the generated agent (see + ``VoiceAgentTool``). Default value is None. + :paramtype tools: list[~azure.ai.voiceagents.models.FunctionTool or + ~azure.ai.voiceagents.models.MCPTool or ~azure.ai.voiceagents.models.VoiceSystemTool or + ~azure.ai.voiceagents.models.VoiceToolboxTool] + :keyword draft: (Preview) When ``true``, the generated voice agent is created as a draft — an + editable, unpublished version the caller can review and refine before publishing it via the + standard create/version path. The service defaults to ``false`` if a value is not specified by + the caller, in which case the agent is created and published normally. Default value is None. + :paramtype draft: bool + :return: VoiceAgentObject. The VoiceAgentObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentObject] = kwargs.pop("cls", None) + + if body is _Unset: + if name is _Unset: + raise TypeError("missing required argument: name") + if model_type is _Unset: + raise TypeError("missing required argument: model_type") + if model is _Unset: + raise TypeError("missing required argument: model") + if agent_type is _Unset: + raise TypeError("missing required argument: agent_type") + if use_case is _Unset: + raise TypeError("missing required argument: use_case") + if goal is _Unset: + raise TypeError("missing required argument: goal") + body = { + "agent_type": agent_type, + "description": description, + "draft": draft, + "goal": goal, + "model": model, + "model_type": model_type, + "name": name, + "tools": tools, + "use_case": use_case, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_generate_voice_agent_request( + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @overload + def create_voice_agent_version( + self, + agent_name: str, + *, + definition: _models.VoiceAgentDefinition, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_voice_agent_version( + self, + agent_name: str, + body: _types.CreateVoiceAgentVersionRequest, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Required. + :type body: ~azure.ai.voiceagents.types.CreateVoiceAgentVersionRequest + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for JSON body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @overload + def create_voice_agent_version( + self, + agent_name: str, + body: IO[bytes], + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + content_type: str = "application/json", + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Required. + :type body: IO[bytes] + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword content_type: Body Parameter content-type. Content type parameter for binary body. + Default value is "application/json". + :paramtype content_type: str + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :raises ~azure.core.exceptions.HttpResponseError: + """ + + @distributed_trace + def create_voice_agent_version( + self, + agent_name: str, + body: Union[JSON, _types.CreateVoiceAgentVersionRequest, IO[bytes]] = _Unset, + *, + definition: _models.VoiceAgentDefinition = _Unset, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + metadata: Optional[dict[str, str]] = None, + description: Optional[str] = None, + blueprint_reference: Optional[_models.AgentBlueprintReference] = None, + draft: Optional[bool] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Create a voice agent version. + + Creates a new version for the specified voice agent and returns the created version resource. + + :param agent_name: The name of the voice agent. Required. + :type agent_name: str + :param body: Is one of the following types: JSON, CreateVoiceAgentVersionRequest, IO[bytes] + Required. + :type body: JSON or ~azure.ai.voiceagents.types.CreateVoiceAgentVersionRequest or IO[bytes] + :keyword definition: The voice agent definition. Required. + :paramtype definition: ~azure.ai.voiceagents.models.VoiceAgentDefinition + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword 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. Default value is None. + :paramtype metadata: dict[str, str] + :keyword description: A human-readable description of the agent. Default value is None. + :paramtype description: str + :keyword blueprint_reference: The blueprint reference for the agent. Default value is None. + :paramtype blueprint_reference: ~azure.ai.voiceagents.models.AgentBlueprintReference + :keyword draft: (Preview) Whether this agent version is a draft (candidate) rather than a + release. The service defaults to ``false`` if a value is not specified by the caller. Draft + versions are recorded but excluded from default 'latest' resolution and are not auto-promoted. + Default value is None. + :paramtype draft: bool + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :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 = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + _params = kwargs.pop("params", {}) or {} + + content_type: Optional[str] = kwargs.pop("content_type", _headers.pop("Content-Type", None)) + cls: ClsType[_models.VoiceAgentVersionObject] = kwargs.pop("cls", None) + + if body is _Unset: + if definition is _Unset: + raise TypeError("missing required argument: definition") + body = { + "blueprint_reference": blueprint_reference, + "definition": definition, + "description": description, + "draft": draft, + "metadata": metadata, + } + body = {k: v for k, v in body.items() if v is not None} + content_type = content_type or "application/json" + _content = None + if isinstance(body, (IOBase, bytes)): + _content = body + else: + _content = json.dumps(body, cls=SdkJSONEncoder, exclude_readonly=True) # type: ignore + + _request = build_voice_agents_create_voice_agent_version_request( + agent_name=agent_name, + foundry_features=foundry_features, + content_type=content_type, + api_version=self._config.api_version, + content=_content, + 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.VoiceAgentVersionObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def list_voice_agent_versions( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + limit: Optional[int] = None, + order: Optional[Union[str, _models.PageOrder]] = None, + before: Optional[str] = None, + include_drafts: Optional[bool] = None, + **kwargs: Any + ) -> ItemPaged["_models.VoiceAgentVersionObject"]: + """List voice agent versions. + + Returns a paged collection of versions for the specified voice agent. + + :param agent_name: The name of the voice agent to retrieve versions for. Required. + :type agent_name: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :keyword limit: A limit on the number of objects to be returned. Limit can range between 1 and + 100, and the + default is 20. Default value is None. + :paramtype limit: int + :keyword order: Sort order by the ``created_at`` timestamp of the objects. ``asc`` for + ascending order and``desc`` + for descending order. Known values are: "asc" and "desc". Default value is None. + :paramtype order: str or ~azure.ai.voiceagents.models.PageOrder + :keyword before: A cursor for use in pagination. ``before`` is an object ID that defines your + place in the list. + For instance, if you make a list request and receive 100 objects, ending with obj_foo, your + subsequent call can include before=obj_foo in order to fetch the previous page of the list. + Default value is None. + :paramtype before: str + :keyword include_drafts: (Preview) Whether to include draft versions in the listing. The + service defaults to ``false`` if a value is not specified by the caller (only non-draft + versions are returned). Default value is None. + :paramtype include_drafts: bool + :return: An iterator like instance of VoiceAgentVersionObject + :rtype: ~azure.core.paging.ItemPaged[~azure.ai.voiceagents.models.VoiceAgentVersionObject] + :raises ~azure.core.exceptions.HttpResponseError: + """ + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[list[_models.VoiceAgentVersionObject]] = kwargs.pop("cls", None) + + error_map: MutableMapping = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + def prepare_request(_continuation_token=None): + + _request = build_voice_agents_list_voice_agent_versions_request( + agent_name=agent_name, + foundry_features=foundry_features, + limit=limit, + order=order, + after=_continuation_token, + before=before, + include_drafts=include_drafts, + 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) + return _request + + def extract_data(pipeline_response): + deserialized = pipeline_response.http_response.json() + list_of_elem = _deserialize( + list[_models.VoiceAgentVersionObject], + deserialized.get("data", []), + ) + if cls: + list_of_elem = cls(list_of_elem) # type: ignore + return deserialized.get("last_id") or None, iter(list_of_elem) + + def get_next(_continuation_token=None): + _request = prepare_request(_continuation_token) + + _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]: + 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) + + return pipeline_response + + return ItemPaged(get_next, extract_data) + + @distributed_trace + def get_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.VoiceAgentVersionObject: + """Get a voice agent version. + + Retrieves the specified version of a voice agent by its agent name and version identifier. + + :param agent_name: The name of the voice agent to retrieve. Required. + :type agent_name: str + :param agent_version: The version of the voice agent to retrieve. Required. + :type agent_version: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: VoiceAgentVersionObject. The VoiceAgentVersionObject is compatible with MutableMapping + :rtype: ~azure.ai.voiceagents.models.VoiceAgentVersionObject + :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.VoiceAgentVersionObject] = kwargs.pop("cls", None) + + _request = build_voice_agents_get_voice_agent_version_request( + agent_name=agent_name, + agent_version=agent_version, + foundry_features=foundry_features, + 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", 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.VoiceAgentVersionObject, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore + + @distributed_trace + def delete_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = None, + **kwargs: Any + ) -> _models.DeleteAgentVersionResponse: + """Delete a voice agent version. + + Deletes a specific version of a voice agent. + + :param agent_name: The name of the voice agent to delete. Required. + :type agent_name: str + :param agent_version: The version of the voice agent to delete. Required. + :type agent_version: str + :keyword foundry_features: A feature flag opt-in required when using preview operations or + modifying persisted preview resources. VOICE_AGENTS_V1_PREVIEW. Default value is None. + :paramtype foundry_features: str or ~azure.ai.voiceagents.models.VOICE_AGENTS_V1_PREVIEW + :return: DeleteAgentVersionResponse. The DeleteAgentVersionResponse is compatible with + MutableMapping + :rtype: ~azure.ai.voiceagents.models.DeleteAgentVersionResponse + :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.DeleteAgentVersionResponse] = kwargs.pop("cls", None) + + _request = build_voice_agents_delete_voice_agent_version_request( + agent_name=agent_name, + agent_version=agent_version, + foundry_features=foundry_features, + 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", 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.DeleteAgentVersionResponse, response.json()) + + if cls: + return cls(pipeline_response, deserialized, {}) # type: ignore + + return deserialized # type: ignore diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_patch.py new file mode 100644 index 000000000000..87676c65a8f0 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_patch.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Customize generated code here. + +Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize +""" + + +__all__: list[str] = [] # Add all objects you want publicly available to users at this package level + + +def patch_sdk(): + """Do not remove from this file. + + `patch_sdk` is a last resort escape hatch that allows you to do customizations + you can't accomplish using the techniques described in + https://aka.ms/azsdk/python/dpcodegen/python/customize + """ diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/py.typed b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/py.typed new file mode 100644 index 000000000000..e5aff4f83af8 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561. \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/types.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/types.py new file mode 100644 index 000000000000..b2a6bf935e12 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/types.py @@ -0,0 +1,1082 @@ +# pylint: disable=too-many-lines +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +from typing import Any, Literal, Optional, TYPE_CHECKING, Union +from typing_extensions import Required, TypedDict + +from .models._enums import ( + AgentBlueprintReferenceType, + AgentEndpointAuthorizationSchemeType, + ToolType, + VersionSelectorType, + VoiceTurnDetectionType, +) + +if TYPE_CHECKING: + from . import _unions + from .models import ( + AgentState, + AzureVoiceType, + VoiceAgentType, + VoiceAgentUseCase, + VoiceAudioFormatType, + VoiceAvatarOutputProtocol, + VoiceAvatarType, + VoiceModelType, + VoiceNoiseReductionType, + VoiceOutputModality, + VoiceSystemToolName, + VoiceTurnDetectionEagerness, + ) + + +class A2AProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the A2A protocol.""" + + +class ActivityProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the activity protocol. + + :ivar enable_m365_public_endpoint: Whether to enable the M365 public endpoint for the activity + protocol. + :vartype enable_m365_public_endpoint: bool + """ + + enable_m365_public_endpoint: bool + """Whether to enable the M365 public endpoint for the activity protocol.""" + + +class AgentCard(TypedDict, total=False): + """AgentCard. + + :ivar version: The version of the agent card. Required. + :vartype version: str + :ivar description: The description of the agent card. + :vartype description: str + :ivar skills: The set of skills that an agent can perform. Required. + :vartype skills: list["AgentCardSkill"] + """ + + version: Required[str] + """The version of the agent card. Required.""" + description: str + """The description of the agent card.""" + skills: Required[list["AgentCardSkill"]] + """The set of skills that an agent can perform. Required.""" + + +class AgentCardSkill(TypedDict, total=False): + """AgentCardSkill. + + :ivar id: a unique identifier for the skill. Required. + :vartype id: str + :ivar name: The name of the skill. Required. + :vartype name: str + :ivar description: A description of the skill. + :vartype description: str + :ivar tags: set of tagwords describing classes of capabilities for the skill. + :vartype tags: list[str] + :ivar examples: A list of example scenarios that the skill can perform. + :vartype examples: list[str] + """ + + id: Required[str] + """a unique identifier for the skill. Required.""" + name: Required[str] + """The name of the skill. Required.""" + description: str + """A description of the skill.""" + tags: list[str] + """set of tagwords describing classes of capabilities for the skill.""" + examples: list[str] + """A list of example scenarios that the skill can perform.""" + + +class AgentEndpointConfig(TypedDict, total=False): + """AgentEndpointConfig. + + :ivar version_selector: The version selector of the agent endpoint determines how traffic is + routed to different versions of the agent. + :vartype version_selector: "VersionSelector" + :ivar protocol_configuration: Per-protocol configuration for the agent endpoint. + :vartype protocol_configuration: "ProtocolConfiguration" + :ivar authorization_schemes: The authorization schemes supported by the agent endpoint. + :vartype authorization_schemes: list["AgentEndpointAuthorizationScheme"] + """ + + version_selector: "VersionSelector" + """The version selector of the agent endpoint determines how traffic is routed to different + versions of the agent.""" + protocol_configuration: "ProtocolConfiguration" + """Per-protocol configuration for the agent endpoint.""" + authorization_schemes: list["AgentEndpointAuthorizationScheme"] + """The authorization schemes supported by the agent endpoint.""" + + +class AzureVoice(TypedDict, total=False): + """An Azure neural voice with optional prosody. Built-in voices are specified as a bare string + instead. + + :ivar type: The Azure voice kind. Required. Known values are: "azure-standard", "azure-custom", + "azure-personal", "avatar-voice-sync", and "azure-realtime-native". + :vartype type: Union[str, "AzureVoiceType"] + :ivar name: The Azure neural voice name, e.g. 'en-US-AvaNeural'. Required. + :vartype name: str + :ivar style: Optional speaking style, e.g. 'cheerful'. + :vartype style: str + :ivar pitch: Optional pitch adjustment, e.g. '+5%'. + :vartype pitch: str + :ivar rate: Optional rate adjustment, e.g. '+10%'. + :vartype rate: str + :ivar locale: Optional BCP-47 locale override, e.g. 'en-US'. + :vartype locale: str + """ + + type: Required[Union[str, "AzureVoiceType"]] + """The Azure voice kind. Required. Known values are: \"azure-standard\", \"azure-custom\", + \"azure-personal\", \"avatar-voice-sync\", and \"azure-realtime-native\".""" + name: Required[str] + """The Azure neural voice name, e.g. 'en-US-AvaNeural'. Required.""" + style: str + """Optional speaking style, e.g. 'cheerful'.""" + pitch: str + """Optional pitch adjustment, e.g. '+5%'.""" + rate: str + """Optional rate adjustment, e.g. '+10%'.""" + locale: str + """Optional BCP-47 locale override, e.g. 'en-US'.""" + + +class BotServiceAuthorizationScheme(TypedDict, total=False): + """BotServiceAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE. + :vartype type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE] + """ + + type: Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE]] + """Required. BOT_SERVICE.""" + + +class BotServiceRbacAuthorizationScheme(TypedDict, total=False): + """BotServiceRbacAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE_RBAC. + :vartype type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC] + """ + + type: Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC]] + """Required. BOT_SERVICE_RBAC.""" + + +class BotServiceTenantAuthorizationScheme(TypedDict, total=False): + """BotServiceTenantAuthorizationScheme. + + :ivar type: Required. BOT_SERVICE_TENANT. + :vartype type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT] + """ + + type: Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT]] + """Required. BOT_SERVICE_TENANT.""" + + +class EntraAuthorizationScheme(TypedDict, total=False): + """EntraAuthorizationScheme. + + :ivar type: Required. ENTRA. + :vartype type: Literal[AgentEndpointAuthorizationSchemeType.ENTRA] + """ + + type: Required[Literal[AgentEndpointAuthorizationSchemeType.ENTRA]] + """Required. ENTRA.""" + + +class FixedRatioVersionSelectionRule(TypedDict, total=False): + """FixedRatioVersionSelectionRule. + + :ivar agent_version: The agent version to route traffic to. Required. + :vartype agent_version: str + :ivar type: Required. FIXED_RATIO. + :vartype type: Literal[VersionSelectorType.FIXED_RATIO] + :ivar traffic_percentage: The percentage of traffic to route to the version. Must be between 0 + and 100. Required. + :vartype traffic_percentage: int + """ + + agent_version: Required[str] + """The agent version to route traffic to. Required.""" + type: Required[Literal[VersionSelectorType.FIXED_RATIO]] + """Required. FIXED_RATIO.""" + traffic_percentage: Required[int] + """The percentage of traffic to route to the version. Must be between 0 and 100. Required.""" + + +class FunctionTool(TypedDict, total=False): + """Function. + + :ivar type: The type of the function tool. Always ``function``. Required. FUNCTION. + :vartype type: Literal[ToolType.FUNCTION] + :ivar name: The name of the function to call. Required. + :vartype name: str + :ivar description: + :vartype description: str + :ivar parameters: Required. + :vartype parameters: dict[str, Any] + :ivar strict: Required. + :vartype strict: bool + :ivar defer_loading: Whether this function is deferred and loaded via tool search. + :vartype defer_loading: bool + """ + + type: Required[Literal[ToolType.FUNCTION]] + """The type of the function tool. Always ``function``. Required. FUNCTION.""" + name: Required[str] + """The name of the function to call. Required.""" + description: Optional[str] + parameters: Required[Optional[dict[str, Any]]] + """Required.""" + strict: Required[Optional[bool]] + """Required.""" + defer_loading: bool + """Whether this function is deferred and loaded via tool search.""" + + +class InvocationsProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the invocations protocol.""" + + +class InvocationsWsProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the WebSocket-based invocations protocol.""" + + +class ManagedAgentIdentityBlueprintReference(TypedDict, total=False): + """ManagedAgentIdentityBlueprintReference. + + :ivar type: Required. MANAGED_AGENT_IDENTITY_BLUEPRINT. + :vartype type: Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT] + :ivar blueprint_id: The ID of the managed blueprint. Required. + :vartype blueprint_id: str + """ + + type: Required[Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT]] + """Required. MANAGED_AGENT_IDENTITY_BLUEPRINT.""" + blueprint_id: Required[str] + """The ID of the managed blueprint. Required.""" + + +class McpProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the MCP protocol.""" + + +class MCPTool(TypedDict, total=False): + """MCP tool. + + :ivar type: The type of the MCP tool. Always ``mcp``. Required. MCP. + :vartype type: Literal[ToolType.MCP] + :ivar server_label: A label for this MCP server, used to identify it in tool calls. Required. + :vartype server_label: str + :ivar server_url: The URL for the MCP server. One of ``server_url``, ``connector_id``, or + ``tunnel_id`` must be provided. + :vartype server_url: str + :ivar connector_id: Identifier for service connectors, like those available in ChatGPT. One of + ``server_url``, ``connector_id``, or ``tunnel_id`` must be provided. Learn more about service + connectors `here `_. Currently supported + ``connector_id`` values are: + + * Dropbox: `connector_dropbox` + * Gmail: `connector_gmail` + * Google Calendar: `connector_googlecalendar` + * Google Drive: `connector_googledrive` + * Microsoft Teams: `connector_microsoftteams` + * Outlook Calendar: `connector_outlookcalendar` + * Outlook Email: `connector_outlookemail` + * SharePoint: `connector_sharepoint`. Is one of the following types: + Literal["connector_dropbox"], Literal["connector_gmail"], Literal["connector_googlecalendar"], + Literal["connector_googledrive"], Literal["connector_microsoftteams"], + Literal["connector_outlookcalendar"], Literal["connector_outlookemail"], + Literal["connector_sharepoint"] + :vartype connector_id: Literal["connector_dropbox", "connector_gmail", + "connector_googlecalendar", "connector_googledrive", "connector_microsoftteams", + "connector_outlookcalendar", "connector_outlookemail", "connector_sharepoint"] + :ivar tunnel_id: The Secure MCP Tunnel ID to use instead of a direct server URL. One of + ``server_url``, ``connector_id``, or ``tunnel_id`` must be provided. + :vartype tunnel_id: str + :ivar authorization: An OAuth access token that can be used with a remote MCP server, either + with a custom MCP server URL or a service connector. Your application must handle the OAuth + authorization flow and provide the token here. + :vartype authorization: str + :ivar server_description: Optional description of the MCP server, used to provide more context. + :vartype server_description: str + :ivar headers: + :vartype headers: dict[str, str] + :ivar allowed_tools: Is either a [str] type or a MCPToolFilter type. + :vartype allowed_tools: Union[list[str], "MCPToolFilter"] + :ivar require_approval: Is one of the following types: MCPToolRequireApproval, + Literal["always"], Literal["never"] + :vartype require_approval: Union["MCPToolRequireApproval", Literal["always"], Literal["never"]] + :ivar defer_loading: Whether this MCP tool is deferred and discovered via tool search. + :vartype defer_loading: bool + :ivar project_connection_id: The connection ID in the project for the MCP server. The + connection stores authentication and other connection details needed to connect to the MCP + server. + :vartype project_connection_id: str + :ivar tool_configs: Deprecated. This property is deprecated and will be removed in a future + version. + :vartype tool_configs: dict[str, "ToolConfig"] + """ + + type: Required[Literal[ToolType.MCP]] + """The type of the MCP tool. Always ``mcp``. Required. MCP.""" + server_label: Required[str] + """A label for this MCP server, used to identify it in tool calls. Required.""" + server_url: str + """The URL for the MCP server. One of ``server_url``, ``connector_id``, or ``tunnel_id`` must be + provided.""" + connector_id: Literal[ + "connector_dropbox", + "connector_gmail", + "connector_googlecalendar", + "connector_googledrive", + "connector_microsoftteams", + "connector_outlookcalendar", + "connector_outlookemail", + "connector_sharepoint", + ] + """Identifier for service connectors, like those available in ChatGPT. One of ``server_url``, + ``connector_id``, or ``tunnel_id`` must be provided. Learn more about service connectors `here + `_. Currently supported ``connector_id`` values are: + + * Dropbox: `connector_dropbox` + * Gmail: `connector_gmail` + * Google Calendar: `connector_googlecalendar` + * Google Drive: `connector_googledrive` + * Microsoft Teams: `connector_microsoftteams` + * Outlook Calendar: `connector_outlookcalendar` + * Outlook Email: `connector_outlookemail` + * SharePoint: `connector_sharepoint`. Is one of the following types: + Literal[\"connector_dropbox\"], Literal[\"connector_gmail\"], + Literal[\"connector_googlecalendar\"], Literal[\"connector_googledrive\"], + Literal[\"connector_microsoftteams\"], Literal[\"connector_outlookcalendar\"], + Literal[\"connector_outlookemail\"], Literal[\"connector_sharepoint\"]""" + tunnel_id: str + """The Secure MCP Tunnel ID to use instead of a direct server URL. One of ``server_url``, + ``connector_id``, or ``tunnel_id`` must be provided.""" + authorization: str + """An OAuth access token that can be used with a remote MCP server, either with a custom MCP + server URL or a service connector. Your application must handle the OAuth authorization flow + and provide the token here.""" + server_description: str + """Optional description of the MCP server, used to provide more context.""" + headers: Optional[dict[str, str]] + allowed_tools: Optional[Union[list[str], "MCPToolFilter"]] + """Is either a [str] type or a MCPToolFilter type.""" + require_approval: Optional[Union["MCPToolRequireApproval", Literal["always"], Literal["never"]]] + """Is one of the following types: MCPToolRequireApproval, Literal[\"always\"], Literal[\"never\"]""" + defer_loading: bool + """Whether this MCP tool is deferred and discovered via tool search.""" + project_connection_id: str + """The connection ID in the project for the MCP server. The connection stores authentication and + other connection details needed to connect to the MCP server.""" + tool_configs: dict[str, "ToolConfig"] + """Deprecated. This property is deprecated and will be removed in a future version.""" + + +class MCPToolFilter(TypedDict, total=False): + """MCP tool filter. + + :ivar tool_names: MCP allowed tools. + :vartype tool_names: list[str] + :ivar read_only: Indicates whether or not a tool modifies data or is read-only. If an MCP + server is `annotated with `readOnlyHint` + `_, + it will match this filter. + :vartype read_only: bool + """ + + tool_names: list[str] + """MCP allowed tools.""" + read_only: bool + """Indicates whether or not a tool modifies data or is read-only. If an MCP server is `annotated + with `readOnlyHint` + `_, + it will match this filter.""" + + +class MCPToolRequireApproval(TypedDict, total=False): + """MCPToolRequireApproval. + + :ivar always: + :vartype always: "MCPToolFilter" + :ivar never: + :vartype never: "MCPToolFilter" + """ + + always: "MCPToolFilter" + never: "MCPToolFilter" + + +class ProtocolConfiguration(TypedDict, total=False): + """Per-protocol configuration for the agent endpoint. + + :ivar activity: Configuration for the activity protocol. + :vartype activity: "ActivityProtocolConfiguration" + :ivar responses: Configuration for the responses protocol. + :vartype responses: "ResponsesProtocolConfiguration" + :ivar a2_a: Configuration for the A2A protocol. + :vartype a2_a: "A2AProtocolConfiguration" + :ivar mcp: Configuration for the MCP protocol. + :vartype mcp: "McpProtocolConfiguration" + :ivar invocations: Configuration for the invocations protocol. + :vartype invocations: "InvocationsProtocolConfiguration" + :ivar invocations_ws: Configuration for the WebSocket-based invocations protocol. + :vartype invocations_ws: "InvocationsWsProtocolConfiguration" + """ + + activity: "ActivityProtocolConfiguration" + """Configuration for the activity protocol.""" + responses: "ResponsesProtocolConfiguration" + """Configuration for the responses protocol.""" + a2a: "A2AProtocolConfiguration" + """Configuration for the A2A protocol.""" + mcp: "McpProtocolConfiguration" + """Configuration for the MCP protocol.""" + invocations: "InvocationsProtocolConfiguration" + """Configuration for the invocations protocol.""" + invocations_ws: "InvocationsWsProtocolConfiguration" + """Configuration for the WebSocket-based invocations protocol.""" + + +class RaiConfig(TypedDict, total=False): + """Configuration for Responsible AI (RAI) content filtering and safety features. + + :ivar rai_policy_name: The name of the RAI policy to apply. Required. + :vartype rai_policy_name: str + """ + + rai_policy_name: Required[str] + """The name of the RAI policy to apply. Required.""" + + +class ResponsesProtocolConfiguration(TypedDict, total=False): + """Configuration specific to the responses protocol.""" + + +class SemanticVadTurnDetection(TypedDict, total=False): + """Semantic (model-based) turn detection. + + :ivar type: Required. SEMANTIC_VAD. + :vartype type: Literal[VoiceTurnDetectionType.SEMANTIC_VAD] + :ivar eagerness: How eagerly the model ends the user's turn. Known values are: "auto", "low", + "medium", and "high". + :vartype eagerness: Union[str, "VoiceTurnDetectionEagerness"] + """ + + type: Required[Literal[VoiceTurnDetectionType.SEMANTIC_VAD]] + """Required. SEMANTIC_VAD.""" + eagerness: Union[str, "VoiceTurnDetectionEagerness"] + """How eagerly the model ends the user's turn. Known values are: \"auto\", \"low\", \"medium\", + and \"high\".""" + + +class ServerVadTurnDetection(TypedDict, total=False): + """Server-side voice activity detection (silence-based). + + :ivar type: Required. SERVER_VAD. + :vartype type: Literal[VoiceTurnDetectionType.SERVER_VAD] + :ivar threshold: Activation threshold (0-1). Higher requires louder audio to trigger. + :vartype threshold: float + :ivar prefix_padding_ms: Audio (ms) to include before detected speech start. + :vartype prefix_padding_ms: int + :ivar silence_duration_ms: Silence (ms) used to detect speech stop. + :vartype silence_duration_ms: int + :ivar create_response: Whether the server automatically creates a response when a turn ends. + Defaults to true. + :vartype create_response: bool + """ + + type: Required[Literal[VoiceTurnDetectionType.SERVER_VAD]] + """Required. SERVER_VAD.""" + threshold: float + """Activation threshold (0-1). Higher requires louder audio to trigger.""" + prefix_padding_ms: int + """Audio (ms) to include before detected speech start.""" + silence_duration_ms: int + """Silence (ms) used to detect speech stop.""" + create_response: bool + """Whether the server automatically creates a response when a turn ends. Defaults to true.""" + + +class StructuredInputDefinition(TypedDict, total=False): + """An structured input that can participate in prompt template substitutions and tool argument + binding. + + :ivar description: A human-readable description of the input. + :vartype description: str + :ivar default_value: The default value for the input if no run-time value is provided. + :vartype default_value: Any + :ivar schema: The JSON schema for the structured input (optional). + :vartype schema: dict[str, Any] + :ivar required: Whether the input property is required when the agent is invoked. The service + defaults to ``false`` if a value is not specified by the caller. + :vartype required: bool + """ + + description: str + """A human-readable description of the input.""" + default_value: Any + """The default value for the input if no run-time value is provided.""" + schema: dict[str, Any] + """The JSON schema for the structured input (optional).""" + required: bool + """Whether the input property is required when the agent is invoked. The service defaults to + ``false`` if a value is not specified by the caller.""" + + +class ToolConfig(TypedDict, total=False): + """Per-tool configuration that controls tool visibility and search behavior. + + :ivar pin: When true, the tool is always included in agent context and visible in + ``tools/list``. When false (default), the tool is hidden from ``tools/list`` and only + discoverable via ``tool_search``. + :vartype pin: bool + :ivar additional_search_text: Additional text indexed for tool_search. Supplements the native + tool description to improve discoverability. Does not alter ``tools/list`` output. + :vartype additional_search_text: str + """ + + pin: bool + """When true, the tool is always included in agent context and visible in ``tools/list``. When + false (default), the tool is hidden from ``tools/list`` and only discoverable via + ``tool_search``.""" + additional_search_text: str + """Additional text indexed for tool_search. Supplements the native tool description to improve + discoverability. Does not alter ``tools/list`` output.""" + + +class VersionSelector(TypedDict, total=False): + """VersionSelector. + + :ivar version_selection_rules: Required. + :vartype version_selection_rules: list["VersionSelectionRule"] + """ + + version_selection_rules: Required[list["VersionSelectionRule"]] + """Required.""" + + +class VoiceAgentDefinition(TypedDict, total=False): + """The voice agent definition. Its configuration (model, instructions, audio, tools, and optional + avatar) drives a managed speech-to-speech experience. The realtime voice session is established + through a separate connect operation that is not defined in this specification. Every create or + update produces a new immutable version. + + :ivar kind: The kind discriminator for a voice agent definition. Always ``voice``. Required. + Default value is "voice". + :vartype kind: Literal["voice"] + :ivar rai_config: Configuration for Responsible AI (RAI) content filtering and safety features. + :vartype rai_config: "RaiConfig" + :ivar model_type: How the model backing this agent is served. Together with ``model``, this + selects the model up front. ``managed`` uses a service-managed model; ``self_deployed`` uses + the customer's own Foundry deployment. This is independent of the architecture (realtime or + cascaded), which the service derives from the selected model. Required. Known values are: + "managed" and "self_deployed". + :vartype model_type: Union[str, "VoiceModelType"] + :ivar model: The model to use for this agent, paired with ``model_type``: the service-managed + model name when ``model_type`` is ``managed``, or the customer's Foundry deployment name when + ``model_type`` is ``self_deployed``. The model must support realtime or cascaded voice. The + service derives the architecture from the selected model. Required. + :vartype model: str + :ivar instructions: A system (or developer) message inserted into the model's context. Supports + template substitution via ``structured_inputs``, rendered per session before the live session + starts. + :vartype instructions: str + :ivar audio: The audio configuration, including input and output formats, voice, turn + detection, noise reduction, and transcription. These values are session defaults; a client may + override supported fields when connecting. + :vartype audio: "VoiceAudioConfig" + :ivar output_modalities: The output modalities the agent produces. Defaults to ``["audio"]``. + ``animation`` and ``avatar`` are available when an avatar is configured. + :vartype output_modalities: list[Union[str, "VoiceOutputModality"]] + :ivar avatar: Optional avatar configuration. These values are session defaults and may be + overridden when connecting. + :vartype avatar: "VoiceAvatarConfig" + :ivar tools: The tools the voice agent may use. Supported tool kinds are ``function`` (executed + by the client), ``mcp``, ``system`` (service-managed session controls), and ``toolbox``. + Server-side tools such as ``web_search``, ``azure_ai_search``, and ``openapi`` are provided + through a toolbox rather than declared directly. + :vartype tools: list["_unions.VoiceAgentTool"] + :ivar structured_inputs: Set of structured inputs that participate in prompt template + substitution, rendered per session before the live session starts. + :vartype structured_inputs: dict[str, "StructuredInputDefinition"] + :ivar store: Whether conversations with this agent are persisted. A single, all-or-nothing + persistence switch that defaults to ``false`` (privacy-safe: off by default). When ``true``, + Foundry persists the full conversation — the transcript/event timeline and raw audio. When + ``false``, nothing is persisted and no conversation is surfaced. There is no separate + audio-logging control; audio is persisted only as part of this switch. Latency/performance + telemetry (e.g. time-to-first-audio, inter-token latency, interruption) is observability-only + (customer trace / App Insights) and is not part of the persisted conversation content. + :vartype store: bool + """ + + kind: Required[Literal["voice"]] + """The kind discriminator for a voice agent definition. Always ``voice``. Required. Default value + is \"voice\".""" + rai_config: "RaiConfig" + """Configuration for Responsible AI (RAI) content filtering and safety features.""" + model_type: Required[Union[str, "VoiceModelType"]] + """How the model backing this agent is served. Together with ``model``, this selects the model up + front. ``managed`` uses a service-managed model; ``self_deployed`` uses the customer's own + Foundry deployment. This is independent of the architecture (realtime or cascaded), which the + service derives from the selected model. Required. Known values are: \"managed\" and + \"self_deployed\".""" + model: Required[str] + """The model to use for this agent, paired with ``model_type``: the service-managed model name + when ``model_type`` is ``managed``, or the customer's Foundry deployment name when + ``model_type`` is ``self_deployed``. The model must support realtime or cascaded voice. The + service derives the architecture from the selected model. Required.""" + instructions: Optional[str] + """A system (or developer) message inserted into the model's context. Supports template + substitution via ``structured_inputs``, rendered per session before the live session starts.""" + audio: "VoiceAudioConfig" + """The audio configuration, including input and output formats, voice, turn detection, noise + reduction, and transcription. These values are session defaults; a client may override + supported fields when connecting.""" + output_modalities: list[Union[str, "VoiceOutputModality"]] + """The output modalities the agent produces. Defaults to ``[\"audio\"]``. ``animation`` and + ``avatar`` are available when an avatar is configured.""" + avatar: "VoiceAvatarConfig" + """Optional avatar configuration. These values are session defaults and may be overridden when + connecting.""" + tools: list["_unions.VoiceAgentTool"] + """The tools the voice agent may use. Supported tool kinds are ``function`` (executed by the + client), ``mcp``, ``system`` (service-managed session controls), and ``toolbox``. Server-side + tools such as ``web_search``, ``azure_ai_search``, and ``openapi`` are provided through a + toolbox rather than declared directly.""" + structured_inputs: dict[str, "StructuredInputDefinition"] + """Set of structured inputs that participate in prompt template substitution, rendered per session + before the live session starts.""" + store: bool + """Whether conversations with this agent are persisted. A single, all-or-nothing persistence + switch that defaults to ``false`` (privacy-safe: off by default). When ``true``, Foundry + persists the full conversation — the transcript/event timeline and raw audio. When ``false``, + nothing is persisted and no conversation is surfaced. There is no separate audio-logging + control; audio is persisted only as part of this switch. Latency/performance telemetry (e.g. + time-to-first-audio, inter-token latency, interruption) is observability-only (customer trace / + App Insights) and is not part of the persisted conversation content.""" + + +class VoiceAudioConfig(TypedDict, total=False): + """The audio configuration for a voice agent. These values are session defaults and may be + overridden when connecting. + + :ivar input: Input (microphone) audio configuration. + :vartype input: "VoiceAudioInputConfig" + :ivar output: Output (agent speech) audio configuration. + :vartype output: "VoiceAudioOutputConfig" + """ + + input: "VoiceAudioInputConfig" + """Input (microphone) audio configuration.""" + output: "VoiceAudioOutputConfig" + """Output (agent speech) audio configuration.""" + + +class VoiceAudioFormat(TypedDict, total=False): + """An audio format. Follows the OpenAI Realtime session schema; ``type`` carries the media + subtype. + + :ivar type: The audio format type, e.g. 'audio/pcm' (16-bit PCM), 'audio/pcmu' (G.711 mu-law), + or 'audio/pcma' (G.711 A-law). Required. Known values are: "audio/pcm", "audio/pcmu", and + "audio/pcma". + :vartype type: Union[str, "VoiceAudioFormatType"] + :ivar rate: The sample rate in Hz. Applies to 'audio/pcm' (e.g. 24000); omit for telephony + G.711 formats (8 kHz). + :vartype rate: int + """ + + type: Required[Union[str, "VoiceAudioFormatType"]] + """The audio format type, e.g. 'audio/pcm' (16-bit PCM), 'audio/pcmu' (G.711 mu-law), or + 'audio/pcma' (G.711 A-law). Required. Known values are: \"audio/pcm\", \"audio/pcmu\", and + \"audio/pcma\".""" + rate: int + """The sample rate in Hz. Applies to 'audio/pcm' (e.g. 24000); omit for telephony G.711 formats (8 + kHz).""" + + +class VoiceAudioInputConfig(TypedDict, total=False): + """Input audio configuration for a voice agent. + + :ivar format: The input audio format. + :vartype format: "VoiceAudioFormat" + :ivar noise_reduction: Input noise reduction. Set to null to disable. + :vartype noise_reduction: "VoiceNoiseReduction" + :ivar turn_detection: Turn (end-of-speech) detection. Set to null to disable server-side turn + detection. + :vartype turn_detection: "VoiceTurnDetection" + :ivar transcription: Optional asynchronous input-audio transcription configuration. + :vartype transcription: "VoiceInputTranscription" + """ + + format: "VoiceAudioFormat" + """The input audio format.""" + noise_reduction: Optional["VoiceNoiseReduction"] + """Input noise reduction. Set to null to disable.""" + turn_detection: Optional["VoiceTurnDetection"] + """Turn (end-of-speech) detection. Set to null to disable server-side turn detection.""" + transcription: "VoiceInputTranscription" + """Optional asynchronous input-audio transcription configuration.""" + + +class VoiceAudioOutputConfig(TypedDict, total=False): + """Output audio configuration for a voice agent. + + :ivar format: The output audio format. + :vartype format: "VoiceAudioFormat" + :ivar voice: The voice. Either a built-in voice name, such as ``alloy``, or an Azure neural + voice object. Is either a str type or a AzureVoice type. + :vartype voice: Union[str, "AzureVoice"] + :ivar speed: The speaking speed multiplier, from 0.25 to 1.5. Defaults to 1. + :vartype speed: float + """ + + format: "VoiceAudioFormat" + """The output audio format.""" + voice: Union[str, "AzureVoice"] + """The voice. Either a built-in voice name, such as ``alloy``, or an Azure neural voice object. Is + either a str type or a AzureVoice type.""" + speed: float + """The speaking speed multiplier, from 0.25 to 1.5. Defaults to 1.""" + + +class VoiceAvatarConfig(TypedDict, total=False): + """Avatar configuration for a voice agent. These values are session defaults and may be overridden + when connecting. + + :ivar type: The avatar type. Required. Known values are: "video-avatar" and "photo-avatar". + :vartype type: Union[str, "VoiceAvatarType"] + :ivar character: The avatar character identifier, e.g. 'lisa'. Required. + :vartype character: str + :ivar style: The avatar style, e.g. 'casual-sitting'. + :vartype style: str + :ivar customized: Whether the avatar is a customer-customized avatar. Defaults to false. + :vartype customized: bool + :ivar output_protocol: The transport used to deliver the avatar video stream. Known values are: + "webrtc" and "websocket". + :vartype output_protocol: Union[str, "VoiceAvatarOutputProtocol"] + """ + + type: Required[Union[str, "VoiceAvatarType"]] + """The avatar type. Required. Known values are: \"video-avatar\" and \"photo-avatar\".""" + character: Required[str] + """The avatar character identifier, e.g. 'lisa'. Required.""" + style: str + """The avatar style, e.g. 'casual-sitting'.""" + customized: bool + """Whether the avatar is a customer-customized avatar. Defaults to false.""" + output_protocol: Union[str, "VoiceAvatarOutputProtocol"] + """The transport used to deliver the avatar video stream. Known values are: \"webrtc\" and + \"websocket\".""" + + +class VoiceInputTranscription(TypedDict, total=False): + """Asynchronous input-audio transcription configuration. + + :ivar model: The transcription model deployment to use. + :vartype model: str + :ivar language: The expected input language as a BCP-47 code (e.g. 'en-US'). Auto-detected when + omitted. + :vartype language: str + :ivar prompt: Optional prompt to bias transcription toward domain terms. + :vartype prompt: str + """ + + model: str + """The transcription model deployment to use.""" + language: str + """The expected input language as a BCP-47 code (e.g. 'en-US'). Auto-detected when omitted.""" + prompt: str + """Optional prompt to bias transcription toward domain terms.""" + + +class VoiceNoiseReduction(TypedDict, total=False): + """Input audio noise reduction configuration. + + :ivar type: The noise reduction mode. Required. Known values are: "near_field", "far_field", + and "azure_deep_noise_suppression". + :vartype type: Union[str, "VoiceNoiseReductionType"] + """ + + type: Required[Union[str, "VoiceNoiseReductionType"]] + """The noise reduction mode. Required. Known values are: \"near_field\", \"far_field\", and + \"azure_deep_noise_suppression\".""" + + +class VoiceSystemTool(TypedDict, total=False): + """A service-managed control that acts on the active voice session without customer code or + external authentication. + + :ivar type: The type of the tool. Always ``system``. Required. Default value is "system". + :vartype type: Literal["system"] + :ivar name: The service-managed control action. Known values are stable; additional values may + be added over time. Required. "end_conversation" + :vartype name: Union[str, "VoiceSystemToolName"] + :ivar description: An optional description of the system tool. + :vartype description: str + """ + + type: Required[Literal["system"]] + """The type of the tool. Always ``system``. Required. Default value is \"system\".""" + name: Required[Union[str, "VoiceSystemToolName"]] + """The service-managed control action. Known values are stable; additional values may be added + over time. Required. \"end_conversation\"""" + description: str + """An optional description of the system tool.""" + + +class VoiceToolboxTool(TypedDict, total=False): + """A reference to a Foundry toolbox, which is a versioned bundle of tools executed through its MCP + endpoint. + + :ivar type: The type of the tool. Always ``toolbox``. Required. Default value is "toolbox". + :vartype type: Literal["toolbox"] + :ivar toolbox_name: The name of the toolbox to attach. Required. + :vartype toolbox_name: str + :ivar toolbox_version: The immutable version of the toolbox to attach. Required. + :vartype toolbox_version: str + """ + + type: Required[Literal["toolbox"]] + """The type of the tool. Always ``toolbox``. Required. Default value is \"toolbox\".""" + toolbox_name: Required[str] + """The name of the toolbox to attach. Required.""" + toolbox_version: Required[str] + """The immutable version of the toolbox to attach. Required.""" + + +class CreateVoiceAgentRequest(TypedDict, total=False): + """CreateVoiceAgentRequest. + + :ivar 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. + :vartype name: str + :ivar state: The initial operational state of the agent. Defaults to 'enabled' if not + specified. Known values are: "enabled" and "disabled". + :vartype state: Union[str, "AgentState"] + :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 description: A human-readable description of the agent. + :vartype description: str + :ivar blueprint_reference: The blueprint reference for the agent. + :vartype blueprint_reference: "AgentBlueprintReference" + :ivar draft: (Preview) Whether this agent version is a draft (candidate) rather than a release. + The service defaults to ``false`` if a value is not specified by the caller. Draft versions are + recorded but excluded from default 'latest' resolution and are not auto-promoted. + :vartype draft: bool + :ivar definition: The voice agent definition. Required. + :vartype definition: "VoiceAgentDefinition" + :ivar agent_endpoint: An optional endpoint configuration. If not specified, a default endpoint + configuration will be set for the agent. + :vartype agent_endpoint: "AgentEndpointConfig" + :ivar agent_card: Optional agent card for the agent. + :vartype agent_card: "AgentCard" + """ + + name: Required[str] + """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.""" + state: Union[str, "AgentState"] + """The initial operational state of the agent. Defaults to 'enabled' if not specified. Known + values are: \"enabled\" and \"disabled\".""" + metadata: dict[str, str] + """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.""" + description: str + """A human-readable description of the agent.""" + blueprint_reference: "AgentBlueprintReference" + """The blueprint reference for the agent.""" + draft: bool + """(Preview) Whether this agent version is a draft (candidate) rather than a release. The service + defaults to ``false`` if a value is not specified by the caller. Draft versions are recorded + but excluded from default 'latest' resolution and are not auto-promoted.""" + definition: Required["VoiceAgentDefinition"] + """The voice agent definition. Required.""" + agent_endpoint: "AgentEndpointConfig" + """An optional endpoint configuration. If not specified, a default endpoint configuration will be + set for the agent.""" + agent_card: "AgentCard" + """Optional agent card for the agent.""" + + +class UpdateVoiceAgentRequest(TypedDict, total=False): + """UpdateVoiceAgentRequest. + + :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 description: A human-readable description of the agent. + :vartype description: str + :ivar blueprint_reference: The blueprint reference for the agent. + :vartype blueprint_reference: "AgentBlueprintReference" + :ivar definition: The voice agent definition. Required. + :vartype definition: "VoiceAgentDefinition" + """ + + metadata: dict[str, str] + """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.""" + description: str + """A human-readable description of the agent.""" + blueprint_reference: "AgentBlueprintReference" + """The blueprint reference for the agent.""" + definition: Required["VoiceAgentDefinition"] + """The voice agent definition. Required.""" + + +class GenerateVoiceAgentRequest(TypedDict, total=False): + """GenerateVoiceAgentRequest. + + :ivar name: The unique name for the agent to create. Required. + :vartype name: str + :ivar model_type: How the model backing the generated agent is served: ``managed`` + (service-managed) or ``self_deployed`` (the customer's own deployment). Carried through to the + generated definition, not generated. Required. Known values are: "managed" and "self_deployed". + :vartype model_type: Union[str, "VoiceModelType"] + :ivar model: The model paired with ``model_type``: the service-managed model name when + ``managed``, or the customer's Foundry deployment name when ``self_deployed``. Carried through, + not generated. Required. + :vartype model: str + :ivar agent_type: The persona/tone to steer generation. Required. Known values are: "personal" + and "business". + :vartype agent_type: Union[str, "VoiceAgentType"] + :ivar use_case: The scenario-template catalog entry the generator specializes for. Required. + Known values are: "customer_support", "reception", "sales", "travel_assistant", "outreach", + "personal_assistant", "learning", "call_center", and "in_car". + :vartype use_case: Union[str, "VoiceAgentUseCase"] + :ivar goal: A natural-language description of what the agent should do; the seed for the + generated ``instructions``. Required. + :vartype goal: str + :ivar description: An optional description for the agent. Generated from ``goal`` when omitted. + :vartype description: str + :ivar tools: Optional tools carried through verbatim onto the generated agent (see + ``VoiceAgentTool``). + :vartype tools: list["_unions.VoiceAgentTool"] + :ivar draft: (Preview) When ``true``, the generated voice agent is created as a draft — an + editable, unpublished version the caller can review and refine before publishing it via the + standard create/version path. The service defaults to ``false`` if a value is not specified by + the caller, in which case the agent is created and published normally. + :vartype draft: bool + """ + + name: Required[str] + """The unique name for the agent to create. Required.""" + model_type: Required[Union[str, "VoiceModelType"]] + """How the model backing the generated agent is served: ``managed`` (service-managed) or + ``self_deployed`` (the customer's own deployment). Carried through to the generated definition, + not generated. Required. Known values are: \"managed\" and \"self_deployed\".""" + model: Required[str] + """The model paired with ``model_type``: the service-managed model name when ``managed``, or the + customer's Foundry deployment name when ``self_deployed``. Carried through, not generated. + Required.""" + agent_type: Required[Union[str, "VoiceAgentType"]] + """The persona/tone to steer generation. Required. Known values are: \"personal\" and + \"business\".""" + use_case: Required[Union[str, "VoiceAgentUseCase"]] + """The scenario-template catalog entry the generator specializes for. Required. Known values are: + \"customer_support\", \"reception\", \"sales\", \"travel_assistant\", \"outreach\", + \"personal_assistant\", \"learning\", \"call_center\", and \"in_car\".""" + goal: Required[str] + """A natural-language description of what the agent should do; the seed for the generated + ``instructions``. Required.""" + description: str + """An optional description for the agent. Generated from ``goal`` when omitted.""" + tools: list["_unions.VoiceAgentTool"] + """Optional tools carried through verbatim onto the generated agent (see ``VoiceAgentTool``).""" + draft: bool + """(Preview) When ``true``, the generated voice agent is created as a draft — an editable, + unpublished version the caller can review and refine before publishing it via the standard + create/version path. The service defaults to ``false`` if a value is not specified by the + caller, in which case the agent is created and published normally.""" + + +class CreateVoiceAgentVersionRequest(TypedDict, total=False): + """CreateVoiceAgentVersionRequest. + + :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 description: A human-readable description of the agent. + :vartype description: str + :ivar blueprint_reference: The blueprint reference for the agent. + :vartype blueprint_reference: "AgentBlueprintReference" + :ivar draft: (Preview) Whether this agent version is a draft (candidate) rather than a release. + The service defaults to ``false`` if a value is not specified by the caller. Draft versions are + recorded but excluded from default 'latest' resolution and are not auto-promoted. + :vartype draft: bool + :ivar definition: The voice agent definition. Required. + :vartype definition: "VoiceAgentDefinition" + """ + + metadata: dict[str, str] + """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.""" + description: str + """A human-readable description of the agent.""" + blueprint_reference: "AgentBlueprintReference" + """The blueprint reference for the agent.""" + draft: bool + """(Preview) Whether this agent version is a draft (candidate) rather than a release. The service + defaults to ``false`` if a value is not specified by the caller. Draft versions are recorded + but excluded from default 'latest' resolution and are not auto-promoted.""" + definition: Required["VoiceAgentDefinition"] + """The voice agent definition. Required.""" + + +AgentBlueprintReference = Union[ManagedAgentIdentityBlueprintReference] +AgentEndpointAuthorizationScheme = Union[ + BotServiceAuthorizationScheme, + BotServiceRbacAuthorizationScheme, + BotServiceTenantAuthorizationScheme, + EntraAuthorizationScheme, +] +VersionSelectionRule = Union[FixedRatioVersionSelectionRule] +Tool = Union[FunctionTool, MCPTool] +VoiceTurnDetection = Union[SemanticVadTurnDetection, ServerVadTurnDetection] diff --git a/sdk/voiceagents/azure-ai-voiceagents/dev_requirements.txt b/sdk/voiceagents/azure-ai-voiceagents/dev_requirements.txt new file mode 100644 index 000000000000..ad0907b03b93 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/dev_requirements.txt @@ -0,0 +1,4 @@ +-e ../../../eng/tools/azure-sdk-tools +../../core/azure-core +../../identity/azure-identity +aiohttp \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml new file mode 100644 index 000000000000..5247f5be1ec5 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml @@ -0,0 +1,61 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# Code generated by Microsoft (R) Python Code Generator. +# Changes may cause incorrect behavior and will be lost if the code is regenerated. +# -------------------------------------------------------------------------- + +[build-system] +requires = ["setuptools>=77.0.3", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "azure-ai-voiceagents" +authors = [ + { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, +] +description = "Microsoft Corporation Azure Ai Voiceagents Client Library for Python" +license = "MIT" +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", +] +requires-python = ">=3.10" +keywords = ["azure", "azure sdk"] + +dependencies = [ + "isodate>=0.6.1", + "azure-core>=1.37.0", + "typing-extensions>=4.6.0", +] +dynamic = [ +"version", "readme" +] + +[project.urls] +repository = "https://github.com/Azure/azure-sdk-for-python" + +[tool.setuptools.dynamic] +version = {attr = "azure.ai.voiceagents._version.VERSION"} +readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} + +[tool.setuptools.packages.find] +exclude = [ + "tests*", + "generated_tests*", + "samples*", + "generated_samples*", + "doc*", + "azure", + "azure.ai", +] + +[tool.setuptools.package-data] +pytyped = ["py.typed"] diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/README.md b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md new file mode 100644 index 000000000000..d7c888e18835 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md @@ -0,0 +1,122 @@ +--- +page_type: sample +languages: + - python +products: + - azure + - azure-ai-foundry +urlFragment: voiceagents-samples +--- + +# Samples for the Azure AI Voice Agents client library for Python + +These code samples show common HTTP-surface scenarios with the +`azure-ai-voiceagents` client library: managing voice agents, working with +agent versions, and reading back persisted conversations (transcript, items, +and audio recordings). + +> [!NOTE] +> These samples cover the **request/response (HTTP)** surface only — +> voice-agent management and read-only conversation/audio playback. The +> **live realtime voice session** (the streaming WebSocket call) is established +> through a separate connect operation that is not part of this client library +> today, so it is intentionally not shown here. + +> [!IMPORTANT] +> Voice agents are a **gated preview**. Every call opts in with the +> `VoiceAgents=V1Preview` feature flag (the samples pass it as `foundry_features`). +> The preview must also be **enabled for your subscription** and **served on your +> project's endpoint/region**. Until then, even a correct, authenticated request +> returns `404 NotFound` — the route simply isn't provisioned for your project +> yet. If you hit this, confirm preview enablement and a supported region with +> your service contact rather than changing the sample code. + +**Manage voice agents** — these run standalone; you only need an endpoint. + +| File | Description | +| ---- | ----------- | +| [sample_create_and_manage_voice_agent.py](sample_create_and_manage_voice_agent.py) | Create (with a voice/audio config and conversation storage enabled), get, list, update, disable/enable, and delete a voice agent. | +| [sample_create_voice_agent_with_tools.py](sample_create_voice_agent_with_tools.py) | Create an agent with tools (`function`, `system`, `mcp`, `toolbox`), input-audio config (turn detection + transcription), and bring-your-own-model (`self_deployed`). | +| [sample_generate_voice_agent.py](sample_generate_voice_agent.py) | Guided authoring: generate and create a voice agent from a persona, use case, and a natural-language goal. | +| [sample_manage_voice_agent_versions.py](sample_manage_voice_agent_versions.py) | Create and list immutable versions of a voice agent, including draft versions. | +| [async_samples/sample_create_and_manage_voice_agent_async.py](async_samples/sample_create_and_manage_voice_agent_async.py) | Async version of the create/manage lifecycle. | + +**Read conversations** — these need an existing agent and a conversation id from +a completed live session (see [Getting a conversation id](#getting-a-conversation-id)). + +| File | Description | +| ---- | ----------- | +| [sample_read_conversation.py](sample_read_conversation.py) | Read a persisted conversation, its responses (and per-response items), and its items (with single get by id). | +| [sample_read_conversation_audio.py](sample_read_conversation_audio.py) | Read the merged whole-call recording and a single turn's audio, streaming each to a WAV file. | + +## Prerequisites + +- Python 3.10 or later. +- An Azure subscription and a Foundry project endpoint. +- The following packages installed: + + ```bash + python -m pip install azure-ai-voiceagents azure-identity + # for the async sample, also install an async transport: + python -m pip install aiohttp + ``` + +## Setup + +The samples read their inputs from environment variables. Only +`AZURE_VOICE_AGENTS_ENDPOINT` is required by every sample; the rest are needed +only by specific samples. + +| Variable | Required by | Description | +| -------- | ----------- | ----------- | +| `AZURE_VOICE_AGENTS_ENDPOINT` | all samples | Foundry project endpoint: `https://.services.ai.azure.com/api/projects/` | +| `AZURE_VOICE_AGENTS_MODEL` | management samples (optional) | Realtime model deployment name. Defaults to `gpt-realtime`. | +| `AZURE_VOICE_AGENTS_MODEL_TYPE` | `sample_create_voice_agent_with_tools.py` (optional) | `managed` (default) for a service-hosted model, or `self_deployed` to bring your own Foundry deployment. | +| `AZURE_VOICE_AGENTS_AGENT_NAME` | `sample_read_conversation*.py` | Name of the voice agent that held the conversation. | +| `AZURE_VOICE_AGENTS_CONVERSATION_ID` | `sample_read_conversation*.py` | Id of a persisted conversation (see below). | + +```bash +# bash +export AZURE_VOICE_AGENTS_ENDPOINT="https://.services.ai.azure.com/api/projects/" +``` + +```powershell +# PowerShell +$env:AZURE_VOICE_AGENTS_ENDPOINT = "https://.services.ai.azure.com/api/projects/" +``` + +The samples authenticate with +[`DefaultAzureCredential`](https://learn.microsoft.com/python/api/azure-identity/azure.identity.defaultazurecredential), +so sign in first (for example, with `az login`) or configure the appropriate +environment variables. Your identity needs access to the Foundry project. + +### Getting a conversation id + +The read samples don't create conversations — this client can only *read* them. +A conversation is created by the **voice orchestrator during a live session**, +and it is persisted only when the agent was created with `store = true` (the +management samples turn this on). During the live session the service emits a +`conversation.created` event whose id you pass as +`AZURE_VOICE_AGENTS_CONVERSATION_ID`. Audio additionally requires the session to +have ended. + +## Running a sample + +```bash +python sample_create_and_manage_voice_agent.py +``` + +## Troubleshooting + +| Symptom | Likely cause and fix | +| ------- | -------------------- | +| `KeyError: 'AZURE_VOICE_AGENTS_...'` | A required environment variable is not set. See the table above. | +| `HttpResponseError` 401 / 403 | Not signed in, or your identity lacks access to the project. Run `az login` and confirm project permissions. | +| `ResourceNotFoundError` / 404 on a **management** call (create, list, generate) | The gated preview isn't enabled for your subscription, or isn't served on your project's endpoint/region yet. The request URL and auth are correct; the route just isn't provisioned. Confirm preview enablement and a supported region with your service contact. | +| `HttpResponseError` 404 on a **read** sample | The conversation was not persisted (agent ran with `store = false`) or the id is wrong. | +| `HttpResponseError` 409 on the audio sample | The session is still in progress, or the agent has no bring-your-own-storage account configured for audio. | +| Model / deployment not found | The `gpt-realtime` default deployment doesn't exist in your project. Set `AZURE_VOICE_AGENTS_MODEL` to a valid realtime deployment name. | + +> [!NOTE] +> The management samples create and delete **real resources** in your project and +> may incur cost. Each sample cleans up the agent it creates. diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py new file mode 100644 index 000000000000..0ccfd0386fe2 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py @@ -0,0 +1,76 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_create_and_manage_voice_agent_async.py + +DESCRIPTION: + This sample demonstrates the voice-agent management lifecycle using the async + client: creating a voice agent, retrieving it, listing the agents in the + project, and deleting it. + +USAGE: + python sample_create_and_manage_voice_agent_async.py + + Set the environment variable before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + + Optional: + 2) AZURE_VOICE_AGENTS_MODEL - the realtime model deployment to use. + Defaults to "gpt-realtime". + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). An async HTTP transport such as aiohttp must + be installed (`pip install aiohttp`). +""" + +import asyncio +import os + +from azure.identity.aio import DefaultAzureCredential + +from azure.ai.voiceagents.aio import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys, VoiceAgentDefinition + + +async def create_and_manage_voice_agent() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") + agent_name = "sample-voice-agent-async" + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + credential = DefaultAzureCredential() + async with VoiceAgentsClient(endpoint=endpoint, credential=credential) as client: + created = await client.voice_agents.create_voice_agent( + name=agent_name, + definition=VoiceAgentDefinition( + model_type="managed", + model=model, + instructions="You are a friendly voice assistant. Keep replies short and natural.", + # Persist conversations so they can be read back later. Defaults to False. + store=True, + ), + description="Created by the azure-ai-voiceagents async sample.", + foundry_features=preview, + ) + print(f"Created voice agent: {created.name}") + + agent = await client.voice_agents.get_voice_agent(agent_name, foundry_features=preview) + print(f"Retrieved voice agent: {agent.name}") + + print("Voice agents in this project:") + async for item in client.voice_agents.list_voice_agents(foundry_features=preview): + print(f" - {item.name}") + + await client.voice_agents.delete_voice_agent(agent_name, foundry_features=preview) + print(f"Deleted voice agent: {agent_name}") + + await credential.close() + + +if __name__ == "__main__": + asyncio.run(create_and_manage_voice_agent()) diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py new file mode 100644 index 000000000000..982ccda9a250 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py @@ -0,0 +1,110 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_create_and_manage_voice_agent.py + +DESCRIPTION: + This sample demonstrates the voice-agent management lifecycle over the HTTP + surface: creating a voice agent (with an audio/voice configuration and + conversation storage enabled), retrieving it, listing the agents in the + project, and deleting it. + +USAGE: + python sample_create_and_manage_voice_agent.py + + Set the environment variable before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + + Optional: + 2) AZURE_VOICE_AGENTS_MODEL - the realtime model deployment to use. + Defaults to "gpt-realtime". + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import ( + AgentDefinitionOptInKeys, + AzureVoice, + VoiceAgentDefinition, + VoiceAudioConfig, + VoiceAudioOutputConfig, + VoiceOutputModality, +) + + +def create_and_manage_voice_agent() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") + agent_name = "sample-voice-agent" + + # Voice agent preview operations require this feature-flag opt-in. + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + definition = VoiceAgentDefinition( + # `managed` uses a service-hosted model; use `self_deployed` with a Foundry + # deployment name to bring your own model. + model_type="managed", + model=model, + instructions="You are a friendly voice assistant. Keep replies short and natural.", + audio=VoiceAudioConfig( + output=VoiceAudioOutputConfig(voice=AzureVoice(type="azure-standard", name="en-US-AvaNeural")), + ), + output_modalities=[VoiceOutputModality.AUDIO], + # Persist conversations so the transcript and audio can be read back later + # (see sample_read_conversation.py). Defaults to False, which stores nothing. + store=True, + ) + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + created = client.voice_agents.create_voice_agent( + name=agent_name, + definition=definition, + description="Created by the azure-ai-voiceagents sample.", + foundry_features=preview, + ) + print(f"Created voice agent: {created.name}") + + agent = client.voice_agents.get_voice_agent(agent_name, foundry_features=preview) + print(f"Retrieved voice agent: {agent.name}") + + print("Voice agents in this project:") + for item in client.voice_agents.list_voice_agents(foundry_features=preview): + print(f" - {item.name}") + + # Update the agent. Each update that changes the definition produces a new version. + updated = client.voice_agents.update_voice_agent( + agent_name, + definition=VoiceAgentDefinition( + model_type="managed", + model=model, + instructions="You are a friendly voice assistant. Always greet the caller warmly.", + store=True, + ), + description="Updated instructions.", + foundry_features=preview, + ) + print(f"Updated voice agent to version: {updated.versions.latest.version}") + + # Disable the agent so its endpoint rejects new requests, then re-enable it. + client.voice_agents.disable_voice_agent(agent_name, foundry_features=preview) + print("Disabled voice agent") + client.voice_agents.enable_voice_agent(agent_name, foundry_features=preview) + print("Enabled voice agent") + + client.voice_agents.delete_voice_agent(agent_name, foundry_features=preview) + print(f"Deleted voice agent: {agent_name}") + + +if __name__ == "__main__": + create_and_manage_voice_agent() diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py new file mode 100644 index 000000000000..9be0959db9a8 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py @@ -0,0 +1,159 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_create_voice_agent_with_tools.py + +DESCRIPTION: + This sample demonstrates the richer parts of a voice agent definition that the + basic create sample leaves out: + + * Input (microphone) audio configuration: audio format, server-side turn + detection (VAD), input-audio transcription, and noise reduction. + * Tools the agent may use during a live session: a client-executed `function` + tool, a service-managed `system` control tool, and (shown as constructed + objects) `mcp` and `toolbox` tools. + * Bring-your-own-model (BYOM): set `model_type="self_deployed"` to point the + agent at your own Foundry model deployment instead of a service-managed model. + + The tools and audio settings are session defaults baked into the agent; the live + realtime session that actually invokes them is established through a separate + connect operation that is not part of this client library. + +USAGE: + python sample_create_voice_agent_with_tools.py + + Set these environment variables before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + 2) AZURE_VOICE_AGENTS_MODEL - optional. The realtime model (managed) or the + Foundry deployment name (BYOM). Defaults to "gpt-realtime". + 3) AZURE_VOICE_AGENTS_MODEL_TYPE - optional. "managed" (default) for a + service-hosted model, or "self_deployed" to bring your own deployment. + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import ( + AgentDefinitionOptInKeys, + AzureVoice, + FunctionTool, + MCPTool, + ServerVadTurnDetection, + VoiceAgentDefinition, + VoiceAudioConfig, + VoiceAudioFormat, + VoiceAudioInputConfig, + VoiceAudioOutputConfig, + VoiceInputTranscription, + VoiceModelType, + VoiceOutputModality, + VoiceSystemTool, + VoiceSystemToolName, + VoiceToolboxTool, +) + + +def create_voice_agent_with_tools() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") + # "managed" runs a service-hosted model; "self_deployed" (BYOM) uses your own + # Foundry deployment named by `model`. The service derives whether the model is + # realtime or cascaded; you don't set that here. + model_type = os.environ.get("AZURE_VOICE_AGENTS_MODEL_TYPE", VoiceModelType.MANAGED) + agent_name = "sample-voice-agent-with-tools" + + # Voice agent preview operations require this feature-flag opt-in. + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + # A client-executed tool: the service forwards the function call to your app, + # and your app returns the result over the live session. + get_weather = FunctionTool( + name="get_weather", + description="Get the current weather for a city.", + parameters={ + "type": "object", + "properties": {"city": {"type": "string", "description": "City name, e.g. Seattle."}}, + "required": ["city"], + }, + strict=True, + ) + + # A service-managed control tool: the platform can end the call on the agent's behalf. + end_call = VoiceSystemTool(name=VoiceSystemToolName.END_CONVERSATION) + + # An MCP tool is executed by the service against a remote MCP server you own. + # It references an external server, so it is constructed here for illustration + # and not attached below. Provide one of server_url, connector_id, or tunnel_id. + example_mcp_tool = MCPTool( + server_label="my-mcp-server", + server_url="https://example.com/mcp", + require_approval="never", + ) + + # A toolbox tool references a versioned Foundry toolbox you have created. It is + # constructed here for illustration; attach it only if the toolbox exists. + example_toolbox_tool = VoiceToolboxTool(toolbox_name="my-toolbox", toolbox_version="1") + + definition = VoiceAgentDefinition( + model_type=model_type, + model=model, + instructions="You are a helpful voice assistant. Use tools when they help answer the caller.", + audio=VoiceAudioConfig( + # Input (microphone) side: 24 kHz PCM, server-side VAD so the agent + # auto-responds when the caller stops speaking, plus input-audio + # transcription so user speech is transcribed. + input=VoiceAudioInputConfig( + format=VoiceAudioFormat(type="audio/pcm", rate=24000), + turn_detection=ServerVadTurnDetection( + threshold=0.5, + prefix_padding_ms=300, + silence_duration_ms=500, + ), + transcription=VoiceInputTranscription(model="whisper-1"), + ), + # Output (agent speech) side: the voice the agent speaks with. Pass an + # AzureVoice for an Azure neural voice, or a plain string such as "alloy" + # for a built-in OpenAI voice (realtime models only): + # output=VoiceAudioOutputConfig(voice="alloy"), + output=VoiceAudioOutputConfig(voice=AzureVoice(type="azure-standard", name="en-US-AvaNeural")), + ), + output_modalities=[VoiceOutputModality.AUDIO], + # Attach the self-contained tools. `example_mcp_tool` and `example_toolbox_tool` + # reference external resources you must own, so they are left out here. + tools=[get_weather, end_call], + store=True, + ) + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + created = client.voice_agents.create_voice_agent( + name=agent_name, + definition=definition, + description="Voice agent with tools and input-audio config (azure-ai-voiceagents sample).", + foundry_features=preview, + ) + print(f"Created voice agent: {created.name} (model_type={model_type}, model={model})") + + agent = client.voice_agents.get_voice_agent(agent_name, foundry_features=preview) + tools = agent.versions.latest.definition.tools or [] + print(f"Configured {len(tools)} tool(s):") + for tool in tools: + # Tools belong to an open union, so on read they surface as mappings + # keyed by their wire fields (``type`` and, for most kinds, ``name``). + print(f" - {tool['type']}: {tool.get('name', '(unnamed)')}") + + client.voice_agents.delete_voice_agent(agent_name, foundry_features=preview) + print(f"Deleted voice agent: {agent_name}") + + +if __name__ == "__main__": + create_voice_agent_with_tools() diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py new file mode 100644 index 000000000000..961c666f6cfc --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_generate_voice_agent.py + +DESCRIPTION: + This sample demonstrates guided authoring: generating and creating a voice + agent from a few high-level inputs plus a natural-language goal. The service + expands the goal into a full, editable definition, creates the agent, and + returns it. Every generated field can be refined afterward through the normal + update/version flow. + +USAGE: + python sample_generate_voice_agent.py + + Set the environment variable before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + + Optional: + 2) AZURE_VOICE_AGENTS_MODEL - the realtime model deployment to use. + Defaults to "gpt-realtime". + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys, VoiceAgentType, VoiceAgentUseCase + + +def generate_voice_agent() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + agent = client.voice_agents.generate_voice_agent( + name="sample-generated-agent", + model_type="managed", + model=model, + agent_type=VoiceAgentType.BUSINESS, + use_case=VoiceAgentUseCase.CUSTOMER_SUPPORT, + goal="Help callers troubleshoot their internet connection and open a support ticket if needed.", + foundry_features=preview, + ) + print(f"Generated voice agent: {agent.name}") + print(f"Instructions:\n{agent.versions.latest.definition.instructions}") + + client.voice_agents.delete_voice_agent(agent.name, foundry_features=preview) + print(f"Deleted voice agent: {agent.name}") + + +if __name__ == "__main__": + generate_voice_agent() diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py new file mode 100644 index 000000000000..3d82c269d4a5 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py @@ -0,0 +1,100 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_manage_voice_agent_versions.py + +DESCRIPTION: + This sample demonstrates working with voice-agent versions. Voice agents are + immutable: every create or update produces a new version. This sample creates + an agent, adds a new version to it, lists the versions, and reads a single + version back. + +USAGE: + python sample_manage_voice_agent_versions.py + + Set the environment variable before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + + Optional: + 2) AZURE_VOICE_AGENTS_MODEL - the realtime model deployment to use. + Defaults to "gpt-realtime". + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys, VoiceAgentDefinition + + +def manage_voice_agent_versions() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") + agent_name = "sample-versioned-voice-agent" + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + def definition(instructions: str) -> VoiceAgentDefinition: + # Each version differs only by its instructions; the rest is identical. + return VoiceAgentDefinition(model_type="managed", model=model, instructions=instructions) + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + # Create the initial agent (this is version 1). + created = client.voice_agents.create_voice_agent( + name=agent_name, + definition=definition("You are a helpful voice assistant."), + foundry_features=preview, + ) + print(f"Created agent '{created.name}', latest version: {created.versions.latest.version}") + + # Create a new version with updated instructions. + new_version = client.voice_agents.create_voice_agent_version( + agent_name, + definition=definition("You are a helpful voice assistant. Always greet the caller by name."), + description="Added a personalized greeting.", + foundry_features=preview, + ) + print(f"Created new version: {new_version.version}") + + # Create a draft version. Drafts are recorded but excluded from the default + # 'latest' resolution and from version listings unless include_drafts=True. + draft_version = client.voice_agents.create_voice_agent_version( + agent_name, + definition=definition("You are a helpful voice assistant. Experimental draft persona."), + description="Candidate persona under review.", + draft=True, + foundry_features=preview, + ) + print(f"Created draft version: {draft_version.version}") + + # List released versions (drafts excluded by default). + print(f"Released versions of '{agent_name}':") + for version in client.voice_agents.list_voice_agent_versions(agent_name, foundry_features=preview): + print(f" - version {version.version} (created_at={version.created_at})") + + # List including drafts. + print(f"All versions of '{agent_name}' (including drafts):") + for version in client.voice_agents.list_voice_agent_versions( + agent_name, include_drafts=True, foundry_features=preview + ): + print(f" - version {version.version} (draft={version.draft})") + + # Read a single version back. + fetched = client.voice_agents.get_voice_agent_version(agent_name, new_version.version, foundry_features=preview) + print(f"Fetched version {fetched.version}: {fetched.definition.instructions}") + + # Clean up. + client.voice_agents.delete_voice_agent(agent_name, foundry_features=preview) + print(f"Deleted agent: {agent_name}") + + +if __name__ == "__main__": + manage_voice_agent_versions() diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py new file mode 100644 index 000000000000..d753c8f02290 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py @@ -0,0 +1,102 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_read_conversation.py + +DESCRIPTION: + This sample demonstrates reading a persisted voice conversation back over the + read-only conversation API: the conversation envelope, its responses (model + inference turns), and its ordered items (the transcript). Conversations are + created and written by the voice orchestrator during a live session; this + client can only read them, and only when the agent was configured with + `store = true`. + +USAGE: + python sample_read_conversation.py + + Set these environment variables before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + 2) AZURE_VOICE_AGENTS_AGENT_NAME - the name of the voice agent. + 3) AZURE_VOICE_AGENTS_CONVERSATION_ID - the id of a persisted conversation + (captured from the `conversation.created` event during a live session). + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.core.exceptions import HttpResponseError +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys + + +def read_conversation() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + agent_name = os.environ["AZURE_VOICE_AGENTS_AGENT_NAME"] + conversation_id = os.environ["AZURE_VOICE_AGENTS_CONVERSATION_ID"] + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + conversations = client.agent_endpoint_conversations + try: + # The conversation envelope: status, timestamps, aggregate usage. + conversation = conversations.get_agent_conversation(agent_name, conversation_id, foundry_features=preview) + print(f"Conversation {conversation.id}: status={conversation.status}, created_at={conversation.created_at}") + + # The responses (model inference turns) in the conversation. + print("Responses:") + for response in conversations.list_agent_conversation_responses( + agent_name, conversation_id, foundry_features=preview + ): + print(f" - {response.id}: status={response.status}") + + # Read a single response back, with its output and token usage. + detail = conversations.get_agent_conversation_response( + agent_name, conversation_id, response.id, foundry_features=preview + ) + print(f" usage={detail.usage}") + + # The items produced by this specific response. Conversation items + # belong to an open union, so on read they surface as mappings keyed + # by their wire fields (``type``, ``id``, ...). + for response_item in conversations.list_agent_conversation_response_items( + agent_name, conversation_id, response.id, foundry_features=preview + ): + print(f" item {response_item.get('type')} id={response_item.get('id')}") + + # The ordered conversation items — the full transcript (user + assistant + tool events). + print("Items (transcript):") + for item in conversations.list_agent_conversation_items( + agent_name, conversation_id, foundry_features=preview + ): + item_id = item.get("id") + print(f" - {item.get('type')} id={item_id}") + + # Read a single item back by id. + if item_id: + single = conversations.get_agent_conversation_item( + agent_name, conversation_id, item_id, foundry_features=preview + ) + print(f" fetched item id={single.get('id')}") + + # Deleting a conversation removes it and all of its responses, items, and audio. + # This is destructive, so it is shown but not run by default. Uncomment to enable. + # deleted = conversations.delete_agent_conversation( + # agent_name, conversation_id, foundry_features=preview + # ) + # print(f"Deleted conversation {deleted.id}: deleted={deleted.deleted}") + except HttpResponseError as e: + # 404 typically means the conversation was not persisted (agent ran with `store = false`). + print(f"Service responded with an error: {e.status_code} {e.reason}") + + +if __name__ == "__main__": + read_conversation() diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py new file mode 100644 index 000000000000..a51155b9e328 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py @@ -0,0 +1,115 @@ +# coding=utf-8 +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- + +""" +FILE: sample_read_conversation_audio.py + +DESCRIPTION: + This sample demonstrates reading the persisted audio of a voice conversation, + both the merged whole-call recording and a single turn's audio segment. For + each it reads the metadata first, then streams the WAV bytes to a local file. + The merged recording is stereo: the caller on the left channel and the agent + on the right. + + Audio is available only after the session has ended and only when the agent + was configured with `store = true`. For bring-your-own-storage (BYOS) + accounts the metadata carries a `blob_path` instead, and the bytes are read + from your own storage rather than streamed here. + +USAGE: + python sample_read_conversation_audio.py + + Set these environment variables before running the sample: + 1) AZURE_VOICE_AGENTS_ENDPOINT - the Foundry project endpoint, in the form + https://.services.ai.azure.com/api/projects/ + 2) AZURE_VOICE_AGENTS_AGENT_NAME - the name of the voice agent. + 3) AZURE_VOICE_AGENTS_CONVERSATION_ID - the id of a persisted conversation. + + The sample authenticates with DefaultAzureCredential, so sign in first + (for example, with `az login`). +""" + +import os + +from azure.core.exceptions import HttpResponseError +from azure.identity import DefaultAzureCredential + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys + + +def read_conversation_audio() -> None: + endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] + agent_name = os.environ["AZURE_VOICE_AGENTS_AGENT_NAME"] + conversation_id = os.environ["AZURE_VOICE_AGENTS_CONVERSATION_ID"] + preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + + with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: + conversations = client.agent_endpoint_conversations + try: + read_merged_recording(conversations, agent_name, conversation_id, preview) + read_first_item_audio(conversations, agent_name, conversation_id, preview) + except HttpResponseError as e: + # 404: not persisted / not ready. 409: session still in progress. + print(f"Service responded with an error: {e.status_code} {e.reason}") + + +def stream_to_wav(stream, output_path) -> None: + """Write a streamed audio-content response to a local WAV file.""" + with open(output_path, "wb") as f: + for chunk in stream: + f.write(chunk) + print(f"Wrote {output_path}") + + +def read_merged_recording(conversations, agent_name, conversation_id, preview) -> None: + """Read the merged whole-call stereo recording (left=user, right=agent).""" + recording = conversations.get_agent_conversation_audio(agent_name, conversation_id, foundry_features=preview) + print( + f"Recording: format={recording.format}, sample_rate={recording.sample_rate}, " + f"channels={recording.channels}, duration_ms={recording.duration_ms}" + ) + + if recording.blob_path: + # Bring-your-own-storage: download from your own storage using the returned path. + print(f"Recording is stored in your own storage at: {recording.blob_path}") + return + + # Foundry-managed storage: stream the bytes and write them to a local WAV file. + stream = conversations.get_agent_conversation_audio_content(agent_name, conversation_id, foundry_features=preview) + stream_to_wav(stream, f"{conversation_id}.wav") + + +def read_first_item_audio(conversations, agent_name, conversation_id, preview) -> None: + """Read the audio segment of the first conversation item that has one.""" + for item in conversations.list_agent_conversation_items(agent_name, conversation_id, foundry_features=preview): + item_id = getattr(item, "id", None) + if not item_id: + continue + try: + metadata = conversations.get_agent_conversation_item_audio( + agent_name, conversation_id, item_id, foundry_features=preview + ) + except HttpResponseError: + # This item has no persisted audio (e.g. a text-only turn); try the next one. + continue + + print(f"Item {item_id}: role={metadata.role}, duration_ms={metadata.duration_ms}") + if metadata.blob_path: + print(f"Item audio is stored in your own storage at: {metadata.blob_path}") + return + + stream = conversations.get_agent_conversation_item_audio_content( + agent_name, conversation_id, item_id, foundry_features=preview + ) + stream_to_wav(stream, f"{conversation_id}_{item_id}.wav") + return + + print("No conversation item with audio was found.") + + +if __name__ == "__main__": + read_conversation_audio() diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/tests/__init__.py new file mode 100644 index 000000000000..bb9094db921e --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/__init__.py @@ -0,0 +1,4 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/conftest.py b/sdk/voiceagents/azure-ai-voiceagents/tests/conftest.py new file mode 100644 index 000000000000..5300f22777a9 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/conftest.py @@ -0,0 +1,9 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Pytest configuration for the azure-ai-voiceagents SDK tests. + +Only offline unit tests are shipped for this package; there are no live or +playback tests, so no test-proxy or recorded assets are required. +""" diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/__init__.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/__init__.py new file mode 100644 index 000000000000..bb9094db921e --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/__init__.py @@ -0,0 +1,4 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_client.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_client.py new file mode 100644 index 000000000000..80288cc3cd64 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_client.py @@ -0,0 +1,73 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Unit tests for client construction and configuration defaults. + +These tests never issue network calls; they only build the client objects and +inspect their configuration. +""" + +import time + +from azure.core.credentials import AccessToken + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.aio import VoiceAgentsClient as AsyncVoiceAgentsClient + +ENDPOINT = "https://example.services.ai.azure.com/api/projects/my-project" + + +class FakeCredential: + """Minimal synchronous TokenCredential stand-in (never actually called).""" + + def get_token(self, *scopes, **kwargs): + return AccessToken("fake-token", int(time.time()) + 3600) + + +class FakeAsyncCredential: + """Minimal asynchronous TokenCredential stand-in (never actually called).""" + + async def get_token(self, *scopes, **kwargs): + return AccessToken("fake-token", int(time.time()) + 3600) + + async def close(self): + return None + + +def test_sync_client_default_api_version(): + client = VoiceAgentsClient(ENDPOINT, FakeCredential()) + assert client._config.api_version == "v1" + + +def test_sync_client_default_credential_scopes(): + client = VoiceAgentsClient(ENDPOINT, FakeCredential()) + assert client._config.credential_scopes == ["https://ai.azure.com/.default"] + + +def test_sync_client_endpoint_preserved(): + client = VoiceAgentsClient(ENDPOINT, FakeCredential()) + assert client._config.endpoint == ENDPOINT + + +def test_sync_client_api_version_override(): + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), api_version="v1") + assert client._config.api_version == "v1" + + +def test_sync_client_exposes_operation_groups(): + client = VoiceAgentsClient(ENDPOINT, FakeCredential()) + assert client.voice_agents is not None + assert client.agent_endpoint_conversations is not None + + +def test_async_client_defaults(): + client = AsyncVoiceAgentsClient(ENDPOINT, FakeAsyncCredential()) + assert client._config.api_version == "v1" + assert client._config.credential_scopes == ["https://ai.azure.com/.default"] + + +def test_async_client_exposes_realtime_property(): + client = AsyncVoiceAgentsClient(ENDPOINT, FakeAsyncCredential()) + # The realtime surface is exposed on the async client only. + assert client.realtime is not None diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_enums.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_enums.py new file mode 100644 index 000000000000..e746393d56f3 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_enums.py @@ -0,0 +1,45 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Unit tests for public enum values. + +These guard against accidental changes to the wire values that the service +depends on. They are offline and require no credentials or network. +""" + +from azure.ai.voiceagents.models import ( + AgentDefinitionOptInKeys, + AgentState, + AzureVoiceType, + VoiceModelType, + VoiceOutputModality, + VoiceSystemToolName, +) + + +def test_agent_definition_opt_in_keys_wire_values(): + assert AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW == "VoiceAgents=V1Preview" + + +def test_voice_model_type_wire_values(): + assert VoiceModelType.MANAGED == "managed" + assert VoiceModelType.SELF_DEPLOYED == "self_deployed" + + +def test_azure_voice_type_wire_values(): + assert AzureVoiceType.AZURE_STANDARD == "azure-standard" + + +def test_voice_output_modality_wire_values(): + assert VoiceOutputModality.AUDIO == "audio" + assert VoiceOutputModality.TEXT == "text" + + +def test_voice_system_tool_name_wire_values(): + assert VoiceSystemToolName.END_CONVERSATION == "end_conversation" + + +def test_agent_state_wire_values(): + assert AgentState.ENABLED == "enabled" + assert AgentState.DISABLED == "disabled" diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_models.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_models.py new file mode 100644 index 000000000000..818eb883ae8f --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_models.py @@ -0,0 +1,128 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Unit tests for model construction and serialization round-trips. + +All tests are offline: they build models in memory and assert on the JSON +shape produced by ``as_dict()`` / accepted by mapping-based construction. +""" + +from azure.ai.voiceagents.models import ( + AzureVoice, + FunctionTool, + ServerVadTurnDetection, + VoiceAgentDefinition, + VoiceAudioConfig, + VoiceAudioFormat, + VoiceAudioInputConfig, + VoiceAudioOutputConfig, + VoiceInputTranscription, + VoiceModelType, + VoiceOutputModality, + VoiceSystemTool, + VoiceSystemToolName, +) + + +def test_voice_agent_definition_minimal(): + definition = VoiceAgentDefinition( + model_type=VoiceModelType.MANAGED, + model="gpt-realtime", + instructions="Be helpful.", + ) + + assert definition.model_type == "managed" + assert definition.model == "gpt-realtime" + assert definition.instructions == "Be helpful." + # ``kind`` is the discriminator and is always "voice". + assert definition.kind == "voice" + + +def test_voice_agent_definition_as_dict_round_trip(): + definition = VoiceAgentDefinition( + model_type="managed", + model="gpt-realtime", + instructions="Hello", + output_modalities=[VoiceOutputModality.AUDIO], + store=True, + ) + + data = definition.as_dict() + assert data["model_type"] == "managed" + assert data["model"] == "gpt-realtime" + assert data["store"] is True + assert data["output_modalities"] == ["audio"] + + # Rebuild from the emitted mapping and confirm equality. + rebuilt = VoiceAgentDefinition(data) + assert rebuilt.model == definition.model + assert rebuilt.store is True + assert rebuilt == definition + + +def test_azure_voice_construction(): + voice = AzureVoice(type="azure-standard", name="en-US-AvaNeural") + assert voice.type == "azure-standard" + assert voice.name == "en-US-AvaNeural" + assert voice.as_dict() == {"type": "azure-standard", "name": "en-US-AvaNeural"} + + +def test_output_config_accepts_plain_string_voice(): + # A bare string voice denotes a built-in (OpenAI) voice. + output = VoiceAudioOutputConfig(voice="alloy") + assert output.voice == "alloy" + + +def test_output_config_accepts_azure_voice(): + output = VoiceAudioOutputConfig(voice=AzureVoice(type="azure-standard", name="en-US-AvaNeural")) + assert isinstance(output.voice, AzureVoice) + assert output.voice.name == "en-US-AvaNeural" + + +def test_audio_config_input_round_trip(): + audio = VoiceAudioConfig( + input=VoiceAudioInputConfig( + format=VoiceAudioFormat(type="audio/pcm", rate=24000), + turn_detection=ServerVadTurnDetection(threshold=0.5), + transcription=VoiceInputTranscription(model="whisper-1"), + ) + ) + + data = audio.as_dict() + assert data["input"]["format"]["type"] == "audio/pcm" + assert data["input"]["format"]["rate"] == 24000 + assert data["input"]["transcription"]["model"] == "whisper-1" + + +def test_function_tool_discriminator_is_set(): + tool = FunctionTool( + name="get_weather", + parameters={"type": "object", "properties": {}}, + strict=True, + ) + assert tool.type == "function" + assert tool.name == "get_weather" + assert tool.strict is True + assert tool.as_dict()["type"] == "function" + + +def test_system_tool_discriminator_is_set(): + tool = VoiceSystemTool(name=VoiceSystemToolName.END_CONVERSATION) + assert tool.type == "system" + assert tool.name == "end_conversation" + + +def test_definition_with_tools_serializes_each_tool_type(): + definition = VoiceAgentDefinition( + model_type="managed", + model="gpt-realtime", + instructions="Hi", + tools=[ + FunctionTool(name="get_weather", parameters={"type": "object"}, strict=True), + VoiceSystemTool(name=VoiceSystemToolName.END_CONVERSATION), + ], + ) + + tool_types = [tool["type"] for tool in definition.as_dict()["tools"]] + assert tool_types == ["function", "system"] diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_realtime.py new file mode 100644 index 000000000000..b84a36460f65 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_realtime.py @@ -0,0 +1,62 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Unit tests for the async realtime (WebSocket) interface. + +Realtime streaming is an interface-only preview: the service-side route is not +wired up yet, so ``connect()`` raises ``NotImplementedError`` up front. These +tests assert that contract plus the URL-derivation helper. No network is used. +""" + +import time + +import pytest + +from azure.core.credentials import AccessToken + +from azure.ai.voiceagents.aio import VoiceAgentsClient +from azure.ai.voiceagents.aio._realtime import AsyncRealtime, _to_ws_url + +ENDPOINT = "https://example.services.ai.azure.com/api/projects/my-project" + + +class FakeAsyncCredential: + async def get_token(self, *scopes, **kwargs): + return AccessToken("fake-token", int(time.time()) + 3600) + + async def close(self): + return None + + +def test_to_ws_url_https_to_wss(): + url = _to_ws_url("https://host.example.com/api/projects/p", "my-agent") + assert url == "wss://host.example.com/api/projects/p/agents/my-agent/endpoint/protocols/voice" + + +def test_to_ws_url_http_to_ws(): + url = _to_ws_url("http://localhost:8080", "agent-1") + assert url == "ws://localhost:8080/agents/agent-1/endpoint/protocols/voice" + + +def test_to_ws_url_strips_trailing_slash(): + url = _to_ws_url("https://host.example.com/api/projects/p/", "agent-1") + assert url == "wss://host.example.com/api/projects/p/agents/agent-1/endpoint/protocols/voice" + + +def test_realtime_property_returns_async_realtime(): + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential()) + assert isinstance(client.realtime, AsyncRealtime) + + +def test_connect_raises_not_implemented(): + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential()) + with pytest.raises(NotImplementedError): + client.realtime.connect(agent_name="my-agent") + + +def test_connect_not_implemented_message_mentions_streaming(): + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential()) + with pytest.raises(NotImplementedError) as exc_info: + client.realtime.connect(agent_name="my-agent") + assert "streaming" in str(exc_info.value).lower() diff --git a/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml b/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml new file mode 100644 index 000000000000..351624a03918 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml @@ -0,0 +1,13 @@ +directory: specification/ai-foundry/data-plane/Foundry/src/sdk-python-azure-ai-voice-agents +commit: +repo: +additionalDirectories: +- specification/ai-foundry/data-plane/Foundry/src/agents +- specification/ai-foundry/data-plane/Foundry/src/common +- specification/ai-foundry/data-plane/Foundry/src/memory-stores +- specification/ai-foundry/data-plane/Foundry/src/openai/responses +- specification/ai-foundry/data-plane/Foundry/src/sdk-common +- specification/ai-foundry/data-plane/Foundry/src/skills +- specification/ai-foundry/data-plane/Foundry/src/tools +- specification/ai-foundry/data-plane/Foundry/src/toolboxes +- specification/ai-foundry/data-plane/Foundry/src/voice-agents diff --git a/sdk/voiceagents/ci.yml b/sdk/voiceagents/ci.yml new file mode 100644 index 000000000000..d0a209af135c --- /dev/null +++ b/sdk/voiceagents/ci.yml @@ -0,0 +1,37 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. + +trigger: + branches: + include: + - main + - hotfix/* + - release/* + - restapi* + paths: + include: + - sdk/voiceagents/ + - sdk/core/ + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + - restapi* + paths: + include: + - sdk/voiceagents/ + - sdk/core/ + +extends: + template: /eng/pipelines/templates/stages/archetype-sdk-client.yml + parameters: + ServiceDirectory: voiceagents + TestProxy: true + BuildDocs: true + TestTimeoutInMinutes: 60 + Artifacts: + - name: azure-ai-voiceagents + safeName: azureaivoiceagents diff --git a/sdk/voiceagents/tests.yml b/sdk/voiceagents/tests.yml new file mode 100644 index 000000000000..02a451f3c14a --- /dev/null +++ b/sdk/voiceagents/tests.yml @@ -0,0 +1,7 @@ +trigger: none + +# NOTE: Service live tests are NOT enabled. This file only enables the analyze stage currently. +extends: + template: /eng/pipelines/templates/stages/python-analyze-weekly-standalone.yml + parameters: + ServiceDirectory: voiceagents From 4a2b4345d0c600f7100e1acf60d0d8c838f5e555 Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 14:19:48 -0700 Subject: [PATCH 02/12] Fix CI: api.md consistency, mypy/pyright/pylint, cspell, sample links - Generate api.md + api.metadata.yml (parserVersion 0.3.30) - Add package-local cspell.json - Convert sample README links to absolute GitHub URLs - mypy: Final-typed preview enum in samples; type-ignore aiohttp ws generic - pylint: drop __init__ override in aio/_patch.py (client-accepts-api-version-keyword) - README descriptions and sample lifecycle/404 fixes --- .../azure-ai-voiceagents/README.md | 3 +- sdk/voiceagents/azure-ai-voiceagents/api.md | 3322 +++++++++++++++++ .../azure-ai-voiceagents/api.metadata.yml | 3 + .../azure/ai/voiceagents/aio/_patch.py | 13 +- .../azure/ai/voiceagents/aio/_realtime.py | 5 +- .../azure-ai-voiceagents/cspell.json | 21 + .../azure-ai-voiceagents/samples/README.md | 18 +- ...ple_create_and_manage_voice_agent_async.py | 7 +- .../sample_create_and_manage_voice_agent.py | 9 +- .../sample_create_voice_agent_with_tools.py | 9 +- .../samples/sample_generate_voice_agent.py | 3 +- .../sample_manage_voice_agent_versions.py | 3 +- .../samples/sample_read_conversation.py | 3 +- .../samples/sample_read_conversation_audio.py | 11 +- 14 files changed, 3395 insertions(+), 35 deletions(-) create mode 100644 sdk/voiceagents/azure-ai-voiceagents/api.md create mode 100644 sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml create mode 100644 sdk/voiceagents/azure-ai-voiceagents/cspell.json diff --git a/sdk/voiceagents/azure-ai-voiceagents/README.md b/sdk/voiceagents/azure-ai-voiceagents/README.md index 87782cb70376..e71e6790de42 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/README.md @@ -1,5 +1,6 @@ # Azure Ai Voiceagents client library for Python - + +The Azure AI Voice Agents client library lets Python applications create and manage voice agents and read persisted conversation, transcript, and audio data through an Azure AI Foundry project endpoint. ## Getting started diff --git a/sdk/voiceagents/azure-ai-voiceagents/api.md b/sdk/voiceagents/azure-ai-voiceagents/api.md new file mode 100644 index 000000000000..f0da30e24b16 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/api.md @@ -0,0 +1,3322 @@ +```py +namespace azure.ai.voiceagents + + class azure.ai.voiceagents.VoiceAgentsClient: implements ContextManager + agent_endpoint_conversations: AgentEndpointConversationsOperations + voice_agents: VoiceAgentsOperations + + def __init__( + self, + endpoint: str, + credential: TokenCredential, + *, + api_version: str = ..., + **kwargs: Any + ) -> None: ... + + def close(self) -> None: ... + + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> HttpResponse: ... + + +namespace azure.ai.voiceagents.aio + + class azure.ai.voiceagents.aio.AsyncRealtime: + + def __init__(self, client: Any) -> None: ... + + def connect( + self, + *, + agent_name: str, + extra_headers: Optional[Mapping[str, str]] = ..., + extra_query: Optional[Mapping[str, str]] = ..., + foundry_features: Optional[str] = ..., + **kwargs: Any + ) -> AsyncRealtimeConnectionManager: ... + + + class azure.ai.voiceagents.aio.AsyncRealtimeConnection: implements AsyncContextManager + + def __aiter__(self) -> AsyncIterator[dict[str, Any]]: ... + + def __init__( + self, + connection: ClientWebSocketResponse, + session: ClientSession + ) -> None: ... + + async def close( + self, + *, + code: int = 1000, + reason: str = "" + ) -> None: ... + + async def recv(self) -> dict[str, Any]: ... + + async def send(self, event: Union[Mapping[str, Any], str]) -> None: ... + + + class azure.ai.voiceagents.aio.AsyncRealtimeConnectionManager: implements AsyncContextManager + + def __init__( + self, + *, + agent_name: str, + api_version: str, + credential: AsyncTokenCredential, + credential_scopes: List[str], + endpoint: str, + extra_headers: Optional[Mapping[str, str]] = ..., + extra_query: Optional[Mapping[str, str]] = ..., + foundry_features: Optional[str] = ..., + **kwargs: Any + ) -> None: ... + + async def enter(self) -> AsyncRealtimeConnection: ... + + + class azure.ai.voiceagents.aio.VoiceAgentsClient(_GeneratedVoiceAgentsClient): implements AsyncContextManager + property realtime: AsyncRealtime # Read-only + + def __init__( + self, + endpoint: str, + credential: Any, + **kwargs: Any + ) -> None: ... + + async def close(self) -> None: ... + + def send_request( + self, + request: HttpRequest, + *, + stream: bool = False, + **kwargs: Any + ) -> Awaitable[AsyncHttpResponse]: ... + + +namespace azure.ai.voiceagents.aio.operations + + class azure.ai.voiceagents.aio.operations.AgentEndpointConversationsOperations: + + def __init__( + self, + *args, + **kwargs + ) -> None: ... + + @distributed_trace_async + async def delete_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceDeletedConversation: ... + + @distributed_trace_async + async def get_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceConversation: ... + + @distributed_trace_async + async def get_agent_conversation_audio( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceRecordingResponse: ... + + @distributed_trace_async + async def get_agent_conversation_audio_content( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> AsyncIterator[bytes]: ... + + @distributed_trace_async + async def get_agent_conversation_item( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceConversationItem: ... + + @distributed_trace_async + async def get_agent_conversation_item_audio( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceItemAudioResponse: ... + + @distributed_trace_async + async def get_agent_conversation_item_audio_content( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> AsyncIterator[bytes]: ... + + @distributed_trace_async + async def get_agent_conversation_response( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceResponse: ... + + @distributed_trace + def list_agent_conversation_items( + self, + agent_name: str, + conversation_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> AsyncItemPaged[VoiceConversationItem]: ... + + @distributed_trace + def list_agent_conversation_response_items( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> AsyncItemPaged[VoiceConversationItem]: ... + + @distributed_trace + def list_agent_conversation_responses( + self, + agent_name: str, + conversation_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> AsyncItemPaged[VoiceResponse]: ... + + + class azure.ai.voiceagents.aio.operations.VoiceAgentsOperations: + + def __init__( + self, + *args, + **kwargs + ) -> None: ... + + @overload + async def create_voice_agent( + self, + *, + agent_card: Optional[AgentCard] = ..., + agent_endpoint: Optional[AgentEndpointConfig] = ..., + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + name: str, + state: Optional[Union[str, AgentState]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def create_voice_agent( + self, + body: CreateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def create_voice_agent( + self, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + *, + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + body: CreateVoiceAgentVersionRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @overload + async def create_voice_agent_version( + self, + agent_name: str, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @distributed_trace_async + async def delete_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> DeleteAgentResponse: ... + + @distributed_trace_async + async def delete_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> DeleteAgentVersionResponse: ... + + @distributed_trace_async + async def disable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> None: ... + + @distributed_trace_async + async def enable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> None: ... + + @overload + async def generate_voice_agent( + self, + *, + agent_type: Union[str, VoiceAgentType], + content_type: str = "application/json", + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + goal: str, + model: str, + model_type: Union[str, VoiceModelType], + name: str, + tools: Optional[list[VoiceAgentTool]] = ..., + use_case: Union[str, VoiceAgentUseCase], + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def generate_voice_agent( + self, + body: GenerateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def generate_voice_agent( + self, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @distributed_trace_async + async def get_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @distributed_trace_async + async def get_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @distributed_trace + def list_voice_agent_versions( + self, + agent_name: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + include_drafts: Optional[bool] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> AsyncItemPaged[VoiceAgentVersionObject]: ... + + @distributed_trace + def list_voice_agents( + self, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> AsyncItemPaged[VoiceAgentObject]: ... + + @overload + async def update_voice_agent( + self, + agent_name: str, + *, + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def update_voice_agent( + self, + agent_name: str, + body: UpdateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + async def update_voice_agent( + self, + agent_name: str, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + +namespace azure.ai.voiceagents.models + + class azure.ai.voiceagents.models.A2AProtocolConfiguration(_Model): + + + class azure.ai.voiceagents.models.ActivityProtocolConfiguration(_Model): + enable_m365_public_endpoint: Optional[bool] + + @overload + def __init__( + self, + *, + enable_m365_public_endpoint: Optional[bool] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentBlueprintReference(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + MANAGED_AGENT_IDENTITY_BLUEPRINT = "ManagedAgentIdentityBlueprint" + + + class azure.ai.voiceagents.models.AgentCard(_Model): + description: Optional[str] + skills: list[AgentCardSkill] + version: str + + @overload + def __init__( + self, + *, + description: Optional[str] = ..., + skills: list[AgentCardSkill], + version: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentCardSkill(_Model): + description: Optional[str] + examples: Optional[list[str]] + id: str + name: str + tags: Optional[list[str]] + + @overload + def __init__( + self, + *, + description: Optional[str] = ..., + examples: Optional[list[str]] = ..., + id: str, + name: str, + tags: Optional[list[str]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentDefinitionOptInKeys(str, Enum, metaclass=CaseInsensitiveEnumMeta): + DRAFT_AGENTS_V1_PREVIEW = "DraftAgents=V1Preview" + EXTERNAL_AGENTS_V1_PREVIEW = "ExternalAgents=V1Preview" + VOICE_AGENTS_V1_PREVIEW = "VoiceAgents=V1Preview" + WORKFLOW_AGENTS_V1_PREVIEW = "WorkflowAgents=V1Preview" + + + class azure.ai.voiceagents.models.AgentEndpointAuthorizationScheme(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentEndpointAuthorizationSchemeType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + BOT_SERVICE = "BotService" + BOT_SERVICE_RBAC = "BotServiceRbac" + BOT_SERVICE_TENANT = "BotServiceTenant" + ENTRA = "Entra" + + + class azure.ai.voiceagents.models.AgentEndpointConfig(_Model): + authorization_schemes: Optional[list[AgentEndpointAuthorizationScheme]] + protocol_configuration: Optional[ProtocolConfiguration] + version_selector: Optional[VersionSelector] + + @overload + def __init__( + self, + *, + authorization_schemes: Optional[list[AgentEndpointAuthorizationScheme]] = ..., + protocol_configuration: Optional[ProtocolConfiguration] = ..., + version_selector: Optional[VersionSelector] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentIdentity(_Model): + client_id: str + principal_id: str + status: Optional[Union[str, AgentIdentityStatus]] + + @overload + def __init__( + self, + *, + client_id: str, + principal_id: str, + status: Optional[Union[str, AgentIdentityStatus]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AgentIdentityStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ACTIVE = "active" + DISABLED = "disabled" + + + class azure.ai.voiceagents.models.AgentObjectType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + AGENT = "agent" + AGENT_CONTAINER = "agent.container" + AGENT_DELETED = "agent.deleted" + AGENT_VERSION = "agent.version" + AGENT_VERSION_DELETED = "agent.version.deleted" + + + class azure.ai.voiceagents.models.AgentState(str, Enum, metaclass=CaseInsensitiveEnumMeta): + DISABLED = "disabled" + ENABLED = "enabled" + + + class azure.ai.voiceagents.models.AgentVersionStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ACTIVE = "active" + CREATING = "creating" + DELETED = "deleted" + DELETING = "deleting" + FAILED = "failed" + + + class azure.ai.voiceagents.models.ApiErrorResponse(_Model): + error: Error + + @overload + def __init__( + self, + *, + error: Error + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AzureVoice(_Model): + locale: Optional[str] + name: str + pitch: Optional[str] + rate: Optional[str] + style: Optional[str] + type: Union[str, AzureVoiceType] + + @overload + def __init__( + self, + *, + locale: Optional[str] = ..., + name: str, + pitch: Optional[str] = ..., + rate: Optional[str] = ..., + style: Optional[str] = ..., + type: Union[str, AzureVoiceType] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.AzureVoiceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + AVATAR_VOICE_SYNC = "avatar-voice-sync" + AZURE_CUSTOM = "azure-custom" + AZURE_PERSONAL = "azure-personal" + AZURE_REALTIME_NATIVE = "azure-realtime-native" + AZURE_STANDARD = "azure-standard" + + + class azure.ai.voiceagents.models.BotServiceAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator='BotService'): + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.BotServiceRbacAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator='BotServiceRbac'): + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.BotServiceTenantAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator='BotServiceTenant'): + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.DeleteAgentResponse(_Model): + deleted: bool + name: str + object: Literal[AgentObjectType.AGENT_DELETED] + + @overload + def __init__( + self, + *, + deleted: bool, + name: str, + object: Literal[AgentObjectType.AGENT_DELETED] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.DeleteAgentVersionResponse(_Model): + deleted: bool + name: str + object: Literal[AgentObjectType.AGENT_VERSION_DELETED] + version: str + + @overload + def __init__( + self, + *, + deleted: bool, + name: str, + object: Literal[AgentObjectType.AGENT_VERSION_DELETED], + version: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.EntraAuthorizationScheme(AgentEndpointAuthorizationScheme, discriminator='Entra'): + type: Literal[AgentEndpointAuthorizationSchemeType.ENTRA] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.Error(_Model): + additional_info: Optional[dict[str, Any]] + code: str + debug_info: Optional[dict[str, Any]] + details: Optional[list[Error]] + message: str + param: Optional[str] + type: Optional[str] + + @overload + def __init__( + self, + *, + additional_info: Optional[dict[str, Any]] = ..., + code: str, + debug_info: Optional[dict[str, Any]] = ..., + details: Optional[list[Error]] = ..., + message: str, + param: Optional[str] = ..., + type: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.FixedRatioVersionSelectionRule(VersionSelectionRule, discriminator='FixedRatio'): + agent_version: str + traffic_percentage: int + type: Literal[VersionSelectorType.FIXED_RATIO] + + @overload + def __init__( + self, + *, + agent_version: str, + traffic_percentage: int + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.FunctionTool(Tool, discriminator='function'): + defer_loading: Optional[bool] + description: Optional[str] + name: str + parameters: dict[str, Any] + strict: bool + type: Literal[ToolType.FUNCTION] + + @overload + def __init__( + self, + *, + defer_loading: Optional[bool] = ..., + description: Optional[str] = ..., + name: str, + parameters: dict[str, Any], + strict: bool + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.InvocationsProtocolConfiguration(_Model): + + + class azure.ai.voiceagents.models.InvocationsWsProtocolConfiguration(_Model): + + + class azure.ai.voiceagents.models.MCPListToolsTool(_Model): + annotations: Optional[MCPListToolsToolAnnotations] + description: Optional[str] + input_schema: MCPListToolsToolInputSchema + name: str + + @overload + def __init__( + self, + *, + annotations: Optional[MCPListToolsToolAnnotations] = ..., + description: Optional[str] = ..., + input_schema: MCPListToolsToolInputSchema, + name: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.MCPListToolsToolAnnotations(_Model): + + + class azure.ai.voiceagents.models.MCPListToolsToolInputSchema(_Model): + + + class azure.ai.voiceagents.models.MCPTool(Tool, discriminator='mcp'): + allowed_tools: Optional[Union[list[str], MCPToolFilter]] + authorization: Optional[str] + connector_id: Optional[Literal["connector_dropbox", "connector_gmail", "connector_googlecalendar", "connector_googledrive", "connector_microsoftteams", "connector_outlookcalendar", "connector_outlookemail", "connector_sharepoint"]] + defer_loading: Optional[bool] + headers: Optional[dict[str, str]] + project_connection_id: Optional[str] + require_approval: Optional[Union[MCPToolRequireApproval, Literal["always"], Literal["never"]]] + server_description: Optional[str] + server_label: str + server_url: Optional[str] + tool_configs: Optional[dict[str, ToolConfig]] + tunnel_id: Optional[str] + type: Literal[ToolType.MCP] + + @overload + def __init__( + self, + *, + allowed_tools: Optional[Union[list[str], MCPToolFilter]] = ..., + authorization: Optional[str] = ..., + connector_id: Optional[Literal[connector_dropbox, connector_gmail, connector_googlecalendar, connector_googledrive, connector_microsoftteams, connector_outlookcalendar, connector_outlookemail, connector_sharepoint]] = ..., + defer_loading: Optional[bool] = ..., + headers: Optional[dict[str, str]] = ..., + project_connection_id: Optional[str] = ..., + require_approval: Optional[Union[MCPToolRequireApproval, Literal[always], Literal[never]]] = ..., + server_description: Optional[str] = ..., + server_label: str, + server_url: Optional[str] = ..., + tool_configs: Optional[dict[str, ToolConfig]] = ..., + tunnel_id: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.MCPToolFilter(_Model): + read_only: Optional[bool] + tool_names: Optional[list[str]] + + @overload + def __init__( + self, + *, + read_only: Optional[bool] = ..., + tool_names: Optional[list[str]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.MCPToolRequireApproval(_Model): + always: Optional[MCPToolFilter] + never: Optional[MCPToolFilter] + + @overload + def __init__( + self, + *, + always: Optional[MCPToolFilter] = ..., + never: Optional[MCPToolFilter] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.ManagedAgentIdentityBlueprintReference(AgentBlueprintReference, discriminator='ManagedAgentIdentityBlueprint'): + blueprint_id: str + type: Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT] + + @overload + def __init__( + self, + *, + blueprint_id: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.McpProtocolConfiguration(_Model): + + + class azure.ai.voiceagents.models.PageOrder(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ASC = "asc" + DESC = "desc" + + + class azure.ai.voiceagents.models.ProtocolConfiguration(_Model): + a2_a: Optional[A2AProtocolConfiguration] + activity: Optional[ActivityProtocolConfiguration] + invocations: Optional[InvocationsProtocolConfiguration] + invocations_ws: Optional[InvocationsWsProtocolConfiguration] + mcp: Optional[McpProtocolConfiguration] + responses: Optional[ResponsesProtocolConfiguration] + + @overload + def __init__( + self, + *, + a2_a: Optional[A2AProtocolConfiguration] = ..., + activity: Optional[ActivityProtocolConfiguration] = ..., + invocations: Optional[InvocationsProtocolConfiguration] = ..., + invocations_ws: Optional[InvocationsWsProtocolConfiguration] = ..., + mcp: Optional[McpProtocolConfiguration] = ..., + responses: Optional[ResponsesProtocolConfiguration] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RaiConfig(_Model): + rai_policy_name: str + + @overload + def __init__( + self, + *, + rai_policy_name: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeAudioFormats(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcm(RealtimeAudioFormats, discriminator='audio/pcm'): + rate: Optional[Literal[24000]] + type: Literal[RealtimeAudioFormatsType.AUDIO_PCM] + + @overload + def __init__( + self, + *, + rate: Optional[Literal[24000]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcma(RealtimeAudioFormats, discriminator='audio/pcma'): + type: Literal[RealtimeAudioFormatsType.AUDIO_PCMA] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeAudioFormatsAudioPcmu(RealtimeAudioFormats, discriminator='audio/pcmu'): + type: Literal[RealtimeAudioFormatsType.AUDIO_PCMU] + + @overload + def __init__(self) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeAudioFormatsType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + AUDIO_PCM = "audio/pcm" + AUDIO_PCMA = "audio/pcma" + AUDIO_PCMU = "audio/pcmu" + + + class azure.ai.voiceagents.models.RealtimeConversationItem(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemFunctionCall(RealtimeConversationItem, discriminator='function_call'): + arguments: str + call_id: Optional[str] + id: Optional[str] + name: str + object: Optional[Literal["item"]] + status: Optional[Literal["completed", "incomplete", "in_progress"]] + type: Literal[RealtimeConversationItemType.FUNCTION_CALL] + + @overload + def __init__( + self, + *, + arguments: str, + call_id: Optional[str] = ..., + id: Optional[str] = ..., + name: str, + object: Optional[Literal[item]] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemFunctionCallOutput(RealtimeConversationItem, discriminator='function_call_output'): + call_id: str + id: Optional[str] + object: Optional[Literal["item"]] + output: str + status: Optional[Literal["completed", "incomplete", "in_progress"]] + type: Literal[RealtimeConversationItemType.FUNCTION_CALL_OUTPUT] + + @overload + def __init__( + self, + *, + call_id: str, + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + output: str, + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessage(_Model): + role: str + + @overload + def __init__( + self, + *, + role: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistant(RealtimeConversationItemMessage, discriminator='assistant'): + content: list[RealtimeConversationItemMessageAssistantContent] + id: Optional[str] + object: Optional[Literal["item"]] + role: Literal[RealtimeConversationItemMessageType.ASSISTANT] + status: Optional[Literal["completed", "incomplete", "in_progress"]] + type: Literal["message"] + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageAssistantContent], + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageAssistantContent(_Model): + audio: Optional[str] + text: Optional[str] + transcript: Optional[str] + type: Optional[Literal["output_text", "output_audio"]] + + @overload + def __init__( + self, + *, + audio: Optional[str] = ..., + text: Optional[str] = ..., + transcript: Optional[str] = ..., + type: Optional[Literal[output_text, output_audio]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageSystem(RealtimeConversationItemMessage, discriminator='system'): + content: list[RealtimeConversationItemMessageSystemContent] + id: Optional[str] + object: Optional[Literal["item"]] + role: Literal[RealtimeConversationItemMessageType.SYSTEM] + status: Optional[Literal["completed", "incomplete", "in_progress"]] + type: Literal["message"] + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageSystemContent], + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageSystemContent(_Model): + text: Optional[str] + type: Optional[Literal["input_text"]] + + @overload + def __init__( + self, + *, + text: Optional[str] = ..., + type: Optional[Literal[input_text]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ASSISTANT = "assistant" + SYSTEM = "system" + USER = "user" + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageUser(RealtimeConversationItemMessage, discriminator='user'): + content: list[RealtimeConversationItemMessageUserContent] + id: Optional[str] + object: Optional[Literal["item"]] + role: Literal[RealtimeConversationItemMessageType.USER] + status: Optional[Literal["completed", "incomplete", "in_progress"]] + type: Literal["message"] + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageUserContent], + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemMessageUserContent(_Model): + audio: Optional[str] + detail: Optional[Literal["auto", "low", "high"]] + image_url: Optional[str] + text: Optional[str] + transcript: Optional[str] + type: Optional[Literal["input_text", "input_audio", "input_image"]] + + @overload + def __init__( + self, + *, + audio: Optional[str] = ..., + detail: Optional[Literal[auto, low, high]] = ..., + image_url: Optional[str] = ..., + text: Optional[str] = ..., + transcript: Optional[str] = ..., + type: Optional[Literal[input_text, input_audio, input_image]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeConversationItemType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + FUNCTION_CALL = "function_call" + FUNCTION_CALL_OUTPUT = "function_call_output" + MCP_APPROVAL_REQUEST = "mcp_approval_request" + MCP_APPROVAL_RESPONSE = "mcp_approval_response" + MCP_CALL = "mcp_call" + MCP_LIST_TOOLS = "mcp_list_tools" + + + class azure.ai.voiceagents.models.RealtimeMCPApprovalRequest(RealtimeConversationItem, discriminator='mcp_approval_request'): + arguments: str + id: str + name: str + server_label: str + type: Literal[RealtimeConversationItemType.MCP_APPROVAL_REQUEST] + + @overload + def __init__( + self, + *, + arguments: str, + id: str, + name: str, + server_label: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPApprovalResponse(RealtimeConversationItem, discriminator='mcp_approval_response'): + approval_request_id: str + approve: bool + id: str + reason: Optional[str] + type: Literal[RealtimeConversationItemType.MCP_APPROVAL_RESPONSE] + + @overload + def __init__( + self, + *, + approval_request_id: str, + approve: bool, + id: str, + reason: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPError(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPHTTPError(RealtimeMCPError, discriminator='http_error'): + code: int + message: str + type: Literal[RealtimeMcpErrorType.HTTP_ERROR] + + @overload + def __init__( + self, + *, + code: int, + message: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPListTools(RealtimeConversationItem, discriminator='mcp_list_tools'): + id: Optional[str] + server_label: str + tools: list[MCPListToolsTool] + type: Literal[RealtimeConversationItemType.MCP_LIST_TOOLS] + + @overload + def __init__( + self, + *, + id: Optional[str] = ..., + server_label: str, + tools: list[MCPListToolsTool] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPProtocolError(RealtimeMCPError, discriminator='protocol_error'): + code: int + message: str + type: Literal[RealtimeMcpErrorType.PROTOCOL_ERROR] + + @overload + def __init__( + self, + *, + code: int, + message: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPToolCall(RealtimeConversationItem, discriminator='mcp_call'): + approval_request_id: Optional[str] + arguments: str + error: Optional[RealtimeMCPError] + id: str + name: str + output: Optional[str] + server_label: str + type: Literal[RealtimeConversationItemType.MCP_CALL] + + @overload + def __init__( + self, + *, + approval_request_id: Optional[str] = ..., + arguments: str, + error: Optional[RealtimeMCPError] = ..., + id: str, + name: str, + output: Optional[str] = ..., + server_label: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMCPToolExecutionError(RealtimeMCPError, discriminator='tool_execution_error'): + message: str + type: Literal[RealtimeMcpErrorType.TOOL_EXECUTION_ERROR] + + @overload + def __init__( + self, + *, + message: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeMcpErrorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + HTTP_ERROR = "http_error" + PROTOCOL_ERROR = "protocol_error" + TOOL_EXECUTION_ERROR = "tool_execution_error" + + + class azure.ai.voiceagents.models.RealtimeResponseStatusDetails(_Model): + error: Optional[RealtimeResponseStatusDetailsError] + reason: Optional[Literal["turn_detected", "client_cancelled", "max_output_tokens", "content_filter"]] + type: Optional[Literal["completed", "cancelled", "failed", "incomplete"]] + + @overload + def __init__( + self, + *, + error: Optional[RealtimeResponseStatusDetailsError] = ..., + reason: Optional[Literal[turn_detected, client_cancelled, max_output_tokens, content_filter]] = ..., + type: Optional[Literal[completed, cancelled, failed, incomplete]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeResponseStatusDetailsError(_Model): + code: Optional[str] + type: Optional[str] + + @overload + def __init__( + self, + *, + code: Optional[str] = ..., + type: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeResponseUsage(_Model): + input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] + input_tokens: Optional[int] + output_token_details: Optional[RealtimeResponseUsageOutputTokenDetails] + output_tokens: Optional[int] + total_tokens: Optional[int] + + @overload + def __init__( + self, + *, + input_token_details: Optional[RealtimeResponseUsageInputTokenDetails] = ..., + input_tokens: Optional[int] = ..., + output_token_details: Optional[RealtimeResponseUsageOutputTokenDetails] = ..., + output_tokens: Optional[int] = ..., + total_tokens: Optional[int] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetails(_Model): + audio_tokens: Optional[int] + cached_tokens: Optional[int] + cached_tokens_details: Optional[RealtimeResponseUsageInputTokenDetailsCachedTokensDetails] + image_tokens: Optional[int] + text_tokens: Optional[int] + + @overload + def __init__( + self, + *, + audio_tokens: Optional[int] = ..., + cached_tokens: Optional[int] = ..., + cached_tokens_details: Optional[RealtimeResponseUsageInputTokenDetailsCachedTokensDetails] = ..., + image_tokens: Optional[int] = ..., + text_tokens: Optional[int] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeResponseUsageInputTokenDetailsCachedTokensDetails(_Model): + audio_tokens: Optional[int] + image_tokens: Optional[int] + text_tokens: Optional[int] + + @overload + def __init__( + self, + *, + audio_tokens: Optional[int] = ..., + image_tokens: Optional[int] = ..., + text_tokens: Optional[int] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.RealtimeResponseUsageOutputTokenDetails(_Model): + audio_tokens: Optional[int] + text_tokens: Optional[int] + + @overload + def __init__( + self, + *, + audio_tokens: Optional[int] = ..., + text_tokens: Optional[int] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.ResponsesProtocolConfiguration(_Model): + + + class azure.ai.voiceagents.models.SemanticVadTurnDetection(VoiceTurnDetection, discriminator='semantic_vad'): + eagerness: Optional[Union[str, VoiceTurnDetectionEagerness]] + type: Literal[VoiceTurnDetectionType.SEMANTIC_VAD] + + @overload + def __init__( + self, + *, + eagerness: Optional[Union[str, VoiceTurnDetectionEagerness]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.ServerVadTurnDetection(VoiceTurnDetection, discriminator='server_vad'): + create_response: Optional[bool] + prefix_padding_ms: Optional[int] + silence_duration_ms: Optional[int] + threshold: Optional[float] + type: Literal[VoiceTurnDetectionType.SERVER_VAD] + + @overload + def __init__( + self, + *, + create_response: Optional[bool] = ..., + prefix_padding_ms: Optional[int] = ..., + silence_duration_ms: Optional[int] = ..., + threshold: Optional[float] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.StructuredInputDefinition(_Model): + default_value: Optional[Any] + description: Optional[str] + required: Optional[bool] + schema: Optional[dict[str, Any]] + + @overload + def __init__( + self, + *, + default_value: Optional[Any] = ..., + description: Optional[str] = ..., + required: Optional[bool] = ..., + schema: Optional[dict[str, Any]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.Tool(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.ToolConfig(_Model): + additional_search_text: Optional[str] + pin: Optional[bool] + + @overload + def __init__( + self, + *, + additional_search_text: Optional[str] = ..., + pin: Optional[bool] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + A2_A_PREVIEW = "a2a_preview" + APPLY_PATCH = "apply_patch" + AZURE_AI_SEARCH = "azure_ai_search" + AZURE_FUNCTION = "azure_function" + BING_CUSTOM_SEARCH_PREVIEW = "bing_custom_search_preview" + BING_GROUNDING = "bing_grounding" + BROWSER_AUTOMATION_PREVIEW = "browser_automation_preview" + CAPTURE_STRUCTURED_OUTPUTS = "capture_structured_outputs" + CODE_INTERPRETER = "code_interpreter" + COMPUTER = "computer" + COMPUTER_USE_PREVIEW = "computer_use_preview" + CUSTOM = "custom" + FABRIC_DATAAGENT_PREVIEW = "fabric_dataagent_preview" + FABRIC_IQ_PREVIEW = "fabric_iq_preview" + FILE_SEARCH = "file_search" + FUNCTION = "function" + IMAGE_GENERATION = "image_generation" + LOCAL_SHELL = "local_shell" + MCP = "mcp" + MEMORY_SEARCH_PREVIEW = "memory_search_preview" + NAMESPACE = "namespace" + OPENAPI = "openapi" + SHAREPOINT_GROUNDING_PREVIEW = "sharepoint_grounding_preview" + SHELL = "shell" + TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" + TOOL_SEARCH = "tool_search" + WEB_IQ_PREVIEW = "web_iq_preview" + WEB_SEARCH = "web_search" + WEB_SEARCH_PREVIEW = "web_search_preview" + WORK_IQ_PREVIEW = "work_iq_preview" + + + class azure.ai.voiceagents.models.VersionSelectionRule(_Model): + agent_version: str + type: str + + @overload + def __init__( + self, + *, + agent_version: str, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VersionSelector(_Model): + version_selection_rules: list[VersionSelectionRule] + + @overload + def __init__( + self, + *, + version_selection_rules: list[VersionSelectionRule] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VersionSelectorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + FIXED_RATIO = "FixedRatio" + + + class azure.ai.voiceagents.models.VoiceAgentDefinition(_Model): + audio: Optional[VoiceAudioConfig] + avatar: Optional[VoiceAvatarConfig] + instructions: Optional[str] + kind: Literal["voice"] + model: str + model_type: Union[str, VoiceModelType] + output_modalities: Optional[list[Union[str, VoiceOutputModality]]] + rai_config: Optional[RaiConfig] + store: Optional[bool] + structured_inputs: Optional[dict[str, StructuredInputDefinition]] + tools: Optional[list[VoiceAgentTool]] + + @overload + def __init__( + self, + *, + audio: Optional[VoiceAudioConfig] = ..., + avatar: Optional[VoiceAvatarConfig] = ..., + instructions: Optional[str] = ..., + model: str, + model_type: Union[str, VoiceModelType], + output_modalities: Optional[list[Union[str, VoiceOutputModality]]] = ..., + rai_config: Optional[RaiConfig] = ..., + store: Optional[bool] = ..., + structured_inputs: Optional[dict[str, StructuredInputDefinition]] = ..., + tools: Optional[list[VoiceAgentTool]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAgentObject(_Model): + agent_card: Optional[AgentCard] + agent_endpoint: Optional[AgentEndpointConfig] + blueprint: Optional[AgentIdentity] + blueprint_reference: Optional[AgentBlueprintReference] + id: str + instance_identity: Optional[AgentIdentity] + name: str + object: Literal[AgentObjectType.AGENT] + state: Union[str, AgentState] + versions: VoiceAgentObjectVersions + + @overload + def __init__( + self, + *, + agent_card: Optional[AgentCard] = ..., + agent_endpoint: Optional[AgentEndpointConfig] = ..., + id: str, + name: str, + object: Literal[AgentObjectType.AGENT], + versions: VoiceAgentObjectVersions + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAgentObjectVersions(_Model): + latest: VoiceAgentVersionObject + + @overload + def __init__( + self, + *, + latest: VoiceAgentVersionObject + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAgentType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + BUSINESS = "business" + PERSONAL = "personal" + + + class azure.ai.voiceagents.models.VoiceAgentUseCase(str, Enum, metaclass=CaseInsensitiveEnumMeta): + CALL_CENTER = "call_center" + CUSTOMER_SUPPORT = "customer_support" + IN_CAR = "in_car" + LEARNING = "learning" + OUTREACH = "outreach" + PERSONAL_ASSISTANT = "personal_assistant" + RECEPTION = "reception" + SALES = "sales" + TRAVEL_ASSISTANT = "travel_assistant" + + + class azure.ai.voiceagents.models.VoiceAgentVersionObject(_Model): + agent_guid: Optional[str] + blueprint: Optional[AgentIdentity] + blueprint_reference: Optional[AgentBlueprintReference] + created_at: datetime + definition: VoiceAgentDefinition + description: Optional[str] + draft: Optional[bool] + id: str + instance_identity: Optional[AgentIdentity] + metadata: dict[str, str] + name: str + object: Literal[AgentObjectType.AGENT_VERSION] + status: Optional[Union[str, AgentVersionStatus]] + version: str + + @overload + def __init__( + self, + *, + created_at: datetime, + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + draft: Optional[bool] = ..., + id: str, + metadata: dict[str, str], + name: str, + object: Literal[AgentObjectType.AGENT_VERSION], + status: Optional[Union[str, AgentVersionStatus]] = ..., + version: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAssistantMessageItem(RealtimeConversationItemMessageAssistant): + content: list[RealtimeConversationItemMessageAssistantContent] + created_at: Optional[datetime] + id: str + object: str + response_id: Optional[str] + role: Union[str, azure.ai.voiceagents.models.ASSISTANT] + status: Union[str, str, str] + type: str + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageAssistantContent], + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + response_id: Optional[str] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAudioConfig(_Model): + input: Optional[VoiceAudioInputConfig] + output: Optional[VoiceAudioOutputConfig] + + @overload + def __init__( + self, + *, + input: Optional[VoiceAudioInputConfig] = ..., + output: Optional[VoiceAudioOutputConfig] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAudioFormat(_Model): + rate: Optional[int] + type: Union[str, VoiceAudioFormatType] + + @overload + def __init__( + self, + *, + rate: Optional[int] = ..., + type: Union[str, VoiceAudioFormatType] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAudioFormatType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + PCM = "audio/pcm" + PCMA = "audio/pcma" + PCMU = "audio/pcmu" + + + class azure.ai.voiceagents.models.VoiceAudioInputConfig(_Model): + format: Optional[VoiceAudioFormat] + noise_reduction: Optional[VoiceNoiseReduction] + transcription: Optional[VoiceInputTranscription] + turn_detection: Optional[VoiceTurnDetection] + + @overload + def __init__( + self, + *, + format: Optional[VoiceAudioFormat] = ..., + noise_reduction: Optional[VoiceNoiseReduction] = ..., + transcription: Optional[VoiceInputTranscription] = ..., + turn_detection: Optional[VoiceTurnDetection] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAudioOutputConfig(_Model): + format: Optional[VoiceAudioFormat] + speed: Optional[float] + voice: Optional[Union[str, AzureVoice]] + + @overload + def __init__( + self, + *, + format: Optional[VoiceAudioFormat] = ..., + speed: Optional[float] = ..., + voice: Optional[Union[str, AzureVoice]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAvatarConfig(_Model): + character: str + customized: Optional[bool] + output_protocol: Optional[Union[str, VoiceAvatarOutputProtocol]] + style: Optional[str] + type: Union[str, VoiceAvatarType] + + @overload + def __init__( + self, + *, + character: str, + customized: Optional[bool] = ..., + output_protocol: Optional[Union[str, VoiceAvatarOutputProtocol]] = ..., + style: Optional[str] = ..., + type: Union[str, VoiceAvatarType] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceAvatarOutputProtocol(str, Enum, metaclass=CaseInsensitiveEnumMeta): + WEBRTC = "webrtc" + WEBSOCKET = "websocket" + + + class azure.ai.voiceagents.models.VoiceAvatarType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + PHOTO_AVATAR = "photo-avatar" + VIDEO_AVATAR = "video-avatar" + + + class azure.ai.voiceagents.models.VoiceConversation(_Model): + completed_at: Optional[datetime] + created_at: datetime + id: str + metadata: Optional[dict[str, str]] + object: Literal["conversation"] + status: Union[str, VoiceConversationStatus] + usage: Optional[RealtimeResponseUsage] + + @overload + def __init__( + self, + *, + completed_at: Optional[datetime] = ..., + created_at: datetime, + id: str, + metadata: Optional[dict[str, str]] = ..., + status: Union[str, VoiceConversationStatus], + usage: Optional[RealtimeResponseUsage] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceConversationStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + COMPLETED = "completed" + IN_PROGRESS = "in_progress" + + + class azure.ai.voiceagents.models.VoiceDeletedConversation(_Model): + deleted: bool + id: str + object: Literal["deleted"] + + @overload + def __init__( + self, + *, + deleted: bool, + id: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceFunctionCallItem(RealtimeConversationItemFunctionCall): + arguments: str + call_id: str + created_at: Optional[datetime] + id: str + name: str + object: str + response_id: Optional[str] + status: Union[str, str, str] + type: Union[str, azure.ai.voiceagents.models.FUNCTION_CALL] + + @overload + def __init__( + self, + *, + arguments: str, + call_id: Optional[str] = ..., + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + name: str, + object: Optional[Literal[item]] = ..., + response_id: Optional[str] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceFunctionCallOutputItem(RealtimeConversationItemFunctionCallOutput): + call_id: str + created_at: Optional[datetime] + id: str + name: Optional[str] + object: str + output: str + response_id: Optional[str] + status: Union[str, str, str] + type: Union[str, azure.ai.voiceagents.models.FUNCTION_CALL_OUTPUT] + + @overload + def __init__( + self, + *, + call_id: str, + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + name: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + output: str, + response_id: Optional[str] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceInputTranscription(_Model): + language: Optional[str] + model: Optional[str] + prompt: Optional[str] + + @overload + def __init__( + self, + *, + language: Optional[str] = ..., + model: Optional[str] = ..., + prompt: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceItemAudioResponse(_Model): + blob_path: Optional[str] + channels: Optional[int] + codec: Optional[str] + conversation_id: str + duration_ms: Optional[int] + format: Optional[str] + item_id: str + role: Optional[str] + sample_rate: Optional[int] + start_offset_ms: Optional[int] + + @overload + def __init__( + self, + *, + blob_path: Optional[str] = ..., + channels: Optional[int] = ..., + codec: Optional[str] = ..., + conversation_id: str, + duration_ms: Optional[int] = ..., + format: Optional[str] = ..., + item_id: str, + role: Optional[str] = ..., + sample_rate: Optional[int] = ..., + start_offset_ms: Optional[int] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceMcpApprovalRequestItem(RealtimeMCPApprovalRequest): + arguments: str + created_at: Optional[datetime] + id: str + name: str + response_id: Optional[str] + server_label: str + type: Union[str, azure.ai.voiceagents.models.MCP_APPROVAL_REQUEST] + + @overload + def __init__( + self, + *, + arguments: str, + created_at: Optional[datetime] = ..., + id: str, + name: str, + response_id: Optional[str] = ..., + server_label: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceMcpApprovalResponseItem(RealtimeMCPApprovalResponse): + approval_request_id: str + approve: bool + created_at: Optional[datetime] + id: str + reason: str + response_id: Optional[str] + type: Union[str, azure.ai.voiceagents.models.MCP_APPROVAL_RESPONSE] + + @overload + def __init__( + self, + *, + approval_request_id: str, + approve: bool, + created_at: Optional[datetime] = ..., + id: str, + reason: Optional[str] = ..., + response_id: Optional[str] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceMcpCallItem(RealtimeMCPToolCall): + approval_request_id: str + arguments: str + created_at: Optional[datetime] + error: RealtimeMCPError + id: str + name: str + output: str + response_id: Optional[str] + server_label: str + type: Union[str, azure.ai.voiceagents.models.MCP_CALL] + + @overload + def __init__( + self, + *, + approval_request_id: Optional[str] = ..., + arguments: str, + created_at: Optional[datetime] = ..., + error: Optional[RealtimeMCPError] = ..., + id: str, + name: str, + output: Optional[str] = ..., + response_id: Optional[str] = ..., + server_label: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceMcpListToolsItem(RealtimeMCPListTools): + created_at: Optional[datetime] + id: str + response_id: Optional[str] + server_label: str + tools: list[MCPListToolsTool] + type: Union[str, azure.ai.voiceagents.models.MCP_LIST_TOOLS] + + @overload + def __init__( + self, + *, + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + response_id: Optional[str] = ..., + server_label: str, + tools: list[MCPListToolsTool] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceModelType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + MANAGED = "managed" + SELF_DEPLOYED = "self_deployed" + + + class azure.ai.voiceagents.models.VoiceNoiseReduction(_Model): + type: Union[str, VoiceNoiseReductionType] + + @overload + def __init__( + self, + *, + type: Union[str, VoiceNoiseReductionType] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceNoiseReductionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + AZURE_DEEP_NOISE_SUPPRESSION = "azure_deep_noise_suppression" + FAR_FIELD = "far_field" + NEAR_FIELD = "near_field" + + + class azure.ai.voiceagents.models.VoiceOutputModality(str, Enum, metaclass=CaseInsensitiveEnumMeta): + ANIMATION = "animation" + AUDIO = "audio" + AVATAR = "avatar" + TEXT = "text" + + + class azure.ai.voiceagents.models.VoiceRecordingChannelLayout(_Model): + left: str + right: str + + @overload + def __init__( + self, + *, + left: str, + right: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceRecordingResponse(_Model): + blob_path: Optional[str] + channel_layout: VoiceRecordingChannelLayout + channels: int + conversation_id: str + duration_ms: int + format: str + sample_rate: int + + @overload + def __init__( + self, + *, + blob_path: Optional[str] = ..., + channel_layout: VoiceRecordingChannelLayout, + channels: int, + conversation_id: str, + duration_ms: int, + format: str, + sample_rate: int + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceResponse(_Model): + audio: Optional[VoiceResponseAudio] + completed_at: Optional[datetime] + conversation_id: str + created_at: Optional[datetime] + id: str + max_output_tokens: Optional[Union[int, Literal["inf"]]] + object: Literal["response"] + output: Optional[list[VoiceConversationItem]] + output_modalities: Optional[list[Literal["text", "audio"]]] + status: Union[str, VoiceResponseStatus] + status_details: Optional[RealtimeResponseStatusDetails] + temperature: Optional[float] + usage: Optional[RealtimeResponseUsage] + + @overload + def __init__( + self, + *, + audio: Optional[VoiceResponseAudio] = ..., + completed_at: Optional[datetime] = ..., + conversation_id: str, + created_at: Optional[datetime] = ..., + id: str, + max_output_tokens: Optional[Union[int, Literal[inf]]] = ..., + output: Optional[list[VoiceConversationItem]] = ..., + output_modalities: Optional[list[Literal[text, audio]]] = ..., + status: Union[str, VoiceResponseStatus], + status_details: Optional[RealtimeResponseStatusDetails] = ..., + temperature: Optional[float] = ..., + usage: Optional[RealtimeResponseUsage] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceResponseAudio(_Model): + output: Optional[VoiceResponseAudioOutput] + + @overload + def __init__( + self, + *, + output: Optional[VoiceResponseAudioOutput] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceResponseAudioOutput(_Model): + format: Optional[RealtimeAudioFormats] + voice: Optional[VoiceResponseVoice] + + @overload + def __init__( + self, + *, + format: Optional[RealtimeAudioFormats] = ..., + voice: Optional[VoiceResponseVoice] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceResponseStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta): + CANCELLED = "cancelled" + COMPLETED = "completed" + FAILED = "failed" + INCOMPLETE = "incomplete" + IN_PROGRESS = "in_progress" + + + class azure.ai.voiceagents.models.VoiceResponseVoice(_Model): + name: str + type: str + + @overload + def __init__( + self, + *, + name: str, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceSystemMessageItem(RealtimeConversationItemMessageSystem): + content: list[RealtimeConversationItemMessageSystemContent] + created_at: Optional[datetime] + id: str + object: str + response_id: Optional[str] + role: Union[str, azure.ai.voiceagents.models.SYSTEM] + status: Union[str, str, str] + type: str + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageSystemContent], + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + response_id: Optional[str] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceSystemTool(_Model): + description: Optional[str] + name: Union[str, VoiceSystemToolName] + type: Literal["system"] + + @overload + def __init__( + self, + *, + description: Optional[str] = ..., + name: Union[str, VoiceSystemToolName] + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceSystemToolName(str, Enum, metaclass=CaseInsensitiveEnumMeta): + END_CONVERSATION = "end_conversation" + + + class azure.ai.voiceagents.models.VoiceToolboxTool(_Model): + toolbox_name: str + toolbox_version: str + type: Literal["toolbox"] + + @overload + def __init__( + self, + *, + toolbox_name: str, + toolbox_version: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceTurnDetection(_Model): + type: str + + @overload + def __init__( + self, + *, + type: str + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + + class azure.ai.voiceagents.models.VoiceTurnDetectionEagerness(str, Enum, metaclass=CaseInsensitiveEnumMeta): + AUTO = "auto" + HIGH = "high" + LOW = "low" + MEDIUM = "medium" + + + class azure.ai.voiceagents.models.VoiceTurnDetectionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + SEMANTIC_VAD = "semantic_vad" + SERVER_VAD = "server_vad" + + + class azure.ai.voiceagents.models.VoiceUserMessageItem(RealtimeConversationItemMessageUser): + content: list[RealtimeConversationItemMessageUserContent] + created_at: Optional[datetime] + id: str + object: str + response_id: Optional[str] + role: Union[str, azure.ai.voiceagents.models.USER] + status: Union[str, str, str] + type: str + + @overload + def __init__( + self, + *, + content: list[RealtimeConversationItemMessageUserContent], + created_at: Optional[datetime] = ..., + id: Optional[str] = ..., + object: Optional[Literal[item]] = ..., + response_id: Optional[str] = ..., + status: Optional[Literal[completed, incomplete, in_progress]] = ... + ) -> None: ... + + @overload + def __init__(self, mapping: Mapping[str, Any]) -> None: ... + + +namespace azure.ai.voiceagents.operations + + class azure.ai.voiceagents.operations.AgentEndpointConversationsOperations: + + def __init__( + self, + *args, + **kwargs + ) -> None: ... + + @distributed_trace + def delete_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceDeletedConversation: ... + + @distributed_trace + def get_agent_conversation( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceConversation: ... + + @distributed_trace + def get_agent_conversation_audio( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceRecordingResponse: ... + + @distributed_trace + def get_agent_conversation_audio_content( + self, + agent_name: str, + conversation_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> Iterator[bytes]: ... + + @distributed_trace + def get_agent_conversation_item( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceConversationItem: ... + + @distributed_trace + def get_agent_conversation_item_audio( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceItemAudioResponse: ... + + @distributed_trace + def get_agent_conversation_item_audio_content( + self, + agent_name: str, + conversation_id: str, + item_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> Iterator[bytes]: ... + + @distributed_trace + def get_agent_conversation_response( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceResponse: ... + + @distributed_trace + def list_agent_conversation_items( + self, + agent_name: str, + conversation_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> ItemPaged[VoiceConversationItem]: ... + + @distributed_trace + def list_agent_conversation_response_items( + self, + agent_name: str, + conversation_id: str, + response_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> ItemPaged[VoiceConversationItem]: ... + + @distributed_trace + def list_agent_conversation_responses( + self, + agent_name: str, + conversation_id: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> ItemPaged[VoiceResponse]: ... + + + class azure.ai.voiceagents.operations.VoiceAgentsOperations: + + def __init__( + self, + *args, + **kwargs + ) -> None: ... + + @overload + def create_voice_agent( + self, + *, + agent_card: Optional[AgentCard] = ..., + agent_endpoint: Optional[AgentEndpointConfig] = ..., + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + name: str, + state: Optional[Union[str, AgentState]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def create_voice_agent( + self, + body: CreateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def create_voice_agent( + self, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def create_voice_agent_version( + self, + agent_name: str, + *, + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @overload + def create_voice_agent_version( + self, + agent_name: str, + body: CreateVoiceAgentVersionRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @overload + def create_voice_agent_version( + self, + agent_name: str, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @distributed_trace + def delete_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> DeleteAgentResponse: ... + + @distributed_trace + def delete_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> DeleteAgentVersionResponse: ... + + @distributed_trace + def disable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> None: ... + + @distributed_trace + def enable_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> None: ... + + @overload + def generate_voice_agent( + self, + *, + agent_type: Union[str, VoiceAgentType], + content_type: str = "application/json", + description: Optional[str] = ..., + draft: Optional[bool] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + goal: str, + model: str, + model_type: Union[str, VoiceModelType], + name: str, + tools: Optional[list[VoiceAgentTool]] = ..., + use_case: Union[str, VoiceAgentUseCase], + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def generate_voice_agent( + self, + body: GenerateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def generate_voice_agent( + self, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @distributed_trace + def get_voice_agent( + self, + agent_name: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @distributed_trace + def get_voice_agent_version( + self, + agent_name: str, + agent_version: str, + *, + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentVersionObject: ... + + @distributed_trace + def list_voice_agent_versions( + self, + agent_name: str, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + include_drafts: Optional[bool] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> ItemPaged[VoiceAgentVersionObject]: ... + + @distributed_trace + def list_voice_agents( + self, + *, + before: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + limit: Optional[int] = ..., + order: Optional[Union[str, PageOrder]] = ..., + **kwargs: Any + ) -> ItemPaged[VoiceAgentObject]: ... + + @overload + def update_voice_agent( + self, + agent_name: str, + *, + blueprint_reference: Optional[AgentBlueprintReference] = ..., + content_type: str = "application/json", + definition: VoiceAgentDefinition, + description: Optional[str] = ..., + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + metadata: Optional[dict[str, str]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def update_voice_agent( + self, + agent_name: str, + body: UpdateVoiceAgentRequest, + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + @overload + def update_voice_agent( + self, + agent_name: str, + body: IO[bytes], + *, + content_type: str = "application/json", + foundry_features: Optional[Literal[AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW]] = ..., + **kwargs: Any + ) -> VoiceAgentObject: ... + + +namespace azure.ai.voiceagents.types + + class azure.ai.voiceagents.types.A2AProtocolConfiguration(TypedDict, total=False): + + + class azure.ai.voiceagents.types.ActivityProtocolConfiguration(TypedDict, total=False): + key "enable_m365_public_endpoint": bool + enable_m365_public_endpoint: bool + + + class azure.ai.voiceagents.types.AgentBlueprintReference(TypedDict, total=False): + key "blueprint_id": Required[str] + key "type": Required[Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT]] + blueprint_id: str + type: Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT] + + + class azure.ai.voiceagents.types.AgentBlueprintReferenceType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + MANAGED_AGENT_IDENTITY_BLUEPRINT = "ManagedAgentIdentityBlueprint" + + + class azure.ai.voiceagents.types.AgentCard(TypedDict, total=False): + key "description": str + key "skills": Required[list[AgentCardSkill]] + key "version": Required[str] + description: str + skills: list[AgentCardSkill] + version: str + + + class azure.ai.voiceagents.types.AgentCardSkill(TypedDict, total=False): + key "description": str + key "id": Required[str] + key "name": Required[str] + description: str + examples: list[str] + id: str + name: str + tags: list[str] + + + class azure.ai.voiceagents.types.AgentEndpointAuthorizationSchemeType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + BOT_SERVICE = "BotService" + BOT_SERVICE_RBAC = "BotServiceRbac" + BOT_SERVICE_TENANT = "BotServiceTenant" + ENTRA = "Entra" + + + class azure.ai.voiceagents.types.AgentEndpointConfig(TypedDict, total=False): + key "protocol_configuration": ForwardRef('ProtocolConfiguration', module='types') + key "version_selector": ForwardRef('VersionSelector', module='types') + authorization_schemes: list[AgentEndpointAuthorizationScheme] + protocol_configuration: ProtocolConfiguration + version_selector: VersionSelector + + + class azure.ai.voiceagents.types.AzureVoice(TypedDict, total=False): + key "locale": str + key "name": Required[str] + key "pitch": str + key "rate": str + key "style": str + key "type": Required[Union[str, AzureVoiceType]] + locale: str + name: str + pitch: str + rate: str + style: str + type: Union[str, AzureVoiceType] + + + class azure.ai.voiceagents.types.BotServiceAuthorizationScheme(TypedDict, total=False): + key "type": Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE]] + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE] + + + class azure.ai.voiceagents.types.BotServiceRbacAuthorizationScheme(TypedDict, total=False): + key "type": Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC]] + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_RBAC] + + + class azure.ai.voiceagents.types.BotServiceTenantAuthorizationScheme(TypedDict, total=False): + key "type": Required[Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT]] + type: Literal[AgentEndpointAuthorizationSchemeType.BOT_SERVICE_TENANT] + + + class azure.ai.voiceagents.types.CreateVoiceAgentRequest(TypedDict, total=False): + key "agent_card": ForwardRef('AgentCard', module='types') + key "agent_endpoint": ForwardRef('AgentEndpointConfig', module='types') + key "blueprint_reference": ForwardRef('AgentBlueprintReference', module='types') + key "definition": Required[VoiceAgentDefinition] + key "description": str + key "draft": bool + key "name": Required[str] + key "state": Union[str, AgentState] + agent_card: AgentCard + agent_endpoint: AgentEndpointConfig + blueprint_reference: AgentBlueprintReference + definition: VoiceAgentDefinition + description: str + draft: bool + metadata: dict[str, str] + name: str + state: Union[str, AgentState] + + + class azure.ai.voiceagents.types.CreateVoiceAgentVersionRequest(TypedDict, total=False): + key "blueprint_reference": ForwardRef('AgentBlueprintReference', module='types') + key "definition": Required[VoiceAgentDefinition] + key "description": str + key "draft": bool + blueprint_reference: AgentBlueprintReference + definition: VoiceAgentDefinition + description: str + draft: bool + metadata: dict[str, str] + + + class azure.ai.voiceagents.types.EntraAuthorizationScheme(TypedDict, total=False): + key "type": Required[Literal[AgentEndpointAuthorizationSchemeType.ENTRA]] + type: Literal[AgentEndpointAuthorizationSchemeType.ENTRA] + + + class azure.ai.voiceagents.types.FixedRatioVersionSelectionRule(TypedDict, total=False): + key "agent_version": Required[str] + key "traffic_percentage": Required[int] + key "type": Required[Literal[VersionSelectorType.FIXED_RATIO]] + agent_version: str + traffic_percentage: int + type: Literal[VersionSelectorType.FIXED_RATIO] + + + class azure.ai.voiceagents.types.FunctionTool(TypedDict, total=False): + key "defer_loading": bool + key "description": Optional[str] + key "name": Required[str] + key "parameters": Required[Optional[dict[str, Any]]] + key "strict": Required[Optional[bool]] + key "type": Required[Literal[ToolType.FUNCTION]] + defer_loading: bool + description: str + name: str + parameters: dict[str, Any] + strict: bool + type: Literal[ToolType.FUNCTION] + + + class azure.ai.voiceagents.types.GenerateVoiceAgentRequest(TypedDict, total=False): + key "agent_type": Required[Union[str, VoiceAgentType]] + key "description": str + key "draft": bool + key "goal": Required[str] + key "model": Required[str] + key "model_type": Required[Union[str, VoiceModelType]] + key "name": Required[str] + key "use_case": Required[Union[str, VoiceAgentUseCase]] + agent_type: Union[str, VoiceAgentType] + description: str + draft: bool + goal: str + model: str + model_type: Union[str, VoiceModelType] + name: str + tools: list[VoiceAgentTool] + use_case: Union[str, VoiceAgentUseCase] + + + class azure.ai.voiceagents.types.InvocationsProtocolConfiguration(TypedDict, total=False): + + + class azure.ai.voiceagents.types.InvocationsWsProtocolConfiguration(TypedDict, total=False): + + + class azure.ai.voiceagents.types.MCPTool(TypedDict, total=False): + key "allowed_tools": Optional[Union[list[str], MCPToolFilter]] + key "authorization": str + key "connector_id": Literal["connector_dropbox", "connector_gmail", "connector_googlecalendar", "connector_googledrive", "connector_microsoftteams", "connector_outlookcalendar", "connector_outlookemail", "connector_sharepoint"] + key "defer_loading": bool + key "headers": Optional[dict[str, str]] + key "project_connection_id": str + key "require_approval": Optional[Union[MCPToolRequireApproval, Literal["always"], Literal["never"]]] + key "server_description": str + key "server_label": Required[str] + key "server_url": str + key "tunnel_id": str + key "type": Required[Literal[ToolType.MCP]] + allowed_tools: Union[list[str], MCPToolFilter] + authorization: str + connector_id: Literal[connector_dropbox, connector_gmail, connector_googlecalendar, connector_googledrive, connector_microsoftteams, + defer_loading: bool + headers: dict[str, str] + project_connection_id: str + require_approval: Union[MCPToolRequireApproval, Literal[always], Literal[never]] + server_description: str + server_label: str + server_url: str + tool_configs: dict[str, ToolConfig] + tunnel_id: str + type: Literal[ToolType.MCP] + + + class azure.ai.voiceagents.types.MCPToolFilter(TypedDict, total=False): + key "read_only": bool + read_only: bool + tool_names: list[str] + + + class azure.ai.voiceagents.types.MCPToolRequireApproval(TypedDict, total=False): + key "always": ForwardRef('MCPToolFilter', module='types') + key "never": ForwardRef('MCPToolFilter', module='types') + always: MCPToolFilter + never: MCPToolFilter + + + class azure.ai.voiceagents.types.ManagedAgentIdentityBlueprintReference(TypedDict, total=False): + key "blueprint_id": Required[str] + key "type": Required[Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT]] + blueprint_id: str + type: Literal[AgentBlueprintReferenceType.MANAGED_AGENT_IDENTITY_BLUEPRINT] + + + class azure.ai.voiceagents.types.McpProtocolConfiguration(TypedDict, total=False): + + + class azure.ai.voiceagents.types.ProtocolConfiguration(TypedDict, total=False): + key "a2a": ForwardRef('A2AProtocolConfiguration', module='types') + key "activity": ForwardRef('ActivityProtocolConfiguration', module='types') + key "invocations": ForwardRef('InvocationsProtocolConfiguration', module='types') + key "invocations_ws": ForwardRef('InvocationsWsProtocolConfiguration', module='types') + key "mcp": ForwardRef('McpProtocolConfiguration', module='types') + key "responses": ForwardRef('ResponsesProtocolConfiguration', module='types') + a2_a: A2AProtocolConfiguration + activity: ActivityProtocolConfiguration + invocations: InvocationsProtocolConfiguration + invocations_ws: InvocationsWsProtocolConfiguration + mcp: McpProtocolConfiguration + responses: ResponsesProtocolConfiguration + + + class azure.ai.voiceagents.types.RaiConfig(TypedDict, total=False): + key "rai_policy_name": Required[str] + rai_policy_name: str + + + class azure.ai.voiceagents.types.ResponsesProtocolConfiguration(TypedDict, total=False): + + + class azure.ai.voiceagents.types.SemanticVadTurnDetection(TypedDict, total=False): + key "eagerness": Union[str, VoiceTurnDetectionEagerness] + key "type": Required[Literal[VoiceTurnDetectionType.SEMANTIC_VAD]] + eagerness: Union[str, VoiceTurnDetectionEagerness] + type: Literal[VoiceTurnDetectionType.SEMANTIC_VAD] + + + class azure.ai.voiceagents.types.ServerVadTurnDetection(TypedDict, total=False): + key "create_response": bool + key "prefix_padding_ms": int + key "silence_duration_ms": int + key "threshold": float + key "type": Required[Literal[VoiceTurnDetectionType.SERVER_VAD]] + create_response: bool + prefix_padding_ms: int + silence_duration_ms: int + threshold: float + type: Literal[VoiceTurnDetectionType.SERVER_VAD] + + + class azure.ai.voiceagents.types.StructuredInputDefinition(TypedDict, total=False): + key "default_value": Any + key "description": str + key "required": bool + default_value: Any + description: str + required: bool + schema: dict[str, Any] + + + class azure.ai.voiceagents.types.ToolConfig(TypedDict, total=False): + key "additional_search_text": str + key "pin": bool + additional_search_text: str + pin: bool + + + class azure.ai.voiceagents.types.ToolType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + A2_A_PREVIEW = "a2a_preview" + APPLY_PATCH = "apply_patch" + AZURE_AI_SEARCH = "azure_ai_search" + AZURE_FUNCTION = "azure_function" + BING_CUSTOM_SEARCH_PREVIEW = "bing_custom_search_preview" + BING_GROUNDING = "bing_grounding" + BROWSER_AUTOMATION_PREVIEW = "browser_automation_preview" + CAPTURE_STRUCTURED_OUTPUTS = "capture_structured_outputs" + CODE_INTERPRETER = "code_interpreter" + COMPUTER = "computer" + COMPUTER_USE_PREVIEW = "computer_use_preview" + CUSTOM = "custom" + FABRIC_DATAAGENT_PREVIEW = "fabric_dataagent_preview" + FABRIC_IQ_PREVIEW = "fabric_iq_preview" + FILE_SEARCH = "file_search" + FUNCTION = "function" + IMAGE_GENERATION = "image_generation" + LOCAL_SHELL = "local_shell" + MCP = "mcp" + MEMORY_SEARCH_PREVIEW = "memory_search_preview" + NAMESPACE = "namespace" + OPENAPI = "openapi" + SHAREPOINT_GROUNDING_PREVIEW = "sharepoint_grounding_preview" + SHELL = "shell" + TOOLBOX_SEARCH_PREVIEW = "toolbox_search_preview" + TOOL_SEARCH = "tool_search" + WEB_IQ_PREVIEW = "web_iq_preview" + WEB_SEARCH = "web_search" + WEB_SEARCH_PREVIEW = "web_search_preview" + WORK_IQ_PREVIEW = "work_iq_preview" + + + class azure.ai.voiceagents.types.UpdateVoiceAgentRequest(TypedDict, total=False): + key "blueprint_reference": ForwardRef('AgentBlueprintReference', module='types') + key "definition": Required[VoiceAgentDefinition] + key "description": str + blueprint_reference: AgentBlueprintReference + definition: VoiceAgentDefinition + description: str + metadata: dict[str, str] + + + class azure.ai.voiceagents.types.VersionSelectionRule(TypedDict, total=False): + key "agent_version": Required[str] + key "traffic_percentage": Required[int] + key "type": Required[Literal[VersionSelectorType.FIXED_RATIO]] + agent_version: str + traffic_percentage: int + type: Literal[VersionSelectorType.FIXED_RATIO] + + + class azure.ai.voiceagents.types.VersionSelector(TypedDict, total=False): + key "version_selection_rules": Required[list[VersionSelectionRule]] + version_selection_rules: list[VersionSelectionRule] + + + class azure.ai.voiceagents.types.VersionSelectorType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + FIXED_RATIO = "FixedRatio" + + + class azure.ai.voiceagents.types.VoiceAgentDefinition(TypedDict, total=False): + key "audio": ForwardRef('VoiceAudioConfig', module='types') + key "avatar": ForwardRef('VoiceAvatarConfig', module='types') + key "instructions": Optional[str] + key "kind": Required[Literal["voice"]] + key "model": Required[str] + key "model_type": Required[Union[str, VoiceModelType]] + key "rai_config": ForwardRef('RaiConfig', module='types') + key "store": bool + audio: VoiceAudioConfig + avatar: VoiceAvatarConfig + instructions: str + kind: Literal[voice] + model: str + model_type: Union[str, VoiceModelType] + output_modalities: list[Union[str, VoiceOutputModality]] + rai_config: RaiConfig + store: bool + structured_inputs: dict[str, StructuredInputDefinition] + tools: list[VoiceAgentTool] + + + class azure.ai.voiceagents.types.VoiceAudioConfig(TypedDict, total=False): + key "input": ForwardRef('VoiceAudioInputConfig', module='types') + key "output": ForwardRef('VoiceAudioOutputConfig', module='types') + input: VoiceAudioInputConfig + output: VoiceAudioOutputConfig + + + class azure.ai.voiceagents.types.VoiceAudioFormat(TypedDict, total=False): + key "rate": int + key "type": Required[Union[str, VoiceAudioFormatType]] + rate: int + type: Union[str, VoiceAudioFormatType] + + + class azure.ai.voiceagents.types.VoiceAudioInputConfig(TypedDict, total=False): + key "format": ForwardRef('VoiceAudioFormat', module='types') + key "noise_reduction": Optional[VoiceNoiseReduction] + key "transcription": ForwardRef('VoiceInputTranscription', module='types') + key "turn_detection": Optional[VoiceTurnDetection] + format: VoiceAudioFormat + noise_reduction: VoiceNoiseReduction + transcription: VoiceInputTranscription + turn_detection: VoiceTurnDetection + + + class azure.ai.voiceagents.types.VoiceAudioOutputConfig(TypedDict, total=False): + key "format": ForwardRef('VoiceAudioFormat', module='types') + key "speed": float + key "voice": Union[str, AzureVoice] + format: VoiceAudioFormat + speed: float + voice: Union[str, AzureVoice] + + + class azure.ai.voiceagents.types.VoiceAvatarConfig(TypedDict, total=False): + key "character": Required[str] + key "customized": bool + key "output_protocol": Union[str, VoiceAvatarOutputProtocol] + key "style": str + key "type": Required[Union[str, VoiceAvatarType]] + character: str + customized: bool + output_protocol: Union[str, VoiceAvatarOutputProtocol] + style: str + type: Union[str, VoiceAvatarType] + + + class azure.ai.voiceagents.types.VoiceInputTranscription(TypedDict, total=False): + key "language": str + key "model": str + key "prompt": str + language: str + model: str + prompt: str + + + class azure.ai.voiceagents.types.VoiceNoiseReduction(TypedDict, total=False): + key "type": Required[Union[str, VoiceNoiseReductionType]] + type: Union[str, VoiceNoiseReductionType] + + + class azure.ai.voiceagents.types.VoiceSystemTool(TypedDict, total=False): + key "description": str + key "name": Required[Union[str, VoiceSystemToolName]] + key "type": Required[Literal["system"]] + description: str + name: Union[str, VoiceSystemToolName] + type: Literal[system] + + + class azure.ai.voiceagents.types.VoiceToolboxTool(TypedDict, total=False): + key "toolbox_name": Required[str] + key "toolbox_version": Required[str] + key "type": Required[Literal["toolbox"]] + toolbox_name: str + toolbox_version: str + type: Literal[toolbox] + + + class azure.ai.voiceagents.types.VoiceTurnDetectionType(str, Enum, metaclass=CaseInsensitiveEnumMeta): + SEMANTIC_VAD = "semantic_vad" + SERVER_VAD = "server_vad" + + +``` \ No newline at end of file diff --git a/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml b/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml new file mode 100644 index 000000000000..3a26e3d14792 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml @@ -0,0 +1,3 @@ +apiMdSha256: 0b7b4c2f10df7983b8c58697ab08136301c4c16ba9cc2dbef482f55f7a78cfef +parserVersion: 0.3.30 +pythonVersion: 3.13.2 diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py index 2d55885f8538..96e7f7cd97e3 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py @@ -7,7 +7,7 @@ Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize """ -from typing import Any, Optional +from typing import Optional from ._client import VoiceAgentsClient as _GeneratedVoiceAgentsClient from ._realtime import ( @@ -18,15 +18,14 @@ class VoiceAgentsClient(_GeneratedVoiceAgentsClient): - """VoiceAgentsClient with realtime (WebSocket) streaming support. + """VoiceAgentsClient with an interface-only realtime namespace. - Adds the :attr:`realtime` namespace on top of the generated HTTP client, - following the OpenAI ``client.realtime.connect(...)`` pattern. + Adds the :attr:`realtime` namespace on top of the generated HTTP client. + Realtime connections are not yet available and ``connect()`` currently raises + :class:`NotImplementedError`. """ - def __init__(self, endpoint: str, credential: Any, **kwargs: Any) -> None: - super().__init__(endpoint, credential, **kwargs) - self._realtime: Optional[AsyncRealtime] = None + _realtime: Optional[AsyncRealtime] = None @property def realtime(self) -> AsyncRealtime: diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py index e04aac4012a0..03f1c66fe89b 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py @@ -340,7 +340,7 @@ async def enter(self) -> AsyncRealtimeConnection: except BaseException: await session.close() raise - self._connection = AsyncRealtimeConnection(connection, session) + self._connection = AsyncRealtimeConnection(connection, session) # type: ignore[arg-type] return self._connection async def __aexit__(self, *exc_details: Any) -> None: @@ -394,7 +394,8 @@ def connect( :keyword str agent_name: The name of the voice agent to connect to. :keyword foundry_features: Preview opt-in value for the ``Foundry-Features`` header, - e.g. ``"VOICE_AGENTS_V1_PREVIEW"``. Default value is None. + e.g. ``AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW`` or + ``"VoiceAgents=V1Preview"``. Default value is None. :paramtype foundry_features: str or None :keyword extra_query: Additional query-string parameters for the handshake. :paramtype extra_query: Mapping[str, str] or None diff --git a/sdk/voiceagents/azure-ai-voiceagents/cspell.json b/sdk/voiceagents/azure-ai-voiceagents/cspell.json new file mode 100644 index 000000000000..b153514abeeb --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/cspell.json @@ -0,0 +1,21 @@ +{ + "ignoreWords": [ + "BYOM", + "byom", + "BYOS", + "byos", + "deser", + "MCPHTTP", + "mcphttp", + "PCMA", + "pcma", + "Pcma", + "PCMU", + "pcmu", + "Pcmu", + "WEBRTC", + "webrtc" + ], + "ignorePaths": [], + "words": [] +} diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/README.md b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md index d7c888e18835..bbc778c8c0d0 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md @@ -35,19 +35,19 @@ and audio recordings). | File | Description | | ---- | ----------- | -| [sample_create_and_manage_voice_agent.py](sample_create_and_manage_voice_agent.py) | Create (with a voice/audio config and conversation storage enabled), get, list, update, disable/enable, and delete a voice agent. | -| [sample_create_voice_agent_with_tools.py](sample_create_voice_agent_with_tools.py) | Create an agent with tools (`function`, `system`, `mcp`, `toolbox`), input-audio config (turn detection + transcription), and bring-your-own-model (`self_deployed`). | -| [sample_generate_voice_agent.py](sample_generate_voice_agent.py) | Guided authoring: generate and create a voice agent from a persona, use case, and a natural-language goal. | -| [sample_manage_voice_agent_versions.py](sample_manage_voice_agent_versions.py) | Create and list immutable versions of a voice agent, including draft versions. | -| [async_samples/sample_create_and_manage_voice_agent_async.py](async_samples/sample_create_and_manage_voice_agent_async.py) | Async version of the create/manage lifecycle. | +| [sample_create_and_manage_voice_agent.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py) | Create (with a voice/audio config and conversation storage enabled), get, list, update, disable/enable, and delete a voice agent. | +| [sample_create_voice_agent_with_tools.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py) | Create an agent with tools (`function`, `system`, `mcp`, `toolbox`), input-audio config (turn detection + transcription), and bring-your-own-model (`self_deployed`). | +| [sample_generate_voice_agent.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py) | Guided authoring: generate and create a voice agent from a persona, use case, and a natural-language goal. | +| [sample_manage_voice_agent_versions.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py) | Create and list immutable versions of a voice agent, including draft versions. | +| [async_samples/sample_create_and_manage_voice_agent_async.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py) | Async version of the create/manage lifecycle. | **Read conversations** — these need an existing agent and a conversation id from a completed live session (see [Getting a conversation id](#getting-a-conversation-id)). | File | Description | | ---- | ----------- | -| [sample_read_conversation.py](sample_read_conversation.py) | Read a persisted conversation, its responses (and per-response items), and its items (with single get by id). | -| [sample_read_conversation_audio.py](sample_read_conversation_audio.py) | Read the merged whole-call recording and a single turn's audio, streaming each to a WAV file. | +| [sample_read_conversation.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py) | Read a persisted conversation, its responses (and per-response items), and its items (with single get by id). | +| [sample_read_conversation_audio.py](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py) | Read the merged whole-call recording and a single turn's audio, streaming each to a WAV file. | ## Prerequisites @@ -119,4 +119,6 @@ python sample_create_and_manage_voice_agent.py > [!NOTE] > The management samples create and delete **real resources** in your project and -> may incur cost. Each sample cleans up the agent it creates. +> may incur cost. Each sample deletes the agent it creates on the success path +> only; if a sample fails partway through, it may leave the agent behind, so +> check your project and delete any leftover agents manually. diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py index 0ccfd0386fe2..f4496c2c891b 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py @@ -30,6 +30,7 @@ import asyncio import os +from typing import Final from azure.identity.aio import DefaultAzureCredential @@ -41,10 +42,10 @@ async def create_and_manage_voice_agent() -> None: endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") agent_name = "sample-voice-agent-async" - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW credential = DefaultAzureCredential() - async with VoiceAgentsClient(endpoint=endpoint, credential=credential) as client: + async with credential, VoiceAgentsClient(endpoint=endpoint, credential=credential) as client: created = await client.voice_agents.create_voice_agent( name=agent_name, definition=VoiceAgentDefinition( @@ -69,8 +70,6 @@ async def create_and_manage_voice_agent() -> None: await client.voice_agents.delete_voice_agent(agent_name, foundry_features=preview) print(f"Deleted voice agent: {agent_name}") - await credential.close() - if __name__ == "__main__": asyncio.run(create_and_manage_voice_agent()) diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py index 982ccda9a250..98e3c3c5e34e 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_and_manage_voice_agent.py @@ -29,6 +29,7 @@ """ import os +from typing import Final from azure.identity import DefaultAzureCredential @@ -49,7 +50,7 @@ def create_and_manage_voice_agent() -> None: agent_name = "sample-voice-agent" # Voice agent preview operations require this feature-flag opt-in. - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW definition = VoiceAgentDefinition( # `managed` uses a service-hosted model; use `self_deployed` with a Foundry @@ -83,13 +84,17 @@ def create_and_manage_voice_agent() -> None: print(f" - {item.name}") # Update the agent. Each update that changes the definition produces a new version. + # Preserve the audio and output-modality configuration from the original + # definition so the new version keeps the same voice behavior. updated = client.voice_agents.update_voice_agent( agent_name, definition=VoiceAgentDefinition( model_type="managed", model=model, instructions="You are a friendly voice assistant. Always greet the caller warmly.", - store=True, + audio=definition.audio, + output_modalities=definition.output_modalities, + store=definition.store, ), description="Updated instructions.", foundry_features=preview, diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py index 9be0959db9a8..aea6b90b8c7e 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py @@ -39,6 +39,7 @@ """ import os +from typing import Final from azure.identity import DefaultAzureCredential @@ -73,7 +74,7 @@ def create_voice_agent_with_tools() -> None: agent_name = "sample-voice-agent-with-tools" # Voice agent preview operations require this feature-flag opt-in. - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW # A client-executed tool: the service forwards the function call to your app, # and your app returns the result over the live session. @@ -94,7 +95,7 @@ def create_voice_agent_with_tools() -> None: # An MCP tool is executed by the service against a remote MCP server you own. # It references an external server, so it is constructed here for illustration # and not attached below. Provide one of server_url, connector_id, or tunnel_id. - example_mcp_tool = MCPTool( + _example_mcp_tool = MCPTool( server_label="my-mcp-server", server_url="https://example.com/mcp", require_approval="never", @@ -102,7 +103,7 @@ def create_voice_agent_with_tools() -> None: # A toolbox tool references a versioned Foundry toolbox you have created. It is # constructed here for illustration; attach it only if the toolbox exists. - example_toolbox_tool = VoiceToolboxTool(toolbox_name="my-toolbox", toolbox_version="1") + _example_toolbox_tool = VoiceToolboxTool(toolbox_name="my-toolbox", toolbox_version="1") definition = VoiceAgentDefinition( model_type=model_type, @@ -128,7 +129,7 @@ def create_voice_agent_with_tools() -> None: output=VoiceAudioOutputConfig(voice=AzureVoice(type="azure-standard", name="en-US-AvaNeural")), ), output_modalities=[VoiceOutputModality.AUDIO], - # Attach the self-contained tools. `example_mcp_tool` and `example_toolbox_tool` + # Attach the self-contained tools. `_example_mcp_tool` and `_example_toolbox_tool` # reference external resources you must own, so they are left out here. tools=[get_weather, end_call], store=True, diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py index 961c666f6cfc..42639e720bd8 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_generate_voice_agent.py @@ -30,6 +30,7 @@ """ import os +from typing import Final from azure.identity import DefaultAzureCredential @@ -40,7 +41,7 @@ def generate_voice_agent() -> None: endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: agent = client.voice_agents.generate_voice_agent( diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py index 3d82c269d4a5..fbbe7b3b8462 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_manage_voice_agent_versions.py @@ -29,6 +29,7 @@ """ import os +from typing import Final from azure.identity import DefaultAzureCredential @@ -40,7 +41,7 @@ def manage_voice_agent_versions() -> None: endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] model = os.environ.get("AZURE_VOICE_AGENTS_MODEL", "gpt-realtime") agent_name = "sample-versioned-voice-agent" - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW def definition(instructions: str) -> VoiceAgentDefinition: # Each version differs only by its instructions; the rest is identical. diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py index d753c8f02290..356eb661eadf 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation.py @@ -30,6 +30,7 @@ """ import os +from typing import Final from azure.core.exceptions import HttpResponseError from azure.identity import DefaultAzureCredential @@ -42,7 +43,7 @@ def read_conversation() -> None: endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] agent_name = os.environ["AZURE_VOICE_AGENTS_AGENT_NAME"] conversation_id = os.environ["AZURE_VOICE_AGENTS_CONVERSATION_ID"] - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: conversations = client.agent_endpoint_conversations diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py index a51155b9e328..80fa0b02b2e1 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py @@ -33,6 +33,7 @@ """ import os +from typing import Final from azure.core.exceptions import HttpResponseError from azure.identity import DefaultAzureCredential @@ -45,7 +46,7 @@ def read_conversation_audio() -> None: endpoint = os.environ["AZURE_VOICE_AGENTS_ENDPOINT"] agent_name = os.environ["AZURE_VOICE_AGENTS_AGENT_NAME"] conversation_id = os.environ["AZURE_VOICE_AGENTS_CONVERSATION_ID"] - preview = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW + preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW with VoiceAgentsClient(endpoint=endpoint, credential=DefaultAzureCredential()) as client: conversations = client.agent_endpoint_conversations @@ -93,9 +94,11 @@ def read_first_item_audio(conversations, agent_name, conversation_id, preview) - metadata = conversations.get_agent_conversation_item_audio( agent_name, conversation_id, item_id, foundry_features=preview ) - except HttpResponseError: - # This item has no persisted audio (e.g. a text-only turn); try the next one. - continue + except HttpResponseError as e: + # A 404 means this item has no persisted audio (for example, a text-only turn). + if e.status_code == 404: + continue + raise print(f"Item {item_id}: role={metadata.role}, duration_ms={metadata.duration_ms}") if metadata.blob_path: From c8e7f7698f874c26ce48795709c5c50f7ae45c8d Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 17:07:22 -0700 Subject: [PATCH 03/12] Regenerate api.md after aio client __init__ removal Removing the __init__ override in aio/_patch.py changed the public API surface (credential now typed AsyncTokenCredential). Regenerated api.md + api.metadata.yml with apistub 0.3.30 to fix the API.md consistency check. --- sdk/voiceagents/azure-ai-voiceagents/api.md | 2 +- sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/api.md b/sdk/voiceagents/azure-ai-voiceagents/api.md index f0da30e24b16..babbe3d1b3af 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/api.md +++ b/sdk/voiceagents/azure-ai-voiceagents/api.md @@ -89,7 +89,7 @@ namespace azure.ai.voiceagents.aio def __init__( self, endpoint: str, - credential: Any, + credential: AsyncTokenCredential, **kwargs: Any ) -> None: ... diff --git a/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml b/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml index 3a26e3d14792..294b611985c6 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml +++ b/sdk/voiceagents/azure-ai-voiceagents/api.metadata.yml @@ -1,3 +1,3 @@ -apiMdSha256: 0b7b4c2f10df7983b8c58697ab08136301c4c16ba9cc2dbef482f55f7a78cfef +apiMdSha256: 2383f8836c558da25dd9cbfad8282c24e0ebb692b992fca0af05ae156e645f05 parserVersion: 0.3.30 pythonVersion: 3.13.2 From ab532dcdac31b7409ca5497aebdcd59a74241ba3 Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 17:35:53 -0700 Subject: [PATCH 04/12] Fix CI: README required sections, pylint unused-import, verifytypes annotations - README.md: add Key concepts, Troubleshooting, Next steps sections (Verify Readmes check) - operations/_operations.py + aio/operations/_operations.py: remove unused urllib.parse import (Pylint W0611) - aio/_realtime.py: annotate AsyncRealtimeConnection sub-resource attrs and ConversationResource.item to improve verifytypes completeness (98.8% -> 99%) --- .../azure-ai-voiceagents/README.md | 28 +++++++++++++++++++ .../azure/ai/voiceagents/aio/_realtime.py | 12 ++++---- .../voiceagents/aio/operations/_operations.py | 1 - .../ai/voiceagents/operations/_operations.py | 1 - 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/README.md b/sdk/voiceagents/azure-ai-voiceagents/README.md index e71e6790de42..8a9af60a3c63 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/README.md @@ -41,6 +41,19 @@ Use the returned token credential to authenticate the client: >>> client = VoiceAgentsClient(endpoint='', credential=DefaultAzureCredential()) ``` +## Key concepts + +- **Voice agent**: A configured agent that handles voice interactions. Use the + `voice_agents` operations on `VoiceAgentsClient` to create, retrieve, update, + list versions of, and delete voice agents. +- **Conversation**: A persisted record of an interaction with a voice agent, + including status, timestamps, and aggregate usage. Use the + `agent_endpoint_conversations` operations to read conversations, transcripts, + and audio. +- **Foundry project endpoint**: Voice agents are accessed through an Azure AI + Foundry project endpoint of the form + `https://.services.ai.azure.com/api/projects/`. + ## Examples ```python @@ -56,6 +69,19 @@ Use the returned token credential to authenticate the client: ``` +## Troubleshooting + +Errors returned by the service are raised as +[`azure.core.exceptions.HttpResponseError`][http_response_error]. The exception's +`status_code` and `response` provide details about the failure. For example, a +missing resource surfaces as a `404` status code, which you can inspect to decide +whether to retry or skip the operation. + +## Next steps + +See the [samples][samples] for more complete examples of creating and managing +voice agents and reading conversation data. + ## Contributing This project welcomes contributions and suggestions. Most contributions require @@ -81,3 +107,5 @@ additional questions or comments. [default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential [pip]: https://pypi.org/project/pip/ [azure_sub]: https://azure.microsoft.com/free/ +[http_response_error]: https://learn.microsoft.com/python/api/azure-core/azure.core.exceptions.httpresponseerror +[samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/voiceagents/azure-ai-voiceagents/samples diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py index 03f1c66fe89b..a729ef1c831e 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py @@ -163,7 +163,7 @@ class ConversationResource(_BaseResource): def __init__(self, connection: "AsyncRealtimeConnection") -> None: super().__init__(connection) - self.item = ConversationItemResource(connection) + self.item: ConversationItemResource = ConversationItemResource(connection) class ResponseResource(_BaseResource): @@ -211,11 +211,11 @@ class AsyncRealtimeConnection: def __init__(self, connection: "ClientWebSocketResponse", session: "ClientSession") -> None: self._connection = connection self._session = session - self.session = SessionResource(self) - self.input_audio_buffer = InputAudioBufferResource(self) - self.output_audio_buffer = OutputAudioBufferResource(self) - self.conversation = ConversationResource(self) - self.response = ResponseResource(self) + self.session: SessionResource = SessionResource(self) + self.input_audio_buffer: InputAudioBufferResource = InputAudioBufferResource(self) + self.output_audio_buffer: OutputAudioBufferResource = OutputAudioBufferResource(self) + self.conversation: ConversationResource = ConversationResource(self) + self.response: ResponseResource = ResponseResource(self) async def __aenter__(self) -> "AsyncRealtimeConnection": return self diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py index 818bb52a667c..d7ec68fa2e2d 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py @@ -10,7 +10,6 @@ from io import IOBase import json from typing import Any, AsyncIterator, Callable, IO, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload -import urllib.parse from azure.core import AsyncPipelineClient from azure.core.async_paging import AsyncItemPaged, AsyncList diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py index 8f63643ca4c2..cad5562c58d4 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py @@ -10,7 +10,6 @@ from io import IOBase import json from typing import Any, Callable, IO, Iterator, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload -import urllib.parse from azure.core import PipelineClient from azure.core.exceptions import ( From e5cbc65cb011e980be5660b08f8c608b84072d04 Mon Sep 17 00:00:00 2001 From: xitzhang Date: Fri, 24 Jul 2026 17:37:23 -0700 Subject: [PATCH 05/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sdk/voiceagents/azure-ai-voiceagents/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml index 5247f5be1ec5..b2eb4023c8ed 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml +++ b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml @@ -14,7 +14,7 @@ name = "azure-ai-voiceagents" authors = [ { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, ] -description = "Microsoft Corporation Azure Ai Voiceagents Client Library for Python" +description = "Microsoft Corporation Azure AI Voice Agents Client Library for Python" license = "MIT" classifiers = [ "Development Status :: 4 - Beta", From d50050d0c773ada91a5abb225c0fc1726fe79977 Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 17:38:09 -0700 Subject: [PATCH 06/12] update changelog --- sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md b/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md index b957b2575b48..6960e66585cb 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md +++ b/sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 1.0.0b1 (1970-01-01) +## 1.0.0b1 (Unreleased) ### Other Changes From b1c49579bf0a5c9e2c965528200ecaa9440f12fb Mon Sep 17 00:00:00 2001 From: xitzhang Date: Fri, 24 Jul 2026 17:38:48 -0700 Subject: [PATCH 07/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- sdk/voiceagents/azure-ai-voiceagents/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/README.md b/sdk/voiceagents/azure-ai-voiceagents/README.md index 8a9af60a3c63..2c4d2a5a4353 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/README.md @@ -1,4 +1,4 @@ -# Azure Ai Voiceagents client library for Python +# Azure AI Voice Agents client library for Python The Azure AI Voice Agents client library lets Python applications create and manage voice agents and read persisted conversation, transcript, and audio data through an Azure AI Foundry project endpoint. From ab1bb95903b3cd4e9e080240eaaa384ee98060b0 Mon Sep 17 00:00:00 2001 From: xitzhang Date: Fri, 24 Jul 2026 17:39:05 -0700 Subject: [PATCH 08/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py index a729ef1c831e..c0dec0f273bc 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py @@ -11,7 +11,7 @@ The connection ergonomics follow the OpenAI Python realtime client so that developers moving between the two libraries get a familiar surface: -* :meth:`VoiceAgentsRealtimeClient.connect` returns an async context manager. +* :meth:`AsyncRealtime.connect` returns an async context manager. * Entering the context yields an :class:`AsyncRealtimeConnection`. * The connection is async-iterable over inbound server events and exposes sub-namespaces (``session``, ``input_audio_buffer``, ``output_audio_buffer``, From 8d2f4b7603f357c3e47f5a7e81af907d90bf0435 Mon Sep 17 00:00:00 2001 From: xitzhang Date: Fri, 24 Jul 2026 17:39:24 -0700 Subject: [PATCH 09/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../samples/sample_read_conversation_audio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py index 80fa0b02b2e1..3c04d436838b 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_read_conversation_audio.py @@ -87,7 +87,7 @@ def read_merged_recording(conversations, agent_name, conversation_id, preview) - def read_first_item_audio(conversations, agent_name, conversation_id, preview) -> None: """Read the audio segment of the first conversation item that has one.""" for item in conversations.list_agent_conversation_items(agent_name, conversation_id, foundry_features=preview): - item_id = getattr(item, "id", None) + item_id = item.get("id") if not item_id: continue try: From a74d4ca03f3ba1baa35471a375897cc7a4febb56 Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 18:29:20 -0700 Subject: [PATCH 10/12] Address PR review comments for azure-ai-voiceagents - Fix README 'Prequisites' typo and replace non-existent resource prerequisite with an Azure AI Foundry project + endpoint (add azure_ai_foundry link) - Reword aio/_realtime.py module docstring to describe the interface-only client.realtime namespace - Reword samples/README realtime NOTE and fix reversed BYOS advice in the 409 troubleshooting row - Reword sample_create_voice_agent_with_tools docstring for client.realtime.connect(...) - Add sync and async HTTP operation tests via mock transport covering routes, Foundry-Features header, serialization, paging, audio streaming, and error mapping --- .../azure-ai-voiceagents/README.md | 5 +- .../azure/ai/voiceagents/aio/_realtime.py | 6 +- .../azure-ai-voiceagents/samples/README.md | 9 +- .../sample_create_voice_agent_with_tools.py | 5 +- .../tests/unit/_mock_transport.py | 146 ++++++++++ .../tests/unit/conftest.py | 14 + .../tests/unit/test_unit_operations.py | 270 ++++++++++++++++++ .../tests/unit/test_unit_operations_async.py | 240 ++++++++++++++++ 8 files changed, 685 insertions(+), 10 deletions(-) create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/conftest.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py create mode 100644 sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py diff --git a/sdk/voiceagents/azure-ai-voiceagents/README.md b/sdk/voiceagents/azure-ai-voiceagents/README.md index 2c4d2a5a4353..b1edde47c4e0 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/README.md @@ -10,11 +10,11 @@ The Azure AI Voice Agents client library lets Python applications create and man python -m pip install azure-ai-voiceagents ``` -#### Prequisites +#### Prerequisites - Python 3.10 or later is required to use this package. - You need an [Azure subscription][azure_sub] to use this package. -- An existing Azure Ai Voiceagents instance. +- An existing [Azure AI Foundry][azure_ai_foundry] project and its endpoint. ### Use with AI tools @@ -107,5 +107,6 @@ additional questions or comments. [default_azure_credential]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/identity/azure-identity#defaultazurecredential [pip]: https://pypi.org/project/pip/ [azure_sub]: https://azure.microsoft.com/free/ +[azure_ai_foundry]: https://learn.microsoft.com/azure/ai-foundry/ [http_response_error]: https://learn.microsoft.com/python/api/azure-core/azure.core.exceptions.httpresponseerror [samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/voiceagents/azure-ai-voiceagents/samples diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py index c0dec0f273bc..a8880f9ed9db 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py @@ -6,8 +6,10 @@ """Hand-written async realtime (WebSocket) streaming client for voice agents. Realtime uses a fundamentally different transport (a persistent WebSocket) -than the request/response HTTP surface, so it is exposed as a **dedicated -standalone client** rather than a member of the HTTP ``VoiceAgentsClient``. +than the request/response HTTP surface. It is exposed as the +``VoiceAgentsClient.realtime`` namespace; the streaming implementation is +interface-only today, so :meth:`AsyncRealtime.connect` raises +``NotImplementedError`` until the transport ships. The connection ergonomics follow the OpenAI Python realtime client so that developers moving between the two libraries get a familiar surface: diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/README.md b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md index bbc778c8c0d0..d0ac6c877074 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/README.md +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/README.md @@ -18,9 +18,10 @@ and audio recordings). > [!NOTE] > These samples cover the **request/response (HTTP)** surface only — > voice-agent management and read-only conversation/audio playback. The -> **live realtime voice session** (the streaming WebSocket call) is established -> through a separate connect operation that is not part of this client library -> today, so it is intentionally not shown here. +> **live realtime voice session** (the streaming WebSocket call) is reached +> through the `client.realtime.connect(...)` namespace, which is exposed as an +> interface today and raises `NotImplementedError` until the streaming +> transport ships, so it is intentionally not shown here. > [!IMPORTANT] > Voice agents are a **gated preview**. Every call opts in with the @@ -114,7 +115,7 @@ python sample_create_and_manage_voice_agent.py | `HttpResponseError` 401 / 403 | Not signed in, or your identity lacks access to the project. Run `az login` and confirm project permissions. | | `ResourceNotFoundError` / 404 on a **management** call (create, list, generate) | The gated preview isn't enabled for your subscription, or isn't served on your project's endpoint/region yet. The request URL and auth are correct; the route just isn't provisioned. Confirm preview enablement and a supported region with your service contact. | | `HttpResponseError` 404 on a **read** sample | The conversation was not persisted (agent ran with `store = false`) or the id is wrong. | -| `HttpResponseError` 409 on the audio sample | The session is still in progress, or the agent has no bring-your-own-storage account configured for audio. | +| `HttpResponseError` 409 on the audio sample | Either the session is still in progress, or the recording lives in your own bring-your-own-storage (BYOS) account — its bytes aren't streamed through the service and must be downloaded directly from the `blob_path` returned by the metadata route. Foundry-managed audio streams normally. | | Model / deployment not found | The `gpt-realtime` default deployment doesn't exist in your project. Set `AZURE_VOICE_AGENTS_MODEL` to a valid realtime deployment name. | > [!NOTE] diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py index aea6b90b8c7e..0a988f101e91 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/sample_create_voice_agent_with_tools.py @@ -20,8 +20,9 @@ agent at your own Foundry model deployment instead of a service-managed model. The tools and audio settings are session defaults baked into the agent; the live - realtime session that actually invokes them is established through a separate - connect operation that is not part of this client library. + realtime session that actually invokes them is reached through the + `client.realtime.connect(...)` namespace, which is exposed as an interface today + and raises `NotImplementedError` until the streaming transport ships. USAGE: python sample_create_voice_agent_with_tools.py diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py new file mode 100644 index 000000000000..42a8e045ac8b --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py @@ -0,0 +1,146 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""In-memory mock transport shared by the HTTP operation unit tests. + +No network is used. A ``handler`` callable maps each outgoing request to a +canned ``(status_code, body, headers)`` triple and the transport wraps it in a +real ``azure-core`` response object. This lets the tests assert routes, the +``Foundry-Features`` opt-in header, request/response serialization, paging, and +audio streaming without recordings or a live service. +""" + +import json +import time +from typing import Any, Callable, Optional, Tuple +from urllib.parse import urlsplit + +from azure.core.credentials import AccessToken +from azure.core.utils import case_insensitive_dict +from azure.core.rest._http_response_impl import HttpResponseImpl +from azure.core.rest._http_response_impl_async import AsyncHttpResponseImpl + +# handler(request) -> (status_code, body, headers) +Handler = Callable[[Any], Tuple[int, Any, Optional[dict]]] + + +class _InMemoryInternalResponse: + """Stand-in for a transport's raw response object. + + The synchronous ``HttpResponseImpl`` calls ``close()`` on its underlying + transport response once the (already in-memory) content is read; this stub + satisfies that call without a real network response. + """ + + def close(self) -> None: + pass + + +def _build_response(request: Any, status: int, body: Any, headers: Optional[dict], is_async: bool): + hdrs = case_insensitive_dict(headers or {}) + if isinstance(body, (bytes, bytearray)): + content = bytes(body) + hdrs.setdefault("content-type", "application/octet-stream") + elif body is None: + content = b"" + else: + content = json.dumps(body).encode("utf-8") + hdrs.setdefault("content-type", "application/json") + + response_cls = AsyncHttpResponseImpl if is_async else HttpResponseImpl + response = response_cls( + request=request, + internal_response=None if is_async else _InMemoryInternalResponse(), + status_code=status, + reason="OK", + content_type=hdrs.get("content-type"), + headers=hdrs, + stream_download_generator=None, + block_size=4096, + ) + # The content is already in memory, so short-circuit any stream download. + response._content = content # pylint: disable=protected-access + return response + + +class MockTransport: + """Synchronous transport that records requests and replays canned responses.""" + + def __init__(self, handler: Handler) -> None: + self._handler = handler + self.requests = [] + + def __enter__(self) -> "MockTransport": + return self + + def __exit__(self, *args: Any) -> None: + pass + + def open(self) -> None: + pass + + def close(self) -> None: + pass + + def send(self, request: Any, **kwargs: Any): + self.requests.append(request) + status, body, headers = self._handler(request) + return _build_response(request, status, body, headers, is_async=False) + + +class AsyncMockTransport: + """Asynchronous transport that records requests and replays canned responses.""" + + def __init__(self, handler: Handler) -> None: + self._handler = handler + self.requests = [] + + async def __aenter__(self) -> "AsyncMockTransport": + return self + + async def __aexit__(self, *args: Any) -> None: + pass + + async def open(self) -> None: + pass + + async def close(self) -> None: + pass + + async def send(self, request: Any, **kwargs: Any): + self.requests.append(request) + status, body, headers = self._handler(request) + return _build_response(request, status, body, headers, is_async=True) + + +def request_path(request: Any) -> str: + return urlsplit(request.url).path + + +def request_query(request: Any) -> str: + return urlsplit(request.url).query + + +def request_json(request: Any) -> Any: + content = request.content + if isinstance(content, (bytes, bytearray)): + content = content.decode("utf-8") + return json.loads(content) + + +class FakeCredential: + """Minimal synchronous TokenCredential stand-in (never contacts an authority).""" + + def get_token(self, *scopes: Any, **kwargs: Any) -> AccessToken: + return AccessToken("fake-token", int(time.time()) + 3600) + + +class FakeAsyncCredential: + """Minimal asynchronous TokenCredential stand-in (never contacts an authority).""" + + async def get_token(self, *scopes: Any, **kwargs: Any) -> AccessToken: + return AccessToken("fake-token", int(time.time()) + 3600) + + async def close(self) -> None: + return None diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/conftest.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/conftest.py new file mode 100644 index 000000000000..b8116c1229d3 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/conftest.py @@ -0,0 +1,14 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Test configuration for the unit test package. + +Ensures the unit test directory is importable so tests can share the local +``_mock_transport`` helper module regardless of the pytest invocation directory. +""" + +import os +import sys + +sys.path.insert(0, os.path.dirname(__file__)) diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py new file mode 100644 index 000000000000..606dbb0b2495 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py @@ -0,0 +1,270 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Synchronous request/response tests for the generated HTTP operations. + +These tests drive the real client pipeline through an in-memory mock transport +(no network, no recordings). They assert the wire contract that would otherwise +only be exercised against a live service: the request route and method, the +``Foundry-Features`` preview opt-in header, request/response serialization, +paging cursor propagation, and audio byte streaming. +""" + +import pytest + +from azure.core.exceptions import ResourceNotFoundError + +from azure.ai.voiceagents import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys + +from _mock_transport import ( + FakeCredential, + MockTransport, + request_json, + request_path, + request_query, +) + +ENDPOINT = "https://example.services.ai.azure.com/api/projects/my-project" +PREVIEW = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW +# The serialized value the opt-in feature flag flows onto the wire as. +FOUNDRY_FEATURES = "VoiceAgents=V1Preview" + + +def _client(handler) -> VoiceAgentsClient: + return VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=MockTransport(handler)) + + +# --------------------------------------------------------------------------- +# Management operations (voice_agents) +# --------------------------------------------------------------------------- + + +def test_create_voice_agent_posts_body_with_opt_in_header(): + def handler(request): + return 200, {"object": "agent", "id": "agent-123", "name": "my-agent", "versions": {}}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.voice_agents.create_voice_agent( + body={"name": "my-agent", "definition": {"model": "gpt-realtime"}}, + foundry_features=PREVIEW, + ) + + request = transport.requests[0] + assert request.method == "POST" + assert request_path(request).endswith("/voice_agents") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert request_json(request)["name"] == "my-agent" + assert result.id == "agent-123" + assert result.name == "my-agent" + + +def test_list_voice_agents_paginates_and_propagates_cursor(): + def handler(request): + if "after=agent-1" in request_query(request): + return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], "last_id": None}, {} + return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], "last_id": "agent-1"}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + agents = list(client.voice_agents.list_voice_agents(foundry_features=PREVIEW)) + + assert [a.name for a in agents] == ["first", "second"] + # Two pages were fetched; the second carried the cursor from the first page. + assert len(transport.requests) == 2 + assert "after" not in request_query(transport.requests[0]) + assert "after=agent-1" in request_query(transport.requests[1]) + for request in transport.requests: + assert request_path(request).endswith("/voice_agents") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + + +def test_get_voice_agent_route_and_deserialization(): + def handler(request): + return 200, {"object": "agent", "id": "agent-9", "name": "my-agent", "versions": {}}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.voice_agents.get_voice_agent("my-agent", foundry_features=PREVIEW) + + request = transport.requests[0] + assert request.method == "GET" + assert request_path(request).endswith("/voice_agents/my-agent") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.id == "agent-9" + + +def test_update_voice_agent_route_and_body(): + def handler(request): + return 200, {"object": "agent", "id": "agent-9", "name": "my-agent", "versions": {}}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.voice_agents.update_voice_agent( + "my-agent", + body={"description": "updated"}, + foundry_features=PREVIEW, + ) + + request = transport.requests[0] + assert request.method == "POST" + assert request_path(request).endswith("/voice_agents/my-agent") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert request_json(request)["description"] == "updated" + assert result.name == "my-agent" + + +def test_delete_voice_agent_route_and_deserialization(): + def handler(request): + return 200, {"object": "agent.deleted", "name": "my-agent", "deleted": True}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.voice_agents.delete_voice_agent("my-agent", foundry_features=PREVIEW) + + request = transport.requests[0] + assert request.method == "DELETE" + assert request_path(request).endswith("/voice_agents/my-agent") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.deleted is True + assert result.name == "my-agent" + + +# --------------------------------------------------------------------------- +# Conversation operations (agent_endpoint_conversations) +# --------------------------------------------------------------------------- + + +def test_get_agent_conversation_route_and_deserialization(): + def handler(request): + return 200, {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.agent_endpoint_conversations.get_agent_conversation( + "my-agent", "conv-1", foundry_features=PREVIEW + ) + + request = transport.requests[0] + assert request.method == "GET" + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.id == "conv-1" + assert result.status == "completed" + + +def test_list_agent_conversation_items_paginates_and_propagates_cursor(): + def handler(request): + if "after=item-1" in request_query(request): + return 200, {"object": "list", "data": [{"id": "item-2", "type": "message"}], "last_id": None}, {} + return 200, {"object": "list", "data": [{"id": "item-1", "type": "message"}], "last_id": "item-1"}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + items = list( + client.agent_endpoint_conversations.list_agent_conversation_items( + "my-agent", "conv-1", foundry_features=PREVIEW + ) + ) + + assert [item["id"] for item in items] == ["item-1", "item-2"] + assert len(transport.requests) == 2 + assert "after=item-1" in request_query(transport.requests[1]) + for request in transport.requests: + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + + +def test_get_agent_conversation_item_audio_metadata(): + def handler(request): + return 200, { + "conversation_id": "conv-1", + "item_id": "item-1", + "format": "wav", + "codec": "pcm16", + "blob_path": "https://storage.example/blob.wav", + }, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + result = client.agent_endpoint_conversations.get_agent_conversation_item_audio( + "my-agent", "conv-1", "item-1", foundry_features=PREVIEW + ) + + request = transport.requests[0] + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items/item-1/audio" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.format == "wav" + assert result.blob_path == "https://storage.example/blob.wav" + + +def test_get_agent_conversation_audio_content_streams_bytes(): + payload = b"RIFF....WAVEfmt merged-call-audio" + + def handler(request): + return 200, payload, {"content-type": "audio/wav"} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + stream = client.agent_endpoint_conversations.get_agent_conversation_audio_content( + "my-agent", "conv-1", foundry_features=PREVIEW + ) + data = b"".join(stream) + + request = transport.requests[0] + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/audio/content" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert data == payload + + +def test_get_agent_conversation_item_audio_content_streams_bytes(): + payload = b"RIFF....WAVEfmt single-turn-audio" + + def handler(request): + return 200, payload, {"content-type": "audio/wav"} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + stream = client.agent_endpoint_conversations.get_agent_conversation_item_audio_content( + "my-agent", "conv-1", "item-1", foundry_features=PREVIEW + ) + data = b"".join(stream) + + request = transport.requests[0] + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items/item-1/audio/content" + ) + assert data == payload + + +def test_error_status_maps_to_typed_exception(): + def handler(request): + return 404, {"error": {"code": "NotFound", "message": "no such conversation"}}, {} + + transport = MockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) + + with pytest.raises(ResourceNotFoundError): + client.agent_endpoint_conversations.get_agent_conversation( + "my-agent", "missing", foundry_features=PREVIEW + ) diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py new file mode 100644 index 000000000000..80593748bc25 --- /dev/null +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py @@ -0,0 +1,240 @@ +# -------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------- +"""Asynchronous request/response tests for the generated HTTP operations. + +Mirrors ``test_unit_operations.py`` for the async client. The coroutines are +driven with ``asyncio.run`` so the module does not depend on a pytest asyncio +plugin. An in-memory mock transport replays canned responses (no network, no +recordings) so routes, the ``Foundry-Features`` opt-in header, serialization, +paging, and audio streaming are all asserted against the real async pipeline. +""" + +import asyncio + +import pytest + +from azure.core.exceptions import ResourceNotFoundError + +from azure.ai.voiceagents.aio import VoiceAgentsClient +from azure.ai.voiceagents.models import AgentDefinitionOptInKeys + +from _mock_transport import ( + AsyncMockTransport, + FakeAsyncCredential, + request_json, + request_path, + request_query, +) + +ENDPOINT = "https://example.services.ai.azure.com/api/projects/my-project" +PREVIEW = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW +# The serialized value the opt-in feature flag flows onto the wire as. +FOUNDRY_FEATURES = "VoiceAgents=V1Preview" + + +# --------------------------------------------------------------------------- +# Management operations (voice_agents) +# --------------------------------------------------------------------------- + + +def test_create_voice_agent_posts_body_with_opt_in_header(): + async def run(): + def handler(request): + return 200, {"object": "agent", "id": "agent-123", "name": "my-agent", "versions": {}}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + result = await client.voice_agents.create_voice_agent( + body={"name": "my-agent", "definition": {"model": "gpt-realtime"}}, + foundry_features=PREVIEW, + ) + + request = transport.requests[0] + assert request.method == "POST" + assert request_path(request).endswith("/voice_agents") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert request_json(request)["name"] == "my-agent" + assert result.id == "agent-123" + + asyncio.run(run()) + + +def test_list_voice_agents_paginates_and_propagates_cursor(): + async def run(): + def handler(request): + if "after=agent-1" in request_query(request): + return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], "last_id": None}, {} + return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], "last_id": "agent-1"}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + + agents = [] + async for agent in client.voice_agents.list_voice_agents(foundry_features=PREVIEW): + agents.append(agent) + + assert [a.name for a in agents] == ["first", "second"] + assert len(transport.requests) == 2 + assert "after" not in request_query(transport.requests[0]) + assert "after=agent-1" in request_query(transport.requests[1]) + for request in transport.requests: + assert request_path(request).endswith("/voice_agents") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + + asyncio.run(run()) + + +def test_get_voice_agent_route_and_deserialization(): + async def run(): + def handler(request): + return 200, {"object": "agent", "id": "agent-9", "name": "my-agent", "versions": {}}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + result = await client.voice_agents.get_voice_agent("my-agent", foundry_features=PREVIEW) + + request = transport.requests[0] + assert request.method == "GET" + assert request_path(request).endswith("/voice_agents/my-agent") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.id == "agent-9" + + asyncio.run(run()) + + +def test_delete_voice_agent_route_and_deserialization(): + async def run(): + def handler(request): + return 200, {"object": "agent.deleted", "name": "my-agent", "deleted": True}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + result = await client.voice_agents.delete_voice_agent("my-agent", foundry_features=PREVIEW) + + request = transport.requests[0] + assert request.method == "DELETE" + assert request_path(request).endswith("/voice_agents/my-agent") + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.deleted is True + + asyncio.run(run()) + + +# --------------------------------------------------------------------------- +# Conversation operations (agent_endpoint_conversations) +# --------------------------------------------------------------------------- + + +def test_get_agent_conversation_route_and_deserialization(): + async def run(): + def handler(request): + return 200, {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + result = await client.agent_endpoint_conversations.get_agent_conversation( + "my-agent", "conv-1", foundry_features=PREVIEW + ) + + request = transport.requests[0] + assert request.method == "GET" + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert result.id == "conv-1" + + asyncio.run(run()) + + +def test_list_agent_conversation_items_paginates_and_propagates_cursor(): + async def run(): + def handler(request): + if "after=item-1" in request_query(request): + return 200, {"object": "list", "data": [{"id": "item-2", "type": "message"}], "last_id": None}, {} + return 200, {"object": "list", "data": [{"id": "item-1", "type": "message"}], "last_id": "item-1"}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + + items = [] + async for item in client.agent_endpoint_conversations.list_agent_conversation_items( + "my-agent", "conv-1", foundry_features=PREVIEW + ): + items.append(item) + + assert [item["id"] for item in items] == ["item-1", "item-2"] + assert len(transport.requests) == 2 + assert "after=item-1" in request_query(transport.requests[1]) + for request in transport.requests: + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + + asyncio.run(run()) + + +def test_get_agent_conversation_item_audio_metadata(): + async def run(): + def handler(request): + return 200, { + "conversation_id": "conv-1", + "item_id": "item-1", + "format": "wav", + "codec": "pcm16", + "blob_path": "https://storage.example/blob.wav", + }, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + result = await client.agent_endpoint_conversations.get_agent_conversation_item_audio( + "my-agent", "conv-1", "item-1", foundry_features=PREVIEW + ) + + request = transport.requests[0] + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items/item-1/audio" + ) + assert result.format == "wav" + assert result.blob_path == "https://storage.example/blob.wav" + + asyncio.run(run()) + + +def test_get_agent_conversation_audio_content_streams_bytes(): + async def run(): + payload = b"RIFF....WAVEfmt merged-call-audio" + + def handler(request): + return 200, payload, {"content-type": "audio/wav"} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + stream = await client.agent_endpoint_conversations.get_agent_conversation_audio_content( + "my-agent", "conv-1", foundry_features=PREVIEW + ) + chunks = [chunk async for chunk in stream] + + request = transport.requests[0] + assert request_path(request).endswith( + "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/audio/content" + ) + assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES + assert b"".join(chunks) == payload + + asyncio.run(run()) + + +def test_error_status_maps_to_typed_exception(): + async def run(): + def handler(request): + return 404, {"error": {"code": "NotFound", "message": "no such conversation"}}, {} + + transport = AsyncMockTransport(handler) + client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) + with pytest.raises(ResourceNotFoundError): + await client.agent_endpoint_conversations.get_agent_conversation( + "my-agent", "missing", foundry_features=PREVIEW + ) + + asyncio.run(run()) From eca690e90fc68c289f52f21553dff208be186c9c Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 19:48:24 -0700 Subject: [PATCH 11/12] Fix CI cspell error and async sample Brotli handling in azure-ai-voiceagents --- .../voiceagents/aio/operations/_operations.py | 1 + .../ai/voiceagents/operations/_operations.py | 1 + .../azure-ai-voiceagents/pyproject.toml | 2 +- ...ple_create_and_manage_voice_agent_async.py | 12 +++- .../tests/unit/_mock_transport.py | 10 ++-- .../tests/unit/test_unit_operations.py | 60 ++++++++++++------- .../tests/unit/test_unit_operations_async.py | 48 +++++++++++---- .../azure-ai-voiceagents/tsp-location.yaml | 2 +- 8 files changed, 93 insertions(+), 43 deletions(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py index d7ec68fa2e2d..818bb52a667c 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py @@ -10,6 +10,7 @@ from io import IOBase import json from typing import Any, AsyncIterator, Callable, IO, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload +import urllib.parse from azure.core import AsyncPipelineClient from azure.core.async_paging import AsyncItemPaged, AsyncList diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py index cad5562c58d4..8f63643ca4c2 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py @@ -10,6 +10,7 @@ from io import IOBase import json from typing import Any, Callable, IO, Iterator, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload +import urllib.parse from azure.core import PipelineClient from azure.core.exceptions import ( diff --git a/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml index b2eb4023c8ed..5247f5be1ec5 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml +++ b/sdk/voiceagents/azure-ai-voiceagents/pyproject.toml @@ -14,7 +14,7 @@ name = "azure-ai-voiceagents" authors = [ { name = "Microsoft Corporation", email = "azpysdkhelp@microsoft.com" }, ] -description = "Microsoft Corporation Azure AI Voice Agents Client Library for Python" +description = "Microsoft Corporation Azure Ai Voiceagents Client Library for Python" license = "MIT" classifiers = [ "Development Status :: 4 - Beta", diff --git a/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py index f4496c2c891b..f832055c9e7d 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py +++ b/sdk/voiceagents/azure-ai-voiceagents/samples/async_samples/sample_create_and_manage_voice_agent_async.py @@ -32,6 +32,8 @@ import os from typing import Final +import aiohttp +from azure.core.pipeline.transport import AioHttpTransport from azure.identity.aio import DefaultAzureCredential from azure.ai.voiceagents.aio import VoiceAgentsClient @@ -45,7 +47,15 @@ async def create_and_manage_voice_agent() -> None: preview: Final = AgentDefinitionOptInKeys.VOICE_AGENTS_V1_PREVIEW credential = DefaultAzureCredential() - async with credential, VoiceAgentsClient(endpoint=endpoint, credential=credential) as client: + # The Foundry endpoint can return Brotli-compressed responses (Content-Encoding: br), + # which azure-core's aiohttp transport does not decode. Ask only for gzip/deflate so + # the transport can decompress the response itself. + transport = AioHttpTransport( + session=aiohttp.ClientSession(auto_decompress=False, headers={"Accept-Encoding": "gzip, deflate"}) + ) + async with credential, VoiceAgentsClient( + endpoint=endpoint, credential=credential, transport=transport + ) as client: created = await client.voice_agents.create_voice_agent( name=agent_name, definition=VoiceAgentDefinition( diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py index 42a8e045ac8b..7fa78398db8e 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/_mock_transport.py @@ -38,15 +38,15 @@ def close(self) -> None: def _build_response(request: Any, status: int, body: Any, headers: Optional[dict], is_async: bool): - hdrs = case_insensitive_dict(headers or {}) + resolved_headers = case_insensitive_dict(headers or {}) if isinstance(body, (bytes, bytearray)): content = bytes(body) - hdrs.setdefault("content-type", "application/octet-stream") + resolved_headers.setdefault("content-type", "application/octet-stream") elif body is None: content = b"" else: content = json.dumps(body).encode("utf-8") - hdrs.setdefault("content-type", "application/json") + resolved_headers.setdefault("content-type", "application/json") response_cls = AsyncHttpResponseImpl if is_async else HttpResponseImpl response = response_cls( @@ -54,8 +54,8 @@ def _build_response(request: Any, status: int, body: Any, headers: Optional[dict internal_response=None if is_async else _InMemoryInternalResponse(), status_code=status, reason="OK", - content_type=hdrs.get("content-type"), - headers=hdrs, + content_type=resolved_headers.get("content-type"), + headers=resolved_headers, stream_download_generator=None, block_size=4096, ) diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py index 606dbb0b2495..ceca5fa46654 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py @@ -65,8 +65,24 @@ def handler(request): def test_list_voice_agents_paginates_and_propagates_cursor(): def handler(request): if "after=agent-1" in request_query(request): - return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], "last_id": None}, {} - return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], "last_id": "agent-1"}, {} + return ( + 200, + { + "object": "list", + "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], + "last_id": None, + }, + {}, + ) + return ( + 200, + { + "object": "list", + "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], + "last_id": "agent-1", + }, + {}, + ) transport = MockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) @@ -144,20 +160,20 @@ def handler(request): def test_get_agent_conversation_route_and_deserialization(): def handler(request): - return 200, {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, {} + return ( + 200, + {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, + {}, + ) transport = MockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) - result = client.agent_endpoint_conversations.get_agent_conversation( - "my-agent", "conv-1", foundry_features=PREVIEW - ) + result = client.agent_endpoint_conversations.get_agent_conversation("my-agent", "conv-1", foundry_features=PREVIEW) request = transport.requests[0] assert request.method == "GET" - assert request_path(request).endswith( - "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1" - ) + assert request_path(request).endswith("/agents/my-agent/endpoint/protocols/voice/conversations/conv-1") assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES assert result.id == "conv-1" assert result.status == "completed" @@ -182,21 +198,23 @@ def handler(request): assert len(transport.requests) == 2 assert "after=item-1" in request_query(transport.requests[1]) for request in transport.requests: - assert request_path(request).endswith( - "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items" - ) + assert request_path(request).endswith("/agents/my-agent/endpoint/protocols/voice/conversations/conv-1/items") assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES def test_get_agent_conversation_item_audio_metadata(): def handler(request): - return 200, { - "conversation_id": "conv-1", - "item_id": "item-1", - "format": "wav", - "codec": "pcm16", - "blob_path": "https://storage.example/blob.wav", - }, {} + return ( + 200, + { + "conversation_id": "conv-1", + "item_id": "item-1", + "format": "wav", + "codec": "pcm16", + "blob_path": "https://storage.example/blob.wav", + }, + {}, + ) transport = MockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) @@ -265,6 +283,4 @@ def handler(request): client = VoiceAgentsClient(ENDPOINT, FakeCredential(), transport=transport) with pytest.raises(ResourceNotFoundError): - client.agent_endpoint_conversations.get_agent_conversation( - "my-agent", "missing", foundry_features=PREVIEW - ) + client.agent_endpoint_conversations.get_agent_conversation("my-agent", "missing", foundry_features=PREVIEW) diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py index 80593748bc25..9a8ccca94082 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py @@ -65,8 +65,24 @@ def test_list_voice_agents_paginates_and_propagates_cursor(): async def run(): def handler(request): if "after=agent-1" in request_query(request): - return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], "last_id": None}, {} - return 200, {"object": "list", "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], "last_id": "agent-1"}, {} + return ( + 200, + { + "object": "list", + "data": [{"object": "agent", "id": "agent-2", "name": "second", "versions": {}}], + "last_id": None, + }, + {}, + ) + return ( + 200, + { + "object": "list", + "data": [{"object": "agent", "id": "agent-1", "name": "first", "versions": {}}], + "last_id": "agent-1", + }, + {}, + ) transport = AsyncMockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) @@ -130,7 +146,11 @@ def handler(request): def test_get_agent_conversation_route_and_deserialization(): async def run(): def handler(request): - return 200, {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, {} + return ( + 200, + {"id": "conv-1", "object": "voice.conversation", "status": "completed", "created_at": 1700000000}, + {}, + ) transport = AsyncMockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) @@ -140,9 +160,7 @@ def handler(request): request = transport.requests[0] assert request.method == "GET" - assert request_path(request).endswith( - "/agents/my-agent/endpoint/protocols/voice/conversations/conv-1" - ) + assert request_path(request).endswith("/agents/my-agent/endpoint/protocols/voice/conversations/conv-1") assert request.headers.get("Foundry-Features") == FOUNDRY_FEATURES assert result.id == "conv-1" @@ -177,13 +195,17 @@ def handler(request): def test_get_agent_conversation_item_audio_metadata(): async def run(): def handler(request): - return 200, { - "conversation_id": "conv-1", - "item_id": "item-1", - "format": "wav", - "codec": "pcm16", - "blob_path": "https://storage.example/blob.wav", - }, {} + return ( + 200, + { + "conversation_id": "conv-1", + "item_id": "item-1", + "format": "wav", + "codec": "pcm16", + "blob_path": "https://storage.example/blob.wav", + }, + {}, + ) transport = AsyncMockTransport(handler) client = VoiceAgentsClient(ENDPOINT, FakeAsyncCredential(), transport=transport) diff --git a/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml b/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml index 351624a03918..7ac7fa478f68 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml +++ b/sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml @@ -5,7 +5,7 @@ additionalDirectories: - specification/ai-foundry/data-plane/Foundry/src/agents - specification/ai-foundry/data-plane/Foundry/src/common - specification/ai-foundry/data-plane/Foundry/src/memory-stores -- specification/ai-foundry/data-plane/Foundry/src/openai/responses +- specification/ai-foundry/data-plane/Foundry/src/openai - specification/ai-foundry/data-plane/Foundry/src/sdk-common - specification/ai-foundry/data-plane/Foundry/src/skills - specification/ai-foundry/data-plane/Foundry/src/tools From 6e3ef8a29057467a73f26b84f807dbd67a91ee37 Mon Sep 17 00:00:00 2001 From: Xiting Zhang Date: Fri, 24 Jul 2026 20:31:42 -0700 Subject: [PATCH 12/12] Fix CI pylint unused-import and cspell Efmt errors in azure-ai-voiceagents --- .../azure/ai/voiceagents/aio/operations/_operations.py | 1 - .../azure/ai/voiceagents/operations/_operations.py | 1 - .../azure-ai-voiceagents/tests/unit/test_unit_operations.py | 4 ++-- .../tests/unit/test_unit_operations_async.py | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py index 818bb52a667c..d7ec68fa2e2d 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_operations.py @@ -10,7 +10,6 @@ from io import IOBase import json from typing import Any, AsyncIterator, Callable, IO, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload -import urllib.parse from azure.core import AsyncPipelineClient from azure.core.async_paging import AsyncItemPaged, AsyncList diff --git a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py index 8f63643ca4c2..cad5562c58d4 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_operations.py @@ -10,7 +10,6 @@ from io import IOBase import json from typing import Any, Callable, IO, Iterator, Literal, Optional, TYPE_CHECKING, TypeVar, Union, overload -import urllib.parse from azure.core import PipelineClient from azure.core.exceptions import ( diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py index ceca5fa46654..1fa69e797fdb 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations.py @@ -233,7 +233,7 @@ def handler(request): def test_get_agent_conversation_audio_content_streams_bytes(): - payload = b"RIFF....WAVEfmt merged-call-audio" + payload = b"RIFF....WAVE....merged-call-audio" def handler(request): return 200, payload, {"content-type": "audio/wav"} @@ -255,7 +255,7 @@ def handler(request): def test_get_agent_conversation_item_audio_content_streams_bytes(): - payload = b"RIFF....WAVEfmt single-turn-audio" + payload = b"RIFF....WAVE....single-turn-audio" def handler(request): return 200, payload, {"content-type": "audio/wav"} diff --git a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py index 9a8ccca94082..5125fe7008be 100644 --- a/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py +++ b/sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_unit_operations_async.py @@ -225,7 +225,7 @@ def handler(request): def test_get_agent_conversation_audio_content_streams_bytes(): async def run(): - payload = b"RIFF....WAVEfmt merged-call-audio" + payload = b"RIFF....WAVE....merged-call-audio" def handler(request): return 200, payload, {"content-type": "audio/wav"}