[feature] update the docstring in chat, worker/fronted, utils

This commit is contained in:
hs 2024-07-24 15:01:26 +08:00
parent ea2d485241
commit ce36c2f0cf
15 changed files with 211 additions and 50 deletions

View file

@ -43,7 +43,7 @@ class CliMemoryChat(BaseMemoryChat):
memory_service (str | BaseMemoryService): The memory service to be used for storing conversation history.
generation_model (str | BaseModel): The model responsible for generating AI responses.
stream (bool, optional): Flag indicating whether responses should be streamed. Defaults to True.
human_name (str, optional): The name assigned to the human user. Defaults to a language-specific default.
human_name (str, optional): The name assigned to the human user. Defaults to a language-specific user.
assistant_name (str, optional): The name of the AI assistant. Defaults to "AI".
**kwargs: Additional keyword arguments for flexibility or future extensions.
@ -98,15 +98,15 @@ class CliMemoryChat(BaseMemoryChat):
def memory_service(self) -> BaseMemoryService:
"""
Property to access the memory service. If the service is initially set as a string,
it will be looked up in the global context's memory service dictionary, initialized,
it will be looked up in the memory service dictionary of global context, initialized,
and then returned as an instance of `BaseMemoryService`. Ensures the memory service
is properly started before use.
Returns:
BaseMemoryService: The active memory service instance.
BaseMemoryService: An active memory service instance.
Raises:
ValueError: If the memory service string reference is not found in the global context's dictionary.
ValueError: If the declaration of memory service is not found in the memory service dictionary of global context.
"""
if isinstance(self._memory_service, str):
if self._memory_service not in G_CONTEXT.memory_service_dict:
@ -123,10 +123,10 @@ class CliMemoryChat(BaseMemoryChat):
context's model dictionary.
Raises:
ValueError: If the model string is not found in the global context's model dictionary.
ValueError: If the declaration of generation model is not found in the model dictionary of global context .
Returns:
BaseModel: The actual generation model instance.
BaseModel: An actual generation model instance.
"""
if isinstance(self._generation_model, str):
if self._generation_model not in G_CONTEXT.model_dict:
@ -146,12 +146,12 @@ class CliMemoryChat(BaseMemoryChat):
Defaults to False.
Returns:
- ModelResponse: In non-streaming mode, returns the complete AI response.
- ModelResponse: In non-streaming mode, returns a complete AI response.
- ModelResponseGen: In streaming mode, returns a generator yielding AI response parts.
Side Effects:
- Updates the conversation memory with the user's query and (optionally) the AI's response.
- Retrieves and includes historical messages and memory content in the conversation context.
- Updates the conversation memory with the query of user and (optionally) the response of AI.
- Retrieves and includes historical messages and memory content in the context of conversation.
"""
new_message: Message = Message(role=MessageRoleEnum.USER.value, role_name=self.human_name, content=query)
self.memory_service.add_messages(new_message)
@ -227,7 +227,7 @@ class CliMemoryChat(BaseMemoryChat):
query (str): The user's input command string.
Returns:
bool: Indicates whether to continue running the CLI after processing the command.
bool: Indicates whether to continue running the CLI after processing the command.
"""
continue_run = True
command, kwargs = self.parse_query_command(query)

View file

@ -7,6 +7,9 @@ from memory_scope.utils.datetime_handler import DatetimeHandler
class FuseRerankWorker(MemoryBaseWorker):
"""
Reranks the memory nodes by scores, types, and temporal relevance. Formats the top-K reranked nodes to print.
"""
def _parse_params(self, **kwargs):
self.fuse_score_threshold: float = kwargs.get("fuse_score_threshold", 0.1)
@ -16,6 +19,9 @@ class FuseRerankWorker(MemoryBaseWorker):
@staticmethod
def match_node_time(extract_time_dict: Dict[str, str], node: MemoryNode):
"""
Determines whether the node is relevant.
"""
if extract_time_dict:
match_event_flag = True
for k, v in extract_time_dict.items():

View file

@ -9,9 +9,18 @@ from memory_scope.utils.datetime_handler import DatetimeHandler
class PrintMemoryWorker(MemoryBaseWorker):
"""
Formats the memories to print.
"""
FILE_PATH: str = __file__
def _run(self):
"""
Executes the primary function, it involves:
1. Fetches the memories.
2. Formats them by 'print_template'.
3. Set the formatted string back into the worker's context
"""
# get long-term memory
memory_node_list: List[MemoryNode] = self.memory_handler.get_memories(RETRIEVE_MEMORY_NODES)
memory_node_list = sorted(memory_node_list, key=lambda x: x.timestamp, reverse=True)

View file

@ -4,8 +4,13 @@ from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker
class ReadMessageWorker(MemoryBaseWorker):
"""
Fetches unmemorized chat messages.
"""
def _run(self):
"""
Executes the primary function to fetch unmemorized chat messages.
"""
chat_messages = [x for x in self.chat_messages if not x.memorized]
if len(chat_messages) > 0 and chat_messages[-1].role == MessageRoleEnum.USER.value:
chat_messages = chat_messages[:-1]

View file

@ -52,7 +52,7 @@ class RetrieveMemoryWorker(MemoryBaseWorker):
@timer
def retrieve_from_insight(self, query: str) -> List[MemoryNode]:
"""
Retrieves memories marked as insights from the database based on a query, filtered by user, target,
Retrieves memories marked as insights from the store based on a query, filtered by user, target,
and set to active status.
Args:
@ -79,6 +79,18 @@ class RetrieveMemoryWorker(MemoryBaseWorker):
@timer
def retrieve_expired_memory(self, query: str) -> List[MemoryNode]:
"""
Retrieves expired memories marked as observation from the store based on a query, filtered by user, target,
and set to active status.
Args:
query (str): The search query to match against the memories.
Returns:
List[MemoryNode]: A list of MemoryNode objects that match the query criteria,
limited by 'retrieve_expired_top_k'.
Returns an empty list if 'retrieve_expired_top_k' is not set.
"""
if not self.retrieve_expired_top_k:
return []

