Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions tools/cost_calculator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 2 additions & 7 deletions tools/cost_calculator/app.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
Expand All @@ -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.`;
}
}

Expand Down
2 changes: 1 addition & 1 deletion tools/cost_calculator/calculator.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
34 changes: 11 additions & 23 deletions tools/cost_calculator/calculator.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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]],
Expand All @@ -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"];
Expand All @@ -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, []);
}
Expand All @@ -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/);
}
Expand Down
Loading