Files
opsxdb/server/internal/ai/agent_stream.go
T
logan 2336c9433c feat(ai): Agent SSE + Anthropic/responses tools + P1 确认写
- Agent 支持 SSE(步骤边推、正文分块),JSON 非流式 fallback
- tool calling 覆盖 openai-chat / anthropic / responses(单测解析回灌)
- P1:mcp.json mode=read_write 才可写;需 confirm_danger;Agent 剥离模型自带令牌
- GET /api/ai/mcp-status、POST /api/ai/agent/confirm;审计 source=mcp|agent
2026-10-15 13:00:40 +08:00

80 lines
1.9 KiB
Go

package ai
import (
"encoding/json"
"fmt"
"net/http"
)
// AgentEventType SSE / 回调事件类型。
const (
AgentEventStepStart = "step_start"
AgentEventStep = "step"
AgentEventDelta = "delta"
AgentEventDone = "done"
AgentEventError = "error"
)
// AgentQueryResult 可供「在结果区打开」的行列结果。
type AgentQueryResult struct {
Columns []string `json:"columns"`
Rows []map[string]interface{} `json:"rows"`
RowCount int `json:"rowCount"`
SQL string `json:"sql,omitempty"`
Tool string `json:"tool,omitempty"`
}
// AgentEvent 流式推送的一项。
type AgentEvent struct {
Type string `json:"type"`
Step *AgentStep `json:"step,omitempty"`
Delta string `json:"delta,omitempty"`
Content string `json:"content,omitempty"`
SQL string `json:"sql,omitempty"`
Steps []AgentStep `json:"steps,omitempty"`
Rounds int `json:"rounds,omitempty"`
Error string `json:"error,omitempty"`
Result *AgentQueryResult `json:"result,omitempty"`
}
// AgentEmitter 可选;nil 时仅返回最终 AgentRes(非流式 fallback)。
type AgentEmitter func(AgentEvent)
func emitEvent(emit AgentEmitter, ev AgentEvent) {
if emit != nil {
emit(ev)
}
}
func chunkDelta(s string, size int) []string {
if s == "" {
return nil
}
if size <= 0 {
size = 32
}
r := []rune(s)
var out []string
for i := 0; i < len(r); i += size {
j := i + size
if j > len(r) {
j = len(r)
}
out = append(out, string(r[i:j]))
}
return out
}
// WriteAgentSSE 写一条 SSE 事件并 Flush。
func WriteAgentSSE(w http.ResponseWriter, flusher http.Flusher, ev AgentEvent) error {
b, err := json.Marshal(ev)
if err != nil {
return err
}
if _, err := fmt.Fprintf(w, "event: %s\ndata: %s\n\n", ev.Type, b); err != nil {
return err
}
flusher.Flush()
return nil
}