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.
86 lines
2.4 KiB
Go
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
|
|
}
|