Skip to content

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 →