mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
[BREAKING] Python: Move single-config fluent methods to constructor parameters (#3693)
* Move single-config fluent methods to constructor parameters * Updates * Adjust magentic and group chat
This commit is contained in:
@@ -178,13 +178,12 @@ async def test_group_chat_builder_basic_flow() -> None:
|
||||
alpha = StubAgent("alpha", "ack from alpha")
|
||||
beta = StubAgent("beta", "ack from beta")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector, orchestrator_name="manager")
|
||||
.participants([alpha, beta])
|
||||
.with_max_rounds(2) # Limit rounds to prevent infinite loop
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[alpha, beta],
|
||||
max_rounds=2, # Limit rounds to prevent infinite loop
|
||||
selection_func=selector,
|
||||
orchestrator_name="manager",
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("coordinate task", stream=True):
|
||||
@@ -205,13 +204,12 @@ async def test_group_chat_as_agent_accepts_conversation() -> None:
|
||||
alpha = StubAgent("alpha", "ack from alpha")
|
||||
beta = StubAgent("beta", "ack from beta")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector, orchestrator_name="manager")
|
||||
.participants([alpha, beta])
|
||||
.with_max_rounds(2) # Limit rounds to prevent infinite loop
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[alpha, beta],
|
||||
max_rounds=2, # Limit rounds to prevent infinite loop
|
||||
selection_func=selector,
|
||||
orchestrator_name="manager",
|
||||
).build()
|
||||
|
||||
agent = workflow.as_agent(name="group-chat-agent")
|
||||
conversation = [
|
||||
@@ -233,64 +231,47 @@ class TestGroupChatBuilder:
|
||||
"""Test that building without a manager raises ValueError."""
|
||||
agent = StubAgent("test", "response")
|
||||
|
||||
builder = GroupChatBuilder().participants([agent])
|
||||
builder = GroupChatBuilder(participants=[agent])
|
||||
|
||||
with pytest.raises(
|
||||
ValueError, match=r"No orchestrator has been configured\. Call with_orchestrator\(\) to set one\."
|
||||
ValueError,
|
||||
match=r"No orchestrator has been configured\.",
|
||||
):
|
||||
builder.build()
|
||||
|
||||
def test_build_without_participants_raises_error(self) -> None:
|
||||
"""Test that building without participants raises ValueError."""
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
"""Test that constructing without participants raises ValueError."""
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=r"No participants provided\. Call \.participants\(\) or \.register_participants\(\) first\.",
|
||||
match=r"Either participants or participant_factories must be provided\.",
|
||||
):
|
||||
builder.build()
|
||||
GroupChatBuilder()
|
||||
|
||||
def test_duplicate_manager_configuration_raises_error(self) -> None:
|
||||
"""Test that configuring multiple managers raises ValueError."""
|
||||
"""Test that configuring multiple orchestrator options raises ValueError."""
|
||||
agent = StubAgent("test", "response")
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=r"A selection function has already been configured\. Call with_orchestrator\(\.\.\.\) once only\.",
|
||||
match=r"Exactly one of",
|
||||
):
|
||||
builder.with_orchestrator(selection_func=selector)
|
||||
GroupChatBuilder(participants=[agent], selection_func=selector, orchestrator_agent=StubManagerAgent())
|
||||
|
||||
def test_empty_participants_raises_error(self) -> None:
|
||||
"""Test that empty participants list raises ValueError."""
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="participants cannot be empty"):
|
||||
builder.participants([])
|
||||
GroupChatBuilder(participants=[])
|
||||
|
||||
def test_duplicate_participant_names_raises_error(self) -> None:
|
||||
"""Test that duplicate participant names raise ValueError."""
|
||||
agent1 = StubAgent("test", "response1")
|
||||
agent2 = StubAgent("test", "response2")
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="Duplicate participant name 'test'"):
|
||||
builder.participants([agent1, agent2])
|
||||
GroupChatBuilder(participants=[agent1, agent2])
|
||||
|
||||
def test_agent_without_name_raises_error(self) -> None:
|
||||
"""Test that agent without name attribute raises ValueError."""
|
||||
@@ -315,25 +296,15 @@ class TestGroupChatBuilder:
|
||||
|
||||
agent = AgentWithoutName()
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="SupportsAgentRun participants must have a non-empty name"):
|
||||
builder.participants([agent])
|
||||
GroupChatBuilder(participants=[agent])
|
||||
|
||||
def test_empty_participant_name_raises_error(self) -> None:
|
||||
"""Test that empty participant name raises ValueError."""
|
||||
agent = StubAgent("", "response") # Agent with empty name
|
||||
|
||||
def selector(state: GroupChatState) -> str:
|
||||
return "agent"
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
|
||||
with pytest.raises(ValueError, match="SupportsAgentRun participants must have a non-empty name"):
|
||||
builder.participants([agent])
|
||||
GroupChatBuilder(participants=[agent])
|
||||
|
||||
|
||||
class TestGroupChatWorkflow:
|
||||
@@ -350,13 +321,11 @@ class TestGroupChatWorkflow:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(2) # Limit to 2 rounds
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[agent],
|
||||
max_rounds=2, # Limit to 2 rounds
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test task", stream=True):
|
||||
@@ -385,13 +354,11 @@ class TestGroupChatWorkflow:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_termination_condition(termination_condition)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[agent],
|
||||
termination_condition=termination_condition,
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test task", stream=True):
|
||||
@@ -413,13 +380,11 @@ class TestGroupChatWorkflow:
|
||||
manager = StubManagerAgent()
|
||||
worker = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(agent=manager)
|
||||
.participants([worker])
|
||||
.with_termination_condition(lambda conv: any(msg.author_name == "agent" for msg in conv))
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[worker],
|
||||
termination_condition=lambda conv: any(msg.author_name == "agent" for msg in conv),
|
||||
orchestrator_agent=manager,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test task", stream=True):
|
||||
@@ -441,7 +406,7 @@ class TestGroupChatWorkflow:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = GroupChatBuilder().with_orchestrator(selection_func=selector).participants([agent]).build()
|
||||
workflow = GroupChatBuilder(participants=[agent], selection_func=selector).build()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Selection function returned unknown participant 'unknown_agent'"):
|
||||
async for _ in workflow.run("test task", stream=True):
|
||||
@@ -460,14 +425,12 @@ class TestCheckpointing:
|
||||
agent = StubAgent("agent", "response")
|
||||
storage = InMemoryCheckpointStorage()
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1)
|
||||
.with_checkpointing(storage)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[agent],
|
||||
max_rounds=1,
|
||||
checkpoint_storage=storage,
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test task", stream=True):
|
||||
@@ -490,13 +453,7 @@ class TestConversationHandling:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(participants=[agent], max_rounds=1, selection_func=selector).build()
|
||||
|
||||
with pytest.raises(ValueError, match="At least one ChatMessage is required to start the group chat workflow."):
|
||||
async for _ in workflow.run([], stream=True):
|
||||
@@ -514,13 +471,7 @@ class TestConversationHandling:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(participants=[agent], max_rounds=1, selection_func=selector).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test string", stream=True):
|
||||
@@ -543,13 +494,7 @@ class TestConversationHandling:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(participants=[agent], max_rounds=1, selection_func=selector).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run(task_message, stream=True):
|
||||
@@ -575,13 +520,7 @@ class TestConversationHandling:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(participants=[agent], max_rounds=1, selection_func=selector).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run(conversation, stream=True):
|
||||
@@ -607,13 +546,11 @@ class TestRoundLimitEnforcement:
|
||||
|
||||
agent = StubAgent("agent", "response")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1) # Very low limit
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[agent],
|
||||
max_rounds=1, # Very low limit
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test", stream=True):
|
||||
@@ -642,13 +579,11 @@ class TestRoundLimitEnforcement:
|
||||
|
||||
agent = StubAgent("agent", "response from agent")
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.participants([agent])
|
||||
.with_max_rounds(1) # Hit limit after first response
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participants=[agent],
|
||||
max_rounds=1, # Hit limit after first response
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[list[ChatMessage]] = []
|
||||
async for event in workflow.run("test", stream=True):
|
||||
@@ -674,13 +609,7 @@ async def test_group_chat_checkpoint_runtime_only() -> None:
|
||||
agent_b = StubAgent("agentB", "Reply from B")
|
||||
selector = make_sequence_selector()
|
||||
|
||||
wf = (
|
||||
GroupChatBuilder()
|
||||
.participants([agent_a, agent_b])
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.with_max_rounds(2)
|
||||
.build()
|
||||
)
|
||||
wf = GroupChatBuilder(participants=[agent_a, agent_b], max_rounds=2, selection_func=selector).build()
|
||||
|
||||
baseline_output: list[ChatMessage] | None = None
|
||||
async for ev in wf.run("runtime checkpoint test", checkpoint_storage=storage, stream=True):
|
||||
@@ -712,14 +641,12 @@ async def test_group_chat_checkpoint_runtime_overrides_buildtime() -> None:
|
||||
agent_b = StubAgent("agentB", "Reply from B")
|
||||
selector = make_sequence_selector()
|
||||
|
||||
wf = (
|
||||
GroupChatBuilder()
|
||||
.participants([agent_a, agent_b])
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.with_max_rounds(2)
|
||||
.with_checkpointing(buildtime_storage)
|
||||
.build()
|
||||
)
|
||||
wf = GroupChatBuilder(
|
||||
participants=[agent_a, agent_b],
|
||||
max_rounds=2,
|
||||
checkpoint_storage=buildtime_storage,
|
||||
selection_func=selector,
|
||||
).build()
|
||||
baseline_output: list[ChatMessage] | None = None
|
||||
async for ev in wf.run("override test", checkpoint_storage=runtime_storage, stream=True):
|
||||
if ev.type == "output":
|
||||
@@ -759,10 +686,12 @@ async def test_group_chat_with_request_info_filtering():
|
||||
return "alpha"
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector, orchestrator_name="manager")
|
||||
.participants([alpha, beta])
|
||||
.with_max_rounds(2)
|
||||
GroupChatBuilder(
|
||||
participants=[alpha, beta],
|
||||
max_rounds=2,
|
||||
selection_func=selector,
|
||||
orchestrator_name="manager",
|
||||
)
|
||||
.with_request_info(agents=["beta"]) # Only pause before beta runs
|
||||
.build()
|
||||
)
|
||||
@@ -811,10 +740,12 @@ async def test_group_chat_with_request_info_no_filter_pauses_all():
|
||||
return "alpha"
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.with_orchestrator(selection_func=selector, orchestrator_name="manager")
|
||||
.participants([alpha])
|
||||
.with_max_rounds(1)
|
||||
GroupChatBuilder(
|
||||
participants=[alpha],
|
||||
max_rounds=1,
|
||||
selection_func=selector,
|
||||
orchestrator_name="manager",
|
||||
)
|
||||
.with_request_info() # No filter - pause for all
|
||||
.build()
|
||||
)
|
||||
@@ -833,12 +764,13 @@ async def test_group_chat_with_request_info_no_filter_pauses_all():
|
||||
|
||||
def test_group_chat_builder_with_request_info_returns_self():
|
||||
"""Test that with_request_info() returns self for method chaining."""
|
||||
builder = GroupChatBuilder()
|
||||
agent = StubAgent("test", "response")
|
||||
builder = GroupChatBuilder(participants=[agent])
|
||||
result = builder.with_request_info()
|
||||
assert result is builder
|
||||
|
||||
# Also test with agents parameter
|
||||
builder2 = GroupChatBuilder()
|
||||
builder2 = GroupChatBuilder(participants=[agent])
|
||||
result2 = builder2.with_request_info(agents=["test"])
|
||||
assert result2 is builder2
|
||||
|
||||
@@ -853,47 +785,41 @@ def test_group_chat_builder_rejects_empty_participant_factories():
|
||||
return list(state.participants.keys())[0]
|
||||
|
||||
with pytest.raises(ValueError, match=r"participant_factories cannot be empty"):
|
||||
GroupChatBuilder().register_participants([])
|
||||
GroupChatBuilder(participant_factories=[])
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match=r"No participants provided\. Call \.participants\(\) or \.register_participants\(\) first\.",
|
||||
match=r"Either participants or participant_factories must be provided\.",
|
||||
):
|
||||
GroupChatBuilder().with_orchestrator(selection_func=selector).build()
|
||||
GroupChatBuilder()
|
||||
|
||||
|
||||
def test_group_chat_builder_rejects_mixing_participants_and_factories():
|
||||
"""Test that mixing .participants() and .register_participants() raises an error."""
|
||||
"""Test that passing both participants and participant_factories to the constructor raises an error."""
|
||||
alpha = StubAgent("alpha", "reply from alpha")
|
||||
|
||||
# Case 1: participants first, then register_participants
|
||||
with pytest.raises(ValueError, match="Cannot mix .participants"):
|
||||
GroupChatBuilder().participants([alpha]).register_participants([lambda: StubAgent("beta", "reply from beta")])
|
||||
|
||||
# Case 2: register_participants first, then participants
|
||||
with pytest.raises(ValueError, match="Cannot mix .participants"):
|
||||
GroupChatBuilder().register_participants([lambda: alpha]).participants([StubAgent("beta", "reply from beta")])
|
||||
|
||||
|
||||
def test_group_chat_builder_rejects_multiple_calls_to_register_participants():
|
||||
"""Test that multiple calls to .register_participants() raises an error."""
|
||||
with pytest.raises(
|
||||
ValueError, match=r"register_participants\(\) has already been called on this builder instance."
|
||||
):
|
||||
(
|
||||
GroupChatBuilder()
|
||||
.register_participants([lambda: StubAgent("alpha", "reply from alpha")])
|
||||
.register_participants([lambda: StubAgent("beta", "reply from beta")])
|
||||
with pytest.raises(ValueError, match="Cannot provide both participants and participant_factories"):
|
||||
GroupChatBuilder(
|
||||
participants=[alpha],
|
||||
participant_factories=[lambda: StubAgent("beta", "reply from beta")],
|
||||
)
|
||||
|
||||
|
||||
def test_group_chat_builder_rejects_multiple_calls_to_participants():
|
||||
"""Test that multiple calls to .participants() raises an error."""
|
||||
with pytest.raises(ValueError, match="participants have already been set"):
|
||||
(
|
||||
GroupChatBuilder()
|
||||
.participants([StubAgent("alpha", "reply from alpha")])
|
||||
.participants([StubAgent("beta", "reply from beta")])
|
||||
def test_group_chat_builder_rejects_both_factories_and_participants():
|
||||
"""Test that passing both participant_factories and participants raises an error."""
|
||||
with pytest.raises(ValueError, match="Cannot provide both participants and participant_factories"):
|
||||
GroupChatBuilder(
|
||||
participant_factories=[lambda: StubAgent("alpha", "reply from alpha")],
|
||||
participants=[StubAgent("beta", "reply from beta")],
|
||||
)
|
||||
|
||||
|
||||
def test_group_chat_builder_rejects_both_participants_and_factories():
|
||||
"""Test that passing both participants and participant_factories raises an error."""
|
||||
with pytest.raises(ValueError, match="Cannot provide both participants and participant_factories"):
|
||||
GroupChatBuilder(
|
||||
participants=[StubAgent("alpha", "reply from alpha")],
|
||||
participant_factories=[lambda: StubAgent("beta", "reply from beta")],
|
||||
)
|
||||
|
||||
|
||||
@@ -913,13 +839,11 @@ async def test_group_chat_with_participant_factories():
|
||||
|
||||
selector = make_sequence_selector()
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.register_participants([create_alpha, create_beta])
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.with_max_rounds(2)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participant_factories=[create_alpha, create_beta],
|
||||
max_rounds=2,
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
# Factories should be called during build
|
||||
assert call_count == 2
|
||||
@@ -948,12 +872,7 @@ async def test_group_chat_participant_factories_reusable_builder():
|
||||
|
||||
selector = make_sequence_selector()
|
||||
|
||||
builder = (
|
||||
GroupChatBuilder()
|
||||
.register_participants([create_alpha, create_beta])
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.with_max_rounds(2)
|
||||
)
|
||||
builder = GroupChatBuilder(participant_factories=[create_alpha, create_beta], max_rounds=2, selection_func=selector)
|
||||
|
||||
# Build first workflow
|
||||
wf1 = builder.build()
|
||||
@@ -980,14 +899,12 @@ async def test_group_chat_participant_factories_with_checkpointing():
|
||||
|
||||
selector = make_sequence_selector()
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.register_participants([create_alpha, create_beta])
|
||||
.with_orchestrator(selection_func=selector)
|
||||
.with_checkpointing(storage)
|
||||
.with_max_rounds(2)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participant_factories=[create_alpha, create_beta],
|
||||
checkpoint_storage=storage,
|
||||
max_rounds=2,
|
||||
selection_func=selector,
|
||||
).build()
|
||||
|
||||
outputs: list[WorkflowEvent] = []
|
||||
async for event in workflow.run("checkpoint test", stream=True):
|
||||
@@ -1014,16 +931,15 @@ def test_group_chat_builder_rejects_multiple_orchestrator_configurations():
|
||||
def agent_factory() -> ChatAgent:
|
||||
return cast(ChatAgent, StubManagerAgent())
|
||||
|
||||
builder = GroupChatBuilder().with_orchestrator(selection_func=selector)
|
||||
agent = StubAgent("test", "response")
|
||||
|
||||
# Already has a selection_func, should fail on second call
|
||||
with pytest.raises(ValueError, match=r"A selection function has already been configured"):
|
||||
builder.with_orchestrator(selection_func=selector)
|
||||
# Both selection_func and orchestrator_agent provided simultaneously - should fail
|
||||
with pytest.raises(ValueError, match=r"Exactly one of"):
|
||||
GroupChatBuilder(participants=[agent], selection_func=selector, orchestrator_agent=StubManagerAgent())
|
||||
|
||||
# Test with agent_factory
|
||||
builder2 = GroupChatBuilder().with_orchestrator(agent=agent_factory)
|
||||
with pytest.raises(ValueError, match=r"A factory has already been configured"):
|
||||
builder2.with_orchestrator(agent=agent_factory)
|
||||
# Test with agent_factory - already has factory, should fail with second config
|
||||
with pytest.raises(ValueError, match=r"Exactly one of"):
|
||||
GroupChatBuilder(participants=[agent], orchestrator_agent=agent_factory, selection_func=selector)
|
||||
|
||||
|
||||
def test_group_chat_builder_requires_exactly_one_orchestrator_option():
|
||||
@@ -1035,13 +951,15 @@ def test_group_chat_builder_requires_exactly_one_orchestrator_option():
|
||||
def agent_factory() -> ChatAgent:
|
||||
return cast(ChatAgent, StubManagerAgent())
|
||||
|
||||
# No options provided
|
||||
with pytest.raises(ValueError, match="Exactly one of"):
|
||||
GroupChatBuilder().with_orchestrator() # type: ignore
|
||||
agent = StubAgent("test", "response")
|
||||
|
||||
# No orchestrator options provided - only fails at build() time
|
||||
with pytest.raises(ValueError, match="No orchestrator has been configured"):
|
||||
GroupChatBuilder(participants=[agent]).build()
|
||||
|
||||
# Multiple options provided
|
||||
with pytest.raises(ValueError, match="Exactly one of"):
|
||||
GroupChatBuilder().with_orchestrator(selection_func=selector, agent=agent_factory) # type: ignore
|
||||
GroupChatBuilder(participants=[agent], selection_func=selector, orchestrator_agent=agent_factory)
|
||||
|
||||
|
||||
async def test_group_chat_with_orchestrator_factory_returning_chat_agent():
|
||||
@@ -1112,7 +1030,7 @@ async def test_group_chat_with_orchestrator_factory_returning_chat_agent():
|
||||
alpha = StubAgent("alpha", "reply from alpha")
|
||||
beta = StubAgent("beta", "reply from beta")
|
||||
|
||||
workflow = GroupChatBuilder().participants([alpha, beta]).with_orchestrator(agent=agent_factory).build()
|
||||
workflow = GroupChatBuilder(participants=[alpha, beta], orchestrator_agent=agent_factory).build()
|
||||
|
||||
# Factory should be called during build
|
||||
assert factory_call_count == 1
|
||||
@@ -1156,7 +1074,7 @@ def test_group_chat_with_orchestrator_factory_returning_base_orchestrator():
|
||||
|
||||
alpha = StubAgent("alpha", "reply from alpha")
|
||||
|
||||
workflow = GroupChatBuilder().participants([alpha]).with_orchestrator(orchestrator=orchestrator_factory).build()
|
||||
workflow = GroupChatBuilder(participants=[alpha], orchestrator=orchestrator_factory).build()
|
||||
|
||||
# Factory should be called during build
|
||||
assert factory_call_count == 1
|
||||
@@ -1176,7 +1094,7 @@ async def test_group_chat_orchestrator_factory_reusable_builder():
|
||||
alpha = StubAgent("alpha", "reply from alpha")
|
||||
beta = StubAgent("beta", "reply from beta")
|
||||
|
||||
builder = GroupChatBuilder().participants([alpha, beta]).with_orchestrator(agent=agent_factory)
|
||||
builder = GroupChatBuilder(participants=[alpha, beta], orchestrator_agent=agent_factory)
|
||||
|
||||
# Build first workflow
|
||||
wf1 = builder.build()
|
||||
@@ -1202,13 +1120,13 @@ def test_group_chat_orchestrator_factory_invalid_return_type():
|
||||
TypeError,
|
||||
match=r"Orchestrator factory must return ChatAgent or BaseGroupChatOrchestrator instance",
|
||||
):
|
||||
(GroupChatBuilder().participants([alpha]).with_orchestrator(orchestrator=invalid_factory).build())
|
||||
GroupChatBuilder(participants=[alpha], orchestrator=invalid_factory).build()
|
||||
|
||||
with pytest.raises(
|
||||
TypeError,
|
||||
match=r"Orchestrator factory must return ChatAgent or BaseGroupChatOrchestrator instance",
|
||||
):
|
||||
(GroupChatBuilder().participants([alpha]).with_orchestrator(agent=invalid_factory).build())
|
||||
GroupChatBuilder(participants=[alpha], orchestrator_agent=invalid_factory).build()
|
||||
|
||||
|
||||
def test_group_chat_with_both_participant_and_orchestrator_factories():
|
||||
@@ -1231,12 +1149,10 @@ def test_group_chat_with_both_participant_and_orchestrator_factories():
|
||||
agent_factory_call_count += 1
|
||||
return cast(ChatAgent, StubManagerAgent())
|
||||
|
||||
workflow = (
|
||||
GroupChatBuilder()
|
||||
.register_participants([create_alpha, create_beta])
|
||||
.with_orchestrator(agent=agent_factory)
|
||||
.build()
|
||||
)
|
||||
workflow = GroupChatBuilder(
|
||||
participant_factories=[create_alpha, create_beta],
|
||||
orchestrator_agent=agent_factory,
|
||||
).build()
|
||||
|
||||
# All factories should be called during build
|
||||
assert participant_factory_call_count == 2
|
||||
@@ -1268,9 +1184,7 @@ async def test_group_chat_factories_reusable_for_multiple_workflows():
|
||||
agent_factory_call_count += 1
|
||||
return cast(ChatAgent, StubManagerAgent())
|
||||
|
||||
builder = (
|
||||
GroupChatBuilder().register_participants([create_alpha, create_beta]).with_orchestrator(agent=agent_factory)
|
||||
)
|
||||
builder = GroupChatBuilder(participant_factories=[create_alpha, create_beta], orchestrator_agent=agent_factory)
|
||||
|
||||
# Build first workflow
|
||||
wf1 = builder.build()
|
||||
|
||||
Reference in New Issue
Block a user