ReMe/reme/memory/file_based/components/context_checker.py
jinliyl 9a6cf2b994
Dev/token (#159)
* update

* refactor(memory): remove unnecessary type check and update error logging

* refactor(core): standardize logger import and update agentscope dependency

* fix(memory): disable console output and add logging for summarizer component

* feat(core): replace OpenAI token counter with custom ReMe token counter

- Replace OpenAITokenCounter with ReMeTokenCounter implementation
- Add support for HuggingFace mirror and configurable tokenizer
- Register ReMeTokenCounter as default token counter in registry
- Update config to use hf backend with Qwen2.5-7B-Instruct model

refactor(memory): convert token counting methods to async in message handlers

- Change count_str_token, stat_message, count_msgs_token to async methods
- Update format_msgs_to_str and context_check to use async token counting
- Modify _format_tool_result_output to support async token counting
- Adjust all dependent methods to await async token counting calls

feat(memory): add dialog persistence to in-memory storage

- Implement _append_messages_to_dialog for saving messages to JSONL files
- Add dialog_path parameter to ReMeInMemoryMemory constructor
- Persist messages to daily JSONL files based on timestamp grouping
- Update mark_messages_compressed to save and remove compressed messages
- Modify clear_content to persist all messages before clearing memory

refactor(ops): update token counter type hints and initialization

- Change BaseOp to use HuggingFaceTokenCounter instead of TokenCounterBase
- Update type annotations for as_token_counter property and parameters
- Remove direct token counter injection from Compactor and ContextChecker
- Pass as_token_counter parameter through service context mechanism

style(logging): improve error logging with exception details

- Replace logger.error with logger.exception in browser control tool
- Change logger.error to logger.exception in memory get tool error handling
- Add proper exception logging with stack trace information

chore(config): add token counter configuration to light YAML

- Add as_token_counters section with default hf backend configuration
- Configure Qwen/Qwen2.5-7B-Instruct model with mirror support enabled
- Set up pretrained_model_name_or_path and use_mirror parameters

test(context): update context check tests to async implementation

- Convert verify_context_check_invariants to async function
- Update context check test methods to use async calls
- Change stat_message calls to await async implementation
- Modify test_empty_messages and test_below_threshold_returns_all to async

* feat(core): implement context checking and memory management features

* refactor(core): replace direct loguru import with logger utility function

* refactor(reme): remove RuntimeContext dependency and simplify context checking

* feat(docs): add raw conversation persistence to ReMe framework
2026-03-17 11:07:31 +08:00

92 lines
3.5 KiB
Python

"""ContextChecker module for checking context size and splitting messages."""
from agentscope.message import Msg
from ..utils import AsMsgHandler
from ....core.op import BaseOp
from ....core.utils import get_logger
logger = get_logger()
class ContextChecker(BaseOp):
"""
ContextChecker class for checking context size and splitting messages.
This class analyzes conversation messages to determine if the context
exceeds the specified token threshold and splits messages into two groups:
those that should be compacted and those to keep in context.
Attributes:
memory_compact_threshold (int): Token count threshold for triggering compaction.
memory_compact_reserve (int): Token count to reserve for recent messages.
"""
def __init__(
self,
memory_compact_threshold: int,
memory_compact_reserve: int = 10000,
**kwargs,
):
"""
Initialize the ContextChecker.
Args:
memory_compact_threshold (int): Token count threshold for triggering
compaction. Messages exceeding this threshold will be split.
memory_compact_reserve (int): Token count to reserve for recent messages
to keep in context. Defaults to 10000 tokens.
**kwargs: Additional keyword arguments passed to BaseOp.
"""
super().__init__(**kwargs)
self.memory_compact_threshold: int = memory_compact_threshold
self.memory_compact_reserve: int = memory_compact_reserve
assert self.memory_compact_threshold > self.memory_compact_reserve
async def execute(self) -> tuple[list[Msg], list[Msg], bool]:
"""
Execute context check and split messages.
Retrieves messages from context and checks if they exceed the token
threshold. If so, splits them into messages to compact and messages
to keep.
Context Parameters:
messages (list[Msg]): List of conversation messages to check.
Retrieved from self.context.get("messages", []).
Returns:
tuple[list[Msg], list[Msg], bool]: A tuple containing:
- messages_to_compact (list[Msg]): Older messages that should
be compacted/summarized.
- messages_to_keep (list[Msg]): Recent messages to keep in context.
- is_valid (bool): True if the split is valid (tool calls aligned),
False if splitting would break conversation integrity.
Note:
- Returns ([], messages, True) if no compaction is needed.
- Ensures conversation pairs (user-assistant) are not split.
- is_valid=False indicates tool_use and tool_result are misaligned.
"""
messages: list[Msg] = self.context.get("messages", [])
if not messages:
logger.info("ContextChecker: No messages to check.")
return [], [], True
msg_handler = AsMsgHandler(self.as_token_counter)
messages_to_compact, messages_to_keep, is_valid = await msg_handler.context_check(
messages=messages,
memory_compact_threshold=self.memory_compact_threshold,
memory_compact_reserve=self.memory_compact_reserve,
)
if messages_to_compact:
logger.info(
f"ContextChecker Result: "
f"to_compact={len(messages_to_compact)}, "
f"to_keep={len(messages_to_keep)}, "
f"is_valid={is_valid}",
)
return messages_to_compact, messages_to_keep, is_valid