From 85cb97734ceb252ad9e49955c5dc5eb8584ad92b Mon Sep 17 00:00:00 2001 From: Mariano Fuentes Date: Thu, 12 Mar 2026 10:26:22 -0400 Subject: [PATCH 1/2] fix(automation): fix suggestion generation broken by @ai-sdk/groq v3 strict JSON schema The @ai-sdk/groq v3 upgrade enabled strict JSON schema validation by default, which requires ALL properties to be in `required`. The Zod schema used `.optional()` for vendorName/vendorWebsite, which excluded them from `required`, causing Groq API to reject every request. Changed to `.nullable()` so properties remain required (satisfying strict mode) while still allowing null values. Also moved DB queries inside try/catch to prevent unhandled errors. Co-Authored-By: Claude Opus 4.6 --- .../actions/generate-suggestions.ts | 73 ++++++++++--------- .../components/AutomationPageClient.tsx | 1 - .../components/chat/EmptyState.tsx | 2 +- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts index 80213bcfec..db3225466f 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts @@ -14,8 +14,8 @@ const SuggestionsSchema = z.object({ z.object({ title: z.string(), prompt: z.string(), - vendorName: z.string().optional(), - vendorWebsite: z.string().optional(), + vendorName: z.string().nullable(), + vendorWebsite: z.string().nullable(), }), ), }); @@ -24,39 +24,39 @@ export async function generateAutomationSuggestions( taskDescription: string, organizationId: string, ): Promise<{ title: string; prompt: string; vendorName?: string; vendorWebsite?: string }[]> { - // Get vendors from the Vendor table - const vendors = await db.vendor.findMany({ - where: { - organizationId, - }, - select: { - name: true, - website: true, - description: true, - }, - }); - // Get vendors from context table as well - const contextEntries = await db.context.findMany({ - where: { - organizationId, - }, - select: { - question: true, - answer: true, - }, - }); - const vendorList = - vendors.length > 0 - ? vendors.map((v) => `${v.name}${v.website ? ` (${v.website})` : ''}`).join(', ') - : 'No vendors configured yet'; + try { + // Get vendors from the Vendor table + const vendors = await db.vendor.findMany({ + where: { + organizationId, + }, + select: { + name: true, + website: true, + description: true, + }, + }); + // Get vendors from context table as well + const contextEntries = await db.context.findMany({ + where: { + organizationId, + }, + select: { + question: true, + answer: true, + }, + }); + const vendorList = + vendors.length > 0 + ? vendors.map((v) => `${v.name}${v.website ? ` (${v.website})` : ''}`).join(', ') + : 'No vendors configured yet'; - const contextInfo = - contextEntries.length > 0 - ? contextEntries.map((c) => `Q: ${c.question}\nA: ${c.answer}`).join('\n\n') - : 'No additional context available'; + const contextInfo = + contextEntries.length > 0 + ? contextEntries.map((c) => `Q: ${c.question}\nA: ${c.answer}`).join('\n\n') + : 'No additional context available'; - // Generate AI suggestions - try { + // Generate AI suggestions const { object } = await generateObject({ model: groq('meta-llama/llama-4-scout-17b-16e-instruct'), schema: SuggestionsSchema, @@ -73,7 +73,12 @@ export async function generateAutomationSuggestions( } } - return suggestions; + return suggestions.map((s) => ({ + title: s.title, + prompt: s.prompt, + vendorName: s.vendorName ?? undefined, + vendorWebsite: s.vendorWebsite ?? undefined, + })); } catch (error) { console.error('[generateAutomationSuggestions] Error generating suggestions:', error); // Try to extract suggestions from error if available diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/AutomationPageClient.tsx b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/AutomationPageClient.tsx index 5489188f6f..2b792e41dd 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/AutomationPageClient.tsx +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/AutomationPageClient.tsx @@ -57,7 +57,6 @@ export function AutomationPageClient({ .catch((error) => { console.error('Failed to generate suggestions:', error); setIsLoadingSuggestions(false); - // Keep empty array, will use static suggestions }); } else { // Not a new automation, no need to load suggestions diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx index 3617ecb9ff..245d18c599 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx @@ -151,7 +151,7 @@ export function EmptyState({ )) : // Show actual suggestion cards - examplesToShow.map((example: AutomationExample, index: number) => ( + examplesToShow?.map((example: AutomationExample, index: number) => ( Date: Thu, 12 Mar 2026 10:33:07 -0400 Subject: [PATCH 2/2] fix(automation): normalize error recovery path and remove unnecessary optional chaining Co-Authored-By: Claude Opus 4.6 --- .../[automationId]/actions/generate-suggestions.ts | 7 ++++++- .../[automationId]/components/chat/EmptyState.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts index db3225466f..4002bc5a12 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/actions/generate-suggestions.ts @@ -92,7 +92,12 @@ export async function generateAutomationSuggestions( ? parsed.suggestions : [parsed.suggestions]; if (suggestions.length > 0 && suggestions[0].title) { - return suggestions; + return suggestions.map((s: Record) => ({ + title: String(s.title), + prompt: String(s.prompt), + vendorName: (s.vendorName as string) ?? undefined, + vendorWebsite: (s.vendorWebsite as string) ?? undefined, + })); } } } diff --git a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx index 245d18c599..3617ecb9ff 100644 --- a/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx +++ b/apps/app/src/app/(app)/[orgId]/tasks/[taskId]/automation/[automationId]/components/chat/EmptyState.tsx @@ -151,7 +151,7 @@ export function EmptyState({ )) : // Show actual suggestion cards - examplesToShow?.map((example: AutomationExample, index: number) => ( + examplesToShow.map((example: AutomationExample, index: number) => (