Files
docker-agent/pkg/modelsdev/snapshot.go
T
David Gageot 2d157247ba feat(modelsdev): embed models.dev catalog snapshot as binary fallback
Adds a build-time snapshot of the models.dev catalog baked into the
binary via go:embed, used as last-resort fallback when the cache and
live fetch are both unavailable. A generator (pkg/modelsdev/internal/gen)
refreshes the snapshot; task update-models and a weekly GitHub Actions
workflow keep it current.

The fallback snapshot is not memoized on transient fetch failure, so the
Store retries the fetch once the network recovers. Context windows for
Fable and Opus 4.6-4.8 now come from the embedded snapshot (always
available, even offline); the Anthropic client falls back to the flat
200k floor only when no catalogue entry exists. A warning is logged when
the embedded snapshot fails to parse so a bad build artifact is
discoverable.

The snapshot freshness check is opt-in: TestSnapshotDateParses always
runs (time-independent) while TestSnapshotDateIsFresh is skipped unless
CHECK_MODELS_SNAPSHOT_FRESHNESS is set. A 'task check-models-fresh'
target provides the CI backstop.

Assisted-By: Claude
2026-06-27 16:00:08 +02:00

63 lines
1.9 KiB
Go

package modelsdev
import (
_ "embed"
"encoding/json"
"log/slog"
"strings"
"sync"
"time"
)
//go:generate go run ./internal/gen
// snapshotJSON is a snapshot of the models.dev catalog, refreshed at build
// time by `go generate ./pkg/modelsdev/...` (see internal/gen). It is the
// last-resort fallback used when neither the in-memory catalog, the on-disk
// cache, nor a live fetch is available — so a fresh binary always has a
// reasonable model catalog instead of a hard-coded default.
//
//go:embed snapshot.json
var snapshotJSON []byte
// snapshotDate records when snapshotJSON was generated (RFC3339, UTC). It is
// used to surface staleness and by tests that guard the snapshot's freshness.
//
//go:embed snapshot_date.txt
var snapshotDateRaw string
var (
snapshotOnce sync.Once
snapshotDB *Database
)
// embeddedSnapshot returns the catalog baked into the binary at build time.
// The JSON is parsed once and cached. A malformed snapshot yields an empty
// (non-nil) database rather than a panic, so a bad embed degrades to
// "provider not found" instead of crashing the process. The parse failure is
// logged because, with the hardcoded context-limit fallback gone, a corrupt
// snapshot would otherwise silently clamp large-context models with no signal.
func embeddedSnapshot() *Database {
snapshotOnce.Do(func() {
var providers map[string]Provider
if err := json.Unmarshal(snapshotJSON, &providers); err != nil {
slog.Warn("embedded models.dev snapshot is malformed; context-window limits will be wrong", "error", err)
snapshotDB = &Database{}
return
}
snapshotDB = &Database{Providers: providers}
})
return snapshotDB
}
// SnapshotDate returns the time the embedded models.dev snapshot was
// generated. The zero value is returned when the recorded date can't be
// parsed.
func SnapshotDate() time.Time {
t, err := time.Parse(time.RFC3339, strings.TrimSpace(snapshotDateRaw))
if err != nil {
return time.Time{}
}
return t
}