Python: Add hosted agent sample for the agent harness#7010
Python: Add hosted agent sample for the agent harness#7010vaibhav-patel wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Python hosting sample under python/samples/04-hosting/af-hosting/local_responses_harness/ that mirrors the existing Responses-route + AgentState seam, but hosts a harness agent created via create_harness_agent(...) instead of a plain Agent.
Changes:
- Registered the new sample in
af-hosting/README.md. - Added a self-contained sample directory with FastAPI
/responsesroute (app.py), a local OpenAI SDK client (call_server.py), and run/docs scaffolding (README.md,pyproject.toml,storage/.gitignore).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| python/samples/04-hosting/af-hosting/README.md | Adds the new local_responses_harness/ entry to the hosting samples index. |
| python/samples/04-hosting/af-hosting/local_responses_harness/storage/.gitignore | Keeps session/storage artifacts out of git for the new sample. |
| python/samples/04-hosting/af-hosting/local_responses_harness/README.md | Documents what the sample demonstrates, how to run it, and production-readiness cautions. |
| python/samples/04-hosting/af-hosting/local_responses_harness/pyproject.toml | Defines dependencies and local uv git sources for hosting packages. |
| python/samples/04-hosting/af-hosting/local_responses_harness/call_server.py | Adds a 3-turn OpenAI SDK client to exercise previous_response_id continuity. |
| python/samples/04-hosting/af-hosting/local_responses_harness/app.py | Implements a FastAPI /responses route hosting a harness agent behind Responses helper functions. |
…mple Addresses PR review: disable the harness file-memory and file-access providers so the headless sample doesn't expose file tools or write outside storage/, and correct the app.py docstring to match call_server.py (which takes no prompt argument).
919f0a5 to
b36eb7f
Compare
| # sample their read/write file tools would expose unintended tools, create | ||
| # on-disk side effects outside storage/, and could block on tool approval. | ||
| disable_file_memory=True, | ||
| disable_file_access=True, |
There was a problem hiding this comment.
Could we update this to the current opt-in API please? main removed disable_file_access in #7094, so once this branch is brought up to date, the first /responses request resolves create_agent() and fails with TypeError: got an unexpected keyword argument 'disable_file_access'. Omitting this flag is now sufficient because file access is disabled unless file_access_store is supplied.
| # `agent.run(...)` updates the session. Store it under the newly minted | ||
| # response id after the run so `previous_response_id=response_id` continues | ||
| # from this exact point. | ||
| await state.set_session(response_id, session) |
There was a problem hiding this comment.
Could we avoid aliasing the mutable AgentSession here and in the streaming path? SessionStore retains object references, so continuing from turn 1 to create turn 2 mutates the object still stored under turn 1; branching again from turn 1 then sees turn 2, and concurrent branches mutate the same state. A per-run lock would only serialize this corruption, so preserving independently resolvable response IDs needs snapshot or copy semantics.
What this adds
A new Python sample,
python/samples/04-hosting/af-hosting/local_responses_harness/, that hosts a harness agent (built withcreate_harness_agent) behind the same helper-first Responses hosting seam used by the existinglocal_responses/sample.Files:
app.py— a module-level FastAPI ASGI app whose target is acreate_harness_agent(...)agent. UsesAgentState+ theagent-framework-hosting-responseshelpers (responses_to_run,responses_session_id,create_response_id,responses_from_run,responses_from_streaming_run) exactly likelocal_responses/app.py, with a native/responsesroute and Hypercorn startup.call_server.py— a plainopenaiSDK client that drives a three-turn conversation (rotatingprevious_response_id) to exercise session continuity.pyproject.toml— mirrorslocal_responses/pyproject.toml([tool.uv.sources]wires the pre-PyPIagent-framework-hosting*packages to the repo).README.md— mirrors the sibling READMEs (what it shows, prerequisites, how to run, production-readiness notes).storage/.gitignore— same session-storage placeholder as the siblings.af-hosting/README.md.The takeaway the sample makes concrete: a harness agent is just an
Agent, so it drops straight into the existingAgentState/ Responses-helper seam. The harness supplies the function-invocation loop, per-service-call persistence, compaction, and todo management on top of a single@tool.Choices I made (issue had no spec)
#6477 has no body and no comments, so I followed the conventions of the existing
02-agents/harness/and04-hosting/af-hosting/samples rather than inventing a new shape:af-hosting/, namedlocal_responses_harness/to match the existinglocal_responses/andlocal_responses_workflow/pair. It reuses that directory's exact layout (app.py+call_server.py+pyproject.toml+storage/).approval_mode="never_require"so a one-shot HTTP request never blocks on human tool approval. Kept todo management and compaction enabled so the target is a genuine harness agent, not a relabelled plainAgent..pyand followedSAMPLE_GUIDELINES.md(AzureCliCredential, over-documented, expected-output comment).Happy to move or rename this (e.g. a top-level
04-hosting/harness-hosted-agent/, or folding it intolocal_responses/as a variant) if maintainers prefer a different structure or scope — glad to adjust.Verification
No cloud credentials were used; this was not run against Azure.
python3 -m py_compile app.py call_server.py— clean.uv run ruff checkanduv run ruff format --checkon both files — clean.create_harness_agent,AgentState, all Responses helpers,FoundryChatClient,tool,ResponseStream).create_agent()assembles the harness agent andcreate_session(session_id=...)succeeds (the exact callAgentState.get_or_create_sessionmakes) — no network required.Fixes #6477.