Files
NexQuant/rdagent/log/utils/__init__.py
T
Linlang c8f1c5364a chore: add a rdagent server with UI & logger storage refinement(#553)
* change_log_object

* lint code

* delete comments

* change_log_object

* change_log_object

* fix import test error

* update code

* update code

* fix bugs

* skip mypy error

* skip mypy error

* skip mypy error

* Start the flask server before running the demo.

* achieve front and back interaction

* fix github-advanced-security comments

* fix github-advanced-security comments

* tmp ignore

* fix CI

* move some logic

* change format

* adjust logic

* log2json changes

* tmp

* fix

* fix bug

* refine log2json between 5 scenarios

* fix

* refine codes

* fix logic

* use localhost

* add loop & all_duration param for old scenario startup

* merge control logic

* add README for server ui api

* update README

* reuse code in logger

* add loop_n and all_duration param

* fix upload

* ui server now use port in setting

* fix port setting

* fix port setting

* fix mypy check

* refine logger and log storage

* fix ruff error

* fix CI

* refine logger, loop, storage

* bind one FileStorage with one logger

* not truncate log storage

* refine LoopBase.load(), use `checkout` instead of `output_path` and `do_truncate`

* clear session folder when loading loop to run

* move component info init step to ExpGen Class

* Update rdagent/utils/workflow.py

* move truncate_session function to LoopBase class

* add checkout param for other scenarios

* fix bug

* move WebStorage to UI

* change web_storage name

* add randomname to requirements

* add typer

* fix requirements

---------

Co-authored-by: WinstonLiyte <1957922024@qq.com>
Co-authored-by: Bowen Xian <xianbowen@outlook.com>
Co-authored-by: you-n-g <you-n-g@users.noreply.github.com>
2025-06-06 18:52:19 +08:00

115 lines
3.4 KiB
Python

import inspect
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional, TypedDict, cast
class LogColors:
"""
ANSI color codes for use in console output.
"""
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
CYAN = "\033[96m"
WHITE = "\033[97m"
GRAY = "\033[90m"
BLACK = "\033[30m"
BOLD = "\033[1m"
ITALIC = "\033[3m"
END = "\033[0m"
@classmethod
def get_all_colors(cls: type["LogColors"]) -> list:
names = dir(cls)
names = [name for name in names if not name.startswith("__") and not callable(getattr(cls, name))]
return [getattr(cls, name) for name in names]
def render(self, text: str, color: str = "", style: str = "") -> str:
"""
render text by input color and style.
It's not recommend that input text is already rendered.
"""
# This method is called too frequently, which is not good.
colors = self.get_all_colors()
# Perhaps color and font should be distinguished here.
if color and color in colors:
error_message = f"color should be in: {colors} but now is: {color}"
raise ValueError(error_message)
if style and style in colors:
error_message = f"style should be in: {colors} but now is: {style}"
raise ValueError(error_message)
text = f"{color}{text}{self.END}"
return f"{style}{text}{self.END}"
@staticmethod
def remove_ansi_codes(s: str) -> str:
"""
It is for removing ansi ctrl characters in the string(e.g. colored text)
"""
ansi_escape = re.compile(r"\x1B\[[0-?]*[ -/]*[@-~]")
return ansi_escape.sub("", s)
class CallerInfo(TypedDict):
function: str
line: int
name: Optional[str]
def get_caller_info() -> CallerInfo:
# Get the current stack information
stack = inspect.stack()
# The second element is usually the caller's information
caller_info = stack[2]
frame = caller_info[0]
info: CallerInfo = {
"line": caller_info.lineno,
"name": frame.f_globals["__name__"], # Get the module name from the frame's globals
"function": frame.f_code.co_name, # Get the caller's function name
}
return info
def is_valid_session(log_path: Path) -> bool:
return log_path.is_dir() and log_path.joinpath("__session__").exists()
def extract_loopid_func_name(tag: str) -> tuple[str, str] | tuple[None, None]:
"""extract loop id and function name from the tag in Message"""
match = re.search(r"Loop_(\d+)\.([^.]+)", tag)
return cast(tuple[str, str], match.groups()) if match else (None, None)
def extract_evoid(tag: str) -> str | None:
"""extract evo id from the tag in Message"""
match = re.search(r"\.evo_loop_(\d+)\.", tag)
return cast(str, match.group(1)) if match else None
def extract_json(log_content: str) -> dict | None:
match = re.search(r"\{.*\}", log_content, re.DOTALL)
if match:
return cast(dict, json.loads(match.group(0)))
return None
def gen_datetime(dt: datetime | None = None) -> datetime:
"""
Generate a datetime object in UTC timezone.
- If `dt` is None, it will return the current time in UTC.
- If `dt` is provided, it will convert it to UTC timezone.
"""
if dt is None:
return datetime.now(timezone.utc)
return dt.astimezone(timezone.utc)