Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from typing_extensions import Never

from ._feature_usage import FeatureIndex
from ._orchestration_request_info import AgentApprovalExecutor
from ._orchestration_request_info import AgentApprovalExecutor, validate_request_info_filter
from ._participant_output_config import (
UNSET,
_coalesce_output_from, # pyright: ignore[reportPrivateUsage]
Expand Down Expand Up @@ -382,6 +382,11 @@ def _resolve_participants(self) -> list[Executor]:
else:
raise TypeError(f"Participants must be SupportsAgentRun or Executor instances. Got {type(p).__name__}.")

if self._request_info_enabled:
validate_request_info_filter(
self._request_info_filter,
[resolve_agent_id(p) for p in participants if not isinstance(p, Executor)],
)
return executors

def build(self) -> Workflow:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
TerminationCondition,
)
from ._feature_usage import FeatureIndex
from ._orchestration_request_info import AgentApprovalExecutor
from ._orchestration_request_info import AgentApprovalExecutor, validate_request_info_filter
from ._orchestrator_helpers import clean_conversation_for_handoff, extract_markdown_fence_bodies
from ._participant_output_config import (
UNSET,
Expand Down Expand Up @@ -1030,6 +1030,11 @@ def _resolve_participants(self) -> list[Executor]:
f"Participants must be SupportsAgentRun or Executor instances. Got {type(participant).__name__}."
)

if self._request_info_enabled:
validate_request_info_filter(
self._request_info_filter,
[resolve_agent_id(p) for p in participants if not isinstance(p, Executor)],
)
return executors

def build(self) -> Workflow:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ def resolve_request_info_filter(agents: list[str | SupportsAgentRun] | None) ->
return result


def validate_request_info_filter(request_info_filter: set[str] | None, agent_ids: list[str]) -> None:
"""Raise if the request info filter names an agent that is not a participant.

Args:
request_info_filter: Agent IDs passed to ``with_request_info(agents=...)``. Empty or None means all agents.
agent_ids: IDs of the agent participants in the orchestration.

Raises:
ValueError: If a name in the filter does not match any agent participant.
"""
unknown = sorted((request_info_filter or set()) - set(agent_ids))
if unknown:
raise ValueError(
f"with_request_info() names agents that are not agent participants: "
f"{', '.join(repr(name) for name in unknown)}. Agent participants: {', '.join(agent_ids)}."
)


@dataclass
class AgentRequestInfoResponse:
"""Response containing additional information requested from users for agents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from agent_framework._workflows._workflow_context import WorkflowContext

from ._feature_usage import FeatureIndex
from ._orchestration_request_info import AgentApprovalExecutor
from ._orchestration_request_info import AgentApprovalExecutor, validate_request_info_filter
from ._participant_output_config import (
UNSET,
_coalesce_output_from, # pyright: ignore[reportPrivateUsage]
Expand Down Expand Up @@ -230,6 +230,11 @@ def _resolve_participants(self) -> list[Executor]:
else:
raise TypeError(f"Participants must be SupportsAgentRun or Executor instances. Got {type(p).__name__}.")

if self._request_info_enabled:
validate_request_info_filter(
self._request_info_filter,
[resolve_agent_id(p) for p in participants if not isinstance(p, Executor)],
)
return executors

def build(self) -> Workflow:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,33 @@ def test_propagate_request_enabled(self):
executor = AgentApprovalExecutor(cast(SupportsAgentRun, agent))

assert executor._propagate_request is True # type: ignore


def _build_with_request_info(builder_name: str, agents: list[str]) -> Any:
from agent_framework.orchestrations import ConcurrentBuilder, GroupChatBuilder, SequentialBuilder

participants = [
cast(SupportsAgentRun, _TestAgent(id="drafter_id", name="drafter")),
cast(SupportsAgentRun, _TestAgent(id="publisher_id", name="publisher")),
]
if builder_name == "sequential":
return SequentialBuilder(participants=participants).with_request_info(agents=agents).build()
if builder_name == "concurrent":
return ConcurrentBuilder(participants=participants).with_request_info(agents=agents).build()
return (
GroupChatBuilder(participants=participants, selection_func=lambda state: "drafter")
.with_request_info(agents=agents)
.build()
)


@pytest.mark.parametrize("builder_name", ["sequential", "concurrent", "group_chat"])
class TestRequestInfoFilterValidation:
"""Names passed to with_request_info(agents=...) must match an agent participant."""

def test_unknown_agent_name_raises(self, builder_name: str) -> None:
with pytest.raises(ValueError, match="'Publisher'"):
_build_with_request_info(builder_name, ["Publisher"])

def test_known_agent_name_builds(self, builder_name: str) -> None:
assert _build_with_request_info(builder_name, ["publisher"]) is not None
Loading