如何通过NestJS的SSE将LangChain流式响应转发至前端?
解决方案
你的问题在于直接使用@Res()手动写入响应,没有遵循NestJS SSE的规范。NestJS的@Sse()装饰器要求方法返回Server-Sent Event格式的数据流,而非直接操作响应对象。以下是修正后的实现方案:
核心修改点
- 移除
@Res()注入,让NestJS自动处理SSE响应 - 返回符合SSE规范的数据流(支持
Observable或异步迭代器) - 在LangChain的回调中按SSE格式发送每个token
实现代码(Observable方式)
import { Observable, Subject } from 'rxjs'; import { Sse, ServerSentEvent } from '@nestjs/common'; import { CreateChatRequestDto } from './dto/create-chat-request.dto'; @Sse('sse') createChat( @Body() createChatRequestDto: CreateChatRequestDto, ): Observable<ServerSentEvent<string>> { const subject = new Subject<ServerSentEvent<string>>(); (async () => { try { const { chain, sanitizedQuestion, histories } = await this.chatService.createChat(createChatRequestDto); await chain.call( { question: sanitizedQuestion, chat_history: histories || [], }, [ { handleLLMNewToken(token: string) { // 按SSE规范发送数据,每个消息以data字段包裹 subject.next({ data: token }); }, handleLLMEnd() { // 流结束时完成Subject subject.complete(); }, handleLLMError(error: Error) { // 错误时发送错误信息并结束 subject.error(error); }, }, ], ); } catch (error) { subject.error(error); } })(); return subject.asObservable(); }
实现代码(异步迭代器方式)
如果你更倾向于使用异步迭代器(更贴合Node.js流式风格),可以这样写:
import { Sse, ServerSentEvent } from '@nestjs/common'; import { CreateChatRequestDto } from './dto/create-chat-request.dto'; @Sse('sse') async *createChat( @Body() createChatRequestDto: CreateChatRequestDto, ): AsyncGenerator<ServerSentEvent<string>> { const { chain, sanitizedQuestion, histories } = await this.chatService.createChat(createChatRequestDto); // 创建一个Promise用于等待流式完成 await new Promise((resolve, reject) => { chain.call( { question: sanitizedQuestion, chat_history: histories || [], }, [ { handleLLMNewToken: (token: string) => { // 通过yield发送每个SSE消息 yield { data: token }; }, handleLLMEnd: () => { resolve(null); }, handleLLMError: (error: Error) => { reject(error); }, }, ], ); }); }
关键说明
- SSE格式要求:每个消息必须以
data: [内容]\n\n格式发送,NestJS的ServerSentEvent会自动帮你处理这个格式转换。 - 流的生命周期:必须在流式结束(
handleLLMEnd)或出错(handleLLMError)时结束数据流,避免前端连接一直挂起。 - HTTP方法注意:SSE通常使用
GET请求(因为是客户端主动拉取流),如果你坚持用POST,需要确保前端请求时设置正确的Accept: text/event-stream头。
内容的提问来源于stack exchange,提问作者정주호
相关产品推荐
相关产品推荐

