Files
coder/coderd/aibridge/prices/prices.go
T
Yevhenii Shcherbina 211f490910 refactor: share the AI Gateway price book contract (#28310)
Follow-up to the review discussion on the price book refresh automation:
https://github.com/coder/coder/pull/28146#discussion_r3806439966

## Problem

The price seed row struct was duplicated three times, so a JSON tag
change had to be repeated in each copy:

- `scripts/aibridgepricesgen.priceRow`
- `scripts/aibridgepricesdiff.priceRow`
- `coderd/aibridge/prices.seedRow`

The `(provider, model)` key and its parsing were duplicated too.

## Change

Add `coderd/aibridge/prices/pricebook` holding the shared contract:
`Row`, `Key`, `Row.Key()`, and `Parse`. The package depends only on the
standard library, so the scripts can import it without pulling in the
database layer that `coderd/aibridge/prices` brings.

Consumer-specific behaviour stays where it is: upstream conversion and
validation in the generator, comparison and Markdown rendering in the
diff tool, DB seeding and runtime lookup in the seeder.

The batch SQL upsert still extracts JSON fields by name, so it remains a
separate contract boundary. The note about keeping it in sync now lives
on the shared `Row`.

No generated artifacts change; the serialized schema is identical.

---

<sub>Opened by Coder Agents on behalf of @evgeniy-scherbina.</sub>
2026-08-20 14:04:23 -04:00

42 lines
1.3 KiB
Go

// Package prices seeds the ai_model_prices table from an embedded JSON
// price book at server startup.
package prices
import (
"context"
_ "embed"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/aibridge/prices/pricebook"
"github.com/coder/coder/v2/coderd/database"
)
//go:embed data/prices.json
var seedJSON []byte
// Seed applies the embedded price seed to ai_model_prices table, replacing the
// price columns of any existing (provider, model) row and inserting new ones.
// Rows already in the table that no longer appear in the seed are left
// untouched, so historical entries persist across upstream model deprecations.
func Seed(ctx context.Context, db database.Store) error {
return SeedFromBytes(ctx, db, seedJSON)
}
// SeedFromBytes applies an arbitrary JSON seed. Most callers should use Seed,
// which applies the seed embedded in this binary; SeedFromBytes is exposed
// for tests that need to inject a deterministic seed.
func SeedFromBytes(ctx context.Context, db database.Store, data []byte) error {
rows, err := pricebook.Parse(data)
if err != nil {
return xerrors.Errorf("parse price seed: %w", err)
}
if len(rows) == 0 {
return xerrors.New("price seed is empty")
}
return db.UpsertAIModelPrices(ctx, database.UpsertAIModelPricesParams{
Seed: data,
Source: database.AIModelPriceSourceDefault,
})
}