新增路由器、主机客户端和共享协议模块,支持多主机部署模式: - 路由器作为中央节点管理主机连接和消息路由 - 主机客户端作为工作节点运行本地代理 - 共享协议定义通信消息格式 - 新增独立运行模式standalone.py - 更新配置系统支持路由模式
95 lines
2.4 KiB
Python
95 lines
2.4 KiB
Python
"""Shared protocol module for Router <-> Host Client communication.
|
|
|
|
All message types are dataclasses that serialize to/from JSON.
|
|
Both router and host client import from this module.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from dataclasses import asdict, dataclass, field
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
@dataclass
|
|
class RegisterMessage:
|
|
"""Host client -> Router: Register this node."""
|
|
type: str = "register"
|
|
node_id: str = ""
|
|
serves_users: List[str] = field(default_factory=list)
|
|
working_dir: str = ""
|
|
capabilities: List[str] = field(default_factory=list)
|
|
display_name: str = ""
|
|
|
|
|
|
@dataclass
|
|
class ForwardRequest:
|
|
"""Router -> Host client: Forward a user message."""
|
|
type: str = "forward"
|
|
id: str = ""
|
|
user_id: str = ""
|
|
chat_id: str = ""
|
|
text: str = ""
|
|
|
|
|
|
@dataclass
|
|
class ForwardResponse:
|
|
"""Host client -> Router: Reply to a forwarded message."""
|
|
type: str = "forward_response"
|
|
id: str = ""
|
|
reply: str = ""
|
|
error: str = ""
|
|
|
|
|
|
@dataclass
|
|
class TaskComplete:
|
|
"""Host client -> Router: Background task finished."""
|
|
type: str = "task_complete"
|
|
task_id: str = ""
|
|
user_id: str = ""
|
|
chat_id: str = ""
|
|
result: str = ""
|
|
|
|
|
|
@dataclass
|
|
class Heartbeat:
|
|
"""Bidirectional ping/pong."""
|
|
type: str = "ping"
|
|
|
|
|
|
@dataclass
|
|
class NodeStatus:
|
|
"""Host client -> Router: Periodic status update."""
|
|
type: str = "node_status"
|
|
node_id: str = ""
|
|
sessions: int = 0
|
|
active_sessions: List[Dict[str, Any]] = field(default_factory=list)
|
|
|
|
|
|
MESSAGE_TYPES = {
|
|
"register": RegisterMessage,
|
|
"forward": ForwardRequest,
|
|
"forward_response": ForwardResponse,
|
|
"task_complete": TaskComplete,
|
|
"ping": Heartbeat,
|
|
"pong": Heartbeat,
|
|
"node_status": NodeStatus,
|
|
}
|
|
|
|
|
|
def encode(msg: Any) -> str:
|
|
"""Encode a message to JSON string."""
|
|
if hasattr(msg, "type"):
|
|
return json.dumps(asdict(msg), ensure_ascii=False)
|
|
raise ValueError(f"Invalid message type: {type(msg)}")
|
|
|
|
|
|
def decode(data: str) -> Any:
|
|
"""Decode a JSON string to a message object."""
|
|
obj = json.loads(data)
|
|
msg_type = obj.get("type")
|
|
if msg_type not in MESSAGE_TYPES:
|
|
raise ValueError(f"Unknown message type: {msg_type}")
|
|
cls = MESSAGE_TYPES[msg_type]
|
|
return cls(**{k: v for k, v in obj.items() if k in cls.__dataclass_fields__})
|