Files
logan d543f3b924 feat(ai): session persist + continue HTTP/UI, checkpoints, queue
落地 docs/16 P1:会话落盘与真续跑(prior tool 轨迹)、incomplete「加预算继续」、
awaiting_confirm outcome、同参拒+第4/6轮检查点、侧栏忙时单槽排队。
Ask 真 token 流式记缺口(三协议 SSE 成本高)。
2026-10-15 14:18:18 +08:00

87 lines
2.4 KiB
Go

package ai
import (
"encoding/json"
"fmt"
"net/http"
)
// AgentEventType SSE / 回调事件类型。
const (
AgentEventStepStart = "step_start"
AgentEventStep = "step"
AgentEventDelta = "delta"
AgentEventDone = "done"
AgentEventError = "error"
AgentEventIncomplete = "incomplete"
)
// 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"`
RunID string `json:"runId,omitempty"`
SessionID string `json:"sessionId,omitempty"`
Outcome string `json:"runOutcome,omitempty"`
IncompleteReason string `json:"incompleteReason,omitempty"`
Resumable bool `json:"resumable,omitempty"`
StepsBudget int `json:"stepsBudget,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
}