Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions sdk/ai/azure-ai-projects/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ GITHUB_USERNAME=
TEAMS_CONNECTION_NAME=
TEAMS_CHANNEL_URL=

# Read by the samples under samples/agents/voice/ (model deployment name, agent name, model type,
# and a conversation ID for the read-conversation samples). Distinct from FOUNDRY_VOICE_MODEL_NAME below.
FOUNDRY_VOICE_MODEL=
FOUNDRY_VOICE_MODEL_TYPE=
FOUNDRY_VOICE_AGENT_NAME=
FOUNDRY_VOICE_CONVERSATION_ID=

#######################################################################
#
# Used in tests
Expand All @@ -116,6 +123,10 @@ AZURE_SKIP_LIVE_RECORDING=true
#Used by hosted agent
FOUNDRY_HOSTED_AGENT_NAME=

# Read by the recorded voice-agent CRUD, conversation, realtime-live, and telephony tests
# (tests/test_base.py and friends), not by any sample.
FOUNDRY_VOICE_MODEL_NAME=

# Used in Fine-tuning tests
COMPLETED_OAI_MODEL_SFT_FINE_TUNING_JOB_ID=
COMPLETED_OAI_MODEL_RFT_FINE_TUNING_JOB_ID=
Expand Down
31 changes: 31 additions & 0 deletions sdk/ai/azure-ai-projects/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,39 @@

## 2.7.0 (Unreleased)

### Features Added

* Added Voice Agents, a new agent kind for real-time, speech-to-speech conversational AI unified with the rest of the Agents API. Define a voice agent's model, audio, turn detection, greeting, and tools; manage it like any other agent; hold a live conversation with it over a WebSocket with barge-in and persisted conversation history/audio; and reach it through telephony (inbound bindings or outbound calls/campaigns).
* The core voice agent definition, as a new `kind="voice"` on `AgentDefinition`:
* Define a voice agent with `VoiceAgentDefinition`, configuring its model (`VoiceModelType`), audio input/output (`VoiceAgentAudioConfig`, `VoiceAgentAudioInputConfig`, `VoiceAgentAudioOutputConfig`), turn detection (`VoiceAgentTurnDetectionConfig` and its `VoiceAgentServerVadTurnDetection` / `VoiceAgentAzureSemanticVadTurnDetection` / `VoiceAgentAzureSemanticVadEnTurnDetection` / `VoiceAgentAzureSemanticVadMultilingualTurnDetection` variants), greeting (`VoiceAgentGreetingConfig` and its `VoiceAgentTemplateGreetingConfig` / `VoiceAgentLlmGeneratedGreetingConfig` variants), tools (`VoiceAgentTool`, `VoiceAgentFunctionTool`, `VoiceAgentMcpTool`, `VoiceAgentSystemTool` and its `VoiceAgentEndConversationSystemTool` variant, `VoiceAgentToolboxTool`), and avatar (`VoiceAgentAvatarConfig`). Manage it like any other agent through `project_client.agents` (`create_version`, `get`, `list`, `disable`/`enable`, `delete`).
* Added guided authoring via `project_client.beta.agents.generate(GenerateVoiceAgentRequest(kind=AgentKind.VOICE, ...))`, which returns a service-generated starter definition that can be edited afterward through the standard `create_version`/`update` flow.
* Added a new `client.beta.realtime` / `async_client.beta.realtime` entry point for realtime speech-to-speech streaming. Use `with client.beta.realtime.connect(agent_name=...) as connection:` to open a WebSocket connection, `connection.send(...)` to send strongly-typed client events (or use the `connection.response`, `connection.conversation.item`, and `connection.session` helpers), and iterate over `connection` to receive strongly-typed server events (`RealtimeServerEvent*`). Conversation items exchanged with `connection.conversation.item.create(...)` are `RealtimeConversationItemMessageSystem`, `RealtimeConversationItemMessageUser`, `RealtimeConversationItemMessageAssistant`, `RealtimeConversationItemFunctionCall`, `RealtimeConversationItemFunctionCallOutput`, `RealtimeMCPApprovalResponse`, or a raw `Mapping[str, Any]`. The new types `Realtime`, `RealtimeConnection`, and `RealtimeConnectionManager` (and their async equivalents `AsyncRealtime`, `AsyncRealtimeConnection`, `AsyncRealtimeConnectionManager`) are exported from `azure.ai.projects.operations` / `azure.ai.projects.aio.operations`. These WebSocket clients identify themselves to the service the same way the generated HTTP surface does, via a standard Azure SDK `User-Agent` header and an `x-ms-client-sdk` query parameter for paths where the header isn't forwarded, so service telemetry can attribute this traffic to the SDK; a caller-supplied `User-Agent` in `extra_headers` still takes precedence. Requires the optional `websockets` package for the sync client, or `aiohttp` for the async client.
* Added the `.beta.agent_endpoint_conversations` operation group for reading back persisted voice-agent conversation transcripts and audio, for agents created with `store=True`.
* Added the underlying `RealtimeConversationItem*`, `RealtimeMCP*`, `RealtimeResponseUsage`, and related realtime event/session models used by the voice agent WebSocket protocol.
* Telephony, WebRTC, and sub-agent consultation:
* Added telephony bindings so a voice agent can receive calls through Teams Phone or Twilio. `project_client.beta.agents.create_telephony_binding`/`get_telephony_binding`/`update_telephony_binding`/`delete_telephony_binding`/`list_telephony_bindings` manage the binding (`TelephonyBinding` and its `TeamsPhoneExtensionTelephonyBinding`/`TwilioTelephonyBinding` variants), and `list_telephony_calls`/`get_telephony_call`/`transfer_telephony_call`/`end_telephony_call`/`get_telephony_transfer_targets`/`replace_telephony_transfer_targets` manage in-progress and historical calls (`TelephonyCallRecord`, `TelephonyCallSummary`, `TelephonyCallTrace`, `TelephonyTransferTarget` and its `PSTNTelephonyTransferDestination`/`SipTelephonyTransferDestination`/`TeamsTelephonyTransferDestination` variants).
* Added outbound telephony call jobs and campaigns through the new `.beta.agent_telephony` operation group. `create_call_job`/`get_call_job`/`cancel_call_job` place and manage a single durable outbound call against a `TelephonyOutboundDestination` (`TelephonyCallJob`), and `create_campaign`/`get_campaign`/`cancel_campaign`/`pause_campaign`/`resume_campaign` manage a bulk outbound-calling campaign (`TelephonyCampaign`). A campaign's recipients are staged with `begin_import_campaign_recipients`, checked with `begin_validate_campaign`, and started with `begin_publish_campaign` - all long-running operations polled through `get_operation` (`TelephonyOperation`, `TelephonyOperationResource`, `TelephonyCampaignRecipientImport`).
* Added an optional WebRTC transport for realtime voice sessions (`VoiceAgentTransport.WEBRTC`), where only SDP signaling travels over the WebSocket connection while media flows peer-to-peer. The new `VoiceAgentClientEventRtcCallSdpCreate`, `VoiceAgentServerEventRtcCallSdpCreated`, and `VoiceAgentServerEventRtcCallError` events carry the signaling exchange.
* Added the `.beta.agent_endpoint_conversations.get_item_generated_audio`/`download_item_generated_audio` methods for reading back a conversation item's *generated* audio, a subordinate artifact that can differ from what the listener heard when playback was interrupted, returning `VoiceGeneratedItemAudioResponse`.
* Added sub-agent consultation, letting a voice agent consult sibling Foundry text agents as background specialists mid-conversation, through the new `subagent_config` property on `VoiceAgentDefinition` (`VoiceAgentSubagentConfig`, `VoiceAgentSubagent`, `VoiceAgentSubagentResponsePolicy`), and the new `session.subagent.started`/`session.subagent.completed`/`session.subagent.aborted` realtime server events.
* Added an optional `conversation_engine` property on `VoiceAgentDefinition` (`VoiceConversationEngine`, `VoiceHostedAgentConversationEngine`) to delegate a voice agent's conversation handling to another hosted agent instead of configuring a model directly.