View file

@ -16,13 +16,13 @@ class SemanticRankWorker(MemoryBaseWorker):
def _run(self):
"""
Executes the primary workflow of the SemanticRankWorker which includes:
- Retrieving query and timestamp from context.
- Fetching memory nodes.
- Removing duplicate nodes.
- Ranking nodes semantically.
- Assigning scores to nodes.
- Sorting nodes by score.
- Saving the ranked nodes back with logging.
- Retrieves query and timestamp from context.
- Fetches memory nodes.
- Removes duplicate nodes.
- Ranks nodes semantically.
- Assigns scores to nodes.
- Sorts nodes by score.
- Saves the ranked nodes back with logging.
If no memory nodes are retrieved or if the ranking model fails,
appropriate warnings are logged.

View file

@ -27,7 +27,7 @@ class DatetimeHandler(object):
Attributes:
self._dt (datetime.datetime): The internal datetime representation of the input.
self._dt_info_dict (dict | None): A dictionary containing parsed datetime information, initialized as None.
self._dt_info_dict (dict | None): A dictionary containing parsed datetime information, defaults to None.
"""
if isinstance(dt, str | int | float):
if isinstance(dt, str):
@ -65,7 +65,7 @@ class DatetimeHandler(object):
def dt_info_dict(self):
"""
Property method to get the dictionary containing parsed datetime information.
If not already parsed, it triggers the parsing process using `_parse_dt_info`.
If None, initialize using `_parse_dt_info`.
Returns:
dict: A dictionary with parsed datetime information.
@ -77,7 +77,7 @@ class DatetimeHandler(object):
@classmethod
def extract_date_parts_cn(cls, input_string: str) -> dict:
"""
Extracts date components from a Chinese text string into a dictionary.
Extracts various components of a date (year, month, day, etc.) from an input string based on Chinese formats.
This method identifies year, month, day, weekday, and hour components within the input
string based on predefined patterns. It supports relative terms like '' (every) and
@ -125,7 +125,7 @@ class DatetimeHandler(object):
text. It supports extraction of year, month name, day, 12-hour and 24-hour time formats, and weekdays.
Args:
input_string (str): The string from which to extract date and time components.
input_string (str): The English text containing date and time information.
Returns:
dict: A dictionary containing the extracted date parts with default values of -1 where components are not
@ -212,7 +212,7 @@ class DatetimeHandler(object):
Extracts various date components from the input string based on the current language context.
This method dynamically selects a language-specific function to parse the input string and extract
date parts such as year, month, day, etc. If the function for the current language context does not exist,
date parts such as year, month, day, etc. If the function for current language context does not exist,
a warning is logged and an empty dictionary is returned.
Args:
@ -233,10 +233,10 @@ class DatetimeHandler(object):
Check if the input query contains any datetime-related words based on the cn language context.
Args:
query (str): The input string to check for datetime words.
query (str): The input string to check for datetime-related words.
Returns:
bool: True if the query contains at least one datetime word, False otherwise.
bool: True if the query contains at least one datetime-related word, False otherwise.
"""
contain_datetime = False
# TODO use re
@ -252,10 +252,10 @@ class DatetimeHandler(object):
Check if the input query contains any datetime-related words based on the en language context.
Args:
query (str): The input string to check for datetime words.
query (str): The input string to check for datetime-related words.
Returns:
bool: True if the query contains at least one datetime word, False otherwise.
bool: True if the query contains at least one datetime-related word, False otherwise.
"""
contain_datetime = False
for datetime_word in DATATIME_WORD_LIST[G_CONTEXT.language]:
@ -283,7 +283,7 @@ class DatetimeHandler(object):
dt_format (str, optional): The datetime format string. Defaults to "%Y%m%d".
Returns:
str: The formatted datetime string.
str: A formatted datetime string.
"""
return self._dt.strftime(dt_format)
@ -295,7 +295,7 @@ class DatetimeHandler(object):
string_format (str): A format string where placeholders are keys from `dt_info_dict`.
Returns:
str: The formatted string with datetime information inserted.
str: A formatted datetime string.
"""
return string_format.format(**self.dt_info_dict)
@ -305,6 +305,6 @@ class DatetimeHandler(object):
Get the timestamp representation of the stored datetime.
Returns:
int: The timestamp value of the datetime.
int: A timestamp value.
"""
return int(self._dt.timestamp())

View file

@ -10,6 +10,10 @@ from memory_scope.storage.base_monitor import BaseMonitor
class GlobalContext(object):
"""
The GlobalContext class archives all configs utilized by store, monitor, services and workers.
"""
def __init__(self):
self.global_config: Dict[str, Any] = {}
self.worker_config: Dict[str, Dict[str, Any]] = {}

View file

@ -9,6 +9,9 @@ LOGGER_DICT = {}
class Logger(logging.Logger):
"""
The `Logger` class handle the stream of information or errors in activities.
"""
def __init__(self,
name: str,
level: int = logging.INFO,
@ -138,8 +141,8 @@ class Logger(logging.Logger):
msg (str): The logged message, before formatting.
args (tuple): The arguments to the log message.
exc_info (tuple): Exception information or None.
func (function): The function where the logging call was made.
extra (dict): Additional information for the log record, can be None.
func (function): The function where the logging call was made. Defaults to None.
extra (dict): Additional information for the log record. Defaults to None.
sinfo (str): Stack trace information or None.
Returns:
@ -165,7 +168,7 @@ class Logger(logging.Logger):
**kwargs: Additional keyword arguments to configure the logger.
Returns:
Logger: The requested or newly created logger instance.
Logger: The requested or newly created logger instance.
"""
if name is None:
if LOGGER_DICT:

