Skip to content

Commit

Permalink
make logging independent module
Browse files Browse the repository at this point in the history
  • Loading branch information
victordibia committed Oct 3, 2024
1 parent b0b0825 commit 13c1351
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
from autogen_agentchat.agents._base_chat_agent import ChatMessage
from autogen_core.application.logging import EVENT_LOGGER_NAME

from ._utils import AgentChatLogHandler
from .logging import ConsoleLogHandler, EVENT_LOGGER_NAME

logger = logging.getLogger(EVENT_LOGGER_NAME + ".agentchatchat")
logger = logging.getLogger(EVENT_LOGGER_NAME)
logger.setLevel(logging.INFO)
log_handler = AgentChatLogHandler(filename="log.jsonl")
logger.handlers = [log_handler]
console_handler = ConsoleLogHandler()
logger.addHandler(console_handler)


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
from autogen_core.components import DefaultTopicId, event
from autogen_core.components.models import FunctionExecutionResult
from autogen_core.components.tool_agent import ToolException
from autogen_core.application.logging import EVENT_LOGGER_NAME

from ...agents import BaseChatAgent, MultiModalMessage, StopMessage, TextMessage, ToolCallMessage, ToolCallResultMessage
from ._events import ContentPublishEvent, ContentRequestEvent
from ._sequential_routed_agent import SequentialRoutedAgent
from ..logging import EVENT_LOGGER_NAME


