Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions .chronus/changes/wujin-voice_submodule-2026-7-12-16-36-34.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
Comment thread
knitvoger marked this conversation as resolved.
changeKind: feature
packages:
- azure-ai-agentserver-invocations
---

Added the experimental `azure.ai.agentserver.invocations.voice` typed event relay and `basic_voice_agent` full-duplex sample.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
include *.md
include LICENSE
recursive-include tests *.py
recursive-include samples *.py *.md
recursive-include samples *.py *.md *.txt *.yaml
include azure/__init__.py
include azure/ai/__init__.py
include azure/ai/agentserver/__init__.py
Expand Down
70 changes: 70 additions & 0 deletions sdk/agentserver/azure-ai-agentserver-invocations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ The `azure-ai-agentserver-invocations` package provides the invocation protocol

- **HTTP** (`invocations` protocol) — `POST /invocations`, `GET /invocations/{id}`, `POST /invocations/{id}/cancel`, `GET /invocations/docs/openapi.json`, `GET /invocations/docs/asyncapi.{json,yaml}`.
- **WebSocket** (`invocations_ws` protocol) — full-duplex streaming at `/invocations_ws`, registered with `@app.ws_handler`.
- **Voice Live Bridge** — an experimental typed event relay in the `azure.ai.agentserver.invocations.voice` submodule, layered on `invocations_ws`.

## Getting started

Expand Down Expand Up @@ -309,6 +310,74 @@ The handler receives a Starlette [`WebSocket`][starlette-ws] and returns `None`.

[starlette-ws]: https://www.starlette.io/websockets/

## Typed Voice Live Bridge submodule (preview)

`VoiceAgentServerHost` provides typed `on_<event>` decorators over the existing
`invocations_ws` transport. Each callback receives an immutable event and a
send-only `Session`:

```python
from azure.ai.agentserver.invocations.voice import (
ResponseCreated,
ResponseDone,
ResponseOutputTextDone,
Session,
SessionReady,
SessionRejected,
SessionStart,
UserMessage,
VoiceAgentServerHost,
new_item_id,
new_response_id,
)

app = VoiceAgentServerHost()


@app.on_session_start
async def on_session_start(session: Session, event: SessionStart) -> None:
if event.protocol_version != "1.0":
await session.send(
SessionRejected(code="protocol_mismatch", retriable=False)
)
return
# Restore durable application state here when event.reconnect is true.
await session.send(SessionReady())


@app.on_user_message
async def on_user_message(session: Session, event: UserMessage) -> None:
response_id = new_response_id()
item_id = new_item_id()
await session.send(
ResponseCreated(response_id=response_id, in_reply_to=(event.item_id,))
)
await session.send(
ResponseOutputTextDone(
response_id=response_id,
item_id=item_id,
text="Hello from the hosted text agent.",
)
)
await session.send(ResponseDone(response_id=response_id))
```

The submodule is deliberately a thin typed event relay. It decodes one inbound frame,
dispatches the corresponding callback, encodes explicit outbound messages, and
serializes concurrent WebSocket writes. It does **not** own pending responses,
terminal arbitration, timeout/cancel operations, generation tasks, history, or
reconnect state.

When the peer or proxy closes the WebSocket, `@app.on_disconnect` receives a
local `SessionDisconnected` event. Applications use that callback to cancel and
join their own connection-scoped tasks; the SDK does not retain or cancel them.

For full-duplex streaming, the agent creates and owns a generation task, returns
from `on_user_message`, and cancels that task from `on_barge_in`,
`on_response_cancelled`, or `on_response_timeout`. See the complete
[`basic_voice_agent`](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-invocations/samples/basic_voice_agent)
sample.

### Reference: configuration

| Environment variable | Default | Description |
Expand Down Expand Up @@ -340,6 +409,7 @@ Visit the [Samples](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/
| [async_invoke_agent](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-invocations/samples/async_invoke_agent/) | Long-running operations with polling and cancellation |
| [ws_invoke_agent](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-invocations/samples/ws_invoke_agent/) | Combined `POST /invocations` (HTTP) and `/invocations_ws` (WebSocket) host |
| [ws_bidirectional_streaming_agent](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-invocations/samples/ws_bidirectional_streaming_agent/) | Full-duplex `/invocations_ws` agent: concurrent token streams + mid-flight cancel (relies on the SDK's WS protocol Ping/Pong keep-alive, not application-level heartbeats) |
| [basic_voice_agent](https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/agentserver/azure-ai-agentserver-invocations/samples/basic_voice_agent/) | Typed Voice Live Bridge callbacks with developer-owned full-duplex streaming and cancellation |

## Contributing

Expand Down
Loading
Loading