From a8604af78f0437a885644f6ff889fba77e78c1f0 Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Sun, 15 Feb 2026 13:29:46 +0800 Subject: [PATCH] feat(cli): enable vector search and improve chat history management --- docs/make_mp4.md | 19 +++++++ reme/agent/chat/fs_cli.py | 71 ++++++++++++++++++-------- reme/agent/fs/fs_summarizer.py | 2 +- reme/config/cli.yaml | 2 +- reme/core/utils/logo_utils.py | 3 +- reme/reme_cli.py | 15 ++++-- reme/tool/search/dashscope_search.yaml | 4 +- 7 files changed, 85 insertions(+), 31 deletions(-) create mode 100644 docs/make_mp4.md diff --git a/docs/make_mp4.md b/docs/make_mp4.md new file mode 100644 index 00000000..c5b5d869 --- /dev/null +++ b/docs/make_mp4.md @@ -0,0 +1,19 @@ +```shell +ffmpeg -i /Users/yuli/Desktop/remecli_en.mov \ + -vf "scale=-2:1080,setpts=0.333*PTS" \ + -c:v libx264 \ + -crf 28 \ + -preset fast \ + -c:a aac \ + -b:a 96k \ + /Users/yuli/Desktop/remecli_en_1080p_3x.mp4 + +ffmpeg -i /Users/yuli/Desktop/remecli_zh.mov \ + -vf "scale=-2:1080,setpts=0.333*PTS" \ + -c:v libx264 \ + -crf 28 \ + -preset fast \ + -c:a aac \ + -b:a 96k \ + /Users/yuli/Desktop/remecli_zh_1080p_3x.mp4 +``` \ No newline at end of file diff --git a/reme/agent/chat/fs_cli.py b/reme/agent/chat/fs_cli.py index 53076add..2ee1f352 100644 --- a/reme/agent/chat/fs_cli.py +++ b/reme/agent/chat/fs_cli.py @@ -1,5 +1,6 @@ """FsCli system prompt""" +import asyncio from datetime import datetime from pathlib import Path @@ -8,6 +9,7 @@ from loguru import logger from ...core.enumeration import Role, ChunkEnum from ...core.op import BaseReactStream from ...core.schema import Message, StreamChunk +from ...core.utils import format_messages from ...tool.fs import BashTool, LsTool, ReadTool, WriteTool, EditTool @@ -31,18 +33,23 @@ class FsCli(BaseReactStream): self.messages: list[Message] = [] self.previous_summary: str = "" + self.summary_tasks: list[asyncio.Task] = [] - async def reset(self) -> str: - """Reset conversation history using summary. + def add_summary_task(self, messages: list[Message]): + """Add summary task to queue.""" + remaining_tasks = [] + for task in self.summary_tasks: + if task.done(): + exc = task.exception() + if exc is not None: + logger.exception(f"Summary task failed: {exc}") + else: + result = task.result() + logger.info(f"Summary task completed: {result}") + else: + remaining_tasks.append(task) + self.summary_tasks = remaining_tasks - Summarizes current messages to memory files and clears history. - """ - if not self.messages: - self.messages.clear() - self.previous_summary = "" - return "No history to reset." - - # Import required modules from ..fs import FsSummarizer # Summarize current conversation and save to memory files @@ -59,14 +66,30 @@ class FsCli(BaseReactStream): language=self.language, ) - result = await summarizer.call( - messages=self.messages, - date=current_date, - service_context=self.service_context, + summary_task = asyncio.create_task( + summarizer.call( + messages=messages, + date=current_date, + service_context=self.service_context, + ), ) + self.summary_tasks.append(summary_task) + + async def new(self) -> str: + """Reset conversation history using summary. + + Summarizes current messages to memory files and clears history. + """ + if not self.messages: + self.messages.clear() + self.previous_summary = "" + return "No history to reset." + + self.add_summary_task(self.messages) + self.messages.clear() self.previous_summary = "" - return f"History saved to memory files and reset. Result: {result.get('answer', 'Done')}" + return "History saved to memory files and reset." async def context_check(self) -> dict: """Check if messages exceed token limits.""" @@ -104,20 +127,16 @@ class FsCli(BaseReactStream): tokens_before = cut_result.get("token_count", 0) if force_compact: - # Force compact: summarize all messages, leave only summary messages_to_summarize = self.messages turn_prefix_messages = [] left_messages = [] elif not cut_result.get("needs_compaction", False): - # No compaction needed return "History is within token limits, no compaction needed." else: - # Normal compaction: use cut point result messages_to_summarize = cut_result.get("messages_to_summarize", []) turn_prefix_messages = cut_result.get("turn_prefix_messages", []) left_messages = cut_result.get("left_messages", []) - # Step 2: Generate summary via Compactor compactor = FsCompactor(language=self.language) summary_content = await compactor.call( messages_to_summarize=messages_to_summarize, @@ -126,14 +145,22 @@ class FsCli(BaseReactStream): service_context=self.service_context, ) - # Step 3: Call reset_history to save and clear - reset_result = await self.reset() + self.add_summary_task(messages=messages_to_summarize) # Step 4: Assemble final messages self.messages = left_messages self.previous_summary = summary_content - return f"History compacted from {tokens_before} tokens. {reset_result}" + return f"History compacted from {tokens_before} tokens." + + def format_history(self) -> str: + """Format history messages.""" + return format_messages( + messages=self.messages, + add_index=False, + add_reasoning=False, + strip_markdown_headers=False, + ) async def build_messages(self) -> list[Message]: """Build system prompt message.""" diff --git a/reme/agent/fs/fs_summarizer.py b/reme/agent/fs/fs_summarizer.py index 91ff0510..38df4fb9 100644 --- a/reme/agent/fs/fs_summarizer.py +++ b/reme/agent/fs/fs_summarizer.py @@ -13,7 +13,7 @@ from ...core.utils import format_messages class FsSummarizer(BaseReact): """Retrieve personal memories through vector search and history reading.""" - def __init__(self, working_dir: str, memory_dir: str = "memory", version: str = "default", **kwargs): + def __init__(self, working_dir: str, memory_dir: str = "memory", version: str = "v1", **kwargs): super().__init__(**kwargs) self.working_dir: str = working_dir self.memory_dir: str = memory_dir diff --git a/reme/config/cli.yaml b/reme/config/cli.yaml index be5a00cd..0a2cc382 100644 --- a/reme/config/cli.yaml +++ b/reme/config/cli.yaml @@ -19,7 +19,7 @@ memory_stores: store_name: reme embedding_model: default fts_enabled: true - vector_enabled: false + vector_enabled: true file_watchers: default: diff --git a/reme/core/utils/logo_utils.py b/reme/core/utils/logo_utils.py index 5b4acd8e..d85a2073 100644 --- a/reme/core/utils/logo_utils.py +++ b/reme/core/utils/logo_utils.py @@ -81,4 +81,5 @@ def print_logo(service_config: "ServiceConfig"): expand=False, ) - Console().print(Group("\n", panel, "\n"), justify="center") + # use justify="center" to adjust position + Console().print(Group("\n", panel, "\n")) diff --git a/reme/reme_cli.py b/reme/reme_cli.py index 14f2267a..0d6b1b8d 100644 --- a/reme/reme_cli.py +++ b/reme/reme_cli.py @@ -39,8 +39,10 @@ class ReMeCli(ReMeFs): "/help": "Show help.", } - async def chat_with_remy(self, tool_result_max_size: int = 100, language: str = "zh", **kwargs): + async def chat_with_remy(self, tool_result_max_size: int = 100, **kwargs): """Interactive CLI chat with Remy using simple streaming output.""" + language = self.service_config.language + print(f"ReMe language={language}") tools: list[BaseTool] = [ FsMemorySearch(vector_weight=self.vector_weight, candidate_multiplier=self.candidate_multiplier), BashTool(cwd=self.working_dir), @@ -53,10 +55,10 @@ class ReMeCli(ReMeFs): tavily_api_key: str = os.getenv("TAVILY_API_KEY", "") dashscope_api_key: str = os.getenv("DASHSCOPE_API_KEY", "") if tavily_api_key: - tools.append(TavilySearch(name="web_search")) + tools.append(TavilySearch(name="web_search", language=language)) print("find tavily_api_key, append Tavily search tool") elif dashscope_api_key: - tools.append(DashscopeSearch(name="web_search")) + tools.append(DashscopeSearch(name="web_search", language=language)) print("find dashscope_api_key, append Dashscope search tool") else: print("No Tavily or Dashscope API key found, skip Tavily and Dashscope search tool") @@ -108,7 +110,7 @@ class ReMeCli(ReMeFs): break if user_input == "/new": - result = await fs_cli.reset() + result = await fs_cli.new() print(f"{result}\nConversation reset\n") continue @@ -117,6 +119,11 @@ class ReMeCli(ReMeFs): print(f"{result}\nHistory compacted.\n") continue + if user_input == "/history": + result = fs_cli.format_history() + print(f"Formated History:\n{result}\n") + continue + if user_input == "/clear": fs_cli.messages.clear() print("History cleared.\n") diff --git a/reme/tool/search/dashscope_search.yaml b/reme/tool/search/dashscope_search.yaml index 0dcef347..5a3dade5 100644 --- a/reme/tool/search/dashscope_search.yaml +++ b/reme/tool/search/dashscope_search.yaml @@ -10,11 +10,11 @@ role_prompt: | {query} # task - Extract the original content related to the user's query directly from the context, maintain accuracy, and avoid excessive processing. + Return all the original search results directly without processing. role_prompt_zh: | # 用户问题 {query} # task - 直接从上下文中提取与用户问题相关的原始内容,保持准确性,避免过度处理。 \ No newline at end of file + 直接返回所有的原始搜索结果,不要处理 \ No newline at end of file