Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Adds epidemiology package, auth epidemiology_fix push, strain-based 3D map, and Vitest coverage.
111 lines
3.4 KiB
Go
111 lines
3.4 KiB
Go
package ai
|
|
|
|
import (
|
|
"encoding/json"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// SeerBridge supplies persisted notes and streams LLM payloads to The Seer UI.
|
|
type SeerBridge interface {
|
|
NotesForPrompt(agentID string) string
|
|
AppendNote(agentID, content, promptHash string) error
|
|
EmitEvent(agentID, direction string, payload interface{})
|
|
}
|
|
|
|
// SeerNoteInstruction is appended to the system prompt when Seer memory is active.
|
|
var SeerNoteInstruction string
|
|
|
|
// SeerToolCatalog documents fleet tool endpoints the model may invoke via tool-call JSON.
|
|
var SeerToolCatalog string
|
|
|
|
func init() {
|
|
SeerNoteInstruction = strings.TrimSpace(`
|
|
Seer memory: after your JSON commands block, append exactly one line:
|
|
SEER_NOTE: <one concise sentence summarizing what you learned this turn for future cycles>.
|
|
The server persists SEER_NOTE lines and replays them on the next turn — this is your only cross-turn memory.`)
|
|
|
|
SeerToolCatalog = strings.TrimSpace(`
|
|
Available Seer tool endpoints (POST /api/v1/seer/tools/{name}):
|
|
- spread_route: BGP-style minimum-clearance spread routing — body {"session_id","target_subnets":[],"join_lane"}
|
|
- graft_strain: clone winning spread strain onto sibling subnet — body {"source_agent_id","target_subnet","strain"}
|
|
- fork_onion: split LOTL tier chain for A/B recovery — body {"agent_id","tier_order":[],"skip_tiers":[]}
|
|
Stub handlers return {"ok":true,"stub":true} until live orchestration ships.`)
|
|
}
|
|
|
|
var seerNoteLineRe = regexp.MustCompile(`(?m)^SEER_NOTE:\s*(.+)$`)
|
|
|
|
// AugmentUserPromptWithNotes prepends replayed Seer notes before the fresh snapshot.
|
|
func AugmentUserPromptWithNotes(basePrompt, notesBlock string) string {
|
|
notesBlock = strings.TrimSpace(notesBlock)
|
|
if notesBlock == "" {
|
|
return basePrompt
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString("Seer notes (server memory — replayed from prior turns):\n")
|
|
b.WriteString(notesBlock)
|
|
b.WriteString("\n\n--- current snapshot ---\n")
|
|
b.WriteString(basePrompt)
|
|
return b.String()
|
|
}
|
|
|
|
// FormatSeerNotesBlock renders DB notes for prompt injection.
|
|
func FormatSeerNotesBlock(notes []string) string {
|
|
if len(notes) == 0 {
|
|
return ""
|
|
}
|
|
var b strings.Builder
|
|
for i, n := range notes {
|
|
n = strings.TrimSpace(n)
|
|
if n == "" {
|
|
continue
|
|
}
|
|
if i > 0 {
|
|
b.WriteByte('\n')
|
|
}
|
|
b.WriteString("- ")
|
|
b.WriteString(n)
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// ExtractSeerNote pulls the SEER_NOTE line from an LLM response.
|
|
func ExtractSeerNote(response string) (note string, ok bool) {
|
|
m := seerNoteLineRe.FindStringSubmatch(response)
|
|
if len(m) < 2 {
|
|
return "", false
|
|
}
|
|
note = strings.TrimSpace(m[1])
|
|
if note == "" {
|
|
return "", false
|
|
}
|
|
if len(note) > 2000 {
|
|
note = note[:2000]
|
|
}
|
|
return note, true
|
|
}
|
|
|
|
// BuildChatCompletionPayload mirrors the exact OpenAI-compatible body sent to the LLM.
|
|
func BuildChatCompletionPayload(model, systemPrompt, userPrompt string) map[string]interface{} {
|
|
if strings.TrimSpace(model) == "" {
|
|
model = "llama3.2"
|
|
}
|
|
return map[string]interface{}{
|
|
"model": model,
|
|
"messages": []map[string]string{
|
|
{"role": "system", "content": systemPrompt},
|
|
{"role": "user", "content": userPrompt},
|
|
},
|
|
"stream": false,
|
|
}
|
|
}
|
|
|
|
// SeerStreamEvent is one request/response frame for The Seer terminal.
|
|
type SeerStreamEvent struct {
|
|
ID int64 `json:"id"`
|
|
AgentID string `json:"agent_id"`
|
|
Direction string `json:"direction"`
|
|
Payload json.RawMessage `json:"payload"`
|
|
Ts string `json:"ts"`
|
|
}
|