dvc890 commited on
Commit
fc13b52
Β·
verified Β·
1 Parent(s): 0033d27

Update ai-routes.js

Browse files
Files changed (1) hide show
  1. ai-routes.js +32 -6
ai-routes.js CHANGED
@@ -214,10 +214,13 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
214
  const lastThinking = user?.doubaoState?.thinkingState;
215
 
216
  // 1. Decide on Caching
 
217
  const requestCachingType = enableSearch ? "disabled" : "enabled";
218
 
219
  // 2. Decide on ID Reuse
 
220
  let idToSend = null;
 
221
  if (lastId && !enableSearch && (!!lastThinking === !!enableThinking)) {
222
  idToSend = lastId;
223
  }
@@ -225,15 +228,19 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
225
  // 3. Build Input
226
  let inputPayload = [];
227
  if (idToSend) {
228
- // INCREMENTAL MODE
 
 
229
  const fullInputs = convertGeminiToDoubaoInput(baseParams);
 
230
  const systemMsg = fullInputs.find(m => m.role === 'system');
 
231
  const lastMsg = fullInputs[fullInputs.length - 1];
232
 
233
  if (systemMsg) inputPayload.push(systemMsg);
234
  if (lastMsg) inputPayload.push(lastMsg);
235
  } else {
236
- // FULL MODE
237
  inputPayload = convertGeminiToDoubaoInput(baseParams);
238
  }
239
  // --- CONTEXT LOGIC END ---
@@ -242,10 +249,15 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
242
  const requestBody = {
243
  model: endpointId,
244
  stream: true,
 
245
  input: inputPayload,
 
 
246
  thinking: { type: enableThinking ? "enabled" : "disabled" },
247
  caching: { type: requestCachingType },
 
248
  ...(idToSend && { previous_response_id: idToSend }),
 
249
  // CORRECTED TOOL STRUCTURE: just type
250
  ...(enableSearch && { tools: [{ type: "web_search" }] })
251
  };
@@ -302,7 +314,7 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
302
  const jsonStr = trimmed.substring(6);
303
  const json = JSON.parse(jsonStr);
304
 
305
- // --- V3 EVENT PARSING ---
306
  const eventType = json.type;
307
 
308
  // 1. Response ID
@@ -319,13 +331,14 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
319
  }
320
 
321
  // 3. Reasoning / Thinking (Reasoning Delta)
322
- // Event: response.reasoning_text.delta OR response.reasoning_summary_text.delta
323
  if ((eventType === 'response.reasoning_text.delta' || eventType === 'response.reasoning_summary_text.delta') && json.delta) {
324
  res.write(`data: ${JSON.stringify({ type: 'thinking', content: json.delta })}\n\n`);
325
  if (res.flush) res.flush();
326
  }
327
 
328
  // 4. Search Status
 
329
  if (eventType === 'response.web_search_call.searching') {
330
  res.write(`data: ${JSON.stringify({ type: 'search', status: 'searching' })}\n\n`);
331
  if (res.flush) res.flush();
@@ -339,7 +352,7 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
339
  }
340
  }
341
 
342
- // Fallback for Legacy/Chat Format (Safety net)
343
  if (!eventType && json.response?.output_text?.delta) {
344
  const content = json.response.output_text.delta;
345
  fullText += content;
@@ -366,6 +379,7 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
366
  }
367
 
368
  // 4. Update User State (Context Persistence)
 
