import { streamText } from "ai"; import { headers } from "next/headers"; import { minuteRateLimit, dailyRateLimit } from "@/lib/rate-limit"; import { playgroundCatalog } from "@/lib/catalog"; export const maxDuration = 30; const SYSTEM_PROMPT = playgroundCatalog.prompt({ customRules: [ "For forms: Card should be the root element, not wrapped in a centering Stack", "NEVER use viewport height classes (min-h-screen, h-screen) - breaks the container", "NEVER use page background colors (bg-gray-50) - container has its own background", ], }); const MAX_PROMPT_LENGTH = 500; const DEFAULT_MODEL = "anthropic/claude-haiku-4.5"; export async function POST(req: Request) { // Get client IP for rate limiting const headersList = await headers(); const ip = headersList.get("x-forwarded-for")?.split(",")[0] ?? "anonymous"; // Check rate limits (minute and daily) const [minuteResult, dailyResult] = await Promise.all([ minuteRateLimit.limit(ip), dailyRateLimit.limit(ip), ]); if (!minuteResult.success || !dailyResult.success) { const isMinuteLimit = !minuteResult.success; return new Response( JSON.stringify({ error: "Rate limit exceeded", message: isMinuteLimit ? "Too many requests. Please wait a moment before trying again." : "Daily limit reached. Please try again tomorrow.", }), { status: 429, headers: { "Content-Type": "application/json" }, }, ); } const { prompt, context } = await req.json(); const previousSpec = context?.previousSpec; const sanitizedPrompt = String(prompt || "").slice(0, MAX_PROMPT_LENGTH); // Build the user prompt, including previous tree for iteration let userPrompt = sanitizedPrompt; if ( previousSpec && previousSpec.root && Object.keys(previousSpec.elements || {}).length > 0 ) { userPrompt = `CURRENT UI STATE (already loaded, DO NOT recreate existing elements): ${JSON.stringify(previousSpec, null, 2)} USER REQUEST: ${sanitizedPrompt} IMPORTANT: The current UI is already loaded. Output ONLY the patches needed to make the requested change: - To add a new element: {"op":"add","path":"/elements/new-key","value":{...}} - To modify an existing element: {"op":"replace","path":"/elements/existing-key","value":{...}} - To remove an element: {"op":"remove","path":"/elements/old-key"} - To update the root: {"op":"replace","path":"/root","value":"new-root-key"} - To add children: update the parent element with new children array DO NOT output patches for elements that don't need to change. Only output what's necessary for the requested modification.`; } const result = streamText({ model: process.env.AI_GATEWAY_MODEL || DEFAULT_MODEL, system: SYSTEM_PROMPT, prompt: userPrompt, temperature: 0.7, }); return result.toTextStreamResponse(); }