- Notifications
You must be signed in to change notification settings - Fork 60
Add AutoGen logger for generating report and integrating with OCI monitoring. #1031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 59 commits
Commits
Show all changes
60 commits Select commit Hold shift + click to select a range
d743c47 Do not pop message from params.
qiuosier 2bd2b65 Add logger for autogen.
qiuosier 1928c21 Add report generation.
qiuosier ee0b154 Generate report when stop logging.
qiuosier 2888b8d Add multi-threading support.
qiuosier 838c13b Format tool call args if it is a valid JSON.
qiuosier c87704b Support OCI object storage for logging and reports.
qiuosier 004def7 Refactor logging.
qiuosier 38b02aa Update logic for creating new logging session.
qiuosier 7683e0d Support multiple AutoGen loggers.
qiuosier 45caba9 Refactor code into ads.llm.autogen.v02
qiuosier 82b067d Update logger repr.
qiuosier 209b5fa Disable chat tab.
qiuosier c2aa1da Add Chat tab.
qiuosier 8c608ef Handle chat rendering error.
qiuosier cc7f186 Add logs tab.
qiuosier f35a995 Log library versions.
qiuosier 4c0ca6f Create report_dir when it does not exist.
qiuosier ab64b62 Fix error when there is no LLM call.
qiuosier 4328487 Fix error when starting multiple loggers.
qiuosier cd600a4 Add usage property to client response.
qiuosier 57e60c4 Fix log_new_client() bug in session_logger.
qiuosier 67c59e1 Print error instead raising exception when failed to create report.
qiuosier 2e30362 Update copyright and sort imports.
qiuosier ee2df37 Add method to get all existing loggers.
qiuosier 2aa919a Catch logging exception and log traceback.
qiuosier 9d13d8f Use space to replace empty message for OCI GenAI LLM call.
qiuosier 1430005 Include recipient in chat.
qiuosier bd239a0 Update session logger serialization.
qiuosier def985c Ignore message from chat manager in chat tab.
qiuosier 76a201c Update Chat box template.
qiuosier 696065d Update response serialization.
qiuosier af5de13 Add HTML escape for displaying raw logs.
qiuosier 728eeb5 Update timeline header and show whether LLM call is cached.
qiuosier 9f89d0f Show more metrics in invocation tab.
qiuosier 9937b32 Count unique agents and chat managers.
qiuosier b4384e8 Align left within code block.
qiuosier d9a11fc Do logging only if logger is started.
qiuosier 20bccba Remove loggers from LoggerManager once stopped.
qiuosier ef7876a Skip logging new clients and new wrappers.
qiuosier cf1f152 Update Request/Response block in invocations.
qiuosier 014f69f Update new client logging.
qiuosier a0ffcd2 Show flow chat with timeline.
qiuosier a3ff197 Move SessionLogger into ads.llm.autogen.v02.loggers.
qiuosier 3da1519 Show empty message as empty instead of None in chat tab.
qiuosier 9750e06 Update copyrights.
qiuosier 3c16972 Merge remote-tracking branch 'origin/main' into feature/autogen
qiuosier 519508b Refactor logging and report generation.
qiuosier faa2ca5 Add context manager for session logger.
qiuosier d84c178 Fix chat manager parsing.
qiuosier 277e3b2 Log and show exception in report.
qiuosier fbb4257 Add OCI monitoring logger.
qiuosier d3ef8e7 Move functions to utils.py
qiuosier 085249c Update metric logger.
qiuosier 6ddf9bf Fix error in session logger.
qiuosier c63e6cf Make dimensions optional in metric logger.
qiuosier 389193d Update default settings for metric logger.
qiuosier d95e4ea Update docs.
qiuosier ec6dcdb Fix typo in docs.
qiuosier 277e3cf Merge branch 'main' into feature/autogen
qiuosier File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
| # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
| # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ | ||
| | ||
| | ||
| class Events: | ||
| KEY = "event_name" | ||
| | ||
| EXCEPTION = "exception" | ||
| LLM_CALL = "llm_call" | ||
| TOOL_CALL = "tool_call" | ||
| NEW_AGENT = "new_agent" | ||
| NEW_CLIENT = "new_client" | ||
| RECEIVED_MESSAGE = "received_message" | ||
| SESSION_START = "logging_session_start" | ||
| SESSION_STOP = "logging_session_stop" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
| # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. | ||
| # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ | ||
| import json | ||
| import logging | ||
| import os | ||
| | ||
| from jinja2 import Environment, FileSystemLoader | ||
| | ||
| logger = logging.getLogger(__name__) | ||
| | ||
| | ||
| class BaseReport: | ||
| """Base class containing utilities for generating reports.""" | ||
| | ||
| @staticmethod | ||
| def format_json_string(s) -> str: | ||
| """Formats the JSON string in markdown.""" | ||
| return f"```json\n{json.dumps(json.loads(s), indent=2)}\n```" | ||
| | ||
| @staticmethod | ||
| def _parse_date_time(datetime_string: str): | ||
| """Parses a datetime string in the logs into date and time. | ||
| Keeps only the seconds in the time. | ||
| """ | ||
| date_str, time_str = datetime_string.split(" ", 1) | ||
| time_str = time_str.split(".", 1)[0] | ||
| return date_str, time_str | ||
| | ||
| @staticmethod | ||
| def _preview_message(message: str, max_length=30) -> str: | ||
| """Shows the beginning part of a string message.""" | ||
| # Return the entire string if it is less than the max_length | ||
| if len(message) <= max_length: | ||
| return message | ||
| # Go backward until we find the first whitespace | ||
| idx = 30 | ||
| while not message[idx].isspace() and idx > 0: | ||
| idx -= 1 | ||
| # If we found a whitespace | ||
| if idx > 0: | ||
| return message[:idx] + "..." | ||
| # If we didn't find a whitespace | ||
| return message[:30] + "..." | ||
| | ||
| @classmethod | ||
| def _render_template(cls, template_path, **kwargs) -> str: | ||
| """Render Jinja template with kwargs.""" | ||
| template_dir = os.path.join(os.path.dirname(__file__), "templates") | ||
| environment = Environment( | ||
| loader=FileSystemLoader(template_dir), autoescape=True | ||
| ) | ||
| template = environment.get_template(template_path) | ||
| try: | ||
| html = template.render(**kwargs) | ||
| except Exception: | ||
| logger.error( | ||
| "Unable to render template %s with data:\n%s", | ||
| template_path, | ||
| str(kwargs), | ||
| ) | ||
| return cls._render_template( | ||
| template_path=template_path, | ||
| sender=kwargs.get("sender", "N/A"), | ||
| content="TEMPLATE RENDER ERROR", | ||
| timestamp=kwargs.get("timestamp", ""), | ||
| ) | ||
| return html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| #!/usr/bin/env python | ||
| # Copyright (c) 2024 Oracle and/or its affiliates. | ||
| # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ | ||
| """Contains the data structure for logging and reporting.""" | ||
| import copy | ||
| import json | ||
| from dataclasses import asdict, dataclass, field | ||
| from typing import Optional, Union | ||
| | ||
| from ads.llm.autogen.constants import Events | ||
| | ||
| | ||
| @dataclass | ||
| class LogData: | ||
| """Base class for the data field of LogRecord.""" | ||
| | ||
| def to_dict(self): | ||
| """Convert the log data to dictionary.""" | ||
| return asdict(self) | ||
| | ||
| | ||
| @dataclass | ||
| class LogRecord: | ||
| """Represents a log record. | ||
| | ||
| The `data` field is for pre-defined structured data, which should be an instance of LogData. | ||
| The `kwargs` field is for freeform key value pairs. | ||
| """ | ||
| | ||
| session_id: str | ||
| thread_id: int | ||
| timestamp: str | ||
| event_name: str | ||
| source_id: Optional[int] = None | ||
| source_name: Optional[str] = None | ||
| # Structured data for specific type of logs | ||
| data: Optional[LogData] = None | ||
| # Freeform data | ||
| kwargs: dict = field(default_factory=dict) | ||
| | ||
| def to_dict(self): | ||
| """Convert the log record to dictionary.""" | ||
| return asdict(self) | ||
| | ||
| def to_string(self): | ||
| """Serialize the log record to JSON string.""" | ||
| return json.dumps(self.to_dict(), default=str) | ||
| | ||
| @classmethod | ||
| def from_dict(cls, data: dict) -> "LogRecord": | ||
| """Initializes a LogRecord object from dictionary.""" | ||
| event_mapping = { | ||
| Events.NEW_AGENT: AgentData, | ||
| Events.TOOL_CALL: ToolCallData, | ||
| Events.LLM_CALL: LLMCompletionData, | ||
| } | ||
| if Events.KEY not in data: | ||
| raise KeyError("event_name not found in data.") | ||
| | ||
| data = copy.deepcopy(data) | ||
| | ||
| event_name = data["event_name"] | ||
| if event_name in event_mapping and data.get("data"): | ||
| data["data"] = event_mapping[event_name](**data.pop("data")) | ||
| | ||
| return cls(**data) | ||
| | ||
| | ||
| @dataclass | ||
| class AgentData(LogData): | ||
| """Represents agent log Data.""" | ||
| | ||
| agent_name: str | ||
| agent_class: str | ||
| agent_module: Optional[str] = None | ||
| is_manager: Optional[bool] = None | ||
| | ||
| | ||
| @dataclass | ||
| class LLMCompletionData(LogData): | ||
| """Represents LLM completion log data.""" | ||
| | ||
| invocation_id: str | ||
| request: dict | ||
| response: dict | ||
| start_time: str | ||
| end_time: str | ||
| cost: Optional[float] = None | ||
| is_cached: Optional[bool] = None | ||
| | ||
| | ||
| @dataclass | ||
| class ToolCallData(LogData): | ||
| """Represents tool call log data.""" | ||
| | ||
| tool_name: str | ||
| start_time: str | ||
| end_time: str | ||
| agent_name: str | ||
| agent_class: str | ||
| agent_module: Optional[str] = None | ||
| input_args: dict = field(default_factory=dict) | ||
| returns: Optional[Union[str, list, dict, tuple]] = None | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it better to use the built in protocol for conversion to dict?
__dict__There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the
dataattribute is another data class, the build in__dict__does not seem to convert it to adict, while theasdict()function will convert it todictrecursively.