Skip to content

Commit bddb18c

Browse files
authored
Merge branch 'main' into distributed_agent_startup
2 parents b5d33cb + 8963196 commit bddb18c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2559
-278
lines changed

.github/workflows/docs.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
[
3434
# For main use the workflow target
3535
{ ref: "${{github.ref}}", dest-dir: dev, uv-version: "0.5.13", sphinx-release-override: "dev" },
36-
{ ref: "v0.4.2", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
36+
{ ref: "v0.4.3", dest-dir: stable, uv-version: "0.5.13", sphinx-release-override: "stable" },
3737
{ ref: "v0.4.0.dev0", dest-dir: "0.4.0.dev0", uv-version: "0.5.11", sphinx-release-override: "" },
3838
{ ref: "v0.4.0.dev1", dest-dir: "0.4.0.dev1", uv-version: "0.5.11", sphinx-release-override: "" },
3939
{ ref: "v0.4.0.dev2", dest-dir: "0.4.0.dev2", uv-version: "0.5.11", sphinx-release-override: "" },
@@ -51,6 +51,7 @@ jobs:
5151
{ ref: "v0.4.0.post1", dest-dir: "0.4.0", uv-version: "0.5.13", sphinx-release-override: "" },
5252
{ ref: "v0.4.1", dest-dir: "0.4.1", uv-version: "0.5.13", sphinx-release-override: "" },
5353
{ ref: "v0.4.2", dest-dir: "0.4.2", uv-version: "0.5.13", sphinx-release-override: "" },
54+
{ ref: "v0.4.3", dest-dir: "0.4.3", uv-version: "0.5.13", sphinx-release-override: "" },
5455
]
5556
steps:
5657
- name: Checkout

docs/switcher.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[
22
{
3-
"name": "0.4.2 (stable)",
3+
"name": "0.4.3 (stable)",
44
"version": "stable",
55
"url": "/autogen/stable/",
66
"preferred": true

protos/agent_worker.proto

+25-6
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ message Event {
4848
}
4949

5050
message RegisterAgentTypeRequest {
51-
string request_id = 1;
51+
string request_id = 1; // TODO: remove once message based requests are removed
5252
string type = 2;
5353
}
5454

5555
message RegisterAgentTypeResponse {
56-
string request_id = 1;
56+
string request_id = 1; // TODO: remove once message based requests are removed
5757
bool success = 2;
5858
optional string error = 3;
5959
}
@@ -69,27 +69,46 @@ message TypePrefixSubscription {
6969
}
7070

7171
message Subscription {
72+
string id = 1;
7273
oneof subscription {
73-
TypeSubscription typeSubscription = 1;
74-
TypePrefixSubscription typePrefixSubscription = 2;
74+
TypeSubscription typeSubscription = 2;
75+
TypePrefixSubscription typePrefixSubscription = 3;
7576
}
7677
}
7778

7879
message AddSubscriptionRequest {
79-
string request_id = 1;
80+
string request_id = 1; // TODO: remove once message based requests are removed
8081
Subscription subscription = 2;
8182
}
8283

8384
message AddSubscriptionResponse {
84-
string request_id = 1;
85+
string request_id = 1; // TODO: remove once message based requests are removed
8586
bool success = 2;
8687
optional string error = 3;
8788
}
8889

90+
message RemoveSubscriptionRequest {
91+
string id = 1;
92+
}
93+
94+
message RemoveSubscriptionResponse {
95+
bool success = 1;
96+
optional string error = 2;
97+
}
98+
99+
message GetSubscriptionsRequest {}
100+
message GetSubscriptionsResponse {
101+
repeated Subscription subscriptions = 1;
102+
}
103+
89104
service AgentRpc {
90105
rpc OpenChannel (stream Message) returns (stream Message);
91106
rpc GetState(AgentId) returns (GetStateResponse);
92107
rpc SaveState(AgentState) returns (SaveStateResponse);
108+
rpc RegisterAgent(RegisterAgentTypeRequest) returns (RegisterAgentTypeResponse);
109+
rpc AddSubscription(AddSubscriptionRequest) returns (AddSubscriptionResponse);
110+
rpc RemoveSubscription(RemoveSubscriptionRequest) returns (RemoveSubscriptionResponse);
111+
rpc GetSubscriptions(GetSubscriptionsRequest) returns (GetSubscriptionsResponse);
93112
}
94113

95114
message AgentState {

python/packages/autogen-agentchat/src/autogen_agentchat/agents/_society_of_mind_agent.py

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from typing import Any, AsyncGenerator, List, Mapping, Sequence
22

3-
from autogen_core import CancellationToken
3+
from autogen_core import CancellationToken, Component, ComponentModel
44
from autogen_core.models import ChatCompletionClient, LLMMessage, SystemMessage, UserMessage
5+
from pydantic import BaseModel
6+
from typing_extensions import Self
57

68
from autogen_agentchat.base import Response
79
from autogen_agentchat.state import SocietyOfMindAgentState
@@ -16,7 +18,18 @@
1618
from ._base_chat_agent import BaseChatAgent
1719

1820

19-
class SocietyOfMindAgent(BaseChatAgent):
21+
class SocietyOfMindAgentConfig(BaseModel):
22+
"""The declarative configuration for a SocietyOfMindAgent."""
23+
24+
name: str
25+
team: ComponentModel
26+
model_client: ComponentModel
27+
description: str
28+
instruction: str
29+
response_prompt: str
30+
31+
32+
class SocietyOfMindAgent(BaseChatAgent, Component[SocietyOfMindAgentConfig]):
2033
"""An agent that uses an inner team of agents to generate responses.
2134
2235
Each time the agent's :meth:`on_messages` or :meth:`on_messages_stream`
@@ -74,6 +87,9 @@ async def main() -> None:
7487
asyncio.run(main())
7588
"""
7689

90+
component_config_schema = SocietyOfMindAgentConfig
91+
component_provider_override = "autogen_agentchat.agents.SocietyOfMindAgent"
92+
7793
DEFAULT_INSTRUCTION = "Earlier you were asked to fulfill a request. You and your team worked diligently to address that request. Here is a transcript of that conversation:"
7894
"""str: The default instruction to use when generating a response using the
7995
inner team's messages. The instruction will be prepended to the inner team's
@@ -173,3 +189,26 @@ async def save_state(self) -> Mapping[str, Any]:
173189
async def load_state(self, state: Mapping[str, Any]) -> None:
174190
society_of_mind_state = SocietyOfMindAgentState.model_validate(state)
175191
await self._team.load_state(society_of_mind_state.inner_team_state)
192+
193+
def _to_config(self) -> SocietyOfMindAgentConfig:
194+
return SocietyOfMindAgentConfig(
195+
name=self.name,
196+
team=self._team.dump_component(),
197+
model_client=self._model_client.dump_component(),
198+
description=self.description,
199+
instruction=self._instruction,
200+
response_prompt=self._response_prompt,
201+
)
202+
203+
@classmethod
204+
def _from_config(cls, config: SocietyOfMindAgentConfig) -> Self:
205+
model_client = ChatCompletionClient.load_component(config.model_client)
206+
team = Team.load_component(config.team)
207+
return cls(
208+
name=config.name,
209+
team=team,
210+
model_client=model_client,
211+
description=config.description,
212+
instruction=config.instruction,
213+
response_prompt=config.response_prompt,
214+
)

python/packages/autogen-agentchat/src/autogen_agentchat/base/_chat_agent.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from dataclasses import dataclass
33
from typing import Any, AsyncGenerator, Mapping, Sequence
44

5-
from autogen_core import CancellationToken
5+
from autogen_core import CancellationToken, ComponentBase
6+
from pydantic import BaseModel
67

78
from ..messages import AgentEvent, ChatMessage
89
from ._task import TaskRunner
@@ -20,9 +21,11 @@ class Response:
2021
or :class:`ChatMessage`."""
2122

2223

23-
class ChatAgent(ABC, TaskRunner):
24+
class ChatAgent(ABC, TaskRunner, ComponentBase[BaseModel]):
2425
"""Protocol for a chat agent."""
2526

27+
component_type = "agent"
28+
2629
@property
2730
@abstractmethod
2831
def name(self) -> str:
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1-
from typing import Any, Mapping, Protocol
1+
from abc import ABC, abstractmethod
2+
from typing import Any, Mapping
3+
4+
from autogen_core import ComponentBase
5+
from pydantic import BaseModel
26

37
from ._task import TaskRunner
48

59

6-
class Team(TaskRunner, Protocol):
10+
class Team(ABC, TaskRunner, ComponentBase[BaseModel]):
11+
component_type = "team"
12+
13+
@abstractmethod
714
async def reset(self) -> None:
815
"""Reset the team and all its participants to its initial state."""
916
...
1017

18+
@abstractmethod
1119
async def save_state(self) -> Mapping[str, Any]:
1220
"""Save the current state of the team."""
1321
...
1422

23+
@abstractmethod
1524
async def load_state(self, state: Mapping[str, Any]) -> None:
1625
"""Load the state of the team."""
1726
...

python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_base_group_chat.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@
1111
AgentType,
1212
CancellationToken,
1313
ClosureAgent,
14+
ComponentBase,
1415
MessageContext,
1516
SingleThreadedAgentRuntime,
1617
TypeSubscription,
1718
)
1819
from autogen_core._closure_agent import ClosureContext
20+
from pydantic import BaseModel
1921

2022
from ... import EVENT_LOGGER_NAME
2123
from ...base import ChatAgent, TaskResult, Team, TerminationCondition
@@ -28,13 +30,15 @@
2830
event_logger = logging.getLogger(EVENT_LOGGER_NAME)
2931

3032

31-
class BaseGroupChat(Team, ABC):
33+
class BaseGroupChat(Team, ABC, ComponentBase[BaseModel]):
3234
"""The base class for group chat teams.
3335
3436
To implement a group chat team, first create a subclass of :class:`BaseGroupChatManager` and then
3537
create a subclass of :class:`BaseGroupChat` that uses the group chat manager.
3638
"""
3739

40+
component_type = "team"
41+
3842
def __init__(
3943
self,
4044
participants: List[ChatAgent],

python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_group_chat.py

+46-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import logging
22
from typing import Callable, List
33

4+
from autogen_core import Component, ComponentModel
45
from autogen_core.models import ChatCompletionClient
6+
from pydantic import BaseModel
7+
from typing_extensions import Self
58

69
from .... import EVENT_LOGGER_NAME, TRACE_LOGGER_NAME
710
from ....base import ChatAgent, TerminationCondition
@@ -13,7 +16,18 @@
1316
event_logger = logging.getLogger(EVENT_LOGGER_NAME)
1417

1518

16-
class MagenticOneGroupChat(BaseGroupChat):
19+
class MagenticOneGroupChatConfig(BaseModel):
20+
"""The declarative configuration for a MagenticOneGroupChat."""
21+
22+
participants: List[ComponentModel]
23+
model_client: ComponentModel
24+
termination_condition: ComponentModel | None = None
25+
max_turns: int | None = None
26+
max_stalls: int
27+
final_answer_prompt: str
28+
29+
30+
class MagenticOneGroupChat(BaseGroupChat, Component[MagenticOneGroupChatConfig]):
1731
"""A team that runs a group chat with participants managed by the MagenticOneOrchestrator.
1832
1933
The orchestrator handles the conversation flow, ensuring that the task is completed
@@ -73,6 +87,9 @@ async def main() -> None:
7387
}
7488
"""
7589

90+
component_config_schema = MagenticOneGroupChatConfig
91+
component_provider_override = "autogen_agentchat.teams.MagenticOneGroupChat"
92+
7693
def __init__(
7794
self,
7895
participants: List[ChatAgent],
@@ -117,3 +134,31 @@ def _create_group_chat_manager_factory(
117134
self._final_answer_prompt,
118135
termination_condition,
119136
)
137+
138+
def _to_config(self) -> MagenticOneGroupChatConfig:
139+
participants = [participant.dump_component() for participant in self._participants]
140+
termination_condition = self._termination_condition.dump_component() if self._termination_condition else None
141+
return MagenticOneGroupChatConfig(
142+
participants=participants,
143+
model_client=self._model_client.dump_component(),
144+
termination_condition=termination_condition,
145+
max_turns=self._max_turns,
146+
max_stalls=self._max_stalls,
147+
final_answer_prompt=self._final_answer_prompt,
148+
)
149+
150+
@classmethod
151+
def _from_config(cls, config: MagenticOneGroupChatConfig) -> Self:
152+
participants = [ChatAgent.load_component(participant) for participant in config.participants]
153+
model_client = ChatCompletionClient.load_component(config.model_client)
154+
termination_condition = (
155+
TerminationCondition.load_component(config.termination_condition) if config.termination_condition else None
156+
)
157+
return cls(
158+
participants,
159+
model_client,
160+
termination_condition=termination_condition,
161+
max_turns=config.max_turns,
162+
max_stalls=config.max_stalls,
163+
final_answer_prompt=config.final_answer_prompt,
164+
)

python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_magentic_one/_magentic_one_orchestrator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ def _thread_to_context(self) -> List[LLMMessage]:
460460
assert isinstance(m, TextMessage | ToolCallSummaryMessage)
461461
context.append(AssistantMessage(content=m.content, source=m.source))
462462
else:
463-
assert isinstance(m, TextMessage) or isinstance(m, MultiModalMessage)
463+
assert isinstance(m, (TextMessage, MultiModalMessage, ToolCallSummaryMessage))
464464
context.append(UserMessage(content=m.content, source=m.source))
465465
return context
466466

python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_round_robin_group_chat.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
from typing import Any, Callable, List, Mapping
22

3+
from autogen_core import Component, ComponentModel
4+
from pydantic import BaseModel
5+
from typing_extensions import Self
6+
37
from ...base import ChatAgent, TerminationCondition
48
from ...messages import AgentEvent, ChatMessage
59
from ...state import RoundRobinManagerState
@@ -61,7 +65,15 @@ async def select_speaker(self, thread: List[AgentEvent | ChatMessage]) -> str:
6165
return current_speaker
6266

6367

64-
class RoundRobinGroupChat(BaseGroupChat):
68+
class RoundRobinGroupChatConfig(BaseModel):
69+
"""The declarative configuration RoundRobinGroupChat."""
70+
71+
participants: List[ComponentModel]
72+
termination_condition: ComponentModel | None = None
73+
max_turns: int | None = None
74+
75+
76+
class RoundRobinGroupChat(BaseGroupChat, Component[RoundRobinGroupChatConfig]):
6577
"""A team that runs a group chat with participants taking turns in a round-robin fashion
6678
to publish a message to all.
6779
@@ -133,6 +145,9 @@ async def main() -> None:
133145
asyncio.run(main())
134146
"""
135147

148+
component_config_schema = RoundRobinGroupChatConfig
149+
component_provider_override = "autogen_agentchat.teams.RoundRobinGroupChat"
150+
136151
def __init__(
137152
self,
138153
participants: List[ChatAgent],
@@ -166,3 +181,20 @@ def _factory() -> RoundRobinGroupChatManager:
166181
)
167182

168183
return _factory
184+
185+
def _to_config(self) -> RoundRobinGroupChatConfig:
186+
participants = [participant.dump_component() for participant in self._participants]
187+
termination_condition = self._termination_condition.dump_component() if self._termination_condition else None
188+
return RoundRobinGroupChatConfig(
189+
participants=participants,
190+
termination_condition=termination_condition,
191+
max_turns=self._max_turns,
192+
)
193+
194+
@classmethod
195+
def _from_config(cls, config: RoundRobinGroupChatConfig) -> Self:
196+
participants = [ChatAgent.load_component(participant) for participant in config.participants]
197+
termination_condition = (
198+
TerminationCondition.load_component(config.termination_condition) if config.termination_condition else None
199+
)
200+
return cls(participants, termination_condition=termination_condition, max_turns=config.max_turns)

0 commit comments

Comments
 (0)