qwenproxy-cli 1.6.4 → 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 +73 -10
- package/dist/delete-chats.js +172 -44
- package/dist/index.js +234 -58
- package/dist/login.js +81 -15
- package/dist/reset-cooldowns.js +76 -11
- 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 +32 -10
- package/src/routes/responses/adapter.ts +4 -1
- package/src/services/context-meter.ts +1 -1
- package/src/services/playwright.ts +41 -15
- 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);
|
|
@@ -5723,6 +5771,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5723
5771
|
}
|
|
5724
5772
|
}
|
|
5725
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
|
+
}
|
|
5726
5790
|
return;
|
|
5727
5791
|
}
|
|
5728
5792
|
const cookies = await acctContext.cookies();
|
|
@@ -6010,7 +6074,7 @@ async function startEmailOtpLogin(email, options = {}) {
|
|
|
6010
6074
|
email: normalizedEmail,
|
|
6011
6075
|
requestId,
|
|
6012
6076
|
version: getQwenWebVersion() || "0.3.12",
|
|
6013
|
-
timezone: (
|
|
6077
|
+
timezone: formatAsciiTimezone()
|
|
6014
6078
|
}
|
|
6015
6079
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6016
6080
|
if (!evalRes.ok || evalRes.data?.success !== true) {
|
|
@@ -6105,7 +6169,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
|
|
|
6105
6169
|
code: cleanCode,
|
|
6106
6170
|
requestId,
|
|
6107
6171
|
version: getQwenWebVersion() || "0.3.12",
|
|
6108
|
-
timezone: (
|
|
6172
|
+
timezone: formatAsciiTimezone()
|
|
6109
6173
|
}
|
|
6110
6174
|
).catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
6111
6175
|
if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
|
|
@@ -6386,7 +6450,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6386
6450
|
const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
|
|
6387
6451
|
const requestId = crypto5.randomUUID();
|
|
6388
6452
|
const evalRes = await page.evaluate(
|
|
6389
|
-
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
|
|
6453
|
+
async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
|
|
6390
6454
|
try {
|
|
6391
6455
|
const response = await fetch(signinUrl2, {
|
|
6392
6456
|
method: "POST",
|
|
@@ -6398,7 +6462,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6398
6462
|
source: "web",
|
|
6399
6463
|
version: "0.3.11",
|
|
6400
6464
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6401
|
-
timezone:
|
|
6465
|
+
timezone: tz,
|
|
6402
6466
|
"x-request-id": requestId2
|
|
6403
6467
|
},
|
|
6404
6468
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6409,7 +6473,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6409
6473
|
return { ok: false, error: e.message };
|
|
6410
6474
|
}
|
|
6411
6475
|
},
|
|
6412
|
-
{ email, password: hashedPassword, signinUrl, requestId }
|
|
6476
|
+
{ email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
|
|
6413
6477
|
).catch(() => null);
|
|
6414
6478
|
let data = evalRes?.data;
|
|
6415
6479
|
let signinSuccess = Boolean(
|
|
@@ -6418,7 +6482,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6418
6482
|
if (!signinSuccess && !data?.data?.code) {
|
|
6419
6483
|
const fallbackUrl = qwenUrl("/api/v2/auths/signin");
|
|
6420
6484
|
const fallbackRes = await page.evaluate(
|
|
6421
|
-
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
|
|
6485
|
+
async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
|
|
6422
6486
|
try {
|
|
6423
6487
|
const response = await fetch(fallbackUrl2, {
|
|
6424
6488
|
method: "POST",
|
|
@@ -6429,7 +6493,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6429
6493
|
"content-type": "application/json",
|
|
6430
6494
|
source: "web",
|
|
6431
6495
|
version: "0.3.11",
|
|
6432
|
-
timezone:
|
|
6496
|
+
timezone: tz,
|
|
6433
6497
|
"x-request-id": requestId2
|
|
6434
6498
|
},
|
|
6435
6499
|
body: JSON.stringify({ email: email2, password: password2 })
|
|
@@ -6440,7 +6504,7 @@ async function loginViaApi(page, email, password) {
|
|
|
6440
6504
|
return { ok: false, error: e.message };
|
|
6441
6505
|
}
|
|
6442
6506
|
},
|
|
6443
|
-
{ email, password: hashedPassword, fallbackUrl, requestId }
|
|
6507
|
+
{ email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
|
|
6444
6508
|
).catch(() => null);
|
|
6445
6509
|
if (fallbackRes?.data) {
|
|
6446
6510
|
data = fallbackRes.data;
|
|
@@ -6772,7 +6836,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6772
6836
|
if (refreshToken) {
|
|
6773
6837
|
try {
|
|
6774
6838
|
const refreshResult = await page.evaluate(
|
|
6775
|
-
async ({ rTok }) => {
|
|
6839
|
+
async ({ rTok, tz }) => {
|
|
6776
6840
|
try {
|
|
6777
6841
|
const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
|
|
6778
6842
|
method: "GET",
|
|
@@ -6782,7 +6846,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6782
6846
|
source: "web",
|
|
6783
6847
|
version: "0.3.11",
|
|
6784
6848
|
"x-request-origin": "https://chat.qwen.ai",
|
|
6785
|
-
timezone:
|
|
6849
|
+
timezone: tz,
|
|
6786
6850
|
authorization: `Bearer ${rTok}`
|
|
6787
6851
|
}
|
|
6788
6852
|
});
|
|
@@ -6792,7 +6856,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6792
6856
|
return { ok: false, error: e.message };
|
|
6793
6857
|
}
|
|
6794
6858
|
},
|
|
6795
|
-
{ rTok: refreshToken }
|
|
6859
|
+
{ rTok: refreshToken, tz: formatAsciiTimezone() }
|
|
6796
6860
|
);
|
|
6797
6861
|
const json = refreshResult?.data;
|
|
6798
6862
|
if (json && json.success === true && json.data) {
|
|
@@ -6830,6 +6894,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6830
6894
|
const cache3 = getHeaderCache(accountId);
|
|
6831
6895
|
cache3.headers.cookie = cookieStr;
|
|
6832
6896
|
cache3.lastRefresh = Date.now();
|
|
6897
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6833
6898
|
markAccountHeadersReady(accountId);
|
|
6834
6899
|
saveAuthSession2(accountId, {
|
|
6835
6900
|
cookie: cookieStr,
|
|
@@ -6864,6 +6929,7 @@ async function refreshAccountToken(accountId) {
|
|
|
6864
6929
|
const cache3 = getHeaderCache(accountId);
|
|
6865
6930
|
cache3.headers.cookie = cookieStr;
|
|
6866
6931
|
cache3.lastRefresh = Date.now();
|
|
6932
|
+
cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
|
|
6867
6933
|
markAccountHeadersReady(accountId);
|
|
6868
6934
|
saveAuthSession2(accountId, {
|
|
6869
6935
|
cookie: cookieStr,
|
|
@@ -7918,11 +7984,11 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7918
7984
|
const now = Date.now();
|
|
7919
7985
|
const currentUrl = page.url();
|
|
7920
7986
|
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
7921
|
-
const cookie = await getCookies(accountId);
|
|
7987
|
+
const cookie = await getCookies(accountId, true);
|
|
7922
7988
|
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
7923
7989
|
lastProactiveTokenRefresh.set(accountId, now);
|
|
7924
|
-
|
|
7925
|
-
|
|
7990
|
+
logger.debug(
|
|
7991
|
+
`[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
7926
7992
|
);
|
|
7927
7993
|
const res = await refreshAccountToken(accountId);
|
|
7928
7994
|
if (res.success) {
|
|
@@ -7930,6 +7996,9 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7930
7996
|
touchAccountActivity(accountId);
|
|
7931
7997
|
return true;
|
|
7932
7998
|
}
|
|
7999
|
+
logger.warn(
|
|
8000
|
+
`[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
|
|
8001
|
+
);
|
|
7933
8002
|
}
|
|
7934
8003
|
const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
|
|
7935
8004
|
const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
|
|
@@ -8167,6 +8236,7 @@ var init_playwright = __esm({
|
|
|
8167
8236
|
init_qwen_headers();
|
|
8168
8237
|
init_paths();
|
|
8169
8238
|
init_jwt();
|
|
8239
|
+
init_timezone();
|
|
8170
8240
|
requireLocal = createRequire(import.meta.url);
|
|
8171
8241
|
contextInitHooks = [];
|
|
8172
8242
|
ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
|
|
@@ -8180,7 +8250,7 @@ var init_playwright = __esm({
|
|
|
8180
8250
|
headerCaches = /* @__PURE__ */ new Map();
|
|
8181
8251
|
HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
8182
8252
|
HEADER_REFRESH_THRESHOLD = 0.8;
|
|
8183
|
-
COOKIE_CACHE_TTL =
|
|
8253
|
+
COOKIE_CACHE_TTL = 60 * 1e3;
|
|
8184
8254
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
8185
8255
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
8186
8256
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
@@ -9599,8 +9669,8 @@ function createUsageAccumulator(estimatedPromptTokens) {
|
|
|
9599
9669
|
function applyUpstreamUsage(accumulator, candidate) {
|
|
9600
9670
|
if (!candidate || typeof candidate !== "object") return;
|
|
9601
9671
|
const usage = candidate;
|
|
9602
|
-
const promptTokens = asFiniteNumber(usage.input_tokens);
|
|
9603
|
-
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);
|
|
9604
9674
|
const totalTokens = asFiniteNumber(usage.total_tokens);
|
|
9605
9675
|
if (promptTokens !== null) {
|
|
9606
9676
|
accumulator.promptTokens = promptTokens;
|
|
@@ -11632,6 +11702,8 @@ __export(qwen_exports, {
|
|
|
11632
11702
|
QwenUpstreamError: () => QwenUpstreamError,
|
|
11633
11703
|
QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
|
|
11634
11704
|
RetryableQwenStreamError: () => RetryableQwenStreamError,
|
|
11705
|
+
STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
|
|
11706
|
+
acquireAccountStreamLock: () => acquireAccountStreamLock,
|
|
11635
11707
|
acquireNewQwenChatSession: () => acquireNewQwenChatSession,
|
|
11636
11708
|
buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
|
|
11637
11709
|
buildChatNewBody: () => buildChatNewBody,
|
|
@@ -11645,10 +11717,13 @@ __export(qwen_exports, {
|
|
|
11645
11717
|
deleteAllQwenChats: () => deleteAllQwenChats,
|
|
11646
11718
|
deleteSingleQwenChat: () => deleteSingleQwenChat,
|
|
11647
11719
|
disableNativeTools: () => disableNativeTools,
|
|
11720
|
+
ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
|
|
11648
11721
|
fetchQwenModels: () => fetchQwenModels,
|
|
11649
11722
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
11650
11723
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
11724
|
+
getAccountStreamMutex: () => getAccountStreamMutex,
|
|
11651
11725
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
11726
|
+
getBrowserFetchHeaders: () => getBrowserFetchHeaders,
|
|
11652
11727
|
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
11653
11728
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
11654
11729
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
@@ -11664,6 +11739,8 @@ __export(qwen_exports, {
|
|
|
11664
11739
|
requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
|
|
11665
11740
|
requestQwenTextInBrowser: () => requestQwenTextInBrowser,
|
|
11666
11741
|
setToolCapNotice: () => setToolCapNotice,
|
|
11742
|
+
stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
|
|
11743
|
+
sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
|
|
11667
11744
|
syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
|
|
11668
11745
|
updateLogicalThreadParent: () => updateLogicalThreadParent,
|
|
11669
11746
|
updateLogicalThreadState: () => updateLogicalThreadState,
|
|
@@ -11950,7 +12027,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
|
|
|
11950
12027
|
function getAccountStreamMutex(accountId) {
|
|
11951
12028
|
let slots2 = accountStreamMutexes.get(accountId);
|
|
11952
12029
|
if (!slots2) {
|
|
11953
|
-
slots2 = {
|
|
12030
|
+
slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
|
|
11954
12031
|
accountStreamMutexes.set(accountId, slots2);
|
|
11955
12032
|
}
|
|
11956
12033
|
return slots2;
|
|
@@ -11958,27 +12035,71 @@ function getAccountStreamMutex(accountId) {
|
|
|
11958
12035
|
function streamSlotCapacity() {
|
|
11959
12036
|
return Math.max(1, config.concurrency.maxStreamsPerAccount);
|
|
11960
12037
|
}
|
|
11961
|
-
function createStreamSlotRelease(slots2) {
|
|
12038
|
+
function createStreamSlotRelease(slots2, slotId) {
|
|
11962
12039
|
let released = false;
|
|
11963
12040
|
return () => {
|
|
11964
12041
|
if (released) return;
|
|
11965
12042
|
released = true;
|
|
11966
|
-
slots2.
|
|
12043
|
+
slots2.activeSlots.delete(slotId);
|
|
11967
12044
|
const next = slots2.queue.shift();
|
|
11968
12045
|
if (next) {
|
|
11969
|
-
|
|
11970
|
-
|
|
12046
|
+
const nextId = nextStreamSlotId++;
|
|
12047
|
+
slots2.activeSlots.set(nextId, Date.now());
|
|
12048
|
+
next(createStreamSlotRelease(slots2, nextId));
|
|
11971
12049
|
}
|
|
11972
12050
|
};
|
|
11973
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
|
+
}
|
|
11974
12093
|
async function acquireAccountStreamLock(accountId) {
|
|
12094
|
+
ensureStaleSlotSweeperStarted();
|
|
11975
12095
|
const slots2 = getAccountStreamMutex(accountId);
|
|
11976
|
-
if (slots2.
|
|
11977
|
-
|
|
11978
|
-
|
|
12096
|
+
if (slots2.activeSlots.size < streamSlotCapacity()) {
|
|
12097
|
+
const slotId = nextStreamSlotId++;
|
|
12098
|
+
slots2.activeSlots.set(slotId, Date.now());
|
|
12099
|
+
return Promise.resolve(createStreamSlotRelease(slots2, slotId));
|
|
11979
12100
|
}
|
|
11980
12101
|
return new Promise((resolve) => {
|
|
11981
|
-
slots2.queue.push(() => resolve(
|
|
12102
|
+
slots2.queue.push((releaseFn) => resolve(releaseFn));
|
|
11982
12103
|
});
|
|
11983
12104
|
}
|
|
11984
12105
|
function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
|
|
@@ -12070,11 +12191,13 @@ function getBrowserFetchHeaders(headers) {
|
|
|
12070
12191
|
"x-request-id",
|
|
12071
12192
|
"x-accel-buffering"
|
|
12072
12193
|
]);
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
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;
|
|
12078
12201
|
}
|
|
12079
12202
|
async function requestQwenTextInBrowser(accountId, method, path21, headers, body, options = {}) {
|
|
12080
12203
|
const url = qwenUrl(path21);
|
|
@@ -12095,7 +12218,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12095
12218
|
browserHeaders["Content-Type"] = "application/json";
|
|
12096
12219
|
}
|
|
12097
12220
|
const evaluateRequest = (page) => page.evaluate(
|
|
12098
|
-
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
|
|
12221
|
+
async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
|
|
12099
12222
|
const controller = new AbortController();
|
|
12100
12223
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12101
12224
|
try {
|
|
@@ -12119,7 +12242,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12119
12242
|
source: "web",
|
|
12120
12243
|
version: "0.3.11",
|
|
12121
12244
|
"x-request-origin": "https://chat.qwen.ai",
|
|
12122
|
-
timezone:
|
|
12245
|
+
timezone: tz,
|
|
12123
12246
|
authorization: `Bearer ${rTok}`
|
|
12124
12247
|
},
|
|
12125
12248
|
signal: AbortSignal.timeout(5e3)
|
|
@@ -12164,7 +12287,8 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
|
|
|
12164
12287
|
headers: browserHeaders,
|
|
12165
12288
|
body,
|
|
12166
12289
|
referrer: options.referrer,
|
|
12167
|
-
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
|
|
12290
|
+
timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
|
|
12291
|
+
tz: formatAsciiTimezone()
|
|
12168
12292
|
}
|
|
12169
12293
|
);
|
|
12170
12294
|
const recoverOnTimeout = !options.noMutexRecovery;
|
|
@@ -12842,7 +12966,7 @@ async function disableNativeTools(accountId) {
|
|
|
12842
12966
|
async (page) => {
|
|
12843
12967
|
const requestId = crypto7.randomUUID();
|
|
12844
12968
|
const response = await page.evaluate(
|
|
12845
|
-
async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
|
|
12969
|
+
async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
|
|
12846
12970
|
const controller = new AbortController();
|
|
12847
12971
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
12848
12972
|
try {
|
|
@@ -12854,7 +12978,7 @@ async function disableNativeTools(accountId) {
|
|
|
12854
12978
|
accept: "application/json, text/plain, */*",
|
|
12855
12979
|
"content-type": "application/json",
|
|
12856
12980
|
"x-request-id": requestId2,
|
|
12857
|
-
timezone:
|
|
12981
|
+
timezone: tz,
|
|
12858
12982
|
source: "web"
|
|
12859
12983
|
},
|
|
12860
12984
|
body: JSON.stringify(payload2),
|
|
@@ -12866,7 +12990,7 @@ async function disableNativeTools(accountId) {
|
|
|
12866
12990
|
clearTimeout(timeoutId);
|
|
12867
12991
|
}
|
|
12868
12992
|
},
|
|
12869
|
-
{ payload, timeoutMs: config.timeouts.http, requestId }
|
|
12993
|
+
{ payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
12870
12994
|
);
|
|
12871
12995
|
return response;
|
|
12872
12996
|
}
|
|
@@ -13083,7 +13207,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
|
|
|
13083
13207
|
const requestHeaders = {
|
|
13084
13208
|
source: "web",
|
|
13085
13209
|
version: "0.2.89",
|
|
13086
|
-
timezone: (
|
|
13210
|
+
timezone: formatAsciiTimezone(),
|
|
13087
13211
|
"x-request-id": crypto7.randomUUID(),
|
|
13088
13212
|
Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
|
|
13089
13213
|
};
|
|
@@ -13127,7 +13251,7 @@ async function fetchRemoteQwenChats(accountId) {
|
|
|
13127
13251
|
}
|
|
13128
13252
|
const requestHeaders = {
|
|
13129
13253
|
version: "0.2.89",
|
|
13130
|
-
timezone: (
|
|
13254
|
+
timezone: formatAsciiTimezone(),
|
|
13131
13255
|
"x-request-id": crypto7.randomUUID(),
|
|
13132
13256
|
Referer: qwenUrl("/settings/chats")
|
|
13133
13257
|
};
|
|
@@ -13179,7 +13303,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13179
13303
|
async (page) => {
|
|
13180
13304
|
const requestId = crypto7.randomUUID();
|
|
13181
13305
|
const response2 = await page.evaluate(
|
|
13182
|
-
async ({ timeoutMs, requestId: requestId2 }) => {
|
|
13306
|
+
async ({ timeoutMs, requestId: requestId2, tz }) => {
|
|
13183
13307
|
const controller = new AbortController();
|
|
13184
13308
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
13185
13309
|
try {
|
|
@@ -13188,7 +13312,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13188
13312
|
headers: {
|
|
13189
13313
|
accept: "application/json, text/plain, */*",
|
|
13190
13314
|
"x-request-id": requestId2,
|
|
13191
|
-
timezone:
|
|
13315
|
+
timezone: tz,
|
|
13192
13316
|
source: "web"
|
|
13193
13317
|
},
|
|
13194
13318
|
signal: controller.signal
|
|
@@ -13198,7 +13322,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13198
13322
|
clearTimeout(timeoutId);
|
|
13199
13323
|
}
|
|
13200
13324
|
},
|
|
13201
|
-
{ timeoutMs: config.timeouts.http, requestId }
|
|
13325
|
+
{ timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
|
|
13202
13326
|
);
|
|
13203
13327
|
return response2;
|
|
13204
13328
|
}
|
|
@@ -13237,7 +13361,7 @@ async function fetchQwenModels(accountId) {
|
|
|
13237
13361
|
bxUa,
|
|
13238
13362
|
bxUmidtoken,
|
|
13239
13363
|
extra: {
|
|
13240
|
-
timezone: (
|
|
13364
|
+
timezone: formatAsciiTimezone()
|
|
13241
13365
|
}
|
|
13242
13366
|
}),
|
|
13243
13367
|
void 0,
|
|
@@ -14000,7 +14124,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
14000
14124
|
clearTimeout(timeoutId);
|
|
14001
14125
|
}
|
|
14002
14126
|
}
|
|
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;
|
|
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;
|
|
14004
14128
|
var init_qwen = __esm({
|
|
14005
14129
|
"src/services/qwen.ts"() {
|
|
14006
14130
|
"use strict";
|
|
@@ -14018,6 +14142,7 @@ var init_qwen = __esm({
|
|
|
14018
14142
|
init_captcha_coordinator();
|
|
14019
14143
|
init_captcha_solver();
|
|
14020
14144
|
init_account_concurrency();
|
|
14145
|
+
init_timezone();
|
|
14021
14146
|
init_qwen_errors();
|
|
14022
14147
|
init_qwen_thread_state();
|
|
14023
14148
|
init_qwen_chat_pool();
|
|
@@ -14047,7 +14172,10 @@ var init_qwen = __esm({
|
|
|
14047
14172
|
directSettingsFetchBlocked = /* @__PURE__ */ new Set();
|
|
14048
14173
|
DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
|
|
14049
14174
|
activePersonalizationByAccount = /* @__PURE__ */ new Map();
|
|
14175
|
+
STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
|
|
14050
14176
|
accountStreamMutexes = /* @__PURE__ */ new Map();
|
|
14177
|
+
nextStreamSlotId = 1;
|
|
14178
|
+
staleSlotSweeperTimer = null;
|
|
14051
14179
|
QWEN_SAFE_SETTINGS_PATCH = {
|
|
14052
14180
|
ui: {
|
|
14053
14181
|
autoTags: false,
|
|
@@ -16777,7 +16905,7 @@ function buildContextMeterSnapshot(input, options = getContextMeterOptions()) {
|
|
|
16777
16905
|
function enrichUsageWithContextMeter(usage, snapshot, options = getContextMeterOptions()) {
|
|
16778
16906
|
if (!snapshot || !options.enabled) return usage;
|
|
16779
16907
|
const upstreamPromptTokens = Number.isFinite(usage.prompt_tokens) && usage.prompt_tokens > 0 ? usage.prompt_tokens : null;
|
|
16780
|
-
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ??
|
|
16908
|
+
const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ?? usage.prompt_tokens : usage.prompt_tokens;
|
|
16781
16909
|
const contextMeter = {
|
|
16782
16910
|
...snapshot,
|
|
16783
16911
|
upstreamPromptTokens,
|
|
@@ -20405,6 +20533,35 @@ var init_parser = __esm({
|
|
|
20405
20533
|
}
|
|
20406
20534
|
});
|
|
20407
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
|
+
|
|
20408
20565
|
// src/routes/chat/errors.ts
|
|
20409
20566
|
function isWafChallenge(value) {
|
|
20410
20567
|
const normalized = value.toLowerCase();
|
|
@@ -21104,10 +21261,17 @@ async function processNonStreamingResponse(params) {
|
|
|
21104
21261
|
message.tool_calls = toolCallsOut;
|
|
21105
21262
|
}
|
|
21106
21263
|
const finishReason = toolCallsOut.length ? "tool_calls" : "stop";
|
|
21107
|
-
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;
|
|
21108
21272
|
const malformedRetryCount = midStreamRetry?.malformedRetryCount ?? 0;
|
|
21109
21273
|
if (allToolsFailed && config.retry.autoRetryMalformedTools !== false && midStreamRetry && malformedRetryCount < config.retry.autoRetryMalformedToolsMax) {
|
|
21110
|
-
const malformedCalls = toolParser
|
|
21274
|
+
const malformedCalls = toolParser?.getMalformedToolCalls() ?? [];
|
|
21111
21275
|
const malformedCount = malformedCalls.length;
|
|
21112
21276
|
const undeclaredNames = malformedCalls.flatMap((mc) => mc.undeclaredNames || []).filter((name, index, self) => self.indexOf(name) === index);
|
|
21113
21277
|
const availableToolNames = declaredTools.map((t) => t.type === "function" ? t.function?.name : t.name).filter((n) => !!n);
|
|
@@ -21115,7 +21279,9 @@ async function processNonStreamingResponse(params) {
|
|
|
21115
21279
|
|
|
21116
21280
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
21117
21281
|
let errorMessage;
|
|
21118
|
-
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) {
|
|
21119
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}`;
|
|
21120
21286
|
} else {
|
|
21121
21287
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -21191,7 +21357,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21191
21357
|
new Error("client aborted during malformed-tool retry")
|
|
21192
21358
|
);
|
|
21193
21359
|
}
|
|
21194
|
-
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"}`);
|
|
21195
21361
|
return processNonStreamingResponse({
|
|
21196
21362
|
...params,
|
|
21197
21363
|
stream: newStreamResult.stream,
|
|
@@ -22287,7 +22453,14 @@ async function processStreamingResponse(params) {
|
|
|
22287
22453
|
let activeRetryStream = null;
|
|
22288
22454
|
while (malformedRetryCount < maxMalformedRetries) {
|
|
22289
22455
|
if (!toolParser) break;
|
|
22290
|
-
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;
|
|
22291
22464
|
if (!allToolsFailed) break;
|
|
22292
22465
|
const malformedCalls = toolParser.getMalformedToolCalls();
|
|
22293
22466
|
const malformedCount = malformedCalls.length;
|
|
@@ -22297,7 +22470,9 @@ async function processStreamingResponse(params) {
|
|
|
22297
22470
|
|
|
22298
22471
|
Available tools: ${availableToolNames.join(", ")}` : "";
|
|
22299
22472
|
let errorMessage;
|
|
22300
|
-
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) {
|
|
22301
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}`;
|
|
22302
22477
|
} else {
|
|
22303
22478
|
const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
|
|
@@ -22374,7 +22549,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
22374
22549
|
break;
|
|
22375
22550
|
}
|
|
22376
22551
|
console.log(
|
|
22377
|
-
`\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"}`
|
|
22378
22553
|
);
|
|
22379
22554
|
const retryEntry = getStream(newStreamResult.completionId);
|
|
22380
22555
|
removeStream(newStreamResult.completionId);
|
|
@@ -22875,6 +23050,7 @@ var init_streaming = __esm({
|
|
|
22875
23050
|
init_retry_policy();
|
|
22876
23051
|
init_account();
|
|
22877
23052
|
init_parser();
|
|
23053
|
+
init_tool_call_heuristics();
|
|
22878
23054
|
init_stream_registry();
|
|
22879
23055
|
init_metrics();
|
|
22880
23056
|
init_logger();
|
|
@@ -24283,7 +24459,7 @@ function chatCompletionsToResponses(chatRes, requestModel, originalRequest) {
|
|
|
24283
24459
|
cached_tokens: chatRes.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
24284
24460
|
},
|
|
24285
24461
|
output_tokens_details: {
|
|
24286
|
-
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
|
|
24287
24463
|
},
|
|
24288
24464
|
...chatRes.usage.context_meter ? { context_meter: chatRes.usage.context_meter } : {}
|
|
24289
24465
|
};
|
|
@@ -25744,7 +25920,7 @@ function translateOpenAIToAnthropic(openaiResponse, requestModel) {
|
|
|
25744
25920
|
content_filter: "end_turn"
|
|
25745
25921
|
};
|
|
25746
25922
|
const hasToolUse = content.some((b) => b.type === "tool_use");
|
|
25747
|
-
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";
|
|
25748
25924
|
return {
|
|
25749
25925
|
id: generateMessageId2(),
|
|
25750
25926
|
type: "message",
|
|
@@ -25882,7 +26058,7 @@ function translateStreamChunk(chunk, state) {
|
|
|
25882
26058
|
state.contentBlockIndex++;
|
|
25883
26059
|
state.currentBlockType = null;
|
|
25884
26060
|
}
|
|
25885
|
-
const stopReason =
|
|
26061
|
+
const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : state.hasEmittedToolUse ? "tool_use" : "end_turn";
|
|
25886
26062
|
events.push(
|
|
25887
26063
|
JSON.stringify({
|
|
25888
26064
|
type: "message_delta",
|
|
@@ -26828,7 +27004,7 @@ function getAppVersion() {
|
|
|
26828
27004
|
}
|
|
26829
27005
|
} catch {
|
|
26830
27006
|
}
|
|
26831
|
-
cachedVersion = "v1.6.
|
|
27007
|
+
cachedVersion = "v1.6.5";
|
|
26832
27008
|
return cachedVersion;
|
|
26833
27009
|
}
|
|
26834
27010
|
var cachedVersion;
|