feat(inference): add providers/model_profiles/rate_cards schema (PR 1/4)#30153
feat(inference): add providers/model_profiles/rate_cards schema (PR 1/4)#30153credence-the-bot[bot] wants to merge 7 commits into
Conversation
… rate_cards tables Creates three Drizzle migrations under assistant/src/inference/migrations/: - 001-providers: id, name (unique), contract, base_url, auth (JSON), is_canonical, canonical_revision, canonical_equivalent_id (self-ref FK), disabled, modality, timestamps - 002-model-profiles: id, name (unique), provider_id (FK → providers, ON DELETE RESTRICT), model, system_prompt, temperature, max_tokens, is_canonical, canonical_revision, disabled, timestamps - 003-rate-cards: id, provider_id (FK → providers), model, input/output/ cache token costs, currency (default USD), effective_from, source Each migration uses withCrashRecovery for idempotent checkpoint tracking and exports a down() for rollback. See DESIGN.md §3.1, §3.2, §3.3. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…les, rate_cards Adds inference/schema/ with: - providers.ts: Drizzle table with self-referential canonical_equivalent_id FK; Zod ProviderAuth discriminated union (api_key | platform | none) per §3.4 - model-profiles.ts: Drizzle table with FK to providers (onDelete: restrict) - rate-cards.ts: Drizzle table with composite index on (provider_id, model, effective_from) for cost-lookup query - index.ts: barrel re-export Auth union is additively extensible without schema migration (column is JSON). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ate_cards One test file per migration covering: - Table created with expected columns (name, type, NOT NULL, default) - Unique constraints enforced (name column) - FK constraints enforced (provider_id, WITH foreign_keys=ON) - Re-running the migration is a no-op (withCrashRecovery checkpoint gate) - down() drops the table and is idempotent 13 tests, 102 expect() calls. All pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er and registry - db-init.ts: adds migrateInferenceProviders, migrateInferenceModelProfiles, migrateInferenceRateCards to the startup migrationSteps array (after existing migrations; providers runs before model_profiles since there is a FK dependency) - registry.ts: adds MIGRATION_REGISTRY entries for versions 48-50 with down() functions so validateMigrationState doesn't warn about unknown checkpoints and rollbackMemoryMigration can reverse inference migrations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0d16ba944d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| CREATE INDEX IF NOT EXISTS idx_rate_cards_provider_model_effective_from | ||
| ON rate_cards (provider_id, model, effective_from) |
There was a problem hiding this comment.
Enforce uniqueness on rate-card version key
Make the (provider_id, model, effective_from) key unique instead of a plain index. Right now the migration allows multiple rows for the same provider/model/effective timestamp, so repeated seed/backfill runs or manual inserts can create duplicate versions and make downstream pricing resolution ambiguous (different rows can be returned for what should be one version).
Useful? React with 👍 / 👎.
Autofixed by eslint --fix: sorts imports alphabetically within each import group across test files, schema/index.ts, db-init.ts, and registry.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Schema files are a public API intended for use in PR 2+ (seed/backfill) and PR 4 (dispatcher). Marking as library entry so knip doesn't flag them as unused files during development of the stacked PR chain. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…index UNIQUE Addresses Codex review: a plain index allows duplicate rows for the same provider/model/timestamp, making cost-lookup ambiguous. The version key (provider_id, model, effective_from) must be unique since each entry represents one pricing version at a point in time. Updated the migration DDL, Drizzle schema (uniqueIndex), and added a UNIQUE constraint test to the 003-rate-cards test suite. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
wrap PR 1/4 — merge-ready ✅
Ready for merge. @noa PR 2 (seed/backfill) and PR 3 (read CLIs) are blocked on this. |
|
Superseded by v1 redesign. Closing per pivot: dropping the data-driven providers approach (Sidd's pushback held — providers need code anyway, OpenRouter-style edge cases force code changes). New direction is auth-first: a single |
Summary
This is PR 1 of 4 in the inference providers redesign (design doc:
scratch/inference-providers/DESIGN.md).assistant/src/inference/migrations/:001-providers.ts—providerstable (§3.1)002-model-profiles.ts—model_profilestable (§3.2)003-rate-cards.ts—rate_cardstable (§3.3)assistant/src/inference/schema/for each table, including a Zod discriminated-union schema forproviders.auth(§3.4)memory/db-init.ts) and inMIGRATION_REGISTRY(memory/migrations/registry.ts) sovalidateMigrationStatecan track them androllbackMemoryMigrationcan reverse themDesign references
providers—id(uuid PK),name(unique),contract(closed enum),base_url,auth(JSON, validated by Zod),is_canonical,canonical_revision,canonical_equivalent_id(self-ref FK for mode-flip pairing),disabled,modality(default'chat'), timestamps.model_profiles— FK toproviders.idwithON DELETE RESTRICT.nameunique,modelfree-form text (§11.2 — no validation at schema layer).system_prompt,temperature,max_tokensnullable.rate_cards— Insert-only versioned history keyed by(provider_id, model, effective_from).cache_write_cost_per_1mandcache_read_cost_per_1mnullable for Anthropic-style cache pricing. Noupdated_at(rows are never updated per §3.3).Schema decisions
providers.authstored asTEXT NOT NULL(raw JSON). TheProviderAuthZod schema ininference/schema/providers.tsvalidates it at the service layer (PR 2+). The three v1 variants areapi_key,platform, andnoneper §3.4. The Zod union is additively extensible without a schema migration.canonical_equivalent_idis a nullable self-referential FK onproviders. SQLite supports this via deferred constraint checking.INTEGER(epoch ms), consistent with all other tables in the daemon.Migration approach
Each migration uses
withCrashRecovery(the daemon's checkpoint-based idempotency guard),CREATE TABLE IF NOT EXISTS, andCREATE INDEX IF NOT EXISTS. They are idempotent and safe to re-run on every startup.down()functions drop the respective tables for rollback support. Checkpoint keys follow themigration_inference_*_v1convention and are registered inMIGRATION_REGISTRYat versions 48-50.Test plan
bun test src/inference/migrations— 13 tests pass across 3 filesbun typecheck— no errorsPRAGMA foreign_keys=ON), re-run idempotency,down()drops table and is idempotentStack
🤖 Generated with Claude Code