php-agentic/Jobs/ProcessContentTask.php

150 lines
4.1 KiB
PHP
Raw Normal View History

2026-01-27 00:28:29 +00:00
<?php
declare(strict_types=1);
namespace Core\Agentic\Jobs;
use Core\Agentic\Services\AgenticManager;
use Mod\Content\Models\ContentTask;
use Mod\Content\Services\ContentProcessingService;
use Core\Mod\Tenant\Services\EntitlementService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Throwable;
class ProcessContentTask implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $backoff = 60;
public int $timeout = 300;
public function __construct(
public ContentTask $task
) {
$this->onQueue('ai');
}
public function handle(
AgenticManager $ai,
ContentProcessingService $processor,
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(),
]
);
}
// If this task has a target, process the output
if ($this->task->target_type && $this->task->target_id) {
$this->processOutput($response->content, $processor);
}
}
public function failed(Throwable $exception): void
{
$this->task->markFailed($exception->getMessage());
}
/**
* Interpolate template variables.
*/
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;
}
/**
* Process the AI output based on target type.
*/
private function processOutput(string $content, ContentProcessingService $processor): void
{
$target = $this->task->target;
if (! $target) {
return;
}
// Handle different target types
// This can be extended for different content types
// For now, just log that processing occurred
}
}