Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build and deploy AI applications on Azure AI Foundry using Microsoft's model catalog and AI services
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
foundry-agent/invocations-ws/references/invocations-ws-protocol.md
1# Invocations WebSocket Protocol Guide23The `invocations_ws` protocol is a **duplex WebSocket pass-through**. After the platform authenticates the upgrade request and routes it to your container, every frame in both directions is forwarded as-is. The agent developer defines the wire format, the framing model, and the streaming semantics. Unlike `responses` (OpenAI-compatible, platform-managed history) and `invocations` (single HTTP request/response, bytes in / bytes out), `invocations_ws` is a long-lived bidirectional channel under full container control.45## Input/Output Contract67| Aspect | `responses` | `invocations` | `invocations_ws` |8|--------|-------------|---------------|------------------|9| **Transport** | HTTPS request/response | HTTPS request/response | WebSocket (`wss://`) |10| **Lifetime** | Per request | Per request | Long-lived duplex connection |11| **Input** | Natural language `inputText` | Raw HTTP request body | Sequence of WS frames in either direction |12| **Output** | Structured OpenAI JSON | Raw response bytes | Sequence of WS frames in either direction |13| **Framing** | n/a (single body) | n/a (single body) | Developer-defined: binary (PCM, protobuf), text (JSON), or mixed |14| **Streaming** | `stream: true` (SSE) | Agent-controlled (SSE-over-HTTP, etc.) | Native — duplex by definition |15| **History** | Platform via `conversationId` | Agent-managed | Agent-managed; keyed by `agent_session_id` |1617## URL and Headers1819```20wss://{account}.services.ai.azure.com21/api/projects/agents/endpoint/protocols/invocations_ws22?project_name={project}23&agent_name={agentName}24&agent_session_id={sessionId}25&foundry_features=HostedAgents=V1Preview26```2728| Query parameter | Required | Notes |29|-----------------|----------|-------|30| `project_name` | ✅ | Foundry project name (the segment after `/api/projects/` in the project endpoint) |31| `agent_name` | ✅ | Hosted agent name as declared in `agent.yaml` |32| `agent_session_id` | ❌ | Per-connection identifier — see [Session Management](../../invoke/references/session-management.md). If omitted, the platform (or the container) generates a random id |33| `foundry_features` | ✅ (preview) | Must be `HostedAgents=V1Preview` while the protocol is in preview. May alternatively be sent as the `Foundry-Features` request header. |3435| Header | Required | Notes |36|--------|----------|-------|37| `Authorization: Bearer <token>` | ✅ | Entra token for audience `https://ai.azure.com` (scope `https://ai.azure.com/.default`) — `az account get-access-token --resource https://ai.azure.com`. Validated by APIM and the Agents service; the container does **not** see this header. |38| `Foundry-Features: HostedAgents=V1Preview` | ✅ (preview) | Required unless the equivalent `foundry_features` query parameter is set. |3940The container receives the upgrade on path `/invocations_ws`. Inside the container, read the session id from the `FOUNDRY_AGENT_SESSION_ID` environment variable (set by `azure-ai-agentserver-invocations`), or fall back to the `agent_session_id` query string.4142> ⚠️ **Browsers cannot set the `Authorization` header on a `WebSocket`.** Browser clients must connect through a thin server-side proxy that adds the header before forwarding. This is a browser API limitation, not a Foundry requirement.4344## Pass-Through Semantics4546The platform is a transparent relay:4748- **No schema validation.** Binary opcodes, text JSON, protobuf, raw PCM — anything ends up at the container untouched.49- **No transcoding.** Sample rate, codec, byte order are entirely between caller and container.50- **No history.** Nothing is persisted by Foundry between connections. Use the container filesystem or an external store, keyed by `agent_session_id`, if you need continuity.51- **No platform-managed turn taking.** There is no concept of "request" vs "response" — both sides may send frames at any time. Implement your own request/reply correlation if you need it (e.g. include an `id` field in each JSON frame).5253## Common Framing Patterns5455These are protocols developers build **on top of** the raw WebSocket. The platform does not require, parse, or validate any of them; they are listed for orientation only.5657| Pattern | Typical use | Notes |58|---------|-------------|-------|59| **Raw binary media frames** | Voice agents (PCM, Opus) | Binary opcode; agree on sample rate, channels, bit depth out-of-band |60| **Length-prefixed protobuf** | Real-time pipeline frameworks | Each WS frame is one serialized message; control + audio multiplexed |61| **JSON control + binary media** | Mixed signaling | Text frames carry control (e.g. start/stop, RTVI events), binary frames carry media |62| **Pure JSON signaling** | Out-of-band media transports (WebRTC offer/answer/ICE, SFU join tokens) | One JSON object per frame; FIFO request/reply if the protocol is purely turn-based |63| **SSE-style event stream** | One-way server push of events | Text frames; the WS is effectively used as a richer SSE |6465## Discovering the Expected Wire Format6667> ⚠️ **Do not guess.** The platform exposes no OpenAPI / AsyncAPI surface for `invocations_ws` agents. The contract lives in the container code.6869### 1. Inspect the WebSocket Handler7071Look at the function decorated with `@app.ws_handler` on an `InvocationAgentServerHost` (the `azure-ai-agentserver-invocations` SDK). The handler determines:7273- Whether frames are binary, text, or mixed74- The expected first frame (handshake, capabilities, auth challenge)75- The control vocabulary (start, stop, mute, hangup, etc.)76- The response cadence (turn-based vs free-running)7778### 2. Ask the User or Author7980If the handler isn't available, ask the agent author for the framing spec before connecting.8182## Examples8384**Connect from a Python client (no browser proxy):**8586```python87import os, uuid, websockets # requires websockets >= 12 for the additional_headers kwarg below8889token = os.popen("az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv").read().strip()90url = (91"wss://{account}.services.ai.azure.com/api/projects/agents/endpoint/protocols/invocations_ws"92"?project_name={project}&agent_name={name}"93f"&agent_session_id={uuid.uuid4().hex}"94"&foundry_features=HostedAgents=V1Preview"95)9697# websockets >= 12 uses `additional_headers`; older versions (<12) expect `extra_headers`.98async with websockets.connect(url, additional_headers={"Authorization": f"Bearer {token}"}) as ws:99await ws.send(b"<first frame in your wire format>")100async for frame in ws:101... # frame is bytes (binary) or str (text) depending on what the container sends102```103104**Connect from a browser** — terminate a local WebSocket in a server-side proxy that injects the token, then forward frames pass-through to the upstream `wss://`.105106## Error Handling107108| Error | Cause | Resolution |109|-------|-------|------------|110| 401 / 403 on upgrade | Missing or expired Entra token | Re-mint with `az account get-access-token --resource https://ai.azure.com` |111| 404 on upgrade | Wrong `project_name` or `agent_name`, missing preview flag, or unsupported region | Verify with `agent_get`; ensure `foundry_features=HostedAgents=V1Preview` is set; confirm the deployed version uses `protocol: invocations_ws` and that the region is supported per [Hosted Agents region availability](https://learn.microsoft.com/azure/foundry/agents/concepts/hosted-agents#region-availability) |112| WS closes after accept | Container raised in the handler | Tail logs with `azd ai agent monitor --session-id <agent_session_id> --follow` |113| Frames silently dropped | Wire-format mismatch (binary vs text, wrong schema) | Confirm both ends agree on framing — the platform performs no transcoding |114| State lost on reconnect | Different `agent_session_id` used | Reuse the same `agent_session_id` to land on the same logical state inside the container |115| Browser fails with `1006 abnormal closure` | Browser tried to connect directly with no `Authorization` | Route through a server-side proxy that adds the header |116