diff --git a/apps/studio/pages/api/ai/feedback/classify.ts b/apps/studio/pages/api/ai/feedback/classify.ts
index 3a064fe7de1..c27a76c3d4d 100644
--- a/apps/studio/pages/api/ai/feedback/classify.ts
+++ b/apps/studio/pages/api/ai/feedback/classify.ts
@@ -1,17 +1,10 @@
-import { ContextLengthError } from 'ai-commands'
-import { classifyFeedback } from 'ai-commands/edge'
-import apiWrapper from 'lib/api/apiWrapper'
+import { generateObject } from 'ai'
import { NextApiRequest, NextApiResponse } from 'next'
-
-const openAiKey = process.env.OPENAI_API_KEY
+import { z } from 'zod'
+import { getModel } from 'lib/ai/model'
+import apiWrapper from 'lib/api/apiWrapper'
async function handler(req: NextApiRequest, res: NextApiResponse) {
- if (!openAiKey) {
- return res.status(500).json({
- error: 'No OPENAI_API_KEY set. Create this environment variable to use AI features.',
- })
- }
-
const { method } = req
switch (method) {
@@ -28,18 +21,83 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
body: { prompt },
} = req
+ if (!prompt) {
+ return res.status(400).json({
+ error: 'Prompt is required',
+ })
+ }
+
try {
- const result = await classifyFeedback(prompt)
- res.status(200).json({ feedback_category: result })
- return
+ const { model, error: modelError } = await getModel()
+
+ if (modelError) {
+ return res.status(500).json({ error: modelError.message })
+ }
+
+ const { object } = await generateObject({
+ model,
+ schema: z.object({
+ feedback_category: z.enum(['support', 'feedback', 'unknown']),
+ }),
+ temperature: 0,
+ prompt: `
+ Classify the following feedback as ONE of: support, feedback, unknown.
+ - support: bug reports, help requests, or issues
+ - feedback: feature requests or suggestions
+ - unknown: unclear or unrelated
+
+ If you can't determine support or feedback, always output "unknown".
+
+ Only output a JSON object in this format: { "feedback_category": "support|feedback|unknown" }
+
+ Examples:
+ Feedback: "Whenever I try to invite a team member, the invite email doesn't get sent."
+ Response: { "feedback_category": "support" }
+
+ Feedback: "I have reached the storage limit for my project and my plan. I cannot understand how I can expand the storage space in my project."
+ Response: { "feedback_category": "support" }
+
+ Feedback: "Please delete the project x in my account"
+ Response: { "feedback_category": "support" }
+
+ Feedback: "My billing page is broken"
+ Response: { "feedback_category": "support" }
+
+ Feedback: "I accidentally deleted my database—can it be recovered?"
+ Response: { "feedback_category": "support" }
+
+ Feedback: "My login tokens are expiring too quickly, even though I didn't change any settings."
+ Response: { "feedback_category": "support" }
+
+ Feedback: "Can you add more integrations?"
+ Response: { "feedback_category": "feedback" }
+
+ Feedback: "I'm getting charged for a project I thought I deleted. Can you help me stop billing?"
+ Response: { "feedback_category": "support" }
+
+ Feedback: "Could you support OAuth login for more providers like Apple or LinkedIn?"
+ Response: { "feedback_category": "feedback" }
+
+ Feedback: "It's unclear in the docs how to set up row-level security with multiple roles."
+ Response: { "feedback_category": "feedback" }
+
+ Feedback: "I am trying to pause my Pro project"
+ Response: { "feedback_category": "feedback" }
+
+ Feedback: "${prompt}"
+ Response:
+ `,
+ })
+
+ return res.json({ feedback_category: object.feedback_category })
} catch (error) {
if (error instanceof Error) {
- console.error(`AI feedback classification failed: ${error.message}`)
+ console.error(`Classifying this feedback failed`)
- if (error instanceof ContextLengthError) {
+ // Check for context length error
+ if (error.message.includes('context_length') || error.message.includes('too long')) {
return res.status(400).json({
- error:
- 'Your feedback prompt is too large for Supabase AI to ingest. Try splitting it into smaller prompts.',
+ error: 'This prompt is too large to ingest',
})
}
} else {
@@ -47,7 +105,7 @@ export async function handlePost(req: NextApiRequest, res: NextApiResponse) {
}
return res.status(500).json({
- error: 'There was an unknown error classifying the feedback. Please try again.',
+ error: 'There was an unknown error generating the feedback category.',
})
}
}
diff --git a/packages/ai-commands/edge.ts b/packages/ai-commands/edge.ts
index 95023e6bb1d..729d55b5128 100644
--- a/packages/ai-commands/edge.ts
+++ b/packages/ai-commands/edge.ts
@@ -1,5 +1,4 @@
export * from './src/errors'
export * from './src/docs'
-export * from './src/sql/classify-feedback'
export * from './src/sql/cron'
diff --git a/packages/ai-commands/src/sql/classify-feedback.ts b/packages/ai-commands/src/sql/classify-feedback.ts
deleted file mode 100644
index 68ecd255533..00000000000
--- a/packages/ai-commands/src/sql/classify-feedback.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { generateObject } from 'ai'
-import { openai as openaiProvider } from '@ai-sdk/openai'
-import { z } from 'zod'
-import { ContextLengthError } from '../errors'
-
-// Given a prompt from the feedback widget, classify it as support, feedback, or unknown.
-export async function classifyFeedback(prompt: string) {
- try {
- const { object } = await generateObject({
- model: openaiProvider('gpt-4o'),
- temperature: 0,
-
- schema: z.object({
- feedback_category: z.enum(['support', 'feedback', 'unknown']),
- }),
- prompt: `
- Classify the following feedback as ONE of: support, feedback, unknown.
- - support: bug reports, help requests, or issues
- - feedback: feature requests or suggestions
- - unknown: unclear or unrelated
-
- If you can't determine support or feedback, always output "unknown".
-
- Only output a JSON object in this format: { "feedback_category": "support|feedback|unknown" }
-
- Examples:
- Feedback: "Whenever I try to invite a team member, the invite email doesn't get sent."
- Response: { "feedback_category": "support" }
-
- Feedback: "I have reached the storage limit for my project and my plan. I cannot understand how I can expand the storage space in my project."
- Response: { "feedback_category": "support" }
-
- Feedback: "Please delete the project x in my account"
- Response: { "feedback_category": "support" }
-
- Feedback: "My billing page is broken"
- Response: { "feedback_category": "support" }
-
- Feedback: "I accidentally deleted my database—can it be recovered?"
- Response: { "feedback_category": "support" }
-
- Feedback: "My login tokens are expiring too quickly, even though I didn't change any settings."
- Response: { "feedback_category": "support" }
-
- Feedback: "Can you add more integrations?"
- Response: { "feedback_category": "feedback" }
-
- Feedback: "I'm getting charged for a project I thought I deleted. Can you help me stop billing?"
- Response: { "feedback_category": "support" }
-
- Feedback: "Could you support OAuth login for more providers like Apple or LinkedIn?"
- Response: { "feedback_category": "feedback" }
-
- Feedback: "It's unclear in the docs how to set up row-level security with multiple roles."
- Response: { "feedback_category": "feedback" }
-
- Feedback: "I am trying to pause my Pro project"
- Response: { "feedback_category": "feedback" }
-
- Feedback: "${prompt}"
- Response:
- `,
- })
-
- return object.feedback_category
- } catch (error) {
- if (error instanceof Error && 'code' in error && error.code === 'context_length_exceeded') {
- throw new ContextLengthError()
- }
- throw error
- }
-}