qwenproxy-cli 1.6.4 → 1.6.6
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 +84 -11
- package/dist/delete-chats.js +183 -45
- package/dist/index.js +267 -77
- package/dist/login.js +92 -16
- package/dist/reset-cooldowns.js +87 -12
- 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/streaming.ts +66 -31
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +57 -20
- package/src/services/qwen-headers.ts +3 -2
- package/src/services/qwen.ts +87 -30
- 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);
|
|
@@ -5509,6 +5557,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5509
5557
|
await existingInit;
|
|
5510
5558
|
return;
|
|
5511
5559
|
}
|
|
5560
|
+
let resolveInit;
|
|
5561
|
+
let rejectInit;
|
|
5562
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
5563
|
+
resolveInit = res;
|
|
5564
|
+
rejectInit = rej;
|
|
5565
|
+
});
|
|
5566
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
5512
5567
|
const initPromise = (async () => {
|
|
5513
5568
|
const release = await acquireAccountMutex(
|
|
5514
5569
|
account.id,
|
|
@@ -5723,6 +5778,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5723
5778
|
}
|
|
5724
5779
|
}
|
|
5725
5780
|
}
|
|
5781
|
+
const currentCache = getHeaderCache(account.id);
|
|
5782
|
+
const currentCookie = currentCache?.headers?.cookie || "";
|
|
5783
|
+
if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
|
|
5784
|
+
try {
|
|
5785
|
+
const refreshRes = await refreshAccountToken(account.id);
|
|
5786
|
+
if (refreshRes.success) {
|
|
5787
|
+
console.log(
|
|
5788
|
+
`\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
|
|
5789
|
+
);
|
|
5790
|
+
}
|
|
5791
|
+
} catch (err) {
|
|
5792
|
+
console.warn(
|
|
5793
|
+
`\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
|
|
5794
|
+
);
|
|
5795
|
+
}
|
|
5796
|
+
}
|
|
5726
5797
|
return;
|
|
5727
5798
|
}
|
|
5728
5799
|
const cookies = await acctContext.cookies();
|
|
@@ -5777,9 +5848,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5777
5848
|
release();
|
|
5778
5849
|
}
|
|
5779
5850
|
})();
|
|
5780
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
5781
5851
|
try {
|
|
5782
5852
|
await initPromise;
|
|
5853
|
+
resolveInit();
|
|
5854
|
+
} catch (error) {
|
|
5855
|
+
rejectInit(error);
|
|
5856
|
+
throw error;
|
|
5783
5857
|
} finally {
|
|
5784
5858
|
inFlightAccountInits.delete(account.id);
|
|
5785
5859
|
}
|
|
@@ -6010,7 +6084,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
6010
6084
|
email: normalizedEmail,
|
|
6011
6085
|
requestId,
|
|
6012
6086
|
version: getQwenWebVersion() || "0.3.12",
|
|
6013
|
-
timezone: (
|
|
6087
|
+
timezone: formatAsciiTimezone()
|
|
6014
6088
|
}
|
|
6015
6089
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6016
6090
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -6105,7 +6179,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
6105
6179
|
code: cleanCode,
|
|
6106
6180
|
requestId,
|
|
6107
6181
|
version: getQwenWebVersion() || "0.3.12",
|
|
6108
|
-
timezone: (
|
|
6182
|
+
timezone: formatAsciiTimezone()
|
|
6109
6183
|
}
|
|
6110
6184
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6111
6185
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -6386,7 +6460,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6386
6460
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
6387
6461
|
const requestId = crypto5.randomUUID();
|
|
6388
6462
|
const evalRes = await page.evaluate(
|
|
6389
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
6463
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
6390
6464
|
try {
|
|
6391
6465
|
const response = await fetch(signinUrl2, {
|
|
6392
6466
|
method: "POST",
|
|
@@ -6398,7 +6472,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6398
6472
|
source: "web",
|
|
6399
6473
|
version: "0.3.11",
|
|
6400
6474
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6401
|
-
timezone:
|
|
6475
|
+
timezone: tz,
|
|
6402
6476
|
"x-request-id": requestId2
|
|
6403
6477
|
},
|
|
6404
6478
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6409,7 +6483,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6409
6483
|
return { ok: false, error: e.message };
|
|
6410
6484
|
}
|
|
6411
6485
|
},
|
|
6412
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
6486
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
6413
6487
|
).catch(() => null);
|
|
6414
6488
|
let data = evalRes?.data;
|
|
6415
6489
|
let signinSuccess = Boolean(
|
|
@@ -6418,7 +6492,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6418
6492
|
if (!signinSuccess && !data?.data?.code) {
|
|
6419
6493
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
6420
6494
|
const fallbackRes = await page.evaluate(
|
|
6421
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
6495
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
6422
6496
|
try {
|
|
6423
6497
|
const response = await fetch(fallbackUrl2, {
|
|
6424
6498
|
method: "POST",
|
|
@@ -6429,7 +6503,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6429
6503
|
"content-type": "application/json",
|
|
6430
6504
|
source: "web",
|
|
6431
6505
|
version: "0.3.11",
|
|
6432
|
-
timezone:
|
|
6506
|
+
timezone: tz,
|
|
6433
6507
|
"x-request-id": requestId2
|
|
6434
6508
|
},
|
|
6435
6509
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6440,7 +6514,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6440
6514
|
return { ok: false, error: e.message };
|
|
6441
6515
|
}
|
|
6442
6516
|
},
|
|
6443
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
6517
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
6444
6518
|
).catch(() => null);
|
|
6445
6519
|
if (fallbackRes?.data) {
|
|
6446
6520
|
data = fallbackRes.data;
|
|
@@ -6772,7 +6846,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6772
6846
|
if (refreshToken) {
|
|
6773
6847
|
try {
|
|
6774
6848
|
const refreshResult = await page.evaluate(
|
|
6775
|
-
async ({ rTok }) => {
|
|
6849
|
+
async ({ rTok, tz }) => {
|
|
6776
6850
|
try {
|
|
6777
6851
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6778
6852
|
method: "GET",
|
|
@@ -6782,7 +6856,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6782
6856
|
source: "web",
|
|
6783
6857
|
version: "0.3.11",
|
|
6784
6858
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6785
|
-
timezone:
|
|
6859
|
+
timezone: tz,
|
|
6786
6860
|
authorization: `Bearer ${rTok}`
|
|
6787
6861
|
}
|
|
6788
6862
|
});
|
|
@@ -6792,7 +6866,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6792
6866
|
return { ok: false, error: e.message };
|
|
6793
6867
|
}
|
|
6794
6868
|
},
|
|
6795
|
-
{ rTok: refreshToken }
|
|
6869
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
6796
6870
|
);
|
|
6797
6871
|
const json = refreshResult?.data;
|
|
6798
6872
|
if (json && json.success === true && json.data) {
|
|
@@ -6830,6 +6904,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6830
6904
|
const cache3 = getHeaderCache(accountId);
|
|
6831
6905
|
cache3.headers.cookie = cookieStr;
|
|
6832
6906
|
cache3.lastRefresh = Date.now();
|
|
6907
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6833
6908
|
markAccountHeadersReady(accountId);
|
|
6834
6909
|
saveAuthSession2(accountId, {
|
|
6835
6910
|
cookie: cookieStr,
|
|
@@ -6864,6 +6939,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6864
6939
|
const cache3 = getHeaderCache(accountId);
|
|
6865
6940
|
cache3.headers.cookie = cookieStr;
|
|
6866
6941
|
cache3.lastRefresh = Date.now();
|
|
6942
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6867
6943
|
markAccountHeadersReady(accountId);
|
|
6868
6944
|
saveAuthSession2(accountId, {
|
|
6869
6945
|
cookie: cookieStr,
|
|
@@ -7918,11 +7994,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7918
7994
|
const now = Date.now();
|
|
7919
7995
|
const currentUrl = page.url();
|
|
7920
7996
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
7921
|
-
const cookie = await getCookies(accountId);
|
|
7997
|
+
const cookie = await getCookies(accountId, true);
|
|
7922
7998
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
7923
7999
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
7924
|
-
|
|
7925
|
-
|
|
8000
|
+
logger.debug(
|
|
8001
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
7926
8002
|
);
|
|
7927
8003
|
const res = await refreshAccountToken(accountId);
|
|
7928
8004
|
if (res.success) {
|
|
@@ -7930,6 +8006,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7930
8006
|
touchAccountActivity(accountId);
|
|
7931
8007
|
return true;
|
|
7932
8008
|
}
|
|
8009
|
+
logger.warn(
|
|
8010
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
8011
|
+
);
|
|
7933
8012
|
}
|
|
7934
8013
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
7935
8014
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -8167,6 +8246,7 @@ var init_playwright = __esm({
|
|
|
8167
8246
|
init_qwen_headers();
|
|
8168
8247
|
init_paths();
|
|
8169
8248
|
init_jwt();
|
|
8249
|
+
init_timezone();
|
|
8170
8250
|
requireLocal = createRequire(import.meta.url);
|
|
8171
8251
|
contextInitHooks = [];
|
|
8172
8252
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -8180,7 +8260,7 @@ var init_playwright = __esm({
|
|
|
8180
8260
|
headerCaches = /* @__PURE__ */ new Map();
|
|
8181
8261
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
8182
8262
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
8183
|
-
COOKIE_CACHE_TTL =
|
|
8263
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
8184
8264
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
8185
8265
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
8186
8266
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -9599,8 +9679,8 @@ function createUsageAccumulator(estimatedPromptTokens) {
|
|
|
9599
9679
|
function applyUpstreamUsage(accumulator, candidate) {
|
|
9600
9680
|
if (!candidate || typeof candidate !== "object") return;
|
|
9601
9681
|
const usage = candidate;
|
|
9602
|
-
const promptTokens = asFiniteNumber(usage.input_tokens);
|
|
9603
|
-
const completionTokens = asFiniteNumber(usage.output_tokens);
|
|
9682
|
+
const promptTokens = asFiniteNumber(usage.input_tokens) ?? asFiniteNumber(usage.prompt_tokens);
|
|
9683
|
+
const completionTokens = asFiniteNumber(usage.output_tokens) ?? asFiniteNumber(usage.completion_tokens);
|
|
9604
9684
|
const totalTokens = asFiniteNumber(usage.total_tokens);
|
|
9605
9685
|
if (promptTokens !== null) {
|
|
9606
9686
|
accumulator.promptTokens = promptTokens;
|
|
@@ -11632,6 +11712,8 @@ __export(qwen_exports, {
|
|
|
11632
11712
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
11633
11713
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
11634
11714
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
11715
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
11716
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
11635
11717
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
11636
11718
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
11637
11719
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -11645,10 +11727,13 @@ __export(qwen_exports, {
|
|
|
11645
11727
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
11646
11728
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
11647
11729
|
disableNativeTools: () => disableNativeTools,
|
|
11730
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
11648
11731
|
fetchQwenModels: () => fetchQwenModels,
|
|
11649
11732
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
11650
11733
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
11734
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
11651
11735
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
11736
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
11652
11737
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
11653
11738
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
11654
11739
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -11664,6 +11749,8 @@ __export(qwen_exports, {
|
|
|
11664
11749
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
11665
11750
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
11666
11751
|
setToolCapNotice: () => setToolCapNotice,
|
|
11752
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
11753
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
11667
11754
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
11668
11755
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
11669
11756
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -11950,7 +12037,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
11950
12037
|
function getAccountStreamMutex(accountId) {
|
|
11951
12038
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
11952
12039
|
if (!slots2) {
|
|
11953
|
-
slots2 = {
|
|
12040
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
11954
12041
|
accountStreamMutexes.set(accountId, slots2);
|
|
11955
12042
|
}
|
|
11956
12043
|
return slots2;
|
|
@@ -11958,27 +12045,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
11958
12045
|
function streamSlotCapacity() {
|
|
11959
12046
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
11960
12047
|
}
|
|
11961
|
-
function createStreamSlotRelease(slots2) {
|
|
12048
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
11962
12049
|
let released = false;
|
|
11963
12050
|
return () => {
|
|
11964
12051
|
if (released) return;
|
|
11965
12052
|
released = true;
|
|
11966
|
-
slots2.
|
|
12053
|
+
slots2.activeSlots.delete(slotId);
|
|
11967
12054
|
const next = slots2.queue.shift();
|
|
11968
12055
|
if (next) {
|
|
11969
|
-
|
|
11970
|
-
|
|
12056
|
+
const nextId = nextStreamSlotId++;
|
|
12057
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
12058
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
11971
12059
|
}
|
|
11972
12060
|
};
|
|
11973
12061
|
}
|
|
12062
|
+
function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
|
|
12063
|
+
const now = Date.now();
|
|
12064
|
+
let sweptCount = 0;
|
|
12065
|
+
for (const [accountId, slots2] of accountStreamMutexes.entries()) {
|
|
12066
|
+
for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
|
|
12067
|
+
if (now - startedAt >= maxHoldMs) {
|
|
12068
|
+
logger.warn(
|
|
12069
|
+
`[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
|
|
12070
|
+
);
|
|
12071
|
+
slots2.activeSlots.delete(slotId);
|
|
12072
|
+
sweptCount++;
|
|
12073
|
+
const next = slots2.queue.shift();
|
|
12074
|
+
if (next) {
|
|
12075
|
+
const nextId = nextStreamSlotId++;
|
|
12076
|
+
slots2.activeSlots.set(nextId, now);
|
|
12077
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
12078
|
+
}
|
|
12079
|
+
}
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
12082
|
+
return sweptCount;
|
|
12083
|
+
}
|
|
12084
|
+
function ensureStaleSlotSweeperStarted() {
|
|
12085
|
+
if (staleSlotSweeperTimer) return;
|
|
12086
|
+
staleSlotSweeperTimer = setInterval(() => {
|
|
12087
|
+
try {
|
|
12088
|
+
sweepStaleAccountStreamSlots();
|
|
12089
|
+
} catch (err) {
|
|
12090
|
+
logger.error("[Qwen] Error in stale stream slot sweeper", {
|
|
12091
|
+
error: err instanceof Error ? err.message : String(err)
|
|
12092
|
+
});
|
|
12093
|
+
}
|
|
12094
|
+
}, 6e4);
|
|
12095
|
+
staleSlotSweeperTimer.unref();
|
|
12096
|
+
}
|
|
12097
|
+
function stopStaleSlotSweeperForTests() {
|
|
12098
|
+
if (staleSlotSweeperTimer) {
|
|
12099
|
+
clearInterval(staleSlotSweeperTimer);
|
|
12100
|
+
staleSlotSweeperTimer = null;
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
11974
12103
|
async function acquireAccountStreamLock(accountId) {
|
|
12104
|
+
ensureStaleSlotSweeperStarted();
|
|
11975
12105
|
const slots2 = getAccountStreamMutex(accountId);
|
|
11976
|
-
if (slots2.
|
|
11977
|
-
|
|
11978
|
-
|
|
12106
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
12107
|
+
const slotId = nextStreamSlotId++;
|
|
12108
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
12109
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
11979
12110
|
}
|
|
11980
12111
|
return new Promise((resolve) => {
|
|
11981
|
-
slots2.queue.push(() => resolve(
|
|
12112
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
11982
12113
|
});
|
|
11983
12114
|
}
|
|
11984
12115
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -12070,11 +12201,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
12070
12201
|
"x-request-id",
|
|
12071
12202
|
"x-accel-buffering"
|
|
12072
12203
|
]);
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12204
|
+
const result = {};
|
|
12205
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
12206
|
+
if (browserAllowedHeaders.has(name.toLowerCase())) {
|
|
12207
|
+
result[name] = sanitizeHeaderForFetch(String(value ?? ""));
|
|
12208
|
+
}
|
|
12209
|
+
}
|
|
12210
|
+
return result;
|
|
12078
12211
|
}
|
|
12079
12212
|
async function requestQwenTextInBrowser(accountId, method, path21, headers, body, options = {}) {
|
|
12080
12213
|
const url = qwenUrl(path21);
|
|
@@ -12095,7 +12228,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12095
12228
|
browserHeaders["Content-Type"] = "application/json";
|
|
12096
12229
|
}
|
|
12097
12230
|
const evaluateRequest = (page) => page.evaluate(
|
|
12098
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
12231
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
12099
12232
|
const controller = new AbortController();
|
|
12100
12233
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12101
12234
|
try {
|
|
@@ -12119,7 +12252,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12119
12252
|
source: "web",
|
|
12120
12253
|
version: "0.3.11",
|
|
12121
12254
|
"x-request-origin": "https://chat.qwen.ai",
|
|
12122
|
-
timezone:
|
|
12255
|
+
timezone: tz,
|
|
12123
12256
|
authorization: `Bearer ${rTok}`
|
|
12124
12257
|
},
|
|
12125
12258
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -12164,7 +12297,8 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12164
12297
|
headers: browserHeaders,
|
|
12165
12298
|
body,
|
|
12166
12299
|
referrer: options.referrer,
|
|
12167
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
12300
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
12301
|
+
tz: formatAsciiTimezone()
|
|
12168
12302
|
}
|
|
12169
12303
|
);
|
|
12170
12304
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -12842,7 +12976,7 @@ async function disableNativeTools(accountId) {
|
|
|
12842
12976
|
async (page) => {
|
|
12843
12977
|
const requestId = crypto7.randomUUID();
|
|
12844
12978
|
const response = await page.evaluate(
|
|
12845
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
12979
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
12846
12980
|
const controller = new AbortController();
|
|
12847
12981
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12848
12982
|
try {
|
|
@@ -12854,7 +12988,7 @@ async function disableNativeTools(accountId) {
|
|
|
12854
12988
|
accept: "application/json, text/plain, */*",
|
|
12855
12989
|
"content-type": "application/json",
|
|
12856
12990
|
"x-request-id": requestId2,
|
|
12857
|
-
timezone:
|
|
12991
|
+
timezone: tz,
|
|
12858
12992
|
source: "web"
|
|
12859
12993
|
},
|
|
12860
12994
|
body: JSON.stringify(payload2),
|
|
@@ -12866,7 +13000,7 @@ async function disableNativeTools(accountId) {
|
|
|
12866
13000
|
clearTimeout(timeoutId);
|
|
12867
13001
|
}
|
|
12868
13002
|
},
|
|
12869
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
13003
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
12870
13004
|
);
|
|
12871
13005
|
return response;
|
|
12872
13006
|
}
|
|
@@ -13083,7 +13217,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
13083
13217
|
const requestHeaders = {
|
|
13084
13218
|
source: "web",
|
|
13085
13219
|
version: "0.2.89",
|
|
13086
|
-
timezone: (
|
|
13220
|
+
timezone: formatAsciiTimezone(),
|
|
13087
13221
|
"x-request-id": crypto7.randomUUID(),
|
|
13088
13222
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
13089
13223
|
};
|
|
@@ -13127,7 +13261,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
13127
13261
|
}
|
|
13128
13262
|
const requestHeaders = {
|
|
13129
13263
|
version: "0.2.89",
|
|
13130
|
-
timezone: (
|
|
13264
|
+
timezone: formatAsciiTimezone(),
|
|
13131
13265
|
"x-request-id": crypto7.randomUUID(),
|
|
13132
13266
|
Referer: qwenUrl("/settings/chats")
|
|
13133
13267
|
};
|
|
@@ -13179,7 +13313,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13179
13313
|
async (page) => {
|
|
13180
13314
|
const requestId = crypto7.randomUUID();
|
|
13181
13315
|
const response2 = await page.evaluate(
|
|
13182
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
13316
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
13183
13317
|
const controller = new AbortController();
|
|
13184
13318
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
13185
13319
|
try {
|
|
@@ -13188,7 +13322,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13188
13322
|
headers: {
|
|
13189
13323
|
accept: "application/json, text/plain, */*",
|
|
13190
13324
|
"x-request-id": requestId2,
|
|
13191
|
-
timezone:
|
|
13325
|
+
timezone: tz,
|
|
13192
13326
|
source: "web"
|
|
13193
13327
|
},
|
|
13194
13328
|
signal: controller.signal
|
|
@@ -13198,7 +13332,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13198
13332
|
clearTimeout(timeoutId);
|
|
13199
13333
|
}
|
|
13200
13334
|
},
|
|
13201
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
13335
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
13202
13336
|
);
|
|
13203
13337
|
return response2;
|
|
13204
13338
|
}
|
|
@@ -13237,7 +13371,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13237
13371
|
bxUa,
|
|
13238
13372
|
bxUmidtoken,
|
|
13239
13373
|
extra: {
|
|
13240
|
-
timezone: (
|
|
13374
|
+
timezone: formatAsciiTimezone()
|
|
13241
13375
|
}
|
|
13242
13376
|
}),
|
|
13243
13377
|
void 0,
|
|
@@ -14000,7 +14134,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
14000
14134
|
clearTimeout(timeoutId);
|
|
14001
14135
|
}
|
|
14002
14136
|
}
|
|
14003
|
-
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;
|
|
14137
|
+
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;
|
|
14004
14138
|
var init_qwen = __esm({
|
|
14005
14139
|
"src/services/qwen.ts"() {
|
|
14006
14140
|
"use strict";
|
|
@@ -14018,6 +14152,7 @@ var init_qwen = __esm({
|
|
|
14018
14152
|
init_captcha_coordinator();
|
|
14019
14153
|
init_captcha_solver();
|
|
14020
14154
|
init_account_concurrency();
|
|
14155
|
+
init_timezone();
|
|
14021
14156
|
init_qwen_errors();
|
|
14022
14157
|
init_qwen_thread_state();
|
|
14023
14158
|
init_qwen_chat_pool();
|
|
@@ -14047,7 +14182,10 @@ var init_qwen = __esm({
|
|
|
14047
14182
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
14048
14183
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
14049
14184
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
14185
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
14050
14186
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
14187
|
+
nextStreamSlotId = 1;
|
|
14188
|
+
staleSlotSweeperTimer = null;
|
|
14051
14189
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
14052
14190
|
ui: {
|
|
14053
14191
|
autoTags: false,
|
|
@@ -16777,7 +16915,7 @@ function buildContextMeterSnapshot(input, options = getContextMeterOptions()) {
|
|
|
16777
16915
|
function enrichUsageWithContextMeter(usage, snapshot, options = getContextMeterOptions()) {
|
|
16778
16916
|
if (!snapshot || !options.enabled) return usage;
|
|
16779
16917
|
const upstreamPromptTokens = Number.isFinite(usage.prompt_tokens) && usage.prompt_tokens > 0 ? usage.prompt_tokens : null;
|
|
16780
|
-
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ??
|
|
16918
|
+
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ?? usage.prompt_tokens : usage.prompt_tokens;
|
|
16781
16919
|
const contextMeter = {
|
|
16782
16920
|
...snapshot,
|
|
16783
16921
|
upstreamPromptTokens,
|
|
@@ -20405,6 +20543,35 @@ var init_parser = __esm({
|
|
|
20405
20543
|
}
|
|
20406
20544
|
});
|
|
20407
20545
|
|
|
20546
|
+
// src/tools/tool-call-heuristics.ts
|
|
20547
|
+
function isToolHesitationText(text) {
|
|
20548
|
+
if (!text) return false;
|
|
20549
|
+
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(
|
|
20550
|
+
text
|
|
20551
|
+
);
|
|
20552
|
+
}
|
|
20553
|
+
function decideMissingToolCallRetry(input) {
|
|
20554
|
+
const {
|
|
20555
|
+
declaredToolCount,
|
|
20556
|
+
successfulToolCallCount,
|
|
20557
|
+
malformedToolCallCount,
|
|
20558
|
+
emittedToolCallCount,
|
|
20559
|
+
text
|
|
20560
|
+
} = input;
|
|
20561
|
+
if (declaredToolCount <= 0) return { retry: false, reason: null };
|
|
20562
|
+
if (successfulToolCallCount > 0) return { retry: false, reason: null };
|
|
20563
|
+
if (malformedToolCallCount > 0) return { retry: true, reason: "malformed" };
|
|
20564
|
+
if (emittedToolCallCount === 0 && isToolHesitationText(text)) {
|
|
20565
|
+
return { retry: true, reason: "no_call" };
|
|
20566
|
+
}
|
|
20567
|
+
return { retry: false, reason: null };
|
|
20568
|
+
}
|
|
20569
|
+
var init_tool_call_heuristics = __esm({
|
|
20570
|
+
"src/tools/tool-call-heuristics.ts"() {
|
|
20571
|
+
"use strict";
|
|
20572
|
+
}
|
|
20573
|
+
});
|
|
20574
|
+
|
|
20408
20575
|
// src/routes/chat/errors.ts
|
|
20409
20576
|
function isWafChallenge(value) {
|
|
20410
20577
|
const normalized = value.toLowerCase();
|
|
@@ -21104,10 +21271,17 @@ async function processNonStreamingResponse(params) {
|
|
|
21104
21271
|
message.tool_calls = toolCallsOut;
|
|
21105
21272
|
}
|
|
21106
21273
|
const finishReason = toolCallsOut.length ? "tool_calls" : "stop";
|
|
21107
|
-
const
|
|
21274
|
+
const retryDecision = toolParser ? decideMissingToolCallRetry({
|
|
21275
|
+
declaredToolCount: declaredTools.length,
|
|
21276
|
+
successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
|
|
21277
|
+
malformedToolCallCount: toolParser.getMalformedToolCalls().length,
|
|
21278
|
+
emittedToolCallCount: toolParser.getEmittedToolCallCount(),
|
|
21279
|
+
text: finalContent
|
|
21280
|
+
}) : { retry: false, reason: null };
|
|
21281
|
+
const allToolsFailed = retryDecision.retry;
|
|
21108
21282
|
const malformedRetryCount = midStreamRetry?.malformedRetryCount ?? 0;
|
|
21109
21283
|
if (allToolsFailed && config.retry.autoRetryMalformedTools !== false && midStreamRetry && malformedRetryCount < config.retry.autoRetryMalformedToolsMax) {
|
|
21110
|
-
const malformedCalls = toolParser
|
|
21284
|
+
const malformedCalls = toolParser?.getMalformedToolCalls() ?? [];
|
|
21111
21285
|
const malformedCount = malformedCalls.length;
|
|
21112
21286
|
const undeclaredNames = malformedCalls.flatMap((mc) => mc.undeclaredNames || []).filter((name, index, self) => self.indexOf(name) === index);
|
|
21113
21287
|
const availableToolNames = declaredTools.map((t) => t.type === "function" ? t.function?.name : t.name).filter((n) => !!n);
|
|
@@ -21115,7 +21289,9 @@ async function processNonStreamingResponse(params) {
|
|
|
21115
21289
|
|
|
21116
21290
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
21117
21291
|
let errorMessage;
|
|
21118
|
-
if (
|
|
21292
|
+
if (retryDecision.reason === "no_call") {
|
|
21293
|
+
errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
|
|
21294
|
+
} else if (undeclaredNames.length > 0) {
|
|
21119
21295
|
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}`;
|
|
21120
21296
|
} else {
|
|
21121
21297
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -21191,7 +21367,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21191
21367
|
new Error("client aborted during malformed-tool retry")
|
|
21192
21368
|
);
|
|
21193
21369
|
}
|
|
21194
|
-
console.log(`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${config.retry.autoRetryMalformedToolsMax}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason
|
|
21370
|
+
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"}`);
|
|
21195
21371
|
return processNonStreamingResponse({
|
|
21196
21372
|
...params,
|
|
21197
21373
|
stream: newStreamResult.stream,
|
|
@@ -21347,7 +21523,7 @@ async function processStreamingResponse(params) {
|
|
|
21347
21523
|
let chatInProgressSameAccountRetries = 0;
|
|
21348
21524
|
let antiBotSameAccountRetries = 0;
|
|
21349
21525
|
const totalConfiguredAccounts = loadAccounts().length;
|
|
21350
|
-
let quotaRotationsLeft = Math.
|
|
21526
|
+
let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
|
|
21351
21527
|
let streamCompletedOk = false;
|
|
21352
21528
|
let stoppedByToolCap = false;
|
|
21353
21529
|
const runDisconnectTeardown = () => {
|
|
@@ -22287,7 +22463,14 @@ async function processStreamingResponse(params) {
|
|
|
22287
22463
|
let activeRetryStream = null;
|
|
22288
22464
|
while (malformedRetryCount < maxMalformedRetries) {
|
|
22289
22465
|
if (!toolParser) break;
|
|
22290
|
-
const
|
|
22466
|
+
const retryDecision = decideMissingToolCallRetry({
|
|
22467
|
+
declaredToolCount: declaredTools.length,
|
|
22468
|
+
successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
|
|
22469
|
+
malformedToolCallCount: toolParser.getMalformedToolCalls().length,
|
|
22470
|
+
emittedToolCallCount: toolParser.getEmittedToolCallCount(),
|
|
22471
|
+
text: finalContent
|
|
22472
|
+
});
|
|
22473
|
+
const allToolsFailed = retryDecision.retry;
|
|
22291
22474
|
if (!allToolsFailed) break;
|
|
22292
22475
|
const malformedCalls = toolParser.getMalformedToolCalls();
|
|
22293
22476
|
const malformedCount = malformedCalls.length;
|
|
@@ -22297,7 +22480,9 @@ async function processStreamingResponse(params) {
|
|
|
22297
22480
|
|
|
22298
22481
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
22299
22482
|
let errorMessage;
|
|
22300
|
-
if (
|
|
22483
|
+
if (retryDecision.reason === "no_call") {
|
|
22484
|
+
errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
|
|
22485
|
+
} else if (undeclaredNames.length > 0) {
|
|
22301
22486
|
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}`;
|
|
22302
22487
|
} else {
|
|
22303
22488
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -22374,7 +22559,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
22374
22559
|
break;
|
|
22375
22560
|
}
|
|
22376
22561
|
console.log(
|
|
22377
|
-
`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${maxMalformedRetries}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason
|
|
22562
|
+
`\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"}`
|
|
22378
22563
|
);
|
|
22379
22564
|
const retryEntry = getStream(newStreamResult.completionId);
|
|
22380
22565
|
removeStream(newStreamResult.completionId);
|
|
@@ -22775,28 +22960,32 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
22775
22960
|
logger.error("[Chat] Stream callback error", errorDetails);
|
|
22776
22961
|
}
|
|
22777
22962
|
try {
|
|
22778
|
-
const
|
|
22963
|
+
const isNetwork = isNetworkLikeError(err);
|
|
22964
|
+
const isModeration = normalizedErrorCode === "data_inspection_failed" || normalizedErrorCode === "fail_sys_user_validate" || err.message?.toLowerCase().includes("content moderation") || err.message?.toLowerCase().includes("security verification");
|
|
22965
|
+
if (!isNetwork && isModeration) {
|
|
22966
|
+
const userFriendlyNotice = `
|
|
22779
22967
|
|
|
22780
|
-
\u26A0\uFE0F **[Qwen
|
|
22968
|
+
\u26A0\uFE0F **[Qwen Modera\xE7\xE3o / Seguran\xE7a]** ${err.message || "A resposta foi interrompida pelas pol\xEDticas de seguran\xE7a do Qwen."}
|
|
22781
22969
|
|
|
22782
22970
|
`;
|
|
22783
|
-
|
|
22784
|
-
|
|
22785
|
-
|
|
22786
|
-
|
|
22787
|
-
|
|
22788
|
-
|
|
22789
|
-
|
|
22790
|
-
|
|
22791
|
-
|
|
22792
|
-
|
|
22793
|
-
|
|
22794
|
-
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22971
|
+
const errorDelta = {
|
|
22972
|
+
id: completionId,
|
|
22973
|
+
object: "chat.completion.chunk",
|
|
22974
|
+
created: Math.floor(Date.now() / 1e3),
|
|
22975
|
+
model: body.model,
|
|
22976
|
+
choices: [
|
|
22977
|
+
{
|
|
22978
|
+
index: 0,
|
|
22979
|
+
delta: { content: userFriendlyNotice },
|
|
22980
|
+
logprobs: null,
|
|
22981
|
+
finish_reason: "content_filter"
|
|
22982
|
+
}
|
|
22983
|
+
]
|
|
22984
|
+
};
|
|
22985
|
+
await errorStream.write(`data: ${JSON.stringify(errorDelta)}
|
|
22798
22986
|
|
|
22799
22987
|
`);
|
|
22988
|
+
}
|
|
22800
22989
|
await errorStream.write(
|
|
22801
22990
|
`data: ${JSON.stringify({
|
|
22802
22991
|
error: {
|
|
@@ -22875,6 +23064,7 @@ var init_streaming = __esm({
|
|
|
22875
23064
|
init_retry_policy();
|
|
22876
23065
|
init_account();
|
|
22877
23066
|
init_parser();
|
|
23067
|
+
init_tool_call_heuristics();
|
|
22878
23068
|
init_stream_registry();
|
|
22879
23069
|
init_metrics();
|
|
22880
23070
|
init_logger();
|
|
@@ -24283,7 +24473,7 @@ function chatCompletionsToResponses(chatRes, requestModel, originalRequest) {
|
|
|
24283
24473
|
cached_tokens: chatRes.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
24284
24474
|
},
|
|
24285
24475
|
output_tokens_details: {
|
|
24286
|
-
reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? 0
|
|
24476
|
+
reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? chatRes.usage.output_tokens_details?.reasoning_tokens ?? 0
|
|
24287
24477
|
},
|
|
24288
24478
|
...chatRes.usage.context_meter ? { context_meter: chatRes.usage.context_meter } : {}
|
|
24289
24479
|
};
|
|
@@ -25744,7 +25934,7 @@ function translateOpenAIToAnthropic(openaiResponse, requestModel) {
|
|
|
25744
25934
|
content_filter: "end_turn"
|
|
25745
25935
|
};
|
|
25746
25936
|
const hasToolUse = content.some((b) => b.type === "tool_use");
|
|
25747
|
-
const stopReason = hasToolUse ? "tool_use" : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
|
|
25937
|
+
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";
|
|
25748
25938
|
return {
|
|
25749
25939
|
id: generateMessageId2(),
|
|
25750
25940
|
type: "message",
|
|
@@ -25882,7 +26072,7 @@ function translateStreamChunk(chunk, state) {
|
|
|
25882
26072
|
state.contentBlockIndex++;
|
|
25883
26073
|
state.currentBlockType = null;
|
|
25884
26074
|
}
|
|
25885
|
-
const stopReason =
|
|
26075
|
+
const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : state.hasEmittedToolUse ? "tool_use" : "end_turn";
|
|
25886
26076
|
events.push(
|
|
25887
26077
|
JSON.stringify({
|
|
25888
26078
|
type: "message_delta",
|
|
@@ -26828,7 +27018,7 @@ function getAppVersion() {
|
|
|
26828
27018
|
}
|
|
26829
27019
|
} catch {
|
|
26830
27020
|
}
|
|
26831
|
-
cachedVersion = "v1.6.
|
|
27021
|
+
cachedVersion = "v1.6.6";
|
|
26832
27022
|
return cachedVersion;
|
|
26833
27023
|
}
|
|
26834
27024
|
var cachedVersion;
|