onQueue('ai'); } public function handle( AgenticManager $ai, EntitlementService $entitlements ): void { $this->task->markProcessing(); $prompt = $this->task->prompt; if (! $prompt) { $this->task->markFailed('Prompt not found'); return; } // Check AI credits entitlement $workspace = $this->task->workspace; if ($workspace) { $result = $entitlements->can($workspace, 'ai.credits'); if ($result->isDenied()) { $this->task->markFailed("Entitlement denied: {$result->message}"); return; } } $provider = $ai->provider($prompt->model); if (! $provider->isAvailable()) { $this->task->markFailed("AI provider '{$prompt->model}' is not configured"); return; } // Interpolate variables into the user template $userPrompt = $this->interpolateVariables( $prompt->user_template, $this->task->input_data ?? [] ); $response = $provider->generate( $prompt->system_prompt, $userPrompt, $prompt->model_config ?? [] ); $this->task->markCompleted($response->content, [ 'tokens_input' => $response->inputTokens, 'tokens_output' => $response->outputTokens, 'model' => $response->model, 'duration_ms' => $response->durationMs, 'estimated_cost' => $response->estimateCost(), ]); // Record AI usage if ($workspace) { $entitlements->recordUsage( $workspace, 'ai.credits', quantity: 1, metadata: [ 'task_id' => $this->task->id, 'prompt_id' => $prompt->id, 'model' => $response->model, 'tokens_input' => $response->inputTokens, 'tokens_output' => $response->outputTokens, 'estimated_cost' => $response->estimateCost(), ] ); } } public function failed(Throwable $exception): void { $this->task->markFailed($exception->getMessage()); } private function interpolateVariables(string $template, array $data): string { foreach ($data as $key => $value) { if (is_string($value)) { $template = str_replace("{{{$key}}}", $value, $template); } elseif (is_array($value)) { $template = str_replace("{{{$key}}}", json_encode($value), $template); } } return $template; } }