### Dependency update

* Added an optional dependency on `websockets` (sync `client.beta.realtime`) and `aiohttp` (async `async_client.beta.realtime`), required only when using the new voice agent realtime streaming APIs.

### Sample updates

* Added voice agent samples under `samples/agents/voice/`:
* `sample_voice_agent_basic.py` / `sample_voice_agent_basic_async.py` demonstrating the voice-agent management lifecycle: create, get, list, and delete.
* `sample_voice_agent_generate.py` demonstrating guided authoring of a voice agent via `.beta.agents.generate` with `kind="voice"`.
* `sample_voice_agent_with_tools.py` demonstrating a richer voice agent definition: audio configuration, turn detection, greeting, and tools.
* `sample_voice_agent_versions.py` demonstrating voice-agent versioning: creating, drafting, listing, and publishing versions.
* `sample_voice_agent_live_text_conversation.py` / `sample_voice_agent_live_text_conversation_async.py` demonstrating a live, typed conversation with a voice agent over `client.beta.realtime`/`async_client.beta.realtime`.
* `sample_voice_agent_live_audio_conversation_async.py` demonstrating a hands-free, bidirectional live audio conversation over `async_client.beta.realtime`.
* `sample_voice_agent_live_function_tool.py` demonstrating handling a client-executed function tool during a live voice-agent session.
* `sample_voice_agent_read_conversation.py` demonstrating reading a persisted voice conversation's transcript back via `.beta.agent_endpoint_conversations`.
* `sample_voice_agent_read_conversation_audio.py` demonstrating reading a persisted voice conversation's audio, both the merged whole-call recording and a single turn's segment, via `.beta.agent_endpoint_conversations`.
* Added `sample_agent_web_iq.py` under `samples/agents/tools/`, demonstrating a Prompt Agent using the `WebIQPreviewTool`.

