Nudge feedback widget, move request to bedrock (#38074)

* Nudge feedback widget, move request to bedrock

* Fix the response object

* Remove an import to deleted file.

---------

Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
Terry SuttonandIvan Vasilov authored and GitHub committed 2025-08-21 08:56:54 -02:30
1 parent 750028b542
commit f9921569a9
4 files changed
+83 -96

No files matched your search

@@ -44,18 +44,20 @@ const FeedbackDropdown = ({ className }: { className?: string }) => {
<div className="flex flex-col gap-4 p-4">
<div className="font-medium text-sm">What would you like to share?</div>
<div className="grid grid-cols-2 gap-3">
<Button type="default" className="h-24" onClick={() => setIsOpen(false)} asChild>
<Button type="default" className="h-32" onClick={() => setIsOpen(false)} asChild>
<Link href="/support/new">
<span className="grid gap-1 text-center">
<TriangleAlert size="28" className="mx-auto text-destructive-600" />
<span className="text-sm">Issue</span>
<span className="text-base">Issue</span>
<span className="text-xm text-foreground-lighter">with my project</span>
</span>
</Link>
</Button>
<Button type="default" className="h-24" onClick={() => setStage('widget')}>
<Button type="default" className="h-32" onClick={() => setStage('widget')}>
<span className="grid gap-1 text-center">
<Lightbulb size="28" className="mx-auto text-warning" />
<span className="text-sm">Idea</span>
<span className="text-base">Idea</span>
<span className="text-xm text-foreground-lighter">to improve Supabase</span>
</span>
</Button>
</div>
+77 -19
View File
@@ -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.',
})
}
}
-1
View File
@@ -1,5 +1,4 @@
export * from './src/errors'
export * from './src/docs'
export * from './src/sql/classify-feedback'
export * from './src/sql/cron'
@@ -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
}
}