|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668 |
- import logging
- import tempfile
- from collections.abc import AsyncGenerator
-
- import pytest
- import pytest_asyncio
- from autogen_agentchat import EVENT_LOGGER_NAME
- from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent
- from autogen_agentchat.base import TaskResult
- from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
- from autogen_agentchat.messages import (
- BaseAgentEvent,
- BaseChatMessage,
- TextMessage,
- )
- from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat, Swarm
- from autogen_core import AgentRuntime, SingleThreadedAgentRuntime
- from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor
- from autogen_ext.models.replay import ReplayChatCompletionClient
-
- # Import test utilities from the main test file
- from utils import FileLogHandler
-
- logger = logging.getLogger(EVENT_LOGGER_NAME)
- logger.setLevel(logging.DEBUG)
- logger.addHandler(FileLogHandler("test_group_chat_nested.log"))
-
-
- @pytest_asyncio.fixture(params=["single_threaded", "embedded"]) # type: ignore
- async def runtime(request: pytest.FixtureRequest) -> AsyncGenerator[AgentRuntime | None, None]:
- if request.param == "single_threaded":
- runtime = SingleThreadedAgentRuntime()
- runtime.start()
- yield runtime
- await runtime.stop()
- elif request.param == "embedded":
- yield None
-
-
- @pytest.mark.asyncio
- async def test_round_robin_group_chat_nested_teams_run(runtime: AgentRuntime | None) -> None:
- """Test RoundRobinGroupChat with nested teams using run method."""
- model_client = ReplayChatCompletionClient(
- [
- 'Here is the program\n ```python\nprint("Hello, world!")\n```',
- "TERMINATE",
- "Good job",
- "TERMINATE",
- ],
- )
- with tempfile.TemporaryDirectory() as temp_dir:
- code_executor = LocalCommandLineCodeExecutor(work_dir=temp_dir)
- assistant = AssistantAgent(
- "assistant",
- model_client=model_client,
- description="An assistant agent that writes code.",
- )
- code_executor_agent = CodeExecutorAgent("code_executor", code_executor=code_executor)
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team (assistant + code executor)
- inner_team = RoundRobinGroupChat(
- participants=[assistant, code_executor_agent],
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Create reviewer agent
- reviewer = AssistantAgent(
- "reviewer",
- model_client=model_client,
- description="A reviewer agent that reviews code.",
- )
-
- # Create outer team with nested inner team
- outer_team = RoundRobinGroupChat(
- participants=[inner_team, reviewer],
- termination_condition=termination,
- runtime=runtime,
- )
-
- result = await outer_team.run(task="Write a program that prints 'Hello, world!'")
-
- # Should have task message + inner team result + reviewer response + termination
- assert len(result.messages) >= 4
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Write a program that prints 'Hello, world!'"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
-
- @pytest.mark.asyncio
- async def test_round_robin_group_chat_nested_teams_run_stream(runtime: AgentRuntime | None) -> None:
- """Test RoundRobinGroupChat with nested teams using run_stream method."""
- model_client = ReplayChatCompletionClient(
- [
- 'Here is the program\n ```python\nprint("Hello, world!")\n```',
- "TERMINATE",
- "Good job",
- "TERMINATE",
- ],
- )
- with tempfile.TemporaryDirectory() as temp_dir:
- code_executor = LocalCommandLineCodeExecutor(work_dir=temp_dir)
- assistant = AssistantAgent(
- "assistant",
- model_client=model_client,
- description="An assistant agent that writes code.",
- )
- code_executor_agent = CodeExecutorAgent("code_executor", code_executor=code_executor)
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team (assistant + code executor)
- inner_team = RoundRobinGroupChat(
- participants=[assistant, code_executor_agent],
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Create reviewer agent
- reviewer = AssistantAgent(
- "reviewer",
- model_client=model_client,
- description="A reviewer agent that reviews code.",
- )
-
- # Create outer team with nested inner team
- outer_team = RoundRobinGroupChat(
- participants=[inner_team, reviewer],
- termination_condition=termination,
- runtime=runtime,
- )
-
- messages: list[BaseAgentEvent | BaseChatMessage] = []
- result = None
- async for message in outer_team.run_stream(task="Write a program that prints 'Hello, world!'"):
- if isinstance(message, TaskResult):
- result = message
- else:
- messages.append(message)
-
- assert result is not None
- assert len(result.messages) >= 4
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Write a program that prints 'Hello, world!'"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
-
- @pytest.mark.asyncio
- async def test_round_robin_group_chat_nested_teams_dump_load_component(runtime: AgentRuntime | None) -> None:
- """Test RoundRobinGroupChat with nested teams dump_component and load_component."""
- model_client = ReplayChatCompletionClient(["Hello from agent1", "Hello from agent2", "Hello from agent3"])
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- termination = MaxMessageTermination(2)
-
- # Create inner team
- inner_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- description="Inner team description",
- )
-
- # Create outer team with nested inner team
- outer_team = RoundRobinGroupChat(
- participants=[inner_team, agent3],
- termination_condition=termination,
- runtime=runtime,
- name="OuterTeam",
- description="Outer team description",
- )
-
- # Test dump_component
- config = outer_team.dump_component()
- assert config.config["name"] == "OuterTeam"
- assert config.config["description"] == "Outer team description"
- assert len(config.config["participants"]) == 2
-
- # First participant should be the inner team
- inner_team_config = config.config["participants"][0]["config"]
- assert inner_team_config["name"] == "InnerTeam"
- assert inner_team_config["description"] == "Inner team description"
- assert len(inner_team_config["participants"]) == 2
-
- # Second participant should be agent3
- agent3_config = config.config["participants"][1]["config"]
- assert agent3_config["name"] == "agent3"
-
- # Test load_component
- loaded_team = RoundRobinGroupChat.load_component(config)
- assert loaded_team.name == "OuterTeam"
- assert loaded_team.description == "Outer team description"
- assert len(loaded_team._participants) == 2 # type: ignore[reportPrivateUsage]
-
- # Verify the loaded team has the same structure
- loaded_config = loaded_team.dump_component()
- assert loaded_config == config
-
-
- @pytest.mark.asyncio
- async def test_round_robin_group_chat_nested_teams_save_load_state(runtime: AgentRuntime | None) -> None:
- """Test RoundRobinGroupChat with nested teams save_state and load_state."""
- model_client = ReplayChatCompletionClient(["Hello from agent1", "Hello from agent2", "TERMINATE"])
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- termination = TextMentionTermination("TERMINATE") # Use TextMentionTermination
-
- # Create inner team
- inner_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Create outer team with nested inner team
- outer_team1 = RoundRobinGroupChat(
- participants=[inner_team, agent3],
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Run the team to generate state
- await outer_team1.run(task="Test message")
-
- # Save state
- state = await outer_team1.save_state()
-
- # Create new agents and teams
- agent4 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent5 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent6 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
-
- inner_team2 = RoundRobinGroupChat(
- participants=[agent4, agent5],
- termination_condition=termination,
- runtime=runtime,
- )
-
- outer_team2 = RoundRobinGroupChat(
- participants=[inner_team2, agent6],
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Load state
- await outer_team2.load_state(state)
-
- # Verify state was loaded correctly
- state2 = await outer_team2.save_state()
- assert state == state2
-
-
- @pytest.mark.asyncio
- async def test_selector_group_chat_nested_teams_run(runtime: AgentRuntime | None) -> None:
- """Test SelectorGroupChat with nested teams using run method."""
- model_client = ReplayChatCompletionClient(
- [
- "InnerTeam", # Select inner team first
- 'Here is the program\n ```python\nprint("Hello, world!")\n```',
- "TERMINATE",
- "agent3", # Select agent3 (reviewer)
- "Good job",
- "TERMINATE",
- ],
- )
- with tempfile.TemporaryDirectory() as temp_dir:
- code_executor = LocalCommandLineCodeExecutor(work_dir=temp_dir)
- assistant = AssistantAgent(
- "assistant",
- model_client=model_client,
- description="An assistant agent that writes code.",
- )
- code_executor_agent = CodeExecutorAgent("code_executor", code_executor=code_executor)
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team (assistant + code executor)
- inner_team = RoundRobinGroupChat(
- participants=[assistant, code_executor_agent],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- description="Team that writes and executes code",
- )
-
- # Create reviewer agent
- reviewer = AssistantAgent(
- "agent3",
- model_client=model_client,
- description="A reviewer agent that reviews code.",
- )
-
- # Create outer team with nested inner team
- outer_team = SelectorGroupChat(
- participants=[inner_team, reviewer],
- model_client=model_client,
- termination_condition=termination,
- runtime=runtime,
- )
-
- result = await outer_team.run(task="Write a program that prints 'Hello, world!'")
-
- # Should have task message + selector events + inner team result + reviewer response
- assert len(result.messages) >= 4
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Write a program that prints 'Hello, world!'"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
-
- @pytest.mark.asyncio
- async def test_selector_group_chat_nested_teams_run_stream(runtime: AgentRuntime | None) -> None:
- """Test SelectorGroupChat with nested teams using run_stream method."""
- model_client = ReplayChatCompletionClient(
- [
- "InnerTeam", # Select inner team first
- 'Here is the program\n ```python\nprint("Hello, world!")\n```',
- "TERMINATE",
- "agent3", # Select agent3 (reviewer)
- "Good job",
- "TERMINATE",
- ],
- )
- with tempfile.TemporaryDirectory() as temp_dir:
- code_executor = LocalCommandLineCodeExecutor(work_dir=temp_dir)
- assistant = AssistantAgent(
- "assistant",
- model_client=model_client,
- description="An assistant agent that writes code.",
- )
- code_executor_agent = CodeExecutorAgent("code_executor", code_executor=code_executor)
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team (assistant + code executor)
- inner_team = RoundRobinGroupChat(
- participants=[assistant, code_executor_agent],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- description="Team that writes and executes code",
- )
-
- # Create reviewer agent
- reviewer = AssistantAgent(
- "agent3",
- model_client=model_client,
- description="A reviewer agent that reviews code.",
- )
-
- # Create outer team with nested inner team
- outer_team = SelectorGroupChat(
- participants=[inner_team, reviewer],
- model_client=model_client,
- termination_condition=termination,
- runtime=runtime,
- )
-
- messages: list[BaseAgentEvent | BaseChatMessage] = []
- result = None
- async for message in outer_team.run_stream(task="Write a program that prints 'Hello, world!'"):
- if isinstance(message, TaskResult):
- result = message
- else:
- messages.append(message)
-
- assert result is not None
- assert len(result.messages) >= 4
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Write a program that prints 'Hello, world!'"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
-
- @pytest.mark.asyncio
- async def test_selector_group_chat_nested_teams_dump_load_component(runtime: AgentRuntime | None) -> None:
- """Test SelectorGroupChat with nested teams dump_component and load_component."""
- model_client = ReplayChatCompletionClient(["agent1", "Hello from agent1", "agent3", "Hello from agent3"])
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- termination = MaxMessageTermination(2)
-
- # Create inner team
- inner_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- description="Inner team description",
- )
-
- # Create outer team with nested inner team
- outer_team = SelectorGroupChat(
- participants=[inner_team, agent3],
- model_client=model_client,
- termination_condition=termination,
- runtime=runtime,
- name="OuterTeam",
- description="Outer team description",
- )
-
- # Test dump_component
- config = outer_team.dump_component()
- assert config.config["name"] == "OuterTeam"
- assert config.config["description"] == "Outer team description"
- assert len(config.config["participants"]) == 2
-
- # First participant should be the inner team
- inner_team_config = config.config["participants"][0]["config"]
- assert inner_team_config["name"] == "InnerTeam"
- assert inner_team_config["description"] == "Inner team description"
- assert len(inner_team_config["participants"]) == 2
-
- # Second participant should be agent3
- agent3_config = config.config["participants"][1]["config"]
- assert agent3_config["name"] == "agent3"
-
- # Test load_component
- loaded_team = SelectorGroupChat.load_component(config)
- assert loaded_team.name == "OuterTeam"
- assert loaded_team.description == "Outer team description"
- assert len(loaded_team._participants) == 2 # type: ignore[reportPrivateUsage]
-
- # Verify the loaded team has the same structure
- loaded_config = loaded_team.dump_component()
- assert loaded_config == config
-
-
- @pytest.mark.asyncio
- async def test_selector_group_chat_nested_teams_save_load_state(runtime: AgentRuntime | None) -> None:
- """Test SelectorGroupChat with nested teams save_state and load_state."""
- model_client = ReplayChatCompletionClient(["InnerTeam", "Hello from inner team", "agent3", "TERMINATE"])
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team
- inner_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- )
-
- # Create outer team with nested inner team
- outer_team1 = SelectorGroupChat(
- participants=[inner_team, agent3],
- model_client=model_client,
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Run the team to generate state
- await outer_team1.run(task="Test message")
-
- # Save state
- state = await outer_team1.save_state()
-
- # Create new agents and teams
- agent4 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent5 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent6 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
-
- inner_team2 = RoundRobinGroupChat(
- participants=[agent4, agent5],
- termination_condition=termination,
- runtime=runtime,
- name="InnerTeam",
- )
-
- outer_team2 = SelectorGroupChat(
- participants=[inner_team2, agent6],
- model_client=model_client,
- termination_condition=termination,
- runtime=runtime,
- )
-
- # Load state
- await outer_team2.load_state(state)
-
- # Verify state was loaded correctly
- state2 = await outer_team2.save_state()
- assert state == state2
-
-
- @pytest.mark.asyncio
- async def test_swarm_doesnt_support_nested_teams() -> None:
- """Test that Swarm raises TypeError when provided with nested teams."""
- model_client = ReplayChatCompletionClient(["Hello", "TERMINATE"])
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- termination = TextMentionTermination("TERMINATE")
-
- # Create inner team
- inner_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=termination,
- )
-
- # Verify that Swarm raises TypeError when trying to use a team as participant
- with pytest.raises(TypeError, match="Participant .* must be a ChatAgent"):
- Swarm(
- participants=[inner_team, agent3], # type: ignore
- termination_condition=termination,
- )
-
-
- @pytest.mark.asyncio
- async def test_round_robin_deeply_nested_teams(runtime: AgentRuntime | None) -> None:
- """Test RoundRobinGroupChat with deeply nested teams (3 levels)."""
- model_client = ReplayChatCompletionClient(
- [
- "Hello from agent1",
- "TERMINATE from agent2",
- "World from agent3",
- "Hello from agent1",
- "Hello from agent2",
- "TERMINATE from agent1",
- "TERMINATE from agent3",
- "Review from agent4",
- "TERMINATE from agent2",
- "TERMINATE from agent3",
- "TERMINATE from agent4",
- ]
- )
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client, description="Third agent")
- agent4 = AssistantAgent("agent4", model_client=model_client, description="Fourth agent")
-
- # Create innermost team (level 1)
- innermost_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent1", "agent2"]),
- runtime=runtime,
- name="InnermostTeam",
- )
-
- # Create middle team (level 2)
- middle_team = RoundRobinGroupChat(
- participants=[innermost_team, agent3],
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent3"]),
- runtime=runtime,
- name="MiddleTeam",
- )
-
- # Create outermost team (level 3)
- outermost_team = RoundRobinGroupChat(
- participants=[middle_team, agent4],
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent4"]),
- runtime=runtime,
- name="OutermostTeam",
- )
-
- result: TaskResult | None = None
- async for msg in outermost_team.run_stream(task="Test deep nesting"):
- if isinstance(msg, TaskResult):
- result = msg
- assert result is not None
- # Should have task message + responses from each level
- assert len(result.messages) == 12
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Test deep nesting"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
- # Test component serialization of deeply nested structure
- config = outermost_team.dump_component()
- loaded_team = RoundRobinGroupChat.load_component(config)
- assert loaded_team.name == "OutermostTeam"
-
- # Verify nested structure is preserved
- loaded_config = loaded_team.dump_component()
- assert loaded_config == config
-
-
- @pytest.mark.asyncio
- async def test_selector_deeply_nested_teams(runtime: AgentRuntime | None) -> None:
- """Test SelectorGroupChat with deeply nested teams (3 levels)."""
- model_client_inner = ReplayChatCompletionClient(
- [
- "Hello from innermost agent 1",
- "Hello from innermost agent 2",
- "TERMINATE from innermost agent 1",
- ]
- )
- model_client_middle = ReplayChatCompletionClient(
- [
- "InnermostTeam", # Select innermost team
- "TERMINATE from agent3",
- ]
- )
- model_client_outter = ReplayChatCompletionClient(
- [
- "MiddleTeam", # Select middle team
- "agent4", # Select agent4
- "Hello from outermost agent 4",
- "agent4", # Select agent4 again
- "TERMINATE from agent4",
- ]
- )
-
- # Create agents
- agent1 = AssistantAgent("agent1", model_client=model_client_inner, description="First agent")
- agent2 = AssistantAgent("agent2", model_client=model_client_inner, description="Second agent")
- agent3 = AssistantAgent("agent3", model_client=model_client_middle, description="Third agent")
- agent4 = AssistantAgent("agent4", model_client=model_client_outter, description="Fourth agent")
-
- # Create innermost team (level 1) - RoundRobin for simplicity
- innermost_team = RoundRobinGroupChat(
- participants=[agent1, agent2],
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent1", "agent2"]),
- runtime=runtime,
- name="InnermostTeam",
- )
-
- # Create middle team (level 2) - Selector
- middle_team = SelectorGroupChat(
- participants=[innermost_team, agent3],
- model_client=model_client_middle,
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent3"]),
- runtime=runtime,
- name="MiddleTeam",
- )
-
- # Create outermost team (level 3) - Selector
- outermost_team = SelectorGroupChat(
- participants=[middle_team, agent4],
- model_client=model_client_outter,
- termination_condition=TextMentionTermination("TERMINATE", sources=["agent4"]),
- runtime=runtime,
- name="OutermostTeam",
- allow_repeated_speaker=True,
- )
-
- result: TaskResult | None = None
- async for msg in outermost_team.run_stream(task="Test deep nesting"):
- if isinstance(msg, TaskResult):
- result = msg
- assert result is not None
-
- # Should have task message + selector events + responses from each level
- assert len(result.messages) == 7
- assert isinstance(result.messages[0], TextMessage)
- assert result.messages[0].content == "Test deep nesting"
- assert result.stop_reason is not None and "TERMINATE" in result.stop_reason
-
- # Test component serialization of deeply nested structure
- config = outermost_team.dump_component()
- loaded_team = SelectorGroupChat.load_component(config)
- assert loaded_team.name == "OutermostTeam"
-
- # Verify nested structure is preserved
- loaded_config = loaded_team.dump_component()
- assert loaded_config == config
|