cron-v2.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { generateText, Output } 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. const cronSchema = z.object({
  9. cron_expression: z.string().describe('The generated cron expression.'),
  10. })
  11. async function handler(req: NextApiRequest, res: NextApiResponse) {
  12. const { method } = req
  13. switch (method) {
  14. case 'POST':
  15. return handlePost(req, res)
  16. default:
  17. res.setHeader('Allow', ['POST'])
  18. res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } })
  19. }
  20. }
  21. export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
  22. const {
  23. body: { prompt },
  24. } = req
  25. if (!prompt) {
  26. return res.status(400).json({
  27. error: 'Prompt is required',
  28. })
  29. }
  30. try {
  31. const { modelParams, error: modelError } = await getModel({
  32. provider: 'openai',
  33. modelEntry: DEFAULT_COMPLETION_MODEL,
  34. })
  35. if (modelError) {
  36. return res.status(500).json({ error: modelError.message })
  37. }
  38. const result = await generateText({
  39. ...modelParams,
  40. output: Output.object({ schema: cronSchema }),
  41. prompt: source`
  42. You are a cron syntax expert. Your purpose is to convert natural language time descriptions into valid cron expressions for pg_cron.
  43. Rules for responses:
  44. - For standard intervals (minutes and above), output cron expressions in the 5-field format supported by pg_cron
  45. - For second-based intervals, use the special pg_cron "x seconds" syntax
  46. - Do not provide any explanation of what the cron expression does
  47. - Do not ask for clarification if you need it. Just output the cron expression.
  48. Example input: "Every Monday at 3am"
  49. Example output: 0 3 * * 1
  50. Example input: "Every 30 seconds"
  51. Example output: 30 seconds
  52. Additional examples:
  53. - Every minute: * * * * *
  54. - Every 5 minutes: */5 * * * *
  55. - Every first of the month, at 00:00: 0 0 1 * *
  56. - Every night at midnight: 0 0 * * *
  57. - Every Monday at 2am: 0 2 * * 1
  58. - Every 15 seconds: 15 seconds
  59. - Every 45 seconds: 45 seconds
  60. Field order for standard cron:
  61. - minute (0-59)
  62. - hour (0-23)
  63. - day (1-31)
  64. - month (1-12)
  65. - weekday (0-6, Sunday=0)
  66. Important: pg_cron uses "x seconds" for second-based intervals, not "x * * * *".
  67. If the user asks for seconds, do not use the 5-field format, instead use "x seconds".
  68. Here is the user's prompt: ${prompt}
  69. `,
  70. })
  71. return res.json(result.output.cron_expression)
  72. } catch (error) {
  73. if (error instanceof Error) {
  74. console.error(`AI cron generation failed: ${error.message}`)
  75. // Check for context length error
  76. if (error.message.includes('context_length') || error.message.includes('too long')) {
  77. return res.status(400).json({
  78. error:
  79. 'Your cron prompt is too large for Briven Assistant to ingest. Try splitting it into smaller prompts.',
  80. })
  81. }
  82. } else {
  83. console.error(`Unknown error: ${error}`)
  84. }
  85. return res.status(500).json({
  86. error: 'There was an unknown error generating the cron syntax. Please try again.',
  87. })
  88. }
  89. }
  90. const wrapper = (req: NextApiRequest, res: NextApiResponse) =>
  91. apiWrapper(req, res, handler, { withAuth: true })
  92. export default wrapper