fomvasss/laravel-ai-tasks
A Laravel package for defining and running AI-powered tasks in your application, providing a clean, configurable workflow for organizing AI features and helping you integrate and reuse prompts, task logic, and results across your Laravel projects.
Treat this as a thin orchestration layer over laravel/ai: the package owns task routing, queueing, audit records, budget accounting, and webhook callbacks, while the actual provider call is delegated to laravel/ai. Because the repository metadata is thin (41 stars, modest score), the fastest path is to install it in a scratch app, publish everything, and read the package source before designing around it.
Minimal setup:
composer require fomvasss/laravel-ai-tasks
php artisan vendor:publish --provider="Fomvasss\AiTasks\AiTasksServiceProvider"
php artisan migrate
Configure at least:
laravel/ai provider credentials and default model.Where to look first:
config/ai-tasks.php — routing rules, queue names, budget defaults, webhook settings, audit storage.database/migrations/ — expect tables for task runs, audit events, and budget counters. Review indexes before production.src/ contracts and jobs — identify the task dispatcher, router, budget resolver, and audit repository. These are your extension points.routes/ — confirm whether webhook endpoints are auto-registered and what middleware protects them.First use case: a queued support-ticket triage task. Define the task, dispatch it, and inspect the audit record.
use Fomvasss\AiTasks\Facades\AiTasks;
$run = AiTasks::task('support.triage')
->input(['ticket' => $ticket->body])
->forTenant($ticket->tenant_id)
->dispatch();
return response()->json(['run_id' => $run->id]);
If the facade or method names differ, use the package’s dispatcher contract from the container instead. The important pattern is: application code submits a named task with input and context; the package decides provider/model, queues the work, and records the run.
Define tasks as named units, not ad hoc prompts. Keep prompt templates and expected output shape in task classes or config. Application code should call support.triage, not gpt-4o-mini with this system prompt. That keeps routing and budget policy centralized.
class TriageTicket extends AiTask
{
public function name(): string
{
return 'support.triage';
}
public function prompt(array $input): string
{
return "Classify this ticket:\n\n{$input['ticket']}";
}
public function schema(): array
{
return ['category' => 'string', 'priority' => 'string'];
}
}
Use config-driven routing with explicit fallbacks. Route by task name, tenant tier, or cost ceiling. A typical policy:
'tasks' => [
'support.triage' => [
'provider' => 'openai',
'model' => 'gpt-4o-mini',
'fallback' => ['openai:gpt-4o'],
'queue' => 'ai-fast',
'timeout' => 60,
],
],
Keep routing in config, but allow per-dispatch overrides only for exceptional cases. Log the actual provider and model used, especially when fallback occurs.
Queue everything that touches a provider. Use a dedicated queue so AI latency does not starve transactional jobs. Set retry and backoff deliberately:
AiTasks::task('support.triage')
->input($payload)
->onQueue('ai-fast')
->tries(3)
->backoff([30, 120])
->dispatch();
For long-running or batch work, dispatch one orchestration job that fans out child tasks, or use the package’s chaining support if present. Avoid nesting provider calls inside a single HTTP request.
Enforce budgets before dispatch, not after. Use tenant-aware budget checks and atomic reservations. A safe pattern is:
Do not implement if ($spent < $limit) { dispatch(); } without a transaction or atomic increment. Concurrent requests will overspend.
Treat audit logs as operational data. Expect records for run ID, task name, tenant, provider, model, input hash, token counts, estimated/actual cost, latency, status, and error. Use them for debugging, billing reconciliation, and incident review.
Do not store raw prompt/completion text by default in audit tables if it may contain PII. Store hashes or redacted excerpts, and keep full transcripts only in a controlled store with retention rules.
Webhooks should be idempotent and verified. If the package registers a webhook route, verify the provider signature before processing. Store the provider event ID and ignore duplicates.
Route::post('/ai/webhooks/{provider}', WebhookController::class)
->middleware(['api', 'verify.ai.webhook']);
On callback:
Integrate through events and contracts. Listen for package events such as task started/completed/failed, and push metrics to your observability stack. Bind custom implementations for routing, budget resolution, or audit storage if the package exposes contracts.
Event::listen(TaskFailed::class, function ($event) {
Log::warning('ai.task.failed', [
'run_id' => $event->run->id,
'task' => $event->run->task,
'provider' => $event->run->provider,
]);
});
Test at the transport boundary. Fake laravel/ai or the package dispatcher so tests never hit a live provider. Assert that the correct task name, tenant, and queue were used. Keep one small integration test against a sandbox provider for routing changes.
composer.json and wrap the package behind your own service where practical.config:cache after every routing or budget config change. A stale config cache is the most common cause of “why is it using the wrong model/queue?”tenant_id, task, status, and created_at; prune or partition audit data on a schedule.failed_jobs, then provider-side request logs. If the package emits events, subscribe temporarily and log every transition.AiTaskDispatcher interface. If you later replace the package or upgrade across breaking changes, the blast radius stays small.How can I help you explore Laravel packages today?