ykachala / meter
Token accounting, multi-provider cost calculation, and budget guardrails for PHP AI apps. Know what every LLM call costs and stop runaway spend before it happens.
Requires
- php: >=8.3
- psr/clock: ^1.0
- psr/event-dispatcher: ^1.0
- psr/simple-cache: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
Suggests
- open-telemetry/api: Emit per-call cost spans for distributed tracing
- promphp/prometheus_client_php: Export usage & cost as Prometheus metrics
This package is auto-updated.
Last update: 2026-06-02 09:06:52 UTC
README
Token accounting, multi-provider cost calculation, and budget guardrails for PHP AI apps. Measure what every LLM call costs, attribute it to a user/team/feature, and enforce spend caps before the request goes out.
Why this exists (the 2026 gap)
AI features in production have one universal failure mode: a surprise invoice. A single buggy loop, a jailbroken agent, or a viral launch can burn thousands of dollars in tokens overnight.
The Python ecosystem solved this with LiteLLM (virtual keys, per-key budgets, cost tracking) and SaaS like Langfuse. PHP teams in 2026 have observability (Langfuse has a PHP-friendly tracing path) but no native, in-process metering layer that can also refuse a call when a budget is blown. Observability tells you what you spent yesterday; Ykachala Meter stops you spending it today.
What it does
- Token estimation — pre-flight token counts per provider so you can budget before sending.
- Cost calculation — a versioned, dated
PriceBookfor OpenAI, Anthropic, Google, Mistral, and custom models (input/output/cached-input/reasoning tiers). - Usage recording — every call recorded with model, tokens, cost, latency, and arbitrary tags (
user_id,team,feature). - Budgets — hard and soft caps scoped to any tag (per user, per team, per day/month). Soft caps fire an event; hard caps throw before the request leaves.
- Exporters — roll usage up to Prometheus, OpenTelemetry, or a plain SQL table.
Install
composer require ykachala/meter
Quick start
use Ykachala\Meter\Meter; use Ykachala\Meter\PriceBook; use Ykachala\Meter\Budget\Budget; $meter = new Meter( prices: PriceBook::default(), // dated, versioned price tables store: new Psr16UsageStore($cache), ); // 1. Enforce a budget BEFORE the call $meter->budget(Budget::monthly('team:acme', usd: 500.00)); $estimate = $meter->estimate('claude-opus-4-8', $messages); $meter->assertWithinBudget('team:acme', $estimate); // throws BudgetExceeded if over // 2. Record actual usage AFTER the call $usage = $meter->record( model: 'claude-opus-4-8', inputTokens: $response->usage->input, outputTokens: $response->usage->output, tags: ['team' => 'acme', 'user' => $userId, 'feature' => 'support-bot'], ); echo $usage->cost->format(); // "$0.0241" echo $meter->spent('team:acme')->format(); // running total this period
Budgets
$meter->budget(Budget::daily('user:'.$userId, usd: 2.00, soft: 1.50)); $meter->on(BudgetSoftLimitReached::class, function ($e) { Notification::send($e->scope, new ApproachingLimit($e->spent, $e->limit)); }); // Hard limit throws BudgetExceeded — catch it and degrade gracefully (smaller model, queue, deny).
Wrap any client
Meter is a measurement layer, not a client. Wrap whatever you already use:
$response = $meter->wrap( fn () => $prism->text()->using('anthropic', 'claude-opus-4-8')->generate(), tags: ['feature' => 'summarizer'], ); // usage auto-recorded from the response; budgets enforced around the closure
Architecture
src/
├── Meter.php # facade: estimate / record / wrap / budget / spent
├── PriceBook.php # dated price tables, per-model, per-tier
├── Money.php # integer-cents money value object
├── Usage.php # one recorded call (tokens, cost, latency, tags)
├── Budget/ # Budget definitions + enforcement + events
├── Store/ # PSR-16, SQL, in-memory usage stores
└── Export/ # Prometheus / OpenTelemetry / SQL exporters
Roadmap
- Money + Usage value objects, integer-cents arithmetic
- PriceBook with dated tables (input/output/cached/reasoning tiers)
- Token estimators per provider family
- Usage stores (in-memory, PSR-16, SQL)
- Budget definitions + soft/hard enforcement + events
- Prometheus & OpenTelemetry exporters
See CLAUDE.md for the full phase plan and conventions.
License
MIT