route.ts 3.7 KB

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