isAuth = false; parent::__construct(); } public function provider(): string { return 'deepseek'; } /** * 调用 DeepSeek chat completions */ public function chatCompletions(array $messages, array $options = []): AiChatResult { $cfg = AiRuntimeConfigService::getInstance()->resolve($this->provider()); $apiUrl = (string) ($cfg['api_url'] ?? ''); $apiKey = (string) ($cfg['api_key'] ?? ''); $defaultModel = (string) ($cfg['model'] ?? 'deepseek-chat'); $timeout = (float) ($cfg['timeout'] ?? 60); if ($apiKey === '') { $this->utils->errorThrow('DeepSeek 暂未开通(请在「AI密钥管理」或 .env DEEPSEEK_API_KEY 配置)'); } if ($apiUrl === '') { $this->utils->errorThrow('未配置 DeepSeek 接口地址'); } if (empty($messages)) { $this->utils->errorThrow('AI 对话消息不能为空'); } $model = (string) ($options['model'] ?? $defaultModel); $body = [ 'model' => $model, 'messages' => array_values($messages), 'stream' => false, ]; foreach (['temperature', 'max_tokens', 'top_p'] as $key) { if (array_key_exists($key, $options)) { $body[$key] = $options[$key]; } } try { $client = new Client([ 'timeout' => $timeout, 'connect_timeout' => min(10.0, $timeout), ]); $response = $client->request('POST', $apiUrl, [ 'headers' => [ 'Authorization' => 'Bearer ' . $apiKey, 'Content-Type' => 'application/json', ], 'json' => $body, ]); $raw = json_decode((string) $response->getBody(), true); if (!is_array($raw)) { $this->utils->errorThrow('DeepSeek 返回非 JSON'); } $content = (string) data_get($raw, 'choices.0.message.content', ''); if ($content === '') { $errMsg = (string) ($raw['error']['message'] ?? ''); $this->utils->errorThrow($errMsg !== '' ? ('DeepSeek 调用失败:' . $errMsg) : 'DeepSeek 未返回有效内容'); } $usage = is_array($raw['usage'] ?? null) ? $raw['usage'] : []; return new AiChatResult( $content, $raw, $this->provider(), $model, $usage, (int) ($cfg['api_key_id'] ?? 0) ); } catch (Throwable $e) { if ($e instanceof GuzzleException) { $this->utils->errorThrow('DeepSeek 请求失败:' . $e->getMessage()); } throw $e; } } }