ReMe/reme/reme_cli.py
zouyingcao e5bb845196
refactor(cli): using AgentScope components to reimplement the reme_cli logic (#153)
* add: as_token_counters config for reme_cli

* add: reme_cli function

* update: format the terminal printing for reme_cli

* update: check for pre-commit

* single quotes for the inner dictionary keys

* update the usage of get_std_logger for pre-commit

* update the usage of get_std_logger for pre-commit

* add 'console_enabled' param in compactor&summarizer
2026-03-12 11:23:36 +08:00

192 lines
6.8 KiB
Python

"""ReMe File System"""
import asyncio
import sys
from pathlib import Path
from prompt_toolkit import PromptSession
from .config import ReMeConfigParser
from .core import Application
from .core.utils import play_horse_easter_egg
from .memory.file_based.components import CliAgent
class ReMeCli(Application):
"""ReMe Cli"""
def __init__(
self,
*args,
working_dir: str = ".reme",
config_path: str = "cli",
enable_logo: bool = True,
log_to_console: bool = True,
llm_api_key: str | None = None,
llm_base_url: str | None = None,
embedding_api_key: str | None = None,
embedding_base_url: str | None = None,
default_as_llm_config: dict | None = None,
default_embedding_model_config: dict | None = None,
default_file_store_config: dict | None = None,
default_token_counter_config: dict | None = None,
default_file_watcher_config: dict | None = None,
context_window_tokens: int = 128000,
reserve_tokens: int = 36000,
keep_recent_tokens: int = 20000,
vector_weight: float = 0.7,
candidate_multiplier: float = 3.0,
**kwargs,
):
"""Initialize ReMe with config."""
working_path = Path(working_dir)
working_path.mkdir(parents=True, exist_ok=True)
memory_path = working_path / "memory"
memory_path.mkdir(parents=True, exist_ok=True)
self.working_dir: str = str(working_path.absolute())
default_file_watcher_config = default_file_watcher_config or {}
if not default_file_watcher_config.get("watch_paths", None):
default_file_watcher_config["watch_paths"] = [
str(working_path / "MEMORY.md"),
str(working_path / "memory.md"),
str(memory_path),
]
super().__init__(
*args,
llm_api_key=llm_api_key,
llm_base_url=llm_base_url,
embedding_api_key=embedding_api_key,
embedding_base_url=embedding_base_url,
working_dir=working_dir,
config_path=config_path,
enable_logo=enable_logo,
log_to_console=log_to_console,
parser=ReMeConfigParser,
default_as_llm_config=default_as_llm_config,
default_embedding_model_config=default_embedding_model_config,
default_file_store_config=default_file_store_config,
default_token_counter_config=default_token_counter_config,
default_file_watcher_config=default_file_watcher_config,
**kwargs,
)
self.service_config.metadata.setdefault("context_window_tokens", context_window_tokens)
self.service_config.metadata.setdefault("reserve_tokens", reserve_tokens)
self.service_config.metadata.setdefault("keep_recent_tokens", keep_recent_tokens)
self.service_config.metadata.setdefault("vector_weight", vector_weight)
self.service_config.metadata.setdefault("candidate_multiplier", candidate_multiplier)
self.commands = {
"/new": "Create a new conversation.",
"/compact": "Compact messages into a summary.",
"/exit": "Exit the application.",
"/clear": "Clear the history.",
"/help": "Show help.",
"/horse": "A surprise.",
}
async def chat_with_remy(self, **kwargs):
"""Interactive CLI chat with Remy using simple streaming output."""
language = self.service_config.language
print(f"ReMe language={language or 'default'}")
cli_agent = CliAgent(
vector_weight=self.service_config.metadata["vector_weight"],
candidate_multiplier=self.service_config.metadata["candidate_multiplier"],
context_window_tokens=self.service_config.metadata["context_window_tokens"],
reserve_tokens=self.service_config.metadata["reserve_tokens"],
keep_recent_tokens=self.service_config.metadata["keep_recent_tokens"],
working_dir=self.working_dir,
language=language,
**kwargs,
)
session = PromptSession()
# Print welcome banner
print("\n========================================")
print(" Welcome to Remy Chat!")
print("========================================\n")
while True:
try:
# Get user input (async)
user_input = await session.prompt_async("You: ")
user_input = user_input.strip()
if not user_input:
continue
# Handle commands
if user_input == "/exit":
break
if user_input == "/new":
result = await cli_agent.new()
print(f"{result}\nConversation reset\n")
continue
if user_input == "/compact":
result = await cli_agent.compact(force_compact=True)
print(f"{result}\nHistory compacted.\n")
continue
if user_input == "/history":
result = cli_agent.format_history()
print(f"Formated History:\n{result}\n")
continue
if user_input == "/clear":
cli_agent.messages.clear()
print("History cleared.\n")
continue
if user_input == "/help":
print("\nCommands:")
for command, description in self.commands.items():
print(f" {command}: {description}")
continue
if user_input == "/horse":
play_horse_easter_egg()
continue
try:
await cli_agent.call(
query=user_input,
service_context=self.service_context,
)
except Exception as e:
print(f"\nStream error: {e}")
# End current streaming line
print("\n")
print("----------------------------------------\n")
except EOFError:
break
except KeyboardInterrupt:
print("\nInterrupted.")
break
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
print("\nGoodbye!\n")
async def async_main():
"""Main function for testing the ReMeFs CLI."""
async with ReMeCli(*sys.argv[1:], log_to_console=False) as reme:
await reme.chat_with_remy()
def main():
"""Main function for testing the ReMeFs CLI."""
asyncio.run(async_main())
if __name__ == "__main__":
main()