qwenproxy-cli 1.6.5 → 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 +11 -1
- package/dist/delete-chats.js +11 -1
- package/dist/index.js +34 -20
- package/dist/login.js +11 -1
- package/dist/reset-cooldowns.js +11 -1
- package/package.json +1 -1
- package/src/core/version.ts +1 -1
- package/src/routes/chat/streaming.ts +34 -21
- package/src/services/playwright.ts +16 -5
package/dist/clean-cache.js
CHANGED
|
@@ -3588,6 +3588,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3588
3588
|
await existingInit;
|
|
3589
3589
|
return;
|
|
3590
3590
|
}
|
|
3591
|
+
let resolveInit;
|
|
3592
|
+
let rejectInit;
|
|
3593
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
3594
|
+
resolveInit = res;
|
|
3595
|
+
rejectInit = rej;
|
|
3596
|
+
});
|
|
3597
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
3591
3598
|
const initPromise = (async () => {
|
|
3592
3599
|
const release = await acquireAccountMutex(
|
|
3593
3600
|
account.id,
|
|
@@ -3872,9 +3879,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3872
3879
|
release();
|
|
3873
3880
|
}
|
|
3874
3881
|
})();
|
|
3875
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
3876
3882
|
try {
|
|
3877
3883
|
await initPromise;
|
|
3884
|
+
resolveInit();
|
|
3885
|
+
} catch (error) {
|
|
3886
|
+
rejectInit(error);
|
|
3887
|
+
throw error;
|
|
3878
3888
|
} finally {
|
|
3879
3889
|
inFlightAccountInits.delete(account.id);
|
|
3880
3890
|
}
|
package/dist/delete-chats.js
CHANGED
|
@@ -4447,6 +4447,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4447
4447
|
await existingInit;
|
|
4448
4448
|
return;
|
|
4449
4449
|
}
|
|
4450
|
+
let resolveInit;
|
|
4451
|
+
let rejectInit;
|
|
4452
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
4453
|
+
resolveInit = res;
|
|
4454
|
+
rejectInit = rej;
|
|
4455
|
+
});
|
|
4456
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
4450
4457
|
const initPromise = (async () => {
|
|
4451
4458
|
const release = await acquireAccountMutex(
|
|
4452
4459
|
account.id,
|
|
@@ -4731,9 +4738,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4731
4738
|
release();
|
|
4732
4739
|
}
|
|
4733
4740
|
})();
|
|
4734
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
4735
4741
|
try {
|
|
4736
4742
|
await initPromise;
|
|
4743
|
+
resolveInit();
|
|
4744
|
+
} catch (error) {
|
|
4745
|
+
rejectInit(error);
|
|
4746
|
+
throw error;
|
|
4737
4747
|
} finally {
|
|
4738
4748
|
inFlightAccountInits.delete(account.id);
|
|
4739
4749
|
}
|
package/dist/index.js
CHANGED
|
@@ -5557,6 +5557,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5557
5557
|
await existingInit;
|
|
5558
5558
|
return;
|
|
5559
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);
|
|
5560
5567
|
const initPromise = (async () => {
|
|
5561
5568
|
const release = await acquireAccountMutex(
|
|
5562
5569
|
account.id,
|
|
@@ -5841,9 +5848,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
5841
5848
|
release();
|
|
5842
5849
|
}
|
|
5843
5850
|
})();
|
|
5844
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
5845
5851
|
try {
|
|
5846
5852
|
await initPromise;
|
|
5853
|
+
resolveInit();
|
|
5854
|
+
} catch (error) {
|
|
5855
|
+
rejectInit(error);
|
|
5856
|
+
throw error;
|
|
5847
5857
|
} finally {
|
|
5848
5858
|
inFlightAccountInits.delete(account.id);
|
|
5849
5859
|
}
|
|
@@ -21513,7 +21523,7 @@ async function processStreamingResponse(params) {
|
|
|
21513
21523
|
let chatInProgressSameAccountRetries = 0;
|
|
21514
21524
|
let antiBotSameAccountRetries = 0;
|
|
21515
21525
|
const totalConfiguredAccounts = loadAccounts().length;
|
|
21516
|
-
let quotaRotationsLeft = Math.
|
|
21526
|
+
let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
|
|
21517
21527
|
let streamCompletedOk = false;
|
|
21518
21528
|
let stoppedByToolCap = false;
|
|
21519
21529
|
const runDisconnectTeardown = () => {
|
|
@@ -22950,28 +22960,32 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
|
|
|
22950
22960
|
logger.error("[Chat] Stream callback error", errorDetails);
|
|
22951
22961
|
}
|
|
22952
22962
|
try {
|
|
22953
|
-
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 = `
|
|
22954
22967
|
|
|
22955
|
-
\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."}
|
|
22956
22969
|
|
|
22957
22970
|
`;
|
|
22958
|
-
|
|
22959
|
-
|
|
22960
|
-
|
|
22961
|
-
|
|
22962
|
-
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
|
|
22966
|
-
|
|
22967
|
-
|
|
22968
|
-
|
|
22969
|
-
|
|
22970
|
-
|
|
22971
|
-
|
|
22972
|
-
|
|
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)}
|
|
22973
22986
|
|
|
22974
22987
|
`);
|
|
22988
|
+
}
|
|
22975
22989
|
await errorStream.write(
|
|
22976
22990
|
`data: ${JSON.stringify({
|
|
22977
22991
|
error: {
|
|
@@ -27004,7 +27018,7 @@ function getAppVersion() {
|
|
|
27004
27018
|
}
|
|
27005
27019
|
} catch {
|
|
27006
27020
|
}
|
|
27007
|
-
cachedVersion = "v1.6.
|
|
27021
|
+
cachedVersion = "v1.6.6";
|
|
27008
27022
|
return cachedVersion;
|
|
27009
27023
|
}
|
|
27010
27024
|
var cachedVersion;
|
package/dist/login.js
CHANGED
|
@@ -4420,6 +4420,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4420
4420
|
await existingInit;
|
|
4421
4421
|
return;
|
|
4422
4422
|
}
|
|
4423
|
+
let resolveInit;
|
|
4424
|
+
let rejectInit;
|
|
4425
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
4426
|
+
resolveInit = res;
|
|
4427
|
+
rejectInit = rej;
|
|
4428
|
+
});
|
|
4429
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
4423
4430
|
const initPromise = (async () => {
|
|
4424
4431
|
const release = await acquireAccountMutex(
|
|
4425
4432
|
account.id,
|
|
@@ -4704,9 +4711,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4704
4711
|
release();
|
|
4705
4712
|
}
|
|
4706
4713
|
})();
|
|
4707
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
4708
4714
|
try {
|
|
4709
4715
|
await initPromise;
|
|
4716
|
+
resolveInit();
|
|
4717
|
+
} catch (error) {
|
|
4718
|
+
rejectInit(error);
|
|
4719
|
+
throw error;
|
|
4710
4720
|
} finally {
|
|
4711
4721
|
inFlightAccountInits.delete(account.id);
|
|
4712
4722
|
}
|
package/dist/reset-cooldowns.js
CHANGED
|
@@ -3851,6 +3851,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
3851
3851
|
await existingInit;
|
|
3852
3852
|
return;
|
|
3853
3853
|
}
|
|
3854
|
+
let resolveInit;
|
|
3855
|
+
let rejectInit;
|
|
3856
|
+
const inFlightDeferred = new Promise((res, rej) => {
|
|
3857
|
+
resolveInit = res;
|
|
3858
|
+
rejectInit = rej;
|
|
3859
|
+
});
|
|
3860
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
3854
3861
|
const initPromise = (async () => {
|
|
3855
3862
|
const release = await acquireAccountMutex(
|
|
3856
3863
|
account.id,
|
|
@@ -4135,9 +4142,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
|
|
|
4135
4142
|
release();
|
|
4136
4143
|
}
|
|
4137
4144
|
})();
|
|
4138
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
4139
4145
|
try {
|
|
4140
4146
|
await initPromise;
|
|
4147
|
+
resolveInit();
|
|
4148
|
+
} catch (error) {
|
|
4149
|
+
rejectInit(error);
|
|
4150
|
+
throw error;
|
|
4141
4151
|
} finally {
|
|
4142
4152
|
inFlightAccountInits.delete(account.id);
|
|
4143
4153
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qwenproxy-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.6",
|
|
4
4
|
"description": "High-performance OpenAI & Anthropic compatible API gateway for Qwen with multi-account rotation, interactive TUI, and resilient tool calling.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
package/src/core/version.ts
CHANGED
|
@@ -937,7 +937,7 @@ export async function processStreamingResponse(
|
|
|
937
937
|
let chatInProgressSameAccountRetries = 0;
|
|
938
938
|
let antiBotSameAccountRetries = 0;
|
|
939
939
|
const totalConfiguredAccounts = loadAccounts().length;
|
|
940
|
-
let quotaRotationsLeft = Math.
|
|
940
|
+
let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
|
|
941
941
|
// Set when the terminal [DONE] reached the client. The `| recovered`
|
|
942
942
|
// suffix on Stream done must only appear for attempts that COMPLETED after
|
|
943
943
|
// a mid-stream retry, not for failed attempts that consumed retries.
|
|
@@ -2831,28 +2831,41 @@ export async function processStreamingResponse(
|
|
|
2831
2831
|
logger.error("[Chat] Stream callback error", errorDetails);
|
|
2832
2832
|
}
|
|
2833
2833
|
// The HTTP response is already committed at this point.
|
|
2834
|
-
// 1. Emit an explicit assistant message delta
|
|
2835
|
-
//
|
|
2836
|
-
//
|
|
2837
|
-
// 2.
|
|
2834
|
+
// 1. Emit an explicit assistant message delta ONLY if this was an upstream content moderation /
|
|
2835
|
+
// policy violation (with finish_reason: "content_filter"), so the user understands why the
|
|
2836
|
+
// prompt was blocked.
|
|
2837
|
+
// 2. For network/transient/quota errors, NEVER emit fake assistant content with finish_reason: "stop".
|
|
2838
|
+
// Doing so masks network failures as successful AI answers, preventing client agent retries and
|
|
2839
|
+
// poisoning chat history with error banners.
|
|
2840
|
+
// 3. Emit a terminal OpenAI-compatible SSE error event + [DONE].
|
|
2838
2841
|
try {
|
|
2839
|
-
const
|
|
2840
|
-
const
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2842
|
+
const isNetwork = isNetworkLikeError(err);
|
|
2843
|
+
const isModeration =
|
|
2844
|
+
normalizedErrorCode === "data_inspection_failed" ||
|
|
2845
|
+
normalizedErrorCode === "fail_sys_user_validate" ||
|
|
2846
|
+
err.message?.toLowerCase().includes("content moderation") ||
|
|
2847
|
+
err.message?.toLowerCase().includes("security verification");
|
|
2848
|
+
|
|
2849
|
+
if (!isNetwork && isModeration) {
|
|
2850
|
+
const userFriendlyNotice = `\n\n⚠️ **[Qwen Moderação / Segurança]** ${err.message || "A resposta foi interrompida pelas políticas de segurança do Qwen."}\n\n`;
|
|
2851
|
+
const errorDelta = {
|
|
2852
|
+
id: completionId,
|
|
2853
|
+
object: "chat.completion.chunk",
|
|
2854
|
+
created: Math.floor(Date.now() / 1000),
|
|
2855
|
+
model: body.model,
|
|
2856
|
+
choices: [
|
|
2857
|
+
{
|
|
2858
|
+
index: 0,
|
|
2859
|
+
delta: { content: userFriendlyNotice },
|
|
2860
|
+
logprobs: null,
|
|
2861
|
+
finish_reason: "content_filter",
|
|
2862
|
+
},
|
|
2863
|
+
],
|
|
2864
|
+
};
|
|
2865
|
+
|
|
2866
|
+
await errorStream.write(`data: ${JSON.stringify(errorDelta)}\n\n`);
|
|
2867
|
+
}
|
|
2854
2868
|
|
|
2855
|
-
await errorStream.write(`data: ${JSON.stringify(errorDelta)}\n\n`);
|
|
2856
2869
|
await errorStream.write(
|
|
2857
2870
|
`data: ${JSON.stringify({
|
|
2858
2871
|
error: {
|
|
@@ -1529,6 +1529,14 @@ export async function initPlaywrightForAccount(
|
|
|
1529
1529
|
return;
|
|
1530
1530
|
}
|
|
1531
1531
|
|
|
1532
|
+
let resolveInit!: () => void;
|
|
1533
|
+
let rejectInit!: (err: any) => void;
|
|
1534
|
+
const inFlightDeferred = new Promise<void>((res, rej) => {
|
|
1535
|
+
resolveInit = res;
|
|
1536
|
+
rejectInit = rej;
|
|
1537
|
+
});
|
|
1538
|
+
inFlightAccountInits.set(account.id, inFlightDeferred);
|
|
1539
|
+
|
|
1532
1540
|
const initPromise = (async () => {
|
|
1533
1541
|
const release = await acquireAccountMutex(
|
|
1534
1542
|
account.id,
|
|
@@ -1536,10 +1544,10 @@ export async function initPlaywrightForAccount(
|
|
|
1536
1544
|
Math.max(PLAYWRIGHT_MUTEX_WAIT_MS, 120_000),
|
|
1537
1545
|
);
|
|
1538
1546
|
try {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1547
|
+
// Double-check after acquiring lock
|
|
1548
|
+
if (accountPages.has(account.id)) {
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1543
1551
|
|
|
1544
1552
|
// If a context limit is configured, make room by closing idle contexts.
|
|
1545
1553
|
await evictIdlePlaywrightContextsToLimit().catch(() => {});
|
|
@@ -1866,9 +1874,12 @@ export async function initPlaywrightForAccount(
|
|
|
1866
1874
|
}
|
|
1867
1875
|
})();
|
|
1868
1876
|
|
|
1869
|
-
inFlightAccountInits.set(account.id, initPromise);
|
|
1870
1877
|
try {
|
|
1871
1878
|
await initPromise;
|
|
1879
|
+
resolveInit();
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
rejectInit(error);
|
|
1882
|
+
throw error;
|
|
1872
1883
|
} finally {
|
|
1873
1884
|
inFlightAccountInits.delete(account.id);
|
|
1874
1885
|
}
|