(#13) Live models deliver in the nest #20

Merged
jacisjake merged 1 commits from task/13-live-models-nest into main 2026-10-28 11:00:10 +00:00
2 changed files with 118 additions and 0 deletions

No files matched your search

+21
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
@@ -55,3 +56,23 @@ func (l *FlightLog) Write(e Event) {
}
func (l *FlightLog) Close() error { return l.f.Close() }
// ReadEvents parses a flight log file back into events, in the order written.
func ReadEvents(path string) ([]Event, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var out []Event
for _, line := range strings.Split(string(data), "\n") {
if strings.TrimSpace(line) == "" {
continue
}
var e Event
if err := json.Unmarshal([]byte(line), &e); err != nil {
return nil, err
}
out = append(out, e)
}
return out, nil
}
+97
View File
@@ -0,0 +1,97 @@
package main
import (
"context"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
// newLiveNestServer builds the server over the real brain from env (NewBrain)
// instead of the mock. Requires an API key; callers must skip first.
func newLiveNestServer(t *testing.T) (*Server, string) {
t.Helper()
dir := t.TempDir()
ledger, _ := LoadLedger(filepath.Join(dir, "ledger.json"))
flog, err := OpenFlightLog(filepath.Join(dir, "flight.jsonl"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { flog.Close() })
nests, _ := LoadNests(filepath.Join(dir, "nests.json"))
msgs, _ := LoadMessageStore(filepath.Join(dir, "messages"))
ctx, cancel := context.WithCancel(context.Background())
approver := NewWebApprover(time.Minute)
p := &Pipeline{Flock: NewFlock(NewBrain(), DefaultGuardrails(), flog), Ledger: ledger, Log: flog, G: DefaultGuardrails(), Approver: approver}
s := NewServer(ctx, p, approver, nests).WithMessages(msgs)
ts := httptest.NewServer(s.Handler())
t.Cleanup(func() { cancel(); ts.Close() })
return s, filepath.Join(dir, "flight.jsonl")
}
func TestLiveCrowDeliversInNest(t *testing.T) {
if os.Getenv("OPENROUTER_API_KEY") == "" && os.Getenv("ANTHROPIC_API_KEY") == "" {
t.Skip("no model key")
}
s, logPath := newLiveNestServer(t)
nest, err := s.nests.Create("Live Lab", []string{"Hugin"})
if err != nil {
t.Fatal(err)
}
req, err := s.msgs.Append(Message{NestID: nest.ID, Kind: "request", Author: "owner", To: "Hugin", Text: "What is the plural of crow?", Timestamp: time.Now().UTC().Format(time.RFC3339)})
if err != nil {
t.Fatal(err)
}
// Synchronous call, like the endpoint's goroutine would.
s.respondToRequest(nest, req)
msgs := s.msgs.All(nest.ID)
var reply *Message
for i := range msgs {
if msgs[i].Author == "Hugin" {
reply = &msgs[i]
}
}
events, err := ReadEvents(logPath)
if err != nil {
t.Fatal(err)
}
if reply == nil {
// No crow message at all: either a flight failure (429/timeout: the
// known free-model instability -> skip) or nothing was even attempted.
flightFailed := false
for _, e := range events {
if e.Kind == "flight" && e.Error != "" {
flightFailed = true
}
}
if flightFailed {
t.Skip("upstream model failed (known free-model instability); the flag path is covered by #11")
}
t.Fatalf("no Hugin reply after a request, log: %+v", msgs)
}
kind := reply.Kind
if kind != "deliver" && kind != "claim" && kind != "decline" {
t.Fatalf("live crow reply must be responsive, got %q (%s)", kind, reply.Text)
}
if reply.To != "owner" {
t.Fatalf("deliver must answer the requester, got to=%q", reply.To)
}
if reply.RefersTo != req.MsgID() {
t.Fatalf("reply must point at the request, got %q", reply.RefersTo)
}
// The call went through Flock.ask, so the flight log has Hugin's flight.
flights := 0
for _, e := range events {
if e.Crow == "Hugin" && e.Model != "" && !strings.Contains(strings.ToLower(e.Model), "mock") {
flights++
}
}
if flights == 0 {
t.Fatalf("want a logged non-mock Hugin flight, events: %+v", events)
}
}