diff --git a/docs/settings-reference.md b/docs/settings-reference.md index bd871a78..8eae3f01 100644 --- a/docs/settings-reference.md +++ b/docs/settings-reference.md @@ -148,7 +148,7 @@ provider's model, command and API-key variable separate. "provider": "openai", "providerConfigs": { "openai": { - "model": "gpt-5.4-nano" + "model": "gpt-5.6-luna" }, "claude-api": { "model": "claude-haiku-4-5" @@ -181,7 +181,7 @@ Provider defaults: | `claude-cli` | `haiku` | None | `model`, `claudeCliPath`, `effort` | | `command` | None | None | `command` | | `claude-api` | `claude-haiku-4-5` | `ANTHROPIC_API_KEY` | `model`, `apiKeyEnv`, `maxTokens`, `effort` | -| `openai` | `gpt-5.4-nano` | `OPENAI_API_KEY` | `model`, `baseUrl`, `apiKeyEnv`, `maxTokens`, `effort` | +| `openai` | `gpt-5.6-luna` | `OPENAI_API_KEY` | `model`, `baseUrl`, `apiKeyEnv`, `maxTokens`, `effort` | | `gemini` | `gemini-3.1-flash-lite` | `GEMINI_API_KEY` | `model`, `apiKeyEnv`, `maxTokens`, `effort` | | `openrouter` | None | `OPENROUTER_API_KEY` | `model`, `apiKeyEnv`, `maxTokens`, `effort` | | `off` | None | None | None | diff --git a/src/llm/providers/openai.ts b/src/llm/providers/openai.ts index c12734fa..6b9d48bf 100644 --- a/src/llm/providers/openai.ts +++ b/src/llm/providers/openai.ts @@ -6,7 +6,7 @@ import { openaiCompatibleComplete } from "./openai-compatible.ts"; import type { ProviderCall, ProviderDescriptor } from "../types.ts"; export const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1"; -export const DEFAULT_OPENAI_MODEL = "gpt-5.4-nano"; +export const DEFAULT_OPENAI_MODEL = "gpt-5.6-luna"; export const openaiProvider: ProviderDescriptor = { name: "openai", diff --git a/src/pricing.ts b/src/pricing.ts index e9051a8a..66a4bf19 100644 --- a/src/pricing.ts +++ b/src/pricing.ts @@ -16,12 +16,24 @@ export interface Price { // OpenAI/Codex and Gemini cached input is represented in the shared cacheRead bucket. // Override any of these via $ARGUS_CONFIG_DIR/pricing.json. const DEFAULTS: Record = { - opus: { input: 15, output: 75, cacheRead: 1.5, cacheWrite5m: 18.75, cacheWrite1h: 30 }, + fable: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 }, + mythos: { input: 10, output: 50, cacheRead: 1, cacheWrite5m: 12.5, cacheWrite1h: 20 }, + opus: { input: 5, output: 25, cacheRead: 0.5, cacheWrite5m: 6.25, cacheWrite1h: 10 }, + // Opus 4.1 (deprecated) and Opus 4 (retired) kept their pre-Opus-4.5 rate. + "opus-legacy": { input: 15, output: 75, cacheRead: 1.5, cacheWrite5m: 18.75, cacheWrite1h: 30 }, + // Sonnet 5 introductory pricing runs through 2026-08-31, then reverts to the `sonnet` rate below. + "sonnet-5": { input: 2, output: 10, cacheRead: 0.2, cacheWrite5m: 2.5, cacheWrite1h: 4 }, sonnet: { input: 3, output: 15, cacheRead: 0.3, cacheWrite5m: 3.75, cacheWrite1h: 6 }, haiku: { input: 1, output: 5, cacheRead: 0.1, cacheWrite5m: 1.25, cacheWrite1h: 2 }, + // Haiku 3.5, retired except on Bedrock/Google Cloud. + "haiku-legacy": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite5m: 1, cacheWrite1h: 1.6 }, + "gpt-5.6": { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 }, + "gpt-5.6-terra": { input: 2, output: 12, cacheRead: 0.2, cacheWrite5m: 0, cacheWrite1h: 0 }, + "gpt-5.6-luna": { input: 0.2, output: 1.2, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 }, "gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite5m: 0, cacheWrite1h: 0 }, "gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite5m: 0, cacheWrite1h: 0 }, "gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite5m: 0, cacheWrite1h: 0 }, + "gpt-5.4-nano": { input: 0.2, output: 1.25, cacheRead: 0.02, cacheWrite5m: 0, cacheWrite1h: 0 }, "gpt-5.3": { input: 1.75, output: 14, cacheRead: 0.175, cacheWrite5m: 0, cacheWrite1h: 0 }, "gpt-5": { input: 1.25, output: 10, cacheRead: 0.125, cacheWrite5m: 0, cacheWrite1h: 0 }, "codex-mini": { input: 1.5, output: 6, cacheRead: 0.375, cacheWrite5m: 0, cacheWrite1h: 0 }, @@ -48,11 +60,29 @@ const unpriced = new Set(); /** Resolve a model id to its price family, or null if unknown / synthetic. */ function priceFor(model: string, usage?: Usage): Price | null { const m = model.toLowerCase(); - if (m.includes("opus")) return table.opus!; - if (m.includes("sonnet")) return table.sonnet!; - if (m.includes("haiku")) return table.haiku!; + if (m.includes("fable")) return table.fable!; + if (m.includes("mythos")) return table.mythos!; + if (m.includes("opus")) { + // Opus 4.1 (e.g. "claude-opus-4-1-...") and bare Opus 4 (e.g. "claude-opus-4-20250514") + // kept the older, higher rate; Opus 4.5 and later (incl. "opus-5") share the current one. + if (/opus-4-1(?!\d)/.test(m) || /opus-4(?:$|-\d{8})/.test(m)) return table["opus-legacy"]!; + return table.opus!; + } + if (m.includes("sonnet")) { + // Sonnet 5 (not 4.x) is on introductory pricing through 2026-08-31. + if (/sonnet-5(?:$|-)/.test(m)) return table["sonnet-5"]!; + return table.sonnet!; + } + if (m.includes("haiku")) { + if (m.includes("haiku-3") || m.includes("haiku-3.5")) return table["haiku-legacy"]!; + return table.haiku!; + } if (m.includes("codex-mini")) return table["codex-mini"]!; + if (m.includes("gpt-5.6-luna")) return table["gpt-5.6-luna"]!; + if (m.includes("gpt-5.6-terra")) return table["gpt-5.6-terra"]!; + if (m.includes("gpt-5.6")) return table["gpt-5.6"]!; if (m.includes("gpt-5.5")) return table["gpt-5.5"]!; + if (m.includes("gpt-5.4-nano")) return table["gpt-5.4-nano"]!; if (m.includes("gpt-5.4-mini") || m.includes("gpt-5.4 mini")) return table["gpt-5.4-mini"]!; if (m.includes("gpt-5.4")) return table["gpt-5.4"]!; if (m.includes("gpt-5.3") || m.includes("gpt-5.2")) return table["gpt-5.3"]!; diff --git a/test/dashboard-views.test.ts b/test/dashboard-views.test.ts index e713961d..c67ae260 100644 --- a/test/dashboard-views.test.ts +++ b/test/dashboard-views.test.ts @@ -58,8 +58,8 @@ describe("usage builders", () => { ]; const { bySource } = buildUsageBySource(rows, [{ source: "claude", sessions: 3 }]); const claude = bySource.find((s) => s.name === "claude")!; - // opus input 15/Mtok + haiku input 1/Mtok, priced separately then summed. - expect(claude.cost).toBeCloseTo((1000 * 15 + 1000 * 1) / 1e6, 9); + // opus input 5/Mtok + haiku input 1/Mtok, priced separately then summed. + expect(claude.cost).toBeCloseTo((1000 * 5 + 1000 * 1) / 1e6, 9); expect(claude.meta?.sessions).toBe(3); }); diff --git a/test/pricing.test.ts b/test/pricing.test.ts index 2998adda..ea5c6f8e 100644 --- a/test/pricing.test.ts +++ b/test/pricing.test.ts @@ -6,10 +6,19 @@ const z = { input: 0, output: 0, cacheRead: 0, cacheWrite5m: 0, cacheWrite1h: 0 describe("cost", () => { test("prices 1M input tokens per model family at list rates", () => { expect(cost({ ...z, input: 1_000_000 }, "claude-sonnet-4-6")).toBeCloseTo(3, 6); - expect(cost({ ...z, input: 1_000_000 }, "claude-opus-4-8")).toBeCloseTo(15, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-opus-4-8")).toBeCloseTo(5, 6); expect(cost({ ...z, input: 1_000_000 }, "claude-haiku-4-5-20251001")).toBeCloseTo(1, 6); }); + test("prices legacy/deprecated Claude tiers and the newest models at their own rates", () => { + expect(cost({ ...z, input: 1_000_000 }, "claude-opus-4-1-20250805")).toBeCloseTo(15, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-opus-4-20250514")).toBeCloseTo(15, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-opus-5")).toBeCloseTo(5, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-sonnet-5")).toBeCloseTo(2, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-haiku-3-5-20241022")).toBeCloseTo(0.8, 6); + expect(cost({ ...z, input: 1_000_000 }, "claude-fable-5")).toBeCloseTo(10, 6); + }); + test("prices output and cache classes", () => { expect(cost({ ...z, output: 1_000_000 }, "claude-sonnet-4-6")).toBeCloseTo(15, 6); expect(cost({ ...z, cacheRead: 1_000_000 }, "claude-sonnet-4-6")).toBeCloseTo(0.3, 6); @@ -27,6 +36,10 @@ describe("cost", () => { expect(cost({ ...z, output: 1_000_000 }, "gpt-5.4-mini")).toBeCloseTo(4.5, 6); expect(cost({ ...z, input: 1_000_000 }, "gpt-5.3-codex")).toBeCloseTo(1.75, 6); expect(cost({ ...z, cacheRead: 1_000_000 }, "codex-mini-latest")).toBeCloseTo(0.375, 6); + expect(cost({ ...z, input: 1_000_000 }, "gpt-5.6-luna")).toBeCloseTo(0.2, 6); + expect(cost({ ...z, output: 1_000_000 }, "gpt-5.6-terra")).toBeCloseTo(12, 6); + expect(cost({ ...z, input: 1_000_000 }, "gpt-5.6")).toBeCloseTo(5, 6); + expect(cost({ ...z, output: 1_000_000 }, "gpt-5.4-nano")).toBeCloseTo(1.25, 6); }); test("prices Gemini models and the 2.5 Pro long-context tier", () => {