Research, compare, and update shared AI model JSON for TypeScript, web, and Rust consumers. Covers text model tiers, image and video generation models, image tool models, release provenance, pricing data sourcing, and provider-cost metering against prepaid org credit. Use when bumping model versions, adding new models, updating pricing, or auditing model specs against provider documentation.
71
87%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
| File | Role |
|---|---|
data/ai/facts.json | Authored model identities, capabilities, provider bindings, published rates and provenance |
data/ai/service.json | Authored Grida membership/lifecycle, preferences, request presets and text tiers |
data/ai/inputs.json | Authored operation input schemas and documented x-grida-* validation rules |
data/ai/schemas/ | Structural JSON Schema envelopes for facts and service data |
data/ai/PROVENANCE.md | Research qualifications and source notes that do not belong in JSON comments |
packages/grida-ai-models/scripts/generate.mjs | Deterministic source generation and Rust bundle drift checks |
packages/grida-ai-models/src/models.ts | Generated factual literals plus handwritten TypeScript types and lookup helpers |
packages/grida-ai-models/src/grida/ | Generated service literals plus handwritten ordering and schema-1 compatibility helpers |
packages/grida-ai/schemas/ | Generated package-local input schemas and operation projections |
crates/grida-ai/data/ | Generated embedded Rust assets; never a separate authoring home |
editor/lib/ai/models.ts | AI Gateway + BYOK provider seam (service catalog from @grida/ai-models/grida) |
editor/lib/ai/ai.ts | toMills() + Replicate call shapes; re-aggregates the shared catalogue under ai.* |
editor/lib/ai/server.ts | AI seam: prepaid-credit gate, provider call, and post-flight usage ingest |
editor/lib/billing/metronome.ts | Organization credit entitlement, cached balance gate, and Metronome usage ledger |
editor/app/(www)/(ai)/ai/models/page.tsx | Public models catalog page |
docs/models/index.md | User-facing models & pricing documentation |
Author catalogue data in repository-root data/ai/. TypeScript packages, the
web API and the Rust CLI consume generated projections of those sources. Follow
the shared data guide and commit authored and
generated changes together.
Use lower_snake_case for authored domain fields. Preserve exact model/provider
IDs and standard JSON Schema keywords. The generator explicitly maps text-card
and cost fields to existing camelCase TypeScript API fields; preserve schema-1
wire spelling instead of renaming public fields during a data update.
Do not hand-edit generated:* literal blocks or package/crate JSON copies.
Types, lookup helpers, validation semantics and provider adapters remain
handwritten code. Update an explicit ID union or type outside generated blocks
when a new card requires it; a JSON binding alone does not implement a provider.
From the repository root, after editing JSON:
node packages/grida-ai-models/scripts/generate.mjs
pnpm --filter @grida/ai-models build
pnpm --filter @grida/ai build
node packages/grida-ai-models/scripts/generate.mjs --bundle
node packages/grida-ai-models/scripts/generate.mjs --bundle --checkThe first pass updates TS literals and package-local input schemas. The bundle pass uses freshly built TS consumers to derive operations, snapshots and service views, and writes the checked-in Rust assets. Cargo builds use these embedded assets without Node or network access. Builds/typechecks reject stale source projections; the CLI contract gate also checks the complete generated bundle.
Script: .agents/skills/ai-models/scripts/model_info.py (symlink to .tools/model_info.py)
# Text / language models
python .agents/skills/ai-models/scripts/model_info.py <model_id>
# Image models
python .agents/skills/ai-models/scripts/model_info.py --image <model_id>
python .agents/skills/ai-models/scripts/model_info.py --image --allDiscovery source: models.dev/api.json. Accepts exact IDs (anthropic/claude-sonnet-4.6) or substring search (gpt-5.4). Its release_date is a lead to verify, not authoritative provenance to copy into the catalogue.
Note: models.dev has per-token costs but not per-image tier breakdowns. For per-image pricing (OpenAI quality tiers, BFL flat rates), consult provider docs directly.
| Provider | URL |
|---|---|
| OpenAI | https://developers.openai.com/api/docs/models/<model_id> |
| Anthropic | https://docs.anthropic.com/en/docs/about-claude/models |
https://ai.google.dev/pricing | |
| BFL (Flux) | https://docs.bfl.ml/pricing |
| fal.ai | https://fal.ai/models/<endpoint-id> · pricing API: https://fal.ai/docs/documentation/model-apis/pricing |
| OpenRouter | https://openrouter.ai/<vendor>/<model> |
The same model has different ids — and different availability and pricing — across providers; an id is never portable. Two cataloguing patterns:
id is in that provider's format, and the provider field (or namespace) fixes the route.vendor/model,
e.g. google/veo-3.1) and carries a providers record (keyed by provider) of bindings,
each with its own call id + meter. Default-provider choice is deferred (see Video Models).
Pick a route with video.binding(card, provider).| Provider | Used in catalogue for | ID format / example |
|---|---|---|
| Vercel AI Gateway | text, image, video binding | google/veo-3.1-generate-001, bytedance/seedance-2.0 |
| Replicate | audio, image_tools | google/lyria-3, nightmareai/real-esrgan |
| fal.ai | video binding (+ image) | fal-ai/veo3.1, fal-ai/kling-video/v3/pro/image-to-video, fal-ai/flux/dev |
| OpenRouter | video binding | google/veo-3.1, google/veo-3.1-fast, google/veo-3.1-lite |
$0.40/s on both Vercel and fal, but fal also meters silent ($0.20/s) and 4K, while OpenRouter exposes only $0/MTok token pricing for video — no usable per-second meter (don't invent one).Keep one package with two explicit entries: @grida/ai-models for facts and
@grida/ai-models/grida for service policy. Service definitions consume facts;
the root entry never imports or re-exports Grida policy.
Add verified model facts independently of Grida admission. Manage Grida choices
in the service definitions, not source declaration order or provider timestamps.
The shared execution SDK (@grida/ai) retains its Grida defaults by explicitly
importing the service entry where needed. Its catalog store accepts an optional
snapshot or refresh URL; callers need not inject a catalog. Keep provider
execution and refresh lifecycle in the SDK and the schema-1 codec in the service
entry; do not restore agent-local adapters or duplicate membership.
Preference discipline: an optional default must be listed and nonlegacy. The independent order is partial; unknown and duplicate IDs are errors. Views sort default first, other active models before legacy, then explicit rank, label and ID. Explicit user selections are not replaced by a recommendation. Native subscription and custom-endpoint choices remain with their own runtime owners.
The catalogue states what is true and useful now. Its shape must never be a record of how recently someone got round to updating it — a stale entry is a wrong answer, not a conservative one.
data/ai/PROVENANCE.md. Otherwise the promotion expiring is a
silent cost increase. Recheck when that date passes: a promotion can also be
made permanent, which changes the fact, not the rule.legacy: true (projected as deprecated for existing consumers) is for a model someone might still reasonably pick — same
price as its successor, or better at something. Delete the entry when the
successor is strictly dominant (never worse on any axis, better on at least
one): a card nobody should choose is noise in every picker, and keeping it is
not caution.Removing a service member changes admission for consumers of the updated
catalogue; it is not merely picker cleanup. It does not require deleting factual
identity or imply upstream retirement. TS consumers configured to refresh a
published snapshot receive updates through that refresh lifecycle
(docs/wg/platform/hosted-ai.md). The Rust CLI embeds its catalogue and does not
refresh it at runtime: regenerate its assets and ship a new CLI version for
updated discovery data. Do not assume a remote catalogue update revokes an
installed binary's bundled knowledge. Hosted authorization remains a separate
runtime boundary. Preserve schema-1 membership and legacy fields when publishing;
installed snapshot clients ignore additive preferences. The v1 snapshot still
has broad GG/BYOK membership and per-family fallback behavior; adapter support
and authorization remain independent checks.
Every bundled entry carries a release object:
{
"date": "2026-07-09",
"basis": "model",
"source_url": "https://vendor.example/release-note"
}Use a YYYY-MM-DD date, or null only when an endpoint day is unknown.
basis is model or provider_endpoint.
The date means the earliest day the exact named model or variant became broadly
available. A public preview counts; a closed, invitation-only, or limited
preview does not. This is intrinsic model metadata, so adding a provider binding
does not change a basis: "model" release. Use basis: "provider_endpoint"
only when the release fact describes a serving route because no exact upstream
model launch can be established. An endpoint-shaped card may still use
basis: "model" when its exact underlying model and launch are documented.
Do not substitute any of these:
generated_atcreated timestampSource priority for release facts:
basis: "provider_endpoint" or when the vendor has no usable record.models.dev only to discover candidates; verify its date against one of the sources above.If no authoritative source establishes the exact day, keep date: null with an
HTTPS source showing the endpoint's history. Never infer a day from search-result
ordering, repository commit time, or Grida history. Base snapshot types keep the
field optional solely for older snapshots and custom models; every bundled card
must include it, and tests enforce valid calendar dates, complete provenance,
and the narrow null rule.
Author text facts in data/ai/facts.json under text.catalog. The generated TypeScript consumer exposes models.text.catalog: Record<CatalogId, ModelSpec>. Author Grida tier assignments in data/ai/service.json under tiers; each must resolve to a listed service member.
Authored fields to update per model:
id — gateway format: provider/model-namelabel — human-readable namerelease — grounded date, basis, and first-party source under the contract abovecontext_window, output_limit — use model_info.py as a discovery lead and verify against provider documentationcost — input, output, optional cache_read and cache_write, per 1M tokens; TS projects these to its existing camelCase fieldsAuthor image facts in data/ai/facts.json under image.models, and membership, legacy state, primary-provider choice and request presets in data/ai/service.json. The generated service view joins them. Editor consumers reach that joined view via import { ai } from "@/lib/ai/ai" (which also adds ai.toMills and ai.server.methods.*).
Three pricing schemes, modeled as discriminated union ImageModelPricing:
per_image_tiered — quality x size tiers (e.g. OpenAI)
{ type: "per_image_tiered", tiers: { "medium/1024x1024": 0.034, ... } }
per_image_flat — single price per image (e.g. BFL Flux)
{ type: "per_image_flat", usd: 0.06 }
per_token — charged by token (e.g. Google Gemini)
{ type: "per_token", input: 0.5, output: 3.0 }pricing — real provider data, one of the three types aboveavg_cost_usd — existing fallback billable-cost estimate, not a provider quote. Retained compatibility surface; do not treat it as independently verified pricing or expand it into service routing/billing policy.release — intrinsic model release; do not use a provider-binding datemin_width, max_width, min_height, max_height, sizes — dimension constraintsImageModelId type union outside the generated block in src/models.tsImage generation currently routes through the Vercel AI Gateway (gateway.image(id)); fal.ai is the main alternative for models the gateway lacks (see Providers & model IDs). For a new provider:
provider label for fal.ai / OpenRouter)Vendor type if neededLogos map on the models pageAuthor video facts in data/ai/facts.json under video.models; models.video.models is the generated TS consumer. Like image, a video card is canonical: id is provider-agnostic (vendor/model, e.g. google/veo-3.1) and holds intrinsic specs; per-provider routes live in providers, keyed by provider.
id (canonical), label, release, vendor, aspect_ratios, min_duration/max_duration, audio, url (original vendor's model card). Grida request default (resolution/aspect/duration/audio) belongs to the service view.providers: Partial<Record<VideoProvider, VideoProviderBinding>> — one binding per serving provider: provider, id, pricing, avg_cost_usd, optional url/deprecated. No preference order — the default-provider choice is deliberately deferred to the runtime. Look a route up with video.binding(card, provider).Cards catalogue the image-to-video route only (canvas-relevant; Grok's sole mode), so each binding has a single id — on fal the capability is keyed into the id (fal-ai/veo3.1/image-to-video). Don't add a per-capability endpoints map until a second capability is actually served: identical ids across capabilities are YAGNI, and divergent ones (other fal endpoints) are a new binding/id when needed.
provider is a bare routing tag — auth (incl. BYOK) is a runtime concern, not catalogue data, so there is no provider registry or byok flag. The catalogue's only job is to hold each provider's real id + rate.
avg_cost_usd (per binding) = its rate at the model's default (resolution, audio) × default duration, plus any required input-image surcharge. Video dwarfs image costs (Veo 3.1 ≈ $3.20 for an 8s 1080p clip). The current prepaid-credit gate checks a global balance floor, not an estimated per-request ceiling, so audit metering and bounded-overspend exposure before serving a new video route.
per_second, nested resolution → audio-mode → USD/s, with an optional
provider-native usd_per_input_image surcharge. The rate varies by both
resolution and whether audio is generated, so the keys are the exact
(resolution, mode) combos that provider serves & meters:
{ type: "per_second", usd_per_second: {
"720p": { audio: 0.4, silent: 0.2 }, // fal: meters both modes
"1080p": { audio: 0.4, silent: 0.2 },
"4k": { audio: 0.6, silent: 0.4 },
} }
// Vercel Veo omits "4k" + "silent" (gateway sells neither); Seedance lists only "audio" (bundled free).data/ai/facts.json and update the handwritten VideoModelId union outside generated blocks. Separately define service membership and request presets in data/ai/service.json; the chosen preset must be supported and priced by the route that executes it.VideoProviderBinding under its provider key, only with a verified rate (e.g. OpenRouter surfaces $0/MTok for video — not usable; leave it out).id shape only then.Author in data/ai/facts.json under image_tools.models; the generated TS consumer exposes models.image_tools.models. Flat cost_usd pricing via Replicate.
Grida Gateway (GG) usage is metered against the organization's prepaid AI credit. Unit: mills (1 mill = $0.001 USD).
ai.toMills(cost_usd) converts a provider cost to the integer usage unit.avg_cost_usd only where the provider does not
expose a more exact billable dimension.data/ai/ JSON sources; authored domain keys remain lower_snake_casegenerate.mjs --bundle --check passesrelease; date semantics and source priority were followedmodels.dev dates were treated as discovery hints and verified against authoritative sourcespnpm --filter @grida/ai-models test and pnpm --filter @grida/ai test); Rust catalogue/input tests pass (cargo test -p grida-ai --locked); repository typecheck passesdocs/models/index.md matches the code/ai/models page renders correctlyd086528
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.