[agentserver] Add azure-ai-agentserver-voice package for Voice Live Bridge Protocol - #48297
[agentserver] Add azure-ai-agentserver-voice package for Voice Live Bridge Protocol#48297knitvoger wants to merge 4 commits into
Conversation
Core: - Add get_tracer/get_meter observability seams for protocol packages - Propagate W3C trace context over WebSocket upgrades and emit x-platform-server identity on websocket.accept - Add enable_sensitive_data host option for a content-free default without mutating process environment Invocations: - Register Invocations identity in the combined x-platform-server value - Preserve an application-selected WebSocket close code in close diagnostics
New hand-written server-host package implementing the Voice Live Bridge Protocol 1.0 (namespace azure.ai.agentserver.voice). Registers the voice artifact in ci.yml and the tracing_e2e test target in tests.yml.
|
Thank you for your contribution @knitvoger! We will review the pull request and get back to you soon. |
|
Azure Pipelines: Successfully started running 1 pipeline(s). 8 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Introduces the preview azure-ai-agentserver-voice package and extends AgentServer Core/Invocations with WebSocket tracing, identity, and close-code support.
Changes:
- Adds the typed Voice Live Bridge Protocol host, models, runtime, documentation, and samples.
- Adds comprehensive protocol, host, and live tracing tests.
- Extends shared WebSocket observability and package identity infrastructure.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
sdk/agentserver/tests.yml |
Adds Voice package testing. |
sdk/agentserver/ci.yml |
Registers Voice in CI. |
sdk/agentserver/azure-ai-agentserver-voice/tests/test_voice_host.py |
Tests host lifecycle and protocol behavior. |
sdk/agentserver/azure-ai-agentserver-voice/tests/test_tracing_e2e.py |
Tests live trace export. |
sdk/agentserver/azure-ai-agentserver-voice/tests/test_protocol.py |
Tests protocol parsing and validation. |
sdk/agentserver/azure-ai-agentserver-voice/tests/conftest.py |
Defines shared test fixtures. |
sdk/agentserver/azure-ai-agentserver-voice/samples/basic_voice_agent/requirements.txt |
Declares sample dependency. |
sdk/agentserver/azure-ai-agentserver-voice/samples/basic_voice_agent/basic_voice_agent.py |
Adds basic Voice agent sample. |
sdk/agentserver/azure-ai-agentserver-voice/README.md |
Documents package usage. |
sdk/agentserver/azure-ai-agentserver-voice/pyrightconfig.json |
Configures type checking. |
sdk/agentserver/azure-ai-agentserver-voice/pyproject.toml |
Defines package metadata and dependencies. |
sdk/agentserver/azure-ai-agentserver-voice/MANIFEST.in |
Configures source distribution contents. |
sdk/agentserver/azure-ai-agentserver-voice/LICENSE |
Adds MIT license. |
sdk/agentserver/azure-ai-agentserver-voice/docs/voice-live-bridge-sdk-design.md |
Documents SDK architecture and protocol. |
sdk/agentserver/azure-ai-agentserver-voice/dev_requirements.txt |
Declares development dependencies. |
sdk/agentserver/azure-ai-agentserver-voice/cspell.json |
Configures spelling checks. |
sdk/agentserver/azure-ai-agentserver-voice/CHANGELOG.md |
Records initial preview features. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/py.typed |
Marks package as typed. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_version.py |
Defines preview version. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_runtime.py |
Implements session and response helpers. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_protocol.py |
Implements protocol encoding and validation. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_models.py |
Defines public event models. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/__init__.py |
Exports the public Voice API. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/__init__.py |
Extends namespace package. |
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/__init__.py |
Extends namespace package. |
sdk/agentserver/azure-ai-agentserver-voice/azure/__init__.py |
Extends namespace package. |
sdk/agentserver/azure-ai-agentserver-voice/api.metadata.yml |
Records APIView metadata. |
sdk/agentserver/azure-ai-agentserver-voice/api.md |
Captures the Voice API surface. |
sdk/agentserver/azure-ai-agentserver-invocations/tests/test_ws_server_routes.py |
Tests WebSocket identity headers. |
sdk/agentserver/azure-ai-agentserver-invocations/tests/test_ws_close_event.py |
Tests application close-code reporting. |
sdk/agentserver/azure-ai-agentserver-invocations/CHANGELOG.md |
Documents Invocations updates. |
sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation.py |
Registers Invocations identity. |
sdk/agentserver/azure-ai-agentserver-invocations/azure/ai/agentserver/invocations/_invocation_ws.py |
Tracks application-selected close codes. |
sdk/agentserver/azure-ai-agentserver-core/tests/test_tracing.py |
Tests tracing additions. |
sdk/agentserver/azure-ai-agentserver-core/tests/test_server_routes.py |
Tests WebSocket platform headers. |
sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md |
Documents Core observability changes. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_tracing.py |
Adds protocol tracing and meter seams. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py |
Adds WebSocket identity and privacy options. |
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/__init__.py |
Exports observability helpers. |
sdk/agentserver/azure-ai-agentserver-core/api.metadata.yml |
Updates Core API metadata. |
sdk/agentserver/azure-ai-agentserver-core/api.md |
Updates the Core API surface. |
Comments suppressed due to low confidence (1)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py:235
- This unconditionally changes the established custom
configure_observabilitycallback contract from two keyword arguments to three. Existing callbacks that accept onlyconnection_stringandlog_levelnow raiseTypeError; the broad handler below suppresses it and silently disables observability. Preserve compatibility by detecting whether the custom callable acceptsenable_sensitive_data(or**kwargs) before passing the new keyword, and add a test with the prior two-argument callback shape.
configure_observability(
connection_string=_conn_str,
log_level=log_level,
enable_sensitive_data=_sensitive_data,
)
| try: | ||
| return await future | ||
| finally: | ||
| self._cancel_pending = False |
| @pytest.fixture(autouse=True, scope="session") | ||
| def _prevent_distro_setup(request): | ||
| """Keep ordinary tests isolated from global OpenTelemetry exporter state.""" | ||
| mark_expression = request.config.getoption("-m", default="") | ||
| normalized = mark_expression.replace(" ", "").replace("(", "").replace(")", "") | ||
| selects_e2e = "tracing_e2e" in normalized and "nottracing_e2e" not in normalized | ||
| if selects_e2e: | ||
| yield | ||
| else: | ||
| with patch("azure.ai.agentserver.core._tracing._setup_distro_export", create=True): | ||
| yield |
- Suppress import-error/no-name-in-module on the core/invocations namespace imports in _host.py (matches responses package convention) - Fix an unterminated inline literal in VoiceSession.end_call docstring that tripped sphinx (warnings-as-errors)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 41 out of 42 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_runtime.py:807
- This measures the raw UTF-8 text, not its encoded JSON frame size. Quotes, backslashes, and control characters expand during
json.dumps(up to six bytes per input byte), so text accepted under the 900 KiB check can still exceed the documented 1 MB WebSocket limit and be disconnected instead of raising locally. Validate the complete encoded frame size, including JSON escaping, envelope fields, and the optional voice patch.
def _text_size(value: str) -> int:
size = len(value.encode("utf-8"))
if size > _MAX_OUTPUT_ITEM_BYTES:
raise ValueError("An output item exceeds the maximum encoded text size")
| continue | ||
| if len(self._seen_messages) >= _MAX_SEEN_MESSAGES: | ||
| raise VoiceBridgeProtocolError("Message dedupe limit exceeded", close_code=1008) | ||
| self._seen_messages[message_id] = canonical |
| pending_ids = tuple(self._pending_turns) | ||
| if pending_ids[: len(in_reply_to)] != in_reply_to: | ||
| raise RuntimeError("in_reply_to must be an ordered prefix of pending inputs") | ||
| self._resolved_input_prefixes[in_reply_to] = (active, True) |
…uirements Voice adds a dependency on azure-ai-agentserver-invocations; list it in the repo-wide shared_requirements.txt allowlist so the Analyze dependencies check passes.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 42 out of 43 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_host.py:545
- This correlation entry is never removed after a normal response; it is only popped if a late pre-response timeout arrives. Consequently every completed
VoiceResponse, including all accumulated output chunks, remains reachable for the entire connection, bypassing the bounded_recent_responsescache and causing memory to grow with every turn. Use bounded lightweight prefix tombstones and release response text/state once the reconciliation window ends.
self._resolved_input_prefixes[in_reply_to] = (active, True)
sdk/agentserver/azure-ai-agentserver-core/azure/ai/agentserver/core/_base.py:235
- Passing this new keyword unconditionally breaks existing custom
configure_observabilitycallbacks that implement the prior(connection_string, log_level)contract. TheirTypeErroris caught by the broad handler below, so host construction continues with observability silently disabled. Add a backward-compatible invocation path or a versioned callback contract rather than treating an unsupported new keyword as setup failure.
configure_observability(
connection_string=_conn_str,
log_level=log_level,
enable_sensitive_data=_sensitive_data,
)
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_host.py:1567
- Every inbound payload's full canonical JSON is retained for the connection. With the documented 1 MiB frame limit and 4,096-entry allowance, a peer can make this cache consume several GiB and exhaust the process. Store a fixed-size digest of the canonical payload (or otherwise enforce a byte budget) so changed-payload detection does not retain all caller content.
if len(self._seen_messages) >= _MAX_SEEN_MESSAGES:
raise VoiceBridgeProtocolError("Message dedupe limit exceeded", close_code=1008)
self._seen_messages[message_id] = canonical
sdk/agentserver/azure-ai-agentserver-voice/azure/ai/agentserver/voice/_runtime.py:807
- This limit measures the raw UTF-8 text, not its encoded JSON representation. JSON escaping can expand control characters sixfold, so a value well below 900 KiB (for example, 200 KiB of
\u0000) passes this check but produces a frame over the documented 1 MiB transport limit. Account forjson.dumps(..., ensure_ascii=False)bytes plus envelope overhead for both complete and accumulated streamed output.
def _text_size(value: str) -> int:
size = len(value.encode("utf-8"))
if size > _MAX_OUTPUT_ITEM_BYTES:
raise ValueError("An output item exceeds the maximum encoded text size")
| async with self._state_lock: | ||
| if response_id in self._terminal_response_ids: | ||
| raise VoiceBridgeConnectionClosedError("The voice response is terminal") | ||
| await self._websocket.send_text(encode_frame(message_type, **fields)) |
|
@knitvoger please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
Description
Why
Developers who already run a hosted text agent (text-in / text-out, custom logic in
their own container) want to give it a voice without rewriting it as a speech-to-speech
agent. The platform puts a managed Voice Live session in front, which owns audio, STT/TTS,
VAD, turn-taking, and barge-in, and relays text turns to the agent. Today an agent must
either be rewritten for speech-to-speech or hand-roll an incompatible schema over the raw
invocations_wsrelay. This PR ships the SDK side of a shared, typed text-turn contractso a text-only handler can serve a real voice call.
What
Adds the new
azure-ai-agentserver-voicepackage — a hand-written server-host layer thatimplements Voice Live Bridge Protocol 1.0 (namespace
azure.ai.agentserver.voice) over theshipped
invocations_wsWebSocket transport. It lets avoiceLiveCompatibleagent thatdeclares
bridgeProtocolVersion: "1.0"handle greeting, streamed replies, barge-in, DTMF,handoff, proactive responses, and clean hang-up with no audio code. Also includes the
Core/Invocations support seams the voice host depends on.
How
_host.py,_models.py,_protocol.py,_runtime.py): a full-duplexhost with a single receive loop over one agent WebSocket, typed inbound/outbound bridge
messages, response-lifecycle arbitration, and callback dispatch — layered on
@app.ws_handler, not modeled as a per-turnchat_bot.get_tracer/get_meterobservability seams, W3Ctrace-context propagation +
x-platform-serveridentity over WebSocket upgrades, anenable_sensitive_datahost option for a content-free default, and preservation of anapplication-selected WebSocket close code. Bumps Core→
2.0.0b9, Invocations→1.0.0b8.ci.ymland thetracing_e2etarget intests.yml.Opt-in rides existing untyped agent metadata (
voiceLiveCompatible,invocations_ws,bridgeProtocolVersion), so no TypeSpec / service-model change is required.Spec: voice_live_bridge/spec.md · Design: hosted_text_agent_and_voice_live_bridge.md
All SDK Contribution checklist:
General Guidelines and Best Practices
Testing Guidelines