Skip to content

Cost Estimation ​

Automatically estimate and track LLM costs in real-time with Mindwave's built-in cost estimation. Monitor spending, set budgets, and optimize costs based on actual usage data.

Overview ​

LLM costs can quickly add up in production. Mindwave automatically calculates the estimated cost of every LLM operation based on token usage and provider pricing, giving you real-time visibility into spending.

Why Cost Estimation? ​

Without cost tracking:

  • Surprise billing at month-end
  • No visibility into expensive operations
  • Difficult to attribute costs to features or users
  • Hard to optimize spending

With Mindwave cost estimation:

  • Real-time cost tracking for every LLM call
  • Automatic cost calculation based on token usage
  • Cost data stored with traces for analysis
  • Budget alerts and monitoring
  • Data-driven optimization decisions

How It Works ​

Mindwave automatically:

  1. Captures token usage from LLM API responses
  2. Looks up pricing from configuration based on provider and model
  3. Calculates cost using the formula: (input_tokens / 1000) × input_price + (output_tokens / 1000) × output_price
  4. Stores cost with the trace in database or OTLP backend
  5. Fires events with cost data for real-time monitoring

Quick Start ​

Enable Cost Estimation ​

Cost estimation is enabled by default:

dotenv
MINDWAVE_COST_ESTIMATION_ENABLED=true

Make an LLM Call ​

Costs are calculated automatically:

php
use Mindwave\Mindwave\Facades\Mindwave;

$response = Mindwave::llm()
    ->generateText('What is Laravel?');

// Cost is automatically calculated and stored

Query Costs ​

Access cost data from traces:

php
use Mindwave\Mindwave\Observability\Models\Trace;

$cost = Trace::whereDate('created_at', today())
    ->sum('estimated_cost');

echo "Today's spend: \$" . number_format($cost, 2);

Cost Calculation ​

Pricing Formula ​

Input Cost  = (Input Tokens / 1000) × Price per 1000 Input Tokens
Output Cost = (Output Tokens / 1000) × Price per 1000 Output Tokens
Total Cost  = Input Cost + Output Cost

Example Calculation ​

For GPT-4:

Pricing (per 1000 tokens):
  Input:  $0.03
  Output: $0.06

Usage:
  Input tokens:  1,500
  Output tokens: 800

Calculation:
  Input cost:  (1,500 / 1000) × $0.03 = $0.045
  Output cost: (800 / 1000) × $0.06   = $0.048
  Total cost:  $0.045 + $0.048        = $0.093

Supported Providers ​

Mindwave includes pricing for:

  • OpenAI - GPT-4, GPT-3.5, embeddings
  • Anthropic - Claude 3 (Opus, Sonnet, Haiku)
  • Mistral AI - All models
  • Google - Gemini Pro
  • Ollama - Self-hosted (zero cost)

Configuration ​

Default Pricing ​

Mindwave ships with current pricing for major providers:

php
// config/mindwave-tracing.php

'cost_estimation' => [
    'enabled' => true,

    'pricing' => [
        'openai' => [
            'gpt-4' => [
                'input' => 0.03,
                'output' => 0.06,
            ],
            'gpt-3.5-turbo' => [
                'input' => 0.0005,
                'output' => 0.0015,
            ],
        ],

        'anthropic' => [
            'claude-3-opus' => [
                'input' => 0.015,
                'output' => 0.075,
            ],
            'claude-3-sonnet' => [
                'input' => 0.003,
                'output' => 0.015,
            ],
        ],
    ],
],

Learn more: Configuration

Update Pricing ​

Providers change pricing occasionally. Update your configuration:

php
'pricing' => [
    'openai' => [
        'gpt-4' => [
            'input' => 0.03,   // Updated price
            'output' => 0.06,  // Updated price
        ],
    ],
],

Custom Provider Pricing ​

Add pricing for custom or self-hosted models:

php
'pricing' => [
    'custom-provider' => [
        'my-model-v1' => [
            'input' => 0.002,
            'output' => 0.004,
        ],
    ],
],

Model Version Handling ​

Mindwave automatically handles versioned model names:

php
// API returns: "gpt-4-0613"
// Mindwave looks up: "gpt-4-0613" → not found
// Falls back to: "gpt-4" → found ✓

// API returns: "claude-3-opus-20240229"
// Mindwave looks up: "claude-3-opus-20240229" → not found
// Falls back to: "claude-3-opus" → found ✓

You don't need to configure every model version.

Accessing Cost Data ​

From Events ​

Listen to LlmResponseCompleted events:

php
use Mindwave\Mindwave\Observability\Events\LlmResponseCompleted;
use Illuminate\Support\Facades\Event;

Event::listen(LlmResponseCompleted::class, function ($event) {
    if ($event->hasCostEstimate()) {
        echo "Cost: {$event->getFormattedCost()}\n";
        echo "Tokens: {$event->getTotalTokens()}\n";
    }
});

From Database ​

Query the mindwave_traces table:

