mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] update context provider APIs, middleware, and per-service-call history persistence (#4992)
* Rename provider base APIs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Allow provider-added chat and function middleware Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simulate service-stored history per model call Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix typing regressions in CI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix response ID suppression review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Rename per-service-call history persistence APIs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address context persistence review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stabilize markdown sample docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Persist service continuation state per call Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
38de991481
commit
b065a4ce51
@@ -39,7 +39,7 @@ from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, SupportsAgentRun
|
||||
from agent_framework._middleware import FunctionInvocationContext, FunctionMiddleware
|
||||
from agent_framework._middleware import FunctionInvocationContext, FunctionMiddleware, MiddlewareTermination
|
||||
from agent_framework._sessions import AgentSession
|
||||
from agent_framework._tools import FunctionTool, tool
|
||||
from agent_framework._types import AgentResponse, Content, Message
|
||||
@@ -138,8 +138,6 @@ class _AutoHandoffMiddleware(FunctionMiddleware):
|
||||
await call_next()
|
||||
return
|
||||
|
||||
from agent_framework._middleware import MiddlewareTermination
|
||||
|
||||
# Short-circuit execution and provide deterministic response payload for the tool call.
|
||||
# Parse the result using the default parser to ensure in a form that can be passed directly to LLM APIs.
|
||||
context.result = FunctionTool.parse_result({
|
||||
@@ -375,6 +373,7 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
description=agent.description,
|
||||
context_providers=agent.context_providers,
|
||||
middleware=agent.agent_middleware,
|
||||
require_per_service_call_history_persistence=agent.require_per_service_call_history_persistence,
|
||||
default_options=cloned_options, # type: ignore[assignment]
|
||||
)
|
||||
|
||||
|
||||
@@ -8,10 +8,11 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
BaseContextProvider,
|
||||
ChatResponse,
|
||||
ChatResponseUpdate,
|
||||
Content,
|
||||
ContextProvider,
|
||||
InMemoryHistoryProvider,
|
||||
Message,
|
||||
ResponseStream,
|
||||
WorkflowEvent,
|
||||
@@ -695,6 +696,48 @@ def test_handoff_clone_disables_provider_side_storage() -> None:
|
||||
assert executor._agent.default_options.get("store") is False
|
||||
|
||||
|
||||
async def test_handoff_clone_preserves_per_service_call_history_persistence() -> None:
|
||||
"""Handoff clones should keep per-service-call history persistence active for auto-handoff termination."""
|
||||
triage_history = InMemoryHistoryProvider()
|
||||
triage = Agent(
|
||||
id="triage",
|
||||
name="triage",
|
||||
client=MockChatClient(name="triage", handoff_to="specialist"),
|
||||
context_providers=[triage_history],
|
||||
require_per_service_call_history_persistence=True,
|
||||
)
|
||||
specialist = Agent(
|
||||
id="specialist",
|
||||
name="specialist",
|
||||
client=MockChatClient(name="specialist"),
|
||||
default_options={"tool_choice": "none"},
|
||||
)
|
||||
|
||||
workflow = (
|
||||
HandoffBuilder(participants=[triage, specialist], termination_condition=lambda _: False)
|
||||
.with_start_agent(triage)
|
||||
.add_handoff(triage, [specialist])
|
||||
.add_handoff(specialist, [triage])
|
||||
.build()
|
||||
)
|
||||
|
||||
await _drain(workflow.run("start", stream=True))
|
||||
|
||||
executor = workflow.executors[resolve_agent_id(triage)]
|
||||
assert isinstance(executor, HandoffAgentExecutor)
|
||||
assert executor._agent.require_per_service_call_history_persistence is True
|
||||
|
||||
provider_state = executor._session.state[triage_history.source_id]
|
||||
stored_messages = await triage_history.get_messages(
|
||||
executor._session.session_id,
|
||||
state=provider_state,
|
||||
)
|
||||
|
||||
assert [message.role for message in stored_messages] == ["user", "assistant"]
|
||||
assert any(content.type == "function_call" for content in stored_messages[-1].contents)
|
||||
assert all(message.role != "tool" for message in stored_messages)
|
||||
|
||||
|
||||
async def test_handoff_clears_stale_service_session_id_before_run() -> None:
|
||||
"""Stale service session IDs must be dropped before each handoff agent turn."""
|
||||
triage = MockHandoffAgent(name="triage", handoff_to="specialist")
|
||||
@@ -997,7 +1040,7 @@ async def test_context_provider_preserved_during_handoff():
|
||||
# Track whether context provider methods were called
|
||||
provider_calls: list[str] = []
|
||||
|
||||
class TestContextProvider(BaseContextProvider):
|
||||
class TestContextProvider(ContextProvider):
|
||||
"""A test context provider that tracks its invocations."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user