Skip to content

Python: Fix ResponsesChannel session continuity and harden _result_to_text for workflows#6642

Open
Ashutosh0x wants to merge 8 commits into
microsoft:mainfrom
Ashutosh0x:fix/hosting-responses-session-continuity
Open

Python: Fix ResponsesChannel session continuity and harden _result_to_text for workflows#6642
Ashutosh0x wants to merge 8 commits into
microsoft:mainfrom
Ashutosh0x:fix/hosting-responses-session-continuity

Conversation

@Ashutosh0x

Copy link
Copy Markdown

Motivation and Context

Fixes three issues flagged by automated DevFlow review on #6580:

  1. Session isolation key continuity - In local (non-Foundry) hosting, the first turn's checkpoint/history data is stored under the initial response_id, but subsequent turns mint a new response_id as their isolation key. Checkpoint storage, FileHistoryProvider, and other session-scoped stores can't find data from the previous turn, silently dropping conversation state across turns.

  2. Non-streaming workflow result rendering - ResponsesChannel._handle calls _result_to_text(result.result) on line 225. When the target is a workflow, result.result is a WorkflowRunResult whose get_outputs() may return an empty list. The current _result_to_text joins the empty list into ''', yielding a blank Responses envelope. Workflows that carry their final state on get_final_state() instead of through output executors are unrenderable.

  3. Local sample session bootstrap - The local_responses/app.py and local_responses_workflow/app.py samples use FileHistoryProvider / checkpoint storage respectively, but the first turn's session is bootstrapped with a per-request response_id as isolation key.

Description

_channel.py - Session continuity fix: When no externally-provided session exists, the fallback now uses previous_response_id or response_id as the isolation key.

_channel.py - _result_to_text hardening: When get_outputs() returns an empty list, falls back to get_final_state() before the generic str() cast.

test_channel.py - New tests: Added 3 tests for empty-output workflow results, final-state fallback, and plain-string rendering.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? No.

eavanvalkenburg and others added 7 commits June 18, 2026 07:55
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…_text for workflows

Fixes three issues flagged by automated review on PR microsoft#6580:

1. Session isolation key continuity (Bugs 2 & 3):
   When no external session is provided, the fallback isolation key now
   prefers `previous_response_id` over the current `response_id`.
   This ensures checkpoint storage, FileHistoryProvider, and other
   session-scoped stores find data written during the preceding turn
   instead of silently starting fresh.

2. Workflow result rendering (Bug 1):
   `_result_to_text` now handles WorkflowRunResult objects whose
   `get_outputs()` returns an empty list by falling back to
   `get_final_state()` before the generic `str()` cast.

3. Added tests for empty-output workflow results, final-state fallback,
   and plain-string rendering.
Copilot AI review requested due to automatic review settings June 20, 2026 09:15
@moonbox3 moonbox3 added documentation Improvements or additions to documentation python Issues related to the Python codebase labels Jun 20, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the Python local hosting stack by ensuring Responses-session continuity across turns (so per-session stores like history/checkpoints can be reused) and by hardening non-streaming Responses rendering for workflow targets (so workflows without emitted outputs still produce a usable Responses envelope). It also introduces the initial agent-framework-hosting and agent-framework-hosting-responses packages with tests and local samples.

Changes:

  • Fix ResponsesChannel session fallback to use previous_response_id or response_id as the isolation key when no session is otherwise provided.
  • Harden _result_to_text to fall back to get_final_state() when a workflow result has no outputs.
  • Add new hosting/hosting-responses packages, unit tests, and local samples demonstrating agent + workflow hosting.

Reviewed changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
python/uv.lock Adds workspace entries and dependencies for new hosting packages (incl. diskcache).
python/pyproject.toml Registers hosting packages in the workspace and pyright env list.
python/PACKAGE_STATUS.md Marks new hosting packages as alpha.
python/samples/04-hosting/af-hosting/README.md Documents the new local multi-channel hosting samples and their relationship to Foundry-hosted samples.
python/samples/04-hosting/af-hosting/local_responses/README.md Documents the minimal Responses hosting sample and run hook behavior.
python/samples/04-hosting/af-hosting/local_responses/pyproject.toml Sample project wiring for local Responses hosting sample.
python/samples/04-hosting/af-hosting/local_responses/call_server.py Local client for exercising the Responses endpoint.
python/samples/04-hosting/af-hosting/local_responses/app.py Starlette app for the minimal agent + ResponsesChannel sample.
python/samples/04-hosting/af-hosting/local_responses_workflow/storage/checkpoints/.gitkeep Placeholder for sample checkpoint storage directory.
python/samples/04-hosting/af-hosting/local_responses_workflow/README.md Documents workflow hosting sample with structured intake and checkpoints.
python/samples/04-hosting/af-hosting/local_responses_workflow/pyproject.toml Sample project wiring for workflow hosting sample.
python/samples/04-hosting/af-hosting/local_responses_workflow/call_server.rest REST examples for the workflow sample endpoint.
python/samples/04-hosting/af-hosting/local_responses_workflow/call_server.py Python client for exercising workflow sample via OpenAI SDK.
python/samples/04-hosting/af-hosting/local_responses_workflow/app.py Workflow sample Starlette app demonstrating typed intake + checkpointing.
python/packages/hosting/README.md New package docs for channel-neutral hosting primitives and state/checkpoint behavior.
python/packages/hosting/pyproject.toml New hosting package definition and dependency set.
python/packages/hosting/LICENSE License file for the new hosting package.
python/packages/hosting/agent_framework_hosting/_types.py Defines channel-neutral request/envelope types and hook protocols.
python/packages/hosting/agent_framework_hosting/_state_store.py Implements disk-backed session-alias persistence using optional diskcache.
python/packages/hosting/agent_framework_hosting/_persistence.py Adds shared persistence helpers (state_dir normalization + locking).
python/packages/hosting/agent_framework_hosting/_isolation.py Adds isolation-key contextvar surface for Foundry-style headers.
python/packages/hosting/agent_framework_hosting/_host.py Implements AgentFrameworkHost, middleware wiring, session caching, checkpoint wiring, and stream adapters.
python/packages/hosting/agent_framework_hosting/init.py Exposes the hosting package public API surface.
python/packages/hosting/tests/hosting/test_types.py Tests for channel-neutral envelope types.
python/packages/hosting/tests/hosting/test_isolation.py Tests for isolation header/contextvar behavior and middleware reset semantics.
python/packages/hosting/tests/hosting/test_host.py Large suite covering host routing, sessions, hooks, workflow bridging, and checkpoint path validation.
python/packages/hosting/tests/hosting/test_host_disk.py Tests for state_dir behavior with optional disk-backed session aliasing.
python/packages/hosting/tests/hosting/conftest.py Loads workflow fixtures for hosting tests via an import hook.
python/packages/hosting/tests/hosting/_workflow_fixtures.py Workflow fixture builders used by host tests.
python/packages/hosting-responses/README.md New channel package docs for the Responses-shaped surface.
python/packages/hosting-responses/pyproject.toml New hosting-responses package definition and dependency set.
python/packages/hosting-responses/LICENSE License file for the new hosting-responses package.
python/packages/hosting-responses/agent_framework_hosting_responses/_parsing.py Parses Responses request bodies into AF Messages/options/session.
python/packages/hosting-responses/agent_framework_hosting_responses/_channel.py Implements the ResponsesChannel HTTP endpoint, SSE streaming, and result rendering.
python/packages/hosting-responses/agent_framework_hosting_responses/init.py Exposes the responses channel package public API surface.
python/packages/hosting-responses/tests/hosting_responses/test_parsing.py Unit tests for request parsing helpers.
python/packages/hosting-responses/tests/hosting_responses/test_channel.py End-to-end tests for ResponsesChannel behavior, streaming, and result text rendering.

Comment on lines +25 to +28
dependencies = [
"agent-framework-core>=1.2.0,<2",
"starlette>=0.37",
]
Comment on lines +25 to +29
dependencies = [
"agent-framework-core>=1.2.0,<2",
"agent-framework-hosting==1.0.0a260424",
"openai>=1.99.0,<3",
]
Comment on lines +121 to +122
else:
parts = []
Comment on lines +141 to +145
try:
body = await request.json()
except Exception:
return JSONResponse({"error": "invalid json"}, status_code=400)

Comment on lines +40 to +44
previous_response_id=previous_response_id,
)
print(f"User: {prompt}")
print(f"Agent: {response.output_text}")

Comment on lines +908 to +914
"""The host walks ``target.context_providers``, descends one level
when a provider exposes a ``providers`` attribute, and calls
``bind_request_context(response_id=..., previous_response_id=...)``
on every provider that supports it. Foundry response-id chaining
plugs into this exact seam — a regression that mistypes the kwarg
name, drops the descent, or fails to keep the binding open across
the agent run silently breaks chained writes."""
Comment on lines +64 to +66
"""Return a deterministic weather report for a city."""
high_temp = randint(5, 25)
reports = {
…terministic sample, response.id print

- Add body type validation in _channel.py (reject non-object JSON with 422)
- Reject invalid message content types in _parsing.py instead of silent empty
- Make lookup_weather deterministic via location hash instead of randint
- Print response.id in call_server.py for multi-turn chain usability
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation python Issues related to the Python codebase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants