mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [BREAKING] Renamed AgentProtocol to SupportsAgentRun (#3717)
* Renamed AgentProtocol to AgentLike * Resolved comments * Renamed AgentLike to SupportsAgentRun * Resolved comments
This commit is contained in:
committed by
GitHub
Unverified
parent
ac17adb595
commit
15256bb616
@@ -6,7 +6,7 @@ import logging
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentProtocol, ChatMessage
|
||||
from agent_framework import ChatMessage, SupportsAgentRun
|
||||
from agent_framework._workflows._agent_executor import AgentExecutor, AgentExecutorRequest, AgentExecutorResponse
|
||||
from agent_framework._workflows._agent_utils import resolve_agent_id
|
||||
from agent_framework._workflows._checkpoint import CheckpointStorage
|
||||
@@ -29,8 +29,8 @@ parallel workflow with:
|
||||
- a default aggregator that combines all agent conversations and completes the workflow
|
||||
|
||||
Notes:
|
||||
- Participants can be provided as AgentProtocol or Executor instances via `.participants()`,
|
||||
or as factories returning AgentProtocol or Executor via `.register_participants()`.
|
||||
- Participants can be provided as SupportsAgentRun or Executor instances via `.participants()`,
|
||||
or as factories returning SupportsAgentRun or Executor via `.register_participants()`.
|
||||
- A custom aggregator can be provided as:
|
||||
- an Executor instance (it should handle list[AgentExecutorResponse],
|
||||
yield output), or
|
||||
@@ -186,8 +186,8 @@ class _CallbackAggregator(Executor):
|
||||
class ConcurrentBuilder:
|
||||
r"""High-level builder for concurrent agent workflows.
|
||||
|
||||
- `participants([...])` accepts a list of AgentProtocol (recommended) or Executor.
|
||||
- `register_participants([...])` accepts a list of factories for AgentProtocol (recommended)
|
||||
- `participants([...])` accepts a list of SupportsAgentRun (recommended) or Executor.
|
||||
- `register_participants([...])` accepts a list of factories for SupportsAgentRun (recommended)
|
||||
or Executor factories
|
||||
- `build()` wires: dispatcher -> fan-out -> participants -> fan-in -> aggregator.
|
||||
- `with_aggregator(...)` overrides the default aggregator with an Executor or callback.
|
||||
@@ -238,8 +238,8 @@ class ConcurrentBuilder:
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._participants: list[AgentProtocol | Executor] = []
|
||||
self._participant_factories: list[Callable[[], AgentProtocol | Executor]] = []
|
||||
self._participants: list[SupportsAgentRun | Executor] = []
|
||||
self._participant_factories: list[Callable[[], SupportsAgentRun | Executor]] = []
|
||||
self._aggregator: Executor | None = None
|
||||
self._aggregator_factory: Callable[[], Executor] | None = None
|
||||
self._checkpoint_storage: CheckpointStorage | None = None
|
||||
@@ -249,16 +249,16 @@ class ConcurrentBuilder:
|
||||
|
||||
def register_participants(
|
||||
self,
|
||||
participant_factories: Sequence[Callable[[], AgentProtocol | Executor]],
|
||||
participant_factories: Sequence[Callable[[], SupportsAgentRun | Executor]],
|
||||
) -> "ConcurrentBuilder":
|
||||
r"""Define the parallel participants for this concurrent workflow.
|
||||
|
||||
Accepts factories (callables) that return AgentProtocol instances (e.g., created
|
||||
Accepts factories (callables) that return SupportsAgentRun instances (e.g., created
|
||||
by a chat client) or Executor instances. Each participant created by a factory
|
||||
is wired as a parallel branch using fan-out edges from an internal dispatcher.
|
||||
|
||||
Args:
|
||||
participant_factories: Sequence of callables returning AgentProtocol or Executor instances
|
||||
participant_factories: Sequence of callables returning SupportsAgentRun or Executor instances
|
||||
|
||||
Raises:
|
||||
ValueError: if `participant_factories` is empty or `.participants()`
|
||||
@@ -300,20 +300,20 @@ class ConcurrentBuilder:
|
||||
self._participant_factories = list(participant_factories)
|
||||
return self
|
||||
|
||||
def participants(self, participants: Sequence[AgentProtocol | Executor]) -> "ConcurrentBuilder":
|
||||
def participants(self, participants: Sequence[SupportsAgentRun | Executor]) -> "ConcurrentBuilder":
|
||||
r"""Define the parallel participants for this concurrent workflow.
|
||||
|
||||
Accepts AgentProtocol instances (e.g., created by a chat client) or Executor
|
||||
Accepts SupportsAgentRun instances (e.g., created by a chat client) or Executor
|
||||
instances. Each participant is wired as a parallel branch using fan-out edges
|
||||
from an internal dispatcher.
|
||||
|
||||
Args:
|
||||
participants: Sequence of AgentProtocol or Executor instances
|
||||
participants: Sequence of SupportsAgentRun or Executor instances
|
||||
|
||||
Raises:
|
||||
ValueError: if `participants` is empty, contains duplicates, or `.register_participants()`
|
||||
or `.participants()` were already called
|
||||
TypeError: if any entry is not AgentProtocol or Executor
|
||||
TypeError: if any entry is not SupportsAgentRun or Executor
|
||||
|
||||
Example:
|
||||
|
||||
@@ -341,13 +341,13 @@ class ConcurrentBuilder:
|
||||
if p.id in seen_executor_ids:
|
||||
raise ValueError(f"Duplicate executor participant detected: id '{p.id}'")
|
||||
seen_executor_ids.add(p.id)
|
||||
elif isinstance(p, AgentProtocol):
|
||||
elif isinstance(p, SupportsAgentRun):
|
||||
pid = id(p)
|
||||
if pid in seen_agent_ids:
|
||||
raise ValueError("Duplicate agent participant detected (same agent instance provided twice)")
|
||||
seen_agent_ids.add(pid)
|
||||
else:
|
||||
raise TypeError(f"participants must be AgentProtocol or Executor instances; got {type(p).__name__}")
|
||||
raise TypeError(f"participants must be SupportsAgentRun or Executor instances; got {type(p).__name__}")
|
||||
|
||||
self._participants = list(participants)
|
||||
return self
|
||||
@@ -459,7 +459,7 @@ class ConcurrentBuilder:
|
||||
def with_request_info(
|
||||
self,
|
||||
*,
|
||||
agents: Sequence[str | AgentProtocol] | None = None,
|
||||
agents: Sequence[str | SupportsAgentRun] | None = None,
|
||||
) -> "ConcurrentBuilder":
|
||||
"""Enable request info after agent participant responses.
|
||||
|
||||
@@ -508,7 +508,7 @@ class ConcurrentBuilder:
|
||||
raise ValueError("No participants provided. Call .participants() or .register_participants() first.")
|
||||
# We don't need to check if both are set since that is handled in the respective methods
|
||||
|
||||
participants: list[Executor | AgentProtocol] = []
|
||||
participants: list[Executor | SupportsAgentRun] = []
|
||||
if self._participant_factories:
|
||||
# Resolve the participant factories now. This doesn't break the factory pattern
|
||||
# since the Sequential builder still creates new instances per workflow build.
|
||||
@@ -522,7 +522,7 @@ class ConcurrentBuilder:
|
||||
for p in participants:
|
||||
if isinstance(p, Executor):
|
||||
executors.append(p)
|
||||
elif isinstance(p, AgentProtocol):
|
||||
elif isinstance(p, SupportsAgentRun):
|
||||
if self._request_info_enabled and (
|
||||
not self._request_info_filter or resolve_agent_id(p) in self._request_info_filter
|
||||
):
|
||||
@@ -531,7 +531,7 @@ class ConcurrentBuilder:
|
||||
else:
|
||||
executors.append(AgentExecutor(p))
|
||||
else:
|
||||
raise TypeError(f"Participants must be AgentProtocol or Executor instances. Got {type(p).__name__}.")
|
||||
raise TypeError(f"Participants must be SupportsAgentRun or Executor instances. Got {type(p).__name__}.")
|
||||
|
||||
return executors
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ from collections.abc import Awaitable, Callable, Sequence
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, ClassVar, cast, overload
|
||||
|
||||
from agent_framework import AgentProtocol, ChatAgent
|
||||
from agent_framework import ChatAgent, SupportsAgentRun
|
||||
from agent_framework._threads import AgentThread
|
||||
from agent_framework._types import ChatMessage
|
||||
from agent_framework._workflows._agent_executor import AgentExecutor, AgentExecutorRequest, AgentExecutorResponse
|
||||
@@ -523,8 +523,8 @@ class GroupChatBuilder:
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the GroupChatBuilder."""
|
||||
self._participants: dict[str, AgentProtocol | Executor] = {}
|
||||
self._participant_factories: list[Callable[[], AgentProtocol | Executor]] = []
|
||||
self._participants: dict[str, SupportsAgentRun | Executor] = {}
|
||||
self._participant_factories: list[Callable[[], SupportsAgentRun | Executor]] = []
|
||||
|
||||
# Orchestrator related members
|
||||
self._orchestrator: BaseGroupChatOrchestrator | None = None
|
||||
@@ -683,13 +683,13 @@ class GroupChatBuilder:
|
||||
|
||||
def register_participants(
|
||||
self,
|
||||
participant_factories: Sequence[Callable[[], AgentProtocol | Executor]],
|
||||
participant_factories: Sequence[Callable[[], SupportsAgentRun | Executor]],
|
||||
) -> "GroupChatBuilder":
|
||||
"""Register participant factories for this group chat workflow.
|
||||
|
||||
Args:
|
||||
participant_factories: Sequence of callables that produce participant definitions
|
||||
when invoked. Each callable should return either an AgentProtocol instance
|
||||
when invoked. Each callable should return either an SupportsAgentRun instance
|
||||
(auto-wrapped as AgentExecutor) or an Executor instance.
|
||||
|
||||
Returns:
|
||||
@@ -711,10 +711,10 @@ class GroupChatBuilder:
|
||||
self._participant_factories = list(participant_factories)
|
||||
return self
|
||||
|
||||
def participants(self, participants: Sequence[AgentProtocol | Executor]) -> "GroupChatBuilder":
|
||||
def participants(self, participants: Sequence[SupportsAgentRun | Executor]) -> "GroupChatBuilder":
|
||||
"""Define participants for this group chat workflow.
|
||||
|
||||
Accepts AgentProtocol instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
Accepts SupportsAgentRun instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
|
||||
Args:
|
||||
participants: Sequence of participant definitions
|
||||
@@ -725,7 +725,7 @@ class GroupChatBuilder:
|
||||
Raises:
|
||||
ValueError: If participants are empty, names are duplicated, or participants
|
||||
or participant factories are already set
|
||||
TypeError: If any participant is not AgentProtocol or Executor instance
|
||||
TypeError: If any participant is not SupportsAgentRun or Executor instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -750,17 +750,17 @@ class GroupChatBuilder:
|
||||
raise ValueError("participants cannot be empty.")
|
||||
|
||||
# Name of the executor mapped to participant instance
|
||||
named: dict[str, AgentProtocol | Executor] = {}
|
||||
named: dict[str, SupportsAgentRun | Executor] = {}
|
||||
for participant in participants:
|
||||
if isinstance(participant, Executor):
|
||||
identifier = participant.id
|
||||
elif isinstance(participant, AgentProtocol):
|
||||
elif isinstance(participant, SupportsAgentRun):
|
||||
if not participant.name:
|
||||
raise ValueError("AgentProtocol participants must have a non-empty name.")
|
||||
raise ValueError("SupportsAgentRun participants must have a non-empty name.")
|
||||
identifier = participant.name
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Participants must be AgentProtocol or Executor instances. Got {type(participant).__name__}."
|
||||
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
|
||||
)
|
||||
|
||||
if identifier in named:
|
||||
@@ -861,7 +861,7 @@ class GroupChatBuilder:
|
||||
self._checkpoint_storage = checkpoint_storage
|
||||
return self
|
||||
|
||||
def with_request_info(self, *, agents: Sequence[str | AgentProtocol] | None = None) -> "GroupChatBuilder":
|
||||
def with_request_info(self, *, agents: Sequence[str | SupportsAgentRun] | None = None) -> "GroupChatBuilder":
|
||||
"""Enable request info after agent participant responses.
|
||||
|
||||
This enables human-in-the-loop (HIL) scenarios for the group chat orchestration.
|
||||
@@ -962,7 +962,7 @@ class GroupChatBuilder:
|
||||
raise ValueError("No participants provided. Call .participants() or .register_participants() first.")
|
||||
# We don't need to check if both are set since that is handled in the respective methods
|
||||
|
||||
participants: list[Executor | AgentProtocol] = []
|
||||
participants: list[Executor | SupportsAgentRun] = []
|
||||
if self._participant_factories:
|
||||
for factory in self._participant_factories:
|
||||
participant = factory()
|
||||
@@ -974,7 +974,7 @@ class GroupChatBuilder:
|
||||
for participant in participants:
|
||||
if isinstance(participant, Executor):
|
||||
executors.append(participant)
|
||||
elif isinstance(participant, AgentProtocol):
|
||||
elif isinstance(participant, SupportsAgentRun):
|
||||
if self._request_info_enabled and (
|
||||
not self._request_info_filter or resolve_agent_id(participant) in self._request_info_filter
|
||||
):
|
||||
@@ -984,7 +984,7 @@ class GroupChatBuilder:
|
||||
executors.append(AgentExecutor(participant))
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Participants must be AgentProtocol or Executor instances. Got {type(participant).__name__}."
|
||||
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
|
||||
)
|
||||
|
||||
return executors
|
||||
|
||||
@@ -36,7 +36,7 @@ from collections.abc import Awaitable, Callable, Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import AgentProtocol, ChatAgent
|
||||
from agent_framework import ChatAgent, SupportsAgentRun
|
||||
from agent_framework._middleware import FunctionInvocationContext, FunctionMiddleware
|
||||
from agent_framework._threads import AgentThread
|
||||
from agent_framework._tools import FunctionTool, tool
|
||||
@@ -89,14 +89,14 @@ class HandoffConfiguration:
|
||||
target_id: str
|
||||
description: str | None = None
|
||||
|
||||
def __init__(self, *, target: str | AgentProtocol, description: str | None = None) -> None:
|
||||
def __init__(self, *, target: str | SupportsAgentRun, description: str | None = None) -> None:
|
||||
"""Initialize HandoffConfiguration.
|
||||
|
||||
Args:
|
||||
target: Target agent identifier or AgentProtocol instance
|
||||
target: Target agent identifier or SupportsAgentRun instance
|
||||
description: Optional human-readable description of the handoff
|
||||
"""
|
||||
self.target_id = resolve_agent_id(target) if isinstance(target, AgentProtocol) else target
|
||||
self.target_id = resolve_agent_id(target) if isinstance(target, SupportsAgentRun) else target
|
||||
self.description = description
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
@@ -193,7 +193,7 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent: AgentProtocol,
|
||||
agent: SupportsAgentRun,
|
||||
handoffs: Sequence[HandoffConfiguration],
|
||||
*,
|
||||
agent_thread: AgentThread | None = None,
|
||||
@@ -236,9 +236,9 @@ class HandoffAgentExecutor(AgentExecutor):
|
||||
|
||||
def _prepare_agent_with_handoffs(
|
||||
self,
|
||||
agent: AgentProtocol,
|
||||
agent: SupportsAgentRun,
|
||||
handoffs: Sequence[HandoffConfiguration],
|
||||
) -> AgentProtocol:
|
||||
) -> SupportsAgentRun:
|
||||
"""Prepare an agent by adding handoff tools for the specified target agents.
|
||||
|
||||
Args:
|
||||
@@ -574,8 +574,8 @@ class HandoffBuilder:
|
||||
self,
|
||||
*,
|
||||
name: str | None = None,
|
||||
participants: Sequence[AgentProtocol] | None = None,
|
||||
participant_factories: Mapping[str, Callable[[], AgentProtocol]] | None = None,
|
||||
participants: Sequence[SupportsAgentRun] | None = None,
|
||||
participant_factories: Mapping[str, Callable[[], SupportsAgentRun]] | None = None,
|
||||
description: str | None = None,
|
||||
) -> None:
|
||||
r"""Initialize a HandoffBuilder for creating conversational handoff workflows.
|
||||
@@ -604,8 +604,8 @@ class HandoffBuilder:
|
||||
self._description = description
|
||||
|
||||
# Participant related members
|
||||
self._participants: dict[str, AgentProtocol] = {}
|
||||
self._participant_factories: dict[str, Callable[[], AgentProtocol]] = {}
|
||||
self._participants: dict[str, SupportsAgentRun] = {}
|
||||
self._participant_factories: dict[str, Callable[[], SupportsAgentRun]] = {}
|
||||
self._start_id: str | None = None
|
||||
if participant_factories:
|
||||
self.register_participants(participant_factories)
|
||||
@@ -629,16 +629,16 @@ class HandoffBuilder:
|
||||
self._termination_condition: Callable[[list[ChatMessage]], bool | Awaitable[bool]] | None = None
|
||||
|
||||
def register_participants(
|
||||
self, participant_factories: Mapping[str, Callable[[], AgentProtocol]]
|
||||
self, participant_factories: Mapping[str, Callable[[], SupportsAgentRun]]
|
||||
) -> "HandoffBuilder":
|
||||
"""Register factories that produce agents for the handoff workflow.
|
||||
|
||||
Each factory is a callable that returns an AgentProtocol instance.
|
||||
Each factory is a callable that returns an SupportsAgentRun instance.
|
||||
Factories are invoked when building the workflow, allowing for lazy instantiation
|
||||
and state isolation per workflow instance.
|
||||
|
||||
Args:
|
||||
participant_factories: Mapping of factory names to callables that return AgentProtocol
|
||||
participant_factories: Mapping of factory names to callables that return SupportsAgentRun
|
||||
instances. Each produced participant must have a unique identifier
|
||||
(`.name` is preferred if set, otherwise `.id` is used).
|
||||
|
||||
@@ -690,11 +690,11 @@ class HandoffBuilder:
|
||||
self._participant_factories = dict(participant_factories)
|
||||
return self
|
||||
|
||||
def participants(self, participants: Sequence[AgentProtocol]) -> "HandoffBuilder":
|
||||
def participants(self, participants: Sequence[SupportsAgentRun]) -> "HandoffBuilder":
|
||||
"""Register the agents that will participate in the handoff workflow.
|
||||
|
||||
Args:
|
||||
participants: Sequence of AgentProtocol instances. Each must have a unique identifier.
|
||||
participants: Sequence of SupportsAgentRun instances. Each must have a unique identifier.
|
||||
(`.name` is preferred if set, otherwise `.id` is used).
|
||||
|
||||
Returns:
|
||||
@@ -703,7 +703,7 @@ class HandoffBuilder:
|
||||
Raises:
|
||||
ValueError: If participants is empty, contains duplicates, or `.participants()` or
|
||||
`.register_participants()` has already been called.
|
||||
TypeError: If participants are not AgentProtocol instances.
|
||||
TypeError: If participants are not SupportsAgentRun instances.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -729,13 +729,13 @@ class HandoffBuilder:
|
||||
if not participants:
|
||||
raise ValueError("participants cannot be empty")
|
||||
|
||||
named: dict[str, AgentProtocol] = {}
|
||||
named: dict[str, SupportsAgentRun] = {}
|
||||
for participant in participants:
|
||||
if isinstance(participant, AgentProtocol):
|
||||
if isinstance(participant, SupportsAgentRun):
|
||||
resolved_id = self._resolve_to_id(participant)
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Participants must be AgentProtocol or Executor instances. Got {type(participant).__name__}."
|
||||
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
|
||||
)
|
||||
|
||||
if resolved_id in named:
|
||||
@@ -748,8 +748,8 @@ class HandoffBuilder:
|
||||
|
||||
def add_handoff(
|
||||
self,
|
||||
source: str | AgentProtocol,
|
||||
targets: Sequence[str] | Sequence[AgentProtocol],
|
||||
source: str | SupportsAgentRun,
|
||||
targets: Sequence[str] | Sequence[SupportsAgentRun],
|
||||
*,
|
||||
description: str | None = None,
|
||||
) -> "HandoffBuilder":
|
||||
@@ -763,11 +763,11 @@ class HandoffBuilder:
|
||||
Args:
|
||||
source: The agent that can initiate the handoff. Can be:
|
||||
- Factory name (str): If using participant factories
|
||||
- AgentProtocol instance: The actual agent object
|
||||
- SupportsAgentRun instance: The actual agent object
|
||||
- Cannot mix factory names and instances across source and targets
|
||||
targets: One or more target agents that the source can hand off to. Can be:
|
||||
- Factory name (str): If using participant factories
|
||||
- AgentProtocol instance: The actual agent object
|
||||
- SupportsAgentRun instance: The actual agent object
|
||||
- Single target: ["billing_agent"] or [agent_instance]
|
||||
- Multiple targets: ["billing_agent", "support_agent"] or [agent1, agent2]
|
||||
- Cannot mix factory names and instances across source and targets
|
||||
@@ -786,7 +786,7 @@ class HandoffBuilder:
|
||||
participants(...) hasn't been called yet.
|
||||
2) If source or targets are factory names (str) but participant_factories(...)
|
||||
hasn't been called yet, or if they are not in the participant_factories list.
|
||||
TypeError: If mixing factory names (str) and AgentProtocol/Executor instances
|
||||
TypeError: If mixing factory names (str) and SupportsAgentRun/Executor instances
|
||||
|
||||
Examples:
|
||||
Single target (using factory name):
|
||||
@@ -848,7 +848,7 @@ class HandoffBuilder:
|
||||
self._handoff_config[source].add(HandoffConfiguration(target=t, description=description))
|
||||
return self
|
||||
|
||||
if isinstance(source, (AgentProtocol)) and all(isinstance(t, AgentProtocol) for t in targets):
|
||||
if isinstance(source, (SupportsAgentRun)) and all(isinstance(t, SupportsAgentRun) for t in targets):
|
||||
# Both source and targets are instances
|
||||
if not self._participants:
|
||||
raise ValueError("Call participants(...) before add_handoff(...)")
|
||||
@@ -881,10 +881,10 @@ class HandoffBuilder:
|
||||
return self
|
||||
|
||||
raise TypeError(
|
||||
"Cannot mix factory names (str) and AgentProtocol instances across source and targets in add_handoff()"
|
||||
"Cannot mix factory names (str) and SupportsAgentRun instances across source and targets in add_handoff()"
|
||||
)
|
||||
|
||||
def with_start_agent(self, agent: str | AgentProtocol) -> "HandoffBuilder":
|
||||
def with_start_agent(self, agent: str | SupportsAgentRun) -> "HandoffBuilder":
|
||||
"""Set the agent that will initiate the handoff workflow.
|
||||
|
||||
If not specified, the first registered participant will be used as the starting agent.
|
||||
@@ -892,7 +892,7 @@ class HandoffBuilder:
|
||||
Args:
|
||||
agent: The agent that will start the workflow. Can be:
|
||||
- Factory name (str): If using participant factories
|
||||
- AgentProtocol instance: The actual agent object
|
||||
- SupportsAgentRun instance: The actual agent object
|
||||
Returns:
|
||||
Self for method chaining.
|
||||
"""
|
||||
@@ -903,7 +903,7 @@ class HandoffBuilder:
|
||||
else:
|
||||
raise ValueError("Call register_participants(...) before with_start_agent(...)")
|
||||
self._start_id = agent
|
||||
elif isinstance(agent, AgentProtocol):
|
||||
elif isinstance(agent, SupportsAgentRun):
|
||||
resolved_id = self._resolve_to_id(agent)
|
||||
if self._participants:
|
||||
if resolved_id not in self._participants:
|
||||
@@ -912,14 +912,14 @@ class HandoffBuilder:
|
||||
raise ValueError("Call participants(...) before with_start_agent(...)")
|
||||
self._start_id = resolved_id
|
||||
else:
|
||||
raise TypeError("Start agent must be a factory name (str) or an AgentProtocol instance")
|
||||
raise TypeError("Start agent must be a factory name (str) or an SupportsAgentRun instance")
|
||||
|
||||
return self
|
||||
|
||||
def with_autonomous_mode(
|
||||
self,
|
||||
*,
|
||||
agents: Sequence[AgentProtocol] | Sequence[str] | None = None,
|
||||
agents: Sequence[SupportsAgentRun] | Sequence[str] | None = None,
|
||||
prompts: dict[str, str] | None = None,
|
||||
turn_limits: dict[str, int] | None = None,
|
||||
) -> "HandoffBuilder":
|
||||
@@ -933,7 +933,7 @@ class HandoffBuilder:
|
||||
Args:
|
||||
agents: Optional list of agents to enable autonomous mode for. Can be:
|
||||
- Factory names (str): If using participant factories
|
||||
- AgentProtocol instances: The actual agent objects
|
||||
- SupportsAgentRun instances: The actual agent objects
|
||||
- If not provided, all agents will operate in autonomous mode.
|
||||
prompts: Optional mapping of agent identifiers/factory names to custom prompts to use when continuing
|
||||
in autonomous mode. If not provided, a default prompt will be used.
|
||||
@@ -1084,7 +1084,7 @@ class HandoffBuilder:
|
||||
|
||||
# region Internal Helper Methods
|
||||
|
||||
def _resolve_agents(self) -> dict[str, AgentProtocol]:
|
||||
def _resolve_agents(self) -> dict[str, SupportsAgentRun]:
|
||||
"""Resolve participant factories into agent instances.
|
||||
|
||||
If agent instances were provided directly via participants(...), those are
|
||||
@@ -1092,7 +1092,7 @@ class HandoffBuilder:
|
||||
those are invoked to create the agent instances.
|
||||
|
||||
Returns:
|
||||
Map of executor IDs or factory names to `AgentProtocol` instances
|
||||
Map of executor IDs or factory names to `SupportsAgentRun` instances
|
||||
"""
|
||||
if not self._participants and not self._participant_factories:
|
||||
raise ValueError("No participants provided. Call .participants() or .register_participants() first.")
|
||||
@@ -1103,13 +1103,13 @@ class HandoffBuilder:
|
||||
|
||||
if self._participant_factories:
|
||||
# Invoke each factory to create participant instances
|
||||
factory_names_to_agents: dict[str, AgentProtocol] = {}
|
||||
factory_names_to_agents: dict[str, SupportsAgentRun] = {}
|
||||
for factory_name, factory in self._participant_factories.items():
|
||||
instance = factory()
|
||||
if isinstance(instance, AgentProtocol):
|
||||
if isinstance(instance, SupportsAgentRun):
|
||||
resolved_id = self._resolve_to_id(instance)
|
||||
else:
|
||||
raise TypeError(f"Participants must be AgentProtocol instances. Got {type(instance).__name__}.")
|
||||
raise TypeError(f"Participants must be SupportsAgentRun instances. Got {type(instance).__name__}.")
|
||||
|
||||
if resolved_id in factory_names_to_agents:
|
||||
raise ValueError(f"Duplicate participant name '{resolved_id}' detected")
|
||||
@@ -1122,11 +1122,11 @@ class HandoffBuilder:
|
||||
|
||||
raise ValueError("No executors or participant_factories have been configured")
|
||||
|
||||
def _resolve_handoffs(self, agents: Mapping[str, AgentProtocol]) -> dict[str, list[HandoffConfiguration]]:
|
||||
def _resolve_handoffs(self, agents: Mapping[str, SupportsAgentRun]) -> dict[str, list[HandoffConfiguration]]:
|
||||
"""Handoffs may be specified using factory names or instances; resolve to executor IDs.
|
||||
|
||||
Args:
|
||||
agents: Map of agent IDs or factory names to `AgentProtocol` instances
|
||||
agents: Map of agent IDs or factory names to `SupportsAgentRun` instances
|
||||
|
||||
Returns:
|
||||
Map of executor IDs to list of HandoffConfiguration instances
|
||||
@@ -1173,13 +1173,13 @@ class HandoffBuilder:
|
||||
|
||||
def _resolve_executors(
|
||||
self,
|
||||
agents: dict[str, AgentProtocol],
|
||||
agents: dict[str, SupportsAgentRun],
|
||||
handoffs: dict[str, list[HandoffConfiguration]],
|
||||
) -> dict[str, HandoffAgentExecutor]:
|
||||
"""Resolve agents into HandoffAgentExecutors.
|
||||
|
||||
Args:
|
||||
agents: Map of agent IDs or factory names to `AgentProtocol` instances
|
||||
agents: Map of agent IDs or factory names to `SupportsAgentRun` instances
|
||||
handoffs: Map of executor IDs to list of HandoffConfiguration instances
|
||||
|
||||
Returns:
|
||||
@@ -1213,9 +1213,9 @@ class HandoffBuilder:
|
||||
|
||||
return executors
|
||||
|
||||
def _resolve_to_id(self, candidate: str | AgentProtocol) -> str:
|
||||
def _resolve_to_id(self, candidate: str | SupportsAgentRun) -> str:
|
||||
"""Resolve a participant reference into a concrete executor identifier."""
|
||||
if isinstance(candidate, AgentProtocol):
|
||||
if isinstance(candidate, SupportsAgentRun):
|
||||
return resolve_agent_id(candidate)
|
||||
if isinstance(candidate, str):
|
||||
return candidate
|
||||
|
||||
@@ -13,9 +13,9 @@ from enum import Enum
|
||||
from typing import Any, ClassVar, TypeVar, cast, overload
|
||||
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentResponse,
|
||||
ChatMessage,
|
||||
SupportsAgentRun,
|
||||
)
|
||||
from agent_framework._workflows._agent_executor import AgentExecutor, AgentExecutorRequest, AgentExecutorResponse
|
||||
from agent_framework._workflows._checkpoint import CheckpointStorage
|
||||
@@ -521,7 +521,7 @@ class StandardMagenticManager(MagenticManagerBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent: AgentProtocol,
|
||||
agent: SupportsAgentRun,
|
||||
task_ledger: _MagenticTaskLedger | None = None,
|
||||
*,
|
||||
task_ledger_facts_prompt: str | None = None,
|
||||
@@ -562,7 +562,7 @@ class StandardMagenticManager(MagenticManagerBase):
|
||||
max_round_count=max_round_count,
|
||||
)
|
||||
|
||||
self._agent: AgentProtocol = agent
|
||||
self._agent: SupportsAgentRun = agent
|
||||
self.task_ledger: _MagenticTaskLedger | None = task_ledger
|
||||
|
||||
# Prompts may be overridden if needed
|
||||
@@ -1311,10 +1311,10 @@ class MagenticOrchestrator(BaseGroupChatOrchestrator):
|
||||
class MagenticAgentExecutor(AgentExecutor):
|
||||
"""Specialized AgentExecutor for Magentic agent participants."""
|
||||
|
||||
def __init__(self, agent: AgentProtocol) -> None:
|
||||
def __init__(self, agent: SupportsAgentRun) -> None:
|
||||
"""Initialize a Magentic Agent Executor.
|
||||
|
||||
This executor wraps an AgentProtocol instance to be used as a participant
|
||||
This executor wraps an SupportsAgentRun instance to be used as a participant
|
||||
in a Magentic One workflow.
|
||||
|
||||
Args:
|
||||
@@ -1377,13 +1377,13 @@ class MagenticBuilder:
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the Magentic workflow builder."""
|
||||
self._participants: dict[str, AgentProtocol | Executor] = {}
|
||||
self._participant_factories: list[Callable[[], AgentProtocol | Executor]] = []
|
||||
self._participants: dict[str, SupportsAgentRun | Executor] = {}
|
||||
self._participant_factories: list[Callable[[], SupportsAgentRun | Executor]] = []
|
||||
|
||||
# Manager related members
|
||||
self._manager: MagenticManagerBase | None = None
|
||||
self._manager_factory: Callable[[], MagenticManagerBase] | None = None
|
||||
self._manager_agent_factory: Callable[[], AgentProtocol] | None = None
|
||||
self._manager_agent_factory: Callable[[], SupportsAgentRun] | None = None
|
||||
self._standard_manager_options: dict[str, Any] = {}
|
||||
self._enable_plan_review: bool = False
|
||||
|
||||
@@ -1394,12 +1394,12 @@ class MagenticBuilder:
|
||||
|
||||
def register_participants(
|
||||
self,
|
||||
participant_factories: Sequence[Callable[[], AgentProtocol | Executor]],
|
||||
participant_factories: Sequence[Callable[[], SupportsAgentRun | Executor]],
|
||||
) -> "MagenticBuilder":
|
||||
"""Register participant factories for this Magentic workflow.
|
||||
|
||||
Args:
|
||||
participant_factories: Sequence of callables that return AgentProtocol or Executor instances.
|
||||
participant_factories: Sequence of callables that return SupportsAgentRun or Executor instances.
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
@@ -1420,10 +1420,10 @@ class MagenticBuilder:
|
||||
self._participant_factories = list(participant_factories)
|
||||
return self
|
||||
|
||||
def participants(self, participants: Sequence[AgentProtocol | Executor]) -> Self:
|
||||
def participants(self, participants: Sequence[SupportsAgentRun | Executor]) -> Self:
|
||||
"""Define participants for this Magentic workflow.
|
||||
|
||||
Accepts AgentProtocol instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
Accepts SupportsAgentRun instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
|
||||
Args:
|
||||
participants: Sequence of participant definitions
|
||||
@@ -1434,7 +1434,7 @@ class MagenticBuilder:
|
||||
Raises:
|
||||
ValueError: If participants are empty, names are duplicated, or participants
|
||||
or participant factories are already set
|
||||
TypeError: If any participant is not AgentProtocol or Executor instance
|
||||
TypeError: If any participant is not SupportsAgentRun or Executor instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1462,17 +1462,17 @@ class MagenticBuilder:
|
||||
raise ValueError("participants cannot be empty.")
|
||||
|
||||
# Name of the executor mapped to participant instance
|
||||
named: dict[str, AgentProtocol | Executor] = {}
|
||||
named: dict[str, SupportsAgentRun | Executor] = {}
|
||||
for participant in participants:
|
||||
if isinstance(participant, Executor):
|
||||
identifier = participant.id
|
||||
elif isinstance(participant, AgentProtocol):
|
||||
elif isinstance(participant, SupportsAgentRun):
|
||||
if not participant.name:
|
||||
raise ValueError("AgentProtocol participants must have a non-empty name.")
|
||||
raise ValueError("SupportsAgentRun participants must have a non-empty name.")
|
||||
identifier = participant.name
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Participants must be AgentProtocol or Executor instances. Got {type(participant).__name__}."
|
||||
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
|
||||
)
|
||||
|
||||
if identifier in named:
|
||||
@@ -1608,7 +1608,7 @@ class MagenticBuilder:
|
||||
def with_manager(
|
||||
self,
|
||||
*,
|
||||
agent: AgentProtocol,
|
||||
agent: SupportsAgentRun,
|
||||
task_ledger: _MagenticTaskLedger | None = None,
|
||||
# Prompt overrides
|
||||
task_ledger_facts_prompt: str | None = None,
|
||||
@@ -1628,7 +1628,7 @@ class MagenticBuilder:
|
||||
This will create a StandardMagenticManager using the provided agent.
|
||||
|
||||
Args:
|
||||
agent: AgentProtocol instance for the standard magentic manager
|
||||
agent: SupportsAgentRun instance for the standard magentic manager
|
||||
(`StandardMagenticManager`)
|
||||
task_ledger: Optional custom task ledger implementation for specialized
|
||||
prompting or structured output requirements
|
||||
@@ -1661,7 +1661,7 @@ class MagenticBuilder:
|
||||
def with_manager(
|
||||
self,
|
||||
*,
|
||||
agent_factory: Callable[[], AgentProtocol],
|
||||
agent_factory: Callable[[], SupportsAgentRun],
|
||||
task_ledger: _MagenticTaskLedger | None = None,
|
||||
# Prompt overrides
|
||||
task_ledger_facts_prompt: str | None = None,
|
||||
@@ -1681,7 +1681,7 @@ class MagenticBuilder:
|
||||
This will create a StandardMagenticManager using the provided agent factory.
|
||||
|
||||
Args:
|
||||
agent_factory: Callable that returns a new AgentProtocol instance for the standard
|
||||
agent_factory: Callable that returns a new SupportsAgentRun instance for the standard
|
||||
magentic manager (`StandardMagenticManager`)
|
||||
task_ledger: Optional custom task ledger implementation for specialized
|
||||
prompting or structured output requirements
|
||||
@@ -1715,9 +1715,9 @@ class MagenticBuilder:
|
||||
*,
|
||||
manager: MagenticManagerBase | None = None,
|
||||
manager_factory: Callable[[], MagenticManagerBase] | None = None,
|
||||
agent_factory: Callable[[], AgentProtocol] | None = None,
|
||||
agent_factory: Callable[[], SupportsAgentRun] | None = None,
|
||||
# Constructor args for StandardMagenticManager when manager is not provided
|
||||
agent: AgentProtocol | None = None,
|
||||
agent: SupportsAgentRun | None = None,
|
||||
task_ledger: _MagenticTaskLedger | None = None,
|
||||
# Prompt overrides
|
||||
task_ledger_facts_prompt: str | None = None,
|
||||
@@ -1956,7 +1956,7 @@ class MagenticBuilder:
|
||||
raise ValueError("No participants provided. Call .participants() or .register_participants() first.")
|
||||
# We don't need to check if both are set since that is handled in the respective methods
|
||||
|
||||
participants: list[Executor | AgentProtocol] = []
|
||||
participants: list[Executor | SupportsAgentRun] = []
|
||||
if self._participant_factories:
|
||||
for factory in self._participant_factories:
|
||||
participant = factory()
|
||||
@@ -1968,11 +1968,11 @@ class MagenticBuilder:
|
||||
for participant in participants:
|
||||
if isinstance(participant, Executor):
|
||||
executors.append(participant)
|
||||
elif isinstance(participant, AgentProtocol):
|
||||
elif isinstance(participant, SupportsAgentRun):
|
||||
executors.append(MagenticAgentExecutor(participant))
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Participants must be AgentProtocol or Executor instances. Got {type(participant).__name__}."
|
||||
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
|
||||
)
|
||||
|
||||
return executors
|
||||
|
||||
+6
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from agent_framework._agents import AgentProtocol
|
||||
from agent_framework._agents import SupportsAgentRun
|
||||
from agent_framework._types import ChatMessage
|
||||
from agent_framework._workflows._agent_executor import AgentExecutor, AgentExecutorRequest, AgentExecutorResponse
|
||||
from agent_framework._workflows._agent_utils import resolve_agent_id
|
||||
@@ -14,11 +14,11 @@ from agent_framework._workflows._workflow_context import WorkflowContext
|
||||
from agent_framework._workflows._workflow_executor import WorkflowExecutor
|
||||
|
||||
|
||||
def resolve_request_info_filter(agents: list[str | AgentProtocol] | None) -> set[str]:
|
||||
def resolve_request_info_filter(agents: list[str | SupportsAgentRun] | None) -> set[str]:
|
||||
"""Resolve a list of agent/executor references to a set of IDs for filtering.
|
||||
|
||||
Args:
|
||||
agents: List of agent names (str), AgentProtocol instances, or Executor instances.
|
||||
agents: List of agent names (str), SupportsAgentRun instances, or Executor instances.
|
||||
If None, returns None (meaning no filtering - pause for all).
|
||||
|
||||
Returns:
|
||||
@@ -31,7 +31,7 @@ def resolve_request_info_filter(agents: list[str | AgentProtocol] | None) -> set
|
||||
for agent in agents:
|
||||
if isinstance(agent, str):
|
||||
result.add(agent)
|
||||
elif isinstance(agent, AgentProtocol):
|
||||
elif isinstance(agent, SupportsAgentRun):
|
||||
result.add(resolve_agent_id(agent))
|
||||
else:
|
||||
raise TypeError(f"Unsupported type for request_info filter: {type(agent).__name__}")
|
||||
@@ -117,7 +117,7 @@ class AgentApprovalExecutor(WorkflowExecutor):
|
||||
agent's output or send the final response to down stream executors in the orchestration.
|
||||
"""
|
||||
|
||||
def __init__(self, agent: AgentProtocol) -> None:
|
||||
def __init__(self, agent: SupportsAgentRun) -> None:
|
||||
"""Initialize the AgentApprovalExecutor.
|
||||
|
||||
Args:
|
||||
@@ -126,7 +126,7 @@ class AgentApprovalExecutor(WorkflowExecutor):
|
||||
super().__init__(workflow=self._build_workflow(agent), id=resolve_agent_id(agent), propagate_request=True)
|
||||
self._description = agent.description
|
||||
|
||||
def _build_workflow(self, agent: AgentProtocol) -> Workflow:
|
||||
def _build_workflow(self, agent: SupportsAgentRun) -> Workflow:
|
||||
"""Build the internal workflow for the AgentApprovalExecutor."""
|
||||
agent_executor = AgentExecutor(agent)
|
||||
request_info_executor = AgentRequestInfoExecutor(id="agent_request_info_executor")
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
This module provides a high-level, agent-focused API to assemble a sequential
|
||||
workflow where:
|
||||
- Participants can be provided as AgentProtocol or Executor instances via `.participants()`,
|
||||
or as factories returning AgentProtocol or Executor via `.register_participants()`
|
||||
- Participants can be provided as SupportsAgentRun or Executor instances via `.participants()`,
|
||||
or as factories returning SupportsAgentRun or Executor via `.register_participants()`
|
||||
- A shared conversation context (list[ChatMessage]) is passed along the chain
|
||||
- Agents append their assistant messages to the context
|
||||
- Custom executors can transform or summarize and return a refined context
|
||||
@@ -15,7 +15,7 @@ Typical wiring:
|
||||
input -> _InputToConversation -> participant1 -> (agent? -> _ResponseToConversation) -> ... -> participantN -> _EndWithConversation
|
||||
|
||||
Notes:
|
||||
- Participants can mix AgentProtocol and Executor objects
|
||||
- Participants can mix SupportsAgentRun and Executor objects
|
||||
- Agents are auto-wrapped by WorkflowBuilder as AgentExecutor (unless already wrapped)
|
||||
- AgentExecutor produces AgentExecutorResponse; _ResponseToConversation converts this to list[ChatMessage]
|
||||
- Non-agent executors must define a handler that consumes `list[ChatMessage]` and sends back
|
||||
@@ -41,7 +41,7 @@ import logging
|
||||
from collections.abc import Callable, Sequence
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentProtocol, ChatMessage
|
||||
from agent_framework import ChatMessage, SupportsAgentRun
|
||||
from agent_framework._workflows._agent_executor import (
|
||||
AgentExecutor,
|
||||
AgentExecutorResponse,
|
||||
@@ -109,8 +109,8 @@ class _EndWithConversation(Executor):
|
||||
class SequentialBuilder:
|
||||
r"""High-level builder for sequential agent/executor workflows with shared context.
|
||||
|
||||
- `participants([...])` accepts a list of AgentProtocol (recommended) or Executor instances
|
||||
- `register_participants([...])` accepts a list of factories for AgentProtocol (recommended)
|
||||
- `participants([...])` accepts a list of SupportsAgentRun (recommended) or Executor instances
|
||||
- `register_participants([...])` accepts a list of factories for SupportsAgentRun (recommended)
|
||||
or Executor factories
|
||||
- Executors must define a handler that consumes list[ChatMessage] and sends out a list[ChatMessage]
|
||||
- The workflow wires participants in order, passing a list[ChatMessage] down the chain
|
||||
@@ -148,8 +148,8 @@ class SequentialBuilder:
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._participants: list[AgentProtocol | Executor] = []
|
||||
self._participant_factories: list[Callable[[], AgentProtocol | Executor]] = []
|
||||
self._participants: list[SupportsAgentRun | Executor] = []
|
||||
self._participant_factories: list[Callable[[], SupportsAgentRun | Executor]] = []
|
||||
self._checkpoint_storage: CheckpointStorage | None = None
|
||||
self._request_info_enabled: bool = False
|
||||
self._request_info_filter: set[str] | None = None
|
||||
@@ -157,7 +157,7 @@ class SequentialBuilder:
|
||||
|
||||
def register_participants(
|
||||
self,
|
||||
participant_factories: Sequence[Callable[[], AgentProtocol | Executor]],
|
||||
participant_factories: Sequence[Callable[[], SupportsAgentRun | Executor]],
|
||||
) -> "SequentialBuilder":
|
||||
"""Register participant factories for this sequential workflow."""
|
||||
if self._participants:
|
||||
@@ -172,10 +172,10 @@ class SequentialBuilder:
|
||||
self._participant_factories = list(participant_factories)
|
||||
return self
|
||||
|
||||
def participants(self, participants: Sequence[AgentProtocol | Executor]) -> "SequentialBuilder":
|
||||
def participants(self, participants: Sequence[SupportsAgentRun | Executor]) -> "SequentialBuilder":
|
||||
"""Define the ordered participants for this sequential workflow.
|
||||
|
||||
Accepts AgentProtocol instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
Accepts SupportsAgentRun instances (auto-wrapped as AgentExecutor) or Executor instances.
|
||||
Raises if empty or duplicates are provided for clarity.
|
||||
"""
|
||||
if self._participant_factories:
|
||||
@@ -196,7 +196,7 @@ class SequentialBuilder:
|
||||
raise ValueError(f"Duplicate executor participant detected: id '{p.id}'")
|
||||
seen_executor_ids.add(p.id)
|
||||
else:
|
||||
# Treat non-Executor as agent-like (AgentProtocol). Structural checks can be brittle at runtime.
|
||||
# Treat non-Executor as agent-like (SupportsAgentRun). Structural checks can be brittle at runtime.
|
||||
pid = id(p)
|
||||
if pid in seen_agent_ids:
|
||||
raise ValueError("Duplicate agent participant detected (same agent instance provided twice)")
|
||||
@@ -213,7 +213,7 @@ class SequentialBuilder:
|
||||
def with_request_info(
|
||||
self,
|
||||
*,
|
||||
agents: Sequence[str | AgentProtocol] | None = None,
|
||||
agents: Sequence[str | SupportsAgentRun] | None = None,
|
||||
) -> "SequentialBuilder":
|
||||
"""Enable request info after agent participant responses.
|
||||
|
||||
@@ -262,7 +262,7 @@ class SequentialBuilder:
|
||||
raise ValueError("No participants provided. Call .participants() or .register_participants() first.")
|
||||
# We don't need to check if both are set since that is handled in the respective methods
|
||||
|
||||
participants: list[Executor | AgentProtocol] = []
|
||||
participants: list[Executor | SupportsAgentRun] = []
|
||||
if self._participant_factories:
|
||||
# Resolve the participant factories now. This doesn't break the factory pattern
|
||||
# since the Sequential builder still creates new instances per workflow build.
|
||||
@@ -276,7 +276,7 @@ class SequentialBuilder:
|
||||
for p in participants:
|
||||
if isinstance(p, Executor):
|
||||
executors.append(p)
|
||||
elif isinstance(p, AgentProtocol):
|
||||
elif isinstance(p, SupportsAgentRun):
|
||||
if self._request_info_enabled and (
|
||||
not self._request_info_filter or resolve_agent_id(p) in self._request_info_filter
|
||||
):
|
||||
@@ -285,7 +285,7 @@ class SequentialBuilder:
|
||||
else:
|
||||
executors.append(AgentExecutor(p))
|
||||
else:
|
||||
raise TypeError(f"Participants must be AgentProtocol or Executor instances. Got {type(p).__name__}.")
|
||||
raise TypeError(f"Participants must be SupportsAgentRun or Executor instances. Got {type(p).__name__}.")
|
||||
|
||||
return executors
|
||||
|
||||
@@ -312,7 +312,7 @@ class SequentialBuilder:
|
||||
builder.set_start_executor(input_conv)
|
||||
|
||||
# Start of the chain is the input normalizer
|
||||
prior: Executor | AgentProtocol = input_conv
|
||||
prior: Executor | SupportsAgentRun = input_conv
|
||||
for p in participants:
|
||||
builder.add_edge(prior, p)
|
||||
prior = p
|
||||
|
||||
@@ -320,7 +320,7 @@ class TestGroupChatBuilder:
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="AgentProtocol participants must have a non-empty name"):
|
||||
with pytest.raises(ValueError, match="SupportsAgentRun participants must have a non-empty name"):
|
||||
builder.participants([agent])
|
||||
|
||||
def test_empty_participant_name_raises_error(self) -> None:
|
||||
@@ -332,7 +332,7 @@ class TestGroupChatBuilder:
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="AgentProtocol participants must have a non-empty name"):
|
||||
with pytest.raises(ValueError, match="SupportsAgentRun participants must have a non-empty name"):
|
||||
builder.participants([agent])
|
||||
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ def test_handoff_builder_rejects_mixed_types_in_add_handoff_source():
|
||||
triage = MockHandoffAgent(name="triage")
|
||||
specialist = MockHandoffAgent(name="specialist")
|
||||
|
||||
with pytest.raises(TypeError, match="Cannot mix factory names \\(str\\) and AgentProtocol.*instances"):
|
||||
with pytest.raises(TypeError, match="Cannot mix factory names \\(str\\) and SupportsAgentRun.*instances"):
|
||||
(
|
||||
HandoffBuilder(participants=[triage, specialist])
|
||||
.with_start_agent(triage)
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Any, ClassVar, cast
|
||||
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
@@ -15,6 +14,7 @@ from agent_framework import (
|
||||
ChatMessage,
|
||||
Content,
|
||||
Executor,
|
||||
SupportsAgentRun,
|
||||
Workflow,
|
||||
WorkflowCheckpoint,
|
||||
WorkflowCheckpointException,
|
||||
@@ -576,7 +576,7 @@ class StubAssistantsAgent(BaseAgent):
|
||||
)
|
||||
|
||||
|
||||
async def _collect_agent_responses_setup(participant: AgentProtocol) -> list[ChatMessage]:
|
||||
async def _collect_agent_responses_setup(participant: SupportsAgentRun) -> list[ChatMessage]:
|
||||
captured: list[ChatMessage] = []
|
||||
|
||||
wf = (
|
||||
@@ -1121,10 +1121,10 @@ async def test_magentic_with_agent_factory():
|
||||
"""Test workflow creation using agent_factory for StandardMagenticManager."""
|
||||
factory_call_count = 0
|
||||
|
||||
def agent_factory() -> AgentProtocol:
|
||||
def agent_factory() -> SupportsAgentRun:
|
||||
nonlocal factory_call_count
|
||||
factory_call_count += 1
|
||||
return cast(AgentProtocol, StubManagerAgent())
|
||||
return cast(SupportsAgentRun, StubManagerAgent())
|
||||
|
||||
participant = StubAgent("agentA", "reply from agentA")
|
||||
workflow = (
|
||||
@@ -1239,10 +1239,10 @@ def test_magentic_agent_factory_with_standard_manager_options():
|
||||
"""Test that agent_factory properly passes through standard manager options."""
|
||||
factory_call_count = 0
|
||||
|
||||
def agent_factory() -> AgentProtocol:
|
||||
def agent_factory() -> SupportsAgentRun:
|
||||
nonlocal factory_call_count
|
||||
factory_call_count += 1
|
||||
return cast(AgentProtocol, StubManagerAgent())
|
||||
return cast(SupportsAgentRun, StubManagerAgent())
|
||||
|
||||
# Custom options to verify they are passed through
|
||||
custom_max_stall_count = 5
|
||||
|
||||
@@ -8,11 +8,11 @@ from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from agent_framework import (
|
||||
AgentProtocol,
|
||||
AgentResponse,
|
||||
AgentResponseUpdate,
|
||||
AgentThread,
|
||||
ChatMessage,
|
||||
SupportsAgentRun,
|
||||
)
|
||||
from agent_framework._workflows._agent_executor import AgentExecutorRequest, AgentExecutorResponse
|
||||
from agent_framework._workflows._workflow_context import WorkflowContext
|
||||
@@ -44,10 +44,10 @@ class TestResolveRequestInfoFilter:
|
||||
assert result == {"agent1", "agent2"}
|
||||
|
||||
def test_resolves_agent_display_names(self):
|
||||
"""Test resolving AgentProtocol instances by name attribute."""
|
||||
agent1 = MagicMock(spec=AgentProtocol)
|
||||
"""Test resolving SupportsAgentRun instances by name attribute."""
|
||||
agent1 = MagicMock(spec=SupportsAgentRun)
|
||||
agent1.name = "writer"
|
||||
agent2 = MagicMock(spec=AgentProtocol)
|
||||
agent2 = MagicMock(spec=SupportsAgentRun)
|
||||
agent2.name = "reviewer"
|
||||
|
||||
result = resolve_request_info_filter([agent1, agent2])
|
||||
@@ -55,7 +55,7 @@ class TestResolveRequestInfoFilter:
|
||||
|
||||
def test_mixed_types(self):
|
||||
"""Test resolving a mix of strings and agents."""
|
||||
agent = MagicMock(spec=AgentProtocol)
|
||||
agent = MagicMock(spec=SupportsAgentRun)
|
||||
agent.name = "writer"
|
||||
|
||||
result = resolve_request_info_filter(["manual_name", agent])
|
||||
|
||||
Reference in New Issue
Block a user