## 2.6.0 (2026-09-04)
Expand Down
19 changes: 18 additions & 1 deletion sdk/ai/azure-ai-projects/GeneratePublicMethods.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ def unwrap_operation(value: Any) -> Any:
return getattr(value, "_operation", value)


# Hand-written sub-client properties that don't follow the generated *Operations naming
# convention (so they're invisible to the `vars(container)` scan below) but are still part
# of the public surface and should be counted, e.g. `beta.realtime`.
_EXTRA_SUBCLIENT_PROPERTIES = {"realtime"}


def operation_instances(container: Any, *, exclude: set[str] | None = None) -> dict[str, Any]:
excluded = exclude or set()
operations: dict[str, Any] = {}
Expand All @@ -63,15 +69,26 @@ def operation_instances(container: Any, *, exclude: set[str] | None = None) -> d
operation = unwrap_operation(value)
if type(operation).__name__.endswith("Operations"):
operations[name] = operation
for name in _EXTRA_SUBCLIENT_PROPERTIES:
if name in excluded or name in operations:
continue
if isinstance(getattr(type(container), name, None), property):
operations[name] = getattr(container, name)
return operations


# Filenames that are fully code-generated from TypeSpec; any other source file backing a
# method (including hand-written modules that aren't named `_patch*.py`, e.g. `_realtime.py`)
# counts as handwritten.
_GENERATED_SOURCE_FILENAMES = {"_operations.py", "_client.py"}


def is_handwritten_method(cls: type[Any], name: str) -> bool:
owner = next((base for base in cls.__mro__ if name in vars(base)), None)
if owner is None:
raise RuntimeError(f"Unable to find the class that defines {cls.__name__}.{name}")
source_path = inspect.getsourcefile(owner)
return source_path is not None and "_patch" in Path(source_path).name
return source_path is not None and Path(source_path).name not in _GENERATED_SOURCE_FILENAMES


