From 9b2acd93c4e7cfb4b88eea9c0d1d9a52dcf884a7 Mon Sep 17 00:00:00 2001 From: Yulin Li Date: Mon, 13 Jul 2026 10:00:03 +0800 Subject: [PATCH] Align invocations_ws samples with GA (drop V1Preview gate) (#681) PR #221 in foundrysdk_specs flips the hosted-agent `invocations_ws` WebSocket endpoint to GA, removing the `HostedAgents=V1Preview` preview gate that callers previously sent via the `Foundry-Features` header (or `foundry_features` query parameter). The samples already build the GA path-based URL (`/api/projects/{project}/agents/{agent}/endpoint/protocols/invocations_ws` with `api-version=v1`), so the only client-facing drift was the lingering preview header. This removes it from every WebSocket client (Python and C#) and updates the doc mentions accordingly. Co-authored-by: Yulin Li Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../src/hello-world-dotnet-invocations-ws/E2ELocal/Program.cs | 1 - .../chat_client/Proxy/Program.cs | 3 +-- .../src/duplex-live-agent/chat_client/proxy.py | 2 -- .../duplex-live-agent/src/duplex-live-agent/e2e_local.py | 1 - .../hello-world/src/hello-world/chat_client/proxy.py | 2 -- .../invocations_ws/hello-world/src/hello-world/e2e_local.py | 1 - .../livekit-server/src/livekit-server/chat_client/README.md | 2 +- .../livekit-server/src/livekit-server/chat_client/upstream.py | 1 - .../bring-your-own/invocations_ws/pipecat-webrtc/README.md | 2 +- .../pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/README.md | 2 +- .../src/pipecat-ws-webrtc/chat_client/upstream.py | 1 - .../src/pipecat-ws-server/chat_client/README.md | 2 +- .../src/pipecat-ws-server/chat_client/upstream.py | 3 +-- 13 files changed, 6 insertions(+), 17 deletions(-) diff --git a/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/E2ELocal/Program.cs b/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/E2ELocal/Program.cs index 8744abbd0..2a96679ae 100644 --- a/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/E2ELocal/Program.cs +++ b/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/E2ELocal/Program.cs @@ -76,7 +76,6 @@ targetUrl = url ?? BuildFoundryUrl(foundryEndpoint.TrimEnd('/'), agent, sessionId, apiVersion); var token = await GetEntraTokenAsync(); headers["Authorization"] = $"Bearer {token}"; - headers["Foundry-Features"] = "HostedAgents=V1Preview"; } else { diff --git a/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/chat_client/Proxy/Program.cs b/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/chat_client/Proxy/Program.cs index 9976a73a6..e7ae949d8 100644 --- a/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/chat_client/Proxy/Program.cs +++ b/samples/csharp/hosted-agents/bring-your-own/invocations_ws/HelloWorld/src/hello-world-dotnet-invocations-ws/chat_client/Proxy/Program.cs @@ -9,7 +9,7 @@ * • serves the static page (chat_client/index.html) on / and /index.html; * • upgrades inbound /invocations_ws to a WebSocket; * • opens an outbound WebSocket to the Foundry hosted agent endpoint - * with Authorization: Bearer + Foundry-Features headers; + * with Authorization: Bearer header; * • forwards binary and text frames in both directions verbatim. * * Run: @@ -108,7 +108,6 @@ using var upstream = new ClientWebSocket(); upstream.Options.SetRequestHeader("Authorization", $"Bearer {token}"); - upstream.Options.SetRequestHeader("Foundry-Features", "HostedAgents=V1Preview"); try { await upstream.ConnectAsync(new Uri(foundryWsUrl), ctx.RequestAborted); diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/chat_client/proxy.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/chat_client/proxy.py index 5f2f87f29..6cd6b44d5 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/chat_client/proxy.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/chat_client/proxy.py @@ -138,7 +138,6 @@ async def _warmup_session(args, session_id: str) -> None: return headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } print(f"[proxy] warming up sandbox session={session_id}") try: @@ -165,7 +164,6 @@ async def handler(browser_ws): return headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } print(f"[proxy] {peer} -> {url}") try: diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/e2e_local.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/e2e_local.py index 5ad5ff20d..1218c112b 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/e2e_local.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/duplex-live-agent/src/duplex-live-agent/e2e_local.py @@ -181,7 +181,6 @@ def main() -> int: token = _entra_token() headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } else: url = args.url or "ws://localhost:8088/invocations_ws" diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/chat_client/proxy.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/chat_client/proxy.py index 5f2f87f29..6cd6b44d5 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/chat_client/proxy.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/chat_client/proxy.py @@ -138,7 +138,6 @@ async def _warmup_session(args, session_id: str) -> None: return headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } print(f"[proxy] warming up sandbox session={session_id}") try: @@ -165,7 +164,6 @@ async def handler(browser_ws): return headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } print(f"[proxy] {peer} -> {url}") try: diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/e2e_local.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/e2e_local.py index 5ad5ff20d..1218c112b 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/e2e_local.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/hello-world/src/hello-world/e2e_local.py @@ -181,7 +181,6 @@ def main() -> int: token = _entra_token() headers = { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } else: url = args.url or "ws://localhost:8088/invocations_ws" diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/README.md b/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/README.md index 57f3b1444..99cf1ced5 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/README.md +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/README.md @@ -20,7 +20,7 @@ The proxy: `LIVEKIT_AGENT_NAME` (Foundry-hosted mode). - In Foundry mode, fetches an Entra Bearer token via `az account get-access-token --resource https://ai.azure.com` and - attaches it + the `Foundry-Features: HostedAgents=V1Preview` header. + attaches it as the `Authorization` header. - Forwards signaling JSON verbatim in both directions. ## Install diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/upstream.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/upstream.py index e49d33d75..3c4db3c37 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/upstream.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/livekit-server/src/livekit-server/chat_client/upstream.py @@ -91,5 +91,4 @@ def resolve() -> tuple[str, str, dict[str, str]]: logger.info("foundry mode session_id=%s", sid) return url, sid, { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/README.md b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/README.md index 634539f5e..62315ec91 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/README.md +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/README.md @@ -19,7 +19,7 @@ A real-time voice agent hosted as a **Bring Your Own** WebSocket container that Why a single `/invocations_ws` endpoint? This sample lives under the `bring-your-own/invocations_ws/` tree, where the Foundry contract is "one duplex WebSocket called `invocations_ws`". We keep that surface here but use the WebSocket purely for **WebRTC signaling** (offer / answer / ICE candidates / ICE config). Audio still flows over the WebRTC peer connection and never touches the signaling WebSocket. -The browser client lives in [`chat_client/`](chat_client/) — a small FastAPI portal that terminates the browser's WebSocket, attaches the Entra token + `Foundry-Features` header in hosted mode, and forwards signaling JSON to the upstream `/invocations_ws`. The same proxy works against either a local server or the Foundry-hosted agent. +The browser client lives in [`chat_client/`](chat_client/) — a small FastAPI portal that terminates the browser's WebSocket, attaches the Entra token as the `Authorization` header in hosted mode, and forwards signaling JSON to the upstream `/invocations_ws`. The same proxy works against either a local server or the Foundry-hosted agent. ## ⚠️ Security Warning diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/README.md b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/README.md index 87a1f730c..16b72ded3 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/README.md +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/README.md @@ -18,7 +18,7 @@ The proxy: `PIPECAT_WEBRTC_AGENT_NAME` (Foundry-hosted mode). - In Foundry mode, fetches an Entra Bearer token via `az account get-access-token --resource https://ai.azure.com` and - attaches it + the `Foundry-Features: HostedAgents=V1Preview` header. + attaches it as the `Authorization` header. - Forwards signaling JSON verbatim in both directions. ## Install diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/upstream.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/upstream.py index 0557c220a..3d2664ab3 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/upstream.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-webrtc/src/pipecat-ws-webrtc/chat_client/upstream.py @@ -91,5 +91,4 @@ def resolve() -> tuple[str, str, dict[str, str]]: logger.info("foundry mode session_id=%s", sid) return url, sid, { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", } diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/README.md b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/README.md index 754e9c3fb..0df0ff604 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/README.md +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/README.md @@ -15,7 +15,7 @@ The proxy: `PIPECAT_WEBSOCKET_AGENT_NAME` (Foundry-hosted mode). - In Foundry mode, fetches an Entra Bearer token via `az account get-access-token --resource https://ai.azure.com` and - attaches it + the `Foundry-Features: HostedAgents=V1Preview` header. + attaches it as the `Authorization` header. - Transcodes between the browser's simple PCM/JSON wire format and pipecat's protobuf framing so the page stays protocol-agnostic. diff --git a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/upstream.py b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/upstream.py index 08f590cea..67eb90529 100644 --- a/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/upstream.py +++ b/samples/python/hosted-agents/bring-your-own/invocations_ws/pipecat-ws-server/src/pipecat-ws-server/chat_client/upstream.py @@ -75,7 +75,7 @@ def resolve() -> tuple[str, str, dict[str, str]]: """Return ``(upstream_url, session_id, headers)``. Local mode: ``(LOCAL_URL value, "", {})`` — no auth. - Foundry mode: ``(public_url, generated_sid, {Authorization, Foundry-Features})``. + Foundry mode: ``(public_url, generated_sid, {Authorization})``. """ local = os.environ.get(LOCAL_ENV, "").strip() if local: @@ -95,5 +95,4 @@ def resolve() -> tuple[str, str, dict[str, str]]: logger.info("foundry mode session_id=%s", sid) return url, sid, { "Authorization": f"Bearer {token}", - "Foundry-Features": "HostedAgents=V1Preview", }