-
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.