View file

@ -9,8 +9,14 @@ from memory_scope.utils.logger import Logger
class MemoryHandler(object):
"""
The `MemoryHandler` class manages memory nodes with memory store.
"""
def __init__(self):
"""
Initializes the MemoryHandler.
"""
self._memory_store: BaseMemoryStore | None = None
# dict: memory_id -> MemoryNode
@ -34,10 +40,21 @@ class MemoryHandler(object):
return self._memory_store
def clear(self):
"""
Clear all memory nodes cached, reset the class instance.
"""
self._id_memory_dict.clear()
self._key_id_dict.clear()
def add_memories(self, key: str, nodes: MemoryNode | List[MemoryNode], log_repeat: bool = True):
"""
Add the memories.
Args:
key (str): The key mapping to memory nodes.
nodes (List[MemoryNode]): A single memory node or a list of memory nodes to be updated.
log_repeat (bool): Log duplicated memory node or not.
"""
if key not in self._key_id_dict:
return self.set_memories(key, nodes, log_repeat)
@ -55,6 +72,13 @@ class MemoryHandler(object):
f"store_status={node.store_status} action_status={node.action_status}")
def set_memories(self, key: str, nodes: MemoryNode | List[MemoryNode], log_repeat: bool = True):
"""
Add the memories into '_id_memory_dict' and '_key_id_dict'.
Args:
key (str): The key mapping to memory nodes.
nodes (List[MemoryNode]): A single memory node or a list of memory nodes to be updated.
"""
if nodes is None:
nodes = []
elif isinstance(nodes, MemoryNode):
@ -74,6 +98,15 @@ class MemoryHandler(object):
self._key_id_dict[key] = [n.memory_id for n in nodes]
def get_memories(self, keys: str | List[str]) -> List[MemoryNode]:
"""
Fetch the memories by keys.
Args:
key (str): The key mapping to memory nodes.
Returns:
List[MemoryNode]: Memories mapped to the key.
"""
memories: Dict[str, MemoryNode] = {}
if isinstance(keys, str):
@ -93,6 +126,13 @@ class MemoryHandler(object):
return list(memories.values())
def delete_memories(self, nodes: MemoryNode | List[MemoryNode], key: str = None):
"""
Delete the memories.
Args:
key (str): The key mapping to memory nodes.
nodes (List[MemoryNode]): A single memory node or a list of memory nodes to be deleted.
"""
if isinstance(nodes, MemoryNode):
nodes = [nodes]
@ -111,6 +151,14 @@ class MemoryHandler(object):
id_list.remove(_id)
def update_memories(self, keys: str = "", nodes: MemoryNode | List[MemoryNode] = None):
"""
Update the memories.
Args:
keys (str): The memories.
nodes (List[MemoryNode]): A single memory node or a list of memory nodes to be updated.
:
"""
update_memories: Dict[str, MemoryNode] = {n.memory_id: n for n in self.get_memories(keys=keys)}
if nodes is not None:

