diff --git a/tools/cost_calculator/README.md b/tools/cost_calculator/README.md index b12812f..9f9a430 100644 --- a/tools/cost_calculator/README.md +++ b/tools/cost_calculator/README.md @@ -84,12 +84,11 @@ The GPT Live meter is hourly and is divided by 60 for USD-per-minute pricing. meter; `gpt-realtime` uses the original output meter. - **Azure Realtime** uses the documented Voice Live **Pro** tier. **Azure Realtime Fast** uses a **user-specified Lite-tier reference**, - not a verified published Fast tariff. Azure Realtime uses managed native - speech-to-speech with no separate STT or Azure HD charge. Azure Realtime Fast - uses **Realtime text response + Azure HD**: Lite-tier native audio input, - text input/output, and standard-speech output meters, without native audio - output charges. Both use the existing Central India tier meters and appear - in their respective managed pipeline selection, comparison, and CSV export. + not a verified published Fast tariff. Both use **native speech-to-speech + billing**: tier-specific text and native audio input/output meters, including + replayed audio history, with no separate STT or Azure HD charge. Both use the + existing Central India tier meters and appear in managed native + speech-to-speech selection, comparison, and CSV export. Neither adds a GPT Live session charge; no BYOM or alternate-pipeline prices are inferred from GPT models. Fast's assumption is labeled in the UI and CSV. The editable audio tokens/second remain planning assumptions, not verified diff --git a/tools/cost_calculator/app.mjs b/tools/cost_calculator/app.mjs index 98854f9..66c705b 100644 --- a/tools/cost_calculator/app.mjs +++ b/tools/cost_calculator/app.mjs @@ -112,9 +112,7 @@ function rateEditor(model, pipeline, book) { if (pipeline === "live") $("price-note").textContent += " GPT Live uses Azure OpenAI's South India Global voice rate: $3/hour = $0.05/minute in this snapshot. Backend model usage is charged separately. GPT Live generates its own audio, so no Azure HD charge is added."; if (model.oss && (hosting === "byom" || book.usesGlobalReference)) $("price-note").textContent += " No OSS cached-input meter was found; reused text is conservatively charged at full input price."; if (model.managedPipeline) { - $("price-note").textContent += model.managedPipeline === "hybrid" ? - " Azure Realtime Fast uses native audio input, text response, and Azure HD speech output. No native audio output, separate Azure STT, or GPT Live session charge is added." : - " Azure Realtime uses native audio only in this calculator; no separate Azure STT, HD voice, or GPT Live session charge is added."; + $("price-note").textContent += ` ${model.name} uses native audio input/output billing; no separate Azure STT, HD voice, or GPT Live session charge is added.`; $("price-note").textContent += " Audio token rates remain editable assumptions, not verified Azure Realtime tokenization."; } if (model.tierAssumption) $("price-note").textContent += " Azure Realtime Fast uses the user-specified Voice Live Lite tier reference, not a verified published Fast tariff."; @@ -140,10 +138,7 @@ function renderOverview(a, model, pipeline, result, book) { "Managed tariff unavailable: showing an AOAI Global model-price + Central India BYO speech reference instead. This does not establish managed model support." : `Economic scenario only. ${$("hosting").value === "managed" ? `Central India managed-tier reference: ${model.tier || "unverified"}.` : "Model rates: South India Global. STT: Central India BYO reference."} Azure HD: Central India. Confirm model and transport availability.`; if (model.managedPipeline) { - const audioNote = model.managedPipeline === "hybrid" ? - "Native audio input + text response + Central India Azure HD output; no native audio output charge." : - "Native audio only; no separate STT/HD charge."; - $("model-note").textContent = `${model.tierAssumption ? "User-specified Lite-tier reference, not a verified published Azure Realtime Fast tariff." : "Central India Voice Live Pro tier reference."} ${audioNote} Confirm regional availability and actual audio token rates.`; + $("model-note").textContent = `${model.tierAssumption ? "User-specified Lite-tier reference, not a verified published Azure Realtime Fast tariff." : "Central India Voice Live Pro tier reference."} Native audio input/output billing; no separate STT/HD charge. Confirm regional availability and actual audio token rates.`; } } diff --git a/tools/cost_calculator/calculator.mjs b/tools/cost_calculator/calculator.mjs index 77f08c2..c65b275 100644 --- a/tools/cost_calculator/calculator.mjs +++ b/tools/cost_calculator/calculator.mjs @@ -16,7 +16,7 @@ export const MODELS = [ { id: "gpt-realtime-1.5", name: "GPT Realtime 1.5", tier: "Pro", realtime: true }, { id: "azure-realtime", name: "Azure Realtime", tier: "Pro", realtime: true, managedPipeline: "native" }, { id: "azure-realtime-fast", name: "Azure Realtime Fast", tier: "Lite", realtime: true, - managedPipeline: "hybrid", tierAssumption: true }, + managedPipeline: "native", tierAssumption: true }, { id: "gpt-4.1", name: "GPT-4.1", tier: "Pro" }, { id: "gpt-4.1-mini", name: "GPT-4.1 mini", tier: "Std" }, { id: "gpt-4.1-nano", name: "GPT-4.1 nano", tier: "Lite" }, diff --git a/tools/cost_calculator/calculator.test.mjs b/tools/cost_calculator/calculator.test.mjs index b7e709a..58b1239 100644 --- a/tools/cost_calculator/calculator.test.mjs +++ b/tools/cost_calculator/calculator.test.mjs @@ -30,7 +30,7 @@ test("every supported model/hosting/pipeline combination resolves without a miss } }); -test("Azure Realtime uses Pro native audio and Fast uses Lite realtime text plus HD", () => { +test("Azure Realtime uses Pro and Fast uses Lite native speech-to-speech billing", () => { for (const [id, tier, expected] of [ ["azure-realtime", "Pro", [4, 0.4, 16, 32, 0.4, 64]], ["azure-realtime-fast", "Lite", [0.11, 0.04, 0.44, 4, 0.04, 12]], @@ -39,6 +39,7 @@ test("Azure Realtime uses Pro native audio and Fast uses Lite realtime text plus assert.equal(m.tier, tier); assert.equal(Boolean(m.tierAssumption), id === "azure-realtime-fast"); const pipeline = m.managedPipeline; + assert.equal(pipeline, "native"); const { rates, sources, usesGlobalReference } = getRates(snapshot, m, "managed", pipeline); assert.equal(usesGlobalReference, false); const keys = ["textInput", "textCached", "textOutput", "audioInput", "audioCached", "audioOutput"]; @@ -49,27 +50,14 @@ test("Azure Realtime uses Pro native audio and Fast uses Lite realtime text plus }); const result = calculate({ ...DEFAULTS, nativeText: 150 }, pipeline, rates); const [textInput, textCached, textOutput, audioInput, audioCached, audioOutput] = expected; - if (pipeline === "hybrid") { - close(rates.speechOutput, 25); - assert.equal(sources.speechOutput.meterName, "Voice Live API Lite - Standard Speech Audio Output Tokens"); - assert.equal(sources.speechOutput.armRegionName, "centralindia"); - close(result.list, (5000 * textInput + 15225 * textCached + 150 * textOutput + - 360 * audioInput + 540 * audioCached + 720 * 25) / 1e6); - assert.ok(result.rows.some(r => r.key === "speechOutput")); - assert.ok(result.rows.every(r => !["audioOutput", "speechInput", "liveMinute"].includes(r.key))); - const noCache = calculate({ ...DEFAULTS, cachePercent: 0 }, pipeline, rates); - close(noCache.list, (20225 * textInput + 900 * audioInput + 150 * textOutput + 720 * 25) / 1e6); - const reasoning = calculate({ ...DEFAULTS, reasoning: 100 }, pipeline, rates); - close(reasoning.list - result.list, 100 * textOutput / 1e6); - const silent = calculate({ ...DEFAULTS, silencePercent: 100 }, pipeline, rates); - close(silent.list, (5000 * textInput + 15225 * textCached + 150 * textOutput) / 1e6); - } else { - close(result.list, (5000 * textInput + 15225 * textCached + 150 * textOutput + - 360 * audioInput + 1620 * audioCached + 720 * audioOutput) / 1e6); - assert.ok(result.rows.every(r => !["speechInput", "speechOutput", "liveMinute"].includes(r.key))); - const noCache = calculate({ ...DEFAULTS, cachePercent: 0 }, pipeline, rates); - close(noCache.list, (20000 * textInput + 1980 * audioInput + 720 * audioOutput) / 1e6); - } + close(result.list, (5000 * textInput + 15225 * textCached + 150 * textOutput + + 360 * audioInput + 1620 * audioCached + 720 * audioOutput) / 1e6); + assert.ok(result.rows.some(r => r.key === "audioOutput")); + assert.ok(result.rows.every(r => !["speechInput", "speechOutput", "liveMinute"].includes(r.key))); + const noCache = calculate({ ...DEFAULTS, cachePercent: 0 }, pipeline, rates); + close(noCache.list, (20000 * textInput + 1980 * audioInput + 720 * audioOutput) / 1e6); + const silent = calculate({ ...DEFAULTS, nativeText: 150, silencePercent: 100 }, pipeline, rates); + close(silent.list, (5000 * textInput + 15225 * textCached + 150 * textOutput) / 1e6); close(result.listPerMinute, result.list / 1.5); assert.deepEqual(result.missing, []); } @@ -78,7 +66,7 @@ test("Azure Realtime uses Pro native audio and Fast uses Lite realtime text plus test("Azure models use their correct managed pipeline, never substituted with GPT BYOM rates", () => { for (const id of ["azure-realtime", "azure-realtime-fast"]) { for (const hosting of ["managed", "byom"]) for (const pipeline of ["native", "hybrid", "cascade", "live"]) { - const supported = hosting === "managed" && pipeline === (id === "azure-realtime-fast" ? "hybrid" : "native"); + const supported = hosting === "managed" && pipeline === "native"; assert.equal(supportsScenario(model(id), hosting, pipeline), supported); if (!supported) assert.throws(() => getRates(snapshot, model(id), hosting, pipeline), /no BYOM or alternative-pipeline rates/); }