Skip to content

Commit e05dde0

Browse files
Merge branch 'main' into ags_save_sessions
2 parents baa66fe + df183be commit e05dde0

23 files changed

+364
-65
lines changed

.github/workflows/checks.yml

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ jobs:
190190
run: |
191191
source ${{ github.workspace }}/python/.venv/bin/activate
192192
poe gen-proto
193+
poe gen-test-proto
193194
working-directory: ./python
194195
- name: Check if there are uncommited changes
195196
id: changes

docs/design/04 - Agent and Topic ID Specs.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ This document describes the structure, constraints, and behavior of Agent IDs an
3434

3535
- Type: `string`
3636
- Description: Topic type is usually defined by application code to mark the type of messages the topic is for.
37-
- Constraints: UTF8 and only contain alphanumeric letters (a-z) and (0-9), or underscores (\_). A valid identifier cannot start with a number, or contain any spaces.
37+
- Constraints: UTF8 and only contain alphanumeric letters (a-z) and (0-9), ':', '=', or underscores (\_). A valid identifier cannot start with a number, or contain any spaces.
3838
- Examples:
3939
- `GitHub_Issues`
4040

protos/agent_worker.proto

+6
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,15 @@ message TypeSubscription {
6363
string agent_type = 2;
6464
}
6565

66+
message TypePrefixSubscription {
67+
string topic_type_prefix = 1;
68+
string agent_type = 2;
69+
}
70+
6671
message Subscription {
6772
oneof subscription {
6873
TypeSubscription typeSubscription = 1;
74+
TypePrefixSubscription typePrefixSubscription = 2;
6975
}
7076
}
7177

python/packages/autogen-core/pyproject.toml

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ dev-dependencies = [
8181

8282
[tool.ruff]
8383
extend = "../../pyproject.toml"
84-
exclude = ["build", "dist", "src/autogen_core/application/protos"]
84+
exclude = ["build", "dist", "src/autogen_core/application/protos", "tests/protos"]
8585
include = ["src/**", "samples/*.py", "docs/**/*.ipynb", "tests/**"]
8686

8787
[tool.ruff.lint.per-file-ignores]
@@ -91,7 +91,7 @@ include = ["src/**", "samples/*.py", "docs/**/*.ipynb", "tests/**"]
9191
[tool.pyright]
9292
extends = "../../pyproject.toml"
9393
include = ["src", "tests", "samples"]
94-
exclude = ["src/autogen_core/application/protos"]
94+
exclude = ["src/autogen_core/application/protos", "tests/protos"]
9595
reportDeprecated = false
9696

9797
[tool.pytest.ini_options]
@@ -111,7 +111,7 @@ include = "../../shared_tasks.toml"
111111
test = "pytest -n auto"
112112
mypy.default_item_type = "cmd"
113113
mypy.sequence = [
114-
"mypy --config-file ../../pyproject.toml --exclude src/autogen_core/application/protos src tests",
114+
"mypy --config-file ../../pyproject.toml --exclude src/autogen_core/application/protos --exclude tests/protos src tests",
115115
"nbqa mypy docs/src --config-file ../../pyproject.toml",
116116
]
117117

python/packages/autogen-core/src/autogen_core/application/_single_threaded_agent_runtime.py

+10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import inspect
55
import logging
66
import threading
7+
import uuid
78
import warnings
89
from asyncio import CancelledError, Future, Task
910
from collections.abc import Sequence
@@ -53,6 +54,7 @@ class PublishMessageEnvelope:
5354
sender: AgentId | None
5455
topic_id: TopicId
5556
metadata: EnvelopeMetadata | None = None
57+
message_id: str
5658

5759

5860
@dataclass(kw_only=True)
@@ -256,6 +258,7 @@ async def publish_message(
256258
*,
257259
sender: AgentId | None = None,
258260
cancellation_token: CancellationToken | None = None,
261+
message_id: str | None = None,
259262
) -> None:
260263
with self._tracer_helper.trace_block(
261264
"create",
@@ -268,6 +271,9 @@ async def publish_message(
268271
content = message.__dict__ if hasattr(message, "__dict__") else message
269272
logger.info(f"Publishing message of type {type(message).__name__} to all subscribers: {content}")
270273

274+
if message_id is None:
275+
message_id = str(uuid.uuid4())
276+
271277
# event_logger.info(
272278
# MessageEvent(
273279
# payload=message,
@@ -285,6 +291,7 @@ async def publish_message(
285291
sender=sender,
286292
topic_id=topic_id,
287293
metadata=get_telemetry_envelope_metadata(),
294+
message_id=message_id,
288295
)
289296
)
290297

@@ -327,6 +334,8 @@ async def _process_send(self, message_envelope: SendMessageEnvelope) -> None:
327334
topic_id=None,
328335
is_rpc=True,
329336
cancellation_token=message_envelope.cancellation_token,
337+
# Will be fixed when send API removed
338+
message_id="NOT_DEFINED_TODO_FIX",
330339
)
331340
with MessageHandlerContext.populate_context(recipient_agent.id):
332341
response = await recipient_agent.on_message(
@@ -385,6 +394,7 @@ async def _process_publish(self, message_envelope: PublishMessageEnvelope) -> No
385394
topic_id=message_envelope.topic_id,
386395
is_rpc=False,
387396
cancellation_token=message_envelope.cancellation_token,
397+
message_id=message_envelope.message_id,
388398
)
389399
agent = await self._get_agent(agent_id)
390400

python/packages/autogen-core/src/autogen_core/application/_worker_runtime.py

+42-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import logging
55
import signal
6+
import uuid
67
import warnings
78
from asyncio import Future, Task
89
from collections import defaultdict
@@ -47,7 +48,7 @@
4748
)
4849
from ..base._serialization import MessageSerializer, SerializationRegistry
4950
from ..base._type_helpers import ChannelArgumentType
50-
from ..components import TypeSubscription
51+
from ..components import TypePrefixSubscription, TypeSubscription
5152
from ._helpers import SubscriptionManager, get_impl
5253
from ._utils import GRPC_IMPORT_ERROR_STR
5354
from .protos import agent_worker_pb2, agent_worker_pb2_grpc
@@ -371,11 +372,17 @@ async def publish_message(
371372
*,
372373
sender: AgentId | None = None,
373374
cancellation_token: CancellationToken | None = None,
375+
message_id: str | None = None,
374376
) -> None:
375377
if not self._running:
376378
raise ValueError("Runtime must be running when publishing message.")
377379
if self._host_connection is None:
378380
raise RuntimeError("Host connection is not set.")
381+
if message_id is None:
382+
message_id = str(uuid.uuid4())
383+
384+
# TODO: consume message_id
385+
379386
message_type = self._serialization_registry.type_name(message)
380387
with self._trace_helper.trace_block(
381388
"create", topic_id, parent=None, extraAttributes={"message_type": message_type}
@@ -447,6 +454,7 @@ async def _process_request(self, request: agent_worker_pb2.RpcRequest) -> None:
447454
topic_id=None,
448455
is_rpc=True,
449456
cancellation_token=CancellationToken(),
457+
message_id=request.request_id,
450458
)
451459

