qwenproxy-cli 1.6.3 → 1.6.5
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 +76 -12
- package/dist/delete-chats.js +182 -49
- package/dist/index.js +245 -64
- package/dist/login.js +90 -19
- package/dist/reset-cooldowns.js +79 -13
- package/package.json +1 -1
- package/src/core/mutex.ts +4 -0
- package/src/core/version.ts +1 -1
- package/src/routes/anthropic/translate.ts +15 -7
- package/src/routes/chat/helpers.ts +4 -2
- package/src/routes/chat/retry-policy.ts +3 -1
- package/src/routes/chat/streaming.ts +32 -10
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +55 -21
- package/src/services/qwen-headers.ts +3 -2
- package/src/services/qwen.ts +88 -31
- package/src/tools/tool-call-heuristics.ts +88 -0
- package/src/utils/timezone.ts +56 -0
package/dist/index.js
CHANGED
|
@@ -2816,6 +2816,7 @@ var init_mutex = __esm({
|
|
|
2816
2816
|
locked = false;
|
|
2817
2817
|
lockedAt = 0;
|
|
2818
2818
|
lockedByKey = "";
|
|
2819
|
+
epoch = 0;
|
|
2819
2820
|
async acquire(timeoutMs = 3e5, key = "") {
|
|
2820
2821
|
const holdLimitMs = this.maxHoldMs;
|
|
2821
2822
|
if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
|
|
@@ -2826,6 +2827,7 @@ var init_mutex = __esm({
|
|
|
2826
2827
|
this.locked = false;
|
|
2827
2828
|
this.lockedAt = 0;
|
|
2828
2829
|
this.lockedByKey = "";
|
|
2830
|
+
this.epoch++;
|
|
2829
2831
|
}
|
|
2830
2832
|
if (!this.locked) {
|
|
2831
2833
|
this.locked = true;
|
|
@@ -2864,10 +2866,12 @@ var init_mutex = __esm({
|
|
|
2864
2866
|
}
|
|
2865
2867
|
}
|
|
2866
2868
|
createRelease() {
|
|
2869
|
+
const epoch = this.epoch;
|
|
2867
2870
|
let released = false;
|
|
2868
2871
|
return () => {
|
|
2869
2872
|
if (released) return;
|
|
2870
2873
|
released = true;
|
|
2874
|
+
if (epoch !== this.epoch) return;
|
|
2871
2875
|
this.release();
|
|
2872
2876
|
};
|
|
2873
2877
|
}
|
|
@@ -4016,6 +4020,49 @@ var init_waf_isolation = __esm({
|
|
|
4016
4020
|
}
|
|
4017
4021
|
});
|
|
4018
4022
|
|
|
4023
|
+
// src/utils/timezone.ts
|
|
4024
|
+
function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
|
|
4025
|
+
const dayName = DAYS[date.getDay()];
|
|
4026
|
+
const monthName = MONTHS[date.getMonth()];
|
|
4027
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
4028
|
+
const year = date.getFullYear();
|
|
4029
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
4030
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
4031
|
+
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
4032
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
4033
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
4034
|
+
const absOffset = Math.abs(offsetMinutes);
|
|
4035
|
+
const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
|
|
4036
|
+
const offsetMins = String(absOffset % 60).padStart(2, "0");
|
|
4037
|
+
const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
|
|
4038
|
+
return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
|
|
4039
|
+
}
|
|
4040
|
+
function sanitizeHeaderForFetch(val) {
|
|
4041
|
+
if (typeof val !== "string") return "";
|
|
4042
|
+
return val.replace(/[^\x00-\xFF]/g, "");
|
|
4043
|
+
}
|
|
4044
|
+
var DAYS, MONTHS;
|
|
4045
|
+
var init_timezone = __esm({
|
|
4046
|
+
"src/utils/timezone.ts"() {
|
|
4047
|
+
"use strict";
|
|
4048
|
+
DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
4049
|
+
MONTHS = [
|
|
4050
|
+
"Jan",
|
|
4051
|
+
"Feb",
|
|
4052
|
+
"Mar",
|
|
4053
|
+
"Apr",
|
|
4054
|
+
"May",
|
|
4055
|
+
"Jun",
|
|
4056
|
+
"Jul",
|
|
4057
|
+
"Aug",
|
|
4058
|
+
"Sep",
|
|
4059
|
+
"Oct",
|
|
4060
|
+
"Nov",
|
|
4061
|
+
"Dec"
|
|
4062
|
+
];
|
|
4063
|
+
}
|
|
4064
|
+
});
|
|
4065
|
+
|
|
4019
4066
|
// src/services/qwen-headers.ts
|
|
4020
4067
|
import { v4 as uuidv4 } from "uuid";
|
|
4021
4068
|
function getSystemAcceptLanguage(customLocale) {
|
|
@@ -4059,8 +4106,8 @@ function buildQwenRequestHeaders(opts) {
|
|
|
4059
4106
|
"bx-v": opts.bxV || "2.5.37",
|
|
4060
4107
|
source: "web",
|
|
4061
4108
|
version: opts.version || getQwenWebVersion(),
|
|
4062
|
-
timezone: opts.extra?.timezone || opts.extra?.Timezone || (
|
|
4063
|
-
Timezone: opts.extra?.Timezone || opts.extra?.timezone || (
|
|
4109
|
+
timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
|
|
4110
|
+
Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
|
|
4064
4111
|
// Use the real browser client-hints when captured (anti-hardcoded); fall
|
|
4065
4112
|
// back to the static fingerprint otherwise.
|
|
4066
4113
|
"sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
|
|
@@ -4087,6 +4134,7 @@ var init_qwen_headers = __esm({
|
|
|
4087
4134
|
init_qwen_url();
|
|
4088
4135
|
init_config();
|
|
4089
4136
|
init_fingerprint();
|
|
4137
|
+
init_timezone();
|
|
4090
4138
|
dynamicWebVersion = null;
|
|
4091
4139
|
QWEN_WEB_VERSION = config.qwen.webVersion;
|
|
4092
4140
|
DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
|
|
@@ -4323,6 +4371,7 @@ __export(playwright_exports, {
|
|
|
4323
4371
|
closeIdlePlaywrightAccounts: () => closeIdlePlaywrightAccounts,
|
|
4324
4372
|
closePlaywrightForAccount: () => closePlaywrightForAccount,
|
|
4325
4373
|
evictIdlePlaywrightContextsToLimit: () => evictIdlePlaywrightContextsToLimit,
|
|
4374
|
+
getAccountMutex: () => getAccountMutex,
|
|
4326
4375
|
getActivePlaywrightAccountIds: () => getActivePlaywrightAccountIds,
|
|
4327
4376
|
getBasicHeaders: () => getBasicHeaders,
|
|
4328
4377
|
getCookies: () => getCookies,
|
|
@@ -4472,9 +4521,10 @@ function getAccountMutex(accountId) {
|
|
|
4472
4521
|
async function recoverStuckAccountMutex(accountId, mutex, key) {
|
|
4473
4522
|
if (accountMutexes.get(accountId) !== mutex) return;
|
|
4474
4523
|
const lockState = mutex.state();
|
|
4475
|
-
|
|
4524
|
+
const minLockTimeout = Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers);
|
|
4525
|
+
if (lockState.heldForMs < minLockTimeout || lockState.heldBy.startsWith("init:") || lockState.heldBy.startsWith("headers:") || lockState.heldBy.startsWith("refresh:") || lockState.heldBy.startsWith("profile-reset:") || isAccountServingStream(accountId)) {
|
|
4476
4526
|
logger.warn(
|
|
4477
|
-
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${
|
|
4527
|
+
`[Playwright] Skipping destructive mutex recovery | account=${accountId} | heldBy=${lockState.heldBy} | heldFor=${lockState.heldForMs}ms | limit=${minLockTimeout}ms | servingStream=${isAccountServingStream(accountId)}`
|
|
4478
4528
|
);
|
|
4479
4529
|
return;
|
|
4480
4530
|
}
|
|
@@ -5276,7 +5326,8 @@ async function getBasicHeaders(accountId) {
|
|
|
5276
5326
|
}
|
|
5277
5327
|
const release = await acquireAccountMutex(
|
|
5278
5328
|
accountId,
|
|
5279
|
-
`headers:${accountId.substring(0, 12)}
|
|
5329
|
+
`headers:${accountId.substring(0, 12)}`,
|
|
5330
|
+
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, config.timeouts.headers + 3e4)
|
|
5280
5331
|
);
|
|
5281
5332
|
try {
|
|
5282
5333
|
touchAccountActivity(accountId);
|
|
@@ -5369,8 +5420,10 @@ async function getBasicHeaders(accountId) {
|
|
|
5369
5420
|
return { cookie: cookie2, userAgent, bxV: bxV2, bxUa: bxUa2, bxUmidtoken: bxUmidtoken2, ...clientHints };
|
|
5370
5421
|
}
|
|
5371
5422
|
}
|
|
5423
|
+
let refreshedInThisCall = false;
|
|
5372
5424
|
if (headersAge > HEADER_CACHE_TTL && !cache3.refreshInProgress) {
|
|
5373
5425
|
try {
|
|
5426
|
+
refreshedInThisCall = true;
|
|
5374
5427
|
await refreshHeadersInternal(accountId);
|
|
5375
5428
|
} catch (error) {
|
|
5376
5429
|
if (!hadUsableHeaders) throw error;
|
|
@@ -5439,7 +5492,7 @@ async function getBasicHeaders(accountId) {
|
|
|
5439
5492
|
} catch {
|
|
5440
5493
|
}
|
|
5441
5494
|
}
|
|
5442
|
-
if (!hasRequiredQwenHeaders(cache3.headers)) {
|
|
5495
|
+
if (!hasRequiredQwenHeaders(cache3.headers) && !refreshedInThisCall) {
|
|
5443
5496
|
console.log(
|
|
5444
5497
|
`\u{1F504} [Playwright] Missing required anti-bot headers for ${accountId}, triggering header interception...`
|
|
5445
5498
|
);
|
|
@@ -5718,6 +5771,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5718
5771
|
}
|
|
5719
5772
|
}
|
|
5720
5773
|
}
|
|
5774
|
+
const currentCache = getHeaderCache(account.id);
|
|
5775
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
5776
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
5777
|
+
try {
|
|
5778
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
5779
|
+
if (refreshRes.success) {
|
|
5780
|
+
console.log(
|
|
5781
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
5782
|
+
);
|
|
5783
|
+
}
|
|
5784
|
+
} catch (err) {
|
|
5785
|
+
console.warn(
|
|
5786
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
5787
|
+
);
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5721
5790
|
return;
|
|
5722
5791
|
}
|
|
5723
5792
|
const cookies = await acctContext.cookies();
|
|
@@ -6005,7 +6074,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
6005
6074
|
email: normalizedEmail,
|
|
6006
6075
|
requestId,
|
|
6007
6076
|
version: getQwenWebVersion() || "0.3.12",
|
|
6008
|
-
timezone: (
|
|
6077
|
+
timezone: formatAsciiTimezone()
|
|
6009
6078
|
}
|
|
6010
6079
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6011
6080
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -6100,7 +6169,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
6100
6169
|
code: cleanCode,
|
|
6101
6170
|
requestId,
|
|
6102
6171
|
version: getQwenWebVersion() || "0.3.12",
|
|
6103
|
-
timezone: (
|
|
6172
|
+
timezone: formatAsciiTimezone()
|
|
6104
6173
|
}
|
|
6105
6174
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6106
6175
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -6381,7 +6450,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6381
6450
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
6382
6451
|
const requestId = crypto5.randomUUID();
|
|
6383
6452
|
const evalRes = await page.evaluate(
|
|
6384
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
6453
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
6385
6454
|
try {
|
|
6386
6455
|
const response = await fetch(signinUrl2, {
|
|
6387
6456
|
method: "POST",
|
|
@@ -6393,7 +6462,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6393
6462
|
source: "web",
|
|
6394
6463
|
version: "0.3.11",
|
|
6395
6464
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6396
|
-
timezone:
|
|
6465
|
+
timezone: tz,
|
|
6397
6466
|
"x-request-id": requestId2
|
|
6398
6467
|
},
|
|
6399
6468
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6404,7 +6473,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6404
6473
|
return { ok: false, error: e.message };
|
|
6405
6474
|
}
|
|
6406
6475
|
},
|
|
6407
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
6476
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
6408
6477
|
).catch(() => null);
|
|
6409
6478
|
let data = evalRes?.data;
|
|
6410
6479
|
let signinSuccess = Boolean(
|
|
@@ -6413,7 +6482,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6413
6482
|
if (!signinSuccess && !data?.data?.code) {
|
|
6414
6483
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
6415
6484
|
const fallbackRes = await page.evaluate(
|
|
6416
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
6485
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
6417
6486
|
try {
|
|
6418
6487
|
const response = await fetch(fallbackUrl2, {
|
|
6419
6488
|
method: "POST",
|
|
@@ -6424,7 +6493,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6424
6493
|
"content-type": "application/json",
|
|
6425
6494
|
source: "web",
|
|
6426
6495
|
version: "0.3.11",
|
|
6427
|
-
timezone:
|
|
6496
|
+
timezone: tz,
|
|
6428
6497
|
"x-request-id": requestId2
|
|
6429
6498
|
},
|
|
6430
6499
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6435,7 +6504,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6435
6504
|
return { ok: false, error: e.message };
|
|
6436
6505
|
}
|
|
6437
6506
|
},
|
|
6438
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
6507
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
6439
6508
|
).catch(() => null);
|
|
6440
6509
|
if (fallbackRes?.data) {
|
|
6441
6510
|
data = fallbackRes.data;
|
|
@@ -6767,7 +6836,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6767
6836
|
if (refreshToken) {
|
|
6768
6837
|
try {
|
|
6769
6838
|
const refreshResult = await page.evaluate(
|
|
6770
|
-
async ({ rTok }) => {
|
|
6839
|
+
async ({ rTok, tz }) => {
|
|
6771
6840
|
try {
|
|
6772
6841
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6773
6842
|
method: "GET",
|
|
@@ -6777,7 +6846,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6777
6846
|
source: "web",
|
|
6778
6847
|
version: "0.3.11",
|
|
6779
6848
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6780
|
-
timezone:
|
|
6849
|
+
timezone: tz,
|
|
6781
6850
|
authorization: `Bearer ${rTok}`
|
|
6782
6851
|
}
|
|
6783
6852
|
});
|
|
@@ -6787,7 +6856,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6787
6856
|
return { ok: false, error: e.message };
|
|
6788
6857
|
}
|
|
6789
6858
|
},
|
|
6790
|
-
{ rTok: refreshToken }
|
|
6859
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
6791
6860
|
);
|
|
6792
6861
|
const json = refreshResult?.data;
|
|
6793
6862
|
if (json && json.success === true && json.data) {
|
|
@@ -6825,6 +6894,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6825
6894
|
const cache3 = getHeaderCache(accountId);
|
|
6826
6895
|
cache3.headers.cookie = cookieStr;
|
|
6827
6896
|
cache3.lastRefresh = Date.now();
|
|
6897
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6828
6898
|
markAccountHeadersReady(accountId);
|
|
6829
6899
|
saveAuthSession2(accountId, {
|
|
6830
6900
|
cookie: cookieStr,
|
|
@@ -6859,6 +6929,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6859
6929
|
const cache3 = getHeaderCache(accountId);
|
|
6860
6930
|
cache3.headers.cookie = cookieStr;
|
|
6861
6931
|
cache3.lastRefresh = Date.now();
|
|
6932
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6862
6933
|
markAccountHeadersReady(accountId);
|
|
6863
6934
|
saveAuthSession2(accountId, {
|
|
6864
6935
|
cookie: cookieStr,
|
|
@@ -7913,11 +7984,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7913
7984
|
const now = Date.now();
|
|
7914
7985
|
const currentUrl = page.url();
|
|
7915
7986
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
7916
|
-
const cookie = await getCookies(accountId);
|
|
7987
|
+
const cookie = await getCookies(accountId, true);
|
|
7917
7988
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
7918
7989
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
7919
|
-
|
|
7920
|
-
|
|
7990
|
+
logger.debug(
|
|
7991
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
7921
7992
|
);
|
|
7922
7993
|
const res = await refreshAccountToken(accountId);
|
|
7923
7994
|
if (res.success) {
|
|
@@ -7925,6 +7996,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7925
7996
|
touchAccountActivity(accountId);
|
|
7926
7997
|
return true;
|
|
7927
7998
|
}
|
|
7999
|
+
logger.warn(
|
|
8000
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
8001
|
+
);
|
|
7928
8002
|
}
|
|
7929
8003
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
7930
8004
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -8162,6 +8236,7 @@ var init_playwright = __esm({
|
|
|
8162
8236
|
init_qwen_headers();
|
|
8163
8237
|
init_paths();
|
|
8164
8238
|
init_jwt();
|
|
8239
|
+
init_timezone();
|
|
8165
8240
|
requireLocal = createRequire(import.meta.url);
|
|
8166
8241
|
contextInitHooks = [];
|
|
8167
8242
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -8175,7 +8250,7 @@ var init_playwright = __esm({
|
|
|
8175
8250
|
headerCaches = /* @__PURE__ */ new Map();
|
|
8176
8251
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
8177
8252
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
8178
|
-
COOKIE_CACHE_TTL =
|
|
8253
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
8179
8254
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
8180
8255
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
8181
8256
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -9594,8 +9669,8 @@ function createUsageAccumulator(estimatedPromptTokens) {
|
|
|
9594
9669
|
function applyUpstreamUsage(accumulator, candidate) {
|
|
9595
9670
|
if (!candidate || typeof candidate !== "object") return;
|
|
9596
9671
|
const usage = candidate;
|
|
9597
|
-
const promptTokens = asFiniteNumber(usage.input_tokens);
|
|
9598
|
-
const completionTokens = asFiniteNumber(usage.output_tokens);
|
|
9672
|
+
const promptTokens = asFiniteNumber(usage.input_tokens) ?? asFiniteNumber(usage.prompt_tokens);
|
|
9673
|
+
const completionTokens = asFiniteNumber(usage.output_tokens) ?? asFiniteNumber(usage.completion_tokens);
|
|
9599
9674
|
const totalTokens = asFiniteNumber(usage.total_tokens);
|
|
9600
9675
|
if (promptTokens !== null) {
|
|
9601
9676
|
accumulator.promptTokens = promptTokens;
|
|
@@ -9825,7 +9900,7 @@ function isModelNotFoundError(err) {
|
|
|
9825
9900
|
function isNetworkLikeError(err) {
|
|
9826
9901
|
if (err instanceof QwenNetworkError) return true;
|
|
9827
9902
|
const message = errMessage(err).toLowerCase();
|
|
9828
|
-
return message === "network error" || message.includes("failed to fetch") || message.includes("fetch failed") || message.includes("network connection was lost") || message.includes("connection reset") || message.includes("connection closed") || message.includes("socket hang up") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("etimedout");
|
|
9903
|
+
return message === "network error" || message.includes("failed to fetch") || message.includes("fetch failed") || message.includes("network connection was lost") || message.includes("connection reset") || message.includes("connection closed") || message.includes("socket hang up") || message.includes("econnreset") || message.includes("econnrefused") || message.includes("etimedout") || message.includes("stream ended before response headers") || message.includes("timed out waiting for response headers");
|
|
9829
9904
|
}
|
|
9830
9905
|
function isCorruptedChatHistoryError(err) {
|
|
9831
9906
|
const message = errMessage(err).toLowerCase();
|
|
@@ -11627,6 +11702,8 @@ __export(qwen_exports, {
|
|
|
11627
11702
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
11628
11703
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
11629
11704
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
11705
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
11706
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
11630
11707
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
11631
11708
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
11632
11709
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -11640,10 +11717,13 @@ __export(qwen_exports, {
|
|
|
11640
11717
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
11641
11718
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
11642
11719
|
disableNativeTools: () => disableNativeTools,
|
|
11720
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
11643
11721
|
fetchQwenModels: () => fetchQwenModels,
|
|
11644
11722
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
11645
11723
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
11724
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
11646
11725
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
11726
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
11647
11727
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
11648
11728
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
11649
11729
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -11659,6 +11739,8 @@ __export(qwen_exports, {
|
|
|
11659
11739
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
11660
11740
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
11661
11741
|
setToolCapNotice: () => setToolCapNotice,
|
|
11742
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
11743
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
11662
11744
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
11663
11745
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
11664
11746
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -11945,7 +12027,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
11945
12027
|
function getAccountStreamMutex(accountId) {
|
|
11946
12028
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
11947
12029
|
if (!slots2) {
|
|
11948
|
-
slots2 = {
|
|
12030
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
11949
12031
|
accountStreamMutexes.set(accountId, slots2);
|
|
11950
12032
|
}
|
|
11951
12033
|
return slots2;
|
|
@@ -11953,27 +12035,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
11953
12035
|
function streamSlotCapacity() {
|
|
11954
12036
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
11955
12037
|
}
|
|
11956
|
-
function createStreamSlotRelease(slots2) {
|
|
12038
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
11957
12039
|
let released = false;
|
|
11958
12040
|
return () => {
|
|
11959
12041
|
if (released) return;
|
|
11960
12042
|
released = true;
|
|
11961
|
-
slots2.
|
|
12043
|
+
slots2.activeSlots.delete(slotId);
|
|
11962
12044
|
const next = slots2.queue.shift();
|
|
11963
12045
|
if (next) {
|
|
11964
|
-
|
|
11965
|
-
|
|
12046
|
+
const nextId = nextStreamSlotId++;
|
|
12047
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
12048
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
11966
12049
|
}
|
|
11967
12050
|
};
|
|
11968
12051
|
}
|
|
12052
|
+
function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
|
|
12053
|
+
const now = Date.now();
|
|
12054
|
+
let sweptCount = 0;
|
|
12055
|
+
for (const [accountId, slots2] of accountStreamMutexes.entries()) {
|
|
12056
|
+
for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
|
|
12057
|
+
if (now - startedAt >= maxHoldMs) {
|
|
12058
|
+
logger.warn(
|
|
12059
|
+
`[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
|
|
12060
|
+
);
|
|
12061
|
+
slots2.activeSlots.delete(slotId);
|
|
12062
|
+
sweptCount++;
|
|
12063
|
+
const next = slots2.queue.shift();
|
|
12064
|
+
if (next) {
|
|
12065
|
+
const nextId = nextStreamSlotId++;
|
|
12066
|
+
slots2.activeSlots.set(nextId, now);
|
|
12067
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
12068
|
+
}
|
|
12069
|
+
}
|
|
12070
|
+
}
|
|
12071
|
+
}
|
|
12072
|
+
return sweptCount;
|
|
12073
|
+
}
|
|
12074
|
+
function ensureStaleSlotSweeperStarted() {
|
|
12075
|
+
if (staleSlotSweeperTimer) return;
|
|
12076
|
+
staleSlotSweeperTimer = setInterval(() => {
|
|
12077
|
+
try {
|
|
12078
|
+
sweepStaleAccountStreamSlots();
|
|
12079
|
+
} catch (err) {
|
|
12080
|
+
logger.error("[Qwen] Error in stale stream slot sweeper", {
|
|
12081
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12082
|
+
});
|
|
12083
|
+
}
|
|
12084
|
+
}, 6e4);
|
|
12085
|
+
staleSlotSweeperTimer.unref();
|
|
12086
|
+
}
|
|
12087
|
+
function stopStaleSlotSweeperForTests() {
|
|
12088
|
+
if (staleSlotSweeperTimer) {
|
|
12089
|
+
clearInterval(staleSlotSweeperTimer);
|
|
12090
|
+
staleSlotSweeperTimer = null;
|
|
12091
|
+
}
|
|
12092
|
+
}
|
|
11969
12093
|
async function acquireAccountStreamLock(accountId) {
|
|
12094
|
+
ensureStaleSlotSweeperStarted();
|
|
11970
12095
|
const slots2 = getAccountStreamMutex(accountId);
|
|
11971
|
-
if (slots2.
|
|
11972
|
-
|
|
11973
|
-
|
|
12096
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
12097
|
+
const slotId = nextStreamSlotId++;
|
|
12098
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
12099
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
11974
12100
|
}
|
|
11975
12101
|
return new Promise((resolve) => {
|
|
11976
|
-
slots2.queue.push(() => resolve(
|
|
12102
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
11977
12103
|
});
|
|
11978
12104
|
}
|
|
11979
12105
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -12047,7 +12173,7 @@ async function withQwenBrowserPage(accountId, fn, targetPath, operationTimeoutMs
|
|
|
12047
12173
|
return fn(page);
|
|
12048
12174
|
},
|
|
12049
12175
|
operationTimeoutMs,
|
|
12050
|
-
Math.
|
|
12176
|
+
Math.max(6e4, operationTimeoutMs),
|
|
12051
12177
|
recoverOnTimeout
|
|
12052
12178
|
);
|
|
12053
12179
|
}
|
|
@@ -12065,11 +12191,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
12065
12191
|
"x-request-id",
|
|
12066
12192
|
"x-accel-buffering"
|
|
12067
12193
|
]);
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12194
|
+
const result = {};
|
|
12195
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
12196
|
+
if (browserAllowedHeaders.has(name.toLowerCase())) {
|
|
12197
|
+
result[name] = sanitizeHeaderForFetch(String(value ?? ""));
|
|
12198
|
+
}
|
|
12199
|
+
}
|
|
12200
|
+
return result;
|
|
12073
12201
|
}
|
|
12074
12202
|
async function requestQwenTextInBrowser(accountId, method, path21, headers, body, options = {}) {
|
|
12075
12203
|
const url = qwenUrl(path21);
|
|
@@ -12090,7 +12218,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12090
12218
|
browserHeaders["Content-Type"] = "application/json";
|
|
12091
12219
|
}
|
|
12092
12220
|
const evaluateRequest = (page) => page.evaluate(
|
|
12093
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
12221
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
12094
12222
|
const controller = new AbortController();
|
|
12095
12223
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12096
12224
|
try {
|
|
@@ -12114,7 +12242,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12114
12242
|
source: "web",
|
|
12115
12243
|
version: "0.3.11",
|
|
12116
12244
|
"x-request-origin": "https://chat.qwen.ai",
|
|
12117
|
-
timezone:
|
|
12245
|
+
timezone: tz,
|
|
12118
12246
|
authorization: `Bearer ${rTok}`
|
|
12119
12247
|
},
|
|
12120
12248
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -12159,7 +12287,8 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12159
12287
|
headers: browserHeaders,
|
|
12160
12288
|
body,
|
|
12161
12289
|
referrer: options.referrer,
|
|
12162
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
12290
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
12291
|
+
tz: formatAsciiTimezone()
|
|
12163
12292
|
}
|
|
12164
12293
|
);
|
|
12165
12294
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -12837,7 +12966,7 @@ async function disableNativeTools(accountId) {
|
|
|
12837
12966
|
async (page) => {
|
|
12838
12967
|
const requestId = crypto7.randomUUID();
|
|
12839
12968
|
const response = await page.evaluate(
|
|
12840
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
12969
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
12841
12970
|
const controller = new AbortController();
|
|
12842
12971
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12843
12972
|
try {
|
|
@@ -12849,7 +12978,7 @@ async function disableNativeTools(accountId) {
|
|
|
12849
12978
|
accept: "application/json, text/plain, */*",
|
|
12850
12979
|
"content-type": "application/json",
|
|
12851
12980
|
"x-request-id": requestId2,
|
|
12852
|
-
timezone:
|
|
12981
|
+
timezone: tz,
|
|
12853
12982
|
source: "web"
|
|
12854
12983
|
},
|
|
12855
12984
|
body: JSON.stringify(payload2),
|
|
@@ -12861,7 +12990,7 @@ async function disableNativeTools(accountId) {
|
|
|
12861
12990
|
clearTimeout(timeoutId);
|
|
12862
12991
|
}
|
|
12863
12992
|
},
|
|
12864
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
12993
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
12865
12994
|
);
|
|
12866
12995
|
return response;
|
|
12867
12996
|
}
|
|
@@ -13078,7 +13207,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
13078
13207
|
const requestHeaders = {
|
|
13079
13208
|
source: "web",
|
|
13080
13209
|
version: "0.2.89",
|
|
13081
|
-
timezone: (
|
|
13210
|
+
timezone: formatAsciiTimezone(),
|
|
13082
13211
|
"x-request-id": crypto7.randomUUID(),
|
|
13083
13212
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
13084
13213
|
};
|
|
@@ -13122,7 +13251,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
13122
13251
|
}
|
|
13123
13252
|
const requestHeaders = {
|
|
13124
13253
|
version: "0.2.89",
|
|
13125
|
-
timezone: (
|
|
13254
|
+
timezone: formatAsciiTimezone(),
|
|
13126
13255
|
"x-request-id": crypto7.randomUUID(),
|
|
13127
13256
|
Referer: qwenUrl("/settings/chats")
|
|
13128
13257
|
};
|
|
@@ -13174,7 +13303,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13174
13303
|
async (page) => {
|
|
13175
13304
|
const requestId = crypto7.randomUUID();
|
|
13176
13305
|
const response2 = await page.evaluate(
|
|
13177
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
13306
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
13178
13307
|
const controller = new AbortController();
|
|
13179
13308
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
13180
13309
|
try {
|
|
@@ -13183,7 +13312,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13183
13312
|
headers: {
|
|
13184
13313
|
accept: "application/json, text/plain, */*",
|
|
13185
13314
|
"x-request-id": requestId2,
|
|
13186
|
-
timezone:
|
|
13315
|
+
timezone: tz,
|
|
13187
13316
|
source: "web"
|
|
13188
13317
|
},
|
|
13189
13318
|
signal: controller.signal
|
|
@@ -13193,7 +13322,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13193
13322
|
clearTimeout(timeoutId);
|
|
13194
13323
|
}
|
|
13195
13324
|
},
|
|
13196
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
13325
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
13197
13326
|
);
|
|
13198
13327
|
return response2;
|
|
13199
13328
|
}
|
|
@@ -13232,7 +13361,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13232
13361
|
bxUa,
|
|
13233
13362
|
bxUmidtoken,
|
|
13234
13363
|
extra: {
|
|
13235
|
-
timezone: (
|
|
13364
|
+
timezone: formatAsciiTimezone()
|
|
13236
13365
|
}
|
|
13237
13366
|
}),
|
|
13238
13367
|
void 0,
|
|
@@ -13995,7 +14124,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
13995
14124
|
clearTimeout(timeoutId);
|
|
13996
14125
|
}
|
|
13997
14126
|
}
|
|
13998
|
-
var sleep5, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, accountStreamMutexes, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
14127
|
+
var sleep5, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, accountStreamMutexes, nextStreamSlotId, staleSlotSweeperTimer, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
13999
14128
|
var init_qwen = __esm({
|
|
14000
14129
|
"src/services/qwen.ts"() {
|
|
14001
14130
|
"use strict";
|
|
@@ -14013,6 +14142,7 @@ var init_qwen = __esm({
|
|
|
14013
14142
|
init_captcha_coordinator();
|
|
14014
14143
|
init_captcha_solver();
|
|
14015
14144
|
init_account_concurrency();
|
|
14145
|
+
init_timezone();
|
|
14016
14146
|
init_qwen_errors();
|
|
14017
14147
|
init_qwen_thread_state();
|
|
14018
14148
|
init_qwen_chat_pool();
|
|
@@ -14042,7 +14172,10 @@ var init_qwen = __esm({
|
|
|
14042
14172
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
14043
14173
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
14044
14174
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
14175
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
14045
14176
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
14177
|
+
nextStreamSlotId = 1;
|
|
14178
|
+
staleSlotSweeperTimer = null;
|
|
14046
14179
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
14047
14180
|
ui: {
|
|
14048
14181
|
autoTags: false,
|
|
@@ -16772,7 +16905,7 @@ function buildContextMeterSnapshot(input, options = getContextMeterOptions()) {
|
|
|
16772
16905
|
function enrichUsageWithContextMeter(usage, snapshot, options = getContextMeterOptions()) {
|
|
16773
16906
|
if (!snapshot || !options.enabled) return usage;
|
|
16774
16907
|
const upstreamPromptTokens = Number.isFinite(usage.prompt_tokens) && usage.prompt_tokens > 0 ? usage.prompt_tokens : null;
|
|
16775
|
-
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ??
|
|
16908
|
+
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ?? usage.prompt_tokens : usage.prompt_tokens;
|
|
16776
16909
|
const contextMeter = {
|
|
16777
16910
|
...snapshot,
|
|
16778
16911
|
upstreamPromptTokens,
|
|
@@ -20400,6 +20533,35 @@ var init_parser = __esm({
|
|
|
20400
20533
|
}
|
|
20401
20534
|
});
|
|
20402
20535
|
|
|
20536
|
+
// src/tools/tool-call-heuristics.ts
|
|
20537
|
+
function isToolHesitationText(text) {
|
|
20538
|
+
if (!text) return false;
|
|
20539
|
+
return /\btools?\b[^.\n]{0,24}?\b(?:does\s*n[o']?t|do\s*n[o']?t|does\s*not|do\s*not)\b[^.\n]{0,12}?\bexist/i.test(
|
|
20540
|
+
text
|
|
20541
|
+
);
|
|
20542
|
+
}
|
|
20543
|
+
function decideMissingToolCallRetry(input) {
|
|
20544
|
+
const {
|
|
20545
|
+
declaredToolCount,
|
|
20546
|
+
successfulToolCallCount,
|
|
20547
|
+
malformedToolCallCount,
|
|
20548
|
+
emittedToolCallCount,
|
|
20549
|
+
text
|
|
20550
|
+
} = input;
|
|
20551
|
+
if (declaredToolCount <= 0) return { retry: false, reason: null };
|
|
20552
|
+
if (successfulToolCallCount > 0) return { retry: false, reason: null };
|
|
20553
|
+
if (malformedToolCallCount > 0) return { retry: true, reason: "malformed" };
|
|
20554
|
+
if (emittedToolCallCount === 0 && isToolHesitationText(text)) {
|
|
20555
|
+
return { retry: true, reason: "no_call" };
|
|
20556
|
+
}
|
|
20557
|
+
return { retry: false, reason: null };
|
|
20558
|
+
}
|
|
20559
|
+
var init_tool_call_heuristics = __esm({
|
|
20560
|
+
"src/tools/tool-call-heuristics.ts"() {
|
|
20561
|
+
"use strict";
|
|
20562
|
+
}
|
|
20563
|
+
});
|
|
20564
|
+
|
|
20403
20565
|
// src/routes/chat/errors.ts
|
|
20404
20566
|
function isWafChallenge(value) {
|
|
20405
20567
|
const normalized = value.toLowerCase();
|
|
@@ -21099,10 +21261,17 @@ async function processNonStreamingResponse(params) {
|
|
|
21099
21261
|
message.tool_calls = toolCallsOut;
|
|
21100
21262
|
}
|
|
21101
21263
|
const finishReason = toolCallsOut.length ? "tool_calls" : "stop";
|
|
21102
|
-
const
|
|
21264
|
+
const retryDecision = toolParser ? decideMissingToolCallRetry({
|
|
21265
|
+
declaredToolCount: declaredTools.length,
|
|
21266
|
+
successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
|
|
21267
|
+
malformedToolCallCount: toolParser.getMalformedToolCalls().length,
|
|
21268
|
+
emittedToolCallCount: toolParser.getEmittedToolCallCount(),
|
|
21269
|
+
text: finalContent
|
|
21270
|
+
}) : { retry: false, reason: null };
|
|
21271
|
+
const allToolsFailed = retryDecision.retry;
|
|
21103
21272
|
const malformedRetryCount = midStreamRetry?.malformedRetryCount ?? 0;
|
|
21104
21273
|
if (allToolsFailed && config.retry.autoRetryMalformedTools !== false && midStreamRetry && malformedRetryCount < config.retry.autoRetryMalformedToolsMax) {
|
|
21105
|
-
const malformedCalls = toolParser
|
|
21274
|
+
const malformedCalls = toolParser?.getMalformedToolCalls() ?? [];
|
|
21106
21275
|
const malformedCount = malformedCalls.length;
|
|
21107
21276
|
const undeclaredNames = malformedCalls.flatMap((mc) => mc.undeclaredNames || []).filter((name, index, self) => self.indexOf(name) === index);
|
|
21108
21277
|
const availableToolNames = declaredTools.map((t) => t.type === "function" ? t.function?.name : t.name).filter((n) => !!n);
|
|
@@ -21110,7 +21279,9 @@ async function processNonStreamingResponse(params) {
|
|
|
21110
21279
|
|
|
21111
21280
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
21112
21281
|
let errorMessage;
|
|
21113
|
-
if (
|
|
21282
|
+
if (retryDecision.reason === "no_call") {
|
|
21283
|
+
errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
|
|
21284
|
+
} else if (undeclaredNames.length > 0) {
|
|
21114
21285
|
errorMessage = `Your previous ${malformedCount} tool call(s) used undeclared tool names: ${undeclaredNames.join(", ")}. Only declared tools can be executed. Please retry with valid tool names.${toolsHint}`;
|
|
21115
21286
|
} else {
|
|
21116
21287
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -21186,7 +21357,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21186
21357
|
new Error("client aborted during malformed-tool retry")
|
|
21187
21358
|
);
|
|
21188
21359
|
}
|
|
21189
|
-
console.log(`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${config.retry.autoRetryMalformedToolsMax}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason
|
|
21360
|
+
console.log(`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${config.retry.autoRetryMalformedToolsMax}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=${retryDecision.reason === "no_call" ? "missing_tool_call" : "malformed_tool_calls"}`);
|
|
21190
21361
|
return processNonStreamingResponse({
|
|
21191
21362
|
...params,
|
|
21192
21363
|
stream: newStreamResult.stream,
|
|
@@ -22282,7 +22453,14 @@ async function processStreamingResponse(params) {
|
|
|
22282
22453
|
let activeRetryStream = null;
|
|
22283
22454
|
while (malformedRetryCount < maxMalformedRetries) {
|
|
22284
22455
|
if (!toolParser) break;
|
|
22285
|
-
const
|
|
22456
|
+
const retryDecision = decideMissingToolCallRetry({
|
|
22457
|
+
declaredToolCount: declaredTools.length,
|
|
22458
|
+
successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
|
|
22459
|
+
malformedToolCallCount: toolParser.getMalformedToolCalls().length,
|
|
22460
|
+
emittedToolCallCount: toolParser.getEmittedToolCallCount(),
|
|
22461
|
+
text: finalContent
|
|
22462
|
+
});
|
|
22463
|
+
const allToolsFailed = retryDecision.retry;
|
|
22286
22464
|
if (!allToolsFailed) break;
|
|
22287
22465
|
const malformedCalls = toolParser.getMalformedToolCalls();
|
|
22288
22466
|
const malformedCount = malformedCalls.length;
|
|
@@ -22292,7 +22470,9 @@ async function processStreamingResponse(params) {
|
|
|
22292
22470
|
|
|
22293
22471
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
22294
22472
|
let errorMessage;
|
|
22295
|
-
if (
|
|
22473
|
+
if (retryDecision.reason === "no_call") {
|
|
22474
|
+
errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
|
|
22475
|
+
} else if (undeclaredNames.length > 0) {
|
|
22296
22476
|
errorMessage = `Your previous ${malformedCount} tool call(s) used undeclared tool names: ${undeclaredNames.join(", ")}. Only declared tools can be executed. Please retry with valid tool names.${toolsHint}`;
|
|
22297
22477
|
} else {
|
|
22298
22478
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -22369,7 +22549,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
22369
22549
|
break;
|
|
22370
22550
|
}
|
|
22371
22551
|
console.log(
|
|
22372
|
-
`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${maxMalformedRetries}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason
|
|
22552
|
+
`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${maxMalformedRetries}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=${retryDecision.reason === "no_call" ? "missing_tool_call" : "malformed_tool_calls"}`
|
|
22373
22553
|
);
|
|
22374
22554
|
const retryEntry = getStream(newStreamResult.completionId);
|
|
22375
22555
|
removeStream(newStreamResult.completionId);
|
|
@@ -22870,6 +23050,7 @@ var init_streaming = __esm({
|
|
|
22870
23050
|
init_retry_policy();
|
|
22871
23051
|
init_account();
|
|
22872
23052
|
init_parser();
|
|
23053
|
+
init_tool_call_heuristics();
|
|
22873
23054
|
init_stream_registry();
|
|
22874
23055
|
init_metrics();
|
|
22875
23056
|
init_logger();
|
|
@@ -24278,7 +24459,7 @@ function chatCompletionsToResponses(chatRes, requestModel, originalRequest) {
|
|
|
24278
24459
|
cached_tokens: chatRes.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
24279
24460
|
},
|
|
24280
24461
|
output_tokens_details: {
|
|
24281
|
-
reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? 0
|
|
24462
|
+
reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? chatRes.usage.output_tokens_details?.reasoning_tokens ?? 0
|
|
24282
24463
|
},
|
|
24283
24464
|
...chatRes.usage.context_meter ? { context_meter: chatRes.usage.context_meter } : {}
|
|
24284
24465
|
};
|
|
@@ -25739,7 +25920,7 @@ function translateOpenAIToAnthropic(openaiResponse, requestModel) {
|
|
|
25739
25920
|
content_filter: "end_turn"
|
|
25740
25921
|
};
|
|
25741
25922
|
const hasToolUse = content.some((b) => b.type === "tool_use");
|
|
25742
|
-
const stopReason = hasToolUse ? "tool_use" : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
|
|
25923
|
+
const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : hasToolUse ? "tool_use" : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
|
|
25743
25924
|
return {
|
|
25744
25925
|
id: generateMessageId2(),
|
|
25745
25926
|
type: "message",
|
|
@@ -25877,7 +26058,7 @@ function translateStreamChunk(chunk, state) {
|
|
|
25877
26058
|
state.contentBlockIndex++;
|
|
25878
26059
|
state.currentBlockType = null;
|
|
25879
26060
|
}
|
|
25880
|
-
const stopReason =
|
|
26061
|
+
const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : state.hasEmittedToolUse ? "tool_use" : "end_turn";
|
|
25881
26062
|
events.push(
|
|
25882
26063
|
JSON.stringify({
|
|
25883
26064
|
type: "message_delta",
|
|
@@ -26823,7 +27004,7 @@ function getAppVersion() {
|
|
|
26823
27004
|
}
|
|
26824
27005
|
} catch {
|
|
26825
27006
|
}
|
|
26826
|
-
cachedVersion = "v1.6.
|
|
27007
|
+
cachedVersion = "v1.6.5";
|
|
26827
27008
|
return cachedVersion;
|
|
26828
27009
|
}
|
|
26829
27010
|
var cachedVersion;
|