OllamaClient.php

Code Hygiene Score: 100

Keine Issues gefunden.

Dependencies 3

Klassen 1

Funktionen 7

Verwendet von 1

Versionen 6

Code

<?php

declare(strict_types=1);

namespace Infrastructure\AI;

// @responsibility: Ollama-Client für LLM-Ausführung (Task-System)

use Domain\Constants;
use Infrastructure\Config\CredentialService;

class OllamaClient implements AIClientInterface
{
    private string $baseUrl;
    private string $model;
    private int $timeout;

    public function __construct(
        ?string $baseUrl = null,
        string $model = 'mistral',
        int $timeout = 120
    ) {
        $this->baseUrl = rtrim($baseUrl ?? CredentialService::getOllamaHost(), '/');
        $this->model = $model;
        $this->timeout = $timeout;
    }

    public function execute(string $prompt, array $options = []): AIResponse
    {
        $model = $options['model'] ?? $this->model;
        $startTime = microtime(true);

        try {
            $ch = curl_init($this->baseUrl . '/api/generate');

            $payload = [
                'model' => $model,
                'prompt' => $prompt,
                'stream' => false,
            ];

            if (isset($options['system'])) {
                $payload['system'] = $options['system'];
            }

            if (isset($options['temperature'])) {
                $payload['options']['temperature'] = $options['temperature'];
            }

            curl_setopt_array($ch, [
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => json_encode($payload),
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                CURLOPT_TIMEOUT => $this->timeout,
            ]);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);

            $durationMs = (int) ((microtime(true) - $startTime) * Constants::MS_PER_SECOND);

            if ($response === false || $error !== '') {
                return AIResponse::error("cURL Error: {$error}", $model);
            }

            if ($httpCode !== 200) {
                return AIResponse::error("HTTP Error: {$httpCode}", $model);
            }

            $data = json_decode($response, true);

            if (!isset($data['response'])) {
                return AIResponse::error('Invalid response format', $model);
            }

            $tokensInput = $data['prompt_eval_count'] ?? $this->estimateTokens($prompt);
            $tokensOutput = $data['eval_count'] ?? $this->estimateTokens($data['response']);

            return AIResponse::success(
                $data['response'],
                $tokensInput,
                $tokensOutput,
                $durationMs,
                $model,
                [
                    'total_duration' => $data['total_duration'] ?? null,
                    'load_duration' => $data['load_duration'] ?? null,
                    'eval_duration' => $data['eval_duration'] ?? null,
                ]
            );
        } catch (\Exception $e) {
            return AIResponse::error($e->getMessage(), $model);
        }
    }

    public function isAvailable(): bool
    {
        try {
            $ch = curl_init($this->baseUrl . '/api/tags');
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 5,
            ]);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            return $httpCode === 200;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function getClientName(): string
    {
        return 'ollama';
    }

    public function getModelName(): string
    {
        return $this->model;
    }

    public function listModels(): array
    {
        try {
            $ch = curl_init($this->baseUrl . '/api/tags');
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT => 10,
            ]);

            $response = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($response === false || $httpCode !== 200) {
                return [];
            }

            $data = json_decode($response, true);

            return array_column($data['models'] ?? [], 'name');
        } catch (\Exception $e) {
            return [];
        }
    }

    private function estimateTokens(string $text): int
    {
        return max(1, (int) (strlen($text) / 4));
    }
}
← Übersicht Graph