phase 5 part 1: typescript, vue, react, ssr(Next.js 16) topics with lessons, scenarios, interviews, verified examples
This commit is contained in:
1 parent
7a32d2c9a6
commit
7b5cae7d24
39 files changed
+2482
-48
No files matched your search
@@ -0,0 +1,35 @@
|
||||
// 不自动运行(Next.js 路由处理器,npm run dev 后 POST /api/chat)
|
||||
// 服务端调用大模型并以流的形式返回;模型密钥只在服务端,浏览器拿不到。
|
||||
export const runtime = "nodejs";
|
||||
|
||||
async function* fakeLLM(question: string, signal: AbortSignal): AsyncGenerator<string> {
|
||||
// 真实项目:调用模型 SDK 的流式接口,并把 signal 传进去
|
||||
const words = `关于“${question}”:签收 7 天内可以无理由退货。`.split("");
|
||||
for (const w of words) {
|
||||
if (signal.aborted) return; // 用户停止或断开:不再继续生成(节省费用)
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
yield w;
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: Request): Promise<Response> {
|
||||
const { question } = (await request.json()) as { question?: unknown };
|
||||
if (typeof question !== "string" || question.length === 0 || question.length > 500) {
|
||||
return Response.json({ error: "question 必须是 1~500 字的字符串" }, { status: 400 });
|
||||
}
|
||||
const encoder = new TextEncoder();
|
||||
const stream = new ReadableStream<Uint8Array>({
|
||||
async start(controller) {
|
||||
try {
|
||||
for await (const token of fakeLLM(question, request.signal)) {
|
||||
controller.enqueue(encoder.encode(token));
|
||||
}
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
return new Response(stream, {
|
||||
headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "no-cache", "X-Accel-Buffering": "no" },
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user