| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- 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();
- }
|