def public_methods(instance: Any) -> dict[str, bool]:
Expand Down
2 changes: 2 additions & 0 deletions sdk/ai/azure-ai-projects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The AI Projects client library is part of the Microsoft Foundry SDK, and provide
resources in your [Microsoft Foundry](https://ai.azure.com/) Project. Use it to:

* **Create and run Agents** using methods on the `.agents` client property. This includes **Hosted Agents**, which let you run your own containerized agent runtime while using Microsoft Foundry for managed hosting and scaling.
* **Build and run Voice Agents (preview)** for real-time, speech-to-speech conversational AI, reachable over a WebSocket (`.beta.realtime`) or telephony (`.beta.agents`, `.beta.agent_telephony`), with persisted conversation transcripts and audio through `.beta.agent_endpoint_conversations`.
* **Enhance Agents with specialized tools and toolbox tools** such as:
* Agent-to-Agent (A2A)
* Azure AI Search
Expand Down Expand Up @@ -191,6 +192,7 @@ The table below lists the operation groups supported by the client library, with
| Sessions | [Manage hosted sessions](https://learn.microsoft.com/azure/foundry/agents/how-to/manage-hosted-sessions?pivots=python) | `samples/hosted_agents/` |
| Skills (preview) | | `samples/skills/` |
| Toolboxes | [Curate intent-based toolbox in Foundry](https://learn.microsoft.com/azure/foundry/agents/how-to/tools/toolbox?pivots=python) | `samples/hosted_agents/`, `samples/toolboxes/` |
| Voice agents (preview) | | `samples/agents/voice/` |


## Client-side tracing
Expand Down
136 changes: 136 additions & 0 deletions sdk/ai/azure-ai-projects/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,73 @@ namespace azure.ai.projects.aio.operations
) -> SessionFileWriteResult: ...


class azure.ai.projects.aio.operations.AsyncRealtime:

def __init__(self, client: _ConfigProvider) -> None: ...

def connect(
self,
*,
agent_name: str,
agent_session_id: Optional[str] = ...,
agent_version_override: Optional[str] = ...,
api_version: Optional[str] = ...,
connection_url: Optional[str] = ...,
credential_scopes: Optional[List[str]] = ...,
extra_headers: Optional[Mapping[str, str]] = ...,
extra_query: Optional[Mapping[str, str]] = ...,
structured_inputs: Optional[Mapping[str, Any]] = ...,
**kwargs: Any
) -> AsyncRealtimeConnectionManager: ...


class azure.ai.projects.aio.operations.AsyncRealtimeConnection: implements AsyncContextManager
property closed: bool # Read-only

def __aiter__(self) -> AsyncIterator[ServerEvent]: ...

def __init__(
self,
connection: ClientWebSocketResponse,
session: ClientSession
) -> None: ...

def __repr__(self) -> str: ...

async def close(
self,
*,
code: int = 1000,
reason: str = ""
) -> None: ...

async def recv(self) -> ServerEvent: ...

async def send(self, event: ClientEvent) -> None: ...


class azure.ai.projects.aio.operations.AsyncRealtimeConnectionManager: implements AsyncContextManager

def __init__(
self,
*,
agent_name: str,
agent_session_id: Optional[str] = ...,
agent_version_override: Optional[str] = ...,
api_version: str,
connection_url: Optional[str] = ...,
credential: AsyncTokenCredential,
credential_scopes: List[str],
endpoint: str,
extra_headers: Optional[Mapping[str, str]] = ...,
extra_query: Optional[Mapping[str, str]] = ...,
structured_inputs: Optional[Mapping[str, Any]] = ...,
**kwargs: Any
) -> None: ...

async def enter(self) -> AsyncRealtimeConnection: ...


class azure.ai.projects.aio.operations.BetaAgentEndpointConversationsOperations:

def __init__(
Expand Down Expand Up @@ -2344,6 +2411,7 @@ namespace azure.ai.projects.aio.operations


class azure.ai.projects.aio.operations.BetaOperations(GeneratedBetaOperations):
property realtime: AsyncRealtime # Read-only
agent_endpoint_conversations: BetaAgentEndpointConversationsOperations
agent_insight_monitors: BetaAgentInsightMonitorsOperations
agent_telephony: BetaAgentTelephonyOperations
Expand Down Expand Up @@ -19021,6 +19089,7 @@ namespace azure.ai.projects.operations


class azure.ai.projects.operations.BetaOperations(GeneratedBetaOperations):
property realtime: Realtime # Read-only
agent_endpoint_conversations: BetaAgentEndpointConversationsOperations
agent_insight_monitors: BetaAgentInsightMonitorsOperations
agent_telephony: BetaAgentTelephonyOperations
Expand Down Expand Up @@ -19793,6 +19862,73 @@ namespace azure.ai.projects.operations
) -> ItemPaged[Index]: ...


class azure.ai.projects.operations.Realtime:

def __init__(self, client: _ConfigProvider) -> None: ...

def connect(
self,
*,
agent_name: str,
agent_session_id: Optional[str] = ...,
agent_version_override: Optional[str] = ...,
api_version: Optional[str] = ...,
connection_url: Optional[str] = ...,
credential_scopes: Optional[List[str]] = ...,
extra_headers: Optional[Mapping[str, str]] = ...,
extra_query: Optional[Mapping[str, str]] = ...,
structured_inputs: Optional[Mapping[str, Any]] = ...,
**kwargs: Any
) -> RealtimeConnectionManager: ...


class azure.ai.projects.operations.RealtimeConnection: implements ContextManager
property closed: bool # Read-only

def __init__(self, connection: ClientConnection) -> None: ...

def __iter__(self) -> Iterator[ServerEvent]: ...

def __repr__(self) -> str: ...

def close(
self,
*,
code: int = 1000,
reason: str = ""
) -> None: ...

def recv(
self,
*,
timeout: Optional[float] = ...
) -> ServerEvent: ...

def send(self, event: ClientEvent) -> None: ...


class azure.ai.projects.operations.RealtimeConnectionManager: implements ContextManager

def __init__(
self,
*,
agent_name: str,
agent_session_id: Optional[str] = ...,
agent_version_override: Optional[str] = ...,
api_version: str,
connection_url: Optional[str] = ...,
credential: TokenCredential,
credential_scopes: List[str],
endpoint: str,
extra_headers: Optional[Mapping[str, str]] = ...,
extra_query: Optional[Mapping[str, str]] = ...,
structured_inputs: Optional[Mapping[str, Any]] = ...,
**kwargs: Any
) -> None: ...

def enter(self) -> RealtimeConnection: ...


class azure.ai.projects.operations.TelemetryOperations:

def __init__(self, outer_instance: AIProjectClient) -> None: ...
Expand Down
2 changes: 1 addition & 1 deletion sdk/ai/azure-ai-projects/api.metadata.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
apiMdSha256: f943d9a8b56c99c2ec0812bc4d1407df497f57c7ff04e18d203f56fc08fcde77
packageVersion: 2.7.0
parserVersion: 0.3.31
pythonVersion: 3.12.10
pythonVersion: 3.13.2
2 changes: 1 addition & 1 deletion sdk/ai/azure-ai-projects/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/ai/azure-ai-projects",
"Tag": "python/ai/azure-ai-projects_feead9fd04"
"Tag": "python/ai/azure-ai-projects_59c7584f68"
}
Loading