Skip to content

[Do not merge][Debug] Preview for voiceagent - #48484

Open
xitzhang (xitzhang) wants to merge 69 commits into
mainfrom
xitzhang/voice-agent-pupr
Open

[Do not merge][Debug] Preview for voiceagent#48484
xitzhang (xitzhang) wants to merge 69 commits into
mainfrom
xitzhang/voice-agent-pupr

Conversation

@xitzhang

@xitzhang xitzhang (xitzhang) commented Aug 6, 2026

Copy link
Copy Markdown
Member

Description

Please add an informative description that covers that changes made by the pull request and link all relevant issues.

If an SDK is being regenerated based on a new API spec, a link to the pull request containing these API spec changes should be included above.

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

Copilot AI balanced review requested due to automatic review settings August 6, 2026 21:05
@xitzhang xitzhang (xitzhang) changed the title [Do not merge] Preview for voiceagent [Do not merge][Debug] Preview for voiceagent Aug 6, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR introduces an initial preview of the azure-ai-voiceagents Python SDK, including generated management clients, a hand-written async realtime (WebSocket) client, and supporting samples/tests for validation and guidance.

Changes:

  • Adds the new azure-ai-voiceagents package skeleton (generated clients/models + packaging metadata).
  • Implements an async realtime WebSocket client (aio/_realtime.py) and an aiohttp transport workaround for brotli negotiation.
  • Adds unit tests, recorded tests (with sanitization), live smoke test, and scenario-based samples + sample README.

Reviewed changes

Copilot reviewed 59 out of 63 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml Tracks source spec location for generation/regeneration.
sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_configuration.py Unit tests for configuration defaults/required params.
sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_client_construction.py Unit tests for sync/async client construction and operation groups.
sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_brotli_workaround.py Unit tests validating async brotli/aiohttp transport workaround behavior.
sdk/voiceagents/azure-ai-voiceagents/tests/unit/conftest.py Disables recorded-test proxy startup for unit-test runs.
sdk/voiceagents/azure-ai-voiceagents/tests/recording/test_voice_agents_client_async.py Recorded async functional tests for read-only operations.
sdk/voiceagents/azure-ai-voiceagents/tests/recording/test_voice_agents_client.py Recorded sync functional tests for read-only operations.
sdk/voiceagents/azure-ai-voiceagents/tests/recording/conftest.py Adds extra URI sanitization for project name in recordings.
sdk/voiceagents/azure-ai-voiceagents/tests/recording/_preparer.py Shared preparer/sanitized env var loader for recorded/live tests.
sdk/voiceagents/azure-ai-voiceagents/tests/live/test_smoke_live.py Always-live smoke test to catch auth/wire/serialization issues.
sdk/voiceagents/azure-ai-voiceagents/tests/live/conftest.py Disables recorded-test proxy startup for live-test runs.
sdk/voiceagents/azure-ai-voiceagents/tests/conftest.py Starts the test-proxy for recorded tests.
sdk/voiceagents/azure-ai-voiceagents/samples/quickstart/sample_quickstart_async.py End-to-end quickstart: create temp agent + realtime audio session + cleanup.
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_read_conversation_audio.py Demonstrates reading and streaming persisted conversation audio to WAV.
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_read_conversation.py Demonstrates reading persisted conversation envelope/items/responses.
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_manage_voice_agent_versions.py Demonstrates creating/listing/fetching voice agent versions (incl drafts).
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_generate_voice_agent.py Demonstrates guided-authoring agent generation and cleanup.
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_create_voice_agent_with_tools.py Demonstrates richer agent definitions (tools + audio config + BYOM).
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_create_and_manage_voice_agent_async.py Async management lifecycle sample: create/get/list/delete.
sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_create_and_manage_voice_agent.py Sync management lifecycle sample incl update and enable/disable.
sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_text_conversation_async.py Live typed conversation + optional audio playback + readback.
sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_audio_conversation_async.py Live mic/speaker conversation + barge-in + readback.
sdk/voiceagents/azure-ai-voiceagents/samples/README.md Scenario-organized samples index + setup/troubleshooting guidance.
sdk/voiceagents/azure-ai-voiceagents/pytest.ini Configures pytest asyncio integration (asyncio_mode=auto).
sdk/voiceagents/azure-ai-voiceagents/pyrightconfig.json Pyright configuration for local development/type-checking.
sdk/voiceagents/azure-ai-voiceagents/pyproject.toml Package metadata and dependencies for azure-ai-voiceagents.
sdk/voiceagents/azure-ai-voiceagents/dev_requirements.txt Dev dependencies including local azure-core/azure-identity and aiohttp.
sdk/voiceagents/azure-ai-voiceagents/azure_ai_voiceagents-1.0.0b1/azure/ai/voiceagents/_configuration.py Snapshot of generated configuration under versioned folder.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/py.typed PEP 561 typing marker.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/_patch.py Customization hook for generated operations (sync).
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/operations/init.py Exposes sync operation groups and patch hook.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_patch.py Customization hook for generated models.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/_enums.py Generated enums supporting voice agents + realtime schemas.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/models/init.py Public models/enums surface exports.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/_patch.py Customization hook for generated operations (async).
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/operations/init.py Exposes async operation groups and patch hook.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py Hand-written async realtime WebSocket client and event typing.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py Async client patch: adds realtime namespace + brotli workaround.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_configuration.py Generated async configuration.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py Generated async HTTP client and operation group wiring.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/init.py Async package exports and patch hook.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_version.py Package version constant.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_utils/init.py Generated utils package init.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py Generated public type aliases/unions.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_patch.py Sync customization hook placeholder.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py Generated sync configuration.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py Generated sync HTTP client and operation group wiring.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/init.py Sync package exports and patch hook + version.
sdk/voiceagents/azure-ai-voiceagents/azure/ai/init.py Namespace package marker for azure.ai.
sdk/voiceagents/azure-ai-voiceagents/azure/init.py Namespace package marker for azure.
sdk/voiceagents/azure-ai-voiceagents/assets.json Azure SDK assets tag configuration for CI.
sdk/voiceagents/azure-ai-voiceagents/apiview-properties.json APIView cross-language mapping metadata.
sdk/voiceagents/azure-ai-voiceagents/_metadata.json Tracks API version metadata for generation.
sdk/voiceagents/azure-ai-voiceagents/README.md Package README scaffold.
sdk/voiceagents/azure-ai-voiceagents/MANIFEST.in Source distribution inclusions (tests/samples/py.typed, etc.).
sdk/voiceagents/azure-ai-voiceagents/LICENSE MIT license text.
sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md Initial changelog entry for version b1.
Suppressed comments (3)

