基于Node.js+Express实现OpenAI流式补全遇类型错误及推送问题
解决TypeScript类型错误并通过Socket.IO实现流式响应推送
一、类型错误原因与解决
报错Property 'on' does not exist on type 'CreateCompletionResponse'.ts(2339)的核心原因是:当设置stream: true时,OpenAI API返回的不是完整的CreateCompletionResponse对象,而是流式响应(AsyncIterable或Node.js可读流),但你的代码仍用CreateCompletionResponse类型接收,导致TypeScript无法识别on方法。
1. 基于OpenAI官方SDK的解决方式(推荐)
如果使用OpenAI v4+ SDK,当stream: true时,返回值是AsyncIterable<ChatCompletionChunk>,需用for await...of遍历流,而非on事件:
import OpenAI from 'openai'; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); // 在Socket.IO事件处理或路由中调用 async function processChatStream(userMessage: string, socket: any) { const stream = await openai.chat.completions.create({ model: "gpt-3.5-turbo", messages: [{ role: "user", content: userMessage }], stream: true, }); // 遍历每个流式chunk for await (const chunk of stream) { const content = chunk.choices[0]?.delta?.content || ''; if (content) { // 直接推送chunk到前端 socket.emit('chat-stream', content); } } // 推送结束标记 socket.emit('chat-stream', '[DONE]'); }
2. 基于HTTP请求库(如Axios)的解决方式
若直接调用OpenAI API,需将响应数据断言为NodeJS.ReadableStream,让TypeScript识别on方法:
import axios from 'axios'; async function processChatStream(userMessage: string, socket: any) { const response = await axios.post('https://api.openai.com/v1/chat/completions', { model: "gpt-3.5-turbo", messages: [{ role: "user", content: userMessage }], stream: true, }, { responseType: 'stream', headers: { 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`, 'Content-Type': 'application/json', } }); // 断言为Node.js可读流 const stream = response.data as NodeJS.ReadableStream; stream.on('data', (chunk: Buffer) => { // 解析OpenAI的SSE格式响应 const lines = chunk.toString().split('\n').filter(line => line.trim() !== ''); for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6); if (data === '[DONE]') { socket.emit('chat-stream', '[DONE]'); return; } try { const completion = JSON.parse(data); const content = completion.choices[0]?.delta?.content || ''; if (content) socket.emit('chat-stream', content); } catch (err) { console.error('解析chunk失败:', err); } } } }); stream.on('error', (err) => { console.error('流式响应错误:', err); socket.emit('chat-error', '请求失败,请重试'); }); }
二、通过Socket.IO推送流式响应到前端
1. 服务器端集成Socket.IO
在Express+TypeScript服务中初始化Socket.IO,并处理客户端请求:
import express from 'express'; import { createServer } from 'http'; import { Server } from 'socket.io'; const app = express(); const server = createServer(app); const io = new Server(server, { cors: { origin: "http://your-frontend-domain.com", // 替换为你的前端域名 methods: ["GET", "POST"] } }); // 监听客户端连接 io.on('connection', (socket) => { console.log('客户端已连接'); // 接收前端的聊天请求 socket.on('send-chat', async (userMessage) => { await processChatStream(userMessage, socket); }); socket.on('disconnect', () => { console.log('客户端已断开'); }); }); server.listen(3000, () => { console.log('服务运行在端口3000'); });
2. 前端接收流式响应
前端通过Socket.IO监听响应,实现逐字显示效果:
import { io } from "socket.io-client"; const socket = io("http://your-server-domain.com:3000"); const responseElement = document.getElementById('chat-response'); let fullResponse = ''; // 发送用户消息 function sendMessage(message) { socket.emit('send-chat', message); fullResponse = ''; responseElement.textContent = ''; } // 接收流式响应 socket.on('chat-stream', (content) => { if (content === '[DONE]') { console.log('响应完成'); return; } fullResponse += content; responseElement.textContent = fullResponse; }); // 处理错误 socket.on('chat-error', (errorMsg) => { alert(errorMsg); });
内容的提问来源于stack exchange,提问作者Alexxino
相关产品推荐
相关产品推荐

