From de6205d2fd9af54f4c5c8a092003391332abb829 Mon Sep 17 00:00:00 2001 From: "Yuyao Huang (Sam)" Date: Sat, 28 Mar 2026 12:33:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=91=BD=E4=BB=A4):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E6=A8=A1=E5=BC=8F=E5=92=8C=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 OrchestrationAgent 中添加 passthrough 状态管理 - 新增 /direct 和 /smart 命令用于切换模式 - 修改 /list 命令为 /status 并显示当前模式状态 - 更新帮助信息包含新模式命令 --- bot/commands.py | 39 ++++++++++++++++++++++++++++++++------- orchestrator/agent.py | 12 ++++++++++-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/bot/commands.py b/bot/commands.py index 93b6ac3..7f12d1f 100644 --- a/bot/commands.py +++ b/bot/commands.py @@ -45,8 +45,8 @@ async def handle_command(user_id: str, text: str) -> Optional[str]: if cmd in ("/new", "/n"): return await _cmd_new(user_id, args) - elif cmd in ("/list", "/ls", "/l"): - return await _cmd_list(user_id) + elif cmd in ("/status", "/list", "/ls", "/l"): + return await _cmd_status(user_id) elif cmd in ("/close", "/c"): return await _cmd_close(user_id, args) elif cmd in ("/switch", "/s"): @@ -55,6 +55,10 @@ async def handle_command(user_id: str, text: str) -> Optional[str]: return await _cmd_retry(user_id) elif cmd in ("/help", "/h", "/?"): return _cmd_help() + elif cmd == "/direct": + return _cmd_direct(user_id) + elif cmd == "/smart": + return _cmd_smart(user_id) else: return None @@ -107,18 +111,22 @@ async def _cmd_new(user_id: str, args: str) -> str: return result -async def _cmd_list(user_id: str) -> str: - """List all sessions for this user.""" +async def _cmd_status(user_id: str) -> str: + """Show status: sessions and current mode.""" sessions = manager.list_sessions(user_id=user_id) if not sessions: return "No active sessions." active = agent.get_active_conv(user_id) + passthrough = agent.get_passthrough(user_id) lines = ["**Your Sessions:**\n"] for i, s in enumerate(sessions, 1): marker = "→ " if s["conv_id"] == active else " " lines.append(f"{marker}{i}. `{s['conv_id']}` - `{s['cwd']}`") - lines.append("\nUse `/switch ` to activate a session.") + status = "Direct 🟢" if passthrough else "Smart ⚪" + lines.append(f"\n**Mode:** {status}") + lines.append("Use `/switch ` to activate a session.") + lines.append("Use `/direct` or `/smart` to change mode.") return "\n".join(lines) @@ -161,7 +169,7 @@ async def _cmd_switch(user_id: str, args: str) -> str: return "No sessions available." if not args: - return "Usage: /switch \n" + await _cmd_list(user_id) + return "Usage: /switch \n" + await _cmd_status(user_id) try: idx = int(args) - 1 @@ -180,12 +188,29 @@ async def _cmd_retry(user_id: str) -> str: return "Retry not yet implemented. Just send your message again." +def _cmd_direct(user_id: str) -> str: + """Enable direct mode - messages go straight to Claude Code.""" + conv = agent.get_active_conv(user_id) + if not conv: + return "No active session. Use `/new` or `/switch` first." + agent.set_passthrough(user_id, True) + return f"✓ Direct mode ON. Messages go directly to session `{conv}`." + + +def _cmd_smart(user_id: str) -> str: + """Enable smart mode - messages go through LLM for routing.""" + agent.set_passthrough(user_id, False) + return "✓ Smart mode ON. Messages go through LLM for intelligent routing." + + def _cmd_help() -> str: """Show help.""" return """**Commands:** /new [msg] [--timeout N] [--idle N] - Create session -/list - List your sessions +/status - Show sessions and current mode /close [n] - Close session (active or by number) /switch - Switch to session by number +/direct - Direct mode: messages → Claude Code (no LLM overhead) +/smart - Smart mode: messages → LLM routing (default) /retry - Retry last message /help - Show this help""" diff --git a/orchestrator/agent.py b/orchestrator/agent.py index 2e62071..a5ec9f2 100644 --- a/orchestrator/agent.py +++ b/orchestrator/agent.py @@ -74,6 +74,8 @@ class OrchestrationAgent: self._active_conv: Dict[str, Optional[str]] = defaultdict(lambda: None) # user_id -> asyncio.Lock (prevents concurrent processing per user) self._user_locks: Dict[str, asyncio.Lock] = defaultdict(asyncio.Lock) + # user_id -> passthrough mode enabled + self._passthrough: Dict[str, bool] = defaultdict(lambda: False) def _build_system_prompt(self, user_id: str) -> str: conv_id = self._active_conv[user_id] @@ -89,6 +91,12 @@ class OrchestrationAgent: def get_active_conv(self, user_id: str) -> Optional[str]: return self._active_conv.get(user_id) + def get_passthrough(self, user_id: str) -> bool: + return self._passthrough.get(user_id, False) + + def set_passthrough(self, user_id: str, enabled: bool) -> None: + self._passthrough[user_id] = enabled + async def run(self, user_id: str, text: str) -> str: """Process a user message and return the agent's reply.""" async with self._user_locks[user_id]: @@ -102,8 +110,8 @@ class OrchestrationAgent: logger.info(">>> user=...%s conv=%s msg=%r", short_uid, active_conv, text[:80]) logger.debug(" history_len=%d", len(self._history[user_id])) - # Passthrough mode: if active session, bypass LLM (bot commands handled earlier) - if active_conv: + # Passthrough mode: if enabled and active session, bypass LLM + if self._passthrough[user_id] and active_conv: try: reply = await manager.send(active_conv, text, user_id=user_id) logger.info("<<< [passthrough] reply: %r", reply[:120])