diff --git a/agents.md b/agents.md index 5521a95..3956be6 100644 --- a/agents.md +++ b/agents.md @@ -41,36 +41,43 @@ ## 当前任务 -### v0.1.0 目标 +### v0.2.0 目标 -实现流式输出功能: -1. 创建 go.mod 配置依赖 -2. 实现 main.go 入口 -3. 实现流式 Provider 调用 -4. 实时打印 token -5. 处理非流式 Provider 回退 -6. Markdown 终端渲染(glamour) +实现 TTS 语音朗读功能: +1. 集成 mimo-tts client(TCP 连接本地 daemon) +2. 添加配置文件 TTS 开关 +3. 实现命令行切换(/tts on/off/status) +4. 实现临时 TTS 前缀(`T 消息`) +5. 动态提示符显示状态(👀 🔊) --- ## 实现进度 -### v0.1.0 已完成功能 +### v0.2.0 已完成功能 -1. **流式输出(新流程)** +1. **TTS 语音朗读** + - 集成 mimo-tts client(TCP 连接) + - 配置文件开关(tts.enabled) + - 命令行切换(/tts on/off/status) + - 临时 TTS 前缀(`T 消息`) + - 动态提示符显示状态(👀 🔊) + - 静默失败处理(网络异常时仅记录日志) + +2. **流式输出(新流程)** - 等待 AI 返回完整响应 - Markdown 转译 - 模拟流式输出(从配置读取速度) - 效果更好,无残留问题 -2. **Markdown 渲染** +3. **Markdown 渲染** - 使用 glamour 库渲染 Markdown - 支持多种主题(dark, light, dracula, tokyo-night 等) - 通过 project.config.yml 配置主题 -3. **项目配置** +4. **项目配置** - 通过 project.config.yml 统一管理配置项 - - 支持流式速度、渲染主题、Logo 等配置 + - 支持流式速度、渲染主题、Logo、TTS 等配置 --- @@ -85,7 +92,8 @@ # 模拟流式输出配置 streaming: - simulated_speed_ms: 30 # 模拟流式输出速度(毫秒/字符) + line_delay_ms: 1000 # 每行输出后的延迟(毫秒) + last_line_delay_ms: 600 # 最后一行延迟(毫秒) # Markdown 渲染配置 markdown: @@ -94,6 +102,13 @@ markdown: # UI 配置 ui: logo: "🦐" + user_prefix: "👀 " + +# TTS 语音配置 +tts: + enabled: false # 全局开关(默认关闭) + port: 9876 # mimo-tts daemon 端口 + auto: true # AI 回复后自动朗读 ``` 配置加载优先级: @@ -102,6 +117,31 @@ ui: --- +## TTS 使用指南 + +### 命令 + +| 输入 | 行为 | +|------|------| +| `/tts` | 切换 TTS 开关 | +| `/tts on` | 开启 TTS | +| `/tts off` | 关闭 TTS | +| `/tts status` | 显示 TTS 状态 | +| `T 消息` | 临时开启 TTS 并发送消息 | + +### 动态提示符 + +- 关闭:`👀 ` +- 开启:`👀 🔊 ` + +### 注意事项 + +- 需要先安装并启动 mimo-tts daemon:`mimo-tts daemon start` +- TTS 服务端地址:本地 9876 端口(默认) +- 网络异常时会静默失败,仅记录日志 + +--- + ## 依赖管理 ### Go 依赖 @@ -111,26 +151,28 @@ ui: - `charm.land/x/term` - 终端控制 - `github.com/muesli/termenv` - 终端环境工具 - `gopkg.in/yaml.v3` - 配置文件解析 +- `github.com/ergochat/readline` - 终端输入 ### 配置文件 - `cmd/hxclaw/main.go` - 主入口逻辑 - `cmd/hxclaw/internal/markdown.go` - Markdown 渲染器 -- `cmd/hxclaw/internal/helpers.go` - 辅助函数 +- `cmd/hxclaw/internal/helpers.go` - 辅助函数(Readline) - `cmd/hxclaw/internal/config.go` - 项目配置加载 +- `cmd/hxclaw/internal/tts.go` - TTS 客户端 --- ## 已知问题 -1. **重绘残留**:某些情况下有轻微文本重复(可接受) +1. **重绘残留**:某些情况下有轻微文本重复(已通过新流程解决) 2. **终端兼容性**:termenv 在某些终端可能不完全工作 --- ## 待优化 -1. 优化重绘逻辑,解决残留问题(已通过新流程解决) +1. 打印和 TTS 朗读同时进行(而非先打印完再读) 2. 添加更多主题支持 3. 添加命令行参数支持主题选择 diff --git a/taolun.md b/taolun.md index 8bc970a..3a38d29 100644 --- a/taolun.md +++ b/taolun.md @@ -46,8 +46,8 @@ hxclaw/ ### 6. Markdown 终端渲染 - 使用 charmbracelet 家族库 +- glamour:Markdown 渲染(自带代码高亮) - lipgloss:终端样式 -- glow:代码高亮 - 流程:Markdown → ANSI 转义序列 → 终端显示 ### 7. 部署方式 @@ -330,7 +330,7 @@ func outputLineByLine(text string) { # 模拟流式输出配置 streaming: line_delay_ms: 1000 # 每行输出后的延迟(毫秒) - last_line_delay_ms: 600 # 最后一行延迟(毫秒) + last_line_delay_ms: 600 # 最后一行延迟(毫秒) # Markdown 渲染配置 markdown: @@ -341,6 +341,12 @@ markdown: ui: logo: "🦐" # Logo user_prefix: "👀 " # 用户输入前缀 + +# TTS 语音配置 +tts: + enabled: false # 全局开关(默认关闭) + port: 9876 # daemon 端口 + auto: true # AI 回复后自动朗读 ``` #### 配置加载优先级 @@ -381,4 +387,72 @@ func getConfigPath() string { - 人眼需要约 30-50ms 才能感知单次视觉变化 - 空白字符不应逐个输出,应批量处理 -- 终端宽度 100% 时 Markdown 渲染会显著增加行数和字符数 \ No newline at end of file +- 终端宽度 100% 时 Markdown 渲染会显著增加行数和字符数 + +--- + +### 19. TTS 语音朗读集成 + +#### 架构设计 + +hxclaw 作为 mimo-tts 的客户端,通过 TCP Socket 连接本地 daemon: + +``` +hxclaw (客户端) --TCP:9876--> mimo-tts daemon (服务端) + | + v + API 调用 (mimo-v2.5-tts) + | + v + 返回音频文件路径 + | + v + afplay 播放 +``` + +#### 配置文件 + +```yaml +tts: + enabled: false # 全局开关(默认关闭) + port: 9876 # daemon 端口 + auto: true # AI 回复后自动朗读 +``` + +#### 命令支持 + +| 输入 | 行为 | +|------|------| +| `/tts` | 切换 TTS 开关 | +| `/tts on` | 开启 TTS | +| `/tts off` | 关闭 TTS | +| `/tts status` | 显示状态 | +| `T 消息` | 临时开启并发送 | + +#### 动态提示符 + +- 关闭:`👀 ` +- 开启:`👀 🔊 ` + +#### 实现要点 + +1. TCP 连接:使用 Go 标准库 `net` 包 +2. JSON 序列化:发送请求格式 `{text, voice, format}` +3. 异步播放:使用 `go func()` 异步调用 afplay +4. 静默失败:网络异常只记录警告日志,不阻塞用户 + +#### 踩坑记录 + +**ergochat/readline SetPrompt 无返回值** + +```go +// 错误 +func (r *Readline) SetPrompt(prompt string) error { + return r.rl.SetPrompt(prompt) // SetPrompt 返回 void +} + +// 正确 +func (r *Readline) SetPrompt(prompt string) { + r.rl.SetPrompt(prompt) // void 类型 +} +``` \ No newline at end of file