Backup #1861

ID1861
Dateipfad/var/www/dev.campus.systemische-tools.de/src/Infrastructure/AI/AnthropicClient.php
Version5
Typ modified
Größe4.9 KB
Hashbb80edb94fb426395a6d073fdbad9cb02d3dcb6e0e8b07cd78a0cb3b144aaf88
Datum2025-12-27 23:48:12
Geändert vonclaude-code-hook
GrundClaude Code Pre-Hook Backup vor Edit-Operation
Datei existiert Ja

Dateiinhalt

<?php

declare(strict_types=1);

namespace Infrastructure\AI;

// @responsibility: Anthropic-Client für Claude-Modelle (Task-System)

use Domain\Constants;

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

    private const PRICING = [
        'claude-sonnet-4-20250514' => ['input' => 3.0, 'output' => 15.0],
        'claude-opus-4-20250514' => ['input' => 15.0, 'output' => 75.0],
        'claude-haiku-3-20250514' => ['input' => 0.25, 'output' => 1.25],
    ];

    public function __construct(
        string $apiKey = '',
        string $model = 'claude-sonnet-4-20250514',
        int $timeout = 120
    ) {
        $this->apiKey = $apiKey !== '' ? $apiKey : $this->loadApiKey();
        $this->model = $model;
        $this->baseUrl = 'https://api.anthropic.com/v1';
        $this->timeout = $timeout;
    }

    private function loadApiKey(): string
    {
        if (defined('ANTHROPIC_API_KEY')) {
            return ANTHROPIC_API_KEY;
        }

        return \Infrastructure\Config\CredentialService::getAnthropicApiKey();
    }

    public function execute(string $prompt, array $options = []): AIResponse
    {
        if ($this->apiKey === '') {
            return AIResponse::error('Anthropic API Key not configured', $this->model);
        }

        $model = $options['model'] ?? $this->model;
        $startTime = microtime(true);

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

            $messages = [
                ['role' => 'user', 'content' => $prompt],
            ];

            $payload = [
                'model' => $model,
                'max_tokens' => $options['max_tokens'] ?? 4096,
                'messages' => $messages,
            ];

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

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

            curl_setopt_array($ch, [
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => json_encode($payload),
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_HTTPHEADER => [
                    'Content-Type: application/json',
                    'x-api-key: ' . $this->apiKey,
                    'anthropic-version: 2023-06-01',
                ],
                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) * 1000);

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

            $data = json_decode($response, true);

            if ($httpCode !== 200) {
                $errorMsg = $data['error']['message'] ?? "HTTP Error: {$httpCode}";

                return AIResponse::error($errorMsg, $model);
            }

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

            $tokensInput = $data['usage']['input_tokens'] ?? null;
            $tokensOutput = $data['usage']['output_tokens'] ?? null;

            $cost = $this->calculateCost($model, $tokensInput, $tokensOutput);

            return AIResponse::success(
                $data['content'][0]['text'],
                $tokensInput,
                $tokensOutput,
                $durationMs,
                $model,
                [
                    'stop_reason' => $data['stop_reason'] ?? null,
                    'cost_usd' => $cost,
                ]
            );
        } catch (\Exception $e) {
            return AIResponse::error($e->getMessage(), $model);
        }
    }

    public function isAvailable(): bool
    {
        return $this->apiKey !== '';
    }

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

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

    private function calculateCost(string $model, ?int $inputTokens, ?int $outputTokens): ?float
    {
        if ($inputTokens === null || $outputTokens === null) {
            return null;
        }

        $pricing = self::PRICING[$model] ?? self::PRICING['claude-sonnet-4-20250514'];

        $inputCost = ($inputTokens / 1_000_000) * $pricing['input'];
        $outputCost = ($outputTokens / 1_000_000) * $pricing['output'];

        return round($inputCost + $outputCost, 6);
    }

    public function setModel(string $model): self
    {
        $this->model = $model;

        return $this;
    }

    public static function getAvailableModels(): array
    {
        return array_keys(self::PRICING);
    }
}

Vollständig herunterladen

Aktionen

Herunterladen

Andere Versionen dieser Datei

ID Version Typ Größe Datum
1861 5 modified 4.9 KB 2025-12-27 23:48
1857 4 modified 4.9 KB 2025-12-27 23:47
756 3 modified 4.8 KB 2025-12-23 08:00
405 2 modified 4.7 KB 2025-12-22 08:54
43 1 modified 5.0 KB 2025-12-20 17:24

← Zurück zur Übersicht