route.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { streamText } from "ai";
  2. import { gateway } from "@ai-sdk/gateway";
  3. import { buildUserPrompt, type Spec } from "@json-render/core";
  4. import { pdfCatalog } from "@/lib/catalog";
  5. import { minuteRateLimit, dailyRateLimit } from "@/lib/rate-limit";
  6. import { headers } from "next/headers";
  7. export const maxDuration = 60;
  8. const SYSTEM_PROMPT = pdfCatalog.prompt();
  9. const DEFAULT_MODEL = "anthropic/claude-haiku-4.5";
  10. export async function POST(req: Request) {
  11. const headersList = await headers();
  12. const ip = headersList.get("x-forwarded-for")?.split(",")[0] ?? "anonymous";
  13. const [minuteResult, dailyResult] = await Promise.all([
  14. minuteRateLimit.limit(ip),
  15. dailyRateLimit.limit(ip),
  16. ]);
  17. if (!minuteResult.success || !dailyResult.success) {
  18. const isMinuteLimit = !minuteResult.success;
  19. return new Response(
  20. JSON.stringify({
  21. error: "Rate limit exceeded",
  22. message: isMinuteLimit
  23. ? "Too many requests. Please wait a moment before trying again."
  24. : "Daily limit reached. Please try again tomorrow.",
  25. }),
  26. {
  27. status: 429,
  28. headers: { "Content-Type": "application/json" },
  29. },
  30. );
  31. }
  32. const { prompt, startingSpec } = (await req.json()) as {
  33. prompt: string;
  34. startingSpec?: Spec | null;
  35. };
  36. if (!prompt || typeof prompt !== "string") {
  37. return Response.json({ error: "prompt is required" }, { status: 400 });
  38. }
  39. const userPrompt = buildUserPrompt({
  40. prompt,
  41. currentSpec: startingSpec,
  42. });
  43. const result = streamText({
  44. model: gateway(process.env.AI_GATEWAY_MODEL ?? DEFAULT_MODEL),
  45. system: SYSTEM_PROMPT,
  46. prompt: userPrompt,
  47. temperature: 0.7,
  48. });
  49. return result.toTextStreamResponse();
  50. }