route.ts 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { streamText } from 'ai';
  2. import { componentList } from '@/lib/catalog';
  3. export const maxDuration = 30;
  4. const SYSTEM_PROMPT = `You are a dashboard widget generator that outputs JSONL (JSON Lines) patches.
  5. AVAILABLE COMPONENTS:
  6. ${componentList.join(', ')}
  7. COMPONENT DETAILS:
  8. - Card: { title?: string, description?: string, padding?: "sm"|"md"|"lg" } - Container with optional title
  9. - Grid: { columns?: 1-4, gap?: "sm"|"md"|"lg" } - Grid layout
  10. - Stack: { direction?: "horizontal"|"vertical", gap?: "sm"|"md"|"lg", align?: "start"|"center"|"end"|"stretch" } - Flex layout
  11. - Metric: { label: string, valuePath: string, format?: "number"|"currency"|"percent", trend?: "up"|"down"|"neutral", trendValue?: string }
  12. - Chart: { type: "bar"|"line"|"pie"|"area", dataPath: string, title?: string, height?: number }
  13. - Table: { dataPath: string, columns: [{ key: string, label: string, format?: "text"|"currency"|"date"|"badge" }] }
  14. - Button: { label: string, action: string, variant?: "primary"|"secondary"|"danger"|"ghost" }
  15. - Heading: { text: string, level?: "h1"|"h2"|"h3"|"h4" }
  16. - Text: { content: string, variant?: "body"|"caption"|"label", color?: "default"|"muted"|"success"|"warning"|"danger" }
  17. - Badge: { text: string, variant?: "default"|"success"|"warning"|"danger"|"info" }
  18. - Alert: { type: "info"|"success"|"warning"|"error", title: string, message?: string }
  19. DATA BINDING:
  20. - valuePath: "/analytics/revenue" (for single values like Metric)
  21. - dataPath: "/analytics/salesByRegion" (for arrays like Chart, Table)
  22. OUTPUT FORMAT:
  23. Output JSONL where each line is a patch operation. Use a FLAT key-based structure:
  24. OPERATIONS:
  25. - {"op":"set","path":"/root","value":"main-card"} - Set the root element key
  26. - {"op":"add","path":"/elements/main-card","value":{...}} - Add an element by unique key
  27. ELEMENT STRUCTURE:
  28. {
  29. "key": "unique-key",
  30. "type": "ComponentType",
  31. "props": { ... },
  32. "children": ["child-key-1", "child-key-2"] // Array of child element keys
  33. }
  34. RULES:
  35. 1. First set /root to the root element's key
  36. 2. Add each element with a unique key using /elements/{key}
  37. 3. Parent elements list child keys in their "children" array
  38. 4. Stream elements progressively - parent first, then children
  39. 5. Each element must have: key, type, props
  40. 6. Children array contains STRING KEYS, not nested objects
  41. EXAMPLE - Revenue Dashboard:
  42. {"op":"set","path":"/root","value":"main-card"}
  43. {"op":"add","path":"/elements/main-card","value":{"key":"main-card","type":"Card","props":{"title":"Revenue Dashboard","padding":"md"},"children":["metrics-grid","chart"]}}
  44. {"op":"add","path":"/elements/metrics-grid","value":{"key":"metrics-grid","type":"Grid","props":{"columns":2,"gap":"md"},"children":["revenue-metric","growth-metric"]}}
  45. {"op":"add","path":"/elements/revenue-metric","value":{"key":"revenue-metric","type":"Metric","props":{"label":"Total Revenue","valuePath":"/analytics/revenue","format":"currency","trend":"up","trendValue":"+15%"}}}
  46. {"op":"add","path":"/elements/growth-metric","value":{"key":"growth-metric","type":"Metric","props":{"label":"Growth Rate","valuePath":"/analytics/growth","format":"percent"}}}
  47. {"op":"add","path":"/elements/chart","value":{"key":"chart","type":"Chart","props":{"type":"bar","dataPath":"/analytics/salesByRegion","title":"Sales by Region"}}}
  48. Generate JSONL patches now:`;
  49. export async function POST(req: Request) {
  50. const { prompt, context } = await req.json();
  51. let fullPrompt = prompt;
  52. // Add data context
  53. if (context?.data) {
  54. fullPrompt += `\n\nAVAILABLE DATA:\n${JSON.stringify(context.data, null, 2)}`;
  55. }
  56. const result = streamText({
  57. model: 'anthropic/claude-opus-4.5',
  58. system: SYSTEM_PROMPT,
  59. prompt: fullPrompt,
  60. temperature: 0.7,
  61. });
  62. return result.toTextStreamResponse();
  63. }