mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-27 23:47:46 +00:00
5090c6153f
* File structure for supporting litellm * more litellm support * feat: Add CachedAPIBackend class and dynamic API backend retrieval function * fix: update benchmark folder path and add default values for architecture and hyperparameters * feat: add LiteLLMAPIBackend and DeprecBackend ; changed structure of the project ; with bus * fix : deprec_backend * feat: Add LiteLLMAPIBackend class and related features; update configuration and test cases. * feat: Enhance LiteLLMAPIBackend with encoder support and dynamic argument handling;Enhance log Colors * lint * fix lint... * fix: Lint * fix:make auto-lint * fix:test oai * fix:redundant _abckend.py * fix: Optimize LiteLLMAPIBackend on token counting functiona, and clean up unused code;add test on this function * feat: Add LiteLLMSettings class and update model settings usage * fix: Update LiteLLMSettings environment variable prefix and model configurations * fix : gitignore * test: Consolidate and relocate test files for litellm backend and oai * fix : lint * fix: lint * auto lint * lint * LINT * lint * chore: remove deprecated backend configuration comments * refactor: Remove unused functions and imports from deprec.py and llm_utils.py * refactor: Move md5_hash function from deprec.py to llm_utils.py * chore: Remove extra newline and add missing import in deprec.py * lint * refactor: Move md5_hash function to utils module * lint * lint * lint --------- Co-authored-by: Young <afe.young@gmail.com> Co-authored-by: Yihua Chen <v-yihuachen@microsoft.com>
159 lines
5.8 KiB
Python
159 lines
5.8 KiB
Python
"""
|
|
This is some common utils functions.
|
|
it is not binding to the scenarios or framework (So it is not placed in rdagent.core.utils)
|
|
"""
|
|
|
|
# TODO: merge the common utils in `rdagent.core.utils` into this folder
|
|
# TODO: split the utils in this module into different modules in the future.
|
|
|
|
import hashlib
|
|
import importlib
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
from typing import Union
|
|
|
|
from rdagent.oai.llm_conf import LLM_SETTINGS
|
|
from rdagent.utils.agent.tpl import T
|
|
|
|
|
|
def get_module_by_module_path(module_path: Union[str, ModuleType]) -> ModuleType:
|
|
"""Load module from path like a/b/c/d.py or a.b.c.d
|
|
|
|
:param module_path:
|
|
:return:
|
|
:raises: ModuleNotFoundError
|
|
"""
|
|
if module_path is None:
|
|
raise ModuleNotFoundError("None is passed in as parameters as module_path")
|
|
|
|
if isinstance(module_path, ModuleType):
|
|
module = module_path
|
|
else:
|
|
if module_path.endswith(".py"):
|
|
module_name = re.sub("^[^a-zA-Z_]+", "", re.sub("[^0-9a-zA-Z_]", "", module_path[:-3].replace("/", "_")))
|
|
module_spec = importlib.util.spec_from_file_location(module_name, module_path)
|
|
if module_spec is None:
|
|
raise ModuleNotFoundError(f"Cannot find module at {module_path}")
|
|
module = importlib.util.module_from_spec(module_spec)
|
|
sys.modules[module_name] = module
|
|
if module_spec.loader is not None:
|
|
module_spec.loader.exec_module(module)
|
|
else:
|
|
raise ModuleNotFoundError(f"Cannot load module at {module_path}")
|
|
else:
|
|
module = importlib.import_module(module_path)
|
|
return module
|
|
|
|
|
|
def convert2bool(value: Union[str, bool]) -> bool:
|
|
"""
|
|
Motivation: the return value of LLM is not stable. Try to convert the value into bool
|
|
"""
|
|
# TODO: if we have more similar functions, we can build a library to converting unstable LLM response to stable results.
|
|
if isinstance(value, str):
|
|
v = value.lower().strip()
|
|
if v in ["true", "yes", "ok"]:
|
|
return True
|
|
if v in ["false", "no"]:
|
|
return False
|
|
raise ValueError(f"Can not convert {value} to bool")
|
|
elif isinstance(value, bool):
|
|
return value
|
|
else:
|
|
raise ValueError(f"Unknown value type {value} to bool")
|
|
|
|
|
|
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)
|
|
|
|
|
|
def filter_progress_bar(stdout: str) -> str:
|
|
"""
|
|
Filter out progress bars from stdout using regex.
|
|
"""
|
|
from rdagent.oai.llm_utils import APIBackend # avoid circular import
|
|
|
|
# Initial progress bar regex pattern
|
|
progress_bar_re = (
|
|
r"(\d+/\d+\s+[━]+\s+\d+s?\s+\d+ms/step.*?\u0008+|"
|
|
r"\d+/\d+\s+[━]+\s+\d+s?\s+\d+ms/step|"
|
|
r"\d+/\d+\s+[━]+\s+\d+s?\s+\d+ms/step.*|"
|
|
r"\d+/\d+\s+[━]+.*?\u0008+|"
|
|
r"\d+/\d+\s+[━]+.*|[ ]*\u0008+|"
|
|
r"\d+%\|[█▏▎▍▌▋▊▉]+\s+\|\s+\d+/\d+\s+\[\d{2}:\d{2}<\d{2}:\d{2},\s+\d+\.\d+it/s\]|"
|
|
r"\d+%\|[█]+\|\s+\d+/\d+\s+\[\d{2}:\d{2}<\d{2}:\d{2},\s*\d+\.\d+it/s\])"
|
|
)
|
|
|
|
filtered_stdout = remove_ansi_codes(stdout)
|
|
filtered_stdout = re.sub(progress_bar_re, "", filtered_stdout)
|
|
filtered_stdout = re.sub(r"\s*\n\s*", "\n", filtered_stdout)
|
|
|
|
needs_sub = True
|
|
# Attempt further filtering up to 5 times
|
|
for _ in range(5):
|
|
filtered_stdout_shortened = filtered_stdout
|
|
system_prompt = T(".prompts:filter_progress_bar.system").r()
|
|
|
|
for __ in range(10):
|
|
user_prompt = T(".prompts:filter_progress_bar.user").r(
|
|
stdout=filtered_stdout_shortened,
|
|
)
|
|
stdout_token_size = APIBackend().build_messages_and_calculate_token(
|
|
user_prompt=user_prompt,
|
|
system_prompt=system_prompt,
|
|
)
|
|
if stdout_token_size < LLM_SETTINGS.chat_token_limit * 0.1:
|
|
return filtered_stdout_shortened
|
|
elif stdout_token_size > LLM_SETTINGS.chat_token_limit * 0.6:
|
|
filtered_stdout_shortened = filtered_stdout_shortened[
|
|
len(filtered_stdout_shortened) // 4 : len(filtered_stdout_shortened) * 3 // 4
|
|
]
|
|
else:
|
|
break
|
|
|
|
response = json.loads(
|
|
APIBackend().build_messages_and_create_chat_completion(
|
|
user_prompt=user_prompt, system_prompt=system_prompt, json_mode=True
|
|
)
|
|
)
|
|
needs_sub = response.get("needs_sub", True)
|
|
regex_patterns = response.get("regex_patterns", [])
|
|
try:
|
|
if isinstance(regex_patterns, list):
|
|
for pattern in regex_patterns:
|
|
filtered_stdout = re.sub(pattern, "", filtered_stdout)
|
|
else:
|
|
filtered_stdout = re.sub(regex_patterns, "", filtered_stdout)
|
|
|
|
if not needs_sub:
|
|
break
|
|
filtered_stdout = re.sub(r"\s*\n\s*", "\n", filtered_stdout)
|
|
except re.error as e: # sometime the generated regex pattern is invalid and yield exception.
|
|
from rdagent.log import rdagent_logger as logger
|
|
|
|
logger.error(f"Error in filtering progress bar: due to {e}")
|
|
return filtered_stdout
|
|
|
|
|
|
def remove_path_info_from_str(base_path: Path, target_string: str) -> str:
|
|
"""
|
|
Remove the absolute path from the target string
|
|
"""
|
|
target_string = re.sub(str(base_path), "...", target_string)
|
|
target_string = re.sub(str(base_path.absolute()), "...", target_string)
|
|
return target_string
|
|
|
|
|
|
def md5_hash(input_string: str) -> str:
|
|
hash_md5 = hashlib.md5(usedforsecurity=False)
|
|
input_bytes = input_string.encode("utf-8")
|
|
hash_md5.update(input_bytes)
|
|
return hash_md5.hexdigest()
|