View file

@ -108,7 +108,7 @@ class PromptHandler(object):
self._prompt_dict[key] = prompts.strip()
@property
def prompt_dict(self):
def prompt_dict(self) -> dict:
"""
Retrieves the internal dictionary containing all prompt messages.
@ -117,7 +117,7 @@ class PromptHandler(object):
"""
return self._prompt_dict
def __getitem__(self, key: str):
def __getitem__(self, key: str) -> str:
"""
Enables accessing prompt messages using dictionary-like indexing.
@ -139,7 +139,7 @@ class PromptHandler(object):
"""
self._prompt_dict[key] = value
def __getattr__(self, key: str):
def __getattr__(self, key: str) -> str:
"""
Overrides attribute access to provide prompt messages dynamically.

View file

@ -26,6 +26,16 @@ class Registry(object):
self.module_dict: Dict[str, Any] = {}
def register(self, module_name: str = None, module: Any = None):
"""
Registers module in the registry in a single call.
Args:
module_name (str): The name of module to be registered.
modules (List[Any] | Dict[str, Any]): The module to be registered.
Raises:
NotImplementedError: If the input is already registered.
"""
assert module is not None
if module_name is None:
module_name = module.__name__
@ -60,7 +70,7 @@ class Registry(object):
module_name (str): The name of the module to retrieve.
Returns:
The registered module corresponding to the given name.
A registered module corresponding to the given name.
Raises:
AssertionError: If the specified module is not found in the registry.

View file

@ -1,3 +1,4 @@
from typing import List
import re
from memory_scope.constants.language_constants import NONE_WORD
@ -7,7 +8,7 @@ from memory_scope.utils.logger import Logger
class ResponseTextParser(object):
"""
The `ResponseTextParser` class is designed to process and parse response texts. It provides methods to extract specific
The `ResponseTextParser` class is designed to parse and process response texts. It provides methods to extract specific
patterns from the text and filter out unnecessary information, while also logging the processing steps and outcomes.
"""
@ -23,7 +24,16 @@ class ResponseTextParser(object):
self.response_text: str = response_text.strip() # Strips leading and trailing whitespace from the response text
self.logger: Logger = Logger.get_logger() # Initializes a logger instance for logging parsing activities
def parse_v1(self, prefix: str = ""):
def parse_v1(self, prefix: str = "") -> List[str]:
"""
Extract specific patterns from the text which match content within angle brackets.
Args:
prefix (str): The prefix of log. Defaults to "".
Returns:
Contents match the specific patterns.
"""
result = []
for line in self.response_text.split("\n"):
line = line.strip()
@ -35,7 +45,16 @@ class ResponseTextParser(object):
self.logger.info(f"{prefix} response_text={self.response_text} result={result}", stacklevel=2)
return result
def parse_v2(self, prefix: str = ""):
def parse_v2(self, prefix: str = "") -> List[str]:
"""
Extract lines which contain NONE_WORD in Chinese or English.
Args:
prefix (str): The prefix of log. Defaults to "".
Returns:
Contents match the specific patterns.
"""
result = []
for line in self.response_text.split("\n"):
line = line.strip()

View file

@ -20,6 +20,18 @@ class Timer(object):
float_precision: int = 4,
**kwargs):
"""
Initializes the `Timer` instance with the provided args and sets up a logger
Args:
name (str): The log name.
time_log_type (str): The log type. Defaults to 'End'.
use_ms (bool): Use 'ms' as the time scale or not. Defaults to True.
stack_level (int): The stack level of log. Defaults to 2.
float_precision (int): The precision of cost time. Defaults to 4.
"""
self.name: str = name
self.time_log_type: TIME_LOG_TYPE = time_log_type
self.use_ms: bool = use_ms
@ -35,6 +47,9 @@ class Timer(object):
self.logger = Logger.get_logger()
def _set_cost(self):
"""
Accumulate the cost time.
"""
self.t_end = time.time()
self.cost = self.t_end - self.t_start
if self.use_ms:
@ -42,6 +57,9 @@ class Timer(object):
@property
def cost_str(self):
"""
Represent the cost time into a formatted string.
"""
self._set_cost()
if self.use_ms:
return f"cost={self.cost:.4f}ms"
@ -49,12 +67,18 @@ class Timer(object):
return f"cost={self.cost:.4f}s"
def __enter__(self, *args, **kwargs):
"""
Begin timing.
"""
self.t_start = time.time()
if self.time_log_type == "wrap":
self.logger.info(f"----- {self.name}.begin -----")
return self
def __exit__(self, *args, **kwargs):
"""
End timing and print the formatted log.
"""
if self.time_log_type == "none":
return

View file

@ -16,7 +16,17 @@ ALL_COLORS = ["red", "green", "yellow", "blue", "magenta", "cyan", "light_grey",
"light_yellow", "light_blue", "light_magenta", "light_cyan", "white"]
def underscore_to_camelcase(name: str, is_first_title: bool = True):
def underscore_to_camelcase(name: str, is_first_title: bool = True) -> str:
"""
Converts a underscore_notation string to CamelCase.
Args:
name (str): The underscore_notation string to be converted.
is_first_title (bool): Title the first word or not. Defaults to True
Returns:
str: A CamelCase formatted string.
"""
name_split = name.split("_")
if is_first_title:
return "".join(x.title() for x in name_split)
@ -24,7 +34,7 @@ def underscore_to_camelcase(name: str, is_first_title: bool = True):
return name_split[0] + ''.join(x.title() for x in name_split[1:])
def camelcase_to_underscore(name: str):
def camelcase_to_underscore(name: str) -> str:
"""
Converts a CamelCase string to underscore_notation.
@ -32,7 +42,7 @@ def camelcase_to_underscore(name: str):
name (str): The CamelCase formatted string to be converted.
Returns:
str: The converted string in underscore_notation.
str: A converted string in underscore_notation.
"""
return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
@ -61,7 +71,7 @@ def init_instance_by_config(config: dict,
**kwargs: Additional keyword arguments to pass to the class constructor.
Returns:
instance: An instance of the class initialized with the provided config and kwargs.
instance: An instance initialized with the provided config and kwargs.
"""
config_copy = deepcopy(config)
@ -99,7 +109,7 @@ def prompt_to_msg(system_prompt: str,
few_shot (str): An example or demonstration input, often used for illustrating expected behavior.
user_query (str): The actual user query or prompt to be processed.
concat_system_prompt(bool): Concat system prompt again or not in the user message.
A simple method to improve the effectiveness for some LLMs.
A simple method to improve the effectiveness for some LLMs. Defaults to True.
Returns:
List[Message]: A list of Message objects, each representing a part of the conversation setup.
@ -125,6 +135,17 @@ def prompt_to_msg(system_prompt: str,
def char_logo(words: str, seed: int = time.time_ns(), color=None):
"""
Render the context of logo with colors
Args:
words: The context of logo.
seed: The random seed which generates colors if there is no specific color. Defaults to the current timestamp.
color: The specific color. Defaults to None.
Returns:
A rendered logo
"""
font = pyfiglet.Figlet()
rendered_text = font.renderText(words)
colored_lines = []
@ -143,22 +164,22 @@ def char_logo(words: str, seed: int = time.time_ns(), color=None):
return colored_lines
def md5_hash(input_string: str):
def md5_hash(input_string: str) -> str:
"""
Computes the MD5 hash of a given input string.
Computes a MD5 hash of the given input string.
Args:
input_string (str): The string for which the MD5 hash needs to be computed.
Returns:
str: The hexadecimal representation of the MD5 hash.
str: A hexadecimal MD5 hash representation.
"""
m = hashlib.md5()
m.update(input_string.encode('utf-8'))
return m.hexdigest()
def contains_keyword(text, keywords):
def contains_keyword(text, keywords) -> bool:
"""
Checks if the given text contains any of the specified keywords, ignoring case.