@@ -3,6 +3,7 @@ import { OpenAI } from "openai";
33import path from "path" ;
44
55import { MessageRecord } from "@/components/ai/context" ;
6+ import { SUGGESTED_QUESTIONS } from "@/components/ai/suggestions" ;
67import {
78 DocsEmbedding ,
89 REJECTION_MESSAGE ,
@@ -48,6 +49,48 @@ setInterval(() => {
4849 }
4950} , MAX_SESSION_AGE ) ;
5051
52+ /**
53+ * Generate contextual follow-up suggestions based on the conversation.
54+ * Uses OpenAI to pick 3 most relevant questions from the curated pool and rephrase them
55+ * to fit the conversation context.
56+ */
57+ async function generateContextualSuggestions (
58+ client : OpenAI ,
59+ conversationHistory : MessageRecord [ ]
60+ ) : Promise < string [ ] > {
61+ try {
62+ const response = await client . chat . completions . create ( {
63+ model : MODEL ,
64+ messages : [
65+ {
66+ role : "system" ,
67+ content : `You are helping generate follow-up questions for a user chatting with the Recall documentation AI.
68+
69+ Here is a curated pool of key questions:
70+ ${ SUGGESTED_QUESTIONS . map ( ( q , i ) => `${ i + 1 } . ${ q } ` ) . join ( "\n" ) }
71+
72+ Based on the conversation history, pick 3 most relevant questions from this pool and rephrase them to fit the conversation context as potential follow-up questions shown to the user for their next input.
73+
74+ Return ONLY a JSON array of 3 strings, nothing else. Example: ["Question 1?", "Question 2?", "Question 3?"]` ,
75+ } ,
76+ ...conversationHistory ,
77+ ] ,
78+ temperature : 0.7 ,
79+ } ) ;
80+
81+ const content = response . choices [ 0 ] ?. message ?. content ?. trim ( ) ;
82+ if ( ! content ) return [ ] ;
83+
84+ // Parse the JSON array response
85+ const suggestions = JSON . parse ( content ) as string [ ] ;
86+ return Array . isArray ( suggestions ) ? suggestions . slice ( 0 , 3 ) : [ ] ;
87+ } catch ( error ) {
88+ console . error ( "Error generating suggestions:" , error ) ;
89+ // Fallback to random questions if generation fails
90+ return [ ...SUGGESTED_QUESTIONS ] . sort ( ( ) => Math . random ( ) - 0.5 ) . slice ( 0 , 3 ) ;
91+ }
92+ }
93+
5194export async function POST ( request : Request ) {
5295 if ( ! openai ) {
5396 return new Response ( JSON . stringify ( { error : "OpenAI API key not configured" } ) , {
@@ -122,16 +165,21 @@ export async function POST(request: Request) {
122165 stream : true ,
123166 } ) ;
124167
125- // Create a transform stream to append source links
168+ // Create a transform stream to append source links and suggestions
126169 const transform = new TransformStream ( {
127170 transform ( chunk , controller ) {
128171 controller . enqueue ( chunk ) ;
129172 } ,
130- flush ( controller ) {
173+ async flush ( controller ) {
131174 // Add reference links after the stream is done
132175 const referenceLinks = getReferenceLinks ( relevant ) ;
133- const sourceJson = JSON . stringify ( { references : referenceLinks } ) + "\n" ; // Match OpenAI response formatting
176+ const sourceJson = JSON . stringify ( { references : referenceLinks } ) + "\n" ;
134177 controller . enqueue ( new TextEncoder ( ) . encode ( sourceJson ) ) ;
178+
179+ // Generate and add contextual suggestions
180+ const suggestions = await generateContextualSuggestions ( openai , messages ) ;
181+ const suggestionsJson = JSON . stringify ( { suggestions } ) + "\n" ;
182+ controller . enqueue ( new TextEncoder ( ) . encode ( suggestionsJson ) ) ;
135183 } ,
136184 } ) ;
137185
0 commit comments