Files
opsxdb/server/internal/ai/agent_stream.go
T
logan 5416e86379 feat(ai): session harness contract — incomplete, run_id, confirm bind
Add docs/16 for session state machine, incomplete/resume, queue semantics,
and agent-loop/harness/MCP gap review. Wire RunOutcome on max-rounds,
run_id through audit, TrimMessages/Redact hooks, ConfirmBindHash on write
confirm path; session file skeleton without HTTP yet. No push.
2026-10-15 13:58:10 +08:00

86 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"`
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
}