452460
# Call the receiving agent.
@@ -530,11 +538,13 @@ async def _process_event(self, event: agent_worker_pb2.Event) -> None:
530538
for agent_id in recipients:
531539
if agent_id == sender:
532540
continue
541+
# TODO: consume message_id
533542
message_context = MessageContext(
534543
sender=sender,
535544
topic_id=topic_id,
536545
is_rpc=False,
537546
cancellation_token=CancellationToken(),
547+
message_id="NOT_DEFINED_TODO_FIX",
538548
)
539549
agent = await self._get_agent(agent_id)
540550
with MessageHandlerContext.populate_context(agent.id):
@@ -705,27 +715,44 @@ async def try_get_underlying_agent_instance(self, id: AgentId, type: Type[T] = A
705715
async def add_subscription(self, subscription: Subscription) -> None:
706716
if self._host_connection is None:
707717
raise RuntimeError("Host connection is not set.")
708-
if not isinstance(subscription, TypeSubscription):
709-
raise ValueError("Only TypeSubscription is supported.")
710-
# Add to local subscription manager.
711-
await self._subscription_manager.add_subscription(subscription)
712718

713719
# Create a future for the subscription response.
714720
future = asyncio.get_event_loop().create_future()
715721
request_id = await self._get_new_request_id()
722+
723+
match subscription:
724+
case TypeSubscription(topic_type=topic_type, agent_type=agent_type):
725+
message = agent_worker_pb2.Message(
726+
addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest(
727+
request_id=request_id,
728+
subscription=agent_worker_pb2.Subscription(
729+
typeSubscription=agent_worker_pb2.TypeSubscription(
730+
topic_type=topic_type, agent_type=agent_type
731+
)
732+
),
733+
)
734+
)
735+
case TypePrefixSubscription(topic_type_prefix=topic_type_prefix, agent_type=agent_type):
736+
message = agent_worker_pb2.Message(
737+
addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest(
738+
request_id=request_id,
739+
subscription=agent_worker_pb2.Subscription(
740+
typePrefixSubscription=agent_worker_pb2.TypePrefixSubscription(
741+
topic_type_prefix=topic_type_prefix, agent_type=agent_type
742+
)
743+
),
744+
)
745+
)
746+
case _:
747+
raise ValueError("Unsupported subscription type.")
748+
749+
# Add the future to the pending requests.
716750
self._pending_requests[request_id] = future
717751

752+
# Add to local subscription manager.
753+
await self._subscription_manager.add_subscription(subscription)
754+
718755
# Send the subscription to the host.
719-
message = agent_worker_pb2.Message(
720-
addSubscriptionRequest=agent_worker_pb2.AddSubscriptionRequest(
721-
request_id=request_id,
722-
subscription=agent_worker_pb2.Subscription(
723-
typeSubscription=agent_worker_pb2.TypeSubscription(
724-
topic_type=subscription.topic_type, agent_type=subscription.agent_type
725-
)
726-
),
727-
)
728-
)
729756
await self._host_connection.send(message)
730757

731758
# Wait for the subscription response.

python/packages/autogen-core/src/autogen_core/application/_worker_runtime_host_servicer.py

+32-18
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from asyncio import Future, Task
55
from typing import Any, Dict, Set
66

7-
from ..base import TopicId
7+
from autogen_core.base._type_prefix_subscription import TypePrefixSubscription
8+
9+
from ..base import Subscription, TopicId
810
from ..components import TypeSubscription
911
from ._helpers import SubscriptionManager
1012
from ._utils import GRPC_IMPORT_ERROR_STR
@@ -221,34 +223,46 @@ async def _process_add_subscription_request(
221223
self, add_subscription_req: agent_worker_pb2.AddSubscriptionRequest, client_id: int
222224
) -> None:
223225
oneofcase = add_subscription_req.subscription.WhichOneof("subscription")
226+
subscription: Subscription | None = None
224227
match oneofcase:
225228
case "typeSubscription":
226229
type_subscription_msg: agent_worker_pb2.TypeSubscription = (
227230
add_subscription_req.subscription.typeSubscription
228231
)
229-
type_subscription = TypeSubscription(
232+
subscription = TypeSubscription(
230233
topic_type=type_subscription_msg.topic_type, agent_type=type_subscription_msg.agent_type
231234
)
232-
try:
233-
await self._subscription_manager.add_subscription(type_subscription)
234-
subscription_ids = self._client_id_to_subscription_id_mapping.setdefault(client_id, set())
235-
subscription_ids.add(type_subscription.id)
236-
success = True
237-
error = None
238-
except ValueError as e:
239-
success = False
240-
error = str(e)
241-
# Send a response back to the client.
242-
await self._send_queues[client_id].put(
243-
agent_worker_pb2.Message(
244-
addSubscriptionResponse=agent_worker_pb2.AddSubscriptionResponse(
245-
request_id=add_subscription_req.request_id, success=success, error=error
246-
)
247-
)
235+
236+
case "typePrefixSubscription":
237+
type_prefix_subscription_msg: agent_worker_pb2.TypePrefixSubscription = (
238+
add_subscription_req.subscription.typePrefixSubscription
239+
)
240+
subscription = TypePrefixSubscription(
241+
topic_type_prefix=type_prefix_subscription_msg.topic_type_prefix,
242+
agent_type=type_prefix_subscription_msg.agent_type,
248243
)
249244
case None:
250245
logger.warning("Received empty subscription message")
251246

247+
if subscription is not None:
248+
try:
249+
await self._subscription_manager.add_subscription(subscription)
250+
subscription_ids = self._client_id_to_subscription_id_mapping.setdefault(client_id, set())
251+
subscription_ids.add(subscription.id)
252+
success = True
253+
error = None
254+
except ValueError as e:
255+
success = False
256+
error = str(e)
257+
# Send a response back to the client.
258+
await self._send_queues[client_id].put(
259+
agent_worker_pb2.Message(
260+
addSubscriptionResponse=agent_worker_pb2.AddSubscriptionResponse(
261+
request_id=add_subscription_req.request_id, success=success, error=error
262+
)
263+
)
264+
)
265+
252266
async def GetState( # type: ignore
253267
self,
254268
request: agent_worker_pb2.AgentId,

0 commit comments

Comments
 (0)