ExecuteAITaskUseCase.php

Code Hygiene Score: 90

Keine Issues gefunden.

Dependencies 12

Klassen 1

Funktionen 6

Verwendet von 2

Versionen 4

Code

<?php

declare(strict_types=1);

namespace UseCases\Task;

// @responsibility: Führt Tasks mit KI-Modellen aus (Ollama/Anthropic)

use Domain\Entity\Task;
use Domain\Entity\TaskResult;
use Domain\Repository\TaskRepositoryInterface;
use Domain\ValueObject\AssigneeType;
use Domain\ValueObject\TaskStatus;
use Infrastructure\AI\AIClientInterface;
use Infrastructure\AI\AnthropicClient;
use Infrastructure\AI\OllamaClient;
use Infrastructure\Config\CredentialService;

class ExecuteAITaskUseCase
{
    public function __construct(
        private TaskRepositoryInterface $taskRepository,
        private SaveTaskResultUseCase $saveResultUseCase,
        private UpdateTaskStatusUseCase $updateStatusUseCase
    ) {
    }

    public function execute(int $taskId, array $options = []): TaskResult
    {
        $task = $this->taskRepository->find($taskId);
        if ($task === null) {
            throw new \InvalidArgumentException("Task {$taskId} not found");
        }

        $executorType = AssigneeType::from($options['executor_type'] ?? 'ollama');
        $client = $this->getClient($executorType, $options);

        if (!$client->isAvailable()) {
            throw new \RuntimeException("{$executorType->label()} is not available");
        }

        if ($task->getStatus() === TaskStatus::PENDING) {
            $this->updateStatusUseCase->startTask($taskId, $client->getClientName(), 'ai');
        }

        $prompt = $this->buildPrompt($task, $options);
        $response = $client->execute($prompt, $options);

        $resultData = [
            'executor' => $client->getClientName(),
            'executor_type' => $executorType->value,
            'model_name' => $response->getModel() !== '' ? $response->getModel() : $client->getModelName(),
            'request' => $prompt,
            'response' => $response->getContent(),
            'tokens_input' => $response->getTokensInput(),
            'tokens_output' => $response->getTokensOutput(),
            'duration_ms' => $response->getDurationMs(),
            'status' => $response->isSuccess() ? 'success' : 'error',
        ];

        if (!$response->isSuccess()) {
            $resultData['error_message'] = $response->getErrorMessage();
        }

        $metadata = $response->getMetadata();
        if (isset($metadata['cost_usd'])) {
            $resultData['cost_usd'] = $metadata['cost_usd'];
        }

        if (isset($options['assignment_id'])) {
            $resultData['assignment_id'] = $options['assignment_id'];
        }

        $result = $this->saveResultUseCase->execute($taskId, $resultData);

        if ($response->isSuccess() && ($options['auto_complete'] ?? false)) {
            $this->updateStatusUseCase->completeTask($taskId, $client->getClientName(), 'ai');
        }

        return $result;
    }

    private function getClient(AssigneeType $type, array $options): AIClientInterface
    {
        return match ($type) {
            AssigneeType::OLLAMA => new OllamaClient(
                CredentialService::getOllamaHost(),
                $options['model'] ?? 'mistral'
            ),
            AssigneeType::ANTHROPIC_API => new AnthropicClient(
                $options['api_key'] ?? '',
                $options['model'] ?? 'claude-sonnet-4-20250514'
            ),
            default => throw new \InvalidArgumentException("Unsupported executor type: {$type->value}")
        };
    }

    private function buildPrompt(Task $task, array $options): string
    {
        $prompt = $task->getTitle();

        if ($task->getDescription() !== null) {
            $prompt .= "\n\n" . $task->getDescription();
        }

        if (isset($options['additional_context'])) {
            $prompt .= "\n\n" . $options['additional_context'];
        }

        return $prompt;
    }

    public function executeWithOllama(int $taskId, string $model = 'mistral', array $options = []): TaskResult
    {
        return $this->execute($taskId, array_merge($options, [
            'executor_type' => 'ollama',
            'model' => $model,
        ]));
    }

    public function executeWithAnthropic(int $taskId, string $model = 'claude-sonnet-4-20250514', array $options = []): TaskResult
    {
        return $this->execute($taskId, array_merge($options, [
            'executor_type' => 'anthropic_api',
            'model' => $model,
        ]));
    }
}
← Übersicht Graph