design.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import { streamText, tool } from 'ai'
  2. import { source } from 'common-tags'
  3. import { NextApiRequest, NextApiResponse } from 'next'
  4. import { z } from 'zod'
  5. import { getModel } from '@/lib/ai/model'
  6. import { DEFAULT_COMPLETION_MODEL } from '@/lib/ai/model.utils'
  7. import apiWrapper from '@/lib/api/apiWrapper'
  8. export const maxDuration = 60
  9. const ServiceSchema = z.object({
  10. name: z.enum(['Auth', 'Storage', 'Database', 'Edge Function', 'Cron', 'Queues', 'Vector']),
  11. reason: z.string().describe("The reason why this service is needed for the user's use case"),
  12. })
  13. const getTools = () => {
  14. return {
  15. executeSql: tool({
  16. description: 'Save the generated database schema definition',
  17. inputSchema: z.object({
  18. sql: z.string().describe('The SQL schema definition'),
  19. }),
  20. }),
  21. reset: tool({
  22. description: 'Reset the database, services and start over',
  23. inputSchema: z.object({}),
  24. }),
  25. setServices: tool({
  26. description:
  27. 'Set the entire list of Briven services needed for the project. Always include the full list',
  28. inputSchema: z.object({
  29. services: z
  30. .array(ServiceSchema)
  31. .describe('Array of services with reasons why they are needed'),
  32. }),
  33. }),
  34. setTitle: tool({
  35. description: "Set the project title based on the user's description",
  36. inputSchema: z.object({
  37. title: z.string().describe('The project title'),
  38. }),
  39. }),
  40. }
  41. }
  42. async function handler(req: NextApiRequest, res: NextApiResponse) {
  43. const { method } = req
  44. switch (method) {
  45. case 'POST':
  46. return handlePost(req, res)
  47. default:
  48. res.setHeader('Allow', ['POST'])
  49. res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
  50. }
  51. }
  52. const wrapper = (req: NextApiRequest, res: NextApiResponse) =>
  53. apiWrapper(req, res, handler, { withAuth: true })
  54. export default wrapper
  55. async function handlePost(req: NextApiRequest, res: NextApiResponse) {
  56. const { modelParams, error: modelError } = await getModel({
  57. provider: 'openai',
  58. modelEntry: DEFAULT_COMPLETION_MODEL,
  59. })
  60. if (modelError) {
  61. return res.status(500).json({ error: modelError.message })
  62. }
  63. const { messages } = req.body
  64. const result = streamText({
  65. ...modelParams,
  66. system: source`
  67. You are a Briven expert who helps people set up their Briven project. You specializes in database schema design. You are to help the user design a database schema for their application but also suggest Briven services they should use.
  68. When designing database schemas, follow these rules:
  69. - Generate the entire schema
  70. - For primary keys, always use "id bigint primary key generated always as identity" (not serial)
  71. - Prefer creating foreign key references in the create statement
  72. - Prefer 'text' over 'varchar'
  73. - Prefer 'timestamp with time zone' over 'date'
  74. - In Briven, the auth schema already has a users table which is used to store users
  75. - Create a profiles table in the public schema where the primary id is uuid and references the auth.users schema instead of creating a users table
  76. - Always include appropriate indexes and foreign key constraints.
  77. Follow these rules:
  78. 1. Generate a database schema that meets the user's requirements by calling the executeSql tool. Make your best guess without needing to ask for more.
  79. 2. Set the services required for the user's use case by calling the setServices tool.
  80. 3. Set the project title by calling the setTitle tool.
  81. 4. Always respond with a short single paragraph of less than 80 words of what you changed and the current state of the schema.
  82. If user requests to reset the database, call the reset tool.
  83. `,
  84. messages,
  85. tools: getTools(),
  86. })
  87. result.pipeUIMessageStreamToResponse(res, { headers: { 'Content-Encoding': 'none' } })
  88. }