What
providers/google-gemini/src/ai/common/Gemini_Pricing.ts:118:
"imagen-4.0-generate-001": { currency: "USD", input: 0.03, output: 0.03 },
0.03 is Imagen's per-image list price. Every field on ModelPricing is per 1M tokens. Executed at 2d36880:
getGeminiModelPricing("imagen-4.0-generate-001") = {"currency":"USD","input":0.03,"output":0.03}
cost a host computes for one image on a 50-token prompt:
(50 / 1e6) * 0.03 = $0.0000015
Imagen 4 list price: $0.03-0.04 per image
under-report factor: 20,000x
At any realistic prompt length the reported cost of an Imagen call rounds to zero next to the real bill.
This is already known, and the guard that would catch it exempts it
packages/test/src/test/ai-provider/inferAdvertisesRegistered.test.ts:303-313, written this window while closing #909, says so:
Gemini's flash and pro image models genuinely bill by token — text tokens in, image tokens out. imagen-4.0-generate-001 is the doubtful one: $0.03 is Imagen's per-IMAGE list price sitting in a per-1M-token field, which is the unit confusion this assertion exists to surface. Left recorded rather than changed here, because correcting it means either a per-image field on ModelPricing or dropping a real rate, and neither is a test's call.
That is the right call for a test. It is also the correct diagnosis, and it names the fix. The consequence of leaving it is that assertPricingMatchesModality — the assertion built specifically to stop a fabricated unit — reports green over the one fabricated unit left in the tree, via namedByTable: ["imagen-4.0-generate-001", "gemini-3.1-flash-image"] (:348).
Note the exemption list mixes two different justifications under one key: gemini-3.1-flash-image and gemini-3-pro-image are exempt because they genuinely bill by token, which is correct and permanent; imagen-4.0-generate-001 is exempt because nobody has anywhere to put its number, which is a defect with a deadline.
Root cause
packages/ai/src/model/ModelPricing.ts:22-33, 76-81:
export interface ModelPricingBase {
input?: number;
output?: number;
cached?: number;
cacheWrite?: number | { cacheWrite5m?: number; cacheWrite1h?: number };
cacheStoragePerHour?: number;
}
export interface ModelPricing extends ModelPricingBase {
currency: string;
batch?: ModelPricingBase;
usageTiers?: ModelUsageTier[];
timingTiers?: ModelTimingTier[];
}
Every field is per-1M-tokens. There is no perImage, no perRequest, no unit discriminator. Three vendors in this repo bill in a unit it cannot express:
- Imagen / DALL-E / Grok image — per image
- OpenRouter — a
request fee and an image fee per route (see the companion issue)
- OpenAI
tts-1 / whisper-1 — per character and per minute
Proposed fix
-
Add the missing unit to ModelPricingBase, e.g.
/** Charged once per request regardless of tokens (OpenRouter routes, some search tools). */
perRequest?: number;
/** Charged per generated image. A card carrying this is not a token card. */
perImage?: number;
and teach CostEstimate to add them. Two fields, both optional, no migration.
-
Then imagen-4.0-generate-001 becomes { currency: "USD", perImage: 0.03 } and quotesTokenRate() (pricingMatchesModality.ts:32-39) returns false for it, so it leaves namedByTable by the assertion's own rule — which is what assertPricingMatchesModality's stale-entry check at :82-96 is already written to demand.
-
Replace namedByTable: readonly string[] with readonly { id: string; because: string }[] so an exemption records why. Today the list cannot distinguish "bills by token, correctly exempt forever" from "we have nowhere to put the real number".
If (1) is too large for one change, the interim honest answer is to drop the imagen-4.0-generate-001 row — unpriced is the documented correct answer for a model the table cannot price (ModelPricing.ts:105-112), and reporting $0.0000015 for a $0.03 call is worse than reporting nothing.
Found during the 2026-09-14 review of providers/. Verified against origin/main @ 2d36880. Companion to the OpenRouter per-image issue.
What
providers/google-gemini/src/ai/common/Gemini_Pricing.ts:118:0.03is Imagen's per-image list price. Every field onModelPricingis per 1M tokens. Executed at2d36880:At any realistic prompt length the reported cost of an Imagen call rounds to zero next to the real bill.
This is already known, and the guard that would catch it exempts it
packages/test/src/test/ai-provider/inferAdvertisesRegistered.test.ts:303-313, written this window while closing #909, says so:That is the right call for a test. It is also the correct diagnosis, and it names the fix. The consequence of leaving it is that
assertPricingMatchesModality— the assertion built specifically to stop a fabricated unit — reports green over the one fabricated unit left in the tree, vianamedByTable: ["imagen-4.0-generate-001", "gemini-3.1-flash-image"](:348).Note the exemption list mixes two different justifications under one key:
gemini-3.1-flash-imageandgemini-3-pro-imageare exempt because they genuinely bill by token, which is correct and permanent;imagen-4.0-generate-001is exempt because nobody has anywhere to put its number, which is a defect with a deadline.Root cause
packages/ai/src/model/ModelPricing.ts:22-33, 76-81:Every field is per-1M-tokens. There is no
perImage, noperRequest, no unit discriminator. Three vendors in this repo bill in a unit it cannot express:requestfee and animagefee per route (see the companion issue)tts-1/whisper-1— per character and per minuteProposed fix
Add the missing unit to
ModelPricingBase, e.g.and teach
CostEstimateto add them. Two fields, both optional, no migration.Then
imagen-4.0-generate-001becomes{ currency: "USD", perImage: 0.03 }andquotesTokenRate()(pricingMatchesModality.ts:32-39) returnsfalsefor it, so it leavesnamedByTableby the assertion's own rule — which is whatassertPricingMatchesModality's stale-entry check at:82-96is already written to demand.Replace
namedByTable: readonly string[]withreadonly { id: string; because: string }[]so an exemption records why. Today the list cannot distinguish "bills by token, correctly exempt forever" from "we have nowhere to put the real number".If (1) is too large for one change, the interim honest answer is to drop the
imagen-4.0-generate-001row — unpriced is the documented correct answer for a model the table cannot price (ModelPricing.ts:105-112), and reporting $0.0000015 for a $0.03 call is worse than reporting nothing.Found during the 2026-09-14 review of
providers/. Verified againstorigin/main@2d36880. Companion to the OpenRouter per-image issue.