Files
agent-framework/python/samples/02-agents/conversations
T
Evan Mattson 5e8fe0be1f Python: Stop emitting duplicate reasoning content from OpenAI response.reasoning_text.done and response.reasoning_summary_text.done events (#5162)
* Fix reasoning text done events duplicating streamed delta content (#5157)

The OpenAI Responses API sends both reasoning_text.delta (incremental
chunks) and reasoning_text.done (full accumulated text) events. The
chat client was emitting Content for both, causing ag-ui to append the
full done text onto already-accumulated delta text, producing
duplicated reasoning output.

Stop emitting Content for reasoning_text.done and
reasoning_summary_text.done events, matching how output_text.done is
already handled (not emitted). The deltas contain all the content;
the done event is redundant.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(openai): emit reasoning done content as fallback when no deltas observed (#5157)

Address PR review feedback:
- Track item_ids that received reasoning deltas via seen_reasoning_delta_item_ids set
- Emit content from done events only when no deltas were received for the
  item_id, preventing silent content loss on stream resumption
- Add comment documenting code_interpreter done event asymmetry
- Replace redundant ag-ui test with deduplication-focused test
- Add integration test for delta+done sequence in OpenAI chat client tests
- Add fallback path tests for done events without preceding deltas

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback for #5157: Python: [Bug]: "type": "response.reasoning_text.delta" and "response.reasoning_text.done" both get exposed as "text_reasoning"

* Fix AG-UI reasoning streaming to use proper Start/End pattern (#5157)

_emit_text_reasoning now follows the same streaming pattern as _emit_text:
- Emits ReasoningStartEvent/ReasoningMessageStartEvent only on the first
  delta for a given message_id
- Emits only ReasoningMessageContentEvent for subsequent deltas
- Defers ReasoningMessageEndEvent/ReasoningEndEvent until
  _close_reasoning_block is called (on content type switch or end-of-run)

This produces the correct protocol pattern:
  ReasoningStartEvent
    ReasoningMessageStartEvent
    ReasoningMessageContentEvent(delta1)
    ReasoningMessageContentEvent(delta2)
    ReasoningMessageEndEvent
  ReasoningEndEvent

Instead of wrapping every delta in a full Start→End sequence.

Backward compatibility is preserved: calling _emit_text_reasoning without
a flow argument still produces the full sequence per call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix import ordering lint error in AG-UI test file (#5157)

Move inline import of TextMessageContentEvent to the top-level import
block and ensure alphabetical ordering to satisfy ruff I001 rule.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix mypy error: rename loop variable to avoid type conflict with WorkflowEvent

The 'event' variable was already typed as WorkflowEvent[Any] from the
async for loop at line 590. Reusing it in the _close_reasoning_block
loop (which returns list[BaseEvent]) caused an incompatible assignment
error. Renamed to 'reasoning_evt' to avoid the conflict.

Fixes #5162

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review feedback for #5157: review comment fixes

* narrow test result reporting to explicit pytest JUnit XML

* Fix test args

* Fix pytest-results-action in merge workflow and remove committed test artifacts

Apply the same JUnit XML fix from python-tests.yml to python-merge-tests.yml:
add --junitxml=pytest.xml to all test commands and narrow the results action
path from ./python/**.xml to ./python/pytest.xml. Also remove accidentally
committed pytest.xml and python-coverage.xml and add them to .gitignore.

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5e8fe0be1f · 2026-04-09 22:44:59 +00:00
History
..

Conversation & Session Management Samples

These samples demonstrate different approaches to managing conversation history and session state in Agent Framework.

Samples

File Description
suspend_resume_session.py Suspend and resume conversation sessions, comparing service-managed sessions (Azure AI Foundry) with in-memory sessions (OpenAI).
custom_history_provider.py Implement a custom history provider by extending HistoryProvider, enabling conversation persistence in your preferred storage backend.
cosmos_history_provider.py Use Azure Cosmos DB as a history provider for durable conversation storage with CosmosHistoryProvider.
cosmos_history_provider_conversation_persistence.py Persist and resume conversations across application restarts using CosmosHistoryProvider — serialize session state, restore it, and continue with full Cosmos DB history.
cosmos_history_provider_messages.py Direct message history operations — retrieve stored messages as a transcript, clear session history, and verify data deletion.
cosmos_history_provider_sessions.py Multi-session and multi-tenant management — per-tenant session isolation, list_sessions() to enumerate, switch between sessions, and resume specific conversations.
redis_history_provider.py Use Redis as a history provider for persistent conversation history storage across sessions.

Prerequisites

For suspend_resume_session.py:

  • FOUNDRY_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint (service-managed session)
  • FOUNDRY_MODEL: The Foundry model deployment name
  • OPENAI_API_KEY: Your OpenAI API key (in-memory session)
  • Azure CLI authentication (az login)

For custom_history_provider.py:

  • OPENAI_API_KEY: Your OpenAI API key

For Cosmos DB samples (cosmos_history_provider*.py):

  • FOUNDRY_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint
  • FOUNDRY_MODEL: The Foundry model deployment name
  • AZURE_COSMOS_ENDPOINT: Your Azure Cosmos DB account endpoint
  • AZURE_COSMOS_DATABASE_NAME: The database that stores conversation history
  • AZURE_COSMOS_CONTAINER_NAME: The container that stores conversation history
  • Either AZURE_COSMOS_KEY or Azure CLI authentication (az login)

For redis_history_provider.py:

  • OPENAI_API_KEY: Your OpenAI API key
  • A running Redis server — default URL is redis://localhost:6379
    • Override via the REDIS_URL environment variable for remote or authenticated instances
    • Quickstart with Docker: docker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest