qwenproxy-cli 1.5.3 → 1.6.0
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 +205 -22
- package/dist/delete-chats.js +151 -33
- package/dist/index.js +441 -51
- package/dist/login.js +95 -24
- package/dist/reset-cooldowns.js +205 -22
- package/package.json +3 -3
- package/src/api/server.ts +1 -0
- package/src/core/daily-usage.ts +276 -0
- package/src/core/database.ts +27 -1
- package/src/core/version.ts +1 -1
- package/src/routes/chat/account.ts +33 -5
- package/src/routes/chat/index.ts +1 -1
- package/src/routes/chat/streaming.ts +31 -3
- package/src/services/captcha-coordinator.ts +12 -7
- package/src/services/playwright.ts +76 -24
- package/src/services/qwen.ts +65 -9
- package/src/tui/proxy-client.ts +11 -1
- package/src/tui/types.ts +11 -0
- package/src/tui/views/accounts-view.ts +19 -2
- package/src/tui/views/status-view.ts +37 -4
package/dist/index.js
CHANGED
|
@@ -1592,7 +1592,26 @@ function runMigrations(db2) {
|
|
|
1592
1592
|
instruction_hash TEXT NOT NULL,
|
|
1593
1593
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1594
1594
|
);
|
|
1595
|
+
|
|
1596
|
+
CREATE TABLE IF NOT EXISTS account_daily_usage (
|
|
1597
|
+
account_id TEXT NOT NULL,
|
|
1598
|
+
date_utc TEXT NOT NULL,
|
|
1599
|
+
prompt_tokens INTEGER NOT NULL DEFAULT 0,
|
|
1600
|
+
completion_tokens INTEGER NOT NULL DEFAULT 0,
|
|
1601
|
+
weighted_tokens REAL NOT NULL DEFAULT 0,
|
|
1602
|
+
total_tokens INTEGER NOT NULL DEFAULT 0,
|
|
1603
|
+
turns_count INTEGER NOT NULL DEFAULT 0,
|
|
1604
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1605
|
+
PRIMARY KEY (account_id, date_utc)
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
CREATE INDEX IF NOT EXISTS idx_daily_usage_date ON account_daily_usage(date_utc);
|
|
1595
1609
|
`);
|
|
1610
|
+
try {
|
|
1611
|
+
db2.exec(`ALTER TABLE account_daily_usage ADD COLUMN weighted_tokens REAL DEFAULT 0;`);
|
|
1612
|
+
} catch (err) {
|
|
1613
|
+
if (!isDuplicateColumnError(err)) throw err;
|
|
1614
|
+
}
|
|
1596
1615
|
try {
|
|
1597
1616
|
db2.exec(
|
|
1598
1617
|
`ALTER TABLE accounts ADD COLUMN cooldown_until INTEGER DEFAULT 0;`
|
|
@@ -1693,7 +1712,11 @@ function saveAuthSession(accountId, session) {
|
|
|
1693
1712
|
) VALUES (
|
|
1694
1713
|
?, ?, ?, ?, ?, ?,
|
|
1695
1714
|
?, ?, ?, ?,
|
|
1696
|
-
?,
|
|
1715
|
+
COALESCE(?, (SELECT user_id FROM qwen_auth_sessions WHERE account_id = ?)),
|
|
1716
|
+
?,
|
|
1717
|
+
COALESCE(?, (SELECT refresh_token FROM qwen_auth_sessions WHERE account_id = ?)),
|
|
1718
|
+
?,
|
|
1719
|
+
datetime('now')
|
|
1697
1720
|
)
|
|
1698
1721
|
`);
|
|
1699
1722
|
stmt.run(
|
|
@@ -1708,8 +1731,10 @@ function saveAuthSession(accountId, session) {
|
|
|
1708
1731
|
session.secChUaPlatform || null,
|
|
1709
1732
|
session.version || null,
|
|
1710
1733
|
session.userId || null,
|
|
1734
|
+
accountId,
|
|
1711
1735
|
session.tokenExpiresAt || null,
|
|
1712
1736
|
session.refreshToken || null,
|
|
1737
|
+
accountId,
|
|
1713
1738
|
session.capturedAt ?? Date.now()
|
|
1714
1739
|
);
|
|
1715
1740
|
}
|
|
@@ -5194,6 +5219,11 @@ async function getBasicHeaders(accountId) {
|
|
|
5194
5219
|
if (!page) {
|
|
5195
5220
|
throw new Error(`Playwright not initialized for account: ${accountId}`);
|
|
5196
5221
|
}
|
|
5222
|
+
const inFlightInit = inFlightAccountInits.get(accountId);
|
|
5223
|
+
if (inFlightInit) {
|
|
5224
|
+
await inFlightInit.catch(() => {
|
|
5225
|
+
});
|
|
5226
|
+
}
|
|
5197
5227
|
const release = await acquireAccountMutex(
|
|
5198
5228
|
accountId,
|
|
5199
5229
|
`headers:${accountId.substring(0, 12)}`
|
|
@@ -6668,6 +6698,11 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
|
|
|
6668
6698
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
6669
6699
|
const { parseJwtExpiry: parseJwtExpiry2 } = await Promise.resolve().then(() => (init_jwt(), jwt_exports));
|
|
6670
6700
|
const tokenExpiry = parseJwtExpiry2(capturedHeaders.cookie);
|
|
6701
|
+
let rTok;
|
|
6702
|
+
try {
|
|
6703
|
+
rTok = (await page.evaluate(() => localStorage.getItem("refresh_token") || "")).trim() || void 0;
|
|
6704
|
+
} catch {
|
|
6705
|
+
}
|
|
6671
6706
|
saveAuthSession2(accountId, {
|
|
6672
6707
|
cookie: capturedHeaders.cookie,
|
|
6673
6708
|
userAgent: capturedHeaders["user-agent"],
|
|
@@ -6679,6 +6714,7 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
|
|
|
6679
6714
|
secChUaPlatform: capturedHeaders["sec-ch-ua-platform"],
|
|
6680
6715
|
version: capturedHeaders["version"],
|
|
6681
6716
|
tokenExpiresAt: tokenExpiry || void 0,
|
|
6717
|
+
refreshToken: rTok,
|
|
6682
6718
|
capturedAt: Date.now()
|
|
6683
6719
|
});
|
|
6684
6720
|
} catch {
|
|
@@ -6710,38 +6746,57 @@ async function captureQwenHeaders(accountId, pageOverride, timeoutMs = config.ti
|
|
|
6710
6746
|
const isAuthUrl = currentUrl.includes("/auth") || currentUrl.includes("/login");
|
|
6711
6747
|
const isSuspectedGuest = isAuthUrl || attempt >= 2 && !await isPageLoggedIn(page, 5e3);
|
|
6712
6748
|
if (isSuspectedGuest) {
|
|
6713
|
-
const
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
`\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
|
|
6749
|
+
const silentRefresh = await refreshAccountToken(accountId);
|
|
6750
|
+
if (silentRefresh.success) {
|
|
6751
|
+
console.log(
|
|
6752
|
+
`\u26A1 [Playwright] Token refreshed silently during header capture for ${accountId}`
|
|
6718
6753
|
);
|
|
6719
|
-
|
|
6720
|
-
const ok = await loginToQwen(accountId, creds.email, creds.password);
|
|
6721
|
-
if (ok) {
|
|
6754
|
+
if (isAuthUrl) {
|
|
6722
6755
|
await page.goto(qwenUrl("/"), {
|
|
6723
6756
|
waitUntil: "domcontentloaded",
|
|
6724
6757
|
timeout: Math.min(config.timeouts.navigation, timeoutMs)
|
|
6725
6758
|
});
|
|
6726
|
-
await sleep2(
|
|
6759
|
+
await sleep2(1500);
|
|
6727
6760
|
}
|
|
6728
|
-
if (
|
|
6761
|
+
if (await isPageLoggedIn(page, 5e3)) {
|
|
6762
|
+
armOverallDeadline();
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
const stillSuspected = !silentRefresh.success || !await isPageLoggedIn(page, 2e3);
|
|
6766
|
+
if (stillSuspected) {
|
|
6767
|
+
const { getAccountCredentials: getAccountCredentials2 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
|
|
6768
|
+
const creds = getAccountCredentials2(accountId);
|
|
6769
|
+
if (creds && creds.email && creds.password) {
|
|
6770
|
+
console.warn(
|
|
6771
|
+
`\u26A0\uFE0F [Playwright] Session expired or guest state detected during header capture for ${accountId}; re-authenticating...`
|
|
6772
|
+
);
|
|
6773
|
+
armOverallDeadline();
|
|
6774
|
+
const ok = await loginToQwen(accountId, creds.email, creds.password);
|
|
6775
|
+
if (ok) {
|
|
6776
|
+
await page.goto(qwenUrl("/"), {
|
|
6777
|
+
waitUntil: "domcontentloaded",
|
|
6778
|
+
timeout: Math.min(config.timeouts.navigation, timeoutMs)
|
|
6779
|
+
});
|
|
6780
|
+
await sleep2(2e3);
|
|
6781
|
+
}
|
|
6782
|
+
if (!ok || !await isPageLoggedIn(page, 6e3)) {
|
|
6783
|
+
settle(
|
|
6784
|
+
new Error(
|
|
6785
|
+
`Header capture failed for ${accountId}: re-login after session expiry did not succeed`
|
|
6786
|
+
)
|
|
6787
|
+
);
|
|
6788
|
+
return;
|
|
6789
|
+
}
|
|
6790
|
+
armOverallDeadline();
|
|
6791
|
+
if (settled || page.isClosed()) return;
|
|
6792
|
+
} else {
|
|
6729
6793
|
settle(
|
|
6730
6794
|
new Error(
|
|
6731
|
-
`Header capture failed for ${accountId}: re-login
|
|
6795
|
+
`Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
|
|
6732
6796
|
)
|
|
6733
6797
|
);
|
|
6734
6798
|
return;
|
|
6735
6799
|
}
|
|
6736
|
-
armOverallDeadline();
|
|
6737
|
-
if (settled || page.isClosed()) return;
|
|
6738
|
-
} else {
|
|
6739
|
-
settle(
|
|
6740
|
-
new Error(
|
|
6741
|
-
`Header capture failed for ${accountId}: session expired and no credentials available for re-login (run 'qpx login')`
|
|
6742
|
-
)
|
|
6743
|
-
);
|
|
6744
|
-
return;
|
|
6745
6800
|
}
|
|
6746
6801
|
}
|
|
6747
6802
|
if (settled || page.isClosed()) return;
|
|
@@ -7057,7 +7112,13 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
7057
7112
|
`[Playwright] Navigation check failed during refresh for ${accountId}:`,
|
|
7058
7113
|
navErr.message
|
|
7059
7114
|
);
|
|
7060
|
-
await
|
|
7115
|
+
if (await isPageLoggedIn(page, 3e3)) {
|
|
7116
|
+
} else {
|
|
7117
|
+
const refreshed = await refreshAccountToken(accountId);
|
|
7118
|
+
if (!refreshed.success) {
|
|
7119
|
+
await executeReauth();
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7061
7122
|
}
|
|
7062
7123
|
}
|
|
7063
7124
|
if (hasRequiredQwenHeaders(cache3.headers)) {
|
|
@@ -7071,6 +7132,11 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
7071
7132
|
const { saveAuthSession: saveAuthSession2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
7072
7133
|
const tokenCookie = liveCookies.find((c) => c.name === "token");
|
|
7073
7134
|
const exp = tokenCookie ? parseJwtExpiry(tokenCookie.value) : void 0;
|
|
7135
|
+
let rTok;
|
|
7136
|
+
try {
|
|
7137
|
+
rTok = (await page.evaluate(() => localStorage.getItem("refresh_token") || "")).trim() || void 0;
|
|
7138
|
+
} catch {
|
|
7139
|
+
}
|
|
7074
7140
|
saveAuthSession2(accountId, {
|
|
7075
7141
|
cookie: cookieStr,
|
|
7076
7142
|
userAgent: cache3.headers["user-agent"] || "",
|
|
@@ -7082,6 +7148,7 @@ async function refreshHeadersInternal(accountId, timeoutMs = config.timeouts.hea
|
|
|
7082
7148
|
secChUaPlatform: cache3.headers["sec-ch-ua-platform"] || void 0,
|
|
7083
7149
|
version: cache3.headers["version"] || void 0,
|
|
7084
7150
|
tokenExpiresAt: exp || void 0,
|
|
7151
|
+
refreshToken: rTok,
|
|
7085
7152
|
capturedAt: Date.now()
|
|
7086
7153
|
});
|
|
7087
7154
|
} catch {
|
|
@@ -7468,8 +7535,10 @@ async function keepAlivePlaywrightAccount(accountId) {
|
|
|
7468
7535
|
if (!page || page.isClosed()) return false;
|
|
7469
7536
|
const now = Date.now();
|
|
7470
7537
|
const currentUrl = page.url();
|
|
7538
|
+
const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
|
|
7471
7539
|
const cookie = await getCookies(accountId);
|
|
7472
|
-
if (cookie && isTokenExpiringSoon(cookie, 5)) {
|
|
7540
|
+
if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
|
|
7541
|
+
lastProactiveTokenRefresh.set(accountId, now);
|
|
7473
7542
|
console.log(
|
|
7474
7543
|
`\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
|
|
7475
7544
|
);
|
|
@@ -7513,6 +7582,7 @@ function cleanupPlaywrightAccountState(accountId) {
|
|
|
7513
7582
|
cookieCaches.delete(accountId);
|
|
7514
7583
|
lastAccountActivity.delete(accountId);
|
|
7515
7584
|
lastKeepAliveNavigation.delete(accountId);
|
|
7585
|
+
lastProactiveTokenRefresh.delete(accountId);
|
|
7516
7586
|
clearFingerprintCache(accountId);
|
|
7517
7587
|
unmarkAccountHeadersReady(accountId);
|
|
7518
7588
|
}
|
|
@@ -7696,7 +7766,7 @@ async function getTokenDiagnostics(accountId) {
|
|
|
7696
7766
|
}
|
|
7697
7767
|
};
|
|
7698
7768
|
}
|
|
7699
|
-
var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
|
|
7769
|
+
var requireLocal, contextInitHooks, ACCOUNT_MUTEX_MAX_HOLD_MS, accountMutexes, accountContexts, accountPages, cachedUserAgents, inFlightAccountInits, sharedBrowser, sharedBrowserPromise, headerCaches, HEADER_CACHE_TTL, HEADER_REFRESH_THRESHOLD, COOKIE_CACHE_TTL, cookieCaches, lastAccountActivity, lastKeepAliveNavigation, lastProactiveTokenRefresh, profileResetQueue, profileResetChain, closingAllPlaywright, sleep2, HEADER_CAPTURE_SETTLE_MS, PLAYWRIGHT_MUTEX_WAIT_MS, ACCOUNT_PAGE_OPERATION_TIMEOUT_MS, SESSION_PROBE_NAVIGATION_TIMEOUT_MS, HEADER_CAPTURE_TRIGGER_GRACE_MS, FIRST_TRIGGER_GRACE_MS, HEADER_CAPTURE_TRIGGER_ATTEMPTS, CHAT_INPUT_APPEAR_TIMEOUT_MS, CHAT_INPUT_ACTION_TIMEOUT_MS, PROFILE_RESET_TIMEOUT_MS;
|
|
7700
7770
|
var init_playwright = __esm({
|
|
7701
7771
|
"src/services/playwright.ts"() {
|
|
7702
7772
|
"use strict";
|
|
@@ -7732,6 +7802,7 @@ var init_playwright = __esm({
|
|
|
7732
7802
|
cookieCaches = /* @__PURE__ */ new Map();
|
|
7733
7803
|
lastAccountActivity = /* @__PURE__ */ new Map();
|
|
7734
7804
|
lastKeepAliveNavigation = /* @__PURE__ */ new Map();
|
|
7805
|
+
lastProactiveTokenRefresh = /* @__PURE__ */ new Map();
|
|
7735
7806
|
profileResetQueue = /* @__PURE__ */ new Map();
|
|
7736
7807
|
profileResetChain = Promise.resolve();
|
|
7737
7808
|
closingAllPlaywright = false;
|
|
@@ -8547,9 +8618,13 @@ var init_model_registry = __esm({
|
|
|
8547
8618
|
// src/services/captcha-coordinator.ts
|
|
8548
8619
|
var captcha_coordinator_exports = {};
|
|
8549
8620
|
__export(captcha_coordinator_exports, {
|
|
8621
|
+
isCaptchaRecoveryActive: () => isCaptchaRecoveryActive,
|
|
8550
8622
|
recoverBaxiaCaptcha: () => recoverBaxiaCaptcha,
|
|
8551
8623
|
solveChallengeOnPage: () => solveChallengeOnPage
|
|
8552
8624
|
});
|
|
8625
|
+
function isCaptchaRecoveryActive(accountId) {
|
|
8626
|
+
return activeCaptchaRecoveries.has(accountId);
|
|
8627
|
+
}
|
|
8553
8628
|
async function gotoBestEffort(page, url) {
|
|
8554
8629
|
await page.goto(url, {
|
|
8555
8630
|
waitUntil: "domcontentloaded",
|
|
@@ -8592,11 +8667,12 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
|
|
|
8592
8667
|
return false;
|
|
8593
8668
|
}
|
|
8594
8669
|
metrics.increment("captcha.challenges.detected", 1, { solver });
|
|
8595
|
-
const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
|
|
8596
8670
|
const solverOperationTimeoutMs = Math.max(
|
|
8597
8671
|
15e3,
|
|
8598
8672
|
config.captcha.timeoutMs + config.captcha.maxAttempts * (3e3 + config.captcha.retryDelayMs + config.captcha.settleMs) + 2 * CHALLENGE_NAVIGATION_TIMEOUT_MS
|
|
8599
8673
|
);
|
|
8674
|
+
const challengeUrl = options.challengeBody ? extractBaxiaChallengeUrl(options.challengeBody, config.qwen.baseUrl) : null;
|
|
8675
|
+
activeCaptchaRecoveries.add(accountId);
|
|
8600
8676
|
try {
|
|
8601
8677
|
const solved = await withAccountPage(
|
|
8602
8678
|
accountId,
|
|
@@ -8629,9 +8705,11 @@ async function recoverBaxiaCaptcha(accountId, label, options = {}) {
|
|
|
8629
8705
|
detail
|
|
8630
8706
|
});
|
|
8631
8707
|
return false;
|
|
8708
|
+
} finally {
|
|
8709
|
+
activeCaptchaRecoveries.delete(accountId);
|
|
8632
8710
|
}
|
|
8633
8711
|
}
|
|
8634
|
-
var CHALLENGE_NAVIGATION_TIMEOUT_MS, CHALLENGE_PATH_MARKER, FAILED_RECOVERY_BACKOFF_MS, lastFailedRecoveryAt;
|
|
8712
|
+
var CHALLENGE_NAVIGATION_TIMEOUT_MS, CHALLENGE_PATH_MARKER, FAILED_RECOVERY_BACKOFF_MS, lastFailedRecoveryAt, activeCaptchaRecoveries;
|
|
8635
8713
|
var init_captcha_coordinator = __esm({
|
|
8636
8714
|
"src/services/captcha-coordinator.ts"() {
|
|
8637
8715
|
"use strict";
|
|
@@ -8644,6 +8722,7 @@ var init_captcha_coordinator = __esm({
|
|
|
8644
8722
|
CHALLENGE_PATH_MARKER = "_____tmd_____";
|
|
8645
8723
|
FAILED_RECOVERY_BACKOFF_MS = 3e4;
|
|
8646
8724
|
lastFailedRecoveryAt = /* @__PURE__ */ new Map();
|
|
8725
|
+
activeCaptchaRecoveries = /* @__PURE__ */ new Set();
|
|
8647
8726
|
}
|
|
8648
8727
|
});
|
|
8649
8728
|
|
|
@@ -11110,6 +11189,7 @@ __export(qwen_exports, {
|
|
|
11110
11189
|
fetchRemoteQwenChats: () => fetchRemoteQwenChats,
|
|
11111
11190
|
flushLogicalThreadState: () => flushLogicalThreadState,
|
|
11112
11191
|
getAnyCachedQwenModels: () => getAnyCachedQwenModels,
|
|
11192
|
+
getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
|
|
11113
11193
|
getLogicalThreadState: () => getLogicalThreadState,
|
|
11114
11194
|
getQwenErrorCode: () => getQwenErrorCode,
|
|
11115
11195
|
getSessionParent: () => getSessionParent,
|
|
@@ -12042,12 +12122,51 @@ async function createQwenBrowserResponse(accountId, url, method, headers, body,
|
|
|
12042
12122
|
captchaWatcher?.stop();
|
|
12043
12123
|
}
|
|
12044
12124
|
}
|
|
12125
|
+
function getInFlightPersonalizationCount() {
|
|
12126
|
+
return inFlightPersonalizationSyncs.size;
|
|
12127
|
+
}
|
|
12045
12128
|
async function syncQwenRequestPersonalization(instruction, accountId, metadata = {}) {
|
|
12046
|
-
if (isAuthMockEnabled()) {
|
|
12047
|
-
if (process.env.TEST_PERSONALIZATION_SYNC_FAIL === "true") return false;
|
|
12048
|
-
return true;
|
|
12049
|
-
}
|
|
12050
12129
|
const cacheKey = accountId || "global";
|
|
12130
|
+
const sent = textSize(instruction);
|
|
12131
|
+
const syncHash = sent.hash ? `${sent.hash}:${QWEN_SAFE_SETTINGS_HASH}` : null;
|
|
12132
|
+
const inFlightKey = `${cacheKey}:${syncHash || "empty"}`;
|
|
12133
|
+
const existingInFlight = inFlightPersonalizationSyncs.get(inFlightKey);
|
|
12134
|
+
if (existingInFlight) {
|
|
12135
|
+
logger.debug("[Qwen] Personalization sync already in flight, sharing promise", {
|
|
12136
|
+
accountId: cacheKey
|
|
12137
|
+
});
|
|
12138
|
+
return existingInFlight;
|
|
12139
|
+
}
|
|
12140
|
+
let syncPromise;
|
|
12141
|
+
syncPromise = (async () => {
|
|
12142
|
+
await Promise.resolve();
|
|
12143
|
+
try {
|
|
12144
|
+
if (isAuthMockEnabled()) {
|
|
12145
|
+
const delay = parseInt(process.env.TEST_PERSONALIZATION_DELAY_MS || "0", 10);
|
|
12146
|
+
if (delay > 0) {
|
|
12147
|
+
await sleep5(delay);
|
|
12148
|
+
}
|
|
12149
|
+
if (process.env.TEST_PERSONALIZATION_SYNC_FAIL === "true") return false;
|
|
12150
|
+
return true;
|
|
12151
|
+
}
|
|
12152
|
+
return await syncQwenRequestPersonalizationInternal(
|
|
12153
|
+
instruction,
|
|
12154
|
+
accountId,
|
|
12155
|
+
metadata,
|
|
12156
|
+
cacheKey,
|
|
12157
|
+
sent,
|
|
12158
|
+
syncHash
|
|
12159
|
+
);
|
|
12160
|
+
} finally {
|
|
12161
|
+
if (inFlightPersonalizationSyncs.get(inFlightKey) === syncPromise) {
|
|
12162
|
+
inFlightPersonalizationSyncs.delete(inFlightKey);
|
|
12163
|
+
}
|
|
12164
|
+
}
|
|
12165
|
+
})();
|
|
12166
|
+
inFlightPersonalizationSyncs.set(inFlightKey, syncPromise);
|
|
12167
|
+
return syncPromise;
|
|
12168
|
+
}
|
|
12169
|
+
async function syncQwenRequestPersonalizationInternal(instruction, accountId, metadata, cacheKey, sent, syncHash) {
|
|
12051
12170
|
let forceRefresh = false;
|
|
12052
12171
|
try {
|
|
12053
12172
|
const basic = await getBasicHeaders2(accountId);
|
|
@@ -12065,8 +12184,6 @@ async function syncQwenRequestPersonalization(instruction, accountId, metadata =
|
|
|
12065
12184
|
});
|
|
12066
12185
|
let currentSettings = null;
|
|
12067
12186
|
let payload = buildQwenSettingsUpdatePayload(currentSettings, instruction);
|
|
12068
|
-
const sent = textSize(instruction);
|
|
12069
|
-
const syncHash = sent.hash ? `${sent.hash}:${QWEN_SAFE_SETTINGS_HASH}` : null;
|
|
12070
12187
|
const bypassCache = metadata.forceSync === true;
|
|
12071
12188
|
const cachedHash = lastSyncedPersonalizationHashes.get(cacheKey);
|
|
12072
12189
|
if (!bypassCache && syncHash && cachedHash === syncHash) {
|
|
@@ -13411,7 +13528,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
|
|
|
13411
13528
|
clearTimeout(timeoutId);
|
|
13412
13529
|
}
|
|
13413
13530
|
}
|
|
13414
|
-
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, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
|
|
13531
|
+
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;
|
|
13415
13532
|
var init_qwen = __esm({
|
|
13416
13533
|
"src/services/qwen.ts"() {
|
|
13417
13534
|
"use strict";
|
|
@@ -13498,6 +13615,7 @@ var init_qwen = __esm({
|
|
|
13498
13615
|
}
|
|
13499
13616
|
};
|
|
13500
13617
|
QWEN_SAFE_SETTINGS_HASH = crypto7.createHash("sha256").update(JSON.stringify(QWEN_SAFE_SETTINGS_PATCH)).digest("hex").slice(0, 12);
|
|
13618
|
+
inFlightPersonalizationSyncs = /* @__PURE__ */ new Map();
|
|
13501
13619
|
DISABLE_TOOLS_MAX_RETRIES = 3;
|
|
13502
13620
|
DISABLE_TOOLS_BACKOFF_MS = 2e3;
|
|
13503
13621
|
UPSTREAM_RESPONSE_PREVIEW_BYTES = 8 * 1024;
|
|
@@ -16430,8 +16548,9 @@ function resolveInitialAccount(preferredAccountId, excludeAccountIds) {
|
|
|
16430
16548
|
syncCooldownsFromDb(configuredAccounts);
|
|
16431
16549
|
const excluded = new Set(excludeAccountIds ?? []);
|
|
16432
16550
|
if (typeof preferredAccountId === "string" && preferredAccountId) {
|
|
16551
|
+
const needle = preferredAccountId.toLowerCase();
|
|
16433
16552
|
const preferred = configuredAccounts.find(
|
|
16434
|
-
(candidate) => candidate.id === preferredAccountId
|
|
16553
|
+
(candidate) => candidate.id === preferredAccountId || candidate.id.toLowerCase().startsWith(needle) || candidate.email.toLowerCase() === needle || candidate.email.toLowerCase().includes(needle)
|
|
16435
16554
|
);
|
|
16436
16555
|
if (preferred && !getAccountCooldownInfo(preferred.id)) {
|
|
16437
16556
|
return { account: preferred, configuredAccounts };
|
|
@@ -16786,7 +16905,12 @@ function shouldWaitQueueForever(isThreadOwnerWaiting, heldByOtherSession, hasFre
|
|
|
16786
16905
|
}
|
|
16787
16906
|
async function tryCreateStreamWithRetry(params, accountId, accountEmail) {
|
|
16788
16907
|
const maxAttempts = Math.max(1, config.retry.maxAttempts);
|
|
16789
|
-
const
|
|
16908
|
+
const accounts = loadAccounts();
|
|
16909
|
+
const configuredAccountCount = accounts.length;
|
|
16910
|
+
const maxAccountSwitches = Math.max(
|
|
16911
|
+
config.retry.maxAccountSwitches,
|
|
16912
|
+
Math.min(configuredAccountCount - 1, 6)
|
|
16913
|
+
);
|
|
16790
16914
|
let attemptsLeft = maxAttempts;
|
|
16791
16915
|
let retryDelay = config.retry.baseDelayMs;
|
|
16792
16916
|
let attempt = 0;
|
|
@@ -16798,7 +16922,6 @@ async function tryCreateStreamWithRetry(params, accountId, accountEmail) {
|
|
|
16798
16922
|
let chatInProgressOriginAccountEmail = null;
|
|
16799
16923
|
let lastAttemptError = null;
|
|
16800
16924
|
let invalidInputSameAccountRetried = false;
|
|
16801
|
-
const accounts = loadAccounts();
|
|
16802
16925
|
const isSingleAccount = accounts.length <= 1;
|
|
16803
16926
|
let currentAccountId = accountId;
|
|
16804
16927
|
let currentAccountEmail = accountEmail;
|
|
@@ -17011,14 +17134,24 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17011
17134
|
const acquireDeadlineMs = config.concurrency.acquireDeadlineMs;
|
|
17012
17135
|
let acquireDeadlineTimer;
|
|
17013
17136
|
const acquireDeadline = new Promise((_, reject) => {
|
|
17014
|
-
|
|
17137
|
+
const checkDeadline = async () => {
|
|
17138
|
+
try {
|
|
17139
|
+
const { isCaptchaRecoveryActive: isCaptchaRecoveryActive2 } = await Promise.resolve().then(() => (init_captcha_coordinator(), captcha_coordinator_exports));
|
|
17140
|
+
if (currentAccountId && isCaptchaRecoveryActive2(currentAccountId)) {
|
|
17141
|
+
acquireDeadlineTimer = setTimeout(checkDeadline, 3e4);
|
|
17142
|
+
acquireDeadlineTimer.unref?.();
|
|
17143
|
+
return;
|
|
17144
|
+
}
|
|
17145
|
+
} catch {
|
|
17146
|
+
}
|
|
17015
17147
|
acquireAbort.abort();
|
|
17016
17148
|
const err2 = new Error(
|
|
17017
17149
|
`Acquire deadline (${acquireDeadlineMs}ms) exceeded creating stream on ${currentAccountEmail}`
|
|
17018
17150
|
);
|
|
17019
17151
|
err2.code = "acquire_deadline";
|
|
17020
17152
|
reject(err2);
|
|
17021
|
-
}
|
|
17153
|
+
};
|
|
17154
|
+
acquireDeadlineTimer = setTimeout(checkDeadline, acquireDeadlineMs);
|
|
17022
17155
|
acquireDeadlineTimer.unref?.();
|
|
17023
17156
|
});
|
|
17024
17157
|
result = await Promise.race([
|
|
@@ -17300,6 +17433,10 @@ ${loopReminder}` : truncation.prompt;
|
|
|
17300
17433
|
currentAccountId = nextAccount.id;
|
|
17301
17434
|
currentAccountEmail = maskEmail(nextAccount.email);
|
|
17302
17435
|
accountSwitches++;
|
|
17436
|
+
const isRealQuotaError = policy.reason === "quota_or_rate_limit" && (policy.accountCooldownReason === "RateLimited" || policy.accountCooldownReason === "QuotaExceeded");
|
|
17437
|
+
if (isRealQuotaError) {
|
|
17438
|
+
attemptsLeft = Math.max(attemptsLeft, 1);
|
|
17439
|
+
}
|
|
17303
17440
|
if (params.useThreadNative) {
|
|
17304
17441
|
const abandonedChatId = params.existingThread?.chatSessionId;
|
|
17305
17442
|
if (abandonedChatId) {
|
|
@@ -19920,6 +20057,199 @@ var init_token_estimation_metrics = __esm({
|
|
|
19920
20057
|
}
|
|
19921
20058
|
});
|
|
19922
20059
|
|
|
20060
|
+
// src/core/daily-usage.ts
|
|
20061
|
+
var daily_usage_exports = {};
|
|
20062
|
+
__export(daily_usage_exports, {
|
|
20063
|
+
DAILY_ACCOUNT_TOKEN_BASELINE: () => DAILY_ACCOUNT_TOKEN_BASELINE,
|
|
20064
|
+
MODEL_DAILY_OUTPUT_BASELINES: () => MODEL_DAILY_OUTPUT_BASELINES,
|
|
20065
|
+
STANDARD_DAILY_TOKEN_BASELINE: () => STANDARD_DAILY_TOKEN_BASELINE,
|
|
20066
|
+
getAccountDailyUsage: () => getAccountDailyUsage,
|
|
20067
|
+
getAllAccountsDailyUsage: () => getAllAccountsDailyUsage,
|
|
20068
|
+
getModelQuotaWeight: () => getModelQuotaWeight,
|
|
20069
|
+
getPoolDailyUsageSummary: () => getPoolDailyUsageSummary,
|
|
20070
|
+
getUtcDateString: () => getUtcDateString,
|
|
20071
|
+
recordTurnUsage: () => recordTurnUsage
|
|
20072
|
+
});
|
|
20073
|
+
function getModelQuotaWeight(model) {
|
|
20074
|
+
if (!model) return 1;
|
|
20075
|
+
const resolved = mapClientModelToQwen(model).toLowerCase();
|
|
20076
|
+
if (resolved.includes("max")) {
|
|
20077
|
+
return STANDARD_DAILY_TOKEN_BASELINE / MODEL_DAILY_OUTPUT_BASELINES.max;
|
|
20078
|
+
}
|
|
20079
|
+
if (resolved.includes("omni") || resolved.includes("flash")) {
|
|
20080
|
+
return STANDARD_DAILY_TOKEN_BASELINE / MODEL_DAILY_OUTPUT_BASELINES.omni;
|
|
20081
|
+
}
|
|
20082
|
+
return 1;
|
|
20083
|
+
}
|
|
20084
|
+
function getUtcDateString(timestampMs = Date.now()) {
|
|
20085
|
+
return new Date(timestampMs).toISOString().slice(0, 10);
|
|
20086
|
+
}
|
|
20087
|
+
function recordTurnUsage(accountId, usage, modelOrDate, maybeDateUtc) {
|
|
20088
|
+
if (!accountId || accountId === "global") return;
|
|
20089
|
+
let model;
|
|
20090
|
+
let dateUtc;
|
|
20091
|
+
if (modelOrDate && /^\d{4}-\d{2}-\d{2}$/.test(modelOrDate)) {
|
|
20092
|
+
dateUtc = modelOrDate;
|
|
20093
|
+
model = void 0;
|
|
20094
|
+
} else {
|
|
20095
|
+
model = modelOrDate;
|
|
20096
|
+
dateUtc = maybeDateUtc || getUtcDateString();
|
|
20097
|
+
}
|
|
20098
|
+
const prompt = Math.max(0, Number(usage.prompt_tokens) || 0);
|
|
20099
|
+
const completion = Math.max(0, Number(usage.completion_tokens) || 0);
|
|
20100
|
+
const total = Math.max(
|
|
20101
|
+
prompt + completion,
|
|
20102
|
+
Number(usage.total_tokens) || prompt + completion
|
|
20103
|
+
);
|
|
20104
|
+
const weight = getModelQuotaWeight(model);
|
|
20105
|
+
const weighted = completion * weight;
|
|
20106
|
+
try {
|
|
20107
|
+
const db2 = getDatabase();
|
|
20108
|
+
db2.prepare(
|
|
20109
|
+
`
|
|
20110
|
+
INSERT INTO account_daily_usage (
|
|
20111
|
+
account_id, date_utc, prompt_tokens, completion_tokens, weighted_tokens, total_tokens, turns_count, updated_at
|
|
20112
|
+
) VALUES (
|
|
20113
|
+
?, ?, ?, ?, ?, ?, 1, datetime('now')
|
|
20114
|
+
)
|
|
20115
|
+
ON CONFLICT(account_id, date_utc) DO UPDATE SET
|
|
20116
|
+
prompt_tokens = prompt_tokens + excluded.prompt_tokens,
|
|
20117
|
+
completion_tokens = completion_tokens + excluded.completion_tokens,
|
|
20118
|
+
weighted_tokens = weighted_tokens + excluded.weighted_tokens,
|
|
20119
|
+
total_tokens = total_tokens + excluded.total_tokens,
|
|
20120
|
+
turns_count = turns_count + 1,
|
|
20121
|
+
updated_at = datetime('now')
|
|
20122
|
+
`
|
|
20123
|
+
).run(accountId, dateUtc, prompt, completion, weighted, total);
|
|
20124
|
+
} catch (err) {
|
|
20125
|
+
}
|
|
20126
|
+
}
|
|
20127
|
+
function getAccountDailyUsage(accountId, dateUtc = getUtcDateString()) {
|
|
20128
|
+
try {
|
|
20129
|
+
const db2 = getDatabase();
|
|
20130
|
+
const row = db2.prepare(
|
|
20131
|
+
`SELECT prompt_tokens, completion_tokens, weighted_tokens, total_tokens, turns_count
|
|
20132
|
+
FROM account_daily_usage
|
|
20133
|
+
WHERE account_id = ? AND date_utc = ?`
|
|
20134
|
+
).get(accountId, dateUtc);
|
|
20135
|
+
const promptTokens = Number(row?.prompt_tokens) || 0;
|
|
20136
|
+
const completionTokens = Number(row?.completion_tokens) || 0;
|
|
20137
|
+
const weightedTokens = Number(row?.weighted_tokens) || completionTokens;
|
|
20138
|
+
const totalTokens = Number(row?.total_tokens) || 0;
|
|
20139
|
+
const turnsCount = Number(row?.turns_count) || 0;
|
|
20140
|
+
const usagePercent = Math.min(
|
|
20141
|
+
100,
|
|
20142
|
+
Math.round(weightedTokens / STANDARD_DAILY_TOKEN_BASELINE * 100)
|
|
20143
|
+
);
|
|
20144
|
+
return {
|
|
20145
|
+
accountId,
|
|
20146
|
+
dateUtc,
|
|
20147
|
+
promptTokens,
|
|
20148
|
+
completionTokens,
|
|
20149
|
+
weightedTokens,
|
|
20150
|
+
totalTokens,
|
|
20151
|
+
turnsCount,
|
|
20152
|
+
usagePercent
|
|
20153
|
+
};
|
|
20154
|
+
} catch {
|
|
20155
|
+
return {
|
|
20156
|
+
accountId,
|
|
20157
|
+
dateUtc,
|
|
20158
|
+
promptTokens: 0,
|
|
20159
|
+
completionTokens: 0,
|
|
20160
|
+
weightedTokens: 0,
|
|
20161
|
+
totalTokens: 0,
|
|
20162
|
+
turnsCount: 0,
|
|
20163
|
+
usagePercent: 0
|
|
20164
|
+
};
|
|
20165
|
+
}
|
|
20166
|
+
}
|
|
20167
|
+
function getAllAccountsDailyUsage(dateUtc = getUtcDateString()) {
|
|
20168
|
+
const result = /* @__PURE__ */ new Map();
|
|
20169
|
+
try {
|
|
20170
|
+
const db2 = getDatabase();
|
|
20171
|
+
const rows = db2.prepare(
|
|
20172
|
+
`SELECT account_id, prompt_tokens, completion_tokens, weighted_tokens, total_tokens, turns_count
|
|
20173
|
+
FROM account_daily_usage
|
|
20174
|
+
WHERE date_utc = ?`
|
|
20175
|
+
).all(dateUtc);
|
|
20176
|
+
for (const row of rows) {
|
|
20177
|
+
const promptTokens = Number(row.prompt_tokens) || 0;
|
|
20178
|
+
const completionTokens = Number(row.completion_tokens) || 0;
|
|
20179
|
+
const weightedTokens = Number(row.weighted_tokens) || completionTokens;
|
|
20180
|
+
const totalTokens = Number(row.total_tokens) || 0;
|
|
20181
|
+
const turnsCount = Number(row.turns_count) || 0;
|
|
20182
|
+
const usagePercent = Math.min(
|
|
20183
|
+
100,
|
|
20184
|
+
Math.round(weightedTokens / STANDARD_DAILY_TOKEN_BASELINE * 100)
|
|
20185
|
+
);
|
|
20186
|
+
result.set(row.account_id, {
|
|
20187
|
+
accountId: row.account_id,
|
|
20188
|
+
dateUtc,
|
|
20189
|
+
promptTokens,
|
|
20190
|
+
completionTokens,
|
|
20191
|
+
weightedTokens,
|
|
20192
|
+
totalTokens,
|
|
20193
|
+
turnsCount,
|
|
20194
|
+
usagePercent
|
|
20195
|
+
});
|
|
20196
|
+
}
|
|
20197
|
+
} catch {
|
|
20198
|
+
}
|
|
20199
|
+
return result;
|
|
20200
|
+
}
|
|
20201
|
+
function getPoolDailyUsageSummary(targetAccountIds, dateUtc = getUtcDateString()) {
|
|
20202
|
+
const accounts = targetAccountIds && targetAccountIds.length > 0 ? targetAccountIds : loadAccounts().map((a) => a.id);
|
|
20203
|
+
const accountCount = Math.max(1, accounts.length);
|
|
20204
|
+
const poolCapacityTokens = accountCount * STANDARD_DAILY_TOKEN_BASELINE;
|
|
20205
|
+
const usageMap = getAllAccountsDailyUsage(dateUtc);
|
|
20206
|
+
let totalPromptTokens = 0;
|
|
20207
|
+
let totalCompletionTokens = 0;
|
|
20208
|
+
let totalWeightedTokens = 0;
|
|
20209
|
+
let totalTokens = 0;
|
|
20210
|
+
let totalTurns = 0;
|
|
20211
|
+
for (const id of accounts) {
|
|
20212
|
+
const usage = usageMap.get(id);
|
|
20213
|
+
if (usage) {
|
|
20214
|
+
totalPromptTokens += usage.promptTokens;
|
|
20215
|
+
totalCompletionTokens += usage.completionTokens;
|
|
20216
|
+
totalWeightedTokens += usage.weightedTokens;
|
|
20217
|
+
totalTokens += usage.totalTokens;
|
|
20218
|
+
totalTurns += usage.turnsCount;
|
|
20219
|
+
}
|
|
20220
|
+
}
|
|
20221
|
+
const poolUsagePercent = Math.min(
|
|
20222
|
+
100,
|
|
20223
|
+
Math.round(totalWeightedTokens / poolCapacityTokens * 100)
|
|
20224
|
+
);
|
|
20225
|
+
return {
|
|
20226
|
+
dateUtc,
|
|
20227
|
+
totalPromptTokens,
|
|
20228
|
+
totalCompletionTokens,
|
|
20229
|
+
totalWeightedTokens,
|
|
20230
|
+
totalTokens,
|
|
20231
|
+
totalTurns,
|
|
20232
|
+
poolCapacityTokens,
|
|
20233
|
+
poolUsagePercent
|
|
20234
|
+
};
|
|
20235
|
+
}
|
|
20236
|
+
var STANDARD_DAILY_TOKEN_BASELINE, DAILY_ACCOUNT_TOKEN_BASELINE, MODEL_DAILY_OUTPUT_BASELINES;
|
|
20237
|
+
var init_daily_usage = __esm({
|
|
20238
|
+
"src/core/daily-usage.ts"() {
|
|
20239
|
+
"use strict";
|
|
20240
|
+
init_database();
|
|
20241
|
+
init_accounts();
|
|
20242
|
+
init_model_alias();
|
|
20243
|
+
STANDARD_DAILY_TOKEN_BASELINE = 5e5;
|
|
20244
|
+
DAILY_ACCOUNT_TOKEN_BASELINE = STANDARD_DAILY_TOKEN_BASELINE;
|
|
20245
|
+
MODEL_DAILY_OUTPUT_BASELINES = {
|
|
20246
|
+
max: 28e4,
|
|
20247
|
+
plus: 5e5,
|
|
20248
|
+
omni: 62e4
|
|
20249
|
+
};
|
|
20250
|
+
}
|
|
20251
|
+
});
|
|
20252
|
+
|
|
19923
20253
|
// src/routes/chat/streaming.ts
|
|
19924
20254
|
import { stream as honoStream } from "hono/streaming";
|
|
19925
20255
|
function firstString(...values) {
|
|
@@ -20425,6 +20755,9 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
20425
20755
|
mode: "non-stream",
|
|
20426
20756
|
context: currentTokenEstimationContext
|
|
20427
20757
|
});
|
|
20758
|
+
if (activeAccountId && usage) {
|
|
20759
|
+
recordTurnUsage(activeAccountId, usage, body.model);
|
|
20760
|
+
}
|
|
20428
20761
|
if (pendingParentId) {
|
|
20429
20762
|
rememberParent(pendingParentId);
|
|
20430
20763
|
}
|
|
@@ -20520,6 +20853,8 @@ async function processStreamingResponse(params) {
|
|
|
20520
20853
|
let activeReader = null;
|
|
20521
20854
|
let invalidInputSameAccountRetries = 0;
|
|
20522
20855
|
let chatInProgressSameAccountRetries = 0;
|
|
20856
|
+
const totalConfiguredAccounts = loadAccounts().length;
|
|
20857
|
+
let quotaRotationsLeft = Math.min(Math.max(1, totalConfiguredAccounts - 1), 6);
|
|
20523
20858
|
let streamCompletedOk = false;
|
|
20524
20859
|
let stoppedByToolCap = false;
|
|
20525
20860
|
const runDisconnectTeardown = () => {
|
|
@@ -20877,13 +21212,18 @@ async function processStreamingResponse(params) {
|
|
|
20877
21212
|
reason: "network"
|
|
20878
21213
|
}
|
|
20879
21214
|
) : null;
|
|
20880
|
-
if (!normalizedError || clientDisconnected && !gracePending || c.req.raw.signal.aborted ||
|
|
21215
|
+
if (!normalizedError || clientDisconnected && !gracePending || c.req.raw.signal.aborted || !midStreamRetry || emittedModelOutput) {
|
|
20881
21216
|
return false;
|
|
20882
21217
|
}
|
|
20883
21218
|
const policy = classifyRetryAction(normalizedError, {
|
|
20884
21219
|
requestAborted: c.req.raw.signal.aborted
|
|
20885
21220
|
});
|
|
20886
21221
|
if (!policy.retryable) return false;
|
|
21222
|
+
const isRealQuotaError = policy.reason === "quota_or_rate_limit" && (policy.accountCooldownReason === "RateLimited" || policy.accountCooldownReason === "QuotaExceeded");
|
|
21223
|
+
const hasBudget = isRealQuotaError ? quotaRotationsLeft > 0 : retryContext.retriesLeft > 0;
|
|
21224
|
+
if (!hasBudget) {
|
|
21225
|
+
return false;
|
|
21226
|
+
}
|
|
20887
21227
|
if (logger.isLevelEnabled("info")) {
|
|
20888
21228
|
console.log(
|
|
20889
21229
|
`\u{1F9ED} [Chat] Stream recovery policy | account=${currentAccountId} | reason=${policy.reason} | retryable=${policy.retryable} | switch=${policy.switchAccount} | newChat=${policy.forceNewChat} | fullPrompt=${policy.retryWithFullPrompt} | retryAfter=${policy.retryAfterMs}ms`
|
|
@@ -20892,9 +21232,13 @@ async function processStreamingResponse(params) {
|
|
|
20892
21232
|
if (policy.reason === "corrupted_chat_history") {
|
|
20893
21233
|
invalidateLogicalThreadParent(midStreamRetry.sessionId);
|
|
20894
21234
|
}
|
|
20895
|
-
|
|
21235
|
+
if (isRealQuotaError) {
|
|
21236
|
+
quotaRotationsLeft--;
|
|
21237
|
+
} else {
|
|
21238
|
+
retryContext.retriesLeft--;
|
|
21239
|
+
}
|
|
20896
21240
|
console.warn(
|
|
20897
|
-
`\u{1F504} [Chat] Stream recovery | account=${currentAccountId} | reason=${policy.reason} | error=${normalizedError.message.substring(0, 150)} | retries_left=${retryContext.retriesLeft}`
|
|
21241
|
+
`\u{1F504} [Chat] Stream recovery | account=${currentAccountId} | reason=${policy.reason} | error=${normalizedError.message.substring(0, 150)} | retries_left=${isRealQuotaError ? quotaRotationsLeft : retryContext.retriesLeft}`
|
|
20898
21242
|
);
|
|
20899
21243
|
const retryInvalidInputOnSameAccount = shouldRetryInvalidInputOnSameAccount(
|
|
20900
21244
|
policy.reason,
|
|
@@ -21775,6 +22119,9 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
21775
22119
|
mode: "stream",
|
|
21776
22120
|
context: currentTokenEstimationContext
|
|
21777
22121
|
});
|
|
22122
|
+
if (activeAccountId && usage) {
|
|
22123
|
+
recordTurnUsage(activeAccountId, usage, body.model);
|
|
22124
|
+
}
|
|
21778
22125
|
} else {
|
|
21779
22126
|
if (isToolcallDebugEnabled()) {
|
|
21780
22127
|
logger.debug(
|
|
@@ -21980,6 +22327,7 @@ var init_streaming = __esm({
|
|
|
21980
22327
|
init_qwen_url();
|
|
21981
22328
|
init_qwen();
|
|
21982
22329
|
init_account();
|
|
22330
|
+
init_accounts();
|
|
21983
22331
|
init_account_manager();
|
|
21984
22332
|
init_account_concurrency();
|
|
21985
22333
|
init_retry_policy();
|
|
@@ -21995,6 +22343,7 @@ var init_streaming = __esm({
|
|
|
21995
22343
|
init_errors2();
|
|
21996
22344
|
init_retry_policy();
|
|
21997
22345
|
init_token_estimation_metrics();
|
|
22346
|
+
init_daily_usage();
|
|
21998
22347
|
init_context_meter();
|
|
21999
22348
|
init_helpers();
|
|
22000
22349
|
MAX_INITIAL_PROTOCOL_BYTES = 64 * 1024;
|
|
@@ -22434,7 +22783,7 @@ async function chatCompletions(c) {
|
|
|
22434
22783
|
updateLogicalThread: parallelEscape ? false : ctx.updateLogicalThread,
|
|
22435
22784
|
allowThreadReuse: ctx.allowThreadReuse,
|
|
22436
22785
|
forceNewChat: parallelEscape,
|
|
22437
|
-
preferredAccountId: void 0,
|
|
22786
|
+
preferredAccountId: c.req.header("x-qwenproxy-account") || void 0,
|
|
22438
22787
|
messageCount: msgCount,
|
|
22439
22788
|
fullMessageCount: parsed.messageCount,
|
|
22440
22789
|
toolsCount: declaredTools.length || void 0,
|
|
@@ -25936,7 +26285,7 @@ function getAppVersion() {
|
|
|
25936
26285
|
}
|
|
25937
26286
|
} catch {
|
|
25938
26287
|
}
|
|
25939
|
-
cachedVersion = "v1.
|
|
26288
|
+
cachedVersion = "v1.6.0";
|
|
25940
26289
|
return cachedVersion;
|
|
25941
26290
|
}
|
|
25942
26291
|
var cachedVersion;
|
|
@@ -26645,7 +26994,8 @@ var init_server = __esm({
|
|
|
26645
26994
|
captchasDetected: Number(metrics.get("captcha.challenges.detected")?.value ?? 0),
|
|
26646
26995
|
captchasSolved: Number(metrics.get("captcha.solves.succeeded")?.value ?? 0),
|
|
26647
26996
|
chatsCleaned: Number(metrics.get("chats.cleaned")?.value ?? 0)
|
|
26648
|
-
}
|
|
26997
|
+
},
|
|
26998
|
+
dailyUsage: (await Promise.resolve().then(() => (init_daily_usage(), daily_usage_exports))).getPoolDailyUsageSummary()
|
|
26649
26999
|
});
|
|
26650
27000
|
});
|
|
26651
27001
|
app5.get("/diagnostics/tokens", async (c) => {
|
|
@@ -27407,12 +27757,17 @@ async function fetchProxyStatus() {
|
|
|
27407
27757
|
}
|
|
27408
27758
|
} catch {
|
|
27409
27759
|
}
|
|
27760
|
+
const dailyUsageMap = getAllAccountsDailyUsage();
|
|
27410
27761
|
const accounts = cachedAccounts.map((acc) => {
|
|
27411
27762
|
const concurrency = concurrencyMap.get(acc.id);
|
|
27763
|
+
const daily = dailyUsageMap.get(acc.id);
|
|
27412
27764
|
return {
|
|
27413
27765
|
...acc,
|
|
27414
27766
|
activeStreams: concurrency?.active ?? 0,
|
|
27415
|
-
streamLimit: concurrency?.limit ?? config.concurrency.maxStreamsPerAccount
|
|
27767
|
+
streamLimit: concurrency?.limit ?? config.concurrency.maxStreamsPerAccount,
|
|
27768
|
+
dailyTokens: daily?.completionTokens ?? 0,
|
|
27769
|
+
dailyUsagePercent: daily?.usagePercent ?? 0,
|
|
27770
|
+
dailyTurns: daily?.turnsCount ?? 0
|
|
27416
27771
|
};
|
|
27417
27772
|
});
|
|
27418
27773
|
let rssMb = 0;
|
|
@@ -27471,6 +27826,7 @@ async function fetchProxyStatus() {
|
|
|
27471
27826
|
cacheHitRatio,
|
|
27472
27827
|
cacheBytesSaved
|
|
27473
27828
|
},
|
|
27829
|
+
dailyUsage: getPoolDailyUsageSummary(cachedAccounts.map((a) => a.id)),
|
|
27474
27830
|
accounts
|
|
27475
27831
|
};
|
|
27476
27832
|
}
|
|
@@ -27611,6 +27967,7 @@ var init_proxy_client = __esm({
|
|
|
27611
27967
|
init_account_concurrency();
|
|
27612
27968
|
init_memory_usage();
|
|
27613
27969
|
init_metrics();
|
|
27970
|
+
init_daily_usage();
|
|
27614
27971
|
init_model_alias();
|
|
27615
27972
|
init_model_alias();
|
|
27616
27973
|
cachedAccounts = [];
|
|
@@ -28341,12 +28698,24 @@ var init_status_view = __esm({
|
|
|
28341
28698
|
const poolPct = accounts.length > 0 ? Math.round(availableCount / accounts.length * 100) : 0;
|
|
28342
28699
|
const poolColor = poolPct >= 70 ? theme.green : poolPct >= 40 ? theme.yellow : theme.red;
|
|
28343
28700
|
const poolBar = renderProgressBar(poolPct, 8, poolColor);
|
|
28701
|
+
const poolDailyUsage = data?.dailyUsage;
|
|
28702
|
+
const poolUsagePct = poolDailyUsage?.poolUsagePercent ?? 0;
|
|
28703
|
+
const poolUsageColor = poolUsagePct >= 80 ? theme.red : poolUsagePct >= 40 ? theme.yellow : theme.cyan;
|
|
28704
|
+
const poolUsageBar = renderProgressBar(poolUsagePct, 8, poolUsageColor);
|
|
28344
28705
|
const innerRightW = Math.max(30, rightW - 2);
|
|
28345
|
-
const
|
|
28706
|
+
const showUsageCol = innerRightW >= 52;
|
|
28707
|
+
const emailWidth = showUsageCol ? Math.max(14, Math.min(18, innerRightW - 33)) : Math.max(16, Math.min(20, innerRightW - 27));
|
|
28346
28708
|
const rightContent = [
|
|
28347
28709
|
` ${theme.bold("Disponibilidade:")} [${poolBar}] ${poolColor(`${availableCount}/${accounts.length} (${poolPct}%)`)}`,
|
|
28710
|
+
...poolDailyUsage ? [
|
|
28711
|
+
` ${theme.bold("Cota Pool Hoje:")} [${poolUsageBar}] ${poolUsageColor(
|
|
28712
|
+
`${poolUsagePct}%`
|
|
28713
|
+
)} ${theme.dim(
|
|
28714
|
+
`(~${Math.round(poolDailyUsage.totalTokens / 1e3)}k tok)`
|
|
28715
|
+
)}`
|
|
28716
|
+
] : [],
|
|
28348
28717
|
` ${theme.dim("\u2500".repeat(Math.max(32, innerRightW - 2)))}`,
|
|
28349
|
-
` ${theme.dim(`# ${pad("Conta", emailWidth)} Carga Status`)}`,
|
|
28718
|
+
` ${theme.dim(`# ${pad("Conta", emailWidth)} Carga ${showUsageCol ? "Uso% " : ""}Status`)}`,
|
|
28350
28719
|
` ${theme.dim("\u2500".repeat(Math.max(32, innerRightW - 2)))}`
|
|
28351
28720
|
];
|
|
28352
28721
|
if (accounts.length === 0) {
|
|
@@ -28358,7 +28727,17 @@ var init_status_view = __esm({
|
|
|
28358
28727
|
const name = pad(truncate(acc.emailOrName, emailWidth - 1), emailWidth);
|
|
28359
28728
|
const active = acc.activeStreams || 0;
|
|
28360
28729
|
const limit = acc.streamLimit || 1;
|
|
28361
|
-
const
|
|
28730
|
+
const usagePct = acc.dailyUsagePercent ?? 0;
|
|
28731
|
+
let loadBadge;
|
|
28732
|
+
if (active > 0) {
|
|
28733
|
+
loadBadge = theme.yellow(`[${active}/${limit}]`);
|
|
28734
|
+
} else if (!showUsageCol && usagePct > 0) {
|
|
28735
|
+
const pctColor = usagePct >= 80 ? theme.red : usagePct >= 40 ? theme.yellow : theme.cyan;
|
|
28736
|
+
loadBadge = pctColor(pad(`[${usagePct}%]`, 5));
|
|
28737
|
+
} else {
|
|
28738
|
+
loadBadge = theme.dim(`[0/${limit}]`);
|
|
28739
|
+
}
|
|
28740
|
+
const usageCol = showUsageCol ? (usagePct > 0 ? usagePct >= 80 ? theme.red(pad(`${usagePct}%`, 5)) : usagePct >= 40 ? theme.yellow(pad(`${usagePct}%`, 5)) : theme.cyan(pad(`${usagePct}%`, 5)) : theme.dim(pad("0%", 5))) + " " : "";
|
|
28362
28741
|
let status = theme.green(`${glyphs.bullet} Pronto`);
|
|
28363
28742
|
if (active > 0 && !acc.onCooldown && acc.headersReady) {
|
|
28364
28743
|
status = theme.yellow(`\u25CF Gerando `);
|
|
@@ -28375,7 +28754,7 @@ var init_status_view = __esm({
|
|
|
28375
28754
|
} else if (!acc.headersReady) {
|
|
28376
28755
|
status = acc.isInitialized ? theme.yellow(`\u25D0 Aquecendo...`) : theme.muted(`\u25CB Standby`);
|
|
28377
28756
|
}
|
|
28378
|
-
rightContent.push(` ${num}${name} ${loadBadge} ${status}`);
|
|
28757
|
+
rightContent.push(` ${num}${name} ${loadBadge} ${usageCol}${status}`);
|
|
28379
28758
|
});
|
|
28380
28759
|
}
|
|
28381
28760
|
const summaryParts = [];
|
|
@@ -32757,6 +33136,7 @@ var init_accounts_view = __esm({
|
|
|
32757
33136
|
init_accounts();
|
|
32758
33137
|
init_server_manager();
|
|
32759
33138
|
init_config();
|
|
33139
|
+
init_status_view();
|
|
32760
33140
|
AccountsView = class {
|
|
32761
33141
|
id = "accounts";
|
|
32762
33142
|
title = "Contas";
|
|
@@ -33522,9 +33902,11 @@ var init_accounts_view = __esm({
|
|
|
33522
33902
|
if (this.selectedIndex >= accounts.length && accounts.length > 0) {
|
|
33523
33903
|
this.selectedIndex = accounts.length - 1;
|
|
33524
33904
|
}
|
|
33905
|
+
const showUsageCol = leftW >= 48;
|
|
33906
|
+
const headerUsage = showUsageCol ? "Uso% " : "";
|
|
33525
33907
|
const leftContent = [
|
|
33526
33908
|
"",
|
|
33527
|
-
` ${theme.dim(
|
|
33909
|
+
` ${theme.dim(`# Conta ${headerUsage}Status`)}`,
|
|
33528
33910
|
` ${theme.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`
|
|
33529
33911
|
];
|
|
33530
33912
|
if (accounts.length === 0) {
|
|
@@ -33569,7 +33951,9 @@ var init_accounts_view = __esm({
|
|
|
33569
33951
|
} else if (!acc.headersReady) {
|
|
33570
33952
|
status = acc.isInitialized ? theme.yellow(`\u25D0 Aquecendo...`) : theme.muted(`\u25CB Standby `);
|
|
33571
33953
|
}
|
|
33572
|
-
const
|
|
33954
|
+
const usagePct = acc.dailyUsagePercent ?? 0;
|
|
33955
|
+
const usagePctStr = showUsageCol ? (usagePct > 0 ? usagePct >= 80 ? theme.red(pad(`${usagePct}%`, 5)) : usagePct >= 40 ? theme.yellow(pad(`${usagePct}%`, 5)) : theme.cyan(pad(`${usagePct}%`, 5)) : theme.dim(pad("0%", 5))) + " " : "";
|
|
33956
|
+
const line = `${pointer}${num}${name}${usagePctStr}${status}`;
|
|
33573
33957
|
if (isHovered) {
|
|
33574
33958
|
leftContent.push(theme.bgHover(line));
|
|
33575
33959
|
} else if (isFocused) {
|
|
@@ -33610,6 +33994,12 @@ var init_accounts_view = __esm({
|
|
|
33610
33994
|
rightContent.push(` ${theme.bold("Conta:")} ${theme.cyan(email)}`);
|
|
33611
33995
|
rightContent.push(` ${theme.bold("Sistema ID:")} ${theme.muted(selected.id.slice(0, 14))}`);
|
|
33612
33996
|
rightContent.push(` ${theme.bold("N\xEDvel:")} ${selected.priority}`);
|
|
33997
|
+
const usagePct = selected.dailyUsagePercent ?? 0;
|
|
33998
|
+
const usageTokens = selected.dailyTokens ?? 0;
|
|
33999
|
+
const usageTurns = selected.dailyTurns ?? 0;
|
|
34000
|
+
const usageColor = usagePct >= 80 ? theme.red : usagePct >= 40 ? theme.yellow : theme.cyan;
|
|
34001
|
+
const usageBar = renderProgressBar(usagePct, 6, usageColor);
|
|
34002
|
+
rightContent.push(` ${theme.bold("Cota Hoje:")} [${usageBar}] ${usageColor(`${usagePct}%`)} ${theme.dim(`(~${Math.round(usageTokens / 1e3)}k / ${usageTurns} reqs)`)}`);
|
|
33613
34003
|
const cdStatus = selected.onCooldown ? theme.yellow(`[!] Cooldown ${Math.round(selected.remainingCooldownMs / 6e4)}m`) : theme.green(`${glyphs.check} Dispon\xEDvel`);
|
|
33614
34004
|
rightContent.push(` ${theme.bold("Estado:")} ${cdStatus}`);
|
|
33615
34005
|
const hStatus = selected.headersReady ? theme.green(`${glyphs.check} Capturados`) : selected.isInitialized ? theme.yellow(`\u25D0 Aquecendo...`) : theme.muted(`${glyphs.circle} Standby (Sob Demanda)`);
|