|
@@ -2768,9 +2768,11 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
* GPT 中转站探活:GET /v1/models,5 秒短超时
|
|
* GPT 中转站探活:GET /v1/models,5 秒短超时
|
|
|
* 连接成功(含 401/403/404)视为可用;连接失败/超时/5xx/429 视为不可用
|
|
* 连接成功(含 401/403/404)视为可用;连接失败/超时/5xx/429 视为不可用
|
|
|
*/
|
|
*/
|
|
|
- private function checkGptServiceAvailable()
|
|
|
|
|
|
|
+ private function checkGptServiceAvailable($apiKey = null)
|
|
|
{
|
|
{
|
|
|
- $apiKey = env('GPT_54_API_KEY');
|
|
|
|
|
|
|
+ if ($apiKey === null) {
|
|
|
|
|
+ $apiKey = env('GPT_54_API_KEY');
|
|
|
|
|
+ }
|
|
|
if (empty($apiKey)) {
|
|
if (empty($apiKey)) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
@@ -2794,13 +2796,13 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
* 探活并重试:最多重试 gptRetryTimes() 次,间隔 gptRetryInterval() 秒
|
|
* 探活并重试:最多重试 gptRetryTimes() 次,间隔 gptRetryInterval() 秒
|
|
|
* 全部失败抛出友好错误
|
|
* 全部失败抛出友好错误
|
|
|
*/
|
|
*/
|
|
|
- private function ensureGptServiceAvailable()
|
|
|
|
|
|
|
+ private function ensureGptServiceAvailable($apiKey = null)
|
|
|
{
|
|
{
|
|
|
$maxRetries = $this->gptRetryTimes();
|
|
$maxRetries = $this->gptRetryTimes();
|
|
|
$interval = $this->gptRetryInterval();
|
|
$interval = $this->gptRetryInterval();
|
|
|
|
|
|
|
|
for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
|
|
for ($attempt = 0; $attempt <= $maxRetries; $attempt++) {
|
|
|
- if ($this->checkGptServiceAvailable()) {
|
|
|
|
|
|
|
+ if ($this->checkGptServiceAvailable($apiKey)) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2828,21 +2830,22 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
* @return \Generator 返回生成器,用于流式输出
|
|
* @return \Generator 返回生成器,用于流式输出
|
|
|
*/
|
|
*/
|
|
|
private function gpt54StreamResponse($post_data) {
|
|
private function gpt54StreamResponse($post_data) {
|
|
|
- $apiKey = env('GPT_54_API_KEY');
|
|
|
|
|
|
|
+ // 按模型取 Key:gpt-5.6-luna 使用 GPT_56_LUNA_API_KEY,其余使用 GPT_54_API_KEY
|
|
|
|
|
+ $modelName = (string)getProp($post_data, 'model', 'gpt-5.4');
|
|
|
|
|
+ $lunaApiKey = env('GPT_56_LUNA_API_KEY');
|
|
|
|
|
+ $apiKey = $modelName === 'gpt-5.6-luna' ? $lunaApiKey : env('GPT_54_API_KEY');
|
|
|
|
|
+ $usedLunaFallback = false;
|
|
|
|
|
+ // 流式请求长时间无任何返回(TTFB)超过该秒数时切换备用 Key(env 可配置,默认 600 秒)
|
|
|
|
|
+ $ttfbTimeoutSeconds = (int)env('GPT_SWITCH_LUNA_AFTER_SECONDS', 600);
|
|
|
|
|
|
|
|
if (empty($apiKey)) {
|
|
if (empty($apiKey)) {
|
|
|
Utils::throwError('20003:GPT API Key未配置');
|
|
Utils::throwError('20003:GPT API Key未配置');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 先探活,服务不可用时自动重试
|
|
// 先探活,服务不可用时自动重试
|
|
|
- $this->ensureGptServiceAvailable();
|
|
|
|
|
|
|
+ $this->ensureGptServiceAvailable($apiKey);
|
|
|
|
|
|
|
|
- $client = new Client(['timeout' => 1800, 'verify' => false]);
|
|
|
|
|
- $headers = [
|
|
|
|
|
- 'Authorization' => 'Bearer ' . $apiKey,
|
|
|
|
|
- 'Content-Type' => 'application/json',
|
|
|
|
|
- 'Accept' => 'text/event-stream'
|
|
|
|
|
- ];
|
|
|
|
|
|
|
+ $client = new Client(['timeout' => 1800, 'verify' => false, 'read_timeout' => $ttfbTimeoutSeconds]);
|
|
|
|
|
|
|
|
// 移除 thinking 参数,GPT-5.4 不支持
|
|
// 移除 thinking 参数,GPT-5.4 不支持
|
|
|
if (isset($post_data['thinking'])) {
|
|
if (isset($post_data['thinking'])) {
|
|
@@ -2869,6 +2872,13 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$buffer = '';
|
|
$buffer = '';
|
|
|
$suspectJsonWrap = false;
|
|
$suspectJsonWrap = false;
|
|
|
$pendingContent = '';
|
|
$pendingContent = '';
|
|
|
|
|
+ $requestStartedAt = microtime(true);
|
|
|
|
|
+
|
|
|
|
|
+ $headers = [
|
|
|
|
|
+ 'Authorization' => 'Bearer ' . $apiKey,
|
|
|
|
|
+ 'Content-Type' => 'application/json',
|
|
|
|
|
+ 'Accept' => 'text/event-stream'
|
|
|
|
|
+ ];
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
// 备用中转站地址1: https://token.ithinkai.cn/v1/chat/completions
|
|
// 备用中转站地址1: https://token.ithinkai.cn/v1/chat/completions
|
|
@@ -2885,6 +2895,10 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
|
|
|
|
|
// 逐行读取流式响应
|
|
// 逐行读取流式响应
|
|
|
while (!$body->eof()) {
|
|
while (!$body->eof()) {
|
|
|
|
|
+ // 长时间没有任何内容返回时,切换备用 Key 重试
|
|
|
|
|
+ if ($fullContent === '' && empty($usage) && (microtime(true) - $requestStartedAt) > $ttfbTimeoutSeconds) {
|
|
|
|
|
+ throw new \RuntimeException('GPT 流式响应长时间无返回,等待切换备用 Key');
|
|
|
|
|
+ }
|
|
|
$chunk = $body->read(1024); // 每次读取 1KB
|
|
$chunk = $body->read(1024); // 每次读取 1KB
|
|
|
$buffer .= $chunk;
|
|
$buffer .= $chunk;
|
|
|
|
|
|
|
@@ -3008,6 +3022,14 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
];
|
|
];
|
|
|
return;
|
|
return;
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
|
|
+ // 报错或长时间无返回时,切到 GPT_56_LUNA_API_KEY 重试一次(收费更高,慎用;仅原本未用 luna key 时触发)
|
|
|
|
|
+ if (!$usedLunaFallback && !empty($lunaApiKey) && $apiKey !== $lunaApiKey) {
|
|
|
|
|
+ $usedLunaFallback = true;
|
|
|
|
|
+ $apiKey = $lunaApiKey;
|
|
|
|
|
+ dLog('deepseek')->warning('GPT 请求异常,切换 GPT_56_LUNA_API_KEY 重试', ['error' => $e->getMessage()]);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 已输出内容不重试(避免重复);非可重试异常不重试;重试次数用完则抛出
|
|
// 已输出内容不重试(避免重复);非可重试异常不重试;重试次数用完则抛出
|
|
|
if (!empty($fullContent) || !$this->isGptRetryableException($e) || $attempt >= $maxRetries) {
|
|
if (!empty($fullContent) || !$this->isGptRetryableException($e) || $attempt >= $maxRetries) {
|
|
|
dLog('deepseek')->error('GPT 流式请求失败: ' . $e->getMessage());
|
|
dLog('deepseek')->error('GPT 流式请求失败: ' . $e->getMessage());
|
|
@@ -14220,20 +14242,20 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
|
|
|
|
|
// 仅调用 GPT-5.4 对话接口(非流式)
|
|
// 仅调用 GPT-5.4 对话接口(非流式)
|
|
|
private function gpt54ChatOnly($post_data) {
|
|
private function gpt54ChatOnly($post_data) {
|
|
|
- $apiKey = env('GPT_54_API_KEY');
|
|
|
|
|
|
|
+ // 按模型取 Key:gpt-5.6-luna 使用 GPT_56_LUNA_API_KEY,其余使用 GPT_54_API_KEY
|
|
|
|
|
+ $modelName = (string)getProp($post_data, 'model', 'gpt-5.4');
|
|
|
|
|
+ $lunaApiKey = env('GPT_56_LUNA_API_KEY');
|
|
|
|
|
+ $apiKey = $modelName === 'gpt-5.6-luna' ? $lunaApiKey : env('GPT_54_API_KEY');
|
|
|
|
|
+ $usedLunaFallback = false;
|
|
|
|
|
|
|
|
if (empty($apiKey)) {
|
|
if (empty($apiKey)) {
|
|
|
Utils::throwError('20003:GPT-5.4 API Key未配置');
|
|
Utils::throwError('20003:GPT-5.4 API Key未配置');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 先探活,服务不可用时自动重试
|
|
// 先探活,服务不可用时自动重试
|
|
|
- $this->ensureGptServiceAvailable();
|
|
|
|
|
|
|
+ $this->ensureGptServiceAvailable($apiKey);
|
|
|
|
|
|
|
|
$client = new Client(['timeout' => 1800, 'verify' => false]);
|
|
$client = new Client(['timeout' => 1800, 'verify' => false]);
|
|
|
- $headers = [
|
|
|
|
|
- 'Authorization' => 'Bearer ' . $apiKey,
|
|
|
|
|
- 'Content-Type' => 'application/json'
|
|
|
|
|
- ];
|
|
|
|
|
|
|
|
|
|
// 移除 thinking 参数,GPT-5.4 不支持
|
|
// 移除 thinking 参数,GPT-5.4 不支持
|
|
|
if (isset($post_data['thinking'])) {
|
|
if (isset($post_data['thinking'])) {
|
|
@@ -14253,6 +14275,11 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
|
|
|
|
|
while (true) {
|
|
while (true) {
|
|
|
try {
|
|
try {
|
|
|
|
|
+ $headers = [
|
|
|
|
|
+ 'Authorization' => 'Bearer ' . $apiKey,
|
|
|
|
|
+ 'Content-Type' => 'application/json'
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
// 备用中转站地址: https://token.ithinkai.cn/v1/chat/completions
|
|
// 备用中转站地址: https://token.ithinkai.cn/v1/chat/completions
|
|
|
// 备用中转站地址2: https://api.nonelinear.com/v1/chat/completions
|
|
// 备用中转站地址2: https://api.nonelinear.com/v1/chat/completions
|
|
|
$result = $client->post('https://ai-api.kkidc.com/v1/chat/completions', [
|
|
$result = $client->post('https://ai-api.kkidc.com/v1/chat/completions', [
|
|
@@ -14277,6 +14304,14 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
'usage' => $usage
|
|
'usage' => $usage
|
|
|
];
|
|
];
|
|
|
} catch (\Exception $e) {
|
|
} catch (\Exception $e) {
|
|
|
|
|
+ // 报错或超时时,切到 GPT_56_LUNA_API_KEY 重试一次(收费更高,慎用;仅原本未用 luna key 时触发)
|
|
|
|
|
+ if (!$usedLunaFallback && !empty($lunaApiKey) && $apiKey !== $lunaApiKey) {
|
|
|
|
|
+ $usedLunaFallback = true;
|
|
|
|
|
+ $apiKey = $lunaApiKey;
|
|
|
|
|
+ dLog('deepseek')->warning('GPT-5.4 请求异常,切换 GPT_56_LUNA_API_KEY 重试', ['error' => $e->getMessage()]);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
if (!$this->isGptRetryableException($e) || $attempt >= $maxRetries) {
|
|
if (!$this->isGptRetryableException($e) || $attempt >= $maxRetries) {
|
|
|
dLog('deepseek')->error('GPT-5.4 请求失败: ' . $e->getMessage());
|
|
dLog('deepseek')->error('GPT-5.4 请求失败: ' . $e->getMessage());
|
|
|
logDB('deepseek', 'error', 'GPT-5.4 请求失败', ['error' => $e->getMessage()]);
|
|
logDB('deepseek', 'error', 'GPT-5.4 请求失败', ['error' => $e->getMessage()]);
|