新增元宝(Yuanbao)渠道的完整适配器实现,包含以下核心模块: - 基础适配器与导出入口 - 协议编解码与WebSocket帧处理 - 会话管理与路由逻辑 - 事件队列与出站消息队列 - 消息格式转换与发送重试 - 安全审计与权限校验 - 配置映射与账户管理 - 视觉分析与工具函数 - 文档生成与设置向导
227 lines
6.3 KiB
Python
227 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
COMMAND_DEFINITIONS: list[dict[str, Any]] = [
|
|
{
|
|
"name": "help",
|
|
"description": "显示可用命令列表和使用说明",
|
|
"usage": "/help [命令名]",
|
|
},
|
|
{
|
|
"name": "status",
|
|
"description": "显示当前机器人的运行状态和会话信息",
|
|
"usage": "/status",
|
|
},
|
|
{
|
|
"name": "new",
|
|
"description": "开启一个新的对话会话",
|
|
"usage": "/new",
|
|
},
|
|
{
|
|
"name": "stop",
|
|
"description": "停止当前对话中正在进行的任务",
|
|
"usage": "/stop",
|
|
},
|
|
{
|
|
"name": "restart",
|
|
"description": "重新启动当前对话会话",
|
|
"usage": "/restart",
|
|
},
|
|
{
|
|
"name": "compact",
|
|
"description": "压缩当前对话历史,释放上下文空间",
|
|
"usage": "/compact",
|
|
},
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class CommandResult:
|
|
command: str
|
|
action: str
|
|
response_text: str | None = None
|
|
metadata: dict[str, Any] = field(default_factory=dict)
|
|
|
|
|
|
@dataclass
|
|
class NativeCommandContext:
|
|
user_id: str
|
|
chat_id: str
|
|
chat_type: str
|
|
thread_key: str | None = None
|
|
|
|
adapter_status: str = "connected"
|
|
ws_connected: bool = False
|
|
|
|
history_length: int = 0
|
|
agent_id: str = "default"
|
|
|
|
|
|
def parse_command(content: str) -> tuple[str | None, str | None]:
|
|
content = content.strip()
|
|
if not content.startswith("/"):
|
|
return None, None
|
|
|
|
parts = content.split(maxsplit=1)
|
|
command = parts[0][1:]
|
|
args = parts[1] if len(parts) > 1 else None
|
|
return command, args
|
|
|
|
|
|
async def handle_command(
|
|
command: str,
|
|
args: str | None,
|
|
ctx: NativeCommandContext,
|
|
) -> CommandResult:
|
|
handlers = {
|
|
"help": _handle_help,
|
|
"status": _handle_status,
|
|
"new": _handle_new,
|
|
"stop": _handle_stop,
|
|
"restart": _handle_restart,
|
|
"compact": _handle_compact,
|
|
}
|
|
|
|
handler = handlers.get(command)
|
|
if handler is None:
|
|
return CommandResult(
|
|
command=command,
|
|
action="unknown",
|
|
response_text=(f"未知命令: /{command}\n输入 /help 查看可用命令列表"),
|
|
)
|
|
|
|
try:
|
|
return await handler(args, ctx)
|
|
except Exception as e:
|
|
logger.error(f"[Yuanbao] Command handler error for /{command}: {e}")
|
|
return CommandResult(
|
|
command=command,
|
|
action="error",
|
|
response_text=f"执行命令 /{command} 时出错: {e}",
|
|
)
|
|
|
|
|
|
async def _handle_help(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
if args:
|
|
for cmd in COMMAND_DEFINITIONS:
|
|
if cmd["name"] == args:
|
|
return CommandResult(
|
|
command="help",
|
|
action="help_detail",
|
|
response_text=(f"**/{cmd['name']}**\n{cmd['description']}\n用法: `{cmd['usage']}`"),
|
|
)
|
|
return CommandResult(
|
|
command="help",
|
|
action="help_not_found",
|
|
response_text=f"未找到命令: /{args}",
|
|
)
|
|
|
|
lines = ["**可用命令列表:**\n"]
|
|
for cmd in COMMAND_DEFINITIONS:
|
|
lines.append(f"- **/{cmd['name']}** — {cmd['description']}")
|
|
lines.append("\n输入 `/help 命令名` 查看详细用法")
|
|
|
|
return CommandResult(
|
|
command="help",
|
|
action="help_list",
|
|
response_text="\n".join(lines),
|
|
)
|
|
|
|
|
|
async def _handle_status(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
lines = [
|
|
"**机器人状态:**",
|
|
f"- 连接状态: {ctx.adapter_status}",
|
|
f"- WebSocket: {'已连接' if ctx.ws_connected else '未连接'}",
|
|
f"- 对话类型: {ctx.chat_type}",
|
|
f"- 当前 Agent: {ctx.agent_id}",
|
|
f"- 历史消息数: {ctx.history_length}",
|
|
]
|
|
return CommandResult(
|
|
command="status",
|
|
action="status_report",
|
|
response_text="\n".join(lines),
|
|
)
|
|
|
|
|
|
async def _handle_new(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
return CommandResult(
|
|
command="new",
|
|
action="new_session",
|
|
response_text="已开启新的对话会话。",
|
|
)
|
|
|
|
|
|
async def _handle_stop(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
return CommandResult(
|
|
command="stop",
|
|
action="stop_task",
|
|
response_text="已请求停止当前任务。",
|
|
metadata={"request_stop": True},
|
|
)
|
|
|
|
|
|
async def _handle_restart(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
return CommandResult(
|
|
command="restart",
|
|
action="restart_session",
|
|
response_text="正在重新启动对话会话...",
|
|
metadata={"request_restart": True},
|
|
)
|
|
|
|
|
|
async def _handle_compact(args: str | None, ctx: NativeCommandContext) -> CommandResult:
|
|
return CommandResult(
|
|
command="compact",
|
|
action="compact_history",
|
|
response_text="已请求压缩对话历史,释放上下文空间。",
|
|
metadata={"request_compact": True},
|
|
)
|
|
|
|
|
|
async def sync_commands_menu(
|
|
api_base: str,
|
|
token: str,
|
|
http_client,
|
|
commands: list[dict[str, Any]] | None = None,
|
|
) -> bool:
|
|
if commands is None:
|
|
commands = COMMAND_DEFINITIONS
|
|
|
|
try:
|
|
import aiohttp
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
payload = {
|
|
"commands": [
|
|
{
|
|
"command": cmd["name"],
|
|
"description": cmd["description"],
|
|
}
|
|
for cmd in commands
|
|
]
|
|
}
|
|
async with http_client.post(
|
|
f"{api_base}/api/v1/bot/commands",
|
|
json=payload,
|
|
headers=headers,
|
|
timeout=aiohttp.ClientTimeout(total=10),
|
|
) as resp:
|
|
if resp.status in (200, 201, 204):
|
|
logger.info(f"[Yuanbao] Commands menu synced ({len(commands)} commands)")
|
|
return True
|
|
else:
|
|
body = await resp.text()
|
|
logger.warning(f"[Yuanbao] Failed to sync commands menu: HTTP {resp.status} {body}")
|
|
return False
|
|
except Exception as e:
|
|
logger.warning(f"[Yuanbao] Commands menu sync failed: {e}")
|
|
return False
|