feat: 初始化 Nest 服务骨架与多平台 Chat SSE 网关
- 新增 NestJS + Fastify 入口、配置模块与 Swagger 集成 - 划分 client-app / admin-app 与 shared-domain ai-gateway - 实现统一 SSE Chat 接口,支持千问、DeepSeek、火山引擎非流式上游与网关分片输出 - 补充项目方案与 JWT 最小实现文档 Made-with: Cursor
This commit is contained in:
52
src/apps/client-app/chat/controllers/chat.controller.ts
Normal file
52
src/apps/client-app/chat/controllers/chat.controller.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Body, Controller, Inject, Post, Res } from '@nestjs/common';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { StreamChatRequest } from '@shared/ai-gateway/types/chat.types';
|
||||
import { ProviderRouterService } from '@shared/ai-gateway/router/provider-router.service';
|
||||
|
||||
function formatSse(event: string, data: unknown) {
|
||||
return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
@Controller('client/v1/chat')
|
||||
export class ChatController {
|
||||
constructor(
|
||||
@Inject(ProviderRouterService)
|
||||
private readonly router: ProviderRouterService,
|
||||
) {}
|
||||
|
||||
@Post('completions/stream')
|
||||
async streamChat(
|
||||
@Body() body: StreamChatRequest,
|
||||
@Res() reply: FastifyReply,
|
||||
) {
|
||||
const response = await this.router.routeAndStream(body);
|
||||
|
||||
reply.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
|
||||
reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');
|
||||
reply.raw.setHeader('Connection', 'keep-alive');
|
||||
reply.raw.setHeader('X-Accel-Buffering', 'no');
|
||||
reply.raw.flushHeaders?.();
|
||||
|
||||
reply.raw.write(
|
||||
formatSse('meta', {
|
||||
requestId: response.requestId,
|
||||
platform: response.providerCode,
|
||||
model: response.model,
|
||||
}),
|
||||
);
|
||||
|
||||
for (const chunk of response.chunks) {
|
||||
reply.raw.write(formatSse('delta', { delta: chunk.content }));
|
||||
await sleep(120);
|
||||
}
|
||||
|
||||
reply.raw.write(formatSse('usage', response.usage));
|
||||
reply.raw.write(formatSse('done', { finishReason: 'stop' }));
|
||||
reply.raw.end();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user