@pedrofariasx/qwenproxy 1.4.0 → 1.5.1
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 +1 -1
- package/src/routes/chat.ts +124 -72
- package/src/services/playwright.ts +111 -0
- package/src/services/qwen.ts +53 -16
package/package.json
CHANGED
package/src/routes/chat.ts
CHANGED
|
@@ -272,48 +272,75 @@ export async function chatCompletions(c: Context) {
|
|
|
272
272
|
const isNewSession = !messages.some(m => m.role === 'assistant');
|
|
273
273
|
|
|
274
274
|
// Account selection with fallback on rate-limit/failure
|
|
275
|
-
|
|
276
|
-
const triedAccountIds = new Set<string>();
|
|
277
|
-
let lastError: any = null;
|
|
278
|
-
|
|
275
|
+
const isGuestModeOnly = process.env.QWEN_GUEST_MODE_ONLY?.toLowerCase() === 'true';
|
|
279
276
|
let stream: ReadableStream | undefined;
|
|
280
277
|
let uiSessionId = '';
|
|
281
278
|
const completionId = 'chatcmpl-' + crypto.randomUUID();
|
|
279
|
+
let lastError: any = null;
|
|
282
280
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
281
|
+
if (isGuestModeOnly) {
|
|
282
|
+
console.log('[Chat] Guest mode only enabled. Bypassing account rotation.');
|
|
283
|
+
try {
|
|
284
|
+
const result = await createQwenStream(
|
|
285
|
+
finalPrompt,
|
|
286
|
+
isThinkingModel,
|
|
287
|
+
body.model,
|
|
288
|
+
null,
|
|
289
|
+
'guest',
|
|
290
|
+
undefined,
|
|
291
|
+
pendingMultimodal.length > 0 ? pendingMultimodal : undefined
|
|
292
|
+
);
|
|
293
|
+
stream = result.stream;
|
|
294
|
+
uiSessionId = result.uiSessionId;
|
|
295
|
+
registerStream(completionId, {
|
|
296
|
+
abortController: result.controller,
|
|
297
|
+
accountId: 'guest',
|
|
298
|
+
uiSessionId: result.uiSessionId,
|
|
299
|
+
targetResponseId: '',
|
|
300
|
+
headers: result.headers,
|
|
301
|
+
});
|
|
302
|
+
} catch (err: any) {
|
|
303
|
+
console.error('[Chat] Guest mode failed:', err.message);
|
|
304
|
+
throw err;
|
|
290
305
|
}
|
|
291
|
-
|
|
306
|
+
} else {
|
|
307
|
+
let account = getNextAccount();
|
|
308
|
+
const triedAccountIds = new Set<string>();
|
|
292
309
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
account = getNextAvailableAccount(triedAccountIds);
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
310
|
+
while (account) {
|
|
311
|
+
const accountId = account.id;
|
|
312
|
+
const accountEmail = account.email;
|
|
299
313
|
|
|
300
|
-
|
|
314
|
+
if (triedAccountIds.has(accountId)) {
|
|
315
|
+
account = getNextAvailableAccount(triedAccountIds);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
triedAccountIds.add(accountId);
|
|
301
319
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
320
|
+
const cooldownInfo = getAccountCooldownInfo(accountId);
|
|
321
|
+
if (cooldownInfo && accountId !== 'global') {
|
|
322
|
+
console.log(`[Chat] Skipping account ${accountEmail} (${accountId}) — on cooldown for ${Math.round(cooldownInfo.remainingMs / 1000)}s (${cooldownInfo.reason})`);
|
|
323
|
+
account = getNextAvailableAccount(triedAccountIds);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
305
326
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
327
|
+
console.log(`[Chat] Routing request to account: ${accountEmail} (${accountId})`);
|
|
328
|
+
|
|
329
|
+
let retries = 3;
|
|
330
|
+
let retryDelay = 500;
|
|
331
|
+
let success = false;
|
|
332
|
+
|
|
333
|
+
while (retries > 0) {
|
|
334
|
+
try {
|
|
335
|
+
const result = await createQwenStream(
|
|
336
|
+
finalPrompt,
|
|
337
|
+
isThinkingModel,
|
|
338
|
+
body.model,
|
|
339
|
+
null, // Always force new chat for concurrency isolation
|
|
340
|
+
accountId === 'global' ? undefined : accountId,
|
|
341
|
+
undefined,
|
|
342
|
+
pendingMultimodal.length > 0 ? pendingMultimodal : undefined
|
|
343
|
+
);
|
|
317
344
|
stream = result.stream;
|
|
318
345
|
uiSessionId = result.uiSessionId;
|
|
319
346
|
registerStream(completionId, {
|
|
@@ -325,59 +352,84 @@ export async function chatCompletions(c: Context) {
|
|
|
325
352
|
});
|
|
326
353
|
success = true;
|
|
327
354
|
break;
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
355
|
+
} catch (err: any) {
|
|
356
|
+
retries--;
|
|
357
|
+
|
|
358
|
+
if (err.upstreamCode === 'RateLimited' || err.upstreamStatus === 429) {
|
|
359
|
+
const hourHint = err.message?.match(/Wait about (\d+) hour/);
|
|
360
|
+
const hours = hourHint ? parseInt(hourHint[1]) : 24;
|
|
361
|
+
const cooldownMs = hours * 60 * 60 * 1000;
|
|
362
|
+
markAccountRateLimited(accountId, cooldownMs, 'RateLimited');
|
|
363
|
+
console.warn(`[Chat] Account ${accountEmail} (${accountId}) rate-limited. Entering cooldown for ${hours} hours.`);
|
|
364
|
+
lastError = err;
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
340
367
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
368
|
+
if (retries === 0) {
|
|
369
|
+
if (err.upstreamStatus && err.upstreamStatus >= 500) {
|
|
370
|
+
markAccountRateLimited(accountId, undefined, 'ServerError');
|
|
371
|
+
console.warn(`[Chat] Account ${accountEmail} (${accountId}) returned server error. Marked for cooldown.`);
|
|
372
|
+
}
|
|
373
|
+
lastError = err;
|
|
374
|
+
break;
|
|
345
375
|
}
|
|
346
|
-
lastError = err;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
376
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
377
|
+
let useDelay = retryDelay;
|
|
378
|
+
if (err instanceof RetryableQwenStreamError && err.retryAfterMs !== undefined) {
|
|
379
|
+
useDelay = err.retryAfterMs;
|
|
380
|
+
}
|
|
381
|
+
const isRetryable = err instanceof RetryableQwenStreamError || err.message?.includes('in progress') || err.message?.includes('Bad_Request');
|
|
382
|
+
if (!isRetryable) {
|
|
383
|
+
lastError = err;
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
console.warn(`[Chat] Qwen request failed for ${accountEmail}, retrying in ${useDelay}ms... (${retries} left)`);
|
|
387
|
+
await new Promise(r => setTimeout(r, useDelay));
|
|
388
|
+
retryDelay = Math.min(retryDelay * 2, 5000);
|
|
358
389
|
}
|
|
359
|
-
console.warn(`[Chat] Qwen request failed for ${accountEmail}, retrying in ${useDelay}ms... (${retries} left)`);
|
|
360
|
-
await new Promise(r => setTimeout(r, useDelay));
|
|
361
|
-
retryDelay = Math.min(retryDelay * 2, 5000);
|
|
362
390
|
}
|
|
363
|
-
}
|
|
364
391
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
392
|
+
if (success) {
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
368
395
|
|
|
369
|
-
|
|
396
|
+
account = getNextAvailableAccount(triedAccountIds);
|
|
397
|
+
}
|
|
370
398
|
}
|
|
371
399
|
|
|
372
400
|
if (!stream) {
|
|
373
401
|
removeStream(completionId);
|
|
374
|
-
// Check if all accounts are on cooldown
|
|
375
402
|
const accounts = loadAccounts();
|
|
376
|
-
const allOnCooldown = accounts.every(a => getAccountCooldownInfo(a.id) !== null);
|
|
403
|
+
const allOnCooldown = accounts.length === 0 || accounts.every(a => getAccountCooldownInfo(a.id) !== null);
|
|
404
|
+
|
|
377
405
|
if (allOnCooldown) {
|
|
378
|
-
console.warn(`[Chat] CRITICAL: All
|
|
406
|
+
console.warn(`[Chat] CRITICAL: All accounts are rate-limited, on cooldown, or none configured! Falling back to GUEST mode.`);
|
|
407
|
+
try {
|
|
408
|
+
const result = await createQwenStream(
|
|
409
|
+
finalPrompt,
|
|
410
|
+
isThinkingModel,
|
|
411
|
+
body.model,
|
|
412
|
+
null,
|
|
413
|
+
'guest',
|
|
414
|
+
undefined,
|
|
415
|
+
pendingMultimodal.length > 0 ? pendingMultimodal : undefined
|
|
416
|
+
);
|
|
417
|
+
stream = result.stream;
|
|
418
|
+
uiSessionId = result.uiSessionId;
|
|
419
|
+
registerStream(completionId, {
|
|
420
|
+
abortController: result.controller,
|
|
421
|
+
accountId: 'guest',
|
|
422
|
+
uiSessionId: result.uiSessionId,
|
|
423
|
+
targetResponseId: '',
|
|
424
|
+
headers: result.headers,
|
|
425
|
+
});
|
|
426
|
+
} catch (guestErr: any) {
|
|
427
|
+
console.error('[Chat] Guest mode also failed:', guestErr.message);
|
|
428
|
+
throw lastError || new Error('All accounts and guest mode failed');
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
throw lastError || new Error('All accounts failed');
|
|
379
432
|
}
|
|
380
|
-
throw lastError || new Error('All accounts failed');
|
|
381
433
|
}
|
|
382
434
|
|
|
383
435
|
if (!isStream) {
|
|
@@ -355,7 +355,118 @@ async function loginToQwenUI(email: string, password: string): Promise<boolean>
|
|
|
355
355
|
return false;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
let guestContext: BrowserContext | null = null;
|
|
359
|
+
let guestPage: Page | null = null;
|
|
360
|
+
let guestHeadersCache: { headers: Record<string, string>, timestamp: number } | null = null;
|
|
361
|
+
const GUEST_HEADERS_TTL = 30 * 60 * 1000;
|
|
362
|
+
|
|
363
|
+
export async function getGuestHeaders(): Promise<Record<string, string>> {
|
|
364
|
+
if (guestHeadersCache && (Date.now() - guestHeadersCache.timestamp) < GUEST_HEADERS_TTL) {
|
|
365
|
+
return guestHeadersCache.headers;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (!guestPage) {
|
|
369
|
+
const profilePath = path.resolve('qwen_profiles', '_guest');
|
|
370
|
+
const { engine, channel } = resolveBrowserEngine('chromium');
|
|
371
|
+
guestContext = await engine.launchPersistentContext(profilePath, {
|
|
372
|
+
headless: config.browser.headless,
|
|
373
|
+
channel,
|
|
374
|
+
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
|
|
375
|
+
ignoreDefaultArgs: ['--enable-automation'],
|
|
376
|
+
args: ['--disable-blink-features=AutomationControlled', '--disable-features=IsolateOrigins,site-per-process', '--disable-infobars', '--no-first-run', '--no-default-browser-check']
|
|
377
|
+
});
|
|
378
|
+
await guestContext.addInitScript(getStealthScript());
|
|
379
|
+
guestPage = await guestContext.newPage();
|
|
380
|
+
|
|
381
|
+
await guestPage.goto('https://chat.qwen.ai/c/guest', { waitUntil: 'domcontentloaded' });
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
const keepSessionBtn = await guestPage.$('button:has-text("Manter sessão terminada"), button:has-text("Keep session ended"), button:has-text("Manter sessão encerrada")');
|
|
385
|
+
if (keepSessionBtn) {
|
|
386
|
+
await keepSessionBtn.click();
|
|
387
|
+
console.log('[Playwright] Guest: Clicked "Manter sessão terminada"');
|
|
388
|
+
await sleep(1000);
|
|
389
|
+
}
|
|
390
|
+
} catch (e) {
|
|
391
|
+
// Modal might not be there
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return new Promise((resolve, reject) => {
|
|
396
|
+
const timeout = setTimeout(() => reject(new Error('Timeout getting guest headers')), 30000);
|
|
397
|
+
|
|
398
|
+
const routeHandler = async (route: any, request: any) => {
|
|
399
|
+
clearTimeout(timeout);
|
|
400
|
+
const reqHeaders = request.headers();
|
|
401
|
+
console.log('[Playwright] Guest intercepted request:', request.url());
|
|
402
|
+
|
|
403
|
+
const extractedHeaders = {
|
|
404
|
+
'cookie': reqHeaders['cookie'] || '',
|
|
405
|
+
'bx-ua': reqHeaders['bx-ua'] || '',
|
|
406
|
+
'bx-umidtoken': reqHeaders['bx-umidtoken'] || '',
|
|
407
|
+
'bx-v': reqHeaders['bx-v'] || '2.5.36',
|
|
408
|
+
'user-agent': reqHeaders['user-agent'] || 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
if (extractedHeaders['bx-ua']) {
|
|
412
|
+
console.log('[Playwright] Guest: Successfully captured bx-ua');
|
|
413
|
+
guestHeadersCache = { headers: extractedHeaders, timestamp: Date.now() };
|
|
414
|
+
await route.abort('aborted');
|
|
415
|
+
await guestPage!.unroute('**/api/v2/chat/completions*', routeHandler);
|
|
416
|
+
|
|
417
|
+
import('./qwen.js').then(m => m.disableNativeTools('guest').catch(() => {}));
|
|
418
|
+
|
|
419
|
+
resolve(extractedHeaders);
|
|
420
|
+
} else {
|
|
421
|
+
console.log('[Playwright] Guest: Request missing bx-ua, continuing route. Headers:', Object.keys(reqHeaders));
|
|
422
|
+
await route.continue();
|
|
423
|
+
// If it's the completions request and we still don't have bx-ua, we might need to resolve anyway
|
|
424
|
+
// or the UI interaction failed to trigger the SDK.
|
|
425
|
+
if (request.url().includes('/api/v2/chat/completions')) {
|
|
426
|
+
console.warn('[Playwright] Guest: Completions request made without bx-ua. Resolving with available headers.');
|
|
427
|
+
guestHeadersCache = { headers: extractedHeaders, timestamp: Date.now() };
|
|
428
|
+
await guestPage!.unroute('**/api/v2/chat/completions*', routeHandler);
|
|
429
|
+
resolve(extractedHeaders);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
guestPage!.route('**/api/v2/chat/completions*', routeHandler).then(async () => {
|
|
435
|
+
const inputSelector = 'textarea:visible, [contenteditable="true"]:visible';
|
|
436
|
+
try {
|
|
437
|
+
await guestPage!.waitForSelector(inputSelector, { timeout: 10000 });
|
|
438
|
+
await guestPage!.focus(inputSelector);
|
|
439
|
+
await guestPage!.fill(inputSelector, '');
|
|
440
|
+
await guestPage!.type(inputSelector, 'a', { delay: 50 });
|
|
441
|
+
await sleep(1000);
|
|
442
|
+
|
|
443
|
+
const selectors = ['.message-input-right-button-send .send-button', '.chat-prompt-send-button', 'button.send-button'];
|
|
444
|
+
let clicked = false;
|
|
445
|
+
for (const selector of selectors) {
|
|
446
|
+
const btn = await guestPage!.$(selector);
|
|
447
|
+
if (btn && await btn.isVisible()) {
|
|
448
|
+
await btn.click({ force: true, delay: 50 }).catch(() => {});
|
|
449
|
+
clicked = true;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (!clicked) {
|
|
454
|
+
await guestPage!.keyboard.press('Enter');
|
|
455
|
+
}
|
|
456
|
+
} catch (e) {
|
|
457
|
+
clearTimeout(timeout);
|
|
458
|
+
reject(e);
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
358
464
|
export async function getQwenHeaders(forceNew = false, accountId?: string): Promise<{ headers: Record<string, string>, chatSessionId: string, parentMessageId: string | null }> {
|
|
465
|
+
if (accountId === 'guest') {
|
|
466
|
+
const headers = await getGuestHeaders();
|
|
467
|
+
return { headers, chatSessionId: 'guest-session', parentMessageId: null };
|
|
468
|
+
}
|
|
469
|
+
|
|
359
470
|
const cacheKey = accountId || 'global';
|
|
360
471
|
const cache = getAccountHeaderCache(cacheKey);
|
|
361
472
|
|
package/src/services/qwen.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getQwenHeaders, getBasicHeaders } from './playwright.js';
|
|
1
|
+
import { getQwenHeaders, getBasicHeaders, getGuestHeaders } from './playwright.js';
|
|
2
2
|
import { MAX_PAYLOAD_SIZE } from '../core/model-registry.js';
|
|
3
3
|
import { markAccountRateLimited } from '../core/account-manager.js';
|
|
4
4
|
import crypto from 'crypto';
|
|
@@ -415,19 +415,56 @@ export async function createQwenStream(
|
|
|
415
415
|
files?: QwenFileEntry[],
|
|
416
416
|
pendingMultimodal?: Array<Array<{ type: string; text?: string; image_url?: { url: string }; video_url?: { url: string }; audio_url?: { url: string }; file_url?: { url: string } }>>
|
|
417
417
|
): Promise<{ stream: ReadableStream, headers: Record<string, string>, uiSessionId: string, controller: AbortController, accountId: string }> {
|
|
418
|
-
let
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
418
|
+
let chatId: string;
|
|
419
|
+
let chatHeaders: Record<string, string>;
|
|
420
|
+
|
|
421
|
+
if (accountId === 'guest') {
|
|
422
|
+
chatHeaders = await getGuestHeaders();
|
|
423
|
+
const response = await fetch('https://chat.qwen.ai/api/v2/chats/new', {
|
|
424
|
+
method: 'POST',
|
|
425
|
+
headers: {
|
|
426
|
+
'accept': 'application/json, text/plain, */*',
|
|
427
|
+
'accept-language': 'pt-BR,pt;q=0.9',
|
|
428
|
+
'content-type': 'application/json',
|
|
429
|
+
cookie: chatHeaders['cookie'],
|
|
430
|
+
origin: 'https://chat.qwen.ai',
|
|
431
|
+
referer: 'https://chat.qwen.ai/c/guest',
|
|
432
|
+
'user-agent': chatHeaders['user-agent'],
|
|
433
|
+
'x-request-id': crypto.randomUUID(),
|
|
434
|
+
'bx-v': chatHeaders['bx-v'],
|
|
435
|
+
'bx-ua': chatHeaders['bx-ua'],
|
|
436
|
+
'bx-umidtoken': chatHeaders['bx-umidtoken'],
|
|
437
|
+
...getClientHintsHeaders(),
|
|
438
|
+
},
|
|
439
|
+
body: JSON.stringify({
|
|
440
|
+
title: 'Guest Chat',
|
|
441
|
+
models: [modelId.replace('-no-thinking', '')],
|
|
442
|
+
chat_mode: 'guest',
|
|
443
|
+
chat_type: 't2t',
|
|
444
|
+
timestamp: Date.now(),
|
|
445
|
+
project_id: '',
|
|
446
|
+
}),
|
|
447
|
+
signal: AbortSignal.timeout(30000),
|
|
448
|
+
});
|
|
449
|
+
if (!response.ok) throw new Error(`Failed to create guest chat: ${response.status}`);
|
|
450
|
+
const json = await response.json();
|
|
451
|
+
chatId = json.chat_id || json.id || json.data?.chat_id || json.data?.id;
|
|
452
|
+
if (!chatId) throw new Error(`Unexpected guest chat response: ${JSON.stringify(json).slice(0, 200)}`);
|
|
453
|
+
} else {
|
|
454
|
+
let chatEntry: WarmPoolEntry;
|
|
455
|
+
try {
|
|
456
|
+
chatEntry = await getWarmedChat(accountId);
|
|
457
|
+
} catch (err: any) {
|
|
458
|
+
if (err.message?.includes('chat is in progress') || err.message?.includes('The chat is in progress')) {
|
|
459
|
+
const retryAfterMs = 2000 + Math.floor(Math.random() * 2000);
|
|
460
|
+
throw new RetryableQwenStreamError(`Qwen: ${err.message}`, retryAfterMs);
|
|
461
|
+
}
|
|
462
|
+
throw err;
|
|
425
463
|
}
|
|
426
|
-
|
|
464
|
+
chatId = chatEntry.chatId;
|
|
465
|
+
chatHeaders = chatEntry.headers;
|
|
427
466
|
}
|
|
428
467
|
|
|
429
|
-
const chatId = chatEntry.chatId;
|
|
430
|
-
const chatHeaders = chatEntry.headers;
|
|
431
468
|
const actualParentId: string | null = null;
|
|
432
469
|
|
|
433
470
|
// Process pending multimodal uploads — requires full headers with bx-ua/bx-umidtoken
|
|
@@ -473,7 +510,7 @@ export async function createQwenStream(
|
|
|
473
510
|
version: '2.1',
|
|
474
511
|
incremental_output: true,
|
|
475
512
|
chat_id: chatId,
|
|
476
|
-
chat_mode: 'normal',
|
|
513
|
+
chat_mode: accountId === 'guest' ? 'guest' : 'normal',
|
|
477
514
|
model: model,
|
|
478
515
|
parent_id: actualParentId,
|
|
479
516
|
messages: [
|
|
@@ -528,7 +565,7 @@ export async function createQwenStream(
|
|
|
528
565
|
'content-type': 'application/json',
|
|
529
566
|
'cookie': chatHeaders['cookie'],
|
|
530
567
|
'origin': 'https://chat.qwen.ai',
|
|
531
|
-
'referer': `https://chat.qwen.ai/c/${chatId}`,
|
|
568
|
+
'referer': accountId === 'guest' ? 'https://chat.qwen.ai/c/guest' : `https://chat.qwen.ai/c/${chatId}`,
|
|
532
569
|
'sec-fetch-dest': 'empty',
|
|
533
570
|
'sec-fetch-mode': 'cors',
|
|
534
571
|
'sec-fetch-site': 'same-origin',
|
|
@@ -584,7 +621,7 @@ export async function createQwenStream(
|
|
|
584
621
|
|
|
585
622
|
const retryContentType = retryResponse.headers.get('content-type') || '';
|
|
586
623
|
if (retryResponse.ok && retryContentType.includes('text/event-stream') && retryResponse.body) {
|
|
587
|
-
return { stream: retryResponse.body, headers: freshHeaders, uiSessionId: chatId, controller: retryController, accountId:
|
|
624
|
+
return { stream: retryResponse.body, headers: freshHeaders, uiSessionId: chatId, controller: retryController, accountId: accountId || 'guest' };
|
|
588
625
|
}
|
|
589
626
|
|
|
590
627
|
const retryPeek = await retryResponse.clone().text().catch(() => '');
|
|
@@ -617,7 +654,7 @@ export async function createQwenStream(
|
|
|
617
654
|
} catch (e) {
|
|
618
655
|
if (e instanceof QwenUpstreamError) throw e;
|
|
619
656
|
}
|
|
620
|
-
return { stream: retryResponse.body, headers: freshHeaders, uiSessionId: chatId, controller: retryController, accountId:
|
|
657
|
+
return { stream: retryResponse.body, headers: freshHeaders, uiSessionId: chatId, controller: retryController, accountId: accountId || 'guest' };
|
|
621
658
|
}
|
|
622
659
|
} catch (retryErr) {
|
|
623
660
|
if (retryErr instanceof QwenUpstreamError) throw retryErr;
|
|
@@ -703,5 +740,5 @@ export async function createQwenStream(
|
|
|
703
740
|
throw new Error(`Failed to fetch from Qwen: ${response.status} ${response.statusText} - ${errText}`);
|
|
704
741
|
}
|
|
705
742
|
|
|
706
|
-
return { stream: response.body, headers: chatHeaders, uiSessionId: chatId, controller, accountId:
|
|
743
|
+
return { stream: response.body, headers: chatHeaders, uiSessionId: chatId, controller, accountId: accountId || 'guest' };
|
|
707
744
|
}
|