$options['model'] ?? 'claude-sonnet-4', 'max_tokens' => $options['max_tokens'] ?? 4096, 'messages' => $formattedMessages, 'system' => $systemMessage, 'temperature' => $options['temperature'] ?? null, 'top_p' => $options['top_p'] ?? null, 'stop_sequences' => $options['stop'] ?? null, ], fn($value) => $value !== null); return $request; } protected function getAuthHeaders(): array { return [ 'x-api-key' => $this->apiKey, 'anthropic-version' => $this->apiVersion, 'Content-Type' => 'application/json', ]; } public function chatCompletion(array $messages, array $options = []): array { $data = $this->buildRequest($messages, $options); return $this->makeRequest('/messages', $data); } public function normalizeResponse(array $response): array { $content = ''; if (isset($response['content']) && is_array($response['content'])) { foreach ($response['content'] as $block) { if ($block['type'] === 'text') { $content .= $block['text']; } } } return [ 'id' => $response['id'] ?? null, 'model' => $response['model'] ?? null, 'content' => $content, 'role' => $response['role'] ?? 'assistant', 'finish_reason' => $response['stop_reason'] ?? null, 'usage' => [ 'prompt_tokens' => $response['usage']['input_tokens'] ?? 0, 'completion_tokens' => $response['usage']['output_tokens'] ?? 0, 'total_tokens' => ($response['usage']['input_tokens'] ?? 0) + ($response['usage']['output_tokens'] ?? 0), ], 'raw_response' => $response, ]; } public function calculateCost(int $promptTokens, int $completionTokens, string $model): float { $cacheKey = "pricing:anthropic:{$model}"; $pricing = Cache::remember($cacheKey, 3600, function () use ($model) { return ModelPricing::where('provider', 'anthropic') ->where('model', $model) ->where('is_active', true) ->first(); }); if (!$pricing) { return 0.0; } $promptCost = ($promptTokens / 1_000_000) * $pricing->input_price_per_million; $completionCost = ($completionTokens / 1_000_000) * $pricing->output_price_per_million; return round($promptCost + $completionCost, 6); } public function getSupportedModels(): array { return [ 'claude-opus-4', 'claude-sonnet-4', 'claude-haiku-4', 'claude-3-opus', 'claude-3-sonnet', 'claude-3-haiku', ]; } }