* Add Python hosting core and Responses channel Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address hosting core review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Adopt source pyright typing setup for hosting packages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Cover ResponsesChannel custom path routing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Align hosting tests with package layout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix hosting workflow fixture imports in aggregate tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply useful Responses channel hardening Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix hosting package typing checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix hosting pyright under Python 3.11 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Avoid static diskcache dependency in hosting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix aggregate typing and Docker test resilience Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify local Responses workflow sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify generic hosting is not Foundry hosting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert "Clarify generic hosting is not Foundry hosting" This reverts commit 73b584d919053bed43a258d75dc2b76406e9c181. * Clarify isolation key source flexibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify isolation header reuse boundary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Support multimodal Responses channel outputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Preserve multimodal streaming Responses output Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stream Responses output items from updates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Improve Responses streaming output handling * Tighten Responses channel default option handling - Restore full option parsing in parse_responses_request: known fields are remapped (max_output_tokens→max_tokens, parallel_tool_calls→ allow_multiple_tool_calls), transport/session keys excluded, None values dropped, everything else forwarded as-is so run_hook can inspect the full set. - Add a default _strip_options_hook on ResponsesChannel that removes all parsed options before reaching the agent. Callers cannot inject generation params (temperature, instructions, tools, …) unless the host explicitly allows it. - A custom run_hook replaces the default entirely and receives the full ChannelRequest.options plus the raw protocol_request. - Update tests to cover remap, default-strip, and custom-hook paths. - Clarify host debug-log docstring to match new option flow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
agent-framework-hosting
Multi-channel hosting for Microsoft Agent Framework agents.
agent-framework-hosting lets you serve a single agent or workflow target
through one or more channels. The host owns one Starlette ASGI app,
route/lifecycle composition, and per-isolation_key session resolution.
Each channel owns its protocol parsing and response rendering.
The base package contains only channel-neutral plumbing:
AgentFrameworkHost— the Starlette host.Channel— the channel protocol.ChannelRequest/ChannelSession/ChannelIdentity— the request envelope and optional channel metadata.ChannelContext/ChannelContribution/ChannelCommand— channel-side hooks for invoking the target and contributing routes, commands, and lifecycle callbacks.ChannelRunHook/ChannelResponseHook/ChannelStreamUpdateHook— host-invoked customization seams.
ChannelStreamUpdateHook applies to streamed updates only. It is not a
substitute for final-response redaction.
Concrete channels live in their own packages so you only install what you use:
| Package | Transport |
|---|---|
agent-framework-hosting-responses |
OpenAI Responses API |
Additional channel packages can build on the same host contract without adding their protocol dependencies to the base package.
Install
pip install agent-framework-hosting agent-framework-hosting-responses
# or with Hypercorn pre-installed for the demo `host.serve(...)` helper
pip install "agent-framework-hosting[serve]" agent-framework-hosting-responses
# add the [disk] extra to persist reset-session aliases
pip install "agent-framework-hosting[disk]"
Quickstart
from agent_framework.openai import OpenAIChatClient
from agent_framework_hosting import AgentFrameworkHost, Channel
agent = OpenAIChatClient().as_agent(name="Assistant")
# Add channels from sibling packages, e.g. `agent-framework-hosting-responses`
# exposes a `ResponsesChannel` that serves the OpenAI Responses API.
channels: list[Channel] = []
host = AgentFrameworkHost(target=agent, channels=channels)
host.serve(port=8000)
Session state and workflow checkpoints
By default the host keeps live AgentSession objects and reset-session aliases
in memory. Channels opt into continuity by setting
ChannelRequest.session = ChannelSession(isolation_key=...); requests with the
same isolation key reuse the same host-created session.
The host treats isolation_key as an opaque partition key. Each channel or
hosting environment decides where that key comes from:
- protocol headers supplied by a trusted platform,
- request body fields such as a previous response or conversation ID,
- route/path parameters,
- channel-native metadata such as chat/user IDs, or
- environment-provided context in an ephemeral host.
The host should be able to carry any of those sources as long as the channel or
platform has already authenticated and authorized the caller before passing the
key to ChannelSession.
The built-in request-context helper recognizes the x-agent-user-isolation-key
and x-agent-chat-isolation-key header names because some hosting
environments, including Foundry Hosted Agents, already use them. Reusing those
header names does not mean agent-framework-hosting is the supported way to
run on Foundry Hosted Agents; use agent-framework-foundry-hosting for that
hosting surface.
For long-running deployments that need reset_session(...) aliases to survive
restart, pass state_dir:
host = AgentFrameworkHost(
target=agent,
channels=channels,
state_dir="./.host-state",
)
This creates ./.host-state/sessions/ and stores only lightweight alias
bookkeeping. Live AgentSession objects are still rehydrated lazily by the
configured history provider on the next turn.
For workflow targets, checkpoint_location=... is the clearest way to enable
checkpoint persistence. As a convenience, state_dir="./.host-state" also
derives ./.host-state/checkpoints/ for workflow targets. Use the mapping form
when you want only one component:
from agent_framework_hosting import HostStatePaths
host = AgentFrameworkHost(
target=workflow,
channels=channels,
state_dir=HostStatePaths(
sessions="/var/lib/myapp/sessions",
checkpoints="/var/lib/myapp/checkpoints",
),
)
Cross-channel identity linking, multicast delivery, background runs, continuation tokens, and durable delivery runners are follow-up enhancements, not part of this v1 host contract.