class BaseChatAgentContainer(SequentialRoutedAgent):
Expand All @@ -31,7 +31,7 @@ def __init__(self, parent_topic_type: str, agent: BaseChatAgent, tool_agent_type
self._agent = agent
self._message_buffer: List[TextMessage | MultiModalMessage | StopMessage] = []
self._tool_agent_id = AgentId(type=tool_agent_type, key=self.id.key)
self._logger = self.logger = logging.getLogger(EVENT_LOGGER_NAME + f".agentchatchat")
self._logger = self.logger = logging.getLogger(EVENT_LOGGER_NAME)

@event
async def handle_content_publish(self, message: ContentPublishEvent, ctx: MessageContext) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ...agents import StopMessage, TextMessage, ChatMessage
from ._events import ContentPublishEvent, ContentRequestEvent
from ._sequential_routed_agent import SequentialRoutedAgent
from autogen_core.application.logging import EVENT_LOGGER_NAME
from ..logging import EVENT_LOGGER_NAME


class BaseGroupChatManager(SequentialRoutedAgent):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,7 @@ class RoundRobinGroupChat(BaseTeam):
"""

def __init__(
self,
participants: List[BaseChatAgent],
*,
tools: List[Tool] | None = None
):
def __init__(self, participants: List[BaseChatAgent], *, tools: List[Tool] | None = None):
if len(participants) == 0:
raise ValueError("At least one participant is required.")
if len(participants) != len(set(participant.name for participant in participants)):
Expand All @@ -75,8 +70,7 @@ def _create_factory(
def _factory() -> BaseChatAgentContainer:
id = AgentInstantiationContext.current_agent_id()
assert id == AgentId(type=agent.name, key=self._team_id)
container = BaseChatAgentContainer(
parent_topic_type, agent, tool_agent_type)
container = BaseChatAgentContainer(parent_topic_type, agent, tool_agent_type)
assert container.id == id
return container

Expand All @@ -95,8 +89,7 @@ async def run(self, task: str) -> TeamRunResult:

# Register the tool agent.
tool_agent_type = await ToolAgent.register(
runtime, "tool_agent", lambda: ToolAgent(
"Tool agent for round-robin group chat", self._tools)
runtime, "tool_agent", lambda: ToolAgent("Tool agent for round-robin group chat", self._tools)
)
# No subscriptions are needed for the tool agent, which will be called via direct messages.

Expand All @@ -109,8 +102,7 @@ async def run(self, task: str) -> TeamRunResult:
topic_type = participant.name
# Register the participant factory.
await BaseChatAgentContainer.register(
runtime, type=agent_type, factory=self._create_factory(
group_topic_type, participant, tool_agent_type)
runtime, type=agent_type, factory=self._create_factory(group_topic_type, participant, tool_agent_type)
)
# Add subscriptions for the participant.
await runtime.add_subscription(TypeSubscription(topic_type=topic_type, agent_type=agent_type))
Expand All @@ -132,16 +124,13 @@ async def run(self, task: str) -> TeamRunResult:
)
# Add subscriptions for the group chat manager.
await runtime.add_subscription(
TypeSubscription(topic_type=group_chat_manager_topic_type,
agent_type=group_chat_manager_agent_type.type)
TypeSubscription(topic_type=group_chat_manager_topic_type, agent_type=group_chat_manager_agent_type.type)
)
await runtime.add_subscription(
TypeSubscription(topic_type=group_topic_type,
agent_type=group_chat_manager_agent_type.type)
TypeSubscription(topic_type=group_topic_type, agent_type=group_chat_manager_agent_type.type)
)
await runtime.add_subscription(
TypeSubscription(topic_type=team_topic_type,
agent_type=group_chat_manager_agent_type.type)
TypeSubscription(topic_type=team_topic_type, agent_type=group_chat_manager_agent_type.type)
)

group_chat_messages: List[ChatMessage] = []
Expand All @@ -156,8 +145,7 @@ async def collect_group_chat_messages(
type="collect_group_chat_messages",
closure=collect_group_chat_messages,
subscriptions=lambda: [
TypeSubscription(topic_type=group_topic_type,
agent_type="collect_group_chat_messages")
TypeSubscription(topic_type=group_topic_type, agent_type="collect_group_chat_messages")
],
)

Expand All @@ -166,11 +154,9 @@ async def collect_group_chat_messages(

# Run the team by publishing the task to the team topic and then requesting the result.
team_topic_id = TopicId(type=team_topic_type, source=self._team_id)
group_chat_manager_topic_id = TopicId(
type=group_chat_manager_topic_type, source=self._team_id)
group_chat_manager_topic_id = TopicId(type=group_chat_manager_topic_type, source=self._team_id)
await runtime.publish_message(
ContentPublishEvent(agent_message=TextMessage(
content=task, source="user")),
ContentPublishEvent(agent_message=TextMessage(content=task, source="user")),
topic_id=team_topic_id,
)
await runtime.publish_message(ContentRequestEvent(), topic_id=group_chat_manager_topic_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,44 @@
import json
import logging
import sys
from typing import Optional, Union, List, Dict, Any, Sequence
from typing import Union, List, Dict, Any, Sequence
from dataclasses import asdict, is_dataclass

from .group_chat._events import ContentPublishEvent
from ..agents import ChatMessage, TextMessage, MultiModalMessage, ToolCallMessage, ToolCallResultMessage, StopMessage
from autogen_core.components import FunctionCall, Image
from autogen_core.components.models import FunctionExecutionResult

ContentType = Union[str, List[Union[str, Image]],
List[FunctionCall], List[FunctionExecutionResult]]
EVENT_LOGGER_NAME = "autogen_agentchat.events"
ContentType = Union[str, List[Union[str, Image]], List[FunctionCall], List[FunctionExecutionResult]]


class AgentChatLogHandler(logging.Handler):
def __init__(self, filename: Optional[str] = None) -> None:
super().__init__()
self.filename = filename
self.file_handler: Optional[logging.FileHandler] = None
if filename:
self.file_handler = logging.FileHandler(filename)
class BaseLogHandler(logging.Handler):
def serialize_content(
self, content: Union[ContentType, Sequence[ChatMessage], ChatMessage]
) -> Union[List[Any], Dict[str, Any], str]:
if isinstance(content, (str, list)):
return content
elif isinstance(content, (TextMessage, MultiModalMessage, ToolCallMessage, ToolCallResultMessage, StopMessage)):
return asdict(content)
elif isinstance(content, Image):
return {"type": "image", "data": content.data_uri}
elif isinstance(content, FunctionCall):
return {"type": "function_call", "name": content.name, "arguments": content.arguments}
elif isinstance(content, FunctionExecutionResult):
return {"type": "function_execution_result", "content": content.content}
return str(content)

@staticmethod
def json_serializer(obj: Any) -> Any:
if is_dataclass(obj) and not isinstance(obj, type):
return asdict(obj)
elif isinstance(obj, type):
return str(obj)
return str(obj)


class ConsoleLogHandler(BaseLogHandler):
def emit(self, record: logging.LogRecord) -> None:
try:
ts = datetime.fromtimestamp(record.created).isoformat()
Expand All @@ -31,9 +49,22 @@ def emit(self, record: logging.LogRecord) -> None:
f"\033[91m[{ts}], {record.msg.agent_message.source}:\033[0m\n"
f"\n{self.serialize_content(record.msg.agent_message.content)}"
)
# print , flush true
sys.stdout.write(console_message)
sys.stdout.flush()
except Exception:
self.handleError(record)


class FileLogHandler(BaseLogHandler):
def __init__(self, filename: str) -> None:
super().__init__()
self.filename = filename
self.file_handler = logging.FileHandler(filename)

def emit(self, record: logging.LogRecord) -> None:
try:
ts = datetime.fromtimestamp(record.created).isoformat()
if isinstance(record.msg, ContentPublishEvent):
log_entry = json.dumps(
{
"timestamp": ts,
Expand All @@ -44,46 +75,19 @@ def emit(self, record: logging.LogRecord) -> None:
default=self.json_serializer,
)

if self.file_handler:
file_record = logging.LogRecord(
name=record.name,
level=record.levelno,
pathname=record.pathname,
lineno=record.lineno,
msg=log_entry,
args=(),
exc_info=record.exc_info,
)
self.file_handler.emit(file_record)
else:
sys.stderr.write(log_entry)
file_record = logging.LogRecord(
name=record.name,
level=record.levelno,
pathname=record.pathname,
lineno=record.lineno,
msg=log_entry,
args=(),
exc_info=record.exc_info,
)
self.file_handler.emit(file_record)
except Exception:
self.handleError(record)

def serialize_content(
self, content: Union[ContentType, Sequence[ChatMessage], ChatMessage]
) -> Union[List[Any], Dict[str, Any], str]:
if isinstance(content, (str, list)):
return content
elif isinstance(content, (TextMessage, MultiModalMessage, ToolCallMessage, ToolCallResultMessage, StopMessage)):
return asdict(content)
elif isinstance(content, Image):
return {"type": "image", "data": content.data_uri}
elif isinstance(content, FunctionCall):
return {"type": "function_call", "name": content.name, "arguments": content.arguments}
elif isinstance(content, FunctionExecutionResult):
return {"type": "function_execution_result", "content": content.content}
return str(content)

@staticmethod
def json_serializer(obj: Any) -> Any:
if is_dataclass(obj) and not isinstance(obj, type):
return asdict(obj)
elif isinstance(obj, type):
return str(obj)
return str(obj)

def close(self) -> None:
if self.file_handler:
self.file_handler.close()
self.file_handler.close()
super().close()

0 comments on commit 13c1351

Please sign in to comment.