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