LLM Cost Calculator — PHP source
Estimate LLM costs per request or per month at billion-token scale — with realistic prompt-cache hit rates, four-lane pricing, and side-by-side model comparison from a dated pricing snapshot.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* LLM Cost Calculator — per-million-token cost math for LLM workloads.
*
* Language: PHP (8.1+, standard library only)
* Source: CosmoDev polyglot showcase port of the LLM Cost Calculator tool,
* ported from src/lib/llmCost.ts (the canonical TypeScript
* implementation), with the tokensPerDollar helper inlined from
* src/lib/ai/models.ts so this file stays dependency-free.
* Tool page: https://dev.cosmolabs.org/tools/llm-cost-calculator
* License: display source — part of CosmoDev's polyglot tool pages.
*
* Design goals:
* - Pure + deterministic; never throws.
* - Functionally equivalent to the TS reference: same inputs -> same outputs
* (a null rate means "unpriced" and propagates to a null cost).
* - Self-contained: stdlib only (no Composer packages, no model snapshot).
*
* Formula: ((inputTokens / 1e6) * inputPerM + (outputTokens / 1e6) * outputPerM)
* * requests * (batch ? 0.5 : 1). Model rates are NEVER hardcoded
* here — the caller supplies them (in the TS lib they flow from the
* model snapshot accessor; custom rates are the one exception).
*/
declare(strict_types=1);
namespace CosmoDev\LLMCost;
/** Multiplier applied to Batch API pricing (the standard 50% discount). */
const BATCH_DISCOUNT = 0.5;
/** Price pair for a model or a custom rate card. Null = unpriced. */
final class CostRates
{
public function __construct(
/** USD per 1M input tokens. */
public readonly ?float $inputPerM,
/** USD per 1M output tokens. */
public readonly ?float $outputPerM,
) {
}
}
/** One workload to price. */
final class CostInput
{
public function __construct(
/** Input tokens per request. */
public readonly int $inputTokens,
/** Output tokens per request. */
public readonly int $outputTokens,
/** Request count. */
public readonly int $requests = 1,
/** Apply the BATCH_DISCOUNT multiplier. */
public readonly bool $batch = false,
) {
}
}
/** Pricing projection of a model. The full AiModel record
* (src/lib/ai/models.ts) carries a dozen non-pricing fields; only these
* three matter to cost math. */
final class Model
{
public function __construct(
public readonly string $id,
public readonly ?float $inputPerM,
public readonly ?float $outputPerM,
) {
}
}
/** One row of a compareModels() result. */
final class ModelCost
{
public function __construct(
public readonly string $id,
public readonly ?float $inputPerM,
public readonly ?float $outputPerM,
/** costFor() with the model's rates; null when unpriced. */
public readonly ?float $cost,
/** Output tokens per USD: 1e6 / outputPerM (null-safe). */
public readonly ?float $tokensPerDollar,
) {
}
}
/**
* Cost in USD for a workload, or null when either rate is unpriced:
* ((inputTokens/1e6)·inputPerM + (outputTokens/1e6)·outputPerM) × requests
* × BATCH_DISCOUNT when batch.
*/
function costFor(CostRates $rates, CostInput $input): ?float
{
if ($rates->inputPerM === null || $rates->outputPerM === null) {
return null;
}
$base =
($input->inputTokens / 1_000_000) * $rates->inputPerM +
($input->outputTokens / 1_000_000) * $rates->outputPerM;
$multiplier = $input->requests * ($input->batch ? BATCH_DISCOUNT : 1);
return $base * $multiplier;
}
/** Project a model onto its CostRates pair. */
function ratesFor(Model $m): CostRates
{
return new CostRates(inputPerM: $m->inputPerM, outputPerM: $m->outputPerM);
}
/** Output tokens per USD: 1e6 / outputPerM. Null when unpriced. */
function tokensPerDollar(Model $m): ?float
{
return $m->outputPerM === null ? null : 1_000_000 / $m->outputPerM;
}
/**
* Cost every requested model for one workload. Unknown ids are dropped.
* Sort: cost asc, nulls last, ties by id asc.
*
* The TS original defaults $models to the live snapshot (allModels()); this
* port has no snapshot dependency, so the model list is always explicit.
* Ids are ASCII slugs, so strcmp matches the TS localeCompare ordering.
*
* @param list<string> $modelIds
* @param list<Model> $models
* @return list<ModelCost>
*/
function compareModels(array $modelIds, CostInput $input, array $models): array
{
$byId = [];
foreach ($models as $m) {
$byId[$m->id] = $m;
}
$rows = [];
foreach ($modelIds as $id) {
if (!isset($byId[$id])) {
continue;
}
$m = $byId[$id];
$rows[] = new ModelCost(
id: $id,
inputPerM: $m->inputPerM,
outputPerM: $m->outputPerM,
cost: costFor(ratesFor($m), $input),
tokensPerDollar: tokensPerDollar($m),
);
}
usort($rows, static function (ModelCost $a, ModelCost $b): int {
if ($a->cost === null || $b->cost === null) {
if ($a->cost === null && $b->cost === null) {
return strcmp($a->id, $b->id);
}
return $a->cost === null ? 1 : -1;
}
if ($a->cost === $b->cost) {
return strcmp($a->id, $b->id);
}
return $a->cost <=> $b->cost;
});
return $rows;
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →