11import type { User } from '@databuddy/auth' ;
2- import type { Website } from '@databuddy/shared' ;
2+ import { createId , type Website } from '@databuddy/shared' ;
33import type { AssistantRequestType } from '../schemas' ;
44import { handleChartResponse } from './handlers/chart-handler' ;
55import { handleMetricResponse } from './handlers/metric-handler' ;
6+ import type
7+ { AIResponse } from './prompts/agent' ;
68import { getAICompletion } from './utils/ai-client' ;
9+ import {
10+ addMessageToConversation ,
11+ createNewConversation ,
12+ } from './utils/conversation-utils' ;
713import type { StreamingUpdate } from './utils/stream-utils' ;
814import { generateThinkingSteps } from './utils/stream-utils' ;
915
@@ -23,20 +29,125 @@ const unexpectedErrorMessages = [
2329 'Something went a bit wonky on my end. Try asking me again?' ,
2430] ;
2531
26- export interface AssistantRequest extends AssistantRequestType {
32+ export interface AssistantRequest extends Omit < AssistantRequestType , 'model' > {
2733 websiteHostname : string ;
34+ model : NonNullable < AssistantRequestType [ 'model' ] > ;
2835}
2936
3037export interface AssistantContext {
31- user ? : User | null ;
38+ user : User ;
3239 website : Website ;
3340 debugInfo : Record < string , unknown > ;
3441}
3542
36- export async function * processAssistantRequest (
43+ async function processResponseByType (
44+ parsedResponse : AIResponse ,
45+ context : AssistantContext ,
46+ startTime : number ,
47+ aiTime : number
48+ ) : Promise < StreamingUpdate > {
49+ switch ( parsedResponse . response_type ) {
50+ case 'text' : {
51+ const textResult = {
52+ type : 'complete' ,
53+ content :
54+ parsedResponse . text_response || "Here's the answer to your question." ,
55+ data : { hasVisualization : false , responseType : 'text' } ,
56+ debugInfo :
57+ context . user . role === 'ADMIN' ? context . debugInfo : undefined ,
58+ } as const ;
59+ return textResult ;
60+ }
61+
62+ case 'metric' : {
63+ return await handleMetricResponse ( parsedResponse , context ) ;
64+ }
65+
66+ case 'chart' : {
67+ if ( parsedResponse . sql ) {
68+ return await handleChartResponse ( parsedResponse , {
69+ ...context ,
70+ startTime,
71+ aiTime,
72+ } ) ;
73+ }
74+ return {
75+ type : 'error' ,
76+ content : 'Invalid chart configuration.' ,
77+ debugInfo :
78+ context . user . role === 'ADMIN' ? context . debugInfo : undefined ,
79+ } ;
80+ }
81+ default : {
82+ return {
83+ type : 'error' ,
84+ content : 'Invalid response format from AI.' ,
85+ debugInfo :
86+ context . user . role === 'ADMIN' ? context . debugInfo : undefined ,
87+ } ;
88+ }
89+ }
90+ }
91+
92+ function saveConversationWithResult (
93+ request : AssistantRequest ,
94+ context : AssistantContext ,
95+ parsedResponse : AIResponse ,
96+ finalResult : StreamingUpdate ,
97+ conversationId : string
98+ ) {
99+ const numberOfUserMessages = request . messages . filter (
100+ ( message ) => message . role === 'user'
101+ ) . length ;
102+ const isNewConversation = numberOfUserMessages === 1 ;
103+
104+ const conversationMessages = [
105+ {
106+ id : createId ( ) ,
107+ role : 'user' ,
108+ content : request . messages . at ( - 1 ) ?. content as string ,
109+ conversationId,
110+ modelType : request . model ,
111+ } ,
112+ {
113+ id : createId ( ) ,
114+ role : 'assistant' ,
115+ content : finalResult . content ,
116+ conversationId,
117+ modelType : request . model ,
118+ sql : parsedResponse . sql ,
119+ chartType : parsedResponse . chart_type ,
120+ responseType : parsedResponse . response_type ,
121+ textResponse : parsedResponse . text_response ,
122+ thinkingSteps : parsedResponse . thinking_steps ,
123+ hasError : finalResult . type === 'error' ,
124+ errorMessage : finalResult . type === 'error' ? finalResult . content : '' ,
125+ finalResult,
126+ } ,
127+ ] ;
128+
129+ if ( isNewConversation ) {
130+ createNewConversation (
131+ conversationId ,
132+ request . websiteId ,
133+ context . user . id ,
134+ 'New Conversation' ,
135+ request . model ,
136+ conversationMessages
137+ ) ;
138+ } else {
139+ addMessageToConversation (
140+ conversationId ,
141+ request . model ,
142+ conversationMessages
143+ ) ;
144+ }
145+ }
146+
147+ export async function processAssistantRequest (
37148 request : AssistantRequest ,
38149 context : AssistantContext
39- ) : AsyncGenerator < StreamingUpdate > {
150+ ) : Promise < StreamingUpdate [ ] > {
40151 const startTime = Date . now ( ) ;
41152
42153 try {
@@ -46,7 +157,7 @@ export async function* processAssistantRequest(
46157 websiteHostname : request . websiteHostname ,
47158 } ) ;
48159
49- if ( context . user ? .role === 'ADMIN' ) {
160+ if ( context . user . role === 'ADMIN' ) {
50161 context . debugInfo . validatedInput = {
51162 message : request . messages . at ( - 1 ) ,
52163 websiteId : request . websiteId ,
@@ -62,13 +173,14 @@ export async function* processAssistantRequest(
62173 const parsedResponse = aiResponse . content ;
63174
64175 if ( ! parsedResponse ) {
65- yield {
66- type : 'error' ,
67- content : getRandomMessage ( parseErrorMessages ) ,
68- debugInfo :
69- context . user ?. role === 'ADMIN' ? context . debugInfo : undefined ,
70- } ;
71- return ;
176+ return [
177+ {
178+ type : 'error' ,
179+ content : getRandomMessage ( parseErrorMessages ) ,
180+ debugInfo :
181+ context . user . role === 'ADMIN' ? context . debugInfo : undefined ,
182+ } ,
183+ ] ;
72184 }
73185
74186 console . info ( '✅ [Assistant Processor] AI response parsed' , {
@@ -77,66 +189,49 @@ export async function* processAssistantRequest(
77189 thinkingSteps : parsedResponse . thinking_steps ?. length || 0 ,
78190 } ) ;
79191
80- // Process thinking steps
81- if ( parsedResponse . thinking_steps ?. length ) {
82- yield * generateThinkingSteps ( parsedResponse . thinking_steps ) ;
83- }
192+ const conversationId = request . conversationId || createId ( ) ;
193+ const assistantResponse : StreamingUpdate [ ] = [ ] ;
84194
85- // Handle different response types
86- switch ( parsedResponse . response_type ) {
87- case 'text' :
88- yield {
89- type : 'complete' ,
90- content :
91- parsedResponse . text_response ||
92- "Here's the answer to your question." ,
93- data : { hasVisualization : false , responseType : 'text' } ,
94- debugInfo :
95- context . user ?. role === 'ADMIN' ? context . debugInfo : undefined ,
96- } ;
97- break ;
98-
99- case 'metric' :
100- yield * handleMetricResponse ( parsedResponse , context ) ;
101- break ;
102-
103- case 'chart' :
104- if ( parsedResponse . sql ) {
105- yield * handleChartResponse ( parsedResponse , {
106- ...context ,
107- startTime,
108- aiTime,
109- } ) ;
110- } else {
111- yield {
112- type : 'error' ,
113- content : 'Invalid chart configuration.' ,
114- debugInfo :
115- context . user ?. role === 'ADMIN' ? context . debugInfo : undefined ,
116- } ;
117- }
118- break ;
119-
120- default :
121- yield {
122- type : 'error' ,
123- content : 'Invalid response format from AI.' ,
124- debugInfo :
125- context . user ?. role === 'ADMIN' ? context . debugInfo : undefined ,
126- } ;
195+ if ( parsedResponse . thinking_steps ) {
196+ assistantResponse . push (
197+ ...generateThinkingSteps ( parsedResponse . thinking_steps )
198+ ) ;
127199 }
200+
201+ const finalResult = await processResponseByType (
202+ parsedResponse ,
203+ context ,
204+ startTime ,
205+ aiTime
206+ ) ;
207+
208+ assistantResponse . push ( finalResult ) ;
209+
210+ setImmediate ( ( ) => {
211+ saveConversationWithResult (
212+ request ,
213+ context ,
214+ parsedResponse ,
215+ finalResult ,
216+ conversationId
217+ ) ;
218+ } ) ;
219+
220+ return assistantResponse ;
128221 } catch ( error : unknown ) {
129222 const errorMessage =
130223 error instanceof Error ? error . message : 'Unknown error' ;
131224 console . error ( '💥 [Assistant Processor] Processing error' , {
132225 error : errorMessage ,
133226 } ) ;
134227
135- yield {
136- type : 'error' ,
137- content : getRandomMessage ( unexpectedErrorMessages ) ,
138- debugInfo :
139- context . user ?. role === 'ADMIN' ? { error : errorMessage } : undefined ,
140- } ;
228+ return [
229+ {
230+ type : 'error' ,
231+ content : getRandomMessage ( unexpectedErrorMessages ) ,
232+ debugInfo :
233+ context . user . role === 'ADMIN' ? { error : errorMessage } : undefined ,
234+ } ,
235+ ] ;
141236 }
142237}
0 commit comments