From cb9ae44f23c70eb2f4dd6322fa67a174cda670c7 Mon Sep 17 00:00:00 2001 From: Eric Zhu Date: Thu, 12 Sep 2024 03:26:33 -0700 Subject: [PATCH] Add documentation to RoutedAgent and message_hander, remove outdated doc content. (#485) --- .../docs/src/cookbook/type-routed-agent.md | 66 ------------------- .../packages/autogen-core/docs/src/index.rst | 1 - .../autogen_core/components/_routed_agent.py | 56 ++++++++++++++++ 3 files changed, 56 insertions(+), 67 deletions(-) delete mode 100644 python/packages/autogen-core/docs/src/cookbook/type-routed-agent.md diff --git a/python/packages/autogen-core/docs/src/cookbook/type-routed-agent.md b/python/packages/autogen-core/docs/src/cookbook/type-routed-agent.md deleted file mode 100644 index d0b587043..000000000 --- a/python/packages/autogen-core/docs/src/cookbook/type-routed-agent.md +++ /dev/null @@ -1,66 +0,0 @@ -# Using Type Routed Agent - -To make it easier to implement agents that respond to certain message types there is a base class called {py:class}`~autogen_core.components.RoutedAgent`. This class provides a simple decorator pattern for associating message types with message handlers. - -The decorator {py:func}`autogen_core.components.message_handler` should be added to functions in the class that are intended to handle messages. These functions have a specific signature that needs to be followed for it to be recognized as a message handler. - -- The function must be an `async` function. -- The function must be decorated with the `message_handler` decorator. -- The function must have exactly 3 arguments. - - `self` - - `message`: The message to be handled, this must be type hinted with the message type that it is intended to handle. - - `cancellation_token`: A {py:class}`autogen_core.base.CancellationToken` object -- The function must be type hinted with what message types it can return. - -```{tip} -Handlers can handle more than one message type by accepting a Union of the message types. It can also return more than one message type by returning a Union of the message types. -``` - -## Example - -The following is an example of a simple agent that broadcasts the fact it received messages, and resets its internal counter when it receives a reset message. - -One important thing to point out is that when an agent is constructed it must be passed a runtime object. This allows the agent to communicate with other agents via the runtime. - -```python -from dataclasses import dataclass -from typing import List, Union -from autogen_core.components import RoutedAgent, message_handler, Image -from autogen_core.base import AgentRuntime, CancellationToken - -@dataclass -class TextMessage: - content: str - source: str - -@dataclass -class MultiModalMessage: - content: List[Union[str, Image]] - source: str - -@dataclass -class Reset: - pass - - -class MyAgent(RoutedAgent): - def __init__(self): - super().__init__(description="I am a demo agent") - self._received_count = 0 - - @message_handler() - async def on_text_message( - self, message: TextMessage | MultiModalMessage, cancellation_token: CancellationToken - ) -> None: - self._received_count += 1 - await self.publish_message( - TextMessage( - content=f"I received a message from {message.source}. Message received #{self._received_count}", - source=self.metadata["type"], - ) - ) - - @message_handler() - async def on_reset(self, message: Reset, cancellation_token: CancellationToken) -> None: - self._received_count = 0 -``` diff --git a/python/packages/autogen-core/docs/src/index.rst b/python/packages/autogen-core/docs/src/index.rst index 13f2a4483..2387912d3 100644 --- a/python/packages/autogen-core/docs/src/index.rst +++ b/python/packages/autogen-core/docs/src/index.rst @@ -50,7 +50,6 @@ from `Agent and Multi-Agent Application MessageHandler[ReceivesT, ProducesT]: @@ -149,6 +170,34 @@ def message_handler( class RoutedAgent(BaseAgent): + """A base class for agents that route messages to handlers based on the type of the message + and optional matching functions. + + To create a routed agent, subclass this class and add message handlers as methods decorated with + the :func:`message_handler` decorator. + + Example: + + .. code-block:: python + + from autogen_core.base import MessageContext + from autogen_core.components import RoutedAgent, message_handler + # Assume Message, MessageWithContent, and Response are defined elsewhere. + + + class MyAgent(RoutedAgent): + def __init__(self): + super().__init__("MyAgent") + + @message_handler + async def handle_message(self, message: Message, ctx: MessageContext) -> Response: + return Response() + + @message_handler(match=lambda message, ctx: message.content == "special") + async def handle_special_message(self, message: MessageWithContent, ctx: MessageContext) -> Response: + return Response() + """ + def __init__(self, description: str) -> None: # Self is already bound to the handlers self._handlers: Dict[ @@ -172,6 +221,9 @@ class RoutedAgent(BaseAgent): super().__init__(description) async def on_message(self, message: Any, ctx: MessageContext) -> Any | None: + """Handle a message by routing it to the appropriate message handler. + Do not override this method in subclasses. Instead, add message handlers as methods decorated with + the :func:`message_handler` decorator.""" key_type: Type[Any] = type(message) # type: ignore handlers = self._handlers.get(key_type) # type: ignore if handlers is not None: @@ -183,11 +235,15 @@ class RoutedAgent(BaseAgent): return await self.on_unhandled_message(message, ctx) # type: ignore async def on_unhandled_message(self, message: Any, ctx: MessageContext) -> None: + """Called when a message is received that does not have a matching message handler. + The default implementation logs an info message.""" logger.info(f"Unhandled message: {message}") # Deprecation warning for TypeRoutedAgent class TypeRoutedAgent(RoutedAgent): + """Deprecated. Use :class:`RoutedAgent` instead.""" + def __init__(self, description: str) -> None: warnings.warn("TypeRoutedAgent is deprecated. Use RoutedAgent instead.", DeprecationWarning, stacklevel=2) super().__init__(description)