pixelworxio/laravel-ai-action
Laravel AI Action adds AI-powered actions to your Laravel models and apps with a clean, fluent API. Define reusable actions, keep prompts organized, and drop AI into jobs, queues, and workflows without boilerplate. Ship features faster with versioned prompts, provider config, and testable actions.
Install the package and publish its config:
composer require pixelworxio/laravel-ai-action
php artisan vendor:publish --tag="ai-action-config"
Set your provider credentials in .env (the package reads them through the published config/ai-action.php), then confirm the service provider is auto-discovered — if you maintain a custom provider list in bootstrap/providers.php, add it manually.
Where to look first, in this order:
config/ai-action.php — the single most important file. As of 1.1.0 it now carries middleware stack, per-model pricing tables, and Pulse integration toggles. Read it end to end before writing an action.handle signature, and metadata conventions.src/Contracts (or equivalent) tells you what you can intercept.First use case: a single, well-scoped action such as "summarise a support ticket" or "classify an inbound lead". Define the prompt and expected output shape inside the action, dispatch it from a controller or job, and watch it appear in Pulse. Resist the urge to build a generic "ask the AI anything" action first — the package's value is in the per-action structure, and a catch-all action teaches you nothing about how pricing, middleware, and observability actually behave.
One action per intent. Treat actions like Laravel Jobs: a named, single-purpose class with a typed input DTO and a typed result. This keeps prompts versioned alongside the code that depends on them and makes pricing attributable — "summarisation costs us $40/month" is only answerable if summarisation is its own action.
Use middleware for every cross-cutting concern. This is the headline 1.1.0 feature and the one most likely to be under-used. Register global middleware in config for concerns that apply everywhere (request logging, PII redaction, prompt/response auditing), and per-action middleware for concerns specific to one flow:
Wire pricing to a real budget, not just a dashboard. The pricing feature is only useful if it changes behaviour. The pattern that works: store cost per execution against a user_id/tenant_id, expose a remainingBudget() check in a middleware, and degrade gracefully (smaller model, cached response, or a hard failure) when the budget is exhausted. Also decide what happens on an unpriced model — see Gotchas.
Let Pulse do the aggregation, your logs do the forensics. Point the Pulse integration at your AI actions to get throughput, latency, and cost trends. Keep the full prompt/response payloads in your own storage (a table or structured log channel) with the same correlation ID. Pulse cards are for spotting a regression in p95 latency or a cost spike; they are not where you go to find out what the model actually said.
Queue anything the user is waiting on. AI calls are slow and flaky. Dispatch actions from jobs with a sensible timeout and retry policy, and return a placeholder to the UI. For interactive features, stream where the package allows it, but note that streaming changes how token counts and therefore costs are reported.
Test without touching the network. Fake the provider or the action in tests and assert on the prompt payload and the parsing of the response. Keep an optional, manually-run suite that hits the real provider for a handful of representative actions — prompt regressions don't show up in mocked tests.
Unpriced models fail silently at $0.00. Pricing tables are static data shipped with the package; the moment you switch to a model variant (a dated snapshot, a -mini suffix, a regional endpoint) that isn't in the table, cost tracking quietly reports zero and your budget guard never fires. Add a test that asserts every model string referenced in your codebase has a price entry, and add an alert on "actions executed with zero recorded cost".
Middleware ordering is not arbitrary. Cost-recording and logging middleware need the response (and its token usage) to exist before they run, while guard/rejection middleware must run before the provider call. Read how the pipeline is composed in the package before adding your own; if the pipeline is a single pass rather than an onion, your "before" logic and "after" logic may live in different middleware. Verify with a deliberate failure or a dd() in a throwaway middleware.
Config caching bites on upgrade. If you have php artisan config:cache in your deploy pipeline, the new pricing and Pulse keys won't appear until you re-cache. A surprising number of "the feature doesn't work" reports after a minor upgrade are stale config caches — clear and re-cache as part of the deploy, not manually.
Pulse is sampled and aggregated. Don't write assertions or alerting logic against Pulse data expecting exact per-request values; resolution and retention are configurable and lossy by design. Also watch cardinality — keying a Pulse metric by user ID or prompt hash will bloat the storage tables fast. Key by action name and model instead.
Streaming and token accounting diverge. If a streamed action is abandoned mid-flight (user navigates away, connection drops), the provider still bills for what it generated but your recorded usage may be partial. Accept a margin of error in cost tracking for streamed actions, or exclude them from strict budget enforcement.
Extension points to prefer over forking. The middleware pipeline is the intended seam for custom behaviour; pricing tables should be overridable via config rather than edited in vendor/. If you find yourself needing to override the base action class, check whether an event or middleware would do the job first — forking a package at 1.1.0 means re-forking at 1.2.0.
Pin your models. Prompt behaviour and price both shift under you when a provider updates an alias like latest. Use dated/snapshot model identifiers in config so an unrelated provider release can't change your costs or your output quality overnight.
How can I help you explore Laravel packages today?