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

+16 -18
View File
@@ -14,18 +14,6 @@ import (
"github.com/gin-gonic/gin"
)
// buildLLMClient 从数据库配置构造 LLM 客户端(按 provider 协议自动路由 OpenAI/Anthropic) // Logan.Li
func (h *LLMHandler) buildLLMClient(cfg model.LLMConfig) llm.Client {
return llm.NewClient(llm.Config{
Protocol: resolveProtocol(cfg.Provider),
Endpoint: resolveEndpoint(cfg.Provider, cfg.Endpoint),
APIKey: cfg.APIKey,
Model: cfg.Model,
Temperature: cfg.Temperature,
MaxTokens: cfg.MaxTokens,
})
}
// toLLMMessages model.ChatMessage[] → llm.Message[](智能助手纯对话,仅填 Role/Content) // Logan.Li
func toLLMMessages(msgs []model.ChatMessage) []llm.Message {
out := make([]llm.Message, len(msgs))
@@ -57,7 +45,7 @@ func (h *LLMHandler) Chat(c *gin.Context) {
}
messages := append([]model.ChatMessage{{Role: "system", Content: systemPrompt}}, req.Messages...)
resp, err := h.buildLLMClient(cfg).Complete(c.Request.Context(), &llm.CompletionRequest{
resp, err := buildLLMClientFromConfig(cfg).Complete(c.Request.Context(), &llm.CompletionRequest{
Messages: toLLMMessages(messages),
})
if err != nil {
@@ -127,7 +115,7 @@ func (h *LLMHandler) ChatStream(c *gin.Context) {
// 流式补全:逐 token 回调转发 SSE;ctx 取消(客户端断开)由 llm.Client 内部终止上游读取
var contentBuilder strings.Builder
_, streamErr := h.buildLLMClient(cfg).Stream(c.Request.Context(), &llm.CompletionRequest{
_, streamErr := buildLLMClientFromConfig(cfg).Stream(c.Request.Context(), &llm.CompletionRequest{
Messages: toLLMMessages(messages),
}, func(delta string) {
contentBuilder.WriteString(delta)
@@ -188,7 +176,7 @@ func writeSSEError(c *gin.Context, code int, message string) {
c.Writer.Flush()
}
// TestLLMConnection 测试 LLM 连接(发送一条 ping 消息) // Logan.Li
// TestLLMConnection 测试 LLM 连接(询问模型身份并返回回答) // Logan.Li
// POST /api/v1/aiops/llm-config/test
func (h *LLMHandler) TestLLMConnection(c *gin.Context) {
var cfg model.LLMConfig
@@ -196,12 +184,22 @@ func (h *LLMHandler) TestLLMConnection(c *gin.Context) {
c.JSON(http.StatusOK, model.Response{Code: 400, Message: "请先保存配置"})
return
}
_, err := h.buildLLMClient(cfg).Complete(c.Request.Context(), &llm.CompletionRequest{
Messages: []llm.Message{{Role: "user", Content: "ping"}},
if !cfg.Enabled {
c.JSON(http.StatusOK, model.Response{Code: 400, Message: "请先启用 LLM 服务"})
return
}
resp, err := buildLLMClientFromConfig(cfg).Complete(c.Request.Context(), &llm.CompletionRequest{
Messages: []llm.Message{{Role: "user", Content: "你是什么模型?请简短回答。"}},
})
if err != nil {
c.JSON(http.StatusOK, model.Response{Code: 502, Message: "连接失败: " + err.Error()})
return
}
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "连接成功"})
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "连接成功", Data: gin.H{
"answer": resp.Message.Content,
"model": resp.Model,
"tokens_used": resp.Usage.TotalTokens,
"provider": cfg.Provider,
"reasoning_enabled": cfg.ReasoningEnabled,
}})
}