From d42c70f5ff61e696c991676ac997d0b1a21e4f6b Mon Sep 17 00:00:00 2001 From: titor Date: Sat, 11 Apr 2026 22:32:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=8A=9F=E8=83=BD=20v0.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 hxclaw 项目,基于 picoclaw 的 CLI 增强工具 - 实现流式输出,使用 fmt.Print + os.Stdout.Sync() 实时刷新 - 解决 onChunk 回调累积文本导致的重复输出问题 - 使用 strings.Builder 收集完整响应并保存到 session - 添加讨论记录和更新日志文档 --- .gitignore | 14 ++ agents.md | 60 ++++++++ changelog.md | 242 +++++++++++++++++++++++++++++++++ cmd/hxclaw/internal/helpers.go | 120 ++++++++++++++++ cmd/hxclaw/main.go | 186 +++++++++++++++++++++++++ go.mod | 77 +++++++++++ go.sum | 184 +++++++++++++++++++++++++ taolun.md | 141 +++++++++++++++++++ 8 files changed, 1024 insertions(+) create mode 100644 .gitignore create mode 100644 agents.md create mode 100644 changelog.md create mode 100644 cmd/hxclaw/internal/helpers.go create mode 100644 cmd/hxclaw/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 taolun.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8084426 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +*.ini + +hxclaw.exe + +.idea/ +.fleet/ +.vs/ +.vscode/ +.zed/ + +# hxclaw specific +bin/ +.hxclaw_history diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..df676d7 --- /dev/null +++ b/agents.md @@ -0,0 +1,60 @@ +# hxclaw AI 行为指南 + +## 开发规范 + +### 设计原则 + +- **面向对象 + 设计模式**:所有功能拆分成模块,模块可复用 +- **模块化**:每个功能独立成包,包内高内聚,包间低耦合 +- **设计模式**:常用单例、工厂、策略模式 + +### 代码要求 + +- **注释**:全局使用中文注释,注释内容详细,说明意图和实现逻辑 +- **测试**:所有代码需编写单元测试和功能测试,通过后才可交付 +- **命名**:变量、函数、文件名使用英文,注释使用中文 + +### 交流规范 + +- **语言**:全程使用中文回答和思考 +- **问题处理**:一个问题若超过3次尝试仍无法解决,立即停止,告诉用户遇到的问题,询问用户接下来怎么办 +- **进度同步**:每次开始编写代码前,更新讨论记录和其他文档 + +--- + +## 项目背景 + +### 定位 + +- hxclaw 是 picoclaw 的 CLI 增强工具 +- 提供流式输出和 Markdown 终端渲染 +- 作为独立二进制,与 picoclaw 共存 + +### 技术栈 + +- 语言:Go 1.21+ +- 依赖:通过 go.mod replace 复用 picoclaw +- 终端库:charmbracelet/lipgloss +- 测试:Go 标准测试框架 + +--- + +## 当前任务 + +### v0.1.0 目标 + +实现流式输出功能: +1. 创建 go.mod 配置依赖 +2. 实现 main.go 入口 +3. 实现流式 Provider 调用 +4. 实时打印 token +5. 处理非流式 Provider 回退 + +--- + +## 注意事项 + +- 不要修改 picoclaw 源码 +- 保持代码独立,便于后续版本同步 +- 优先实现核心功能,再考虑增强功能 +- 文档和代码同步更新 \ No newline at end of file diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..32af91d --- /dev/null +++ b/changelog.md @@ -0,0 +1,242 @@ +# hxclaw 更新日志 + +## 版本记录 + +### v0.1.0 (规划中) + +- 创建 hxclaw 项目 +- 实现流式输出功能 +- Markdown 渲染功能(待实现) +- 代码高亮功能(待实现) + +--- + +## 待实现功能 + +### v0.1.0 (当前) + +- [x] 流式输出功能 + - [x] 导入 picoclaw 核心库 + - [x] 实现流式 Provider 调用 + - [x] 实时打印 token + - [x] 处理非流式 Provider 回退 + +### v0.2.0 (计划) + +- [ ] Markdown 渲染 + - [ ] Markdown 解析 + - [ ] 基础样式(粗体、斜体、链接) + - [ ] 代码块渲染 + - [ ] 表格渲染 + - [ ] 列表渲染 + +### v0.3.0 (计划) + +- [ ] 代码高亮 + - [ ] 集成 glow 或类似库 + - [ ] 支持常见语言语法高亮 + +--- + +## 目前进度 + +- [x] 创建项目目录结构 +- [x] 编写讨论记录(taolun.md) +- [x] 编写更新日志(changelog.md) +- [x] 编写 AI 行为指南(agents.md) +- [x] 创建 go.mod +- [x] 实现 main.go 入口 +- [x] 实现流式输出核心逻辑 +- [x] 编译成功,生成 hxclaw 二进制 + +--- + +## 认知纠正(踩坑记录) + +### Go replace 机制不需要发布到 registry + +**问题**:最初担心需要像 npm 那样发布到 registry 才能被其他项目引用 + +**纠正**:Go 的 replace 机制可以直接指向: +- 本地路径(如 `../picoclaw`) +- GitHub 仓库 + tag(如 `github.com/sipeed/picoclaw v0.2.4`) + +**知识点**:Go 模块不需要发布到任何 registry,GitHub 就是事实上的 registry + +--- + +### hxclaw 不需要实现全部 picoclaw 功能 + +**问题**:最初担心需要自己实现 onboard、tools、mcp 等全部功能 + +**纠正**:hxclaw 是 CLI 增强层,只替换交互逻辑。picoclaw 的核心功能(agent loop、tools、mcp、skills)通过导入其 pkg 即可复用 + +**知识点**:采用组合优于继承的设计,需要什么功能就导入对应的包 + +--- + +### 流式输出需要判断 Provider 是否支持 + +**问题**:不是所有 Provider 都支持流式输出 + +**纠正**:需要使用类型断言判断 Provider 是否实现 `providers.StreamingProvider` 接口: +```go +if sp, ok := provider.(providers.StreamingProvider); ok { + // 使用 ChatStream +} else { + // 使用普通 Chat +} +``` + +**知识点**:picoclaw 的 Provider 设计使用了接口分离原则,流式是可选能力 + +--- + +### 终端渲染使用 charmbracelet 库 + +**问题**:如何实现 Markdown 终端渲染 + +**纠正**:使用 charmbracelet 家族: +- lipgloss:样式定义 +- glow:代码高亮 + +**知识点**:charmbracelet 是 Go 终端UI 的事实标准,API 设计优雅 + +--- + +### 独立二进制部署方式 + +**问题**:hxclaw 和 picoclaw 的关系 + +**纠正**:hxclaw 作为独立二进制,用户可以同时保留两个命令: +- `picoclaw agent` 使用原版 +- `hxclaw` 使用增强版 + +**知识点**:通过 go.mod replace 实现依赖绑定,用户无需安装 picoclaw 源码 + +--- + +### AgentRegistry 没有 BuildMessages 方法 + +**问题**:最初尝试调用 agentLoop.GetRegistry().BuildMessages() 构建消息 + +**纠正**:BuildMessages 属于 ContextBuilder,不是 AgentRegistry: +```go +// 正确方式 +agentInstance.ContextBuilder.BuildMessages(history, summary, input, media, channel, chatID, senderID, senderDisplayName) +``` + +**知识点**:picoclaw 代码结构中,ContextBuilder 负责消息构建,AgentRegistry 负责 agent 管理 + +--- + +### ToolDefinitions 获取方式 + +**问题**:如何获取可用的工具定义列表 + +**纠正**:通过 ToolRegistry 的 ToProviderDefs 方法: +```go +toolDefs := agentInstance.Tools.ToProviderDefs() +``` + +**知识点**:ToolRegistry 维护工具注册,ToProviderDefs 转换为 provider 可用的格式 + +--- + +### 流式输出实时刷新 + +**问题**:流式输出时字符不是实时显示,要等很久才一次性出现 + +**纠正**:在 onChunk 回调中添加 `os.Stdout.Sync()` 强制刷新 stdout: +```go +func(token string) { + fmt.Print(token) + os.Stdout.Sync() // 强制刷新 +} +``` + +**知识点**:Go 的 `fmt.Print` 使用缓冲输出,需要手动刷新才能实时显示 + +--- + +### Session 历史消息获取 + +**问题**:如何获取会话历史用于流式调用 + +**纠正**:通过 `SessionStore` 接口: +```go +history := agentInstance.Sessions.GetHistory(sessionKey) +summary := agentInstance.Sessions.GetSummary(sessionKey) +``` + +**知识点**:`AgentInstance.Sessions` 实现了 `SessionStore` 接口,支持 `GetHistory` 和 `GetSummary` 方法 + +--- + +### 流式调用后的消息保存 + +**问题**:流式调用绕过了 agent loop,消息没有保存到 session + +**纠正**:流式调用后手动保存消息: +```go +agentInstance.Sessions.AddMessage(sessionKey, "user", input) +agentInstance.Sessions.AddMessage(sessionKey, "assistant", result) +``` + +**知识点**:`SessionStore` 接口提供 `AddMessage` 方法,支持 "user" 和 "assistant" 角色 + +--- + +### onChunk 回调接收累积文本导致重复输出 + +**问题**:picoclaw 的 `StreamingProvider` 接口定义: +```go +onChunk func(accumulated string) +``` + +注释说明:"onChunk receives the accumulated text so far (not individual deltas)"。每次回调时参数是累积的完整文本(如 "你好" → "你好!再次" → "你好!再次见到"),而不是增量。 + +**纠正**:使用 `printedLen` 跟踪已打印位置,只打印新增部分: +```go +var printedLen int +func(accumulated string) { + if len(accumulated) > printedLen { + fmt.Print(accumulated[printedLen:]) + printedLen = len(accumulated) + } +} +``` + +**知识点**:picoclaw 故意设计为累积文本,这样可以在任意时刻获取完整内容用于调试 + +--- + +### 尝试 uilive 库但只显示最后一行 + +**问题**:为了实现同行流动效果,尝试使用 `github.com/gosuri/uilive` 库 + +**现象**:该库会覆盖每一行,只显示最后一行内容 + +**纠正**:移除 uilive,直接使用 `fmt.Print` + `os.Stdout.Sync()`,让终端自然处理换行 + +**知识点**:uilive 适用于进度条等场景,不适合长文本流式输出 + +--- + +### 流式输出期望同行流动但实际换行显示 + +**问题**:用户期望像 ollama 那样在同行逐字符流动 + +**最终方案**: +```go +fmt.Print(accumulated[printedLen:]) +os.Stdout.Sync() +``` + +效果: +- 字符串自然累积增长 +- 终端自动处理换行(满一行自动 wrap) +- 保留所有历史输出 +- 每次刷新缓冲区确保立即显示 + +**知识点**:最简单的方案就是最有效的方案,不需要额外库 \ No newline at end of file diff --git a/cmd/hxclaw/internal/helpers.go b/cmd/hxclaw/internal/helpers.go new file mode 100644 index 0000000..864e74f --- /dev/null +++ b/cmd/hxclaw/internal/helpers.go @@ -0,0 +1,120 @@ +package internal + +import ( + "bufio" + "errors" + "io" + "os" + "path/filepath" + + "github.com/ergochat/readline" + + "github.com/sipeed/picoclaw/pkg/config" + "github.com/sipeed/picoclaw/pkg/logger" +) + +// 错误定义 +var ( + ErrInterrupt = errors.New("interrupt") + ErrEOF = errors.New("EOF") +) + +// Logo 是 hxclaw 的 Logo +const Logo = "🦐" + +// GetPicoclawHome 返回 picoclaw 的家目录 +// 优先级: $PICOCLAW_HOME > ~/.picoclaw +func GetPicoclawHome() string { + return config.GetHome() +} + +// LoadConfig 加载配置文件 +// 复用 picoclaw 的配置加载逻辑 +func LoadConfig() (*config.Config, error) { + cfg, err := config.LoadConfig(GetConfigPath()) + if err != nil { + return nil, err + } + logger.SetLevelFromString(cfg.Gateway.LogLevel) + return cfg, nil +} + +// GetConfigPath 获取配置文件路径 +func GetConfigPath() string { + if configPath := os.Getenv(config.EnvConfig); configPath != "" { + return configPath + } + return filepath.Join(GetPicoclawHome(), "config.json") +} + +// Readline 实例包装 +type Readline struct { + rl *readline.Instance +} + +// NewReadline 创建一个新的 Readline 实例 +func NewReadline(prompt string) (*Readline, error) { + // 确保历史文件目录存在 + historyDir := filepath.Dir(filepath.Join(GetPicoclawHome(), ".hxclaw_history")) + os.MkdirAll(historyDir, 0755) + + rl, err := readline.NewEx(&readline.Config{ + Prompt: prompt, + HistoryFile: filepath.Join(GetPicoclawHome(), ".hxclaw_history"), + HistoryLimit: 100, + InterruptPrompt: "^C", + EOFPrompt: "exit", + }) + if err != nil { + return nil, err + } + return &Readline{rl: rl}, nil +} + +// Readline 读取一行输入 +func (r *Readline) Readline() (string, error) { + line, err := r.rl.Readline() + if err != nil { + if err == readline.ErrInterrupt { + return "", ErrInterrupt + } + if err == io.EOF { + return "", ErrEOF + } + return "", err + } + return line, nil +} + +// Close 关闭 Readline 实例 +func (r *Readline) Close() error { + return r.rl.Close() +} + +// SimpleReader 简单输入读取器(无历史记录) +type SimpleReader struct { + reader *bufio.Reader +} + +// NewSimpleReader 创建一个新的简单读取器 +func NewSimpleReader() *SimpleReader { + return &SimpleReader{ + reader: bufio.NewReader(os.Stdin), + } +} + +// ReadString 读取一行输入 +func (r *SimpleReader) ReadString() (string, error) { + line, err := r.reader.ReadString('\n') + if err != nil { + if err == io.EOF { + return "", ErrEOF + } + return "", err + } + // 去掉换行符 + if len(line) > 0 && line[len(line)-1] == '\n' { + line = line[:len(line)-1] + } + return line, nil +} diff --git a/cmd/hxclaw/main.go b/cmd/hxclaw/main.go new file mode 100644 index 0000000..4e96ca8 --- /dev/null +++ b/cmd/hxclaw/main.go @@ -0,0 +1,186 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/hxclaw/hxclaw/cmd/hxclaw/internal" + "github.com/sipeed/picoclaw/pkg/agent" + "github.com/sipeed/picoclaw/pkg/bus" + "github.com/sipeed/picoclaw/pkg/logger" + "github.com/sipeed/picoclaw/pkg/providers" +) + +const Logo = "🦐" + +func main() { + fmt.Printf("%s HxClaw - PicoClaw 增强版 CLI\n\n", Logo) + + cfg, err := internal.LoadConfig() + if err != nil { + fmt.Fprintf(os.Stderr, "错误:加载配置失败: %v\n", err) + os.Exit(1) + } + + logger.ConfigureFromEnv() + + provider, modelID, err := providers.CreateProvider(cfg) + if err != nil { + fmt.Fprintf(os.Stderr, "错误:创建 Provider 失败: %v\n", err) + os.Exit(1) + } + + if modelID != "" { + cfg.Agents.Defaults.ModelName = modelID + } + + msgBus := bus.NewMessageBus() + defer msgBus.Close() + + agentLoop := agent.NewAgentLoop(cfg, msgBus, provider) + defer agentLoop.Close() + + startupInfo := agentLoop.GetStartupInfo() + logger.InfoCF("hxclaw", "HxClaw 已初始化", + map[string]any{ + "tools_count": startupInfo["tools"].(map[string]any)["count"], + "skills_total": startupInfo["skills"].(map[string]any)["total"], + "skills_available": startupInfo["skills"].(map[string]any)["available"], + }) + + fmt.Printf("%s Interactive mode (Ctrl+C to exit)\n\n", Logo) + interactiveMode(agentLoop, "cli:default") +} + +func interactiveMode(agentLoop *agent.AgentLoop, sessionKey string) { + prompt := fmt.Sprintf("%s You: ", Logo) + + rl, err := internal.NewReadline(prompt) + if err != nil { + fmt.Printf("初始化 readline 失败: %v\n", err) + fmt.Println("回退到简单输入模式...") + simpleInteractiveMode(agentLoop, sessionKey) + return + } + defer rl.Close() + + for { + line, err := rl.Readline() + if err != nil { + if err == internal.ErrInterrupt || err == internal.ErrEOF { + fmt.Println("\n再见!") + return + } + fmt.Printf("读取输入错误: %v\n", err) + continue + } + + input := line + if input == "" { + continue + } + + if input == "exit" || input == "quit" { + fmt.Println("再见!") + return + } + + runWithStreaming(agentLoop, input, sessionKey) + } +} + +func simpleInteractiveMode(agentLoop *agent.AgentLoop, sessionKey string) { + reader := internal.NewSimpleReader() + for { + fmt.Print(fmt.Sprintf("%s You: ", Logo)) + line, err := reader.ReadString() + if err != nil { + if err == internal.ErrEOF { + fmt.Println("\n再见!") + return + } + fmt.Printf("读取输入错误: %v\n", err) + continue + } + + input := line + if input == "" { + continue + } + + if input == "exit" || input == "quit" { + fmt.Println("再见!") + return + } + + runWithStreaming(agentLoop, input, sessionKey) + } +} + +// runWithStreaming 尝试使用流式输出,如果 Provider 不支持则回退到普通模式 +func runWithStreaming(agentLoop *agent.AgentLoop, input, sessionKey string) { + agentInstance := agentLoop.GetRegistry().GetDefaultAgent() + if agentInstance == nil { + fmt.Println("错误:无法获取 Agent 实例") + return + } + + provider := agentInstance.Provider + ctx := context.Background() + + // 判断是否支持流式 + if sp, ok := provider.(providers.StreamingProvider); ok { + // 从 session 中获取历史消息 + history := agentInstance.Sessions.GetHistory(sessionKey) + summary := agentInstance.Sessions.GetSummary(sessionKey) + + // 使用 ContextBuilder 构建消息,包含历史 + messages := agentInstance.ContextBuilder.BuildMessages( + history, + summary, + input, + nil, // media + "cli", // channel + sessionKey, + "", // senderID + "", // senderDisplayName + ) + + // 获取工具定义 + toolDefs := agentInstance.Tools.ToProviderDefs() + + fmt.Print("\n") + var result strings.Builder + var printedLen int + _, err := sp.ChatStream(ctx, messages, toolDefs, agentInstance.Model, nil, func(accumulated string) { + if len(accumulated) > printedLen { + fmt.Print(accumulated[printedLen:]) + os.Stdout.Sync() + result.WriteString(accumulated[printedLen:]) + printedLen = len(accumulated) + } + }) + fmt.Println() + + if err != nil { + fmt.Printf("流式调用错误: %v\n", err) + return + } + + // 将用户消息和回复保存到 session + if result.Len() > 0 { + agentInstance.Sessions.AddMessage(sessionKey, "user", input) + agentInstance.Sessions.AddMessage(sessionKey, "assistant", result.String()) + } + } else { + // 回退到普通模式 + response, err := agentLoop.ProcessDirect(ctx, input, sessionKey) + if err != nil { + fmt.Printf("错误: %v\n", err) + return + } + fmt.Printf("\n%s %s\n\n", Logo, response) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7540346 --- /dev/null +++ b/go.mod @@ -0,0 +1,77 @@ +module github.com/hxclaw/hxclaw + +go 1.25.9 + +require ( + github.com/ergochat/readline v0.1.3 + github.com/sipeed/picoclaw v0.0.0 +) + +require ( + github.com/adhocore/gronx v1.19.6 // indirect + github.com/anthropics/anthropic-sdk-go v1.26.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect + github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect + github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect + github.com/aws/smithy-go v1.24.2 // indirect + github.com/caarlos0/env/v11 v11.4.0 // indirect + github.com/creack/pty v1.1.24 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/github/copilot-sdk/go v0.2.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab // indirect + github.com/google/jsonschema-go v0.4.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gosuri/uilive v0.0.4 // indirect + github.com/h2non/filetype v1.1.3 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modelcontextprotocol/go-sdk v1.5.0 // indirect + github.com/ncruces/go-strftime v1.0.0 // indirect + github.com/openai/openai-go/v3 v3.22.0 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtp v1.10.1 // indirect + github.com/pion/webrtc/v3 v3.3.6 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/rs/zerolog v1.35.0 // indirect + github.com/segmentio/asm v1.1.3 // indirect + github.com/segmentio/encoding v0.5.4 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.2.0 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/term v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/libc v1.70.0 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.11.0 // indirect + modernc.org/sqlite v1.48.2 // indirect +) + +// 开发时指向本地 picoclaw +replace github.com/sipeed/picoclaw => /Users/titor/picoclaw diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b3373c7 --- /dev/null +++ b/go.sum @@ -0,0 +1,184 @@ +github.com/adhocore/gronx v1.19.6 h1:5KNVcoR9ACgL9HhEqCm5QXsab/gI4QDIybTAWcXDKDc= +github.com/adhocore/gronx v1.19.6/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg= +github.com/anthropics/anthropic-sdk-go v1.26.0 h1:oUTzFaUpAevfuELAP1sjL6CQJ9HHAfT7CoSYSac11PY= +github.com/anthropics/anthropic-sdk-go v1.26.0/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q= +github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= +github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= +github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI= +github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo= +github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.4 h1:W6tKfa/s37faUnwJ71pGqsBO7/wfUX1L7tVprupQGo4= +github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.4/go.mod h1:BZ+9thH0QOTDUwE8KAv/ZwUzsNC7CSMJXj/wtnZMs5k= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/caarlos0/env/v11 v11.4.0 h1:Kcb6t5kIIr4XkoQC9AF2j+8E1Jsrl3Wz/hhm1LtoGAc= +github.com/caarlos0/env/v11 v11.4.0/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo= +github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY= +github.com/github/copilot-sdk/go v0.2.0 h1:RnrIIirmtp4wGgqSQFJ2k9phbeveIxOtYZqDogoNEa0= +github.com/github/copilot-sdk/go v0.2.0/go.mod h1:uGWkjVYcp2DV9DgtqYihh5tEoJjNqxIFaUNnrwY4FxM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc= +github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= +github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= +github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= +github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modelcontextprotocol/go-sdk v1.5.0 h1:CHU0FIX9kpueNkxuYtfYQn1Z0slhFzBZuq+x6IiblIU= +github.com/modelcontextprotocol/go-sdk v1.5.0/go.mod h1:gggDIhoemhWs3BGkGwd1umzEXCEMMvAnhTrnbXJKKKA= +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/openai/openai-go/v3 v3.22.0 h1:6MEoNoV8sbjOVmXdvhmuX3BjVbVdcExbVyGixiyJ8ys= +github.com/openai/openai-go/v3 v3.22.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA= +github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= +github.com/pion/webrtc/v3 v3.3.6 h1:7XAh4RPtlY1Vul6/GmZrv7z+NnxKA6If0KStXBI2ZLE= +github.com/pion/webrtc/v3 v3.3.6/go.mod h1:zyN7th4mZpV27eXybfR/cnUf3J2DRy8zw/mdjD9JTNM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= +github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= +github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= +github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0= +github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= +modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw= +modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0= +modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= +modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= +modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= +modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= +modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw= +modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= +modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.48.2 h1:5CnW4uP8joZtA0LedVqLbZV5GD7F/0x91AXeSyjoh5c= +modernc.org/sqlite v1.48.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/taolun.md b/taolun.md new file mode 100644 index 0000000..12ecc42 --- /dev/null +++ b/taolun.md @@ -0,0 +1,141 @@ +# hxclaw 讨论记录 + +## 知识点汇总 + +### 1. hxclaw 是什么? + +- hxclaw 是基于 picoclaw 的 CLI 增强工具 +- 提供流式输出(替代原有的批量输出) +- 提供 Markdown 终端渲染(提升阅读体验) +- 定位: picoclaw 的变体,面向需要更好终端交互体验的用户 + +### 2. 为什么采用独立二进制而非插件? + +- picoclaw 目前没有正式的插件系统 +- 工具扩展通过 ToolRegistry 注册(如 spi.go, i2c.go) +- 技能通过 SKILL.md 文件加载 +- MCP 通过配置连接外部服务器 +- 最小侵入方式:独立二进制 + 复用核心库 + +### 3. 如何复用 picoclaw 功能? + +- 使用 Go 的 replace 机制在 go.mod 中声明依赖 +- 开发时 replace 指向本地 picoclaw 目录 +- 发布时 replace 指向 GitHub 具体版本(如 v0.2.4) +- hxclaw 只需导入:pkg/agent, pkg/providers, pkg/config, pkg/bus + +### 4. hxclaw 架构设计 + +``` +hxclaw/ +├── cmd/hxclaw/ # CLI 入口(自己实现) +├── go.mod # 依赖配置 +└── pkg/ # 空目录,全量复用 picoclaw +``` + +- 不依赖:pkg/channels(不需要消息通道)、pkg/gateway(不需要 HTTP 服务)、web/(不需要网页) +- 依赖:pkg/agent(核心逻辑)、pkg/providers(LLM 调用)、pkg/config(配置加载) + +### 5. 流式输出原理 + +- picoclaw 的 providers 已支持 StreamingProvider 接口 +- 接口定义:ChatStream(ctx, messages, tools, model, options, onChunk) +- onChunk 是回调函数,每个 token 生成时调用 +- CLI 层需要判断 provider 是否实现 StreamingProvider,然后选择调用 + +### 6. Markdown 终端渲染 + +- 使用 charmbracelet 家族库 +- lipgloss:终端样式 +- glow:代码高亮 +- 流程:Markdown → ANSI 转义序列 → 终端显示 + +### 7. 部署方式 + +- 独立二进制 hxclaw,与 picoclaw 二进制共存于同一目录 +- 用户使用 `hxclaw` 命令调用增强版 CLI +- 配置文件复用 picoclaw 的 config.json(位于 ~/.picoclaw/config.json) + +### 8. 版本同步策略 + +- 关键版本跟进(功能大版本更新时) +- 不需要每次 picoclaw 升级都同步 +- 依赖版本在 go.mod 中声明,更新时修改 replace 目标版本即可 + +### 9. AgentRegistry 的正确使用方式 + +- `AgentRegistry` 负责管理多个 Agent 实例 +- `GetDefaultAgent()` 获取默认的 Agent 实例 +- `AgentInstance` 包含: + - `Provider` - LLM 提供者 + - `ContextBuilder` - 消息构建器 + - `Tools` - 工具注册表 + - `Model` - 模型名称 +- 注意:`AgentRegistry` 没有 `BuildMessages` 方法,该方法属于 `ContextBuilder` + +### 10. ToolDefinitions 的获取方式 + +- 通过 `agentInstance.Tools.ToProviderDefs()` 获取 +- 返回 `[]providers.ToolDefinition` 格式 +- 该方法将工具注册表中的工具转换为 provider 可用的格式 + +### 11. 流式输出的实现问题与解决方案 + +#### 问题 1:onChunk 回调接收累积文本 + +picoclaw 的 `StreamingProvider` 接口定义: +```go +onChunk func(accumulated string) +``` + +注释明确说明:"onChunk receives the accumulated text so far (not individual deltas)"。 + +这意味着每次回调时参数是完整的累积文本(如 "你好" → "你好!再次" → "你好!再次见到"),而不是增量。 + +#### 问题 2:直接打印导致重复输出 + +如果直接打印 token: +```go +func(token string) { + fmt.Print(token) // 打印累积文本! +} +``` +会导致输出: +``` +你好 +你好!再次 +你好!再次见到 +... +``` + +#### 解决方案 1:跟踪已打印长度 + +使用 `printedLen` 跟踪已打印的字符位置,只打印新增部分: +```go +var printedLen int +func(accumulated string) { + if len(accumulated) > printedLen { + fmt.Print(accumulated[printedLen:]) + printedLen = len(accumulated) + } +} +``` + +#### 问题 3:尝试使用 uilive 库 + +尝试使用 `github.com/gosuri/uilive` 库实现同行流动效果,但发现该库会覆盖每一行,只显示最后一行内容,不符合需求。 + +#### 最终解决方案:直接 Print + Sync + +```go +fmt.Print(accumulated[printedLen:]) +os.Stdout.Sync() +``` + +这样: +1. 字符串自然累积增长 +2. 终端自动处理换行(满一行自动 wrap) +3. 保留所有历史输出 +4. 每次刷新缓冲区确保立即显示 + +这正是 ollama 等工具的流式输出效果。 \ No newline at end of file