feat: 实现多主机架构的核心组件
新增路由器、主机客户端和共享协议模块,支持多主机部署模式: - 路由器作为中央节点管理主机连接和消息路由 - 主机客户端作为工作节点运行本地代理 - 共享协议定义通信消息格式 - 新增独立运行模式standalone.py - 更新配置系统支持路由模式
This commit is contained in:
@@ -67,6 +67,8 @@ async def handle_command(user_id: str, text: str) -> Optional[str]:
|
||||
return await _cmd_shell(args)
|
||||
elif cmd == "/remind":
|
||||
return await _cmd_remind(args)
|
||||
elif cmd in ("/nodes", "/node"):
|
||||
return await _cmd_nodes(user_id, args)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -278,6 +280,39 @@ async def _cmd_remind(args: str) -> str:
|
||||
return f"⏰ Reminder #{job_id} set for {value}{unit} from now"
|
||||
|
||||
|
||||
async def _cmd_nodes(user_id: str, args: str) -> str:
|
||||
"""List nodes or switch active node."""
|
||||
from config import ROUTER_MODE
|
||||
if not ROUTER_MODE:
|
||||
return "Not in router mode. Run standalone.py for multi-host support."
|
||||
|
||||
from router.nodes import get_node_registry
|
||||
registry = get_node_registry()
|
||||
|
||||
if args:
|
||||
args = args.strip()
|
||||
if registry.set_active_node(user_id, args):
|
||||
return f"✓ Active node set to: {args}"
|
||||
return f"Error: Node '{args}' not found"
|
||||
|
||||
nodes = registry.list_nodes()
|
||||
if not nodes:
|
||||
return "No nodes connected."
|
||||
|
||||
active_node_id = None
|
||||
active_node = registry.get_active_node(user_id)
|
||||
if active_node:
|
||||
active_node_id = active_node.node_id
|
||||
|
||||
lines = ["**Connected Nodes:**\n"]
|
||||
for n in nodes:
|
||||
marker = "→ " if n["node_id"] == active_node_id else " "
|
||||
status = "🟢" if n["status"] == "online" else "🔴"
|
||||
lines.append(f"{marker}{n['display_name']} {status} sessions={n['sessions']}")
|
||||
lines.append("\nUse `/node <name>` to switch active node.")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _cmd_help() -> str:
|
||||
"""Show help."""
|
||||
return """**Commands:**
|
||||
@@ -290,5 +325,7 @@ def _cmd_help() -> str:
|
||||
/shell <cmd> - Run shell command (bypasses LLM)
|
||||
/remind <time> <msg> - Set reminder (e.g. /remind 10m check build)
|
||||
/tasks - List background tasks
|
||||
/nodes - List connected host nodes
|
||||
/node <name> - Switch active node
|
||||
/retry - Retry last message
|
||||
/help - Show this help"""
|
||||
|
||||
+42
-4
@@ -86,7 +86,7 @@ def _handle_message(data: P2ImMessageReceiveV1) -> None:
|
||||
|
||||
|
||||
async def _process_message(user_id: str, chat_id: str, text: str) -> None:
|
||||
"""Process message: check allowlist, then commands, then agent."""
|
||||
"""Process message: check allowlist, then commands, then route to node or local agent."""
|
||||
try:
|
||||
set_current_chat(chat_id)
|
||||
|
||||
@@ -96,10 +96,48 @@ async def _process_message(user_id: str, chat_id: str, text: str) -> None:
|
||||
return
|
||||
|
||||
reply = await handle_command(user_id, text)
|
||||
if reply is None:
|
||||
if reply is not None:
|
||||
if reply:
|
||||
await send_text(chat_id, "chat_id", reply)
|
||||
return
|
||||
|
||||
from config import ROUTER_MODE
|
||||
if ROUTER_MODE:
|
||||
from router.routing_agent import route
|
||||
from router.rpc import forward
|
||||
from router.nodes import get_node_registry
|
||||
|
||||
node_id, reason = await route(user_id, chat_id, text)
|
||||
|
||||
if node_id is None:
|
||||
await send_text(chat_id, "chat_id", f"No host available: {reason}")
|
||||
return
|
||||
|
||||
if node_id == "meta":
|
||||
registry = get_node_registry()
|
||||
nodes = registry.list_nodes()
|
||||
if nodes:
|
||||
lines = ["Connected Nodes:"]
|
||||
for n in nodes:
|
||||
marker = " → " if n.get("node_id") == registry.get_active_node(user_id) else " "
|
||||
lines.append(f"{marker}{n['display_name']} sessions={n['sessions']} {n['status']}")
|
||||
lines.append("\nUse \"/node <name>\" to switch active node.")
|
||||
await send_text(chat_id, "chat_id", "\n".join(lines))
|
||||
else:
|
||||
await send_text(chat_id, "chat_id", "No nodes connected.")
|
||||
return
|
||||
|
||||
try:
|
||||
reply = await forward(node_id, user_id, chat_id, text)
|
||||
if reply:
|
||||
await send_text(chat_id, "chat_id", reply)
|
||||
except Exception as e:
|
||||
logger.exception("Failed to forward to node %s", node_id)
|
||||
await send_text(chat_id, "chat_id", f"Error communicating with node: {e}")
|
||||
else:
|
||||
reply = await agent.run(user_id, text)
|
||||
if reply:
|
||||
await send_text(chat_id, "chat_id", reply)
|
||||
if reply:
|
||||
await send_text(chat_id, "chat_id", reply)
|
||||
except Exception:
|
||||
logger.exception("Error processing message for user %s", user_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user