FN-6869: add Codex model pricing
Add explicit OpenAI Codex pricing so token analytics can estimate Codex run costs. - Add openai-codex pricing entries for GPT-5 Codex variants and codex-mini-latest. - Bump the pricing verification date and document why Codex keys must use the openai-codex provider. - Cover Codex pricing and lookup behavior in model-pricing tests. - Add a changeset for the published Fusion package. Files changed: .changeset/fn-6869-codex-pricing.md | 7 ++++ packages/core/src/__tests__/model-pricing.test.ts | 37 ++++++++++++++++++- packages/core/src/model-pricing.ts | 44 ++++++++++++++++++++++- 3 files changed, 86 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-6869 Fusion-Task-Lineage: 4bf119db-0401-4b18-b389-4a762e665cfc
This commit is contained in:
7
.changeset/fn-6869-codex-pricing.md
Normal file
7
.changeset/fn-6869-codex-pricing.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
Add pricing entries for OpenAI Codex models used through the `openai-codex` provider, so Command Center token analytics can estimate costs for Codex runs instead of showing them as unavailable.
|
||||
|
||||
This is marked minor because it expands the set of priced models surfaced by the published CLI/dashboard without changing existing pricing behavior.
|
||||
@@ -34,6 +34,34 @@ describe("model-pricing", () => {
|
||||
expect(result.usd).toBeCloseTo(10.0, 2);
|
||||
});
|
||||
|
||||
it("prices OpenAI Codex GPT-5 models instead of reporting unavailable", () => {
|
||||
// gpt-5-codex: input $1.25/1M, output $10/1M.
|
||||
// 1,000,000 input + 200,000 output = 1.25 + 2.00 = 3.25
|
||||
const result = costFor(
|
||||
{ ...ZERO, inputTokens: 1_000_000, outputTokens: 200_000 },
|
||||
{ provider: "openai-codex", model: "gpt-5-codex" },
|
||||
);
|
||||
expect(result.unavailable).toBe(false);
|
||||
expect(result.usd).not.toBeNull();
|
||||
expect(result.usd).toBeCloseTo(3.25, 2);
|
||||
});
|
||||
|
||||
it("prices Codex mini latest instead of reporting unavailable", () => {
|
||||
// codex-mini-latest: input $1.50/1M, output $6/1M, cached input $0.375/1M.
|
||||
const result = costFor(
|
||||
{
|
||||
...ZERO,
|
||||
inputTokens: 1_000_000,
|
||||
outputTokens: 500_000,
|
||||
cachedTokens: 1_000_000,
|
||||
},
|
||||
{ provider: "openai-codex", model: "codex-mini-latest" },
|
||||
);
|
||||
expect(result.unavailable).toBe(false);
|
||||
expect(result.usd).not.toBeNull();
|
||||
expect(result.usd).toBeCloseTo(4.875, 3);
|
||||
});
|
||||
|
||||
it("returns unavailable + null usd for an unknown model (never guesses)", () => {
|
||||
const result = costFor(
|
||||
{ ...ZERO, inputTokens: 1_000_000 },
|
||||
@@ -134,6 +162,12 @@ describe("model-pricing", () => {
|
||||
).toBe(MODEL_PRICING["openai:gpt-4o"]);
|
||||
});
|
||||
|
||||
it("resolves OpenAI Codex models by explicit provider:model keys", () => {
|
||||
expect(
|
||||
lookupPricing({ provider: " OpenAI-Codex ", model: " GPT-5-Codex " }),
|
||||
).toBe(MODEL_PRICING["openai-codex:gpt-5-codex"]);
|
||||
});
|
||||
|
||||
it("falls back to a bare model id when provider is unset", () => {
|
||||
expect(lookupPricing({ model: "gemini-2.5-pro" })).toBe(
|
||||
MODEL_PRICING["google:gemini-2.5-pro"],
|
||||
@@ -147,12 +181,13 @@ describe("model-pricing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("seeds Anthropic, OpenAI, and Google providers", () => {
|
||||
it("seeds Anthropic, OpenAI Codex, OpenAI, and Google providers", () => {
|
||||
const providers = new Set(
|
||||
Object.keys(MODEL_PRICING).map((k) => k.split(":")[0]),
|
||||
);
|
||||
expect(providers).toContain("anthropic");
|
||||
expect(providers).toContain("openai");
|
||||
expect(providers).toContain("openai-codex");
|
||||
expect(providers).toContain("google");
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* The date the rates in {@link MODEL_PRICING} were last verified, ISO-8601.
|
||||
* Bump this whenever you edit a rate. Surfaced in the UI as "prices as of".
|
||||
*/
|
||||
export const pricingAsOf = "2026-06-15";
|
||||
export const pricingAsOf = "2026-06-21";
|
||||
|
||||
/**
|
||||
* Pricing entries older than this (relative to a caller-supplied `now`) are
|
||||
@@ -233,6 +233,48 @@ export const MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
|
||||
// ── OpenAI Codex ────────────────────────────────────────────────────
|
||||
// OpenAI has no separate cache-write charge → cacheWrite = input rate.
|
||||
/*
|
||||
* FNXC:CommandCenter 2026-06-21-12:14:
|
||||
* Codex runs store the `openai-codex` provider, so pricing must be keyed as `openai-codex:<modelId>` instead of relying on the OpenAI provider or bare-model fallback. Keep these entries explicit so Command Center token cost does not show `unavailable`; rates mirror OpenAI GPT-5 Codex pricing, and `pricingAsOf` must be bumped on every rate edit.
|
||||
*/
|
||||
"openai-codex:gpt-5-codex": {
|
||||
inputPer1M: 1.25,
|
||||
outputPer1M: 10,
|
||||
cacheReadPer1M: 0.125,
|
||||
cacheWritePer1M: 1.25,
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
"openai-codex:gpt-5.1-codex": {
|
||||
inputPer1M: 1.25,
|
||||
outputPer1M: 10,
|
||||
cacheReadPer1M: 0.125,
|
||||
cacheWritePer1M: 1.25,
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
"openai-codex:gpt-5.2-codex": {
|
||||
inputPer1M: 1.25,
|
||||
outputPer1M: 10,
|
||||
cacheReadPer1M: 0.125,
|
||||
cacheWritePer1M: 1.25,
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
"openai-codex:gpt-5.3-codex": {
|
||||
inputPer1M: 1.25,
|
||||
outputPer1M: 10,
|
||||
cacheReadPer1M: 0.125,
|
||||
cacheWritePer1M: 1.25,
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
"openai-codex:codex-mini-latest": {
|
||||
inputPer1M: 1.5,
|
||||
outputPer1M: 6,
|
||||
cacheReadPer1M: 0.375,
|
||||
cacheWritePer1M: 1.5,
|
||||
source: "openai.com/api/pricing",
|
||||
},
|
||||
|
||||
// ── Google Gemini ───────────────────────────────────────────────────
|
||||
// No distinct cache-write token charge → cacheWrite = input rate.
|
||||
"google:gemini-2.5-pro": {
|
||||
|
||||
Reference in New Issue
Block a user