SPB Git
2.3 KB · 33 lines typescript
1import { z } from "zod";2import { guarded, body, json } from "@/lib/api";3import { generateImageTool } from "@/lib/tools/image";4import { openaiEditImage } from "@/lib/ai/openai";5import { readAttachment, saveAttachment } from "@/lib/storage";6import { recordUsage, assertWithinBudget } from "@/lib/usage";7import { sql } from "@/lib/db";89export const maxDuration = 300;1011/** Direct image generation / editing (outside of a chat turn). */12export const POST = guarded(async (req) => {13  const b = await body(req, z.object({ prompt: z.string().min(1).max(4000), model: z.string().optional(), size: z.string().optional(), quality: z.string().optional(), editAttachmentId: z.string().optional(), conversationId: z.string().nullable().optional(), projectId: z.string().nullable().optional() }));14  await assertWithinBudget();15  if (b.editAttachmentId) {16    const src = await readAttachment(b.editAttachmentId);17    if (!src) return json({ error: "Image source introuvable" }, { status: 404 });18    const t0 = Date.now();19    const r = await openaiEditImage(new File([new Uint8Array(src.data)], src.name, { type: src.mime }), b.prompt, b.model ?? "gpt-image-1.5");20    const att = await saveAttachment({ data: Buffer.from(r.images[0].b64, "base64"), filename: `edited-${Date.now()}.png`, mime: "image/png", conversationId: b.conversationId ?? null, projectId: b.projectId ?? null, kind: "generated-image", meta: { prompt: b.prompt, model: r.model, editedFrom: b.editAttachmentId } });21    const u = r.usage as { input_tokens?: number; output_tokens?: number } | undefined;22    await recordUsage({ provider: "openai", modelId: r.model, kind: "image", usage: { input: u?.input_tokens ?? 0, output: u?.output_tokens ?? 0 }, costEstimated: u ? ((u.input_tokens ?? 0) * 8 + (u.output_tokens ?? 0) * 32) / 1e6 : null, latencyMs: Date.now() - t0 });23    return json({ attachmentId: att.id, model: r.model });24  }25  const r = await generateImageTool(b.prompt, { conversationId: b.conversationId ?? "", projectId: b.projectId ?? null, messageId: "", allow: new Set() }, { model: b.model, size: b.size, quality: b.quality });26  return json(r);27});2829export const GET = guarded(async () => {30  const rows = await sql`SELECT id, filename, meta, conversation_id, created_at FROM attachments WHERE kind = 'generated-image' ORDER BY created_at DESC LIMIT 200`;31  return json({ images: rows });32});33
Files · main
Menu

Theme