$model, 'max_tokens' => $thinkingMode ? 16000 : 4096, 'messages' => $claudeMessages, 'stream' => true ]; if (!empty($systemPrompt)) { $bodyData['system'] = $systemPrompt; } if ($thinkingMode) { $bodyData['thinking'] = [ 'type' => 'enabled', 'budget_tokens' => 10000 ]; } $body = json_encode($bodyData); $headers = [ 'x-api-key: ' . $apiKey, 'anthropic-version: 2023-06-01', 'Content-Type: application/json', 'Accept: text/event-stream' ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 120, CURLOPT_RETURNTRANSFER => false, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_WRITEFUNCTION => function ($ch, $data) use ($onChunk) { $lines = explode("\n", $data); $currentEvent = ''; foreach ($lines as $line) { $line = trim($line); if ($line === '') { $currentEvent = ''; continue; } if (str_starts_with($line, 'event: ')) { $currentEvent = substr($line, 7); continue; } if (str_starts_with($line, 'data: ')) { $payload = substr($line, 6); $json = json_decode($payload, true); if (!$json) { continue; } switch ($currentEvent) { case 'content_block_delta': if (isset($json['delta'])) { $delta = $json['delta']; if (isset($delta['type']) && $delta['type'] === 'thinking_delta' && isset($delta['thinking'])) { $onChunk($delta['thinking'], 'thinking'); } elseif (isset($delta['text'])) { $onChunk($delta['text'], 'content'); } } break; case 'message_stop': return strlen($data); case 'error': if (isset($json['error']['message'])) { throw new \RuntimeException('Claude API错误: ' . $json['error']['message']); } break; } } } return strlen($data); } ]); $result = curl_exec($ch); if ($result === false) { $error = curl_error($ch); curl_close($ch); throw new \RuntimeException('API请求失败: ' . $error); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode >= 400) { throw new \RuntimeException('API返回错误,状态码: ' . $httpCode); } } }