php
use Mindwave\Mindwave\Observability\Models\Trace;

// Get today's cost
$todayCost = Trace::whereDate('created_at', today())
    ->sum('estimated_cost');

// Get expensive traces
$expensive = Trace::where('estimated_cost', '>', 0.10)
    ->orderBy('estimated_cost', 'desc')
    ->get();

Cost Attribution ​

Track costs by user, feature, or any dimension:

php
// In your LLM call
$tracer = app(TracerManager::class);
$span = $tracer->getCurrentSpan();

$span?->setAttribute('app.user_id', auth()->id());
$span?->setAttribute('app.feature', 'chat');

// Query costs by user
$userCost = DB::table('mindwave_traces')
    ->whereJsonContains('metadata->user_id', $userId)
    ->sum('estimated_cost');

Cost Monitoring ​

Daily Cost Report ​

php
use Mindwave\Mindwave\Observability\Models\Trace;
use Carbon\Carbon;

$dailyCosts = Trace::whereDate('created_at', '>=', now()->subDays(30))
    ->selectRaw('DATE(created_at) as date')
    ->selectRaw('SUM(estimated_cost) as cost')
    ->selectRaw('COUNT(*) as calls')
    ->groupBy('date')
    ->orderBy('date')
    ->get();

foreach ($dailyCosts as $day) {
    echo "{$day->date}: \${$day->cost} ({$day->calls} calls)\n";
}

Monthly Budget Tracking ​

php
$monthlyBudget = 100.00; // $100/month

$spent = Trace::whereMonth('created_at', now()->month)
    ->sum('estimated_cost');

$percentUsed = ($spent / $monthlyBudget) * 100;

echo "Budget: \${$monthlyBudget}\n";
echo "Spent: \${$spent} ({$percentUsed}%)\n";

if ($percentUsed > 80) {
    echo "WARNING: 80% of budget used!\n";
}

Budget Alerts ​

Set up real-time alerts:

php
use Mindwave\Mindwave\Observability\Events\LlmResponseCompleted;

Event::listen(LlmResponseCompleted::class, function ($event) {
    // Alert on expensive single calls
    if ($event->costEstimate > 0.50) {
        Slack::send("Expensive LLM call: \${$event->costEstimate}");
    }

    // Check daily budget
    $dailySpend = Trace::whereDate('created_at', today())
        ->sum('estimated_cost');

    if ($dailySpend > 10.00) {
        Mail::to('admin@example.com')
            ->send(new DailyBudgetExceeded($dailySpend));
    }
});

Cost Optimization ​

Identify Expensive Operations ​

Find operations that consume the most budget:

php
use Mindwave\Mindwave\Observability\Models\Span;

// Most expensive models
$expensiveModels = Span::selectRaw('request_model')
    ->selectRaw('COUNT(*) as call_count')
    ->selectRaw('SUM(input_tokens + output_tokens) as total_tokens')
    ->whereNotNull('request_model')
    ->groupBy('request_model')
    ->orderByDesc('total_tokens')
    ->get();

foreach ($expensiveModels as $model) {
    echo "{$model->request_model}: {$model->total_tokens} tokens\n";
}

Compare Provider Costs ​

php
$providerCosts = Span::selectRaw('provider_name')
    ->selectRaw('COUNT(*) as calls')
    ->selectRaw('SUM(input_tokens + output_tokens) as tokens')
    ->whereNotNull('provider_name')
    ->groupBy('provider_name')
    ->get();

// Calculate estimated costs
foreach ($providerCosts as $provider) {
    echo "{$provider->provider_name}:\n";
    echo "  Calls: {$provider->calls}\n";
    echo "  Tokens: {$provider->tokens}\n\n";
}

Optimize Model Selection ​

Test cheaper models for comparable quality:

php
// Test GPT-3.5 vs GPT-4 for your use case
$gpt35Cost = Span::where('request_model', 'like', 'gpt-3.5%')
    ->whereDate('created_at', today())
    ->sum(\DB::raw('input_tokens + output_tokens'));

$gpt4Cost = Span::where('request_model', 'like', 'gpt-4%')
    ->whereDate('created_at', today())
    ->sum(\DB::raw('input_tokens + output_tokens'));

echo "GPT-3.5 tokens: {$gpt35Cost}\n";
echo "GPT-4 tokens: {$gpt4Cost}\n";

// If quality is similar, GPT-3.5 is ~30x cheaper

Best Practices ​

1. Update Pricing Regularly ​

Providers change pricing. Set a reminder to review quarterly:

php
// config/mindwave-tracing.php

'cost_estimation' => [
    // Document last update
    'pricing_last_updated' => '2025-01-15',
    'pricing' => [
        // ... pricing data
    ],
],

2. Validate Against Invoices ​

Compare estimated costs to actual invoices:

php
// Get January costs
$estimated = Trace::whereMonth('created_at', 1)
    ->whereYear('created_at', 2025)
    ->sum('estimated_cost');

