Skip to content

Python: [Feature]: Support Responses API server-side compaction (context_management) with store=false #8890

Description

Description

The Responses API supports server-side compaction via context_management=[{"type": "compaction", "compact_threshold": N}], and it works with store=false. When the context crosses the threshold the service emits an encrypted compaction output item. For stateless (input-array) chaining, the caller appends that item to the next request's input and may drop the items that came before it.

Today OpenAIChatClient and FoundryChatClient (which inherits from RawOpenAIChatClient) only half support this:

  • Request side works. context_management passes through _prepare_options to responses.create(...), so the service does compact.
  • The compaction item is dropped. Neither the non-streaming parser nor the response.output_item.done streaming handler knows the compaction item type, so it is logged as unparsed and never reaches ChatResponse / chat history.
  • Nothing is replayed. With store=false, the next turn resends the full uncompacted history without the compaction item. The service has to compact again, and no tokens are saved.
  • context_management is not declared on OpenAIChatOptions / FoundryChatOptions.

Expected behavior

  • A compaction output item (streaming and non-streaming) is preserved as content in the response and in history, including its encrypted_content.
  • With store=false, the next request replays the latest compaction item as an input item and omits the messages that came before it (keeping system/developer messages). This is what makes compaction actually save tokens in stateless mode.
  • With previous_response_id / conversation continuation, the compaction item is not re-sent (the service already has it) and no pruning happens, as the docs require.
  • context_management is available as a typed option.

Alternatives considered

The framework's compaction_strategy does client-side compaction, which is a different feature and doesn't benefit from the service's encrypted compaction state.

Code Sample

from agent_framework import Agent, InMemoryHistoryProvider
from agent_framework_foundry import FoundryChatClient

agent = Agent(
    client=FoundryChatClient(),
    context_providers=[InMemoryHistoryProvider()],
    default_options={
        "store": False,
        "context_management": [{"type": "compaction", "compact_threshold": 200000}],
    },
)
session = agent.create_session()

# Long-running conversation: once the service compacts, later turns should send only
# the latest compaction item plus newer messages instead of the full history.
while True:
    await agent.run(get_next_user_input(), session=session)

Language/SDK

