diff --git a/src/app/api/chat/index+api.ts b/src/app/api/chat/index+api.ts index 54d4145..4693d2e 100644 --- a/src/app/api/chat/index+api.ts +++ b/src/app/api/chat/index+api.ts @@ -26,9 +26,34 @@ export async function POST(request: Request) { ...(previousResponseId && { previous_response_id: previousResponseId }), }); + let relatedQuestions: string[] | undefined = undefined; + try { + const questionsCompletion = await openai.chat.completions.create({ + model: 'gpt-4.1', + messages: [ + { + role: 'user', + content: + `Provide 3 advanced follow-up questions related to: ${message}. ` + + 'Respond in JSON format as { "questions": ["q1","q2","q3"] }.', + }, + ], + response_format: { type: 'json_object' }, + }); + const parsed = JSON.parse( + questionsCompletion.choices[0].message.content || '{}' + ); + if (Array.isArray(parsed.questions)) { + relatedQuestions = parsed.questions; + } + } catch (err) { + console.error('Failed to generate related questions:', err); + } + return Response.json({ responseMessage: response.output_text, responseId: response.id, + ...(relatedQuestions && { relatedQuestions }), }); } catch (error) { console.error('OpenAI error:', error); diff --git a/src/app/api/chat/speech+api.ts b/src/app/api/chat/speech+api.ts index 3ce2091..7b808a9 100644 --- a/src/app/api/chat/speech+api.ts +++ b/src/app/api/chat/speech+api.ts @@ -21,10 +21,35 @@ export async function POST(request: Request) { ...(previousResponseId && { previous_response_id: previousResponseId }), }); + let relatedQuestions: string[] | undefined = undefined; + try { + const questionsCompletion = await openai.chat.completions.create({ + model: 'gpt-4.1', + messages: [ + { + role: 'user', + content: + `Provide 3 advanced follow-up questions related to: ${transcription.text}. ` + + 'Respond in JSON format as { "questions": ["q1","q2","q3"] }.', + }, + ], + response_format: { type: 'json_object' }, + }); + const parsed = JSON.parse( + questionsCompletion.choices[0].message.content || '{}' + ); + if (Array.isArray(parsed.questions)) { + relatedQuestions = parsed.questions; + } + } catch (err) { + console.error('Failed to generate related questions:', err); + } + return Response.json({ responseId: response.id, responseMessage: response.output_text, transcribedMessage: transcription.text, + ...(relatedQuestions && { relatedQuestions }), }); } catch (error) { console.log(error); diff --git a/src/app/chat/[id].tsx b/src/app/chat/[id].tsx index 7bb92f7..e56bcdf 100644 --- a/src/app/chat/[id].tsx +++ b/src/app/chat/[id].tsx @@ -9,7 +9,9 @@ import { getTextResponse, createAIImage, getSpeechResponse, + type ChatResponse, } from '@/services/chatService'; +import type { Message } from '@/types/types'; export default function ChatScreen() { const { id } = useLocalSearchParams(); @@ -29,6 +31,10 @@ export default function ChatScreen() { const addNewMessage = useChatStore((state) => state.addNewMessage); + const handleQuestionPress = async (question: string) => { + await handleSend(question, null, false, null); + }; + useEffect(() => { const timeout = setTimeout(() => { flatListRef.current?.scrollToEnd({ animated: true }); @@ -60,7 +66,7 @@ export default function ChatScreen() { )?.responseId; try { - let data; + let data: ChatResponse | { image: string }; if (audioBase64) { data = await getSpeechResponse(audioBase64, previousResponseId); const myMessage = { @@ -75,13 +81,16 @@ export default function ChatScreen() { data = await getTextResponse(message, imageBase64, previousResponseId); } - const aiResponseMessage = { + const aiResponseMessage: Message = { id: Date.now().toString(), - message: data.responseMessage, - responseId: data.responseId, - image: data.image, - role: 'assistant' as const, - }; + role: 'assistant', + ...("responseMessage" in data && { + message: data.responseMessage, + responseId: data.responseId, + relatedQuestions: data.relatedQuestions, + }), + ...("image" in data && { image: data.image }), + } as Message; addNewMessage(chat.id, aiResponseMessage); } catch (error) { @@ -104,7 +113,12 @@ export default function ChatScreen() { } + renderItem={({ item }) => ( + + )} ListFooterComponent={() => isWaitingForResponse && ( diff --git a/src/app/index.tsx b/src/app/index.tsx index 9efbb90..3540e7f 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -7,7 +7,9 @@ import { createAIImage, getSpeechResponse, getTextResponse, + type ChatResponse, } from '@/services/chatService'; +import type { Message } from '@/types/types'; export default function HomeScreen() { const createNewChat = useChatStore((state) => state.createNewChat); @@ -36,7 +38,7 @@ export default function HomeScreen() { router.push(`/chat/${newChatId}`); try { - let data; + let data: ChatResponse | { image: string }; if (audioBase64) { data = await getSpeechResponse(audioBase64); const myMessage = { @@ -51,13 +53,16 @@ export default function HomeScreen() { data = await getTextResponse(message, imageBase64); } - const aiResponseMessage = { + const aiResponseMessage: Message = { id: Date.now().toString(), - message: data.responseMessage, - responseId: data.responseId, - image: data.image, - role: 'assistant' as const, - }; + role: 'assistant', + ...("responseMessage" in data && { + message: data.responseMessage, + responseId: data.responseId, + relatedQuestions: data.relatedQuestions, + }), + ...("image" in data && { image: data.image }), + } as Message; addNewMessage(newChatId, aiResponseMessage); diff --git a/src/components/MessageListItem.tsx b/src/components/MessageListItem.tsx index bdb7ec0..90ed824 100644 --- a/src/components/MessageListItem.tsx +++ b/src/components/MessageListItem.tsx @@ -1,14 +1,18 @@ -import { Text, View, Image } from 'react-native'; +import { Text, View, Image, Pressable } from 'react-native'; import Markdown from 'react-native-markdown-display'; import { Message } from '@/types/types'; import { markdownStyles } from '@/utils/markdown'; interface MessageListItemProps { messageItem: Message; + onQuestionPress?: (question: string) => void; } -export default function MessageListItem({ messageItem }: MessageListItemProps) { - const { message, role, image } = messageItem; +export default function MessageListItem({ + messageItem, + onQuestionPress, +}: MessageListItemProps) { + const { message, role, image, relatedQuestions } = messageItem; const isUser = role === 'user'; return ( @@ -32,6 +36,27 @@ export default function MessageListItem({ messageItem }: MessageListItemProps) { {message} )} + {!isUser && relatedQuestions?.length ? ( + + + Related questions + + + {relatedQuestions.map((q, idx) => { + const display = q.length > 80 ? `${q.slice(0, 77)}...` : q; + return ( + onQuestionPress?.(q)} + className='bg-[#262626] rounded-lg p-2' + > + {display} + + ); + })} + + + ) : null} ); } diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 0e4bfd4..586de99 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,4 +1,12 @@ -export async function createAIImage(prompt: string) { +export interface ChatResponse { + responseMessage: string; + responseId: string; + image?: string; + transcribedMessage?: string; + relatedQuestions?: string[]; +} + +export async function createAIImage(prompt: string): Promise<{ image: string }> { const res = await fetch('/api/chat/createImage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -14,7 +22,7 @@ export const getTextResponse = async ( message: string, imageBase64: string | null, previousResponseId?: string -) => { +): Promise => { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -23,13 +31,13 @@ export const getTextResponse = async ( const data = await res.json(); if (!res.ok) throw new Error(data.error); - return data; + return data as ChatResponse; }; export const getSpeechResponse = async ( audioBase64: string, previousResponseId?: string -) => { +): Promise => { const res = await fetch('/api/chat/speech', { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -38,5 +46,5 @@ export const getSpeechResponse = async ( const data = await res.json(); if (!res.ok) throw new Error(data.error); - return data; + return data as ChatResponse; }; diff --git a/src/types/types.ts b/src/types/types.ts index 522ea6d..d44919c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -4,6 +4,7 @@ export interface Message { message?: string; responseId?: string; image?: string; + relatedQuestions?: string[]; } export interface Chat {