promptphp/intercept
PromptPHP Intercept is a Laravel package for capturing, inspecting, and modifying AI prompts and responses before they reach LLM providers, with middleware-style hooks for logging, redaction, caching, and testing.
Intercept v1.0.0 is the first stable release of PromptPHP's interception layer for laravel/ai. It ships from a monorepo as split Composer packages, so the first decision is which package you need — you don't necessarily install the whole monorepo.
Minimal path:
composer require promptphp/intercept
php artisan vendor:publish --tag="intercept-config"
Start with the published config/intercept.php. That file is the map of the package: it tells you which interceptors are registered, what they're allowed to touch, and which driver bindings are active. Read it before reading the docs.
Your first use case should be the lowest-risk one: request/response logging of AI calls. Wire the logging interceptor, hit one laravel/ai call from php artisan tinker, and confirm you see the prompt, model, token counts, and latency. Once that loop is visible you can layer on redaction, caching, and cost accounting with confidence.
Where to look first:
config/intercept.php — defaults and registration points.composer show promptphp/intercept — confirms the laravel/ai constraint you'll be living with.Global interception via config. Register interceptors in the config array so every laravel/ai call flows through them. This is the right default for observability: one place to add logging, metrics, and redaction, no call-site changes.
Per-call interception for targeted behavior. Reserve fluent/opt-in interception for cases where global behavior is wrong — a single high-cost endpoint that should be cached aggressively, or a test-only stub. Keep the global stack thin and push conditional logic to call sites.
Redaction as a first-class interceptor. Put PII scrubbing in the interception pipeline before the logging interceptor, not inside your logger. Ordering is the whole point of an interception layer — think of it as a middleware stack for AI traffic and reason about it the same way.
Caching and replay. Cache deterministic, low-temperature calls (classification, extraction, summarization of stable inputs) keyed on the normalized prompt + model + parameters. For debugging, capture and replay real payloads rather than hand-reconstructing them in a test.
Cost and token accounting. Interceptors see every response, which makes them the natural home for emitting token/cost events. Push these to your existing metrics or log channel instead of building a bespoke dashboard — the value is in the aggregate, not the tool.
Testing. Use the package's fake/stub facility to keep test suites hermetic. Prefer asserting on the intercepted payload (did the redactor strip the email? did the cache hit?) over asserting on the model's prose — the latter is flaky and expensive. Combine with laravel/ai's own fakes where available.
Composition across split packages. If you only need logging, install the narrow split package rather than the metapackage. Keeps your dependency graph honest and your upgrade surface small. Pin all promptphp/* packages to the same minor version — they are released in lockstep from one monorepo.
Integration with queues. AI calls are slow; interceptors run inside whatever context the call runs in. If you dispatch AI work to a queue, make sure correlation IDs are attached to the job so your interceptor logs are traceable back to the originating request.
laravel/ai is pinned to <1.0. This release explicitly caps the dependency across all packages. If your app is on laravel/ai 1.x, Composer will refuse to install Intercept. This is the single most likely blocker on day one — check composer why-not laravel/ai 1.0 before opening an issue.promptphp/intercept 1.0 with a satis/dev-main split package of another version produces confusing class-not-found or contract-mismatch errors. Align versions and run composer update "promptphp/*" --with-dependencies.php artisan config:cache in deployed environments means your interceptor ordering is frozen. If behavior differs between local and production, that's the first thing to check.tearDown or use the package's scoped helper if one exists.vendor/, you've missed a binding.How can I help you explore Laravel packages today?