""" MaiSaka CLI and conversation loop. """ from datetime import datetime import asyncio from rich import box from rich.panel import Panel from rich.text import Text from src.chat.heart_flow.heartflow_manager import heartflow_manager from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver from src.chat.message_receive.chat_manager import BotChatSession, chat_manager from src.chat.message_receive.message import SessionMessage from src.common.data_models.mai_message_data_model import MessageInfo, UserInfo from src.common.data_models.message_component_data_model import MessageSequence, TextComponent from src.config.config import config_manager, global_config from .maisaka_cli_sender import CLI_PLATFORM_NAME from .console import console from .input_reader import InputReader class BufferCLI: """Maisaka 命令行交互入口。""" _CLI_PLATFORM = CLI_PLATFORM_NAME _CLI_USER_ID = "maisaka_user" def __init__(self) -> None: self._reader = InputReader() self._message_receiver = HeartFCMessageReceiver() self._session: BotChatSession | None = None @staticmethod def _get_current_model_name() -> str: """读取当前 planner 模型名。""" try: model_task_config = config_manager.get_model_config().model_task_config if model_task_config.planner.model_list: return model_task_config.planner.model_list[0] except Exception: pass return "未配置" def _show_banner(self) -> None: """渲染启动横幅。""" banner = Text() banner.append("MaiSaka", style="bold cyan") banner.append(" v2.0\n", style="muted") banner.append(f"模型: {self._get_current_model_name()}\n", style="muted") banner.append("输入内容开始对话 | Ctrl+C 退出", style="muted") console.print(Panel(banner, box=box.DOUBLE_EDGE, border_style="cyan", padding=(1, 2))) console.print() @staticmethod def _build_cli_session_message( *, user_text: str, timestamp: datetime, ) -> SessionMessage: """构造一条供 heartflow 复用的 CLI 用户消息。""" message = SessionMessage( message_id=f"maisaka_cli_{int(timestamp.timestamp() * 1000)}", timestamp=timestamp, platform=BufferCLI._CLI_PLATFORM, ) user_name = global_config.maisaka.cli_user_name.strip() or "用户" message.message_info = MessageInfo( user_info=UserInfo( user_id=BufferCLI._CLI_USER_ID, user_nickname=user_name, user_cardname=None, ), group_info=None, additional_config={}, ) message.raw_message = MessageSequence([TextComponent(text=user_text)]) message.processed_plain_text = user_text message.display_message = user_text message.initialized = True return message async def _dispatch_input(self, user_text: str) -> None: """将 CLI 输入转发到 heartflow 路径。""" message = self._build_cli_session_message( user_text=user_text, timestamp=datetime.now(), ) chat_manager.register_message(message) self._session = await chat_manager.get_or_create_session( platform=self._CLI_PLATFORM, user_id=self._CLI_USER_ID, ) await self._message_receiver.process_message(message) async def run(self) -> None: """主交互循环。""" self._reader.start(asyncio.get_event_loop()) self._show_banner() try: while True: console.print("[bold cyan]> [/bold cyan]", end="") raw_input = await self._reader.get_line() if raw_input is None: console.print("\n[muted]再见[/muted]") break user_text = raw_input.strip() if not user_text: continue await self._dispatch_input(user_text) finally: if self._session is not None: runtime = heartflow_manager.heartflow_chat_list.pop(self._session.session_id, None) if runtime is not None: await runtime.stop()