Files
Rogeeandrogee d948222ac6 H-337: restore Captain inbox takeover and KB citations (#65)
* fix(captain): restore inbox takeover and KB citations

* fix(captain): harden grounded citations and smoke seed

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-20 22:33:38 +08:00

292 lines
9.1 KiB
Go

package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
)
const (
listenAddr = ":8080"
maxRequestBody = 1 << 20
knowledgeHTML = `<!doctype html><html><head><title>GoChat Local Acceptance Knowledge</title></head><body><main><h1>Local acceptance policy</h1><p id="fixture-fact">The local fixture warranty window is 37 days. Reference marker: KBASE-LOCAL-2026.</p></main></body></html>`
)
type fixtureServer struct {
mu sync.Mutex
attempts map[[32]byte]int
}
type chatRequest struct {
Model string `json:"model"`
Messages []chatMessage `json:"messages"`
Tools []toolDefinition `json:"tools"`
Stream bool `json:"stream"`
}
type chatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
Name string `json:"name"`
ToolCallID string `json:"tool_call_id"`
ToolCalls []toolCall `json:"tool_calls"`
}
type toolDefinition struct {
Function struct {
Name string `json:"name"`
} `json:"function"`
}
type toolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function struct {
Name string `json:"name"`
Arguments string `json:"arguments"`
} `json:"function"`
}
type embeddingRequest struct {
Model string `json:"model"`
Input []string `json:"input"`
}
func main() {
logger := log.New(os.Stdout, "captain-fixture ", log.LstdFlags)
server := &http.Server{Addr: listenAddr, Handler: newFixtureServer(logger), ReadHeaderTimeout: 5 * time.Second}
logger.Printf("listening addr=%s", listenAddr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Fatal(err)
}
}
func newFixtureServer(logger *log.Logger) http.Handler {
s := &fixtureServer{attempts: make(map[[32]byte]int)}
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
})
mux.HandleFunc("GET /knowledge", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = io.WriteString(w, knowledgeHTML)
})
mux.HandleFunc("POST /v1/chat/completions", s.chat)
mux.HandleFunc("POST /v1/embeddings", s.embeddings)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
status := &statusWriter{ResponseWriter: w, status: http.StatusOK}
mux.ServeHTTP(status, r)
logger.Printf("method=%s path=%s status=%d", r.Method, r.URL.Path, status.status)
})
}
func (s *fixtureServer) chat(w http.ResponseWriter, r *http.Request) {
body, ok := readBody(w, r)
if !ok {
return
}
var req chatRequest
if err := json.Unmarshal(body, &req); err != nil || len(req.Messages) == 0 {
writeError(w, http.StatusBadRequest, "invalid_request")
return
}
latest := latestUserContent(req.Messages)
if strings.Contains(latest, "[fixture:fail]") {
writeError(w, http.StatusServiceUnavailable, "fixture_failure")
return
}
if strings.Contains(latest, "[fixture:retry]") && s.firstAttempt(body) {
writeError(w, http.StatusServiceUnavailable, "fixture_retry_once")
return
}
message, finishReason := fixtureChatMessage(req)
if req.Stream {
writeStream(w, req.Model, message.Content)
return
}
writeJSON(w, http.StatusOK, map[string]any{
"id": "chatcmpl-fixture", "object": "chat.completion", "created": 1, "model": req.Model,
"choices": []map[string]any{{"index": 0, "message": message, "finish_reason": finishReason}},
"usage": map[string]int{"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2},
})
}
func (s *fixtureServer) embeddings(w http.ResponseWriter, r *http.Request) {
body, ok := readBody(w, r)
if !ok {
return
}
var req embeddingRequest
if err := json.Unmarshal(body, &req); err != nil || len(req.Input) == 0 {
writeError(w, http.StatusBadRequest, "invalid_request")
return
}
content := strings.Join(req.Input, "\n")
if strings.Contains(content, "[fixture:embedding-fail]") {
writeError(w, http.StatusServiceUnavailable, "fixture_embedding_failure")
return
}
if strings.Contains(content, "[fixture:embedding-retry]") && s.firstAttempt(body) {
writeError(w, http.StatusServiceUnavailable, "fixture_embedding_retry_once")
return
}
vector := make([]float64, 1536)
for i := range vector {
vector[i] = 0.01
}
data := make([]map[string]any, len(req.Input))
for i := range req.Input {
data[i] = map[string]any{"object": "embedding", "index": i, "embedding": vector}
}
writeJSON(w, http.StatusOK, map[string]any{
"object": "list", "data": data, "model": req.Model,
"usage": map[string]int{"prompt_tokens": len(req.Input), "total_tokens": len(req.Input)},
})
}
func (s *fixtureServer) firstAttempt(body []byte) bool {
key := sha256.Sum256(body)
s.mu.Lock()
defer s.mu.Unlock()
s.attempts[key]++
return s.attempts[key] == 1
}
func fixtureChatMessage(req chatRequest) (chatMessage, string) {
all := allMessageContent(req.Messages)
if strings.Contains(all, "Extract factual, self-contained FAQs") {
return chatMessage{Role: "assistant", Content: `{"faqs":[{"question":"What is the local fixture warranty window?","answer":"The local fixture warranty window is 37 days."}]}`}, "stop"
}
if last := req.Messages[len(req.Messages)-1]; last.Role == "tool" {
if last.Name == "activate_skill" {
if key := firstJSONListValue(last.Content, "reference_keys"); key != "" {
return toolMessage("fixture-read", "read_skill_reference", map[string]string{"skill_name": firstCatalogSkill(all), "reference_key": key}), "tool_calls"
}
}
return chatMessage{Role: "assistant", Content: "Fixture skill path completed."}, "stop"
}
if strings.Contains(latestUserContent(req.Messages), "[fixture:skill]") && hasTool(req.Tools, "activate_skill") {
if name := firstCatalogSkill(all); name != "" {
return toolMessage("fixture-activate", "activate_skill", map[string]string{"skill_name": name}), "tool_calls"
}
}
if strings.Contains(all, "Knowledge Base Context:") {
return chatMessage{Role: "assistant", Content: "The local fixture warranty window is 37 days [[1](http://captain-fixture:8080/knowledge)]."}, "stop"
}
return chatMessage{Role: "assistant", Content: "Fixture chat response."}, "stop"
}
func toolMessage(id, name string, args map[string]string) chatMessage {
raw, _ := json.Marshal(args)
call := toolCall{ID: id, Type: "function"}
call.Function.Name, call.Function.Arguments = name, string(raw)
return chatMessage{Role: "assistant", ToolCalls: []toolCall{call}}
}
func firstCatalogSkill(content string) string {
const start, end = "<available_skills_json>", "</available_skills_json>"
i := strings.Index(content, start)
if i < 0 {
return ""
}
j := strings.Index(content[i+len(start):], end)
if j < 0 {
return ""
}
var catalog []struct {
Name string `json:"name"`
}
if json.Unmarshal([]byte(content[i+len(start):i+len(start)+j]), &catalog) != nil || len(catalog) == 0 {
return ""
}
return catalog[0].Name
}
func firstJSONListValue(content, key string) string {
var payload map[string]any
start, end := strings.Index(content, "{"), strings.LastIndex(content, "}")
if start < 0 || end < start || json.Unmarshal([]byte(content[start:end+1]), &payload) != nil {
return ""
}
values, _ := payload[key].([]any)
if len(values) == 0 {
return ""
}
value, _ := values[0].(string)
return value
}
func hasTool(tools []toolDefinition, name string) bool {
for _, tool := range tools {
if tool.Function.Name == name {
return true
}
}
return false
}
func allMessageContent(messages []chatMessage) string {
var b strings.Builder
for _, message := range messages {
b.WriteString(message.Content)
b.WriteByte('\n')
}
return b.String()
}
func latestUserContent(messages []chatMessage) string {
for i := len(messages) - 1; i >= 0; i-- {
if messages[i].Role == "user" {
return messages[i].Content
}
}
return ""
}
func readBody(w http.ResponseWriter, r *http.Request) ([]byte, bool) {
body, err := io.ReadAll(io.LimitReader(r.Body, maxRequestBody+1))
if err != nil || len(body) == 0 || len(body) > maxRequestBody {
writeError(w, http.StatusBadRequest, "invalid_request")
return nil, false
}
return body, true
}
func writeStream(w http.ResponseWriter, model, content string) {
w.Header().Set("Content-Type", "text/event-stream")
chunk := map[string]any{
"id": "chatcmpl-fixture", "object": "chat.completion.chunk", "created": 1, "model": model,
"choices": []map[string]any{{"index": 0, "delta": map[string]string{"role": "assistant", "content": content}, "finish_reason": "stop"}},
}
raw, _ := json.Marshal(chunk)
_, _ = fmt.Fprintf(w, "data: %s\n\ndata: [DONE]\n\n", raw)
}
func writeError(w http.ResponseWriter, status int, code string) {
writeJSON(w, status, map[string]any{"error": map[string]string{"message": "local acceptance fixture error", "type": "fixture_error", "code": code}})
}
func writeJSON(w http.ResponseWriter, status int, value any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(value)
}
type statusWriter struct {
http.ResponseWriter
status int
}
func (w *statusWriter) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
}