lunetics/llm-cost-tracking-bundle
Composer 安装命令:
composer require lunetics/llm-cost-tracking-bundle
包简介
Symfony bundle that tracks LLM API costs and displays them in the Web Debug Toolbar and Profiler
README 文档
README
A Symfony bundle that tracks LLM API costs and displays them in the Web Debug Toolbar and Profiler.
Hooks into symfony/ai-bundle's TraceablePlatform to calculate per-request costs based on token usage, with support for input, output, cached, and thinking tokens.
Requirements
- PHP >= 8.2
- Symfony >= 7.0
- symfony/ai-bundle >= 0.4
Installation
composer require lunetics/llm-cost-tracking-bundle
If you are not using Symfony Flex, register the bundle manually:
// config/bundles.php return [ // ... Lunetics\LlmCostTrackingBundle\LuneticsLlmCostTrackingBundle::class => ['all' => true], ];
Features
- Web Debug Toolbar — shows total cost and call count for the current request
- Profiler Panel — per-call breakdown with model, tokens, and cost
- Per-model aggregation — costs grouped by model with totals
- Budget warnings — toolbar alerts when costs exceed a configurable threshold
- Color-coded costs — green/yellow/red based on configurable thresholds
- Dynamic pricing — automatically fetches live model pricing from models.dev, covering hundreds of models without any manual configuration
- Unconfigured model detection — warns when a model has no pricing data
- Injectable cost tracking — use
CostTrackerInterfacein your own services to access cost data outside the profiler - Extensible — implement
CostCalculatorInterfaceto provide custom pricing logic
Model Pricing
The bundle resolves pricing for a model using the following priority order:
- Your YAML config —
models:entries take precedence over everything else - Dynamic pricing from models.dev — the bundle fetches live pricing from models.dev (thousands of models) and caches it for 24 hours. When models.dev is unreachable, a bundled snapshot of ~3000 models is used as a fallback so known models are still priced correctly during outages
- Not found — cost is shown as zero with a warning in the profiler
This means most models work out of the box with no configuration. Your own entries always win.
All prices are in USD. The models.dev feed and any prices you configure are all treated as USD. There is no currency conversion; the
$prefix shown in the profiler is a literal dollar sign.
Configuration
# config/packages/lunetics_llm_cost_tracking.yaml lunetics_llm_cost_tracking: budget_warning: 0.50 # toolbar turns red when exceeded cost_thresholds: low: 0.01 # below = green medium: 0.10 # between low/medium = yellow, above = red logging: enabled: false # default: false — opt in to log per-request cost data via Monolog channel: 'ai' # Monolog channel (default: 'ai'); route it via your handlers dynamic_pricing: enabled: true # default: true — fetch live pricing from models.dev ttl: 86400 # cache duration in seconds (default: 24h, max: 7 days) models: my-custom-model: display_name: 'My Custom Model' provider: 'MyProvider' input_price_per_million: 1.00 output_price_per_million: 5.00 cached_input_price_per_million: 0.10 # optional thinking_price_per_million: 5.00 # optional
Logging
Logging is disabled by default. Enable it explicitly to have per-request LLM cost data written via Monolog:
lunetics_llm_cost_tracking: logging: enabled: true
Each request that makes at least one AI call produces:
- one
infolog per call (model, provider, token breakdown, cost) - one
infosummary log (total calls, cost, tokens) - one
warningif any models lack pricing configuration
Logs are emitted on kernel.terminate — after the response is sent — so there is no latency impact.
To route AI cost logs to a dedicated file, configure a Monolog handler for the ai channel:
# config/packages/monolog.yaml monolog: channels: [ai] handlers: ai_costs: type: stream path: '%kernel.logs_dir%/ai_costs.log' channels: [ai]
If the ai channel is not explicitly configured, logs fall through to your default handler.
To use a different channel name:
lunetics_llm_cost_tracking: logging: channel: 'llm'
Disabling Dynamic Pricing
If you want fully offline/air-gapped operation, or prefer explicit control over every model's price:
lunetics_llm_cost_tracking: dynamic_pricing: enabled: false
When disabled, the bundled snapshot continues to provide model coverage as a read-only baseline — no live HTTP requests are made, and the lunetics:llm:update-pricing command is removed from the container. Your explicit models: config always takes priority over the snapshot. This is the right choice for air-gapped or reproducible-pricing environments where you want stable costs without live API calls.
Upgrade note (v0.3): The old static list of bundled defaults has been replaced with a versioned pricing snapshot (
resources/pricing_snapshot.json). Whendynamic_pricing.enabled: false, the snapshot serves as an always-on baseline — models that previously returnednullpricing may now resolve from the snapshot. If you need strict "only my config" behaviour where unknown models always return nothing, addmodels:entries for every model you use and configure your app to treat unresolved models as an error.
Adjusting the Cache TTL
The dynamic pricing response is cached to avoid unnecessary HTTP requests on every page load. The default TTL is 24 hours. To refresh more or less frequently:
lunetics_llm_cost_tracking: dynamic_pricing: ttl: 3600 # 1 hour
Minimum: 1 second. Maximum: 604800 (7 days).
Console Command
To manually refresh the cached pricing from models.dev:
php bin/console lunetics:llm:update-pricing
This clears the cache and immediately fetches fresh pricing. Add --verbose to see the full model table:
php bin/console lunetics:llm:update-pricing --verbose
The command exits with a non-zero status if the API is unreachable or returns no models, making it safe to use in deployment pipelines.
Model Coverage
The bundle ships a versioned snapshot of the models.dev catalogue (~3000 models across ~100 providers) as resources/pricing_snapshot.json. This snapshot is used as a fallback when the live API is unreachable, so pricing works correctly even in offline or air-gapped environments.
The snapshot is regenerated before each release. To regenerate it locally:
make update-snapshot
# or directly:
php bin/generate_snapshot.php
The model string passed to $platform->invoke() (e.g. 'gpt-5') is the same string the bundle uses to look up pricing.
Example: OpenAI GPT
# config/packages/symfony_ai.yaml symfony_ai: platform: openai: api_key: '%env(OPENAI_API_KEY)%'
// The model string 'gpt-5' is matched against the pricing registry $result = $platform->invoke('gpt-5', 'Explain Symfony in one sentence.');
No lunetics_llm_cost_tracking config is needed — gpt-5 is covered by dynamic pricing (or the bundled snapshot when offline). Costs appear automatically in the profiler toolbar.
Overriding or Adding Model Pricing
If you use a model that isn't on models.dev (e.g. a fine-tuned or self-hosted model), add it to your config:
lunetics_llm_cost_tracking: models: ft:gpt-5:my-finetuned-2025: display_name: 'My Fine-tuned GPT-5' provider: 'OpenAI' input_price_per_million: 3.00 output_price_per_million: 15.00
Your models: entries always take precedence over bundle defaults and dynamic pricing.
Using Cost Data in Your Services
The CostTrackerInterface service is available for dependency injection. Use it to access cost data outside the profiler — for example, in middleware, event listeners, or API responses:
use Lunetics\LlmCostTrackingBundle\Service\CostTrackerInterface; class MyService { public function __construct( private readonly CostTrackerInterface $costTracker, ) {} public function logCosts(): void { $totals = $this->costTracker->getTotals(); // $totals = ['calls' => 3, 'input_tokens' => 5000, ..., 'cost' => 0.042] // Or get everything in one call: $snapshot = $this->costTracker->getSnapshot(); // $snapshot = ['calls' => [...], 'by_model' => [...], 'totals' => [...], 'unconfigured_models' => [...]] } }
Available methods: getCalls(), getTotals(), getByModel(), getUnconfiguredModels(), getSnapshot().
The ModelRegistryInterface and CostCalculatorInterface are also available for injection if you need lower-level access to model definitions or cost calculation.
How It Works
The bundle collects data from all services tagged with ai.traceable_platform (provided by symfony/ai-bundle). The CostTracker service iterates over all recorded LLM calls, extracts token usage metadata, and calculates costs using the configured model pricing. Results are memoized for the lifetime of the request, so repeated calls to any getter return the same data without recomputation.
The LlmCostCollector (Symfony Profiler data collector) delegates to CostTracker via getSnapshot(), which returns all cost data in a single atomic call. This separation keeps business logic in a standalone service that can be injected anywhere, while the data collector focuses on profiler integration.
Cost formula per call:
cost = (regular_input_tokens / 1M × input_price)
+ (output_tokens / 1M × output_price)
+ (cached_tokens / 1M × cached_price)
+ (thinking_tokens / 1M × thinking_price)
Where regular_input_tokens = max(0, input_tokens - cached_tokens).
Development
A Docker-based Makefile is provided for local development:
make install # Install dependencies make test # Run PHPUnit tests make phpstan # Run PHPStan (level 8) make cs-check # Check coding standards make cs-fix # Fix coding standards make ci # Run all checks make update-snapshot # Regenerate resources/pricing_snapshot.json from models.dev
Override the PHP version with PHP_VERSION=8.2 make test.
License
MIT License. See LICENSE for details.
lunetics/llm-cost-tracking-bundle 适用场景与选型建议
lunetics/llm-cost-tracking-bundle 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 55 次下载、GitHub Stars 达 4, 最近一次更新时间为 2026 年 02 月 25 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「symfony」 「toolbar」 「profiler」 「tracking」 「cost」 「ai」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 lunetics/llm-cost-tracking-bundle 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 lunetics/llm-cost-tracking-bundle 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 lunetics/llm-cost-tracking-bundle 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
g4 application profiler package
Expand, collapse, change the status of, or delete multiple blocks in a Matrix field simultaneously.
The bundle for easy using json-rpc api on your project
ZF2 Zend developer toolbar addon : Playground Template Hint
Bundle Symfony DaplosBundle
Allows easy API consumer debugging with web debug toolbar extension.
统计信息
- 总下载量: 55
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 4
- 点击次数: 34
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-25