feat(providers): 更新 Claude Kiro 版本并增强 Grok API 重试机制
- 升级 Claude Kiro 版本至 0.11.63 并更新 AWS SDK 版本 - 为 Grok API 添加统一的重试机制,支持 429/5xx 状态码和网络错误 - 在 Claude Kiro 请求头中添加 agentTaskType 和 x-amzn-codewhisperer-optout - 优化 Grok 错误处理,网络错误时触发凭证切换但不增加错误计数
This commit is contained in:
parent
82c667ac5d
commit
2a3312df15
3 changed files with 120 additions and 69 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
2.11.5.2
|
2.11.6
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ const KIRO_CONSTANTS = {
|
||||||
AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests
|
AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests
|
||||||
TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking)
|
TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking)
|
||||||
USER_AGENT: 'KiroIDE',
|
USER_AGENT: 'KiroIDE',
|
||||||
KIRO_VERSION: '0.8.140',
|
KIRO_VERSION: '0.11.63',
|
||||||
CONTENT_TYPE_JSON: 'application/json',
|
CONTENT_TYPE_JSON: 'application/json',
|
||||||
ACCEPT_JSON: 'application/json',
|
ACCEPT_JSON: 'application/json',
|
||||||
AUTH_METHOD_SOCIAL: 'social',
|
AUTH_METHOD_SOCIAL: 'social',
|
||||||
|
|
@ -463,10 +463,12 @@ export class KiroApiService {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': KIRO_CONSTANTS.CONTENT_TYPE_JSON,
|
'Content-Type': KIRO_CONSTANTS.CONTENT_TYPE_JSON,
|
||||||
'Accept': KIRO_CONSTANTS.ACCEPT_JSON,
|
'Accept': KIRO_CONSTANTS.ACCEPT_JSON,
|
||||||
'amz-sdk-request': 'attempt=1; max=1',
|
'amz-sdk-invocation-id': uuidv4(),
|
||||||
|
'amz-sdk-request': 'attempt=1; max=3',
|
||||||
|
'x-amzn-codewhisperer-optout': true,
|
||||||
'x-amzn-kiro-agent-mode': 'vibe',
|
'x-amzn-kiro-agent-mode': 'vibe',
|
||||||
'x-amz-user-agent': `aws-sdk-js/1.0.0 KiroIDE-${kiroVersion}-${machineId}`,
|
'x-amz-user-agent': `aws-sdk-js/1.0.34 KiroIDE-${kiroVersion}-${machineId}`,
|
||||||
'user-agent': `aws-sdk-js/1.0.0 ua/2.1 os/${osName} lang/js md/nodejs#${nodeVersion} api/codewhispererruntime#1.0.0 m/E KiroIDE-${kiroVersion}-${machineId}`,
|
'user-agent': `aws-sdk-js/1.0.34 ua/2.1 os/${osName} lang/js md/nodejs#${nodeVersion} api/codewhispererstreaming#1.0.34 m/E KiroIDE-${kiroVersion}-${machineId}`,
|
||||||
'Connection': 'close'
|
'Connection': 'close'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -1290,6 +1292,7 @@ async saveCredentialsToFile(filePath, newData) {
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
conversationState: {
|
conversationState: {
|
||||||
|
agentTaskType: "vibe",
|
||||||
chatTriggerType: KIRO_CONSTANTS.CHAT_TRIGGER_TYPE_MANUAL,
|
chatTriggerType: KIRO_CONSTANTS.CHAT_TRIGGER_TYPE_MANUAL,
|
||||||
conversationId: conversationId,
|
conversationId: conversationId,
|
||||||
currentMessage: {} // Will be populated as userInputMessage
|
currentMessage: {} // Will be populated as userInputMessage
|
||||||
|
|
@ -2979,8 +2982,8 @@ async saveCredentialsToFile(filePath, newData) {
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'Authorization': `Bearer ${this.accessToken}`,
|
'Authorization': `Bearer ${this.accessToken}`,
|
||||||
'x-amz-user-agent': `aws-sdk-js/1.0.0 KiroIDE-${kiroVersion}-${machineId}`,
|
'x-amz-user-agent': `aws-sdk-js/1.0.34 KiroIDE-${kiroVersion}-${machineId}`,
|
||||||
'user-agent': `aws-sdk-js/1.0.0 ua/2.1 os/${osName} lang/js md/nodejs#${nodeVersion} api/codewhispererruntime#1.0.0 m/E KiroIDE-${kiroVersion}-${machineId}`,
|
'user-agent': `aws-sdk-js/1.0.34 ua/2.1 os/${osName} lang/js md/nodejs#${nodeVersion} api/codewhispererstreaming#1.0.34 m/E KiroIDE-${kiroVersion}-${machineId}`,
|
||||||
'amz-sdk-invocation-id': uuidv4(),
|
'amz-sdk-invocation-id': uuidv4(),
|
||||||
'amz-sdk-request': 'attempt=1; max=1',
|
'amz-sdk-request': 'attempt=1; max=1',
|
||||||
'Connection': 'close'
|
'Connection': 'close'
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,9 @@ import logger from '../../utils/logger.js';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as https from 'https';
|
import * as https from 'https';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
import { MODEL_PROTOCOL_PREFIX, MODEL_PROVIDER, isRetryableNetworkError } from '../../utils/common.js';
|
||||||
import { getProviderModels } from '../provider-models.js';
|
import { getProviderModels } from '../provider-models.js';
|
||||||
import { configureAxiosProxy, configureTLSSidecar } from '../../utils/proxy-utils.js';
|
import { configureAxiosProxy, configureTLSSidecar } from '../../utils/proxy-utils.js';
|
||||||
import { MODEL_PROVIDER } from '../../utils/common.js';
|
|
||||||
import { ConverterFactory } from '../../converters/ConverterFactory.js';
|
import { ConverterFactory } from '../../converters/ConverterFactory.js';
|
||||||
import * as readline from 'readline';
|
import * as readline from 'readline';
|
||||||
import { getProviderPoolManager } from '../../services/service-manager.js';
|
import { getProviderPoolManager } from '../../services/service-manager.js';
|
||||||
|
|
@ -101,15 +100,13 @@ export class GrokApiService {
|
||||||
async acceptTos() {
|
async acceptTos() {
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/accept-tos`, headers: this.buildHeaders(), data: {}, httpAgent, httpsAgent, timeout: 15000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/accept-tos`, headers: this.buildHeaders(), data: {}, httpAgent, httpsAgent, timeout: 15000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
try { await this.callApi(axiosConfig); } catch (e) { logger.debug(`[Grok TOS] ${e.message}`); }
|
||||||
try { await axios(axiosConfig); } catch (e) { logger.debug(`[Grok TOS] ${e.message}`); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setBirthDate() {
|
async setBirthDate() {
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/set-birth-date`, headers: this.buildHeaders(), data: { "birthDate": "1990-01-01" }, httpAgent, httpsAgent, timeout: 15000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/set-birth-date`, headers: this.buildHeaders(), data: { "birthDate": "1990-01-01" }, httpAgent, httpsAgent, timeout: 15000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
try { await this.callApi(axiosConfig); } catch (e) { logger.debug(`[Grok Birth] ${e.message}`); }
|
||||||
try { await axios(axiosConfig); } catch (e) { logger.debug(`[Grok Birth] ${e.message}`); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async enableNsfwAccount() {
|
async enableNsfwAccount() {
|
||||||
|
|
@ -128,25 +125,105 @@ export class GrokApiService {
|
||||||
headers['x-user-agent'] = 'connect-es/2.1.1';
|
headers['x-user-agent'] = 'connect-es/2.1.1';
|
||||||
headers['referer'] = `${this.baseUrl}/?_s=data`;
|
headers['referer'] = `${this.baseUrl}/?_s=data`;
|
||||||
|
|
||||||
const axiosConfig = {
|
const axiosConfig = {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `${this.baseUrl}/auth_mgmt.AuthManagement/UpdateUserFeatureControls`,
|
url: `${this.baseUrl}/auth_mgmt.AuthManagement/UpdateUserFeatureControls`,
|
||||||
headers,
|
headers,
|
||||||
data: payload,
|
data: payload,
|
||||||
httpAgent,
|
httpAgent,
|
||||||
httpsAgent,
|
httpsAgent,
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
responseType: 'arraybuffer'
|
responseType: 'arraybuffer'
|
||||||
};
|
};
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
try { await this.callApi(axiosConfig); } catch (e) { throw e; }
|
||||||
try { await axios(axiosConfig); } catch (e) { throw e; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_applySidecar(axiosConfig) {
|
_applySidecar(axiosConfig) {
|
||||||
return configureTLSSidecar(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
return configureTLSSidecar(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async callApi(axiosConfig, retryCount = 0) {
|
||||||
|
const maxRetries = this.config.REQUEST_MAX_RETRIES || 3;
|
||||||
|
const baseDelay = this.config.REQUEST_BASE_DELAY || 1000;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._applySidecar(axiosConfig);
|
||||||
|
const response = await axios(axiosConfig);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
const status = error.response?.status;
|
||||||
|
const isNetworkError = isRetryableNetworkError(error);
|
||||||
|
|
||||||
|
if (status === 401 || status === 403) {
|
||||||
|
this.handleApiError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((status === 429 || (status >= 500 && status < 600) || isNetworkError) && retryCount < maxRetries) {
|
||||||
|
const delay = baseDelay * Math.pow(2, retryCount);
|
||||||
|
const reason = status ? `Status ${status}` : (error.code || 'Network error');
|
||||||
|
logger.info(`[Grok API] ${reason}. Retrying in ${delay}ms... (${retryCount + 1}/${maxRetries})`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
return this.callApi(axiosConfig, retryCount + 1);
|
||||||
|
}
|
||||||
|
this.handleApiError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async * _streamChat(axiosConfig, reqBaseUrl, payload, retryCount = 0) {
|
||||||
|
const maxRetries = this.config.REQUEST_MAX_RETRIES || 3;
|
||||||
|
const baseDelay = this.config.REQUEST_BASE_DELAY || 1000;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._applySidecar(axiosConfig);
|
||||||
|
const response = await axios(axiosConfig);
|
||||||
|
const rl = readline.createInterface({ input: response.data, terminal: false });
|
||||||
|
let lastResponseId = payload.responseMetadata?.requestModelDetails?.modelId || "final";
|
||||||
|
|
||||||
|
for await (const line of rl) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed) continue;
|
||||||
|
let dataStr = trimmed.startsWith('data: ') ? trimmed.slice(6).trim() : trimmed;
|
||||||
|
if (dataStr === '[DONE]') break;
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(dataStr);
|
||||||
|
if (json.result?.response) {
|
||||||
|
const resp = json.result.response;
|
||||||
|
resp._requestBaseUrl = reqBaseUrl;
|
||||||
|
resp._uuid = this.uuid;
|
||||||
|
if (resp.responseId) lastResponseId = resp.responseId;
|
||||||
|
if (resp.streamingVideoGenerationResponse) {
|
||||||
|
const vid = resp.streamingVideoGenerationResponse;
|
||||||
|
if (vid.progress === 100 && vid.videoUrl && (payload.responseMetadata?.modelConfigOverride?.modelMap?.videoGenModelConfig?.resolutionName === "720p")) {
|
||||||
|
const hdUrl = await this.upscaleVideo(vid.videoUrl);
|
||||||
|
if (hdUrl) vid.videoUrl = hdUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield json;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
yield { result: { response: { isDone: true, responseId: lastResponseId, _requestBaseUrl: reqBaseUrl, _uuid: this.uuid } } };
|
||||||
|
} catch (error) {
|
||||||
|
const status = error.response?.status;
|
||||||
|
const isNetworkError = isRetryableNetworkError(error);
|
||||||
|
|
||||||
|
if (status === 401 || status === 403) {
|
||||||
|
this.handleApiError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((status === 429 || (status >= 500 && status < 600) || isNetworkError) && retryCount < maxRetries) {
|
||||||
|
const delay = baseDelay * Math.pow(2, retryCount);
|
||||||
|
const reason = status ? `Status ${status}` : (error.code || 'Network error');
|
||||||
|
logger.info(`[Grok API] ${reason} during stream. Retrying in ${delay}ms... (${retryCount + 1}/${maxRetries})`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
yield* this._streamChat(axiosConfig, reqBaseUrl, payload, retryCount + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handleApiError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
if (this.isInitialized) return;
|
if (this.isInitialized) return;
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
|
|
@ -165,10 +242,8 @@ export class GrokApiService {
|
||||||
const payload = { "requestKind": "DEFAULT", "modelName": "grok-3" };
|
const payload = { "requestKind": "DEFAULT", "modelName": "grok-3" };
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/rate-limits`, headers, data: payload, httpAgent, httpsAgent, timeout: 30000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/rate-limits`, headers, data: payload, httpAgent, httpsAgent, timeout: 30000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
|
||||||
try {
|
try {
|
||||||
const response = await axios(axiosConfig);
|
const data = await this.callApi(axiosConfig);
|
||||||
const data = response.data;
|
|
||||||
let remaining = data.remainingTokens !== undefined ? data.remainingTokens : (data.remainingQueries !== undefined ? data.remainingQueries : data.totalQueries);
|
let remaining = data.remainingTokens !== undefined ? data.remainingTokens : (data.remainingQueries !== undefined ? data.remainingQueries : data.totalQueries);
|
||||||
if (data.totalQueries > 0) {
|
if (data.totalQueries > 0) {
|
||||||
data.totalLimit = data.totalQueries;
|
data.totalLimit = data.totalQueries;
|
||||||
|
|
@ -245,10 +320,9 @@ export class GrokApiService {
|
||||||
|
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/media/post/create`, headers, data: payload, httpAgent, httpsAgent, timeout: 30000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/media/post/create`, headers, data: payload, httpAgent, httpsAgent, timeout: 30000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
|
||||||
try {
|
try {
|
||||||
const response = await axios(axiosConfig);
|
const data = await this.callApi(axiosConfig);
|
||||||
const postId = response.data?.post?.id;
|
const postId = data?.post?.id;
|
||||||
if (postId) logger.info(`[Grok Post] Media post created: ${postId} (type=${mediaType})`);
|
if (postId) logger.info(`[Grok Post] Media post created: ${postId} (type=${mediaType})`);
|
||||||
return postId;
|
return postId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -265,10 +339,9 @@ export class GrokApiService {
|
||||||
const videoId = idMatch[1];
|
const videoId = idMatch[1];
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/media/video/upscale`, headers: this.buildHeaders(), data: { videoId }, httpAgent, httpsAgent, timeout: 30000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/media/video/upscale`, headers: this.buildHeaders(), data: { videoId }, httpAgent, httpsAgent, timeout: 30000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
|
||||||
try {
|
try {
|
||||||
const response = await axios(axiosConfig);
|
const data = await this.callApi(axiosConfig);
|
||||||
return response.data?.hdMediaUrl || videoUrl;
|
return data?.hdMediaUrl || videoUrl;
|
||||||
} catch (error) { return videoUrl; }
|
} catch (error) { return videoUrl; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,10 +365,9 @@ export class GrokApiService {
|
||||||
timeout: 15000
|
timeout: 15000
|
||||||
};
|
};
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
|
||||||
try {
|
try {
|
||||||
const response = await axios(axiosConfig);
|
const data = await this.callApi(axiosConfig);
|
||||||
const shareLink = response.data?.shareLink;
|
const shareLink = data?.shareLink;
|
||||||
if (shareLink) {
|
if (shareLink) {
|
||||||
// 从 shareLink 中提取 ID (通常与输入的 postId 一致)
|
// 从 shareLink 中提取 ID (通常与输入的 postId 一致)
|
||||||
const idMatch = shareLink.match(/\/post\/([0-9a-fA-F-]{36}|[0-9a-fA-F]{32})/);
|
const idMatch = shareLink.match(/\/post\/([0-9a-fA-F-]{36}|[0-9a-fA-F]{32})/);
|
||||||
|
|
@ -477,8 +549,7 @@ export class GrokApiService {
|
||||||
if (!b64) return null;
|
if (!b64) return null;
|
||||||
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/upload-file`, headers: this.buildHeaders(), data: { fileName: `file.${mime.split("/")[1] || "bin"}`, fileMimeType: mime, content: b64 }, httpAgent, httpsAgent, timeout: 30000 };
|
const axiosConfig = { method: 'post', url: `${this.baseUrl}/rest/app-chat/upload-file`, headers: this.buildHeaders(), data: { fileName: `file.${mime.split("/")[1] || "bin"}`, fileMimeType: mime, content: b64 }, httpAgent, httpsAgent, timeout: 30000 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
try { return await this.callApi(axiosConfig); } catch (error) { return null; }
|
||||||
try { return (await axios(axiosConfig)).data; } catch (error) { return null; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async * generateContentStream(model, requestBody) {
|
async * generateContentStream(model, requestBody) {
|
||||||
|
|
@ -571,43 +642,20 @@ export class GrokApiService {
|
||||||
const payload = this.buildPayload(model, requestBody);
|
const payload = this.buildPayload(model, requestBody);
|
||||||
const axiosConfig = { method: 'post', url: this.chatApi, headers: this.buildHeaders(), data: payload, responseType: 'stream', httpAgent, httpsAgent, timeout: 60000, maxRedirects: 0 };
|
const axiosConfig = { method: 'post', url: this.chatApi, headers: this.buildHeaders(), data: payload, responseType: 'stream', httpAgent, httpsAgent, timeout: 60000, maxRedirects: 0 };
|
||||||
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
configureAxiosProxy(axiosConfig, this.config, MODEL_PROVIDER.GROK_CUSTOM);
|
||||||
this._applySidecar(axiosConfig);
|
|
||||||
|
|
||||||
try {
|
yield* this._streamChat(axiosConfig, reqBaseUrl, payload);
|
||||||
const response = await axios(axiosConfig);
|
|
||||||
const rl = readline.createInterface({ input: response.data, terminal: false });
|
|
||||||
let lastResponseId = payload.responseMetadata?.requestModelDetails?.modelId || "final";
|
|
||||||
|
|
||||||
for await (const line of rl) {
|
|
||||||
const trimmed = line.trim();
|
|
||||||
if (!trimmed) continue;
|
|
||||||
let dataStr = trimmed.startsWith('data: ') ? trimmed.slice(6).trim() : trimmed;
|
|
||||||
if (dataStr === '[DONE]') break;
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(dataStr);
|
|
||||||
if (json.result?.response) {
|
|
||||||
const resp = json.result.response;
|
|
||||||
resp._requestBaseUrl = reqBaseUrl;
|
|
||||||
resp._uuid = this.uuid;
|
|
||||||
if (resp.responseId) lastResponseId = resp.responseId;
|
|
||||||
if (resp.streamingVideoGenerationResponse) {
|
|
||||||
const vid = resp.streamingVideoGenerationResponse;
|
|
||||||
if (vid.progress === 100 && vid.videoUrl && (requestBody.videoGenModelConfig?.resolutionName === "720p")) {
|
|
||||||
const hdUrl = await this.upscaleVideo(vid.videoUrl);
|
|
||||||
if (hdUrl) vid.videoUrl = hdUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield json;
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
yield { result: { response: { isDone: true, responseId: lastResponseId, _requestBaseUrl: reqBaseUrl, _uuid: this.uuid } } };
|
|
||||||
} catch (error) { this.handleApiError(error); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleApiError(error) {
|
handleApiError(error) {
|
||||||
const status = error.response?.status;
|
const status = error.response?.status;
|
||||||
if (status === 401 || status === 403) { error.shouldSwitchCredential = true; error.message = 'Grok authentication failed (SSO token invalid or expired)'; }
|
if (status === 401 || status === 403) {
|
||||||
|
error.shouldSwitchCredential = true;
|
||||||
|
error.message = 'Grok authentication failed (SSO token invalid or expired)';
|
||||||
|
} else if (isRetryableNetworkError(error)) {
|
||||||
|
// 网络超时等可重试错误,触发凭证切换而不立即增加错误计数(避免因偶发波动导致节点下线)
|
||||||
|
error.shouldSwitchCredential = true;
|
||||||
|
error.skipErrorCount = true;
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue