11'use server' ;
22
33import { authActionClient } from '@/actions/safe-action' ;
4- import { vendorQuestionnaireOrchestratorTask } from '@/jobs/tasks/vendors/vendor-questionnaire-orchestrator' ;
5- import { tasks } from '@trigger.dev/sdk' ;
4+ import { answerQuestion } from '@/jobs/tasks/vendors/answer-question' ;
5+ import { syncOrganizationEmbeddings } from '@/lib/vector' ;
6+ import { logger } from '@/utils/logger' ;
7+ import { headers } from 'next/headers' ;
8+ import { revalidatePath } from 'next/cache' ;
69import { z } from 'zod' ;
710
811const inputSchema = z . object ( {
912 questionsAndAnswers : z . array (
1013 z . object ( {
1114 question : z . string ( ) ,
1215 answer : z . string ( ) . nullable ( ) ,
16+ _originalIndex : z . number ( ) . optional ( ) , // Preserves original index from QuestionnaireResult
1317 } ) ,
1418 ) ,
1519} ) ;
@@ -34,26 +38,99 @@ export const vendorQuestionnaireOrchestrator = authActionClient
3438 const organizationId = session . activeOrganizationId ;
3539
3640 try {
37- // Trigger the root orchestrator task - it will handle batching internally
38- const handle = await tasks . trigger < typeof vendorQuestionnaireOrchestratorTask > (
39- 'vendor-questionnaire-orchestrator' ,
40- {
41- vendorId : `org_${ organizationId } ` ,
41+ logger . info ( 'Starting auto-answer questionnaire' , {
42+ organizationId,
43+ questionCount : questionsAndAnswers . length ,
44+ } ) ;
45+
46+ // Sync organization embeddings before generating answers
47+ // Uses incremental sync: only updates what changed (much faster than full sync)
48+ try {
49+ await syncOrganizationEmbeddings ( organizationId ) ;
50+ logger . info ( 'Organization embeddings synced successfully' , {
4251 organizationId,
43- questionsAndAnswers,
44- } ,
52+ } ) ;
53+ } catch ( error ) {
54+ logger . warn ( 'Failed to sync organization embeddings' , {
55+ organizationId,
56+ error : error instanceof Error ? error . message : 'Unknown error' ,
57+ } ) ;
58+ // Continue with existing embeddings if sync fails
59+ }
60+
61+ // Filter questions that need answers (skip already answered)
62+ // Preserve original index if provided (for single question answers)
63+ const questionsToAnswer = questionsAndAnswers
64+ . map ( ( qa , index ) => ( {
65+ ...qa ,
66+ index : qa . _originalIndex !== undefined ? qa . _originalIndex : index ,
67+ } ) )
68+ . filter ( ( qa ) => ! qa . answer || qa . answer . trim ( ) . length === 0 ) ;
69+
70+ logger . info ( 'Questions to answer' , {
71+ total : questionsAndAnswers . length ,
72+ toAnswer : questionsToAnswer . length ,
73+ } ) ;
74+
75+ // Process all questions in parallel by calling answerQuestion directly
76+ // Note: metadata updates are disabled since we're not in a Trigger.dev task context
77+ const results = await Promise . all (
78+ questionsToAnswer . map ( ( qa ) =>
79+ answerQuestion (
80+ {
81+ question : qa . question ,
82+ organizationId,
83+ questionIndex : qa . index ,
84+ totalQuestions : questionsAndAnswers . length ,
85+ } ,
86+ { useMetadata : false } ,
87+ ) ,
88+ ) ,
4589 ) ;
4690
91+ // Process results
92+ const allAnswers : Array < {
93+ questionIndex : number ;
94+ question : string ;
95+ answer : string | null ;
96+ sources ?: Array < {
97+ sourceType : string ;
98+ sourceName ?: string ;
99+ score : number ;
100+ } > ;
101+ } > = results . map ( ( result ) => ( {
102+ questionIndex : result . questionIndex ,
103+ question : result . question ,
104+ answer : result . answer ,
105+ sources : result . sources ,
106+ } ) ) ;
107+
108+ logger . info ( 'Auto-answer questionnaire completed' , {
109+ organizationId,
110+ totalQuestions : questionsAndAnswers . length ,
111+ answered : allAnswers . filter ( ( a ) => a . answer ) . length ,
112+ } ) ;
113+
114+ // Revalidate the page to show updated answers
115+ const headersList = await headers ( ) ;
116+ let path = headersList . get ( 'x-pathname' ) || headersList . get ( 'referer' ) || '' ;
117+ path = path . replace ( / \/ [ a - z ] { 2 } \/ / , '/' ) ;
118+ revalidatePath ( path ) ;
119+
47120 return {
48121 success : true ,
49122 data : {
50- taskId : handle . id , // Return orchestrator task ID for polling
123+ answers : allAnswers ,
51124 } ,
52125 } ;
53126 } catch ( error ) {
127+ logger . error ( 'Failed to answer questions' , {
128+ organizationId,
129+ error : error instanceof Error ? error . message : 'Unknown error' ,
130+ } ) ;
54131 throw error instanceof Error
55132 ? error
56- : new Error ( 'Failed to trigger vendor questionnaire orchestrator ' ) ;
133+ : new Error ( 'Failed to answer questions ' ) ;
57134 }
58135 } ) ;
59136
0 commit comments