369
  if (!enableSearch && newResponseID) {
370
  await User.findOneAndUpdate({ username }, {
371
  doubaoState: {
@@ -375,6 +389,7 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
375
  });
376
  console.log(`[AI] πŸ’Ύ Doubao Context Updated: ID=${newResponseID}`);
377
  } else {
 
378
  await User.findOneAndUpdate({ username }, {
379
  doubaoState: { responseId: null, thinkingState: null }
380
  });
@@ -386,15 +401,22 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
386
  } catch (e) {
387
  console.error(`[AI] ❌ Doubao Request Failed!`);
388
 
 
389
  if (e.response) {
390
  console.error(`[AI] ❌ Status: ${e.response.status}`);
 
 
391
  if (e.response.data) {
392
  try {
 
393
  if (typeof e.response.data.on === 'function' || (e.response.data && typeof e.response.data.read === 'function')) {
394
  let errData = '';
395
- for await (const chunk of e.response.data) { errData += chunk.toString(); }
 
 
396
  console.error(`[AI] ❌ Response Body (Stream):`, errData);
397
  } else {
 
398
  console.error(`[AI] ❌ Response Body (Object):`, JSON.stringify(e.response.data, null, 2));
399
  }
400
  } catch (readErr) {
@@ -407,10 +429,14 @@ async function streamDoubao(baseParams, res, username, mode = 'chat', config, en
407
  console.log(`[AI] πŸ”„ Quota exceeded, trying next key...`);
408
  continue;
409
  }
 
 
410
  if (e.response?.status === 400 && idToSend) {
411
  console.log(`[AI] ⚠️ 400 Error with ID. Retrying with full history...`);
412
  idToSend = null;
 
413
  await User.findOneAndUpdate({ username }, { doubaoState: { responseId: null } });
 
414
  throw new Error("Context invalid. Please retry.");
415
  }
416
  throw e;
 
214
  const lastThinking = user?.doubaoState?.thinkingState;
215
 
216
  // 1. Decide on Caching
217
+ // Rule: Search disables cache. Otherwise enable it.
218
  const requestCachingType = enableSearch ? "disabled" : "enabled";
219
 
220
  // 2. Decide on ID Reuse
221
+ // Rule: Must have ID + No Search + Thinking State Unchanged
222
  let idToSend = null;
223
+ // Note: 'undefined' matches 'false' effectively for our boolean logic check if we cast both
224
  if (lastId && !enableSearch && (!!lastThinking === !!enableThinking)) {
225
  idToSend = lastId;
226
  }
 
228
  // 3. Build Input
229
  let inputPayload = [];
230
  if (idToSend) {
231
+ // INCREMENTAL MODE: We assume the server has context.
232
+ // We need to send System Prompt + NEWEST Message only.
233
+
234
  const fullInputs = convertGeminiToDoubaoInput(baseParams);
235
+
236
  const systemMsg = fullInputs.find(m => m.role === 'system');
237
+ // Get the last message (User's new question)
238
  const lastMsg = fullInputs[fullInputs.length - 1];
239
 
240
  if (systemMsg) inputPayload.push(systemMsg);
241
  if (lastMsg) inputPayload.push(lastMsg);
242
  } else {
243
+ // FULL MODE: ID Invalid/Missing/SearchOn -> Send Full History
244
  inputPayload = convertGeminiToDoubaoInput(baseParams);
245
  }
246
  // --- CONTEXT LOGIC END ---
 
249
  const requestBody = {
250
  model: endpointId,
251
  stream: true,
252
+ // Doubao Bot API uses 'input' parameter with specific content types
253
  input: inputPayload,
254
+
255
+ // CRITICAL: Explicitly send disabled/enabled as requested by user
256
  thinking: { type: enableThinking ? "enabled" : "disabled" },
257
  caching: { type: requestCachingType },
258
+ // Add ID if valid
259
  ...(idToSend && { previous_response_id: idToSend }),
260
+ // Add Search Tools if enabled
261
  // CORRECTED TOOL STRUCTURE: just type
262
  ...(enableSearch && { tools: [{ type: "web_search" }] })
263
  };
 
314
  const jsonStr = trimmed.substring(6);
315
  const json = JSON.parse(jsonStr);
316
 
317
+ // --- V3 EVENT PARSING (Flat Structure) ---
318
  const eventType = json.type;
319
 
320
  // 1. Response ID
 
331
  }
332
 
333
  // 3. Reasoning / Thinking (Reasoning Delta)
334
+ // Support both regular reasoning and summary reasoning
335
  if ((eventType === 'response.reasoning_text.delta' || eventType === 'response.reasoning_summary_text.delta') && json.delta) {
336
  res.write(`data: ${JSON.stringify({ type: 'thinking', content: json.delta })}\n\n`);
337
  if (res.flush) res.flush();
338
  }
339
 
340
  // 4. Search Status
341
+ // Explicitly catch the searching event
342
  if (eventType === 'response.web_search_call.searching') {
343
  res.write(`data: ${JSON.stringify({ type: 'search', status: 'searching' })}\n\n`);
344
  if (res.flush) res.flush();
 
352
  }
353
  }
354
 
355
+ // Fallback for Legacy/Chat Format (Safety net, in case they revert or mix formats)
356
  if (!eventType && json.response?.output_text?.delta) {
357
  const content = json.response.output_text.delta;
358
  fullText += content;
 
379
  }
380
 
381
  // 4. Update User State (Context Persistence)
382
+ // Rules: Only save ID if Search was OFF.
383
  if (!enableSearch && newResponseID) {
384
  await User.findOneAndUpdate({ username }, {
385
  doubaoState: {
 
389
  });
390
  console.log(`[AI] πŸ’Ύ Doubao Context Updated: ID=${newResponseID}`);
391
  } else {
392
+ // If search was ON, ID is invalid for next turn. Clear it to force full history next time.
393
  await User.findOneAndUpdate({ username }, {
394
  doubaoState: { responseId: null, thinkingState: null }
395
  });
 
401
  } catch (e) {
402
  console.error(`[AI] ❌ Doubao Request Failed!`);
403
 
404
+ // ERROR RESPONSE BODY CAPTURE (Stream Handling)
405
  if (e.response) {
406
  console.error(`[AI] ❌ Status: ${e.response.status}`);
407
+ console.error(`[AI] ❌ Headers:`, JSON.stringify(e.response.headers, null, 2));
408
+
409
  if (e.response.data) {
410
  try {
411
+ // Check if it's a stream we need to consume
412
  if (typeof e.response.data.on === 'function' || (e.response.data && typeof e.response.data.read === 'function')) {
413
  let errData = '';
414
+ for await (const chunk of e.response.data) {
415
+ errData += chunk.toString();
416
+ }
417
  console.error(`[AI] ❌ Response Body (Stream):`, errData);
418
  } else {
419
+ // It's likely a JSON object already if axios parsed it (but responseType='stream' usually prevents this)
420
  console.error(`[AI] ❌ Response Body (Object):`, JSON.stringify(e.response.data, null, 2));
421
  }
422
  } catch (readErr) {
 
429
  console.log(`[AI] πŸ”„ Quota exceeded, trying next key...`);
430
  continue;
431
  }
432
+ // If error is 400 (Bad Request), it might be due to invalid ID.
433
+ // Retry ONCE with ID=null (Full History) if we tried with ID
434
  if (e.response?.status === 400 && idToSend) {
435
  console.log(`[AI] ⚠️ 400 Error with ID. Retrying with full history...`);
436
  idToSend = null;
437
+ // Recursive retry with modified params (careful with infinite loops, handled by loop structure somewhat, but strictly we should just clear DB and fail this request gracefully or implement simpler retry)
438
  await User.findOneAndUpdate({ username }, { doubaoState: { responseId: null } });
439
+ // We won't recurse here to keep it simple, just fail and let user retry (which will pick up null ID)
440
  throw new Error("Context invalid. Please retry.");
441
  }
442
  throw e;