feat: Agent CLI 与终端审计回放全面优化

- Agent CLI: 多轮对话、SSH 长连接、流式推理、Evolver 按用户隔离、历史侧栏
- 终端审计: 回放播放器、命令书签、筛选分页导出、命令输出统计、录制落盘修复
- 修复 session_commands 表迁移、Kill 竞态导致录制丢失、无录制会话回放提示
- 审计历史页只读(移除删除),移除回放弹框录制搜索
This commit is contained in:
Logan committed 2026-07-01 12:13:53 +08:00
1 parent 9a3dd304f7
commit 1acb0c49af
81 files changed
+3916 -1655

No files matched your search

+26
View File
@@ -42,6 +42,12 @@ type anthropicMessage struct {
Content json.RawMessage `json:"content"`
}
// anthropicThinking 启用 Claude thinking 模式(Claude 3.7 Sonnet 支持) // Logan.Li
type anthropicThinking struct {
Type string `json:"type"`
BudgetTokens int `json:"budget_tokens"`
}
// anthropicRequest Anthropic /v1/messages 请求体 // Logan.Li
type anthropicRequest struct {
Model string `json:"model"`
@@ -51,6 +57,7 @@ type anthropicRequest struct {
Tools []anthropicTool `json:"tools,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
Stream bool `json:"stream,omitempty"`
Thinking *anthropicThinking `json:"thinking,omitempty"` // Claude 3.7 Sonnet 推理模式 // Logan.Li
}
// anthropicTool Anthropic 工具定义(input_schema,对应 OpenAI 的 parameters) // Logan.Li
@@ -157,6 +164,12 @@ func buildTools(tools []Tool) []anthropicTool {
return out
}
// isAnthropicThinkingModel 判断是否为支持 thinking 的 Claude 模型 // Logan.Li
func isAnthropicThinkingModel(model string) bool {
m := strings.ToLower(model)
return strings.Contains(m, "claude-3-7") || strings.Contains(m, "claude-3.7")
}
// buildRequest 构造 Anthropic 请求体(Complete 与 Stream 共用) // Logan.Li
func (c *AnthropicClient) buildRequest(req *CompletionRequest, stream bool) ([]byte, error) {
system, msgs := c.buildMessages(req)
@@ -168,6 +181,19 @@ func (c *AnthropicClient) buildRequest(req *CompletionRequest, stream bool) ([]b
if payload.MaxTokens <= 0 {
payload.MaxTokens = 4096
}
// 启用 Claude thinking 模式时,temperature 必须固定为 1,max_tokens 需大于 budget_tokens // Logan.Li
enabled := req.ReasoningEnabled || c.cfg.ReasoningEnabled
if enabled && isAnthropicThinkingModel(payload.Model) {
payload.Temperature = 1
budget := payload.MaxTokens / 2
if budget < 1024 {
budget = 1024
}
if payload.MaxTokens <= budget {
payload.MaxTokens = budget + 1024
}
payload.Thinking = &anthropicThinking{Type: "enabled", BudgetTokens: budget}
}
body, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
+47 -14
View File
@@ -17,13 +17,14 @@ import (
// Config LLM 客户端配置(endpoint 已由上层解析为完整 base url) // Logan.Li
type Config struct {
Protocol string // "openai" | "anthropic",由 client.go 工厂决定具体实现
Endpoint string // 完整 base url,如 https://api.deepseek.com/v1
APIKey string // Bearer token
Model string // 模型名,如 deepseek-chat / gpt-4o-mini
Temperature float64 // 采样温度
MaxTokens int // 单次最大输出 token
Timeout time.Duration // HTTP 超时(0 表示使用默认 180s)
Protocol string // "openai" | "anthropic",由 client.go 工厂决定具体实现
Endpoint string // 完整 base url,如 https://api.deepseek.com/v1
APIKey string // Bearer token
Model string // 模型名,如 deepseek-chat / gpt-4o-mini
Temperature float64 // 采样温度
MaxTokens int // 单次最大输出 token
ReasoningEnabled bool // 是否启用推理/Plan 模式(OpenAI o-series / DeepSeek R1 等) // Logan.Li
Timeout time.Duration // HTTP 超时(0 表示使用默认 180s)
}
// OpenAIClient OpenAI 兼容客户端 // Logan.Li
@@ -48,15 +49,45 @@ func NewOpenAIClient(cfg Config) Client {
// Name 返回客户端名称 // Logan.Li
func (c *OpenAIClient) Name() string { return "openai-compatible(" + c.cfg.Model + ")" }
// isOpenAIReasoningModel 判断是否为需要 reasoning_effort 的 OpenAI o-series 模型 // Logan.Li
func isOpenAIReasoningModel(model string) bool {
m := strings.ToLower(model)
return strings.Contains(m, "o1") || strings.Contains(m, "o3")
}
// isGenericReasoningModel 判断是否为通用推理开关模型(DeepSeek R1 等) // Logan.Li
func isGenericReasoningModel(model string) bool {
m := strings.ToLower(model)
return strings.Contains(m, "reasoning") || strings.Contains(m, "r1") || strings.Contains(m, "deepseek-reasoner")
}
// applyReasoningParams 根据模型类型应用 reasoning 参数 // Logan.Li
func applyReasoningParams(payload *openaiRequest, enabled bool) {
if !enabled {
return
}
if isOpenAIReasoningModel(payload.Model) {
// OpenAI o-series 使用 reasoning_effort,temperature 必须固定为 1(不传) // Logan.Li
payload.ReasoningEffort = "medium"
payload.Temperature = 0
return
}
if isGenericReasoningModel(payload.Model) {
payload.Reasoning = true
}
}
// openaiRequest OpenAI chat/completions 请求体 // Logan.Li
type openaiRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice string `json:"tool_choice,omitempty"` // "auto" 让模型自主决定
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"` // 流式补全(Stream 方法置 true)
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice string `json:"tool_choice,omitempty"` // "auto" 让模型自主决定
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"` // 流式补全(Stream 方法置 true)
ReasoningEffort string `json:"reasoning_effort,omitempty"` // OpenAI o-series 推理强度 // Logan.Li
Reasoning bool `json:"reasoning,omitempty"` // DeepSeek 等部分平台推理开关 // Logan.Li
}
// openaiResponse OpenAI chat/completions 响应体 // Logan.Li
@@ -89,6 +120,7 @@ func (c *OpenAIClient) Complete(ctx context.Context, req *CompletionRequest) (*C
if payload.MaxTokens <= 0 {
payload.MaxTokens = 4096
}
applyReasoningParams(&payload, req.ReasoningEnabled || c.cfg.ReasoningEnabled)
body, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
@@ -151,6 +183,7 @@ func (c *OpenAIClient) Stream(ctx context.Context, req *CompletionRequest, onDel
if payload.MaxTokens <= 0 {
payload.MaxTokens = 4096
}
applyReasoningParams(&payload, req.ReasoningEnabled || c.cfg.ReasoningEnabled)
body, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("序列化请求失败: %w", err)
+5 -4
View File
@@ -43,10 +43,11 @@ type ToolFunction struct {
// CompletionRequest 补全请求参数 // Logan.Li
type CompletionRequest struct {
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
ReasoningEnabled bool `json:"reasoning_enabled,omitempty"` // 是否启用推理/Plan 模式(协议相关) // Logan.Li
}
// CompletionResponse 补全响应(取首个 choice) // Logan.Li