wcflabs / ai-ledger
Telescope-like monitoring dashboard for Laravel AI SDK (laravel/ai) interactions
Requires
- php: ^8.3
- illuminate/console: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/events: ^12.0|^13.0
- illuminate/routing: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- laravel/ai: ^0.8.1
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/mcp: ^0.5
- laravel/pint: ^1.29
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
- laravel/mcp: Required for MCP server/client tool call logging
This package is auto-updated.
Last update: 2026-07-07 18:10:21 UTC
README
A Telescope-like monitoring dashboard for Laravel AI SDK interactions. Logs agent invocations, tool calls, token usage, costs, and latency — with a built-in web dashboard and support for MCP server/client tracing.
Features
- Automatic recording of every
laravel/aiagent invocation (prompt text, response, token counts, cost, latency) - Per-invocation tool call tracing for agent tools, MCP server tools, and MCP client tools
- Cost calculation for OpenAI, Anthropic, Gemini, Groq, DeepSeek, and Mistral models
- Built-in web dashboard (Blade + Alpine.js + Chart.js, no build step required)
- Tag invocations programmatically or declaratively with
#[Tag] - Flexible filtering: skip individual invocations or tool calls via events
- Two storage drivers:
database(full-featured) andfile(lightweight, local dev) - Automatic daily pruning via the Laravel scheduler
Requirements
- PHP ^8.3
- Laravel ^12.0
laravel/ai^0.8.1
Installation
1. Install via Composer
composer require wcflabs/ai-ledger
2. Publish the configuration
php artisan vendor:publish --tag=ai-ledger-config
This creates config/ai-ledger.php.
3. Publish and register the authorization provider
php artisan vendor:publish --tag=ai-ledger-provider
This copies AiLedgerApplicationServiceProvider into app/Providers/. Register it in bootstrap/providers.php:
return [ // ... App\Providers\AiLedgerApplicationServiceProvider::class, ];
4. Publish and run the migrations
php artisan vendor:publish --tag=ai-ledger-migrations php artisan migrate
Dashboard
Once installed, the dashboard is available at /ai-ledger (configurable via dashboard.path in the config).
Authentication:
By default, the dashboard is accessible in the local environment only. For production access, open app/Providers/AiLedgerApplicationServiceProvider.php and add authorized email addresses to the viewAiLedger gate:
protected function gate(): void { Gate::define('viewAiLedger', function ($user) { return in_array($user?->email, [ 'admin@example.com', ]); }); }
You can also provide a fully custom auth callback anywhere (e.g., in a service provider):
use WCFLabs\AiLedger\Facades\AiLedger; AiLedger::auth(function ($request) { return $request->user()?->isAdmin(); });
Configuration
All options live in config/ai-ledger.php.
| Key | Default | Description |
|---|---|---|
enabled |
true |
Master switch. Set AI_LEDGER_ENABLED=false to disable all recording. |
driver |
"database" |
Storage driver: "database" or "file". Env: AI_LEDGER_DRIVER. |
drivers.database.connection |
null |
Database connection name. Env: AI_LEDGER_DB_CONNECTION. Uses the default connection when null. |
drivers.file.path |
storage/logs/ai-ledger |
Directory for file-driver JSON logs. |
dashboard.enabled |
true |
Whether to register the dashboard routes. |
dashboard.path |
"ai-ledger" |
URL prefix for the dashboard. |
dashboard.middleware |
["web"] |
Middleware applied to all dashboard routes. |
pruning.enabled |
true |
Whether to schedule automatic daily pruning. Env: AI_LEDGER_PRUNING_ENABLED. |
pruning.days |
30 |
Number of days of records to retain. Env: AI_LEDGER_PRUNING_DAYS. |
recording.prompt_text |
true |
Store the full prompt sent to the model. |
recording.response_text |
true |
Store the full response from the model. |
recording.tool_arguments |
true |
Store tool call input arguments. |
recording.tool_results |
true |
Store tool call results. |
recording.max_prompt_length |
null |
Truncate stored prompts to this many characters (null = no limit). |
recording.max_response_length |
null |
Truncate stored responses to this many characters (null = no limit). |
recording.max_tool_result_length |
5000 |
Truncate stored tool results to this many characters. |
pricing |
(see config) | USD-per-million-token pricing table keyed by provider and model name. |
Storage Drivers
database (default)
Stores invocations and tool calls in two database tables (ai_ledger_invocations, ai_ledger_tool_calls). Supports full filtering, aggregation, and the metrics dashboard.
file
Writes one JSON file per invocation to storage/logs/ai-ledger/. Intended for local development only — it does not support filtering events, dashboard aggregation, or the metrics chart. No migrations required.
AI_LEDGER_DRIVER=file
Tagging Invocations
Tags appear in the dashboard and can be used to categorize invocations by feature, user type, or any other dimension.
Declarative — #[Tag] attribute
Apply the #[Tag] attribute to any Agent or Tool class:
use WCFLabs\AiLedger\Attributes\Tag; use Laravel\Ai\Agent; #[Tag('checkout', 'billing')] class CheckoutAgent extends Agent { // ... }
Programmatic — AiLedger::tag()
Call AiLedger::tag() before the invocation runs. Tags are flushed and attached to the next recorded invocation:
use WCFLabs\AiLedger\Facades\AiLedger; AiLedger::tag('user:premium', 'feature:recommendations'); $result = $agent->handle($input);
Suppressing Logging
Wrap any code block with AiLedger::withoutLogging() to prevent it from being recorded:
use WCFLabs\AiLedger\Facades\AiLedger; $result = AiLedger::withoutLogging(function () use ($agent, $input) { return $agent->handle($input); });
MCP Integration
Requires
laravel/mcpto be installed.
MCP calls are logged automatically — no manual setup required.
- Inbound MCP server tool calls (external MCP clients calling your app): intercepted transparently by overriding
laravel/mcp'sCallToolhandler in the container. - MCP tools used inside Laravel AI agents (
McpServerTool,McpTool): captured via the standardInvokingTool/ToolInvokedevents with the correcttool_type(mcp_serverormcp_client).
Manual decorator (advanced)
If you call an MCP client directly outside of a Laravel AI agent context, you can opt in to logging via LoggingMcpClientDecorator:
use WCFLabs\AiLedger\Mcp\LoggingMcpClientDecorator; $client = new LoggingMcpClientDecorator($mcpClient); $result = $client->callTool('search', ['query' => 'example']);
All other methods on the underlying client are proxied transparently via __call.
Filtering
You can prevent individual invocations or tool calls from being stored by listening to the filtering events and returning false.
Skip an invocation
use WCFLabs\AiLedger\Events\FilteringInvocation; use Illuminate\Support\Facades\Event; Event::listen(FilteringInvocation::class, function (FilteringInvocation $event) { // Skip invocations triggered by internal health-check agents if (str_contains($event->data['agent_class'] ?? '', 'HealthCheck')) { return false; } });
Skip a tool call
use WCFLabs\AiLedger\Events\FilteringToolCall; use Illuminate\Support\Facades\Event; Event::listen(FilteringToolCall::class, function (FilteringToolCall $event) { if ($event->data['tool_name'] === 'internal_ping') { return false; } });
Returning false from the listener skips storage but still executes the agent or tool normally.
Artisan Commands
| Command | Description |
|---|---|
php artisan ai-ledger:status |
Show driver, total invocation count, oldest record, and pruning configuration. |
php artisan ai-ledger:prune [--days=N] |
Delete records older than N days (defaults to pruning.days from config). |
php artisan ai-ledger:clear [--force] |
Delete all AI Ledger records. Prompts for confirmation unless --force is passed. |
Pruning
When pruning.enabled is true, the package automatically schedules ai-ledger:prune to run daily via the Laravel scheduler (callAfterResolving). No manual scheduler entry is required.
To override the retention period at runtime:
php artisan ai-ledger:prune --days=7
License
MIT — see LICENSE.