* Python: Move session persistence into core Move SessionStore and durable msgspec-backed storage into core, restore sessions in Foundry Responses hosting with per-user isolation, and document the serialization design. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Address session persistence review feedback Harden scoped file paths and corruption recovery, preserve session serialization compatibility, clarify dependency placement, and add reproducible benchmark evidence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Preserve session snapshot compatibility Deep-copy in-memory session writes and retain existing Telegram session keys so stored conversations continue resolving. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Simplify Foundry session isolation Add experimental FoundrySessionStore backed by Agent Server request context, remove resolver plumbing, and centralize v2 user isolation for sessions, checkpoints, and approvals. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Reduce Foundry session helper layering Inline the single-use request user accessor while keeping separate context validation, fingerprint, and directory helpers for their distinct callers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Clarify Foundry request context validation Separate fail-fast request validation from context retrieval so Responses no longer appears to discard a returned context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Python: Share Foundry request context helpers Move protocol validation and user-scope derivation into a dedicated request-context module, leaving the session-store module focused on storage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Restore Foundry checkpoint storage paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Simplify Foundry session storage paths Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Persist Foundry sessions under hosted home Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Make hosted path test platform independent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Address session persistence review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Isolate Foundry session path handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Clarify Foundry session path terminology Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Align Foundry sessions with Responses continuity Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Finalize Foundry Responses session persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Add session store feature usage telemetry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c * Fix hosted per-call history persistence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c --------- Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c
6.2 KiB
agent-framework-hosting
Shared execution-state helpers for app-owned Agent Framework hosting.
This package keeps Agent Framework state separate from web-framework concerns:
AgentState— pairs an agent target with a coreSessionStore(session_id -> AgentSession).WorkflowState— resolves a workflow target, including directWorkflowinstances, workflow factories,WorkflowBuilder, and orchestration builders.
The experimental SessionStore and FileSessionStore implementations live in
agent-framework-core and are imported from agent_framework. SessionStore
provides get/set/delete by an app-selected id. Each
successful get returns an independent copy, so a run works from a snapshot
instead of mutating an older continuation point in place. The store does not
know how to create a new value for an id it hasn't seen before — use
AgentState.get_or_create_session(...) for that, since only the state object
has both the store and the resolved target. Workflow
checkpointing should use the existing CheckpointStorage abstraction directly;
if an app needs per-session resume, keep a small app-owned cursor such as
session_id -> checkpoint_id.
SessionStore accepts opaque non-empty IDs so custom database and Redis
implementations can use their native key contracts. FileSessionStore limits
its direct keys to 128 ASCII letters, digits, -, and _. AgentState passes
the app-selected ID to the configured store unchanged; each store implementation
owns any validation or normalization required by its backend. This does not
replace parameterized queries or backend-specific validation in custom stores.
FileSessionStore uses msgspec JSON. Register custom objects placed in
AgentSession.state explicitly before sessions are saved or restored:
from agent_framework import register_state_type
class MyState:
...
# Register at module import time, before any provider instance is created.
register_state_type(MyState, type_id="my_state")
Classes with to_dict() / from_dict() methods and explicitly registered
Pydantic models receive default codecs. Other classes can provide encoder=
and decoder= callbacks. Keep registration at module level so importing the
module prepares cold-start session restoration before its context provider is
instantiated. Unregistered Pydantic models still use legacy same-process
auto-registration for now, but emit DeprecationWarning; cold-start
deserialization is not guaranteed on that path.
JSON is the default file format. Use MessagePack for a compact binary file:
store = FileSessionStore("storage/sessions", serialization_format="msgpack")
Use FastAPI, Starlette, Azure Functions, Django, or another framework for route registration, auth, middleware, response construction, and background work.
The core
SessionStoreis an in-memorydictwith no eviction — every id ever stored stays resolvable for the life of the process. That is intentional: protocols such as OpenAI Responses'previous_response_idare designed to let a caller continue from any earlier point in a conversation, not just the latest turn, so every id handed out needs to stay independently resolvable. If you back the store with real storage (Redis, a database, ...), you are responsible for that store's own TTL/eviction policy; this in-memory reference implementation does not model that concern.
Quickstart
from agent_framework import FileSessionStore
from agent_framework.openai import OpenAIChatClient
from agent_framework_hosting import AgentState
agent = OpenAIChatClient().as_agent(name="Assistant")
state = AgentState(agent, session_store=FileSessionStore("storage/sessions"))
session = await state.get_or_create_session("conversation-1")
result = await (await state.get_target()).run("Hello", session=session)
If a protocol mints a new continuation id on every response, store the session
explicitly after run(...) returns. run(...) may update the session, so store
the post-run object:
session = await state.get_or_create_session(previous_response_id)
result = await (await state.get_target()).run("Hello", session=session)
await state.set_session(response_id, session)
This response-keyed pattern supports simultaneous branches: callers may read
the same previous_response_id, receive independent working copies, and store
each result under a different new response id. A stable conversation_id is
different: explicitly write the completed session back under that same id to
advance its mutable head, and ensure only one caller advances it at a time.
AgentState does not provide that application-level locking or optimistic
concurrency control.
Targets can be direct instances, synchronous factories, asynchronous factories, or awaitables:
state = AgentState(create_agent) # cached by default
state = AgentState(create_agent, cache_target=False)
WorkflowState mirrors this shape for workflow targets:
from agent_framework import InMemoryCheckpointStorage
from agent_framework_hosting import WorkflowState
state = WorkflowState(create_workflow)
storage = InMemoryCheckpointStorage()
result = await (await state.get_target()).run("Hello", checkpoint_storage=storage)
latest = await storage.get_latest(workflow_name=(await state.get_target()).name)
A Workflow instance allows only one active run. Hosts that need simultaneous
workflow runs should pass a factory or builder and set cache_target=False so
each request receives a fresh workflow instance.
WorkflowState also accepts an unbuilt workflow builder directly:
from agent_framework import WorkflowBuilder
from agent_framework_hosting import WorkflowState
builder = WorkflowBuilder(start_executor=executor)
state = WorkflowState(builder) # calls builder.build() when the target is resolved
This is structural: orchestration builders from agent_framework_orchestrations
(SequentialBuilder, ConcurrentBuilder, HandoffBuilder, GroupChatBuilder,
and MagenticBuilder) also work because they expose the same zero-argument
build() -> Workflow method.
Cross-channel identity linking, multicast delivery, background runs, continuation tokens, and durable delivery runners are follow-up enhancements, not part of this v1 state surface.