TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1import { withUser, parseBody } from "@/lib/api";2import { LIMITS } from "@/lib/rate-limit";3import { chatRequestSchema } from "@/lib/chat/schemas";4import { prepareTurn, runTurn, toPublicMessage } from "@/lib/chat/service";5import { sseResponse } from "@/lib/chat/sse";67export const dynamic = "force-dynamic";8export const maxDuration = 900;910/**11 * POST /api/chat — streams a model turn as Server-Sent Events.12 * Events: meta → (text-delta | reasoning-delta | tool-* | citation | server-tool)* → done | error13 *14 * `meta` carries `ephemeral` (temporary chat: conversationId is the "ephemeral" sentinel and nothing15 * is persisted except the usage record) and `requestId` (shown in the error details sheet).16 */17export const POST = withUser(18 async (ctx) => {19 const input = await parseBody(ctx.req, chatRequestSchema, 2_500_000);20 const turn = await prepareTurn({ userId: ctx.user.id, requestId: ctx.requestId, ip: ctx.ip }, input);21 return sseResponse(22 async (emit, signal) => {23 // Abort provider work when the client disconnects.24 const onAbort = () => controller.abort();25 const controller = new AbortController();26 signal.addEventListener("abort", onAbort, { once: true });27 ctx.req.signal?.addEventListener("abort", onAbort, { once: true });28 emit({29 type: "meta",30 conversationId: turn.conversation.id,31 isNewConversation: turn.isNewConversation,32 userMessage: turn.userMessage ? toPublicMessage(turn.userMessage) : null,33 assistantMessageId: turn.assistantMessage.id,34 modelKey: turn.model.key,35 clientId: input.clientId,36 ephemeral: turn.ephemeral,37 requestId: ctx.requestId,38 });39 const outcome = await runTurn({ userId: ctx.user.id, requestId: ctx.requestId, ip: ctx.ip }, turn, emit, controller.signal);40 if (outcome.error && !outcome.message.content) emit({ type: "error", error: outcome.error });41 emit({ type: "done", message: toPublicMessage(outcome.message), usage: outcome.usage, costUsd: outcome.costUsd, latencyMs: outcome.latencyMs, ttftMs: outcome.ttftMs, status: outcome.status, title: outcome.title, error: outcome.error });42 },43 { requestId: ctx.requestId },44 );45 },46 { limit: { ...LIMITS.chat, key: "chat" } },47);48