sdk/voiceagents/azure-ai-voiceagents/tsp-location.yaml:1

  • The PR description is still the template (missing a real summary of changes, links to relevant issues/spec PRs, and checklist updates). Please replace the placeholder description with a concise overview of what this PR adds (new SDK package, realtime WebSocket client, samples/tests) and link the spec change PR/commit referenced in tsp-location.yaml.
    sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_configuration.py:1
  • This test doesn’t actually validate overriding behavior because it passes the same value as the default (\"v1\"). Consider changing it to pass a different value (or None if supported by the client) so the test can detect regressions where the override is ignored.
    sdk/voiceagents/azure-ai-voiceagents/samples/management/sample_create_and_manage_voice_agent.py:1
  • Using a fixed, non-namespaced agent_name risks collisions across repeated runs and (more importantly) can inadvertently delete/modify a real agent if a user already has an agent with that name. Safer pattern for samples is to generate a unique name (e.g., including a UUID suffix) and only delete the resource that the sample created.

Comment thread sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_unions.py Outdated
Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/aio/_realtime.py Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/CHANGELOG.md Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/README.md Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/README.md Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/README.md Outdated
Copilot AI review requested due to automatic review settings August 6, 2026 22:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Note

This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 67 changed files in this pull request and generated 2 comments.

Suppressed comments (8)

sdk/voiceagents/ci.yml:1

  • The PR description is still the template text (it asks to 'add an informative description' and link issues), but the change set is substantial (new SDK + CI + tests + samples). Please update the PR description to summarize the intent/scope and link any relevant spec/issue/PR references so reviewers can validate the changes against requirements.
    sdk/voiceagents/azure-ai-voiceagents/pyproject.toml:1
  • Excluding the azure and azure.ai packages from package discovery is likely to produce an invalid wheel where the namespace package initializers (azure/__init__.py, azure/ai/__init__.py) are not installed, breaking imports in environments that rely on pkgutil-style namespace packaging. Recommended fix: remove \"azure\" and \"azure.ai\" from the exclude list (or switch to an explicit include pattern for azure* packages consistent with other Azure SDK Python packages).
    sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_audio_conversation_async.py:1
  • The callback can return fewer than wanted bytes when it hits the end-of-stream marker (if not data: break). pyaudio callbacks must return exactly the requested frame size; otherwise playback can glitch or error. Instead of break, pad the remainder with silence (or return paComplete with a correctly-sized buffer) to satisfy pyaudio’s size contract.
    sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_configuration.py:1
  • This test name claims the API version is 'overridden', but it sets the value to the default (\"v1\"), so it doesn’t actually verify override behavior. Either rename the test to reflect what it asserts (e.g., that api_version is stored), or set api_version to a non-default value (if supported by the client) to validate that overriding works.
    sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py:648
  • Passing the WebSocket subprotocol by manually setting Sec-WebSocket-Protocol is fragile because many WS clients (including aiohttp) expect to manage/validate this header via the protocols=[...] argument. Prefer ws_connect(..., protocols=[\"realtime\"], ...) and remove the manual header to avoid handshake issues and ensure the negotiated subprotocol is tracked correctly by the client.
        headers: Dict[str, str] = {
            "Authorization": f"Bearer {token.token}",
            "Foundry-Features": str(foundry_features),
            "Sec-WebSocket-Protocol": "realtime",
        }

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py:655

  • Passing the WebSocket subprotocol by manually setting Sec-WebSocket-Protocol is fragile because many WS clients (including aiohttp) expect to manage/validate this header via the protocols=[...] argument. Prefer ws_connect(..., protocols=[\"realtime\"], ...) and remove the manual header to avoid handshake issues and ensure the negotiated subprotocol is tracked correctly by the client.
            connection = await session.ws_connect(url, headers=headers, params=params, **self._kwargs)

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py:36

  • The docstring says the default api_version is None, but the implementation defaults it to \"v1\". Please align the docstring with the actual behavior (and consider applying the same fix in the async configuration file as well) to avoid confusing SDK consumers.
    :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.

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_configuration.py:41

  • The docstring says the default api_version is None, but the implementation defaults it to \"v1\". Please align the docstring with the actual behavior (and consider applying the same fix in the async configuration file as well) to avoid confusing SDK consumers.
    def __init__(self, endpoint: str, credential: "TokenCredential", **kwargs: Any) -> None:
        api_version: str = kwargs.pop("api_version", "v1")

Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/aio/_realtime.py
Comment thread sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py Outdated
Copilot AI review requested due to automatic review settings August 6, 2026 23:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 67 changed files in this pull request and generated 3 comments.

Suppressed comments (6)

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py:51

  • The exported connection's public methods and attributes use ClientEvent, ServerEvent, ConversationItem, and the resource helper classes in their annotations, but this module and aio/_patch.py export only the three connection classes. Consumers therefore see public annotations that can only be imported from the private _realtime module. Re-export all types used by this public surface from azure.ai.voiceagents.aio.
__all__ = [
    "AsyncRealtime",
    "AsyncRealtimeConnection",
    "AsyncRealtimeConnectionManager",
]

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py:45

  • Constructing aiohttp.ClientSession here eagerly requires a running event loop and binds the client to that loop. This makes otherwise valid async-client construction outside asyncio.run() fail before any request. Set the default Accept-Encoding through the headers policy instead so AioHttpTransport can retain its lazy session creation.
                kwargs["transport"] = AioHttpTransport(
                    session=aiohttp.ClientSession(auto_decompress=False, headers={"Accept-Encoding": "gzip, deflate"})
                )

sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_audio_conversation_async.py:138

  • Barge-in does not discard a partially consumed chunk: each callback copies bytes from remaining before checking that chunk's sequence against _playback_base. A large delta can therefore continue playing across callbacks after skip_pending_audio(). Track the sequence associated with remaining and invalidate it before slicing whenever it is older than the playback base.
    sdk/voiceagents/azure-ai-voiceagents/samples/quickstart/sample_quickstart_async.py:100
  • Barge-in does not discard a partially consumed chunk: bytes are copied from remaining before the callback checks sequence validity. If a delta spans multiple callback buffers, stale audio continues playing after skip_pending_audio(). Associate remaining with its sequence and clear it before slicing when that sequence predates _playback_base.
    sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_configuration.py:47
  • This test passes the same value as the default, so it still passes if the api_version override is ignored. Use a distinct sentinel value to verify that the keyword is actually honored.
    sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_text_conversation_async.py:179
  • After a timeout, the response remains active and its eventual events stay queued. On the next turn, pump() can consume the previous response's response.done and return before the new response completes, or the service can reject the new response while the old one is active. Cancel the timed-out response and drain/match its terminal event before accepting another prompt.

Comment thread sdk/voiceagents/azure-ai-voiceagents/pyproject.toml Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/tests/recording/test_voice_agents_client.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 67 changed files in this pull request and generated 2 comments.

Suppressed comments (8)

sdk/voiceagents/azure-ai-voiceagents/pyproject.toml:13

  • This new package is missing both api.md and api.metadata.yml. The repository API consistency gate explicitly marks an affected package missing either file as a failure (.github/workflows/src/api-md-consistency/find_mismatches.js:76-92). Generate and commit both files with azpysdk apistub azure-ai-voiceagents.
    sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py:43
  • Creating aiohttp.ClientSession in the client constructor makes VoiceAgentsClient(...) require a running event loop and binds the transport to that loop. This breaks the normal pattern of constructing an async SDK client before asyncio.run(...) (the new test even documents this limitation). Create/configure the session lazily when the transport opens instead.
                kwargs["transport"] = AioHttpTransport(
                    session=aiohttp.ClientSession(auto_decompress=False, headers={"Accept-Encoding": "gzip, deflate"})
                )

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py:242

  • agent_name is inserted into the URL as raw path text, unlike the generated operation which URL-serializes this path parameter. Reserved characters can therefore change the route instead of identifying the requested agent. Percent-encode the path segment before interpolation.
    return f"{base}/agents/{agent_name}/endpoint/protocols/voice"

sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_patch.py:65

  • The public annotations/docstrings refer to azure.ai.voiceagents.aio.ClientEvent and ServerEvent, but these aliases are defined only in the private _realtime module and are not re-exported here. Those documented imports therefore fail; include both aliases in the public aio surface.
__all__: list[str] = [
    "VoiceAgentsClient",
    "AsyncRealtime",
    "AsyncRealtimeConnection",
    "AsyncRealtimeConnectionManager",
]  # Add all objects you want publicly available to users at this package level

sdk/voiceagents/azure-ai-voiceagents/samples/quickstart/sample_quickstart_async.py:115

  • The callback consumes remaining before checking its sequence against _playback_base. After skip_pending_audio() advances the base during barge-in, bytes left from the old response are still played on the next callback, so interruption does not actually drop all pending audio. Track the sequence associated with remaining and clear it before consuming stale bytes.
    sdk/voiceagents/azure-ai-voiceagents/samples/live/sample_live_audio_conversation_async.py:153
  • The callback consumes remaining before checking its sequence against _playback_base. After barge-in advances the base, bytes left from the previous response are still played on the next callback, contradicting the promised immediate drop of pending audio. Track the sequence associated with remaining and discard stale bytes before playback.
    sdk/voiceagents/azure-ai-voiceagents/tests/unit/test_configuration.py:47
  • This test supplies the same v1 value as the default, so it still passes if the override is ignored. Use a non-default sentinel to exercise the behavior named by the test.
    sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_realtime.py:522
  • The hand-written realtime implementation has no deterministic unit coverage for frame serialization/deserialization, typed-event mapping and unknown-event fallback, close/error handling, or resource helpers; the live smoke test covers only one service path. Add mocked WebSocket/session tests for these public behaviors so regressions do not require a gated live environment to detect.
    async def recv(self) -> ServerEvent:

Comment thread sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/_client.py Outdated
Comment thread sdk/voiceagents/azure-ai-voiceagents/azure/ai/voiceagents/aio/_client.py Outdated
@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings August 7, 2026 18:33
…hony/WebRTC/sub-agent voice features, fix missing preview-header wiring

- Regenerate SDK from azure-rest-api-specs commit 1070c74ae519b6f86540bbd44ea295ff12642e60;
  update tsp-location.yaml.saved accordingly.
- New voice-agent features from this commit (purely additive, no removed/renamed classes):
  - Telephony bindings for Teams Phone/Twilio on �gents.* (create/list/get/update/delete
    binding, list/get/transfer/end call, get/replace transfer targets - 11 new methods).
  - Optional WebRTC transport for realtime sessions (VoiceAgentTransport.WEBRTC) with SDP
    signaling events.
  - New top-level �gent_endpoint_conversations operation group (generated-audio reads),
    distinct from the unchanged �eta.agent_endpoint_conversations.
  - Sub-agent consultation (VoiceAgentDefinition.subagent_config) and conversation_engine
    delegation to a hosted agent.
- list_memories() unbound-variable bug fixed upstream: verified the fix, removed the
  now-obsolete PostEmitter.ps1 fixup (kept an explanatory comment for future regression
  detection).
- Fixed a real functional bug: the new telephony and agent_endpoint_conversations methods
  require the Foundry-Features: VoiceAgents=V1Preview opt-in header per TypeSpec, but had no
  header-injection wiring (would fail with preview_feature_required). Added header injection
  (gated on �llow_preview, matching the existing generate_agent pattern) via 11 new method
  overrides in _patch_agents.py/_patch_agents_async.py and new
  _patch_agent_endpoint_conversations.py/_async.py files for the 2 new conversation methods.
  Also fixed a generator bug where replace_telephony_transfer_targets's 2nd/3rd @overload
  signatures had etag/match_condition types swapped.
- Registered 6 new realtime event types (1 client + 5 server, for RTC signaling and sub-agent
  consultation) in _realtime.py/aio/_realtime.py.
- Added 13 new unit tests in tests/foundry_features_header/ covering the header-injection fix
  for all new methods.
- Added tests/agents/test_voice_agent_telephony.py/_async.py (6 methods) covering telephony
  bindings/calls/transfer-targets and generated-audio not-found paths. Currently skipped: the
  telephony routes are defined in TypeSpec but not yet deployed on the live test service
  (confirmed via empty-body 404s vs a real app-level 404's full JSON error body), and the
  generated-audio not-found path hits the same pre-existing conversation-ID validation quirk as
  the already-documented beta.agent_endpoint_conversations limitation.
- Updated docs/public-methods.md (170->183 methods) and CHANGELOG.md.
- Regenerated api.md/api.metadata.yml.
- Validated: full test suite (1026 passed, 111 skipped, 0 failed) run fresh in one pass, plus
  live e2e validation of 10 of 11 voice-agent samples against the real service, including both
  live-streaming samples (text conversation sync+async, function tool) which exercise realtime
  WebSocket sessions, tool-calling, and conversation persistence/readback end-to-end. Confirmed
  the regeneration is reproducible by re-running tsp-client update + PostEmitter.ps1 from
  scratch and diffing against the working tree (identical result).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Resolve sample-runner hangs, truncated audio frames, missing telephony coverage, and inaccurate WebSocket documentation.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

sdk/ai/azure-ai-projects/azure/ai/projects/_realtime.py:786

  • The public documentation says a ws:// override is accepted, but RealtimeConnectionManager.enter() rejects every URL that does not start with wss://. Document this as wss:// only so callers are not promised an option that always raises ValueError.
        :keyword connection_url: Full ``wss://``/``ws://`` URL that overrides the route computed
         from the client endpoint. Query parameters are still appended. Default value is None.

sdk/ai/azure-ai-projects/azure/ai/projects/aio/_realtime.py:792

  • The public documentation says a ws:// override is accepted, but AsyncRealtimeConnectionManager.enter() rejects every URL that does not start with wss://. Document this as wss:// only so callers are not promised an option that always raises ValueError.
        :keyword connection_url: Full ``wss://``/``ws://`` URL that overrides the route computed
         from the client endpoint. Query parameters are still appended. Default value is None.
  • Files reviewed: 67/71 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Comment thread eng/tools/azure-sdk-tools/azpysdk/samples.py
Comment thread scripts/devops_tasks/test_run_samples.py
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py
…pupr

Resolved conflicts in azure-ai-projects between this branch's regeneration from
TypeSpec commit 1070c74ae519b6f86540bbd44ea295ff12642e60 (telephony, WebRTC,
sub-agent consultation, generated-audio conversation reads) and main's
independent regeneration/hand-written additions (agent_insight_monitors patch
classes, ShellToolboxTool, WebIQPreviewTool, Hosted Agent session
configuration, routines.create_or_update authorization).

Resolution strategy:
- Fully generated files (_operations.py x2, _models.py, _enums.py,
  models/__init__.py): took this branch's side after verifying, at the class
  and method level, that it is a strict superset of main's content (zero
  classes/methods present on main but absent here).
- Hand-written orchestration files (operations/_patch.py and aio equivalent):
  merged both sides' additions (this branch's AgentEndpointConversationsOperations
  wiring + main's BetaAgentInsightMonitorsOperations wiring); removed a
  resulting duplicate import of BetaAgentInsightMonitorsOperations from the
  generated module now that it's patched.
- CHANGELOG.md: merged both sides' entries; corrected an inaccurate entry
  (the new �uthorization parameter is on .beta.routines.create_or_update,
  not .beta.routines.dispatch as this branch's changelog had said).
- tsp-location.yaml: adopted main's convention of tracking the file directly
  (main had renamed away from the .saved split specifically to avoid
  breaking the release build), keeping this branch's commit
  1070c74ae519b6f86540bbd44ea295ff12642e60.
- api.md/api.metadata.yml: regenerated fresh via �zpysdk apistub . against
  the resolved source rather than hand-merging generated markdown.
- docs/public-methods.md: kept this branch's version; verified method counts
  are still accurate post-merge (no method-set changes from main's patch).
- Fixed two duplicate dict keys (�gent_insight_monitors in both
  models/_patch.py's _BETA_OPERATION_FEATURE_HEADERS and
  foundry_features_header_test_base.py's EXPECTED_FOUNDRY_FEATURES) that
  git's line-based auto-merge introduced silently (both entries had identical
  values, so this was not a functional bug, just redundant code).

Validated: full test suite re-run after resolution (1026 passed, 111 skipped,
0 failed) - identical results to before the merge, confirming no regressions
in either branch's contributions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate issues affect realtime behavior, sample automation, dependencies, API metadata, and telephony coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (7)

eng/tools/azure-sdk-tools/azpysdk/samples.py:92

  • The samples runner recursively executes every Python file not listed here. The two new read-conversation samples require a pre-existing persisted FOUNDRY_VOICE_CONVERSATION_ID and do not create one, so the automated samples check cannot run them reliably. Ignore those two files as well, or make them self-contained.
    "azure-ai-projects": [
        # These interactively read from stdin via input(), which raises EOFError when this
        # runner executes the file non-interactively.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",
    ],

scripts/devops_tasks/test_run_samples.py:96

  • Keep this legacy runner's ignore list aligned with the azpysdk runner. It also recursively executes every Python sample, so the two read-conversation samples will fail without a valid pre-existing FOUNDRY_VOICE_CONVERSATION_ID.
    "azure-ai-projects": [
        # These interactively read from stdin via input(), which raises EOFError when this
        # runner executes the file non-interactively.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",
    ],

sdk/ai/azure-ai-projects/docs/public-methods.md:63

  • The legend says hand-written methods carry *, and both of these methods are overridden in _patch_agent_endpoint_conversations.py; omitting the markers makes this generated inventory inaccurate.
    sdk/ai/azure-ai-projects/docs/public-methods.md:162
  • begin_create_run is still implemented by the custom BetaAgentInsightMonitorsOperations in operations/_patch_agent_insights.py, so removing its * contradicts this document's hand-written-method legend.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:82
  • Every test in this new module is skipped, so none of the telephony request serialization, response handling, or generated-audio behavior is exercised in CI. Before merging the new API surface, enable these tests or add transport-mocked coverage that does not depend on undeployed service routes.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony_async.py:82
  • The async counterpart is also entirely skipped, leaving the new async telephony and generated-audio paths without executable coverage. Enable these cases or replace them with transport-mocked tests before merging.
    sdk/ai/azure-ai-projects/tsp-location.yaml:2
  • This regeneration advances the TypeSpec commit, but the PR description is still the template and provides no link to the corresponding API-spec change. Please add the source spec PR/commit context so reviewers can validate the generated API against its requirements.
  • Files reviewed: 66/70 changed files
  • Comments generated: 7
  • Review effort level: Balanced

Comment thread sdk/ai/azure-ai-projects/api.metadata.yml Outdated
Comment thread sdk/ai/azure-ai-projects/dev_requirements.txt Outdated
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_realtime_client.py Outdated
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_realtime_client.py Outdated
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_realtime_client_async.py Outdated
The previous regeneration used a cached wheel in .venv_apistub/.staging
predating some merge-resolution fixes, causing api.md to omit
AgentInsightRunLROPoller/AsyncAgentInsightRunLROPoller and the base class
of BetaAgentInsightMonitorsOperations, even though the actual source was
always correct. Cleared the stale cache and regenerated fresh; verified a
full line-by-line diff against main's api.md now shows zero API elements
present in main but absent here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved moderate issues affect preview gating, compatibility, sample automation, API metadata, linting, and test coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (10)

eng/tools/azure-sdk-tools/azpysdk/samples.py:91

  • This ignore list is incomplete for the newly added voice samples. The sample runner executes every unlisted *.py; the read-conversation samples require FOUNDRY_VOICE_AGENT_NAME and FOUNDRY_VOICE_CONVERSATION_ID, which no test configuration supplies, and the live-audio sample runs until Ctrl-C when PyAudio is available. Add all three so the automated samples check does not fail or hang.
    "azure-ai-projects": [
        # These interactively read from stdin via input(), which raises EOFError when this
        # runner executes the file non-interactively.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",

scripts/devops_tasks/test_run_samples.py:95

  • Keep this legacy sample runner's ignore list aligned with the azpysdk runner. Otherwise it will execute the two read-conversation samples without their required pre-existing IDs, and can run the live-audio sample indefinitely when PyAudio is installed.
    "azure-ai-projects": [
        # These interactively read from stdin via input(), which raises EOFError when this
        # runner executes the file non-interactively.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",

sdk/ai/azure-ai-projects/PostEmitter.ps1:40

  • Deleting azure.ai.projects.types removes an existing, non-private import path, so users importing its generated TypedDicts will get ModuleNotFoundError after upgrading. Retain a compatibility module (and deprecate it if needed) or correct its generation rather than removing it silently; this also conflicts with the PR checklist's no-breaking-changes claim.
$typesFile = 'azure\ai\projects\types.py'
if (Test-Path $typesFile) {
    Remove-Item $typesFile -Force

sdk/ai/azure-ai-projects/api.metadata.yml:3

  • This metadata was generated with parser 0.3.30, but the repository pins apiview-stub-generator==0.3.31 in eng/apiview_reqs.txt:18. Regenerate api.md and its metadata with the pinned parser; otherwise the committed parser version/hash will not match the repository's API snapshot tooling.
parserVersion: 0.3.30

sdk/ai/azure-ai-projects/docs/public-methods.md:10

  • The updated claim that this document lists all 183 public methods omits the newly exported realtime API entirely (realtime.connect, connection send/recv/close, and the session/conversation/audio/response helpers). Add that namespace and recalculate the summary so the public-surface inventory matches the API introduced by this PR.
    sdk/ai/azure-ai-projects/docs/public-methods.md:63
  • These methods are implemented by the new hand-written AgentEndpointConversationsOperations subclass, but the document says hand-written methods carry an asterisk. Mark both entries consistently so the API inventory remains accurate.
    sdk/ai/azure-ai-projects/docs/public-methods.md:162
  • Removing the asterisk makes this inventory claim begin_create_run is generated, but it is still overridden in operations/_patch_agent_insights.py:63. Keep the hand-written marker.
    sdk/ai/azure-ai-projects/tests/agents/test_realtime_client.py:28
  • RealtimeServerEventError is never referenced in this test module, so the newly added file introduces an unused-import pylint failure. Remove it from the import list.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:82
  • All tests in this new telephony/generated-audio module are unconditionally skipped, so these new operation paths receive no request/response or error-handling coverage in CI. Add transport-mocked or recorded coverage that can run without the unavailable service routes instead of committing only disabled placeholders.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony_async.py:82
  • All tests in this async telephony/generated-audio module are unconditionally skipped, leaving the new async operation paths unexecuted in CI. Add async transport-mocked or recorded tests that can run independently of deployment availability rather than relying only on header-injection tests.
  • Files reviewed: 66/70 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py
Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py
…ple/test fixes

Security:
- _assert_trusted_connection_url (sync + async _realtime.py) now compares
  normalized (host, port) tuples with scheme-default-port resolution instead
  of hostname alone, so a connection_url override targeting the same host on
  a different, non-default port (a different origin) is correctly rejected
  instead of receiving the live Authorization token.

Protocol/correctness fixes:
- Async realtime client now passes protocols=("realtime",) to aiohttp's
  ws_connect() instead of a raw Sec-WebSocket-Protocol header, which aiohttp
  never validates/negotiates on its own. Guarded against a caller-kwarg
  collision on "protocols".
- Removed the dead/misleading http:// -> ws:// translation in _to_ws_url
  (both sync and async): enter() unconditionally rejects any non-wss:// URL,
  so that path could never actually be used to connect.
- Live-audio sample (sample_voice_agent_live_audio_conversation_async.py):
  - End-of-stream playback callback branch now pads to the exact frame size
    pyaudio requested instead of returning a short buffer.
  - speech_started no longer calls response.cancel() when no response is
    active (fixes a false-positive service error on the very first user turn).
  - Mic capture callback now bounds concurrent in-flight sends to 1 and drops
    (rather than unboundedly scheduling) frames while a send is still
    in-flight, reporting the dropped-frame count at shutdown.

Test/tooling/doc fixes:
- Added test_voice_samples parametrization (tests/samples/test_samples.py,
  test_samples_async.py) so samples/agents/voice/ is discovered by the
  package's recorded sample tests (previously not wired up at all).
- Added the live-audio sample to IGNORED_SAMPLES in both
  eng/tools/azure-sdk-tools/azpysdk/samples.py and
  scripts/devops_tasks/test_run_samples.py: it runs until Ctrl-C and would
  hang indefinitely under the non-interactive sample-runner.
- Pinned dev_requirements.txt's websockets to >=13.0 to match the realtime
  extra's minimum version.
- Regenerated api.metadata.yml with the pinned apiview-stub-generator==0.3.31
  (was stale at 0.3.30 from a cached build).
- Removed an unused RealtimeServerEventError import in test_realtime_client.py.
- Added regression tests: port-mismatch trusted-connection-url rejection,
  explicit-default-port acceptance, protocols kwarg override, and non-https
  scheme left unchanged in _to_ws_url.

Verified: full test suite passes (1029 passed, 113 skipped, 0 failed).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical sample-runner failures and moderate API/header issues must be resolved before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (8)

Previously missed (1) — in code that hasn't changed since the last review.

sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py:81

  • Routing this new group through _OperationMethodHeaderProxy exposes its immutable-header fallback: if a caller passes a read-only mapping without Foundry-Features, the proxy catches the assignment failure and replaces the mapping with only the preview header, silently dropping all caller headers. Update the proxy fallback to merge {**headers, Foundry-Features: value}; _AcceptEncodingIdentityProxy already demonstrates the preserving behavior.

sdk/ai/azure-ai-projects/CHANGELOG.md:9

  • This describes a standard agent update flow, but AgentsOperations exposes create_version and update_details; there is no agents.update method. Since editing the definition is done by publishing another version, the release note currently directs users to a nonexistent API.
  * Added guided authoring via `project_client.agents.generate_agent(GenerateVoiceAgentRequest(kind=AgentKind.VOICE, ...))`, which returns a service-generated starter definition that can be edited afterward through the standard `create_version`/`update` flow.

sdk/ai/azure-ai-projects/docs/public-methods.md:63

  • These methods are implemented as handwritten overrides in operations/_patch_agent_endpoint_conversations.py, but the document's legend requires handwritten methods to carry *. Without the markers, this inventory incorrectly presents both as generated methods.
    sdk/ai/azure-ai-projects/docs/public-methods.md:162
  • begin_create_run is still implemented by the handwritten _patch_agent_insights.py override, so removing its * makes this inventory contradict its own legend.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_generate.py:13
  • The client has no agents.update method, so the documented create_version/update flow is misleading. Voice-agent definitions are changed by creating another immutable version.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_audio_conversation_async.py:32
  • The sample's installation instructions are missing python-dotenv, which is a required direct import on line 53 and is not a package dependency. A clean environment following this command cannot run the sample.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation.py:30
  • The setup command omits python-dotenv, but this sample imports load_dotenv unconditionally. A user following these instructions in a clean environment will fail with ModuleNotFoundError before the sample starts.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation_async.py:27
  • The setup command omits python-dotenv, although load_dotenv is imported unconditionally below. Following the documented install command in a clean environment therefore makes the sample fail at startup.
  • Files reviewed: 68/72 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread eng/tools/azure-sdk-tools/azpysdk/samples.py
Comment on lines +91 to +99
"azure-ai-projects": [
# These interactively read from stdin via input(), which raises EOFError when this
# runner executes the file non-interactively.
"sample_voice_agent_live_text_conversation.py",
"sample_voice_agent_live_text_conversation_async.py",
# Runs until Ctrl-C (continuous microphone capture/playback); would hang indefinitely
# under this non-interactive runner whenever PyAudio and live credentials are available.
"sample_voice_agent_live_audio_conversation_async.py",
],
… identification fix

- _realtime.py/aio: add User-Agent + x-ms-client-sdk identification (ports fix from
  azure-ai-voicelive PR #48848), with case-insensitive header collision guard so a
  caller-supplied extra_headers User-Agent (any casing) is not duplicated
- test_realtime_client(_async).py: add regression tests for identification headers
  and case-insensitive override behavior
- tests/agents/test_voice_agent_realtime_live(_async).py: new live-only tests for
  voice agent realtime session lifecycle, text-to-audio/transcript turns, and
  function tool-call round trip
- tests/agents/test_voice_agent_conversations(_async).py: new recorded tests for
  beta.agent_endpoint_conversations REST surface, with a live-only setup step to
  obtain a sanitized conversation_id for playback
- assets.json: pin new recordings via test-proxy push (tag ..._d354d861da)
- test-resources.bicep/test-resources-post.ps1/tests.yml: new live-test CI
  infrastructure (Foundry account/project + gpt-realtime model deployment),
  following the azure-ai-voicelive package pattern
- CHANGELOG.md: document the identification fix under Bugs Fixed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Moderate issues remain in live-test provisioning, deployment configuration, and executable test coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (16)

sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_basic.py:24

  • Voice-agent lifecycle APIs are introduced in 2.6.0, but this command permits 2.0.0. Raise the minimum so the models and operations used by the sample are present.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_basic_async.py:18
  • Voice-agent lifecycle APIs are introduced in 2.6.0, but this command permits 2.0.0. Raise the minimum so the async sample installs a compatible SDK.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_generate.py:20
  • generate_agent for voice agents is added in 2.6.0, so the current minimum can install an SDK that lacks the imported request type and method. Require 2.6.0.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_function_tool.py:23
  • The realtime voice-agent API used by this sample is introduced in 2.6.0, but this command permits 2.0.0. Raise the minimum so client.realtime and the event models are available.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation.py:30
  • The realtime extra and client.realtime API are introduced in 2.6.0, but this command accepts 2.0.0. Users following it can install an older SDK with no realtime namespace; require 2.6.0.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation_async.py:27
  • The realtime extra and async realtime API are introduced in 2.6.0, but this command accepts 2.0.0. Require 2.6.0 so the documented install includes the APIs used below.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation.py:22
  • The beta voice-conversation operation group is added in 2.6.0, so allowing 2.0.0 makes these instructions install versions without beta.agent_endpoint_conversations. Raise the minimum to 2.6.0.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation_audio.py:26
  • The beta voice-conversation audio APIs are added in 2.6.0, so allowing 2.0.0 can install an SDK without these methods. Raise the documented minimum to 2.6.0.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_versions.py:19
  • Voice-agent APIs are introduced in 2.6.0 (CHANGELOG.md:3-17), but this command permits 2.0.0, where VoiceAgentDefinition does not provide this surface. Require 2.6.0 in the setup command.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_with_tools.py:25
  • Voice-agent APIs are introduced in 2.6.0 (CHANGELOG.md:3-17), but this command permits 2.0.0, where these models are unavailable. Require 2.6.0 so the documented setup installs a compatible SDK.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_crud.py:33
  • This limitation contradicts the new test_voice_agent_conversations.py, which creates a persisted realtime conversation ID and then reads it through beta.agent_endpoint_conversations. Update this note now that read-back is covered separately.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_crud_async.py:34
  • This limitation contradicts the new async conversation test, which creates a persisted realtime conversation ID and reads it through this REST operation group. Replace the stale service-bug note with a reference to the dedicated coverage.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:34
  • Every test in this new sync test module is unconditionally skipped, leaving the telephony and generated-audio operation behavior without executable functional coverage; the feature-header tests only validate header injection. Add transport-mocked tests or enable these scenarios before the API is considered covered.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:62
  • This says realtime-generated conversation IDs are rejected, but the new test_voice_agent_conversations.py exercises exactly that setup and read-back path. Update this bullet to point to the dedicated test instead of documenting a conflicting limitation.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony_async.py:34
  • Every test in this new async test module is unconditionally skipped, leaving the async telephony and generated-audio operation behavior without executable functional coverage; the feature-header tests only validate header injection. Add transport-mocked tests or enable these scenarios before the API is considered covered.
    sdk/ai/azure-ai-projects/tsp-location.yaml:2
  • This regeneration pin changes the source specification, but the PR description is still the unfilled template and does not link the corresponding azure-rest-api-specs PR. Replace the template with the voice-agent scope and the source-spec PR so reviewers can validate these generated API changes.
  • Files reviewed: 75/79 changed files
  • Comments generated: 6
  • Review effort level: Balanced

Comment on lines +176 to +178
if (-not $ready) {
Write-Warning "The '$deploymentName' deployment did not finish provisioning in time. Live voice-agent tests may fail until it finishes."
}
TestResourceDirectories:
- ai/azure-ai-projects
EnvVars:
AZURE_TEST_RUN_LIVE: 'true'
agent_name=agent_name,
definition=VoiceAgentDefinition(
model_type=VoiceModelType.MANAGED,
model="gpt-realtime",
agent_name=agent_name,
definition=VoiceAgentDefinition(
model_type=VoiceModelType.MANAGED,
model="gpt-realtime",
Comment on lines +405 to +409
# PR #48484: recording not yet available for these REST-only samples.
"sample_voice_agent_basic.py",
"sample_voice_agent_generate.py",
"sample_voice_agent_versions.py",
"sample_voice_agent_with_tools.py",
Comment on lines +325 to +328
"sample_voice_agent_live_text_conversation_async.py",
"sample_voice_agent_live_audio_conversation_async.py",
# PR #48484: recording not yet available for this REST-only sample.
"sample_voice_agent_basic_async.py",
Xiting Zhang and others added 2 commits September 3, 2026 15:56
…pupr

Resolves conflicts in sdk/ai/azure-ai-projects:
- CHANGELOG.md: combined voice-agent entries with main's content-safety
  moderation, ShellToolboxTool/WebIQPreviewTool, external_web_access, and
  sample-update entries (deduplicated bullets that existed on both sides);
  consolidated to a single Bugs Fixed section per release.
- tests/foundry_features_header/foundry_features_header_test_base.py: the
  expected Foundry-Features value for .agents.* methods now includes BOTH
  VoiceAgents=V1Preview (ours) and ModelRouterControls=V1Preview (main's),
  matching the already-correctly-merged _AGENT_OPERATION_FEATURE_HEADERS
  constant in azure/ai/projects/models/_patch.py.
- assets.json: our recordings tag and main's recordings tag had diverged
  (disjoint file sets - our 8 new voice-agent recordings vs main's 71
  updated sample/telemetry recordings). Restored main's tag, layered our
  voice-agent recordings on top, and pushed a new combined tag so both
  sides' recorded tests have working cassettes.
- tsp-location.yaml/.saved: main renamed this file to tsp-location.yaml.saved;
  followed main's convention for consistency.
- api.metadata.yml, apiview-properties.json, docs/public-methods.md: kept
  ours for now; will be regenerated via �zpysdk apistub . immediately
  after this merge to reflect the fully-merged API surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…merge

Follow-up to the merge commit: these are generated artifacts that were
resolved with a placeholder during conflict resolution. Regenerated via
`azpysdk apistub .` (api.md, apiview-properties.json - unchanged) and via
a fresh runtime introspection of AIProjectClient (docs/public-methods.md)
so they correctly reflect the fully-merged API surface (voice agents +
main's independent additions). Also corrected api.metadata.yml's
apiMdSha256, which the apistub tool did not update to match the
regenerated api.md.

Verified: full test suite passes (1038 passed, 119 skipped, 0 failed),
including the foundry_features_header tests and the recorded voice-agent
conversation tests against the newly-combined assets recordings tag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Multiple moderate issues remain in sample execution, live-test configuration, realtime handling, and test coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (9)

eng/tools/azure-sdk-tools/azpysdk/samples.py:96

  • This ignore list still allows both read-conversation samples to run, although they unconditionally read FOUNDRY_VOICE_CONVERSATION_ID and the new test resources cannot provide a pre-existing conversation ID. Because azpysdk samples executes every unignored Python sample, the samples check will fail with KeyError; add both read samples to this list as the package-level sample tests already do.
        "sample_voice_agent_live_audio_conversation_async.py",

scripts/devops_tasks/test_run_samples.py:98

  • This legacy runner also executes every unignored Python sample, so the two read-conversation samples will fail when FOUNDRY_VOICE_CONVERSATION_ID is absent. Keep this duplicate ignore table aligned with the azpysdk table and exclude both state-dependent samples.
        "sample_voice_agent_live_audio_conversation_async.py",

sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation.py:29

  • The included live conversation samples obtain the persisted ID from the session.created event, not conversation.created. Referencing the latter sends users looking for an event this client intentionally leaves untyped; update the instruction to match the demonstrated API.
    sdk/ai/azure-ai-projects/tests.yml:11
  • This switches the entire azure-ai-projects test run to live mode, but the accompanying Bicep template explicitly provisions only the voice-agent surface and omits resources needed by the package's existing datasets, evaluations, fine-tuning, storage, and connection tests. The manual pipeline will therefore run unrelated recorded tests against an incomplete live environment; filter the test job to dedicated voice markers/tests or provision the complete package resource set.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_realtime_live.py:222
  • This live test bypasses the configured FOUNDRY_VOICE_MODEL_NAME used by the other tests in this class. Any test deployment whose resource name is not literally gpt-realtime will fail before reaching the WebSocket behavior being tested. Use the prepared deployment name instead.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_realtime_live_async.py:219
  • The async function-tool test also hardcodes the model resource name instead of using the deployment supplied by servicePreparer. This makes the test fail in otherwise valid environments that deploy the realtime model under another name.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:82
  • Every test in this new module is unconditionally skipped, so none of the telephony request paths, response deserialization, or generated-audio operations execute in CI. Header-proxy tests alone cannot validate those contracts; add transport-mocked/recorded coverage that can run now, and keep only genuinely service-dependent success scenarios skipped.
    sdk/ai/azure-ai-projects/tests/samples/test_samples.py:409
  • After get_sample_paths filters out _async.py, this blacklist excludes every remaining voice sample, so this parametrized test has zero runnable cases. Commit recordings for at least the REST-only samples (or omit this no-op test until they are available) so the newly added samples receive actual execution coverage.
    sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py:328
  • These three entries are all of the async voice samples discovered by get_async_sample_paths, so this new test parametrizes no runnable case. Add a recording for the REST-only async sample (or defer this no-op test) rather than presenting an empty sample test suite.
  • Files reviewed: 74/78 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/_realtime.py
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations.py Outdated
Comment thread sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations_async.py Outdated
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor
[Pilot] PR Pipeline Failure Analysis

What failed

Azure Pipeline build 6787439 (python - pullrequest) failed for sdk/ai/azure-ai-projects across four static-analysis steps: CSpell, Pyright, Mypy, and Pylint. No failed test artifacts were reported — this is purely a static analysis / type-checking failure tied to the new telephony-transfer-target (PSTN) API surface.

Relevant pipeline output
##[error]sdk/ai/azure-ai-projects/CHANGELOG.md:15:680 - Unknown word (PSTN)
##[error]sdk/ai/azure-ai-projects/api.md:9031:36 - Unknown word (PSTN)
##[error]sdk/ai/azure-ai-projects/azure/ai/projects/models/__init__.py:303:5 - Unknown word (PSTN)
##[error]sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py:1724:5 - Unknown word (PSTN)
##[error]sdk/ai/azure-ai-projects/azure/ai/projects/models/_models.py:14413:5 - Unknown word (PSTN)
##[error]Spelling errors detected. To correct false positives ... see: https://aka.ms/azsdk/engsys/spellcheck

/mnt/vss/_work/1/s/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_operations.py:4307:15 - error:
  Overloaded implementation is not consistent with signature of overload 2
    Keyword parameter "etag" of type "List" is incompatible with type "str"
    "list[TelephonyTransferTarget]" is not assignable to "str" (reportInconsistentOverload)

/mnt/vss/_work/1/s/sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/_patch_agents_async.py:1272:17 - error:
  Argument of type "JSON | IO[bytes]" cannot be assigned to parameter "body" of type "IO[bytes]"
  .../_patch_agents_async.py:1274:22 - error: Argument of type "str" cannot be assigned to parameter "etag" of type "List"

azure/ai/projects/operations/_operations.py:9033: error: Overloaded function implementation does not accept
  all possible arguments of signature 2  [misc]
azure/ai/projects/aio/operations/_patch_agents_async.py:1270: error: No overload variant of
  "replace_telephony_transfer_targets" of "AgentsOperations" matches argument types
  "str", "MutableMapping[str, Any] | IO[bytes]", "list[TelephonyTransferTarget]", "str",
  "MatchConditions", "dict[str, Any]"  [call-overload]

=== SUMMARY ===
/mnt/vss/_work/1/s/sdk/ai/azure-ai-projects  pyright  FAIL(1)   34.43
/mnt/vss/_work/1/s/sdk/ai/azure-ai-projects  mypy     FAIL(1)   27.82
/mnt/vss/_work/1/s/sdk/ai/azure-ai-projects  pylint   FAIL(16)  43.26

Recommended next steps

  • Fix the replace_telephony_transfer_targets overloads on AgentsOperations (both sync azure/ai/projects/operations/_operations.py:9034 and async azure/ai/projects/aio/operations/_operations.py:4307) so the etag parameter type is consistent across all overloads and the implementation — one overload declares etag: List[TelephonyTransferTarget] while others/the implementation declare etag: str. This same mismatch causes the pyright reportInconsistentOverload, mypy [misc]/call-overload, and downstream call-site errors in _patch_agents.py / _patch_agents_async.py (~lines 1272-1307).
  • Add PSTN/pstn to the repository's CSpell dictionary to resolve the "Unknown word" errors in CHANGELOG.md, api.md, and the generated models files.
  • Re-run pylint after the overload fix — azure-ai-projects failed with exit code 16 while azure-ai-agents passed at 10.00/10; verify no independent pylint violations remain once the type signatures are corrected.
  • See https://aka.ms/ci-fix

Automated fix: Requested

Generated by Pipeline Analysis Next Steps · auto · 80 AIC · ⌖ 2.01 AIC · ⊞ 9.6K ·

…e PR review comments

CI fixes (build 6787439 Analyze stage):
- cspell: add PSTN/pstn (telephony acronym) to the allowed words list
- mypy/pyright: fix a genuine TypeSpec-emitter bug where the generated
  replace_telephony_transfer_targets JSON/IO[bytes]-body @overload stubs had
  etag/match_condition types swapped vs. the real implementation, in both sync
  and async _operations.py; also add a PostEmitter.ps1 fixup so this self-heals
  on future regenerations
- pylint: fix C0411 wrong-import-order in _realtime.py/aio/_realtime.py (the
  UserAgentPolicy import was placed after local imports)

PR #48484 review comment fixes:
- Add the two persisted-conversation voice samples to IGNORED_SAMPLES in both
  sample-runner copies (they require a pre-existing FOUNDRY_VOICE_CONVERSATION_ID
  that automation doesn't provide)
- test-resources-post.ps1: fail resource provisioning immediately on a
  deployment-readiness timeout instead of warning and continuing into live
  tests against a not-ready model
- tests.yml: restrict the live-test pipeline to voice-specific tests via a new
  live_test_only marker + TestMarkArgument, since this pipeline's Bicep only
  provisions the voice model, not the full package's resource set
- test_voice_agent_realtime_live(_async).py: use the prepared
  foundry_voice_model_name instead of a hardcoded "gpt-realtime" deployment name
- _realtime.py: fix a real bug in the sync WebSocket connect -- disable
  websockets' own default User-Agent (user_agent_header=None) and drop a
  caller-supplied subprotocols kwarg to prevent a collision with the fixed
  "realtime" subprotocol, matching the async implementation's existing handling;
  add 2 regression tests
- test_voice_agent_conversations(_async).py: make conversation completion a
  hard requirement for the audio assertions instead of a silent skip, so a
  future re-recording can't hide a regression in all four audio methods

Verified: 1042 passed, 119 skipped, 0 failed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Multiple moderate issues leave async error handling, model selection, and voice test coverage incomplete.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (5)

Previously missed (2) — in code that hasn't changed since the last review.

sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations.py:246

  • This loop can fall through after every item returns 404, so the test passes without finding any item audio even though the preceding comment says per-item audio is a hard requirement. Use the loop's else branch to fail when no iteration reaches break.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations_async.py:239
  • This loop can exhaust all items after 404 responses and still let the test pass, leaving both per-item audio methods effectively unverified. Fail via a for/else when no item reaches this break.

sdk/ai/azure-ai-projects/CHANGELOG.md:9

  • agents has no public update method (the generated surface exposes create_version and update_details), and voice-agent definitions are updated by creating a new immutable version. Referring users to create_version/update therefore points them to a nonexistent API.
  * Added guided authoring via `project_client.agents.generate_agent(GenerateVoiceAgentRequest(kind=AgentKind.VOICE, ...))`, which returns a service-generated starter definition that can be edited afterward through the standard `create_version`/`update` flow.

sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation.py:29

  • The referenced live-audio sample obtains the persisted ID from RealtimeServerEventSessionCreated, not from a conversation.created event. The current instruction sends users looking for an event that the sample never handles.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:82
  • All three tests in this new module are unconditionally skipped, so CI never exercises telephony response deserialization, service error handling, or generated-audio error mapping. The header-capture suite stops at request transmission; please add transport-mocked or recorded response tests that can run until the service routes are available.
  • Files reviewed: 75/79 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py
Comment thread sdk/ai/azure-ai-projects/test-resources-post.ps1
Xiting Zhang and others added 2 commits September 3, 2026 19:49
…odel output

sample_voice_agent_generate.py, sample_voice_agent_live_function_tool.py,
sample_voice_agent_live_text_conversation(_async).py, and
sample_voice_agent_live_audio_conversation_async.py print agent/LLM-generated
text (instructions, transcripts) that can contain characters (curly quotes,
em-dashes, etc.) outside some legacy, non-Unicode console encodings. When
stdout isn't a real interactive console (for example piped or redirected on
Windows, falling back to cp1252), a plain print() of that text can raise
UnicodeEncodeError and crash the sample outright -- reproduced live in
sample_voice_agent_live_audio_conversation_async.py.

Fix is scoped locally to each risky print() call site via a small _safe_print
helper (duplicated per file, matching this samples folder's existing
convention of self-contained, standalone scripts): try a normal print() first,
and only on UnicodeEncodeError fall back to replacing the unsupported
characters. This is not a global sys.stdout.reconfigure(): this package's own
sample_executor.py runs these samples in-process via exec_module() for
tests/samples/test_samples.py, so any global stdout mutation at module level
would leak into every other sample executed afterward in the same pytest
worker. The local, per-call fallback has no effect outside its own print
statement.

Verified live against the real service, including re-running the exact
sample/agent that originally crashed: no exception in any case, and zero data
loss (verified via PYTHONUTF8=1, matching how a modern terminal behaves) when
the console can actually represent the text; only the pathological
legacy-codepage-without-a-real-console case substitutes a placeholder
character instead of crashing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace hardcoded agent names with FOUNDRY_VOICE_AGENT_NAME env var
  (sample_voice_agent_versions.py, sample_voice_agent_with_tools.py)
- Use FOUNDRY_VOICE_MODEL env var instead of hardcoded model name
  (sample_voice_agent_live_function_tool.py)
- Use typed isinstance checks instead of dict/getattr dual-path access
  for conversation items and tools, since the SDK deserializes these to
  real typed model instances, not raw mappings
  (sample_voice_agent_live_function_tool.py, sample_voice_agent_with_tools.py)
- Remove unnecessary discriminator kwarg and its type: ignore suppression
  when constructing VoiceAgentMcpTool (sample_voice_agent_with_tools.py)
- Update docstrings for the newly-documented environment variables

All 3 changes verified end-to-end against the live service.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One critical live-test provisioning failure and three moderate preview/sample issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py:265

  • This top-level preview surface bypasses the client's documented allow_preview opt-in: Realtime.connect() always sends VoiceAgents=V1Preview, even when the client was constructed with the default allow_preview=False. Unlike .beta, client.realtime is a top-level API, and every added sample passes allow_preview=True, so accessing it without that consent should fail rather than silently enabling preview behavior. Apply the same guard to the async property.
    @property
    def realtime(self) -> Realtime:
        """Realtime streaming entry point for voice agents.

        :return: The realtime namespace, exposing ``connect(...)``.
        :rtype: ~azure.ai.projects.Realtime
        """
        if self._realtime is None:
            self._realtime = Realtime(self)

sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py:210

  • The async top-level realtime surface also silently opts callers into VoiceAgents=V1Preview even when allow_preview is left at its documented default of False. This differs from other top-level preview APIs and makes the explicit opt-in ineffective; mirror the sync guard here.
    @property
    def realtime(self) -> AsyncRealtime:
        """Realtime streaming entry point for voice agents.

        :return: The realtime namespace, exposing ``connect(...)``.
        :rtype: ~azure.ai.projects.aio.AsyncRealtime
        """
        if self._realtime is None:
            self._realtime = AsyncRealtime(self)
  • Files reviewed: 75/79 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread sdk/ai/azure-ai-projects/tests.yml
Comment on lines +135 to +140
elif isinstance(event, RealtimeServerEventResponseDone):
# A response.done that isn't a function call is the final answer for this turn.
if not any(
isinstance(item, RealtimeConversationItemFunctionCall) for item in (event.response.output or [])
):
return
…safety check

- tests.yml: add Location: eastus2. The shared archetype-sdk-tests template
  defaults this cloud's region to westus when unset, but gpt-realtime
  (GlobalStandard, 2025-08-28) is only deployable in eastus2/centralus/
  canadacentral per the official Azure OpenAI region-availability docs, so
  resource provisioning was failing before any live test could run.

- sample_voice_agent_live_function_tool.py: restore the dict/getattr
  dual-path check for identifying a function-call item in response.done's
  output list, in place of a pure isinstance() check introduced by an
  earlier cleanup pass. response.output is a documented open/extensible
  union; an item kind not yet mapped by this SDK version can surface as a
  plain mapping instead of a typed model, and the isinstance-only check
  would silently mistake the first tool-call turn for the final answer,
  closing the connection before the tool result is ever sent. Matches the
  same defensive pattern already used by the SDK's own
  test_voice_agent_realtime_live.py live test.

Both changes verified: tests.yml against a live re-run of the affected
pytest suite; the sample against a fresh live get_weather tool-call
round-trip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A critical bearer-token authentication defect and unresolved realtime sequencing, sample-runner, and test-coverage issues block approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (6)

Previously missed (4) — in code that hasn't changed since the last review.

sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations.py:226

  • If every item returns 404, this loop falls through and the test passes without ever exercising get_agent_conversation_item_audio_content, despite the preceding comment making per-item audio coverage a hard requirement. Track whether an item with audio was found and fail after the loop when none was found, so a cassette cannot silently omit this API coverage.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_conversations_async.py:218
  • If every item returns 404, this loop falls through and the test passes without ever exercising get_agent_conversation_item_audio_content, despite the preceding comment making per-item audio coverage a hard requirement. Track whether an item with audio was found and fail after the loop when none was found, so a cassette cannot silently omit this API coverage.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_realtime_live.py:267
  • The follow-up response is created before the current function-call response emits response.done. The realtime protocol can reject this as a concurrent active response; the existing Voice Live test waits for response.done before adding the tool output and calling response.create (sdk/voicelive/azure-ai-voicelive/tests/live/test_live_realtime_tools.py:271-281). Defer these operations until the function-call response completes.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_realtime_live_async.py:265
  • The follow-up response is created before the current function-call response emits response.done. The realtime protocol can reject this as a concurrent active response; the existing Voice Live test waits for response.done before adding the tool output and calling response.create (sdk/voicelive/azure-ai-voicelive/tests/live/test_live_realtime_tools.py:271-281). Defer these operations until the function-call response completes.

sdk/ai/azure-ai-projects/tests/samples/test_samples.py:390

  • This parameterization currently discovers no tests: every synchronous sample_*.py in samples/agents/voice is present in samples_to_skip. Pytest therefore reports only an empty-parameter skip, so this method provides no sample execution coverage. Add a recording for at least the REST-only samples (or remove the placeholder test until one is runnable).
    sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py:320
  • This parameterization currently discovers no tests: all three asynchronous sample_*.py files in samples/agents/voice are listed in samples_to_skip. The new test is therefore always an empty-parameter skip and exercises no sample. Add a recording for the REST-only basic sample (or remove the placeholder until it is runnable).
  • Files reviewed: 75/79 changed files
  • Comments generated: 4
  • Review effort level: Balanced

_args, kwargs = fake_session.ws_connect.call_args
assert _args[0].startswith("wss://my-account.services.ai.azure.com")
assert kwargs["params"]["api-version"] == "v1"
assert kwargs["headers"]["Authorization"] == "Bearer fake-token"
"sample_voice_agent_live_text_conversation_async.py",
# Runs until Ctrl-C (continuous microphone capture/playback); would hang indefinitely
# under this non-interactive runner whenever PyAudio and live credentials are available.
"sample_voice_agent_live_audio_conversation_async.py",
"sample_voice_agent_live_text_conversation_async.py",
# Runs until Ctrl-C (continuous microphone capture/playback); would hang indefinitely
# under this non-interactive runner whenever PyAudio and live credentials are available.
"sample_voice_agent_live_audio_conversation_async.py",
Comment on lines +126 to +129
conn.conversation.item.create(
item=RealtimeConversationItemFunctionCallOutput(call_id=event.call_id, output=result)
)
conn.response.create()
Xiting Zhang and others added 4 commits September 4, 2026 11:01
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pupr

Resolves conflicts in api.metadata.yml, assets.json, and docs/public-methods.md.
Since main already shipped 2.6.0 without Voice Agents, retargets Voice Agents
work to a new 2.7.0b1 (beta) CHANGELOG section / package version instead of
folding it into the already-released 2.6.0 section, leaving main's 2.6.0
CHANGELOG content and all prior versions untouched.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Preview opt-in, realtime response sequencing, and executable test-coverage issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (8)

sdk/ai/azure-ai-projects/CHANGELOG.md:9

  • .agents.update() was removed from this package; the existing changelog explicitly directs users to create_version() instead (CHANGELOG.md:406-407). Referring to an update flow here sends users to a nonexistent API.
  * Added guided authoring via `project_client.agents.generate_agent(GenerateVoiceAgentRequest(kind=AgentKind.VOICE, ...))`, which returns a service-generated starter definition that can be edited afterward through the standard `create_version`/`update` flow.

sdk/ai/azure-ai-projects/azure/ai/projects/_patch.py:269

  • This top-level preview entry point bypasses the client's explicit preview opt-in. allow_preview defaults to False, but Realtime.connect() defaults foundry_features to VoiceAgents=V1Preview and the connection manager always sends that header, so client.realtime.connect(...) opts in even when the caller did not. Gate this property/connection on self._config.allow_preview, or omit the preview header unless the client was opted in.
    @property
    def realtime(self) -> Realtime:
        """Realtime streaming entry point for voice agents.

        :return: The realtime namespace, exposing ``connect(...)``.
        :rtype: ~azure.ai.projects.Realtime
        """
        if self._realtime is None:
            self._realtime = Realtime(self)
        return self._realtime

sdk/ai/azure-ai-projects/azure/ai/projects/aio/_patch.py:215

  • The async realtime entry point also bypasses the client's explicit preview opt-in: AsyncRealtime.connect() supplies VoiceAgents=V1Preview unconditionally, even when allow_preview=False. Make the async path enforce the same opt-in contract as the other top-level preview APIs.
    @property
    def realtime(self) -> AsyncRealtime:
        """Realtime streaming entry point for voice agents.

        :return: The realtime namespace, exposing ``connect(...)``.
        :rtype: ~azure.ai.projects.aio.AsyncRealtime
        """
        if self._realtime is None:
            self._realtime = AsyncRealtime(self)
        return self._realtime

sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_function_tool.py:129

  • response.function_call_arguments.done completes the arguments, but the current response is still active until its subsequent response.done. Starting response.create() here can be rejected as a concurrent response; the repository's VoiceLive flow waits for response.done before adding the tool output and creating the follow-up response (sdk/voicelive/azure-ai-voicelive/tests/live/test_live_realtime_tools.py:271-281). Defer the output/follow-up until that event.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:33
  • Every test in this new module is unconditionally skipped, so none of the advertised telephony, transfer-target, or generated-audio behavior is exercised in CI. Please add transport-mocked/recorded coverage for request serialization and response handling, or defer these tests until they can run.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony_async.py:33
  • Every async test in this new module is unconditionally skipped, so the async telephony and generated-audio paths receive no executable coverage. Please add transport-mocked/recorded cases or defer these placeholders until they can run.
    sdk/ai/azure-ai-projects/tests/samples/test_samples.py:409
  • This blacklist excludes every synchronous sample_*.py currently present under samples/agents/voice, so parametrization produces no runnable cases and this test adds no sample coverage. Record the REST-only samples (or otherwise provide executable fixtures) and remove them from this list.
    sdk/ai/azure-ai-projects/tests/samples/test_samples_async.py:328
  • These entries, together with the WebSocket skip above, exclude all three asynchronous voice samples, leaving this parametrized test with zero runnable cases. Add a recording/fixture for the REST-only sample and stop skipping it so the async sample suite exercises at least one new sample.
  • Files reviewed: 77/81 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment on lines +264 to +267
conn.conversation.item.create(
item=RealtimeConversationItemFunctionCallOutput(call_id=event.call_id, output=result)
)
conn.response.create()
Comment on lines +261 to +264
await conn.conversation.item.create(
item=RealtimeConversationItemFunctionCallOutput(call_id=event.call_id, output=result)
)
await conn.response.create()
Comment on lines +27 to +29
3) FOUNDRY_VOICE_CONVERSATION_ID - The id of a persisted conversation
(captured from the `conversation.created` event during a live session,
see sample_voice_agent_live_audio_conversation_async.py).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unresolved moderate issues affect public API usability, sample execution, documentation, and telephony test coverage.

Review details

Suppressed comments (8)

eng/tools/azure-sdk-tools/azpysdk/samples.py:93

  • This ignore list omits sample_voice_agent_live_function_tool.py. That sample also opens a real client.realtime WebSocket (and the package sample suite skips it for that reason), so the azpysdk sample check will execute it non-interactively and attempt a live connection. Add it beside the other realtime samples.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",

scripts/devops_tasks/test_run_samples.py:95

  • This runner has the same missing exclusion for sample_voice_agent_live_function_tool.py. Since that sample requires an uncaptured live WebSocket, running it in this non-interactive sample job will fail instead of exercising a replayable sample.
        "sample_voice_agent_live_text_conversation.py",
        "sample_voice_agent_live_text_conversation_async.py",

sdk/ai/azure-ai-projects/.env.template:27

  • This comment is inaccurate: foundry_voice_model_name is also consumed by the conversation setup, telephony tests, and realtime live tests, not only the CRUD tests. Update it so contributors know which tests require this environment variable.
# Read by the recorded voice-agent CRUD tests only (tests/test_base.py), not by any sample.
FOUNDRY_VOICE_MODEL_NAME=

sdk/ai/azure-ai-projects/azure/ai/projects/aio/operations/init.py:22

  • The async VoiceAgentWebSocketOperations export has no corresponding AIProjectClient.voice_agent_web_socket attribute either, despite the generated class directing users there. Remove this unusable public surface or expose a functional async operation group consistently with the intended async_client.realtime API.
from ._operations import VoiceAgentWebSocketOperations  # type: ignore

sdk/ai/azure-ai-projects/azure/ai/projects/operations/init.py:22

  • VoiceAgentWebSocketOperations is exported as public API, but AIProjectClient never creates the documented voice_agent_web_socket attribute; the supported implementation is client.realtime. Users following this generated operation class's documentation therefore get AttributeError. Hide this generated WebSocket operation group or wire it to a usable client surface consistently.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation.py:29
  • This instruction names a conversation.created event, but the referenced live sample obtains the ID from RealtimeServerEventSessionCreated (session.created). Following the current text leaves users waiting for an event this client does not handle; point them to session.created instead.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony.py:37
  • Every test in this newly added module is unconditionally skipped, so the sync telephony and generated-audio APIs receive no executable functional coverage. Before this feature is merged, add transport-mocked/recorded coverage that runs in CI (or enable these tests once the routes are available); placeholder tests cannot detect regressions.
    sdk/ai/azure-ai-projects/tests/agents/test_voice_agent_telephony_async.py:37
  • Every test in this newly added async module is unconditionally skipped, so the async telephony and generated-audio APIs receive no executable functional coverage. Before this feature is merged, add transport-mocked/recorded coverage that runs in CI (or enable these tests once the routes are available); placeholder tests cannot detect regressions.
  • Files reviewed: 77/81 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Regenerate from azure-rest-api-specs commit b538ac90619e094630e3c773d5231070809caf48 and wire preview headers for sync and async agent telephony operations.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Broad preview SDK changes retain unresolved realtime URL-encoding and sample-correctness issues.

Review details

Suppressed comments (4)

sdk/ai/azure-ai-projects/docs/public-methods.md:15

  • The new agent_endpoint_conversations and agent_telephony methods are counted as stable solely because they are top-level, but this PR identifies voice agents as preview and gates these calls on VoiceAgents=V1Preview/allow_preview. Calling all 97 methods “stable” misstates their support status; update GeneratePublicMethods.ps1 to distinguish top-level placement from API stability, then regenerate this document.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation.py:30
  • The setup command omits python-dotenv, although this sample imports dotenv below. In a clean environment following these instructions, the sample fails before it can run.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_live_text_conversation_async.py:27
  • The setup command omits python-dotenv, although this sample imports dotenv below. In a clean environment following these instructions, the sample fails before it can run.
    sdk/ai/azure-ai-projects/samples/agents/voice/sample_voice_agent_read_conversation.py:29
  • The referenced live-audio sample reads this ID from RealtimeServerEventSessionCreated, whose wire event is session.created; it does not capture a conversation.created event. Naming the wrong event sends users looking for an event the example never handles.
  • Files reviewed: 79/83 changed files
  • Comments generated: 5
  • Review effort level: Balanced

base = endpoint.rstrip("/")
if base.startswith("https://"):
base = "wss://" + base[len("https://") :]
return f"{base}/agents/{agent_name}/endpoint/protocols/voice"
base = endpoint.rstrip("/")
if base.startswith("https://"):
base = "wss://" + base[len("https://") :]
return f"{base}/agents/{agent_name}/endpoint/protocols/voice"
Comment on lines +265 to +273
project_client.agents.create_version(
agent_name=agent_name,
definition=VoiceAgentDefinition(
model_type=definition.model_type, # type: ignore[attr-defined]
model=definition.model, # type: ignore[attr-defined]
instructions=definition.instructions, # type: ignore[attr-defined]
store=True,
),
)
Comment on lines +264 to +272
await project_client.agents.create_version(
agent_name=agent_name,
definition=VoiceAgentDefinition(
model_type=definition.model_type, # type: ignore[attr-defined]
model=definition.model, # type: ignore[attr-defined]
instructions=definition.instructions, # type: ignore[attr-defined]
store=True,
),
)
FOUNDRY_PROJECT_ENDPOINT=
FOUNDRY_PROJECT_API_KEY=
FOUNDRY_MODEL_NAME=
# Read by the recorded voice-agent CRUD tests only (tests/test_base.py), not by any sample.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants