feat: 添加 Web 前端及服务端 SSE 流式支持,扩展多模型兼容

后端:
  - server: 实现完整的 HTTP 会话管理(CRUD)+ SSE 事件流推送,
    支持双通道架构(POST 发消息 + GET SSE 接收流式响应)
  - runtime: ContentBlock 新增 Thinking / RedactedThinking 变体,
    支持思考过程和已编辑思考的序列化/反序列化
  - api: 注册 GLM 系列模型(glm-4/5 等)到模型注册表,
    扩展 XAI/OpenAI 兼容提供商的请求构建逻辑

  前端:
  - 基于 Ant Design X 构建完整聊天界面:Bubble.List 消息列表、
    Sender 输入框、Conversations 会话管理、Think 思考过程折叠、
    ThoughtChain 工具调用链展示
  - XMarkdown 集成:代码高亮、Mermaid 图表、LaTeX 公式、
    自定义脚注、流式渲染(incomplete 占位符)
  - SSE Hook 对接服务端事件流,手动管理 AssistantBuffer 累积 delta
  - 深色/浅色主题切换,会话侧边栏(新建/切换/删除)
This commit is contained in:
fengmengqi
2026-04-10 16:29:27 +08:00
parent 0782159ecd
commit 4a04faf926
56 changed files with 8611 additions and 172 deletions
+55
View File
@@ -0,0 +1,55 @@
# 命令模块 (commands)
本模块负责定义和管理 Claw 交互界面中使用的“斜杠命令”(Slash Commands),并提供相关的解析和执行逻辑。
## 概览
`commands` 模块的主要职责包括:
- 定义所有可用的斜杠命令及其元数据(别名、说明、类别等)。
- 提供命令注册表 (`CommandRegistry`),用于在 CLI 中发现和分发命令。
- 实现复杂的管理命令,如插件管理 (`/plugins`)、智能体查看 (`/agents`) 和技能查看 (`/skills`)。
- 提供命令建议功能,支持基于编辑距离 (Levenshtein distance) 的模糊匹配。
## 关键特性
- **斜杠命令规范 (SlashCommandSpec)**:每个命令都包含详尽的元数据,包括所属类别(核心、工作区、会话、Git、自动化)以及是否支持在恢复会话时执行。
- **命令分类**
- **核心 (Core)**`/help`, `/status`, `/model`, `/permissions`, `/cost` 等。
- **工作区 (Workspace)**`/config`, `/memory`, `/diff`, `/teleport` 等。
- **会话 (Session)**`/clear`, `/resume`, `/export`, `/session` 等。
- **Git 交互**`/branch`, `/commit`, `/pr`, `/issue` 等。
- **自动化 (Automation)**`/plugins`, `/agents`, `/skills`, `/ultraplan` 等。
- **模糊匹配与建议**:当用户输入错误的命令时,系统会自动推荐最接近的合法命令。
- **插件集成**`/plugins` 命令允许用户动态安装、启用、禁用或卸载插件,并能通知运行时重新加载环境。
## 实现逻辑
### 核心模块
- **`lib.rs`**: 包含了绝大部分逻辑。
- **`SlashCommand` 枚举**: 定义了所有命令的强类型表示。
- **`SlashCommandSpec` 结构体**: 存储命令的静态配置信息。
- **`handle_plugins_slash_command`**: 处理复杂的插件管理工作流。
- **`suggest_slash_commands`**: 实现基于 Levenshtein 距离的建议算法。
### 工作流程
1. 用户在 REPL 中输入以 `/` 开头的字符串。
2. `claw-cli` 调用 `SlashCommand::parse` 进行解析。
3. 解析后的命令被分发到相应的处理器。
4. 处理结果(通常包含要显示给用户的消息,以及可选的会话更新或运行时重新加载请求)返回给 CLI。
## 使用示例 (内部)
```rust
use commands::{SlashCommand, suggest_slash_commands};
// 解析命令
if let Some(cmd) = SlashCommand::parse("/model sonnet") {
// 处理模型切换逻辑
}
// 获取建议
let suggestions = suggest_slash_commands("hpel", 3);
// 返回 ["/help"]
```
+3 -3
View File
@@ -603,7 +603,7 @@ pub fn suggest_slash_commands(input: &str, limit: usize) -> Vec<String> {
})
.collect::<Vec<_>>();
ranked.sort_by(|left, right| left.cmp(right));
ranked.sort();
ranked.dedup_by(|left, right| left.2 == right.2);
ranked
.into_iter()
@@ -842,7 +842,7 @@ pub fn handle_branch_slash_command(
Ok(if trimmed.is_empty() {
"Branch\n Result no branches found".to_string()
} else {
format!("Branch\n Result listed\n\n{}", trimmed)
format!("Branch\n Result listed\n\n{trimmed}")
})
}
Some("create") => {
@@ -882,7 +882,7 @@ pub fn handle_worktree_slash_command(
Ok(if trimmed.is_empty() {
"Worktree\n Result no worktrees found".to_string()
} else {
format!("Worktree\n Result listed\n\n{}", trimmed)
format!("Worktree\n Result listed\n\n{trimmed}")
})
}
Some("add") => {