Server¶
TAC server adapter for Hosted Agents in Foundry Agent Service. Requires the
hosted-agents extra.
tac_microsoft.hosted_agents_server ¶
TACHostedAgentsApp: TAC server for Hosted Agents in Foundry Agent Service.
Hosted Agents (the Foundry Agent Service runtime under
azure-ai-agentserver-invocations) exposes exactly two endpoints —
POST /invocations and WS /invocations_ws — bound to a fixed port,
with sandbox affinity keyed on an agent_session_id query parameter set
by APIM upstream. TACFastAPIServer's five-route layout doesn't fit
that contract, so this server is a peer implementation that overloads the
two fixed routes:
/invocationsdispatches by payload shape:- Twilio Conversation Orchestrator JSON envelope → fan out to every
registered channel's
process_webhook(body, idempotency_token), matchingTACFastAPIServer.conversation_webhook. -
Twilio voice TwiML form (APIM converts form → JSON before this point; we detect by
CallSid+ absenteventType) → callvoice_channel.handle_incoming_call(...)and return the TwiML XML. -
/invocations_wswraps the StarletteWebSocketinStarletteWebSocketAdapterand delegates tovoice_channel.handle_websocket(...).
Auth (HMAC) is handled by APIM upstream, not here. The server validates
that agent_session_id (set by APIM from the upstream request) matches
the body's conversation/call ID for correctness — this is a sandbox-affinity
invariant, not a security check. It also dedups CO retries by Twilio's
i-twilio-idempotency-token header in a bounded LRU.
For voice, agent_session_id reaches Foundry on the /invocations_ws
upgrade as a query string parameter. To make that happen, this server
emits the wss URL with ?agent_session_id=<CallSid> already attached
in the <ConversationRelay> TwiML, and additionally lists it as a
<Parameter> child (Twilio CR appends those to the upgrade URL too —
belt + suspenders so APIM has at least one place to pluck it from).
StarletteWebSocketAdapter ¶
StarletteWebSocketAdapter(websocket: WebSocket)
Adapt a Starlette WebSocket to WebSocketProtocol.
Equivalent to tac.server.FastAPIWebSocketAdapter but for the raw
Starlette WebSocket type that InvocationAgentServerHost exposes.
Two Hosted-Agents specifics:
accept()is a no-op.InvocationAgentServerHostaccepts the socket before the user handler runs; calling accept again raises.VoiceChannel.handle_websocketcalls accept once at the top, so we silently swallow it.WebSocketDisconnect(Starlette) is translated toWebSocketDisconnectErrorso VoiceChannel's framework-agnostic exception handling works.
TACHostedAgentsApp ¶
TACHostedAgentsApp(
tac: TAC,
voice_channel: VoiceChannel | None = None,
messaging_channels: list[MessagingChannel]
| None = None,
*,
idempotency_cache_size: int = 4096,
)
TAC server hosted inside Hosted Agents in Foundry Agent Service.
Mirrors the public surface of TACFastAPIServer so it's a drop-in
peer for users deploying to Foundry instead of Container Apps::
connector = AgentFrameworkConnector(tac=tac, create_agent=...)
server = TACHostedAgentsApp(
tac=tac,
voice_channel=connector.voice_channel,
messaging_channels=[connector.sms_channel],
)
server.start()
Voice URL config is read from tac.config (a TACConfig):
voice_public_domain should be the APIM hostname plus any path
prefix the Twilio API points at (e.g.
my-apim.azure-api.net/twilio). The wss URL emitted to Twilio is
wss://{voice_public_domain}{voice_websocket_path}?agent_session_id={CallSid};
APIM rewrites that down to /invocations_ws on the Foundry backend.
Voice URL fields are read from tac.config (voice_public_domain /
voice_websocket_path / voice_action_path). Configure a welcome
greeting via VoiceChannelConfig.default_twiml_options on the voice
channel.
start ¶
start() -> None
Run the Hosted Agents server until exit.
InvocationAgentServerHost.run() binds to 0.0.0.0 on the
port the platform passes via $PORT.