sanjayacloud / ai-model-usage-tracker
Accurately track AI model usage, tokens, and cost in Laravel with automatic instrumentation, reporting, and an Inertia dashboard.
Package info
github.com/sanjayacloud/ai-model-usage-tracker
pkg:composer/sanjayacloud/ai-model-usage-tracker
Fund package maintenance!
Requires
- php: ^8.3
- illuminate/support: ^12.0||^13.0
Requires (Dev)
- inertiajs/inertia-laravel: ^2.0
- larastan/larastan: ^3.9
- laravel/ai: ^0.4
- laravel/pao: ^1.0
- laravel/pint: ^1.29
- orchestra/testbench: ^10.0||^11.0
- pestphp/pest: ^4.6||^5.0
- pestphp/pest-plugin-laravel: ^4.1||^5.0
- pestphp/pest-plugin-type-coverage: ^4.0||^5.0
- phpstan/extension-installer: ^1.4
Suggests
- inertiajs/inertia-laravel: Required to render the bundled Inertia/Vue usage dashboard.
- laravel/ai: Automatic usage instrumentation for the first-party Laravel AI SDK.
- prism-php/prism: Capture usage from Prism responses via capturePrism().
README
AI Model Usage Tracker
Accurately track AI model usage in your Laravel app: token counts, computed cost, latency, success/failure, per-user attribution, and per-conversation attribution. Capture usage automatically from the first-party laravel/ai SDK (plus Prism and raw HTTP clients), or record it manually with a fluent API. Includes a headless reporting layer, budget alerts, and an optional Inertia/Vue dashboard.
Features
- Automatic capture for the
laravel/aiSDK — no code changes required. - Computed cost from a configurable per-model pricing table (input, output, cache read/write, reasoning).
- Per-request, per-user, and per-conversation attribution.
- Reporting API — totals, breakdowns by model/provider/operation, daily trends, top consumers.
- Budgets with threshold events, retention pruning, and an optional Inertia/Vue dashboard.
- Sync or queued persistence.
Requirements
- PHP 8.3+
- Laravel 12 or 13
- (Optional)
laravel/aifor automatic instrumentation - (Optional) Inertia + Vue for the bundled dashboard
How it works
Every AI request becomes one row in the ai_usage_records table, written through a single pipeline regardless of how it was captured:
manual API ─┐
laravel/ai ─┤
Prism ─┼─▶ UsageTracker ─▶ CostCalculator ─▶ (sync | queue) ─▶ ai_usage_records
raw HTTP ─┘
Getting started
Step 1 — Install
composer require sanjayacloud/ai-model-usage-tracker
The service provider and AiModelUsageTracker facade are auto-discovered.
Step 2 — Publish and run the migration
php artisan vendor:publish --tag="ai-model-usage-tracker-migrations"
php artisan migrate
This creates the ai_usage_records table.
Step 3 — Publish the config (recommended)
php artisan vendor:publish --tag="ai-model-usage-tracker-config"
This writes config/ai-model-usage-tracker.php, where you control pricing, recording mode, instrumentation, budgets, retention, and the dashboard.
Step 4 — Capture your first usage
If you use laravel/ai, you're already done — the next agent prompt or embedding call is recorded automatically (see Automatic capture).
To record manually from anywhere:
use AiModelUsageTracker\AiModelUsageTracker\Facades\AiModelUsageTracker; AiModelUsageTracker::track() ->provider('openai') ->model('gpt-4o') ->tokens(prompt: 1200, completion: 350) ->record();
Step 5 — Read it back
use AiModelUsageTracker\AiModelUsageTracker\Reporting\UsageReporter; app(UsageReporter::class)->totals(); // ['records' => 1, 'total_tokens' => 1550, 'total_cost' => 0.0065, ...]
That's the full loop: capture → cost → report.
Capturing usage
Automatic capture (laravel/ai)
Enabled by default. The package listens to the SDK's events (AgentPrompted, AgentStreamed, EmbeddingsGenerated, ImageGenerated, AudioGenerated, TranscriptionGenerated, ProviderFailedOver) and records tokens, model, provider, latency, failures — and the conversation id when the call is part of a laravel/ai conversation.
Toggle it in config:
'instrumentation' => [ 'laravel-ai' => true, 'prism' => false, 'http' => false, ],
Manual API (fluent builder)
use AiModelUsageTracker\AiModelUsageTracker\Facades\AiModelUsageTracker; use AiModelUsageTracker\AiModelUsageTracker\Enums\Operation; AiModelUsageTracker::for($user) // attribute to any Eloquent model ->provider('openai') ->model('gpt-4o') ->operation(Operation::Chat) ->tokens(prompt: 1200, completion: 350, cacheRead: 800, reasoning: 120) ->conversation($conversationId) // optional, for per-conversation reporting ->latency(840) ->meta(['feature' => 'support-bot']) ->record();
Available builder methods: provider(), model(), operation(), tokens(), latency(), status(), failed(), streamed(), for(), conversation(), invocation(), meta(), startedAt(), endedAt(), record().
Prism
$response = Prism::text()->using('openai', 'gpt-4o')->withPrompt('...')->generate(); AiModelUsageTracker::capturePrism($response, $user);
Enable it in config (instrumentation.prism => true).
Raw HTTP clients
Set instrumentation.http => true to parse OpenAI/Anthropic-style usage blocks from outgoing HTTP responses automatically.
Attribution
Per-user (or any model)
Use for() on the manual builder, or set a global resolver so every recorded row is attributed automatically (great for auto-instrumentation):
use AiModelUsageTracker\AiModelUsageTracker\Facades\AiModelUsageTracker; // e.g. in a service provider or middleware AiModelUsageTracker::resolveTrackableUsing(fn () => auth()->user());
Per-conversation
When you use laravel/ai conversations, the conversation id is captured automatically on each recorded row (conversation_id). You can then aggregate usage for a single conversation:
use AiModelUsageTracker\AiModelUsageTracker\Reporting\UsageReporter; $summary = app(UsageReporter::class)->forConversation($conversationId); $summary['totals']; // ['records' => 5, 'total_tokens' => 8120, 'total_cost' => 0.0123, ...] $summary['by_model']; // per-model breakdown for that conversation
For manual records, pass the id explicitly with ->conversation($conversationId).
Per-request usage in your responses
Each request is recorded individually, so you can surface its cost inline:
use AiModelUsageTracker\AiModelUsageTracker\Facades\AiModelUsageTracker; use AiModelUsageTracker\AiModelUsageTracker\Http\Resources\UsageResource; $record = AiModelUsageTracker::forInvocation($invocationId); // or ::latest() return response()->json([ 'answer' => $answer, 'usage' => $record ? (new UsageResource($record))->toArray($request) : null, ]);
$record->toUsageArray() returns a compact block of tokens, cost, currency, and latency.
Cost accuracy
Costs are computed from a configurable pricing table in config/ai-model-usage-tracker.php, with bundled rates for common OpenAI, Anthropic, and Gemini models, expressed per 1,000,000 tokens. Cached read/write and reasoning tokens are priced separately when rates are provided.
Requests for models not in the table are recorded with a zero cost and a
pricing_missingflag inmetadata, so gaps are auditable rather than silently wrong.
Adding pricing for a model
If a model shows $0 cost, add its rates.
- Publish the config (Step 3 above) if you haven't.
- Add the model under its provider in
pricing.models. Rates are per 1M tokens:
'pricing' => [ 'currency' => 'USD', 'models' => [ 'gemini' => [ 'gemini-3.1-flash-lite' => ['input' => 0.25, 'output' => 1.50, 'cache_read' => 0.025], ], ], ],
- Clear the config cache so the new rate is picked up:
php artisan config:clear
New records for that model will now be priced. Existing zero-cost rows are left untouched (recompute them yourself if you need a backfill).
Supported rate keys: input, output, cache_write, cache_read, reasoning. Missing cache_write/cache_read fall back to input; missing reasoning falls back to output.
Reporting
use AiModelUsageTracker\AiModelUsageTracker\Reporting\UsageReporter; $reporter = app(UsageReporter::class); $reporter->totals(); // records, tokens, cost, failures $reporter->forConversation($id); // totals + per-model breakdown for one conversation $reporter->byModel(); // grouped by model $reporter->byProvider(); // grouped by provider $reporter->byOperation(); // grouped by operation $reporter->dailyTrend(); // cost/tokens per day $reporter->topConsumers(); // biggest spenders (attributed models)
Every method except forConversation() accepts optional $from/$to DateTimeInterface bounds.
CLI summary:
php artisan ai-usage:report --days=30
Budgets
Define spending caps in config; a BudgetThresholdReached event fires the moment a threshold is crossed:
'budgets' => [ 'monthly' => ['period' => 'month', 'limit' => 500.0, 'thresholds' => [0.8, 1.0]], ],
Listen for the event to send alerts:
use AiModelUsageTracker\AiModelUsageTracker\Events\BudgetThresholdReached; Event::listen(BudgetThresholdReached::class, function (BudgetThresholdReached $event) { // notify your team });
Recording mode
Set recording.mode to sync (default, always exact) or queue to offload writes to a job for high-throughput apps:
'recording' => [ 'mode' => 'queue', 'queue' => ['connection' => null, 'queue' => null], ],
Retention
php artisan ai-usage:prune --days=90
Set retention_days in config and schedule the command in routes/console.php (or app/Console/Kernel.php):
use Illuminate\Support\Facades\Schedule; Schedule::command('ai-usage:prune')->daily();
Dashboard (Inertia/Vue)
Requires a host app using Inertia + Vue.
Step 1 — Publish the page component
php artisan vendor:publish --tag="ai-model-usage-tracker-assets"
Step 2 — Build assets
npm run build
Step 3 — Define the access gate
The dashboard is served at the configured dashboard.path (default /ai-usage) and is protected by the viewAiUsageDashboard gate:
use Illuminate\Support\Facades\Gate; Gate::define('viewAiUsageDashboard', fn ($user) => $user->isAdmin());
Adjust dashboard.path and dashboard.middleware in config as needed.
Testing
composer test
This runs static analysis (PHPStan/Larastan), code style (Pint), 100% type coverage, and the Pest test suite.
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
AI Model Usage Tracker is open-sourced software licensed under the MIT license.