Files
so-many-crows/flightlog.go
T
SmokeyandClaude Opus 5.5 73cf298fee OpenRouter crows; log which model answered each flight
- ChatBrain speaks OpenAI-style chat completions (OpenRouter now)
- CROWS_PROVIDER picks anthropic or openrouter; default model
  openrouter/free; CROWS_MODEL overrides
- Flight log records the model that actually answered
- Token budgets fall back to a length estimate if usage is missing
- start.command reads either key from .env

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQnSw4RRWp8uqpj3nsnnXF
2026-10-27 16:23:59 -06:00

54 lines
1.4 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"sync"
"time"
)
// Event is one line in the flight log: every triage, flight and verdict.
type Event struct {
Time time.Time `json:"ts"`
Kind string `json:"kind"`
Requester string `json:"requester,omitempty"`
Item string `json:"item,omitempty"`
Crow string `json:"crow,omitempty"`
Mode string `json:"mode,omitempty"`
TaskType string `json:"task_type,omitempty"`
Model string `json:"model,omitempty"` // which model actually answered (routers vary)
Tokens int `json:"tokens,omitempty"`
Ms int64 `json:"ms,omitempty"`
Verdict string `json:"verdict,omitempty"`
Output string `json:"output,omitempty"`
Error string `json:"error,omitempty"`
Note string `json:"note,omitempty"`
}
type FlightLog struct {
mu sync.Mutex
f *os.File
}
func OpenFlightLog(path string) (*FlightLog, error) {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return nil, err
}
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return nil, err
}
return &FlightLog{f: f}, nil
}
func (l *FlightLog) Write(e Event) {
l.mu.Lock()
defer l.mu.Unlock()
e.Time = time.Now()
b, _ := json.Marshal(e)
l.f.Write(append(b, '\n'))
}
func (l *FlightLog) Close() error { return l.f.Close() }