qwenproxy-cli 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clean-cache.js +2 -0
- package/dist/delete-chats.js +101 -3
- package/dist/index.js +74 -58
- package/dist/login.js +2 -0
- package/dist/reset-cooldowns.js +2 -0
- package/dist/sync-clients.js +2 -0
- package/package.json +1 -1
- package/src/api/server.ts +17 -59
- package/src/core/config.ts +2 -0
- package/src/core/version.ts +1 -1
- package/src/routes/chat/account.ts +10 -5
- package/src/services/auth-playwright.ts +5 -0
- package/src/services/qwen.ts +65 -7
package/dist/clean-cache.js
CHANGED
|
@@ -965,6 +965,7 @@ var init_config = __esm({
|
|
|
965
965
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
966
966
|
// drop out of the warm set and get evicted.
|
|
967
967
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
968
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
968
969
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
969
970
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
970
971
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -1113,6 +1114,7 @@ var init_config = __esm({
|
|
|
1113
1114
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
1114
1115
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
1115
1116
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
1117
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
1116
1118
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
1117
1119
|
},
|
|
1118
1120
|
captcha: {
|
package/dist/delete-chats.js
CHANGED
|
@@ -1020,6 +1020,7 @@ var init_config = __esm({
|
|
|
1020
1020
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
1021
1021
|
// drop out of the warm set and get evicted.
|
|
1022
1022
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
1023
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
1023
1024
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
1024
1025
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
1025
1026
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -1168,6 +1169,7 @@ var init_config = __esm({
|
|
|
1168
1169
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
1169
1170
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
1170
1171
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
1172
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
1171
1173
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
1172
1174
|
},
|
|
1173
1175
|
captcha: {
|
|
@@ -1901,6 +1903,14 @@ var init_mutex = __esm({
|
|
|
1901
1903
|
});
|
|
1902
1904
|
|
|
1903
1905
|
// src/core/account-priority.ts
|
|
1906
|
+
var account_priority_exports = {};
|
|
1907
|
+
__export(account_priority_exports, {
|
|
1908
|
+
ensureAccountInPriority: () => ensureAccountInPriority,
|
|
1909
|
+
getAccountsByPriority: () => getAccountsByPriority,
|
|
1910
|
+
invalidatePriorityCache: () => invalidatePriorityCache,
|
|
1911
|
+
markAccountFailed: () => markAccountFailed,
|
|
1912
|
+
markAccountSuccessful: () => markAccountSuccessful
|
|
1913
|
+
});
|
|
1904
1914
|
import {
|
|
1905
1915
|
existsSync,
|
|
1906
1916
|
mkdirSync,
|
|
@@ -1910,6 +1920,9 @@ import {
|
|
|
1910
1920
|
function isPersistableAccount(accountId) {
|
|
1911
1921
|
return accountId !== MOCK_ACCOUNT_ID;
|
|
1912
1922
|
}
|
|
1923
|
+
function invalidatePriorityCache() {
|
|
1924
|
+
priorityCache = null;
|
|
1925
|
+
}
|
|
1913
1926
|
function loadPriority() {
|
|
1914
1927
|
if (priorityCache) return priorityCache;
|
|
1915
1928
|
try {
|
|
@@ -1930,6 +1943,41 @@ function loadPriority() {
|
|
|
1930
1943
|
priorityCache = { accountOrder: [], lastUpdated: 0 };
|
|
1931
1944
|
return priorityCache;
|
|
1932
1945
|
}
|
|
1946
|
+
function savePriority(data) {
|
|
1947
|
+
try {
|
|
1948
|
+
mkdirSync(DATA_DIR3, { recursive: true });
|
|
1949
|
+
writeFileSync(PRIORITY_FILE, JSON.stringify(data, null, 2), "utf-8");
|
|
1950
|
+
priorityCache = data;
|
|
1951
|
+
} catch (err) {
|
|
1952
|
+
console.error("\u274C [AccountPriority] Failed to save priority file:", err.message);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function markAccountSuccessful(accountId) {
|
|
1956
|
+
if (!isPersistableAccount(accountId)) return;
|
|
1957
|
+
const data = loadPriority();
|
|
1958
|
+
data.accountOrder = data.accountOrder.filter((id) => id !== accountId);
|
|
1959
|
+
data.accountOrder.unshift(accountId);
|
|
1960
|
+
data.lastUpdated = Date.now();
|
|
1961
|
+
savePriority(data);
|
|
1962
|
+
}
|
|
1963
|
+
function markAccountFailed(accountId) {
|
|
1964
|
+
if (!isPersistableAccount(accountId)) return;
|
|
1965
|
+
const data = loadPriority();
|
|
1966
|
+
data.accountOrder = data.accountOrder.filter((id) => id !== accountId);
|
|
1967
|
+
data.accountOrder.push(accountId);
|
|
1968
|
+
data.lastUpdated = Date.now();
|
|
1969
|
+
savePriority(data);
|
|
1970
|
+
}
|
|
1971
|
+
function ensureAccountInPriority(accountId) {
|
|
1972
|
+
if (!isPersistableAccount(accountId)) return;
|
|
1973
|
+
const data = loadPriority();
|
|
1974
|
+
if (data.accountOrder.includes(accountId)) {
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
data.accountOrder.push(accountId);
|
|
1978
|
+
data.lastUpdated = Date.now();
|
|
1979
|
+
savePriority(data);
|
|
1980
|
+
}
|
|
1933
1981
|
function getAccountsByPriority(accounts) {
|
|
1934
1982
|
const data = loadPriority();
|
|
1935
1983
|
if (data.accountOrder.length === 0) {
|
|
@@ -6583,6 +6631,11 @@ async function ensurePlaywrightInitialized(accountId) {
|
|
|
6583
6631
|
});
|
|
6584
6632
|
} catch {
|
|
6585
6633
|
}
|
|
6634
|
+
try {
|
|
6635
|
+
const { ensureAccountInPriority: ensureAccountInPriority2 } = await Promise.resolve().then(() => (init_account_priority(), account_priority_exports));
|
|
6636
|
+
ensureAccountInPriority2(accountId);
|
|
6637
|
+
} catch {
|
|
6638
|
+
}
|
|
6586
6639
|
}
|
|
6587
6640
|
async function getBasicHeaders2(accountId) {
|
|
6588
6641
|
if (isAuthMockEnabled()) {
|
|
@@ -9514,9 +9567,13 @@ function parseQwenJsonError(raw, status, accountId) {
|
|
|
9514
9567
|
}
|
|
9515
9568
|
if (errorJson?.success === false) {
|
|
9516
9569
|
const code = errorJson.data?.code || errorJson.code || "UpstreamError";
|
|
9517
|
-
|
|
9570
|
+
const detailsStr = typeof details === "string" ? details : "";
|
|
9571
|
+
const codeStr = String(code).toLowerCase();
|
|
9572
|
+
const detailsLower = detailsStr.toLowerCase();
|
|
9573
|
+
const isAuthExpired = status === 401 || codeStr.includes("unauthorized") || detailsLower.includes("token has expired") || detailsLower.includes("log in") || detailsLower.includes("login") || detailsLower.includes("session") || detailsLower.includes("n\xE3o autorizado") || detailsLower.includes("unauthorized");
|
|
9574
|
+
if (isAuthExpired) {
|
|
9518
9575
|
return new QwenSessionExpiredError(
|
|
9519
|
-
`Session expired: ${
|
|
9576
|
+
`Session expired: ${detailsStr || codeStr}`,
|
|
9520
9577
|
accountId || "global"
|
|
9521
9578
|
);
|
|
9522
9579
|
}
|
|
@@ -10055,8 +10112,24 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
10055
10112
|
)
|
|
10056
10113
|
);
|
|
10057
10114
|
}
|
|
10115
|
+
const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
|
|
10116
|
+
if (parsedJsonErr instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
|
|
10117
|
+
retriedNonSseResponse = true;
|
|
10118
|
+
logger.warn(
|
|
10119
|
+
"[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
|
|
10120
|
+
{ accountId, chatId: chatSessionId ?? "new" }
|
|
10121
|
+
);
|
|
10122
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
10123
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
10124
|
+
if (refreshed.success) {
|
|
10125
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
10126
|
+
activeHeaders = fresh.headers;
|
|
10127
|
+
response = await fetchCompletion(activeHeaders);
|
|
10128
|
+
continue;
|
|
10129
|
+
}
|
|
10130
|
+
}
|
|
10058
10131
|
throw withCreatedChatMetadata(
|
|
10059
|
-
|
|
10132
|
+
parsedJsonErr ?? new QwenUpstreamError(
|
|
10060
10133
|
`Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
|
|
10061
10134
|
"NonStreamJsonResponse",
|
|
10062
10135
|
502
|
|
@@ -10107,6 +10180,31 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
10107
10180
|
response.status,
|
|
10108
10181
|
accountId
|
|
10109
10182
|
);
|
|
10183
|
+
if (parsedError instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
|
|
10184
|
+
retriedNonSseResponse = true;
|
|
10185
|
+
logger.warn(
|
|
10186
|
+
"[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
|
|
10187
|
+
{ accountId, chatId: chatSessionId ?? "new", status: response.status }
|
|
10188
|
+
);
|
|
10189
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
10190
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
10191
|
+
if (refreshed.success) {
|
|
10192
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
10193
|
+
activeHeaders = fresh.headers;
|
|
10194
|
+
response = await fetchCompletion(activeHeaders);
|
|
10195
|
+
if (response.ok && response.body) {
|
|
10196
|
+
return {
|
|
10197
|
+
stream: wrapUpstreamStream(response.body, controller),
|
|
10198
|
+
headers: activeHeaders,
|
|
10199
|
+
uiSessionId: chatSessionId || "",
|
|
10200
|
+
controller,
|
|
10201
|
+
accountId: accountId ?? "global",
|
|
10202
|
+
createdNewChat,
|
|
10203
|
+
tokenEstimationContext
|
|
10204
|
+
};
|
|
10205
|
+
}
|
|
10206
|
+
}
|
|
10207
|
+
}
|
|
10110
10208
|
if (parsedError) {
|
|
10111
10209
|
throw withCreatedChatMetadata(parsedError);
|
|
10112
10210
|
}
|
package/dist/index.js
CHANGED
|
@@ -263,6 +263,7 @@ var init_config = __esm({
|
|
|
263
263
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
264
264
|
// drop out of the warm set and get evicted.
|
|
265
265
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
266
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
266
267
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
267
268
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
268
269
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -411,6 +412,7 @@ var init_config = __esm({
|
|
|
411
412
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
412
413
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
413
414
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
415
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
414
416
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
415
417
|
},
|
|
416
418
|
captcha: {
|
|
@@ -8022,6 +8024,11 @@ async function ensurePlaywrightInitialized(accountId) {
|
|
|
8022
8024
|
});
|
|
8023
8025
|
} catch {
|
|
8024
8026
|
}
|
|
8027
|
+
try {
|
|
8028
|
+
const { ensureAccountInPriority: ensureAccountInPriority2 } = await Promise.resolve().then(() => (init_account_priority(), account_priority_exports));
|
|
8029
|
+
ensureAccountInPriority2(accountId);
|
|
8030
|
+
} catch {
|
|
8031
|
+
}
|
|
8025
8032
|
}
|
|
8026
8033
|
async function getBasicHeaders2(accountId) {
|
|
8027
8034
|
if (isAuthMockEnabled()) {
|
|
@@ -12729,9 +12736,13 @@ function parseQwenJsonError(raw, status, accountId) {
|
|
|
12729
12736
|
}
|
|
12730
12737
|
if (errorJson?.success === false) {
|
|
12731
12738
|
const code = errorJson.data?.code || errorJson.code || "UpstreamError";
|
|
12732
|
-
|
|
12739
|
+
const detailsStr = typeof details === "string" ? details : "";
|
|
12740
|
+
const codeStr = String(code).toLowerCase();
|
|
12741
|
+
const detailsLower = detailsStr.toLowerCase();
|
|
12742
|
+
const isAuthExpired = status === 401 || codeStr.includes("unauthorized") || detailsLower.includes("token has expired") || detailsLower.includes("log in") || detailsLower.includes("login") || detailsLower.includes("session") || detailsLower.includes("n\xE3o autorizado") || detailsLower.includes("unauthorized");
|
|
12743
|
+
if (isAuthExpired) {
|
|
12733
12744
|
return new QwenSessionExpiredError(
|
|
12734
|
-
`Session expired: ${
|
|
12745
|
+
`Session expired: ${detailsStr || codeStr}`,
|
|
12735
12746
|
accountId || "global"
|
|
12736
12747
|
);
|
|
12737
12748
|
}
|
|
@@ -13270,8 +13281,24 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
13270
13281
|
)
|
|
13271
13282
|
);
|
|
13272
13283
|
}
|
|
13284
|
+
const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
|
|
13285
|
+
if (parsedJsonErr instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
|
|
13286
|
+
retriedNonSseResponse = true;
|
|
13287
|
+
logger.warn(
|
|
13288
|
+
"[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
|
|
13289
|
+
{ accountId, chatId: chatSessionId ?? "new" }
|
|
13290
|
+
);
|
|
13291
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
13292
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
13293
|
+
if (refreshed.success) {
|
|
13294
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
13295
|
+
activeHeaders = fresh.headers;
|
|
13296
|
+
response = await fetchCompletion(activeHeaders);
|
|
13297
|
+
continue;
|
|
13298
|
+
}
|
|
13299
|
+
}
|
|
13273
13300
|
throw withCreatedChatMetadata(
|
|
13274
|
-
|
|
13301
|
+
parsedJsonErr ?? new QwenUpstreamError(
|
|
13275
13302
|
`Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
|
|
13276
13303
|
"NonStreamJsonResponse",
|
|
13277
13304
|
502
|
|
@@ -13322,6 +13349,31 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
13322
13349
|
response.status,
|
|
13323
13350
|
accountId
|
|
13324
13351
|
);
|
|
13352
|
+
if (parsedError instanceof QwenSessionExpiredError && accountId && !retriedNonSseResponse) {
|
|
13353
|
+
retriedNonSseResponse = true;
|
|
13354
|
+
logger.warn(
|
|
13355
|
+
"[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
|
|
13356
|
+
{ accountId, chatId: chatSessionId ?? "new", status: response.status }
|
|
13357
|
+
);
|
|
13358
|
+
const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
13359
|
+
const refreshed = await refreshAccountToken2(accountId);
|
|
13360
|
+
if (refreshed.success) {
|
|
13361
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
13362
|
+
activeHeaders = fresh.headers;
|
|
13363
|
+
response = await fetchCompletion(activeHeaders);
|
|
13364
|
+
if (response.ok && response.body) {
|
|
13365
|
+
return {
|
|
13366
|
+
stream: wrapUpstreamStream(response.body, controller),
|
|
13367
|
+
headers: activeHeaders,
|
|
13368
|
+
uiSessionId: chatSessionId || "",
|
|
13369
|
+
controller,
|
|
13370
|
+
accountId: accountId ?? "global",
|
|
13371
|
+
createdNewChat,
|
|
13372
|
+
tokenEstimationContext
|
|
13373
|
+
};
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13376
|
+
}
|
|
13325
13377
|
if (parsedError) {
|
|
13326
13378
|
throw withCreatedChatMetadata(parsedError);
|
|
13327
13379
|
}
|
|
@@ -17099,16 +17151,16 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17099
17151
|
error: new Error("Failed to create Qwen stream")
|
|
17100
17152
|
};
|
|
17101
17153
|
}
|
|
17102
|
-
|
|
17154
|
+
const isSessionExpired = err instanceof QwenSessionExpiredError || err?.name === "QwenSessionExpiredError" || err instanceof Error && (err.message.toLowerCase().includes("token has expired") || err.message.toLowerCase().includes("session expired") || err.message.toLowerCase().includes("401 n\xE3o autorizado") || err.message.toLowerCase().includes("unauthorized"));
|
|
17155
|
+
if (isSessionExpired) {
|
|
17103
17156
|
console.warn(
|
|
17104
|
-
`\u{1F504} [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting re-login...`
|
|
17157
|
+
`\u{1F504} [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting silent token refresh/re-login...`
|
|
17105
17158
|
);
|
|
17106
17159
|
const reLoginOk = await attemptRelogin(
|
|
17107
17160
|
currentAccountId,
|
|
17108
17161
|
currentAccountEmail
|
|
17109
17162
|
);
|
|
17110
17163
|
if (reLoginOk) continue;
|
|
17111
|
-
return { success: false, error: err };
|
|
17112
17164
|
}
|
|
17113
17165
|
if (isAccountUnavailableError(err)) {
|
|
17114
17166
|
const quotaMsg = err.message || "Unknown quota error";
|
|
@@ -25884,7 +25936,7 @@ function getAppVersion() {
|
|
|
25884
25936
|
}
|
|
25885
25937
|
} catch {
|
|
25886
25938
|
}
|
|
25887
|
-
cachedVersion = "v1.5.
|
|
25939
|
+
cachedVersion = "v1.5.3";
|
|
25888
25940
|
return cachedVersion;
|
|
25889
25941
|
}
|
|
25890
25942
|
var cachedVersion;
|
|
@@ -26288,17 +26340,16 @@ async function startServer(options) {
|
|
|
26288
26340
|
);
|
|
26289
26341
|
});
|
|
26290
26342
|
} else if (remainingAccounts.length > 0) {
|
|
26291
|
-
console.log(
|
|
26292
|
-
`\u{1FAB6} [Server] ${remainingAccounts.length} standby account(s) will initialize on demand`
|
|
26293
|
-
);
|
|
26294
26343
|
void (async () => {
|
|
26295
|
-
const { validateAccountLogin: validateAccountLogin2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
|
|
26296
26344
|
const { ensureAccountInPriority: ensureAccountInPriority2 } = await Promise.resolve().then(() => (init_account_priority(), account_priority_exports));
|
|
26297
|
-
|
|
26298
|
-
|
|
26299
|
-
|
|
26300
|
-
|
|
26301
|
-
|
|
26345
|
+
const targetWarmTotal = Math.min(
|
|
26346
|
+
config.playwright.startupWarmAccounts,
|
|
26347
|
+
totalAccounts
|
|
26348
|
+
);
|
|
26349
|
+
let currentReadyCount = readyAccountIds.size;
|
|
26350
|
+
if (currentReadyCount < targetWarmTotal && remainingAccounts.length > 0) {
|
|
26351
|
+
for (const reserveAccount of remainingAccounts) {
|
|
26352
|
+
if (currentReadyCount >= targetWarmTotal) break;
|
|
26302
26353
|
try {
|
|
26303
26354
|
const ok = await prepareAccountRuntime(
|
|
26304
26355
|
reserveAccount,
|
|
@@ -26308,12 +26359,12 @@ async function startServer(options) {
|
|
|
26308
26359
|
warmQwenChatPool2
|
|
26309
26360
|
);
|
|
26310
26361
|
if (ok) {
|
|
26362
|
+
readyAccountIds.add(reserveAccount.id);
|
|
26311
26363
|
ensureAccountInPriority2(reserveAccount.id);
|
|
26364
|
+
currentReadyCount++;
|
|
26312
26365
|
console.log(
|
|
26313
|
-
`\u2705 [Server] Reserve account ready (
|
|
26366
|
+
`\u2705 [Server] Reserve account ready (${currentReadyCount}/${totalAccounts}): ${maskEmail(reserveAccount.email)}`
|
|
26314
26367
|
);
|
|
26315
|
-
reserveCandidateIdx++;
|
|
26316
|
-
break;
|
|
26317
26368
|
}
|
|
26318
26369
|
} catch (err) {
|
|
26319
26370
|
console.warn(
|
|
@@ -26321,51 +26372,16 @@ async function startServer(options) {
|
|
|
26321
26372
|
);
|
|
26322
26373
|
}
|
|
26323
26374
|
}
|
|
26324
|
-
accountsToValidate = remainingAccounts.slice(reserveCandidateIdx);
|
|
26325
|
-
}
|
|
26326
|
-
let validated = 0;
|
|
26327
|
-
let failed = 0;
|
|
26328
|
-
for (const account of accountsToValidate) {
|
|
26329
|
-
try {
|
|
26330
|
-
const creds = getAccountCredentials2(account.id) ?? account;
|
|
26331
|
-
const ok = await validateAccountLogin2(
|
|
26332
|
-
creds,
|
|
26333
|
-
config.playwright.headless,
|
|
26334
|
-
config.playwright.browser
|
|
26335
|
-
);
|
|
26336
|
-
if (ok) {
|
|
26337
|
-
ensureAccountInPriority2(account.id);
|
|
26338
|
-
validated++;
|
|
26339
|
-
console.log(
|
|
26340
|
-
`\u2705 [Server] Standby account validated: ${maskEmail(account.email)}`
|
|
26341
|
-
);
|
|
26342
|
-
} else {
|
|
26343
|
-
failed++;
|
|
26344
|
-
console.warn(
|
|
26345
|
-
`\u26A0\uFE0F [Server] Standby account login failed: ${maskEmail(account.email)} (quarantined)`
|
|
26346
|
-
);
|
|
26347
|
-
}
|
|
26348
|
-
} catch (error) {
|
|
26349
|
-
failed++;
|
|
26350
|
-
console.warn(
|
|
26351
|
-
`\u26A0\uFE0F [Server] Standby account validation error: ${maskEmail(account.email)}: ${getErrorMessage2(error)} (quarantined)`
|
|
26352
|
-
);
|
|
26353
|
-
const { markAccountRateLimited: markAccountRateLimited2 } = await Promise.resolve().then(() => (init_account_manager(), account_manager_exports));
|
|
26354
|
-
markAccountRateLimited2(
|
|
26355
|
-
account.id,
|
|
26356
|
-
24 * 3600 * 1e3,
|
|
26357
|
-
`StandbyValidationError: ${getErrorMessage2(error)}`
|
|
26358
|
-
);
|
|
26359
|
-
}
|
|
26360
26375
|
}
|
|
26361
|
-
|
|
26376
|
+
const standbyCount = totalAccounts - currentReadyCount;
|
|
26377
|
+
if (standbyCount > 0) {
|
|
26362
26378
|
console.log(
|
|
26363
|
-
`\
|
|
26379
|
+
`\u{1FAB6} [Server] ${standbyCount} standby account(s) ready in pool (will initialize on demand when active accounts rotate)`
|
|
26364
26380
|
);
|
|
26365
26381
|
}
|
|
26366
26382
|
})().catch((error) => {
|
|
26367
26383
|
console.warn(
|
|
26368
|
-
`\u274C [Server] Background
|
|
26384
|
+
`\u274C [Server] Background account preparation failed: ${getErrorMessage2(error)}`
|
|
26369
26385
|
);
|
|
26370
26386
|
});
|
|
26371
26387
|
}
|
package/dist/login.js
CHANGED
|
@@ -1200,6 +1200,7 @@ var init_config = __esm({
|
|
|
1200
1200
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
1201
1201
|
// drop out of the warm set and get evicted.
|
|
1202
1202
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
1203
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
1203
1204
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
1204
1205
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
1205
1206
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -1348,6 +1349,7 @@ var init_config = __esm({
|
|
|
1348
1349
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
1349
1350
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
1350
1351
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
1352
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
1351
1353
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
1352
1354
|
},
|
|
1353
1355
|
captcha: {
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -1302,6 +1302,7 @@ var init_config = __esm({
|
|
|
1302
1302
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
1303
1303
|
// drop out of the warm set and get evicted.
|
|
1304
1304
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
1305
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
1305
1306
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
1306
1307
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
1307
1308
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -1450,6 +1451,7 @@ var init_config = __esm({
|
|
|
1450
1451
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
1451
1452
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
1452
1453
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
1454
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
1453
1455
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
1454
1456
|
},
|
|
1455
1457
|
captcha: {
|
package/dist/sync-clients.js
CHANGED
|
@@ -157,6 +157,7 @@ var envSchema = z.object({
|
|
|
157
157
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
158
158
|
// drop out of the warm set and get evicted.
|
|
159
159
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
160
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
160
161
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
161
162
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
162
163
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -305,6 +306,7 @@ var config = {
|
|
|
305
306
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
306
307
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
307
308
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
309
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
308
310
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false"
|
|
309
311
|
},
|
|
310
312
|
captcha: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
package/src/api/server.ts
CHANGED
|
@@ -877,23 +877,20 @@ export async function startServer(options?: {
|
|
|
877
877
|
);
|
|
878
878
|
});
|
|
879
879
|
} else if (remainingAccounts.length > 0) {
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
);
|
|
883
|
-
|
|
884
|
-
// In background: warm 1 reserve account (if maxActiveContexts > 1) and
|
|
885
|
-
// validate the rest of the standby accounts
|
|
880
|
+
// In background: warm reserve accounts up to a safe limit (default 4 total ready accounts)
|
|
881
|
+
// to prevent IP-level login flood / anti-bot challenge rate-limits on Alibaba.
|
|
886
882
|
void (async () => {
|
|
887
|
-
const { validateAccountLogin } = await import("../services/playwright.ts");
|
|
888
883
|
const { ensureAccountInPriority } = await import("../core/account-priority.ts");
|
|
889
884
|
|
|
890
|
-
|
|
885
|
+
const targetWarmTotal = Math.min(
|
|
886
|
+
config.playwright.startupWarmAccounts,
|
|
887
|
+
totalAccounts,
|
|
888
|
+
);
|
|
889
|
+
let currentReadyCount = readyAccountIds.size;
|
|
891
890
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
for (; reserveCandidateIdx < remainingAccounts.length; reserveCandidateIdx++) {
|
|
896
|
-
const reserveAccount = remainingAccounts[reserveCandidateIdx];
|
|
891
|
+
if (currentReadyCount < targetWarmTotal && remainingAccounts.length > 0) {
|
|
892
|
+
for (const reserveAccount of remainingAccounts) {
|
|
893
|
+
if (currentReadyCount >= targetWarmTotal) break;
|
|
897
894
|
try {
|
|
898
895
|
const ok = await prepareAccountRuntime(
|
|
899
896
|
reserveAccount,
|
|
@@ -903,12 +900,12 @@ export async function startServer(options?: {
|
|
|
903
900
|
warmQwenChatPool,
|
|
904
901
|
);
|
|
905
902
|
if (ok) {
|
|
903
|
+
readyAccountIds.add(reserveAccount.id);
|
|
906
904
|
ensureAccountInPriority(reserveAccount.id);
|
|
905
|
+
currentReadyCount++;
|
|
907
906
|
console.log(
|
|
908
|
-
`✅ [Server] Reserve account ready (
|
|
907
|
+
`✅ [Server] Reserve account ready (${currentReadyCount}/${totalAccounts}): ${maskEmail(reserveAccount.email)}`,
|
|
909
908
|
);
|
|
910
|
-
reserveCandidateIdx++;
|
|
911
|
-
break;
|
|
912
909
|
}
|
|
913
910
|
} catch (err) {
|
|
914
911
|
console.warn(
|
|
@@ -916,56 +913,17 @@ export async function startServer(options?: {
|
|
|
916
913
|
);
|
|
917
914
|
}
|
|
918
915
|
}
|
|
919
|
-
accountsToValidate = remainingAccounts.slice(reserveCandidateIdx);
|
|
920
916
|
}
|
|
921
917
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
for (const account of accountsToValidate) {
|
|
926
|
-
try {
|
|
927
|
-
const creds = getAccountCredentials(account.id) ?? account;
|
|
928
|
-
// Validate login in background with real unmasked credentials
|
|
929
|
-
const ok = await validateAccountLogin(
|
|
930
|
-
creds,
|
|
931
|
-
config.playwright.headless,
|
|
932
|
-
config.playwright.browser,
|
|
933
|
-
);
|
|
934
|
-
if (ok) {
|
|
935
|
-
// Add to priority list only once validated
|
|
936
|
-
ensureAccountInPriority(account.id);
|
|
937
|
-
validated++;
|
|
938
|
-
console.log(
|
|
939
|
-
`✅ [Server] Standby account validated: ${maskEmail(account.email)}`,
|
|
940
|
-
);
|
|
941
|
-
} else {
|
|
942
|
-
failed++;
|
|
943
|
-
console.warn(
|
|
944
|
-
`⚠️ [Server] Standby account login failed: ${maskEmail(account.email)} (quarantined)`,
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
} catch (error) {
|
|
948
|
-
failed++;
|
|
949
|
-
console.warn(
|
|
950
|
-
`⚠️ [Server] Standby account validation error: ${maskEmail(account.email)}: ${getErrorMessage(error)} (quarantined)`,
|
|
951
|
-
);
|
|
952
|
-
const { markAccountRateLimited } = await import("../core/account-manager.ts");
|
|
953
|
-
markAccountRateLimited(
|
|
954
|
-
account.id,
|
|
955
|
-
24 * 3600 * 1000,
|
|
956
|
-
`StandbyValidationError: ${getErrorMessage(error)}`,
|
|
957
|
-
);
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
if (validated > 0 || failed > 0) {
|
|
918
|
+
const standbyCount = totalAccounts - currentReadyCount;
|
|
919
|
+
if (standbyCount > 0) {
|
|
962
920
|
console.log(
|
|
963
|
-
|
|
921
|
+
`🪶 [Server] ${standbyCount} standby account(s) ready in pool (will initialize on demand when active accounts rotate)`,
|
|
964
922
|
);
|
|
965
923
|
}
|
|
966
924
|
})().catch((error) => {
|
|
967
925
|
console.warn(
|
|
968
|
-
`❌ [Server] Background
|
|
926
|
+
`❌ [Server] Background account preparation failed: ${getErrorMessage(error)}`,
|
|
969
927
|
);
|
|
970
928
|
});
|
|
971
929
|
}
|
package/src/core/config.ts
CHANGED
|
@@ -76,6 +76,7 @@ const envSchema = z
|
|
|
76
76
|
// own context while serving. Accounts in cooldown (rate-limited) sit idle,
|
|
77
77
|
// drop out of the warm set and get evicted.
|
|
78
78
|
PLAYWRIGHT_MAX_ACTIVE_CONTEXTS: z.string().default("2"),
|
|
79
|
+
PLAYWRIGHT_STARTUP_WARM_ACCOUNTS: z.string().default("4"),
|
|
79
80
|
PLAYWRIGHT_PREPARE_ALL_ON_STARTUP: z.string().default("false"),
|
|
80
81
|
CAPTCHA_SOLVER_ENABLED: z.string().default("true"),
|
|
81
82
|
CAPTCHA_SOLVER_MAX_ATTEMPTS: z.string().default("3"),
|
|
@@ -235,6 +236,7 @@ export const config = {
|
|
|
235
236
|
jsHeapMb: Math.max(64, parseInt(env.PLAYWRIGHT_JS_HEAP_MB)),
|
|
236
237
|
lowMemoryFlags: env.PLAYWRIGHT_LOW_MEMORY_FLAGS !== "false",
|
|
237
238
|
maxActiveContexts: Math.max(0, parseInt(env.PLAYWRIGHT_MAX_ACTIVE_CONTEXTS)),
|
|
239
|
+
startupWarmAccounts: Math.max(1, parseInt(env.PLAYWRIGHT_STARTUP_WARM_ACCOUNTS)),
|
|
238
240
|
prepareAllOnStartup: env.PLAYWRIGHT_PREPARE_ALL_ON_STARTUP !== "false",
|
|
239
241
|
},
|
|
240
242
|
captcha: {
|
package/src/core/version.ts
CHANGED
|
@@ -1487,19 +1487,24 @@ async function tryCreateStreamWithRetry(
|
|
|
1487
1487
|
};
|
|
1488
1488
|
}
|
|
1489
1489
|
|
|
1490
|
-
|
|
1490
|
+
const isSessionExpired =
|
|
1491
1491
|
err instanceof QwenSessionExpiredError ||
|
|
1492
|
-
err
|
|
1493
|
-
|
|
1492
|
+
err?.name === "QwenSessionExpiredError" ||
|
|
1493
|
+
(err instanceof Error &&
|
|
1494
|
+
(err.message.toLowerCase().includes("token has expired") ||
|
|
1495
|
+
err.message.toLowerCase().includes("session expired") ||
|
|
1496
|
+
err.message.toLowerCase().includes("401 não autorizado") ||
|
|
1497
|
+
err.message.toLowerCase().includes("unauthorized")));
|
|
1498
|
+
|
|
1499
|
+
if (isSessionExpired) {
|
|
1494
1500
|
console.warn(
|
|
1495
|
-
`🔄 [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting re-login...`,
|
|
1501
|
+
`🔄 [Chat] Session expired for ${currentAccountEmail} (${currentAccountId}). Attempting silent token refresh/re-login...`,
|
|
1496
1502
|
);
|
|
1497
1503
|
const reLoginOk = await attemptRelogin(
|
|
1498
1504
|
currentAccountId,
|
|
1499
1505
|
currentAccountEmail,
|
|
1500
1506
|
);
|
|
1501
1507
|
if (reLoginOk) continue;
|
|
1502
|
-
return { success: false, error: err };
|
|
1503
1508
|
}
|
|
1504
1509
|
|
|
1505
1510
|
|
|
@@ -56,6 +56,11 @@ async function ensurePlaywrightInitialized(accountId: string): Promise<void> {
|
|
|
56
56
|
} catch {
|
|
57
57
|
// Non-fatal: chat creation will still work with default account settings.
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const { ensureAccountInPriority } = await import("../core/account-priority.ts");
|
|
62
|
+
ensureAccountInPriority(accountId);
|
|
63
|
+
} catch {}
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
export async function getBasicHeaders(accountId?: string): Promise<{
|
package/src/services/qwen.ts
CHANGED
|
@@ -2502,15 +2502,23 @@ function parseQwenJsonError(
|
|
|
2502
2502
|
|
|
2503
2503
|
if (errorJson?.success === false) {
|
|
2504
2504
|
const code = errorJson.data?.code || errorJson.code || "UpstreamError";
|
|
2505
|
+
const detailsStr = typeof details === "string" ? details : "";
|
|
2506
|
+
const codeStr = String(code).toLowerCase();
|
|
2507
|
+
const detailsLower = detailsStr.toLowerCase();
|
|
2505
2508
|
|
|
2506
|
-
|
|
2509
|
+
const isAuthExpired =
|
|
2507
2510
|
status === 401 ||
|
|
2508
|
-
|
|
2509
|
-
(
|
|
2510
|
-
|
|
2511
|
-
|
|
2511
|
+
codeStr.includes("unauthorized") ||
|
|
2512
|
+
detailsLower.includes("token has expired") ||
|
|
2513
|
+
detailsLower.includes("log in") ||
|
|
2514
|
+
detailsLower.includes("login") ||
|
|
2515
|
+
detailsLower.includes("session") ||
|
|
2516
|
+
detailsLower.includes("não autorizado") ||
|
|
2517
|
+
detailsLower.includes("unauthorized");
|
|
2518
|
+
|
|
2519
|
+
if (isAuthExpired) {
|
|
2512
2520
|
return new QwenSessionExpiredError(
|
|
2513
|
-
`Session expired: ${
|
|
2521
|
+
`Session expired: ${detailsStr || codeStr}`,
|
|
2514
2522
|
accountId || "global",
|
|
2515
2523
|
);
|
|
2516
2524
|
}
|
|
@@ -3301,8 +3309,29 @@ async function createQwenStreamInternal(
|
|
|
3301
3309
|
);
|
|
3302
3310
|
}
|
|
3303
3311
|
|
|
3312
|
+
const parsedJsonErr = parseQwenJsonError(errText, response.status, accountId);
|
|
3313
|
+
if (
|
|
3314
|
+
parsedJsonErr instanceof QwenSessionExpiredError &&
|
|
3315
|
+
accountId &&
|
|
3316
|
+
!retriedNonSseResponse
|
|
3317
|
+
) {
|
|
3318
|
+
retriedNonSseResponse = true;
|
|
3319
|
+
logger.warn(
|
|
3320
|
+
"[Qwen] Token expired on completion fetch (200 JSON); refreshing session and retrying",
|
|
3321
|
+
{ accountId, chatId: chatSessionId ?? "new" },
|
|
3322
|
+
);
|
|
3323
|
+
const { refreshAccountToken } = await import("./playwright.ts");
|
|
3324
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
3325
|
+
if (refreshed.success) {
|
|
3326
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
3327
|
+
activeHeaders = fresh.headers;
|
|
3328
|
+
response = await fetchCompletion(activeHeaders);
|
|
3329
|
+
continue;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3304
3333
|
throw withCreatedChatMetadata(
|
|
3305
|
-
|
|
3334
|
+
parsedJsonErr ??
|
|
3306
3335
|
new QwenUpstreamError(
|
|
3307
3336
|
`Qwen returned non-stream JSON response: ${errText.substring(0, 300)}`,
|
|
3308
3337
|
"NonStreamJsonResponse",
|
|
@@ -3378,6 +3407,35 @@ async function createQwenStreamInternal(
|
|
|
3378
3407
|
response.status,
|
|
3379
3408
|
accountId,
|
|
3380
3409
|
);
|
|
3410
|
+
if (
|
|
3411
|
+
parsedError instanceof QwenSessionExpiredError &&
|
|
3412
|
+
accountId &&
|
|
3413
|
+
!retriedNonSseResponse
|
|
3414
|
+
) {
|
|
3415
|
+
retriedNonSseResponse = true;
|
|
3416
|
+
logger.warn(
|
|
3417
|
+
"[Qwen] Token expired on completion fetch (!response.ok); refreshing session and retrying",
|
|
3418
|
+
{ accountId, chatId: chatSessionId ?? "new", status: response.status },
|
|
3419
|
+
);
|
|
3420
|
+
const { refreshAccountToken } = await import("./playwright.ts");
|
|
3421
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
3422
|
+
if (refreshed.success) {
|
|
3423
|
+
const fresh = await getQwenHeaders(false, accountId);
|
|
3424
|
+
activeHeaders = fresh.headers;
|
|
3425
|
+
response = await fetchCompletion(activeHeaders);
|
|
3426
|
+
if (response.ok && response.body) {
|
|
3427
|
+
return {
|
|
3428
|
+
stream: wrapUpstreamStream(response.body, controller),
|
|
3429
|
+
headers: activeHeaders,
|
|
3430
|
+
uiSessionId: chatSessionId || "",
|
|
3431
|
+
controller,
|
|
3432
|
+
accountId: accountId ?? "global",
|
|
3433
|
+
createdNewChat,
|
|
3434
|
+
tokenEstimationContext,
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3381
3439
|
if (parsedError) {
|
|
3382
3440
|
throw withCreatedChatMetadata(parsedError);
|
|
3383
3441
|
}
|