Python: Add more types - #5377
Conversation
* Experiment * Update dependency and add non streaming * Add more samples * Rename samples * Add invocations * Comments 1 * Comments 2 * Comments 3 * Improve README * Add local shell sample * WIP: Add eval and memory samples * Update user agent prefix * Update user agent prefix doc
* Add tests * fix tests and sample * Fix formatting * Remove function approval contents
* Refine samples and upgrade pacakges * Upgrade to a new package that fixes a bug * Update model env var
* Upgrade agentserver packages * Fix new types
* Add special handling for workflows * Address comments
Python Test Coverage Report •
Python Unit Test Overview
|
|||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Pull request overview
This PR expands the Python side of the Agent Framework repo by adding a new Foundry Hosting integration package, updating core telemetry User-Agent handling, and replacing older “hosted agent” end-to-end samples with a new set of Foundry Hosted Agents samples (Responses + Invocations).
Changes:
- Add new
agent-framework-foundry-hostingpackage (ResponsesHostServer / InvocationsHostServer) plus HTTP round-trip tests for Responses. - Update core telemetry to support adding hosting-layer prefixes to the framework User-Agent.
- Restructure hosting samples: remove older
05-end-to-end/hosted_agents/*samples and add new04-hosting/foundry-hosted-agents/*sample set (code + manifests + Docker assets).
Reviewed changes
Copilot reviewed 81 out of 85 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
| python/uv.lock | Adds workspace member and locks new Foundry Hosting/Azure AgentServer dependencies. |
| python/pyproject.toml | Registers agent-framework-foundry-hosting as a workspace package. |
| python/.cspell.json | Adds agentserver to spellchecker dictionary. |
| python/packages/core/agent_framework/_telemetry.py | Adds support for User-Agent prefix stacking for hosting layers. |
| python/packages/foundry_hosting/pyproject.toml | New package metadata, dependencies, and test/tooling configuration. |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/init.py | Exposes ResponsesHostServer and InvocationsHostServer. |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_responses.py | Implements Responses API hosting bridge and content/event conversions. |
| python/packages/foundry_hosting/agent_framework_foundry_hosting/_invocations.py | Implements Invocations API hosting bridge. |
| python/packages/foundry_hosting/tests/test_responses.py | Adds comprehensive HTTP round-trip tests for ResponsesHostServer. |
| python/packages/foundry_hosting/README.md | Introduces package README (currently placeholder content). |
| python/packages/foundry_hosting/LICENSE | Adds MIT license for the new package. |
| python/samples/04-hosting/foundry-hosted-agents/README.md | New top-level README for Foundry Hosted Agents samples. |
| python/samples/04-hosting/foundry-hosted-agents/responses/README.md | Adds Responses API sample index + “using_deployed_agent.py” helper script. |
| python/samples/04-hosting/foundry-hosted-agents/responses/using_deployed_agent.py | Demonstrates calling a deployed agent via OpenAIChatClient. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/requirements.txt | Sample requirements for basic Responses-hosted agent. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/main.py | Basic Responses-hosted agent server sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/agent.yaml | Hosted agent definition for Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/agent.manifest.yaml | Deployable manifest for Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md | How-to for the Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/Dockerfile | Container build for the Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.env.example | Example env vars for the Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/.dockerignore | Build context excludes for the Responses basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/requirements.txt | Sample requirements for Responses + local tools. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/main.py | Responses-hosted agent demonstrating local tools (incl. shell tool). |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/agent.yaml | Hosted agent definition for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/agent.manifest.yaml | Deployable manifest for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/README.md | How-to for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/Dockerfile | Container build for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/.env.example | Example env vars for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/02_local_tools/.dockerignore | Build context excludes for local tools sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/requirements.txt | Sample requirements for Responses + remote MCP tools. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/main.py | Responses-hosted agent demonstrating Foundry toolbox MCP + GitHub MCP. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/agent.yaml | Hosted agent definition for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/agent.manifest.yaml | Deployable manifest for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/README.md | How-to for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/Dockerfile | Container build for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/.env.example | Example env vars for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/03_remote_mcp/.dockerignore | Build context excludes for remote MCP sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/requirements.txt | Sample requirements for Responses-hosted workflow. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/main.py | Responses-hosted workflow sample using Agent Framework workflows. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/agent.yaml | Hosted agent definition for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/agent.manifest.yaml | Deployable manifest for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/README.md | How-to for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/Dockerfile | Container build for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/.env.example | Example env vars for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/responses/04_workflows/.dockerignore | Build context excludes for workflow sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/README.md | Adds Invocations API sample index. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/requirements.txt | Sample requirements for basic Invocations-hosted agent. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/main.py | Basic Invocations-hosted agent server sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/agent.yaml | Hosted agent definition for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/agent.manifest.yaml | Deployable manifest for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/README.md | How-to for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/Dockerfile | Container build for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/.env.example | Example env vars for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/01_basic/.dockerignore | Build context excludes for Invocations basic sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/requirements.txt | Sample requirements for Invocations “break glass” scenario. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/main.py | Invocations sample with custom invoke handler and session store. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/agent.yaml | Hosted agent definition for “break glass” sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/agent.manifest.yaml | Deployable manifest for “break glass” sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/README.md | How-to for “break glass” sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/Dockerfile | Container build for “break glass” sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/.env.example | Example env vars for “break glass” sample. |
| python/samples/04-hosting/foundry-hosted-agents/invocations/02_break_glass/.dockerignore | Build context excludes for “break glass” sample. |
| python/samples/05-end-to-end/hosted_agents/README.md | Removes older hosted agent sample index under 05-end-to-end. |
| python/samples/05-end-to-end/hosted_agents/agents_in_workflow/requirements.txt | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agents_in_workflow/main.py | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agents_in_workflow/agent.yaml | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_text_search_rag/requirements.txt | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_text_search_rag/main.py | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_text_search_rag/agent.yaml | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/requirements.txt | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/main.py | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/agent.yaml | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/README.md | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/.env.sample | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_local_tools/.dockerignore | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_hosted_mcp/requirements.txt | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_hosted_mcp/main.py | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/agent_with_hosted_mcp/agent.yaml | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/requirements.txt | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/main.py | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/agent.yaml | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/README.md | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/.env.sample | Removes older end-to-end hosted agent sample assets. |
| python/samples/05-end-to-end/hosted_agents/writer_reviewer_agents_in_workflow/.dockerignore | Removes older end-to-end hosted agent sample assets. |
| Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: | ||
|
|
There was a problem hiding this comment.
This README says requests should include a JSON body with a "message" field, but the Responses API (and the curl examples below) uses the "input" field. Please update the text to avoid confusing users.
| # Foundry Toolbox as a MCP tool | ||
| project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"] | ||
| toolbox_name = os.environ["TOOLBOX_NAME"] | ||
| toolbox_endpoint = f"{project_endpoint.rstrip('/')}/toolboxes/{toolbox_name}/mcp?api-version=v1" | ||
| http_client = httpx.AsyncClient(auth=ToolboxAuth(), headers={"Foundry-Features": "Toolboxes=V1Preview"}) | ||
| foundry_mcp_tool = MCPStreamableHTTPTool( | ||
| name="toolbox", | ||
| url=toolbox_endpoint, | ||
| http_client=http_client, | ||
| load_prompts=False, | ||
| ) |
There was a problem hiding this comment.
http_client = httpx.AsyncClient(...) is created but never closed. Since this sample runs as a long-lived server, this can leak connections/resources; consider using a lifespan/shutdown hook to aclose() the client or manage it with an async context manager.
| @@ -0,0 +1,2 @@ | |||
| FOUNDRY_PROJECT_ENDPOINT= "..." | |||
There was a problem hiding this comment.
There is an extra space after = in FOUNDRY_PROJECT_ENDPOINT= "...", which can result in the value containing a leading space depending on the dotenv parser. Please remove the extra space (and keep quoting consistent with the other examples).
| FOUNDRY_PROJECT_ENDPOINT= "..." | |
| FOUNDRY_PROJECT_ENDPOINT="..." |
| query = "You name is Javis. What can you do?" | ||
| print(f"\nUser: {query}") |
There was a problem hiding this comment.
The sample prompt has a grammatical typo: "You name is ..." should be "Your name is ..." (and confirm whether the intended name is "Jarvis"). This is user-facing sample text and will be copied verbatim by readers.
| if stream: | ||
|
|
||
| async def stream_response() -> AsyncGenerator[str]: | ||
| async for update in self._agent.run(user_message, session=session, stream=True): | ||
| yield update.text | ||
|
|
||
| return StreamingResponse( | ||
| stream_response(), | ||
| media_type="text/event-stream", | ||
| headers={"Cache-Control": "no-cache", "Connection": "keep-alive"}, | ||
| ) |
There was a problem hiding this comment.
The streaming path sets media_type="text/event-stream" but yields raw text chunks (update.text) without SSE framing (data: ...\n\n). If clients interpret this as SSE, they may fail to parse the stream. Consider either formatting each chunk as a proper SSE event, or change the content-type to text/plain/chunked text if SSE isn’t intended.
| ```bash | ||
| docker run -p 8088:8088 \ | ||
| -e FOUNDRY_PROJECT_ENDPOINT=<your-endpoint> \ | ||
| -e FOUNDRY_MODEL=<your-model> \ |
There was a problem hiding this comment.
The top-level README uses FOUNDRY_MODEL in the docker run example, but the samples’ code (and .env.example files) expect MODEL_DEPLOYMENT_NAME. Please align the documented env var names to what the code actually reads to avoid startup failures.
| -e FOUNDRY_MODEL=<your-model> \ | |
| -e MODEL_DEPLOYMENT_NAME=<your-model> \ |
| Send a POST request to the server with a JSON body containing a "message" field to interact with the agent. For example: | ||
|
|
||
| ```bash |
There was a problem hiding this comment.
This README says requests should include a JSON body with a "message" field, but the Responses API (and the curl example below) uses the "input" field. Please update the text to avoid confusing users.
| | Sample | Description | | ||
| | --- | --- | | ||
| | [01_basic](./01_basic) | A basic example of hosting an agent with the `invocations` API and carrying on a multi-turn conversation. | | ||
| | [02_break_glass](./02-break-glass) | An example of hosting an agent with the `invocations` API and a "break glass" scenario where you can create your own `invoke_handler` to handle specific types of invocations. | |
There was a problem hiding this comment.
The link target uses ./02-break-glass, but the folder added in this PR is 02_break_glass. Please update the README link so it resolves correctly.
| | [02_break_glass](./02-break-glass) | An example of hosting an agent with the `invocations` API and a "break glass" scenario where you can create your own `invoke_handler` to handle specific types of invocations. | | |
| | [02_break_glass](./02_break_glass) | An example of hosting an agent with the `invocations` API and a "break glass" scenario where you can create your own `invoke_handler` to handle specific types of invocations. | |
| # Foundry Hosting | ||
|
|
||
| This package provides the integration of Agent Framework agents and workflows with the Foundry Agent Server, which can be hosted on Foundry infrastructure. | ||
|
|
||
| ## Responses | ||
|
|
||
| TODO | ||
|
|
||
| ## Invocations | ||
|
|
||
| TODO |
There was a problem hiding this comment.
This package README still contains placeholder "TODO" sections. Since this is a new published workspace package, please replace the TODOs with at least minimal usage examples (how to host an agent via ResponsesHostServer/InvocationsHostServer) and any required environment variables.
| async def _handler( | ||
| self, | ||
| request: CreateResponse, | ||
| context: ResponseContext, | ||
| cancellation_signal: asyncio.Event, | ||
| ) -> AsyncIterable[ResponseStreamEvent | dict[str, Any]]: | ||
| """Handle the creation of a response.""" | ||
| if self._is_workflow_agent: | ||
| # Workflow agents are handled differently because they require checkpoint restoration |
There was a problem hiding this comment.
cancellation_signal is accepted by _handler / _handle_workflow_agent but never checked. If the hosting layer sets this event on client disconnect/cancel, the current implementation will keep running the agent/workflow unnecessarily. Consider periodically checking cancellation_signal.is_set() (especially inside the streaming loops) and stopping early / closing builders.
Motivation and Context
Description
Contribution Checklist