AnthropicClient.php
- Pfad:
src/Infrastructure/AI/AnthropicClient.php - Namespace: Infrastructure\AI
- Zeilen: 171 | Größe: 5,019 Bytes
- Geändert: 2025-12-27 23:48:12 | Gescannt: 2025-12-31 10:22:15
Code Hygiene Score: 100
- Dependencies: 100 (25%)
- LOC: 100 (20%)
- Methods: 100 (20%)
- Secrets: 100 (15%)
- Classes: 100 (10%)
- Magic Numbers: 100 (10%)
Keine Issues gefunden.
Dependencies 2
- implements Infrastructure\AI\AIClientInterface
- use Domain\Constants
Klassen 1
-
AnthropicClientclass Zeile 11
Funktionen 9
-
__construct()public Zeile 24 -
loadApiKey()private Zeile 35 -
execute()public Zeile 44 -
isAvailable()Zeile 130 -
getClientName()Zeile 135 -
getModelName()Zeile 140 -
calculateCost()Zeile 145 -
setModel()Zeile 159 -
getAvailableModels()Zeile 166
Verwendet von 1
Versionen 5
-
v5
2025-12-27 23:48 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation -
v4
2025-12-27 23:47 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation -
v3
2025-12-23 08:00 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation -
v2
2025-12-22 08:54 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation -
v1
2025-12-20 17:24 | claude-code-hook | modified
Claude Code Pre-Hook Backup vor Edit-Operation
Code
<?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) * Constants::MS_PER_SECOND);
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);
}
}