package app import ( "context" "time" "finance-duck/internal/classification" ) // VerifiedModels lists provider models that currently satisfy the fail-closed // routing controls every classification request carries (a live // zero-data-retention endpoint with strict structured outputs). Anything else // routes to zero providers, so the UI offers only these. The public catalog // changes slowly; an hour of caching keeps the settings screen instant without // hiding newly usable models for long. func (a *App) VerifiedModels(ctx context.Context) ([]classification.VerifiedModel, error) { a.mu.Lock() if a.verifiedModels != nil && time.Since(a.verifiedModelsAt) < time.Hour { cached := append([]classification.VerifiedModel{}, a.verifiedModels...) a.mu.Unlock() return cached, nil } // The catalog fetch must not hold a.mu: it is a network call, and the // probe client shares only immutable configuration with the classifier. probe := &classification.Client{BaseURL: a.classifier.BaseURL, HTTPClient: a.classifier.HTTPClient} a.mu.Unlock() models, err := probe.VerifiedModels(ctx) if err != nil { return nil, err } a.mu.Lock() a.verifiedModels, a.verifiedModelsAt = models, time.Now() a.mu.Unlock() return append([]classification.VerifiedModel{}, models...), nil }