ai-sdk-agent-logger maintained by laravel-neuro
AI Agent Logger for Laravel
Logging and token-cost estimation middleware for the Laravel AI SDK.
Provides a single, unified AgentLogger middleware that you attach to any AI SDK agent. Choose exactly what to log via AgentLog enums — prompt input, response output, token usage with cost estimates, or any combination. Logs to either Laravel's Log facade or a database table, with configurable per-model pricing.
Requirements
- PHP 8.3+
- Laravel 13.x
laravel/aiSDK
Installation
composer require laravel-neuro/ai-sdk-agent-logger
Publish the config:
php artisan vendor:publish --tag=ai-agent-logger-config
If using the database driver, publish and run migrations:
php artisan vendor:publish --tag=ai-agent-logger-migrations
php artisan migrate
Quick Start
Attach the middleware to any agent implementing HasMiddleware:
use LaravelNeuro\AiSdk\Enums\AgentLog;
use LaravelNeuro\AiSdk\Middleware\AgentLogger;
class ResearchAgent implements Agent, HasMiddleware //,...
{
//...
public function middleware(): array
{
return [
new AgentLogger('Research Agent', AgentLog::Prompt, AgentLog::Response, AgentLog::Usage),
];
}
}
That's it. Every prompt and response through this agent is now logged with token usage and estimated cost.
What to Log
Pass any combination of AgentLog enums — order doesn't matter, duplicates are ignored:
// Just log prompts
new AgentLogger('Chat Agent', AgentLog::Prompt)
// Log responses and token costs, skip prompt content
new AgentLogger('Cost Tracker', AgentLog::Response, AgentLog::Usage)
// Name is optional — skip it for a quick anonymous setup
new AgentLogger(AgentLog::Prompt, AgentLog::Response)
// Everything
new AgentLogger('Full Trace Agent', AgentLog::Prompt, AgentLog::Response, AgentLog::Usage)
| Enum | When it fires | What it captures |
|---|---|---|
AgentLog::Prompt |
Before the provider call (synchronous) | Agent name, prompt input, timestamp |
AgentLog::Response |
After the response resolves (in .then()) |
Agent name, response text, timestamp |
AgentLog::Usage |
After the response resolves (in .then()) |
Provider, model, token usage breakdown, estimated cost |
Drivers
Log Driver (default)
Writes structured log entries through Laravel's Log facade. Configure a dedicated channel if desired:
AI_AGENT_LOGGER_DRIVER=log
AI_AGENT_LOGGER_CHANNEL=ai
Output looks like:
[Research Agent] Prompt received. {"prompt": "Summarize this document..."}
[Research Agent] Response received. {"text": "Here is the summary..."}
[Research Agent] Token usage data: {"usage": {"prompt_tokens": 1200, "completion_tokens": 450}}
=> total prompt expense: 0.011$
Database Driver
Persists to two UUID-keyed tables — agent_prompt_logs and agent_response_logs — with a foreign key linking responses to their prompts. Token usage is stored as JSON, cost as a decimal.
AI_AGENT_LOGGER_DRIVER=database
AI_AGENT_LOGGER_CONNECTION=null # uses default connection
The database driver is idempotent: regardless of which enum combinations you enable or what order they run in, each invocation produces at most one prompt row and one response row. logResponse creates the row; logUsage updates it with usage and cost data rather than creating a duplicate.
Cost Estimation
Pricing is defined per-provider, per-model in the published config file. Rates are specified per 1 million tokens in USD.
Some sample pricing for the openai provider are included, but will not be kept up-to-date. If you want to get cost estimations for your agents, you need to fill in the current pricing for the models your application utilizes.
'pricing' => [
'openai' => [
'updated_at' => '2026-07-14',
'models' => [
'gpt-5.4-mini' => [
'prompt_tokens' => 0.75,
'completion_tokens' => 4.50,
'cache_write_input_tokens' => 0,
'cache_read_input_tokens' => 0,
'reasoning_tokens' => 4.50,
],
// ...
],
],
'anthropic' => [
'updated_at' => '2026-07-14',
'models' => [
'claude-sonnet-4-5' => [
// ...
],
],
],
// ...
],
The CostCalculator automatically:
- Normalizes model names — strips date suffixes (e.g.
gpt-4o-2026-07-14→gpt-4o) before lookup. - Returns
nullgracefully — if a provider or model isn't in the pricing config, cost is logged as "No pricing information available" rather than throwing.
Note: Model names containing dots (like
gpt-5.4-mini) are handled correctly — the calculator fetches the models array viaconfig()then uses plain array access for the final key lookup, bypassing Laravel's dot-notation parser.
Configuration
The full published config at config/ai-agent-logger.php:
return [
'driver' => env('AI_AGENT_LOGGER_DRIVER', 'log'),
'channel' => env('AI_AGENT_LOGGER_CHANNEL', null),
'connection' => env('AI_AGENT_LOGGER_CONNECTION', null),
'capture' => [
'prompt_input' => true, // store prompt text
'response_output' => true, // store response text
'token_usage' => true, // store token counts
'cost_estimate' => true, // store calculated cost
'duration' => true, // store request duration
'invocation_id' => true, // store SDK invocation ID
],
'pricing' => [
// ...see above...
],
];
Set any capture key to false to omit that field from logs or database rows — useful for compliance or storage concerns.
Nested Sub-Agents
When an agent has sub-agents as tools and both implement AgentLogger, the middleware generates a unique correlation ID per handle() call. Each sub-agent invocation gets its own ID, so prompt↔response linking is automatically isolated across nesting levels — no manual stack management or context cleanup required.
License
MIT