Python

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 30, 2026
  2. removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 30, 2026
  3. eavanvalkenburg commented on Oct 15, 2026

    @eavanvalkenburg
    Member

    /plan

  4. github-actions commented on Oct 15, 2026

    @github-actions
    Contributor

    Summary

    The issue reports that OpenAIChatClient (and FoundryChatClient, which wraps it) don't support the Responses API's server-side compaction feature end-to-end. Server-side compaction is a service feature: once a conversation gets long, OpenAI's Responses API can automatically "compact" (summarize) older turns server-side and hand back a special compaction item. When the app sends store=false (it doesn't ask OpenAI to retain the conversation), the app is responsible for re-sending that compaction item on the next turn instead of the full raw history, which is what actually saves tokens/cost.

    Investigation

    I read the relevant client code in python/packages/openai/agent_framework_openai/_chat_client.py and confirmed each claim in the issue against current (not any PR's) code:

    • Request side works today: context_management isn't a declared option on OpenAIChatOptions (confirmed — no mention of it anywhere in the option TypedDicts), but because these option objects aren't runtime-validated, any extra key in the options dict still flows through _prepare_options into the raw request sent to the API. So callers can pass it today, just without IDE/type-checker support.
    • The compaction item is dropped: In the non-streaming parser (_parse_response_from_openai), the big match item.type: block has cases for message, reasoning, function_call, mcp_call, image_generation_call, etc., but no case for "compaction" — it falls through to the catch-all case _: which just logs "Unparsed output of type" and discards it. The streaming handler for response.output_item.done has the same gap (cases for reasoning, message, mcp_call, search/shell/computer/image items — no compaction case).
    • Nothing is replayed: Looking at _prepare_message_for_openai (which turns a stored Message/Content back into a Responses API input item) and the core Content type (ContentType literal in agent_framework/_types.py), there's no "compaction" content type at all, so there's nothing to even attach the item to, let alone replay or use to prune older history.
    • No typed option: Confirmed, context_management doesn't appear on OpenAIChatOptions (used directly by OpenAIChatClient, and aliased as FoundryChatOptions for FoundryChatClient).

    I checked the OpenAI Python SDK's types directly (response_compaction_item.py, response_output_item.py, response_create_params.py in openai/openai-python) and confirmed the SDK already models ResponseCompactionItem (with id, encrypted_content, type: "compaction") as part of the ResponseOutputItem union, and context_management as a typed ResponseCreateParamsBase field — so the SDK already exposes everything needed; the gap is entirely in this framework's client code.

    There's already a closely related pattern in this same file for OpenAI's encrypted reasoning items (_prepare_reasoning_items_for_openai, protected_data, the include=["reasoning.encrypted_content"] opt-in, and the stateless-replay validation in _validate_reasoning_groups_for_stateless_replay). Server-side compaction is conceptually the same shape of problem (an opaque, encrypted, server-issued item that must be preserved and selectively replayed in stateless mode), so the framework already has the pattern and plumbing to extend — this isn't a totally new kind of integration.

    I noted PR #8891 is open and linked to this issue, but per the workflow rules I did not inspect its diff/approach; the plan below is derived independently from the current repository code and the SDK's type definitions.

    Recommendation

    Code changes are warranted. This is a real framework gap, not an application-specific scenario: anyone using OpenAIChatClient or FoundryChatClient with the Responses API in stateless (store=false) mode on a long-running conversation hits this — the compaction item is silently dropped, so the service has to recompact every turn and no tokens are ever saved, defeating the point of the feature. There's no existing workaround available to callers since this is internal request/response translation logic they don't control. Client-side compaction_strategy (mentioned as "alternatives considered" in the issue) is a different, already-supported feature and doesn't reduce redundant encrypted state round-trips the service-side feature avoids.

    The scope is proportionate to the benefit: it needs a new content type plus handling in a small, well-isolated set of methods that already have an analogous pattern (reasoning items), not a redesign.

    Proposed plan

    1. Add a compaction content type. Extend ContentType in agent_framework/_types.py with a "compaction" value and add a Content constructor (e.g. Content.from_compaction(id=..., protected_data=...)), mirroring how from_text_reasoning carries protected_data/additional_properties for encrypted payloads.
    2. Parse the compaction item. In _chat_client.py, add a case "compaction": branch to the non-streaming _parse_response_from_openai match block, and a corresponding branch in the streaming response.output_item.done handler, each converting the SDK's ResponseCompactionItem/event item into the new Content, preserving encrypted_content and id.
    3. Replay the compaction item on the next request. Add a case in _prepare_message_for_openai to serialize a stored compaction Content back into a {"type": "compaction", "id": ..., "encrypted_content": ...} input item, similar to how reasoning items are reconstructed.
    4. Prune older history in stateless mode only. In _prepare_messages_for_openai/_prepare_options, when request_uses_service_side_storage is False (no previous_response_id/conversation), scan for the latest compaction content in the message history; if present, send only that item plus later messages (keeping system/developer messages), instead of the full prior history. When continuation (previous_response_id/conversation) is used, skip this entirely — per the docs, the service already has the item and no pruning should happen.
    5. Add the typed option. Add context_management to OpenAIChatOptions (which FoundryChatOptions already aliases) with a type matching the SDK's ContextManagement entries (type, compact_threshold).
    6. Tests: non-streaming and streaming parsing of a compaction item (including encrypted_content round-trip); serialization back to a request input item; pruning behavior with store=false (only trailing compaction + newer messages sent); no pruning/no resend when previous_response_id/conversation is set; and a check that context_management is now a recognized, typed option. Existing reasoning-item and stateless-replay tests should keep passing unchanged, since this only adds a new, previously-unhandled item type.
    7. Docs: update python/packages/openai/AGENTS.md (and foundry package docs if relevant) to describe the new compaction replay/pruning behavior, following the existing documentation style used for the encrypted-reasoning-item handling.

    This is a proposal only — no code has been changed. You'd know this is resolved once a long store=false conversation using context_management compaction shows the compaction item surviving in ChatResponse/history and subsequent requests shrinking to roughly system messages + latest compaction item + new turns, instead of resending the full growing history.

    Generated by Issue Planning for #8890 · copilot · auto · 156.7 AIC · ⌖ 7.1 AIC · ⊞ 10.8K · ◷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: Python

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions