Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: hello-world-dotnet-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: human-in-the-loop-dotnet-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: notetaking-agent-dotnet-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: hello-world-dotnet-invocations-ws
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "1"
memory: 2Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: hello-world-dotnet-responses
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: background-agent-dotnet-responses
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ metadata:
- .NET
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ metadata:
- .NET
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: notetaking-agent-dotnet-responses
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: ag-ui-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ metadata:
- Bring Your Own
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: diagnostic-agent-python-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: |
BYO Invocations agent that Event Grid POSTs to directly using the system topic's system-assigned managed identity with audience https://ai.azure.com. Receives Storage BlobCreated events, downloads the blob with the per-agent Microsoft Entra identity, and writes a summary back to a sibling container.
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: github-copilot-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: '0.25'
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: hello-world-python-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: human-in-the-loop-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ metadata:
- Python
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ kind: hosted
name: notetaking-agent-invocations-python
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: toolbox-python-invocations
protocols:
- protocol: invocations
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@
_TOOLBOX_FEATURES = os.getenv(
"FOUNDRY_AGENT_TOOLBOX_FEATURES", "Toolboxes=V1Preview")

# Platform-injected per-request call identifier (container protocol v2.0.0).
# Extracted from the inbound invocation request and forwarded verbatim on every
# egress call to the Foundry toolbox MCP proxy so the platform can correlate the
# downstream tool calls with the originating invocation. Never parse this value.
_FOUNDRY_CALL_ID_HEADER = "x-agent-foundry-call-id"

_credential = DefaultAzureCredential()
_project_client = AIProjectClient(endpoint=_endpoint, credential=_credential)
_responses_client = _project_client.get_openai_client().responses
Expand All @@ -142,13 +148,16 @@ def __init__(self, endpoint: str, token_provider):
self._session_id: str | None = None
self._req_id = 0

def _headers(self) -> dict:
def _headers(self, call_id: str | None = None) -> dict:
h = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self._get_token()}",
}
if _TOOLBOX_FEATURES:
h["Foundry-Features"] = _TOOLBOX_FEATURES
# Forward the per-request call ID extracted from the inbound invocation.
if call_id:
h[_FOUNDRY_CALL_ID_HEADER] = call_id
if self._session_id:
h["mcp-session-id"] = self._session_id
return h
Expand All @@ -157,12 +166,12 @@ def _next_id(self) -> int:
self._req_id += 1
return self._req_id

def initialize(self) -> str:
def initialize(self, call_id: str | None = None) -> str:
"""Send MCP initialize + initialized notification."""
with httpx.Client(timeout=60) as client:
resp = client.post(
self.endpoint,
headers=self._headers(),
headers=self._headers(call_id),
json={
"jsonrpc": "2.0",
"id": self._next_id(),
Expand All @@ -181,29 +190,29 @@ def initialize(self) -> str:
# Send initialized notification
client.post(
self.endpoint,
headers=self._headers(),
headers=self._headers(call_id),
json={"jsonrpc": "2.0", "method": "notifications/initialized"},
)
return data.get("result", {}).get("serverInfo", {}).get("name", "unknown")

def list_tools(self) -> list[dict]:
def list_tools(self, call_id: str | None = None) -> list[dict]:
"""Call tools/list and return tool definitions."""
with httpx.Client(timeout=60) as client:
resp = client.post(
self.endpoint,
headers=self._headers(),
headers=self._headers(call_id),
json={"jsonrpc": "2.0", "id": self._next_id(
), "method": "tools/list", "params": {}},
)
resp.raise_for_status()
return resp.json().get("result", {}).get("tools", [])

def call_tool(self, name: str, arguments: dict) -> str:
def call_tool(self, name: str, arguments: dict, call_id: str | None = None) -> str:
"""Call a tool and return the text result."""
with httpx.Client(timeout=120) as client:
resp = client.post(
self.endpoint,
headers=self._headers(),
headers=self._headers(call_id),
json={
"jsonrpc": "2.0",
"id": self._next_id(),
Expand Down Expand Up @@ -235,13 +244,13 @@ def call_tool(self, name: str, arguments: dict) -> str:
_tools_initialized = False


def _ensure_tools():
def _ensure_tools(call_id: str | None = None):
global _mcp_client, _tool_definitions, _tools_initialized
if _tools_initialized:
return
_mcp_client = _McpToolboxClient(TOOLBOX_ENDPOINT, _token_provider)
server_name = _mcp_client.initialize()
mcp_tools = _mcp_client.list_tools()
server_name = _mcp_client.initialize(call_id)
mcp_tools = _mcp_client.list_tools(call_id)
logger.info("Toolbox '%s' connected: %d tool(s) discovered",
server_name, len(mcp_tools))
for t in mcp_tools:
Expand All @@ -265,9 +274,9 @@ def _ensure_tools():
_MAX_TOOL_ROUNDS = 10


def _call_model(input_items: list[dict]) -> object:
def _call_model(input_items: list[dict], call_id: str | None = None) -> object:
"""Call the model with tool definitions and return the response."""
_ensure_tools()
_ensure_tools(call_id)
return _responses_client.create(
model=_model,
instructions=_SYSTEM_PROMPT,
Expand All @@ -277,15 +286,15 @@ def _call_model(input_items: list[dict]) -> object:
)


def _run_agent_loop(input_items: list[dict]) -> str:
def _run_agent_loop(input_items: list[dict], call_id: str | None = None) -> str:
"""Execute the agentic tool-calling loop synchronously.

Calls the model, checks for tool calls, executes them, feeds results
back, and repeats until the model produces a text response or we hit
the max rounds limit.
"""
for _ in range(_MAX_TOOL_ROUNDS):
response = _call_model(input_items)
response = _call_model(input_items, call_id)

# Check if the model wants to call tools
tool_calls = [
Expand All @@ -302,7 +311,7 @@ def _run_agent_loop(input_items: list[dict]) -> str:
try:
arguments = json.loads(tc.arguments) if isinstance(
tc.arguments, str) else tc.arguments
result_text = _mcp_client.call_tool(tc.name, arguments)
result_text = _mcp_client.call_tool(tc.name, arguments, call_id)
logger.info("Tool '%s' returned %d chars",
tc.name, len(result_text))
except Exception as e:
Expand All @@ -326,10 +335,10 @@ def _run_agent_loop(input_items: list[dict]) -> str:
return "(Reached maximum tool call rounds)"


async def _stream_agent_reply(input_items: list[dict]):
async def _stream_agent_reply(input_items: list[dict], call_id: str | None = None):
"""Run the agent loop in a thread and yield the result as SSE events."""
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, _run_agent_loop, input_items)
result = await loop.run_in_executor(None, _run_agent_loop, input_items, call_id)
yield f"data: {json.dumps({'type': 'token', 'content': result})}\n\n"


Expand Down Expand Up @@ -374,8 +383,12 @@ async def handle_invoke(request: Request):
session_id = request.state.session_id
invocation_id = request.state.invocation_id

logger.info("Processing invocation %s (session %s)",
invocation_id, session_id)
# Extract the per-request call ID (container protocol v2.0.0) directly from
# the inbound request headers so it can be forwarded on toolbox egress calls.
call_id = request.headers.get(_FOUNDRY_CALL_ID_HEADER)

logger.info("Processing invocation %s (session %s, call_id %s)",
invocation_id, session_id, call_id)

# Retrieve or create conversation history for this session.
history = _sessions.setdefault(session_id, [])
Expand All @@ -385,7 +398,7 @@ async def handle_invoke(request: Request):
async def event_generator():
full_reply = ""
try:
async for delta in _stream_agent_reply(input_items):
async for delta in _stream_agent_reply(input_items, call_id):
# Parse the SSE data to extract the content for history
try:
event_data = json.loads(delta.split(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: duplex-live-agent
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "1"
memory: 1Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: hello-world
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "1"
memory: 2Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: livekit-server
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "2"
memory: 4Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: pipecat-ws-webrtc
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "2"
memory: 4Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: pipecat-ws-server
protocols:
- protocol: invocations_ws
version: 1.0.0
version: 2.0.0
resources:
cpu: "2"
memory: 4Gi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: hosted
name: background-agent-responses
protocols:
- protocol: responses
version: 1.0.0
version: 2.0.0
resources:
cpu: "0.25"
memory: 0.5Gi
Expand Down
Loading
Loading