llm.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. function registerLlmRoutes(app, options) {
  2. const llmBaseUrl = options.llmBaseUrl;
  3. const llmApiKey = options.llmApiKey;
  4. // 非流式调用已迁移至云函数 proxyHub(cloud-functions/08-proxyHub.js)。
  5. // 但流式 SSE 调用必须走本地 Express(云函数不支持流式输出),由前端 fetch 直连。
  6. app.post('/api/llm/chat/stream', async (req, res) => {
  7. try {
  8. const { model, messages, temperature, max_tokens, ...rest } = req.body || {};
  9. if (!Array.isArray(messages) || messages.length === 0) {
  10. return res.status(400).json({ error: '缺少 messages 参数' });
  11. }
  12. const payload = {
  13. model: model || 'gpt-4o',
  14. messages,
  15. temperature: temperature ?? 0.7,
  16. max_tokens: max_tokens || 4096,
  17. ...rest,
  18. stream: true,
  19. };
  20. const url = `${llmBaseUrl}/v1/chat/completions`;
  21. console.log(`🤖 [Stream] LLM Chat 请求: model=${payload.model}, messages=${messages.length}条`);
  22. const upstream = await fetch(url, {
  23. method: 'POST',
  24. headers: {
  25. 'Content-Type': 'application/json',
  26. 'Authorization': `Bearer ${llmApiKey}`,
  27. },
  28. body: JSON.stringify(payload),
  29. });
  30. if (!upstream.ok) {
  31. const errText = await upstream.text();
  32. console.error('🤖 [Stream] 上游错误:', upstream.status, errText);
  33. return res.status(upstream.status).json({ error: errText || `upstream ${upstream.status}` });
  34. }
  35. res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
  36. res.setHeader('Cache-Control', 'no-cache, no-transform');
  37. res.setHeader('Connection', 'keep-alive');
  38. res.setHeader('X-Accel-Buffering', 'no');
  39. res.flushHeaders?.();
  40. const reader = upstream.body.getReader();
  41. const onAbort = () => {
  42. try { reader.cancel(); } catch {}
  43. };
  44. req.on('close', onAbort);
  45. try {
  46. while (true) {
  47. const { value, done } = await reader.read();
  48. if (done) break;
  49. if (value) res.write(Buffer.from(value));
  50. }
  51. } finally {
  52. req.off('close', onAbort);
  53. res.end();
  54. }
  55. } catch (err) {
  56. console.error('🤖 [Stream] 异常:', err.message);
  57. if (!res.headersSent) {
  58. res.status(500).json({ error: `LLM 流式请求失败: ${err.message}` });
  59. } else {
  60. try { res.end(); } catch {}
  61. }
  62. }
  63. });
  64. }
  65. module.exports = { registerLlmRoutes };