echo "Estimated: \${$estimated}\n";
echo "Invoice: \$X.XX (from OpenAI)\n";
echo "Difference: " . (($invoice - $estimated) / $invoice * 100) . "%\n";

3. Set Budget Limits ​

Implement hard limits to prevent overspending:

php
class BudgetManager
{
    public function checkBudget(): bool
    {
        $dailyLimit = 10.00;
        $spent = Trace::whereDate('created_at', today())
            ->sum('estimated_cost');

        if ($spent >= $dailyLimit) {
            Cache::put('llm:disabled', true, now()->endOfDay());
            return false;
        }

        return true;
    }
}

// Before making LLM call
if (!app(BudgetManager::class)->checkBudget()) {
    throw new Exception('Daily LLM budget exceeded');
}

4. Track Costs by Feature ​

Attribute costs to understand which features are expensive:

php
// When calling LLM
$tracer = app(TracerManager::class);
$span = $tracer->getCurrentSpan();
$span?->setAttribute('app.feature', 'document-summarization');

// Query feature costs
$featureCosts = DB::table('mindwave_traces')
    ->whereNotNull('metadata->feature')
    ->selectRaw("JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.feature')) as feature")
    ->selectRaw('SUM(estimated_cost) as cost')
    ->groupBy('feature')
    ->get();

5. Use Caching to Reduce Costs ​

Cache LLM responses to avoid repeated calls:

php
use Illuminate\Support\Facades\Cache;

function generateWithCache(string $prompt): string
{
    $key = 'llm:' . md5($prompt);

    return Cache::remember($key, 3600, function () use ($prompt) {
        return Mindwave::llm()->generateText($prompt);
    });
}

// Track cache savings
$cacheHits = Cache::get('llm:cache:hits', 0);
$avgCost = 0.05; // Average cost per call
$savings = $cacheHits * $avgCost;

echo "Cache saved: \${$savings}\n";

Per-User Cost Tracking ​

Track and bill users based on their LLM usage:

php
<?php

namespace App\Listeners;

use Mindwave\Mindwave\Observability\Events\LlmResponseCompleted;
use Illuminate\Contracts\Queue\ShouldQueue;

class TrackUserCost implements ShouldQueue
{
    public function handle(LlmResponseCompleted $event): void
    {
        if (!$event->hasCostEstimate()) {
            return;
        }

        $userId = $event->getMetadata('user_id');

        if (!$userId) {
            return;
        }

        // Store cost for billing
        DB::table('user_llm_usage')->insert([
            'user_id' => $userId,
            'cost_usd' => $event->costEstimate,
            'tokens' => $event->getTotalTokens(),
            'model' => $event->model,
            'created_at' => now(),
        ]);

        // Update user balance
        User::find($userId)
            ->decrement('llm_credits', $event->getTotalTokens() / 1000);
    }
}

Cost Dashboards ​

Build custom cost dashboards:

php
class CostDashboardController extends Controller
{
    public function index()
    {
        $summary = [
            'today' => Trace::whereDate('created_at', today())
                ->sum('estimated_cost'),

            'this_month' => Trace::whereMonth('created_at', now()->month)
                ->sum('estimated_cost'),

            'this_year' => Trace::whereYear('created_at', now()->year)
                ->sum('estimated_cost'),
        ];

        $daily = Trace::selectRaw('DATE(created_at) as date')
            ->selectRaw('SUM(estimated_cost) as cost')
            ->where('created_at', '>=', now()->subDays(30))
            ->groupBy('date')
            ->orderBy('date')
            ->get();

        $byModel = Span::selectRaw('request_model')
            ->selectRaw('COUNT(*) as calls')
            ->selectRaw('SUM(input_tokens + output_tokens) as tokens')
            ->whereNotNull('request_model')
            ->groupBy('request_model')
            ->orderByDesc('tokens')
            ->get();

        return view('admin.costs', [
            'summary' => $summary,
            'daily' => $daily,
            'byModel' => $byModel,
        ]);
    }
}

Troubleshooting ​

Costs Not Calculated ​

Check if cost estimation is enabled:

php
php artisan tinker
>>> config('mindwave-tracing.cost_estimation.enabled')
=> true

Verify pricing exists for your model:

php
>>> config('mindwave-tracing.cost_estimation.pricing.openai.gpt-4')
=> ["input" => 0.03, "output" => 0.06]

Inaccurate Costs ​

Compare to actual invoice:

php
$estimated = Trace::whereMonth('created_at', 10)->sum('estimated_cost');
$invoice = 1234.56; // From provider

$diff = abs($invoice - $estimated);
$percentOff = ($diff / $invoice) * 100;

echo "Difference: {$percentOff}%\n";

if ($percentOff > 5) {
    echo "Check if pricing is up to date\n";
}

Update pricing if consistently off:

Check provider pricing pages:

Missing Pricing for New Models ​

Add pricing for newly released models:

php
'pricing' => [
    'openai' => [
        'gpt-4o' => [  // New model
            'input' => 0.005,
            'output' => 0.015,
        ],
    ],
],