qwenproxy-cli 1.0.24 → 1.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwenproxy-cli",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
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": {
@@ -1841,6 +1841,34 @@ export async function processStreamingResponse(
1841
1841
  // executed, so the model can re-issue them.
1842
1842
  setToolCapNotice(logicalSessionId);
1843
1843
  await reader.cancel().catch(() => undefined);
1844
+ // Explicitly tell Qwen to stop generating on the backend so the upstream chat
1845
+ // settles immediately instead of remaining in "in progress" state for 30s.
1846
+ const capSessionId = currentUiSessionId || logicalSessionId;
1847
+ const capHeaders = getStream(completionId)?.headers;
1848
+ if (capSessionId && targetResponseId && capHeaders?.cookie && capHeaders["user-agent"]) {
1849
+ const capAccountId = currentAccountId;
1850
+ void requestQwenTextInBrowser(
1851
+ capAccountId,
1852
+ "POST",
1853
+ `/api/v2/chat/completions/stop?chat_id=${encodeURIComponent(capSessionId)}`,
1854
+ buildQwenRequestHeaders({
1855
+ cookie: capHeaders.cookie,
1856
+ userAgent: capHeaders["user-agent"],
1857
+ bxUa: capHeaders["bx-ua"],
1858
+ bxUmidtoken: capHeaders["bx-umidtoken"],
1859
+ bxV: capHeaders["bx-v"],
1860
+ chatSessionId: capSessionId,
1861
+ }),
1862
+ JSON.stringify({
1863
+ chat_id: capSessionId,
1864
+ response_id: targetResponseId,
1865
+ }),
1866
+ {
1867
+ referrer: qwenUrl(`/c/${encodeURIComponent(capSessionId)}`),
1868
+ noMutexRecovery: true,
1869
+ },
1870
+ ).catch(() => undefined);
1871
+ }
1844
1872
  }
1845
1873
 
1846
1874
  // Post-stream: error check + flush remaining content
@@ -261,7 +261,10 @@ export function computeDynamicIdleTimeout(opts: {
261
261
  }): number {
262
262
  const payloadMB = opts.payloadSize / (1024 * 1024);
263
263
  const dynamic = opts.baseTimeoutMs + Math.ceil(payloadMB * 30_000);
264
- if (opts.parallelEscape && !opts.enableThinking) {
264
+ // The tight 15s cap is ONLY for small auxiliary requests (e.g. title generation).
265
+ // Larger parallel requests (such as Zed/OMP context compaction with big history)
266
+ // need the full dynamic timeout so they do not time out at 15s.
267
+ if (opts.parallelEscape && !opts.enableThinking && opts.payloadSize < 16_384) {
265
268
  return Math.min(15_000, dynamic);
266
269
  }
267
270
  return dynamic;
@@ -857,25 +860,33 @@ export async function requestQwenTextInBrowser(
857
860
 
858
861
  const evaluateRequest = (page: Page) =>
859
862
  page.evaluate(
860
- async ({ url, method, headers, body, referrer }: {
863
+ async ({ url, method, headers, body, referrer, timeoutMs }: {
861
864
  url: string;
862
865
  method: "GET" | "POST" | "DELETE";
863
866
  headers: Record<string, string>;
864
867
  body?: string;
865
868
  referrer?: string;
869
+ timeoutMs: number;
866
870
  }): Promise<BrowserTextResponse> => {
867
- const response = await fetch(url, {
868
- method,
869
- credentials: "include",
870
- headers,
871
- body,
872
- ...(referrer ? { referrer } : {}),
873
- });
874
- return {
875
- status: response.status,
876
- contentType: response.headers.get("content-type") || "",
877
- raw: await response.text(),
878
- };
871
+ const controller = new AbortController();
872
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
873
+ try {
874
+ const response = await fetch(url, {
875
+ method,
876
+ credentials: "include",
877
+ headers,
878
+ body,
879
+ signal: controller.signal,
880
+ ...(referrer ? { referrer } : {}),
881
+ });
882
+ return {
883
+ status: response.status,
884
+ contentType: response.headers.get("content-type") || "",
885
+ raw: await response.text(),
886
+ };
887
+ } finally {
888
+ clearTimeout(timeoutId);
889
+ }
879
890
  },
880
891
  {
881
892
  url,
@@ -883,6 +894,7 @@ export async function requestQwenTextInBrowser(
883
894
  headers: browserHeaders,
884
895
  body,
885
896
  referrer: options.referrer,
897
+ timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 20_000),
886
898
  },
887
899
  );
888
900
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -960,8 +960,8 @@ function repairCommonMalformedToolJson(content: string): string {
960
960
  '$1"arguments": ',
961
961
  )
962
962
  .replace(
963
- /([,{]\s*)arguments"\s*:/g,
964
- '$1"arguments":',
963
+ /([,{]\s*)([A-Za-z_][A-Za-z0-9_]*)"\s*:/g,
964
+ '$1"$2":',
965
965
  )
966
966
  .replace(
967
967
  /([,{]\s*)arguments\s*:\s*(?={|\[|")/g,