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
This commit is contained in:
1 parent
1d6e89449a
commit
2336c9433c
17 files changed
+1324
-197
No files matched your search
@@ -0,0 +1,79 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user