isAuth = false; parent::__construct(); } public function provider(): string { return 'spark'; } /** * 调用星火 chat completions * Authorization 使用 Bearer + API Key(与官方 OpenAPI 一致) */ public function chatCompletions(array $messages, array $options = []): AiChatResult { $cfg = AiRuntimeConfigService::getInstance()->resolve($this->provider()); $apiUrl = (string) ($cfg['api_url'] ?? ''); $password = (string) ($cfg['api_key'] ?? ''); $defaultModel = (string) ($cfg['model'] ?? 'lite'); $timeout = (float) ($cfg['timeout'] ?? 60); if ($password === '') { $this->utils->errorThrow('未配置讯飞星火 API 密钥(请在「AI密钥管理」或 .env SPARK_API_PASSWORD 配置)'); } if ($apiUrl === '') { $this->utils->errorThrow('未配置讯飞星火接口地址'); } if (empty($messages)) { $this->utils->errorThrow('AI 对话消息不能为空'); } $model = (string) ($options['model'] ?? $defaultModel); $body = [ 'model' => $model, 'messages' => array_values($messages), ]; // 可选参数透传,便于业务微调温度等 foreach (['temperature', 'max_tokens', 'top_p', 'stream'] as $key) { if (array_key_exists($key, $options)) { $body[$key] = $options[$key]; } } // 默认关闭流式,业务侧解析整包 JSON 更简单 if (!isset($body['stream'])) { $body['stream'] = false; } try { $client = new Client([ 'timeout' => $timeout, 'connect_timeout' => min(10.0, $timeout), ]); $response = $client->request('POST', $apiUrl, [ 'headers' => [ 'Authorization' => 'Bearer ' . $password, 'Content-Type' => 'application/json', ], 'json' => $body, ]); $raw = json_decode((string) $response->getBody(), true); if (!is_array($raw)) { $this->utils->errorThrow('讯飞星火返回非 JSON'); } $content = (string) data_get($raw, 'choices.0.message.content', ''); if ($content === '') { $errMsg = (string) ($raw['error']['message'] ?? $raw['message'] ?? ''); $this->utils->errorThrow($errMsg !== '' ? ('讯飞星火调用失败:' . $errMsg) : '讯飞星火未返回有效内容'); } $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('讯飞星火请求失败:' . $e->getMessage()); } throw $e; } } }