mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
1e350ea22f
* PR2: Wire context provider pipeline and update all internal consumers - Replace AgentThread with AgentSession across all packages - Replace ContextProvider with BaseContextProvider across all packages - Replace context_provider param with context_providers (Sequence) - Replace thread= with session= in run() signatures - Replace get_new_thread() with create_session() - Add get_session(service_session_id) to agent interface - DurableAgentThread -> DurableAgentSession - Remove _notify_thread_of_new_messages from WorkflowAgent - Wire before_run/after_run context provider pipeline in RawAgent - Auto-inject InMemoryHistoryProvider when no providers configured * fix: update all tests for context provider pipeline, fix lazy-loaders, remove old test files * refactor: update all sample files for context provider pipeline (AgentThread→AgentSession, ContextProvider→BaseContextProvider) * fix: update remaining ag-ui references (client docstring, getting_started sample) * fix: make get_session service_session_id keyword-only to avoid confusion with session_id * refactor: rename _RunContext.thread_messages to session_messages * refactor: remove _threads.py, _memory.py, and old provider files; migrate devui to use plain message lists * rename: remove _new_ prefix from test files * refactor: rewrite SlidingWindowChatMessageStore as SlidingWindowHistoryProvider(InMemoryHistoryProvider) * fix: read full history from session state directly instead of reaching into provider internals * fix: update stale .pyi stubs, sample imports, and README references for new provider types * fix: remove stale message_store, _notify_thread_of_new_messages, and session_id.key references in samples * refactor: merge context_providers and sessions sample folders into sessions, remove aggregate_context_provider * refactor: UserInfoMemory stores state in session.state instead of instance attributes * feat: add Pydantic BaseModel support to session state serialization Pydantic models stored in session.state are now automatically serialized via model_dump() and restored via model_validate() during to_dict()/from_dict() round-trips. Models are auto-registered on first serialization; use register_state_type() for cold-start deserialization. Also export register_state_type as a public API. * fix mem0 * Update sample README links and descriptions for session terminology - Replace 'thread' with 'session' in sample descriptions across all READMEs - Update file links for renamed samples (mem0_sessions, redis_sessions, etc.) - Fix Threads section → Sessions section in main samples/README.md - Update tools, middleware, workflows, durabletask, azure_functions READMEs - Update architecture diagrams in concepts/tools/README.md - Update migration guides (autogen, semantic-kernel) * Fix broken Redis README link to renamed sample * Fix Mem0 OSS client search: pass scoping params as direct kwargs AsyncMemory (OSS) expects user_id/agent_id/run_id as direct kwargs, while AsyncMemoryClient (Platform) expects them in a filters dict. Adds tests for both client types. Port of fix from #3844 to new Mem0ContextProvider. * Fix rebase issues: restore missing _conversation_state.py and checkpoint decode logic - Add back _conversation_state.py (encode/decode_chat_messages) lost in rebase - Fix on_checkpoint_restore to decode cache/conversation with decode_chat_messages - Fix on_checkpoint_restore to use decode_checkpoint_value for pending requests - Add tests/workflow/__init__.py for relative import support - Fix test_agent_executor checkpoint selection (checkpoints[1] not superstep) * Add STORES_BY_DEFAULT ClassVar to skip redundant InMemoryHistoryProvider injection Chat clients that store history server-side by default (OpenAI Responses API, Azure AI Agent) now declare STORES_BY_DEFAULT = True. The agent checks this during auto-injection and skips InMemoryHistoryProvider unless the user explicitly sets store=False. * Fix broken markdown links in azure_ai and redis READMEs * Fix getting-started samples to use session API instead of removed thread/ContextProvider API * updates to workflow as agent * fix group chat import * Rename Thread→Session throughout, fix service_session_id propagation, remove stale AGUIThread - Fix: Propagate conversation_id from ChatResponse back to session.service_session_id in both streaming and non-streaming paths in _agents.py - Rename AgentThreadException → AgentSessionException - Remove stale AGUIThread from ag_ui lazy-loader - Rename use_service_thread → use_service_session in ag-ui package - Rename test functions from *_thread_* to *_session_* - Rename sample files from *_thread* to *_session* - Update docstrings and comments: thread → session - Update _mcp.py kwargs filter: add 'session' alongside 'thread' - Fix ContinuationToken docstring example: thread=thread → session=session - Fix _clients.py docstring: 'Agent threads' → 'Agent sessions' * Fix broken markdown links after thread→session file renames * fix azure ai test
140 lines
4.1 KiB
Python
140 lines
4.1 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
||
|
||
import asyncio
|
||
|
||
from agent_framework import Agent
|
||
from agent_framework.openai import OpenAIResponsesClient
|
||
|
||
"""Background Responses Sample.
|
||
|
||
This sample demonstrates long-running agent operations using the OpenAI
|
||
Responses API ``background`` option. Two patterns are shown:
|
||
|
||
1. **Non-streaming polling** – start a background run, then poll with the
|
||
``continuation_token`` until the operation completes.
|
||
2. **Streaming with resumption** – start a background streaming run, simulate
|
||
an interruption, and resume from the last ``continuation_token``.
|
||
|
||
Prerequisites:
|
||
- Set the ``OPENAI_API_KEY`` environment variable.
|
||
- A model that benefits from background execution (e.g. ``o3``).
|
||
"""
|
||
|
||
|
||
# 1. Create the agent with an OpenAI Responses client.
|
||
agent = Agent(
|
||
name="researcher",
|
||
instructions="You are a helpful research assistant. Be concise.",
|
||
client=OpenAIResponsesClient(model_id="o3"),
|
||
)
|
||
|
||
|
||
async def non_streaming_polling() -> None:
|
||
"""Demonstrate non-streaming background run with polling."""
|
||
print("=== Non-Streaming Polling ===\n")
|
||
|
||
session = agent.create_session()
|
||
|
||
# 2. Start a background run — returns immediately.
|
||
response = await agent.run(
|
||
messages="Briefly explain the theory of relativity in two sentences.",
|
||
session=session,
|
||
options={"background": True},
|
||
)
|
||
|
||
print(f"Initial status: continuation_token={'set' if response.continuation_token else 'None'}")
|
||
|
||
# 3. Poll until the operation completes.
|
||
poll_count = 0
|
||
while response.continuation_token is not None:
|
||
poll_count += 1
|
||
await asyncio.sleep(2)
|
||
response = await agent.run(
|
||
session=session,
|
||
options={"continuation_token": response.continuation_token},
|
||
)
|
||
print(f" Poll {poll_count}: continuation_token={'set' if response.continuation_token else 'None'}")
|
||
|
||
# 4. Done — print the final result.
|
||
print(f"\nResult ({poll_count} poll(s)):\n{response.text}\n")
|
||
|
||
|
||
async def streaming_with_resumption() -> None:
|
||
"""Demonstrate streaming background run with simulated interruption and resumption."""
|
||
print("=== Streaming with Resumption ===\n")
|
||
|
||
session = agent.create_session()
|
||
|
||
# 2. Start a streaming background run.
|
||
last_token = None
|
||
stream = agent.run(
|
||
messages="Briefly list three benefits of exercise.",
|
||
stream=True,
|
||
session=session,
|
||
options={"background": True},
|
||
)
|
||
|
||
# 3. Read some chunks, then simulate an interruption.
|
||
chunk_count = 0
|
||
print("First stream (before interruption):")
|
||
async for update in stream:
|
||
last_token = update.continuation_token
|
||
if update.text:
|
||
print(update.text, end="", flush=True)
|
||
chunk_count += 1
|
||
if chunk_count >= 3:
|
||
print("\n [simulated interruption]")
|
||
break
|
||
|
||
# 4. Resume from the last continuation token.
|
||
if last_token is not None:
|
||
print("Resumed stream:")
|
||
stream = agent.run(
|
||
stream=True,
|
||
session=session,
|
||
options={"continuation_token": last_token},
|
||
)
|
||
async for update in stream:
|
||
if update.text:
|
||
print(update.text, end="", flush=True)
|
||
|
||
print("\n")
|
||
|
||
|
||
async def main() -> None:
|
||
await non_streaming_polling()
|
||
await streaming_with_resumption()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(main())
|
||
|
||
"""
|
||
Sample output:
|
||
|
||
=== Non-Streaming Polling ===
|
||
|
||
Initial status: continuation_token=set
|
||
Poll 1: continuation_token=set
|
||
Poll 2: continuation_token=None
|
||
|
||
Result (2 poll(s)):
|
||
The theory of relativity, developed by Albert Einstein, consists of special
|
||
relativity (1905), which shows that the laws of physics are the same for all
|
||
non-accelerating observers and that the speed of light is constant, and general
|
||
relativity (1915), which describes gravity as the curvature of spacetime caused
|
||
by mass and energy.
|
||
|
||
=== Streaming with Resumption ===
|
||
|
||
First stream (before interruption):
|
||
Here are three
|
||
[simulated interruption]
|
||
Resumed stream:
|
||
key benefits of regular exercise:
|
||
|
||
1. **Improved cardiovascular health** ...
|
||
2. **Better mental health** ...
|
||
3. **Stronger muscles and bones** ...
|
||
"""
|