SPB Git
3.0 KB · 44 lines typescript
1import { z } from "zod";2import { guarded, body, sse } from "@/lib/api";3import { createConversation, getConversation, insertMessage, getActiveMessages } from "@/lib/chat/store";4import { runAssistant } from "@/lib/chat/engine";5import { sql } from "@/lib/db";6import type { Part } from "@/lib/ai/types";78export const maxDuration = 900;910const schema = z.object({11  conversationId: z.string().optional(),12  projectId: z.string().nullable().optional(),13  text: z.string().max(200_000),14  attachmentIds: z.array(z.string()).max(20).optional(),15  modelId: z.string().optional(),16  routingMode: z.enum(["manual", "fast", "quality", "economy", "auto"]).optional(),17  settings: z.object({ effort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(), maxOutputTokens: z.number().int().min(256).max(128_000).optional(), temperature: z.number().min(0).max(2).optional(), reasoningSummary: z.boolean().optional() }).optional(),18  toolsEnabled: z.boolean().optional(),19  toolNames: z.array(z.string()).optional(),20});2122export const POST = guarded(async (req) => {23  const b = await body(req, schema);24  let conv = b.conversationId ? await getConversation(b.conversationId) : null;25  if (b.conversationId && !conv) return Response.json({ error: "Conversation introuvable" }, { status: 404 });26  if (!conv) conv = await createConversation({ projectId: b.projectId ?? null, modelId: b.modelId ?? null, settings: { routingMode: b.routingMode ?? (b.modelId ? "manual" : "auto"), toolsEnabled: b.toolsEnabled ?? true } });27  const parts: Part[] = [];28  if (b.attachmentIds?.length) {29    const atts = await sql<{ id: string; mime: string; filename: string; size_bytes: number; page_count: number | null }[]>`SELECT id, mime, filename, size_bytes, page_count FROM attachments WHERE id = ANY(${b.attachmentIds})`;30    for (const a of atts) parts.push(a.mime.startsWith("image/") ? { type: "image", attachmentId: a.id, mime: a.mime, name: a.filename } : { type: "file", attachmentId: a.id, mime: a.mime, name: a.filename, size: a.size_bytes, pages: a.page_count ?? undefined });31  }32  if (b.text.trim()) parts.push({ type: "text", text: b.text });33  if (!parts.length) return Response.json({ error: "Message vide" }, { status: 400 });34  const prev = await getActiveMessages(conv.id);35  const user = await insertMessage({ conversationId: conv.id, parentId: prev.at(-1)?.id ?? null, role: "user", parts });36  if (b.attachmentIds?.length) await sql`UPDATE attachments SET conversation_id = ${conv.id}, message_id = ${user.id} WHERE id = ANY(${b.attachmentIds})`;37  if (b.modelId && b.modelId !== conv.model_id) await sql`UPDATE conversations SET model_id = ${b.modelId} WHERE id = ${conv.id}`;38  const convId = conv.id;39  return sse(async (emit, signal) => {40    emit("conversation", { id: convId, userMessageId: user.id, created: !b.conversationId });41    await runAssistant({ conversationId: convId, modelId: b.modelId, routingMode: b.routingMode, settings: b.settings, toolsEnabled: b.toolsEnabled, toolNames: b.toolNames, parentMessageId: user.id, signal, emit });42  }, req.signal);43});44
Files · laptop-build-20261101
Menu

Theme