@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
33import { Button , FormTextarea } from 'components' ;
44import { useToast } from 'hooks/useToast' ;
55import { useStreamingResponse } from 'hooks/useStreamingResponse' ;
6+ import { ChoiceButton } from 'services/inference' ;
67import './TestProductionLLM.scss' ;
78import MessageContent from 'components/MessageContent' ;
89interface Message {
@@ -12,6 +13,7 @@ interface Message {
1213 timestamp : string ;
1314 hasError ?: boolean ;
1415 errorMessage ?: string ;
16+ buttons ?: ChoiceButton [ ] ;
1517}
1618
1719const TestProductionLLM : FC = ( ) => {
@@ -115,6 +117,16 @@ const TestProductionLLM: FC = () => {
115117 } ) ;
116118 } ;
117119
120+ const onButtons = ( buttons : ChoiceButton [ ] ) => {
121+ setMessages ( prev => {
122+ const botMsgIndex = prev . findIndex ( msg => msg . id === botMessageId ) ;
123+ if ( botMsgIndex === - 1 ) return prev ;
124+ const updated = [ ...prev ] ;
125+ updated [ botMsgIndex ] = { ...updated [ botMsgIndex ] , buttons } ;
126+ return updated ;
127+ } ) ;
128+ } ;
129+
118130 const onComplete = ( ) => {
119131 console . log ( '[Component] Stream completed' ) ;
120132 // Always reset loading state on completion
@@ -160,7 +172,7 @@ const TestProductionLLM: FC = () => {
160172
161173 // Start streaming
162174 try {
163- await startStreaming ( userMessageText , streamingOptions , onToken , onComplete , onError ) ;
175+ await startStreaming ( userMessageText , streamingOptions , onToken , onComplete , onError , onButtons ) ;
164176 } catch ( error ) {
165177 console . error ( '[Component] Failed to start streaming:' , error ) ;
166178 // Reset loading state if streaming fails to start
@@ -175,6 +187,62 @@ const TestProductionLLM: FC = () => {
175187 }
176188 } ;
177189
190+ const handleButtonClick = async ( title : string , payload : string ) => {
191+ if ( isLoading || isStreaming ) return ;
192+
193+ const userMessage : Message = {
194+ id : `user-${ Date . now ( ) } ` ,
195+ content : title ,
196+ isUser : true ,
197+ timestamp : new Date ( ) . toISOString ( ) ,
198+ } ;
199+ setMessages ( prev => [ ...prev , userMessage ] ) ;
200+ setIsLoading ( true ) ;
201+
202+ const botMessageId = `bot-${ Date . now ( ) } ` ;
203+ const conversationHistory = messages . map ( msg => ( {
204+ authorRole : msg . isUser ? 'user' : 'bot' ,
205+ message : msg . content ,
206+ timestamp : msg . timestamp ,
207+ } ) ) ;
208+ const streamingOptions = {
209+ authorId : 'test-user-456' ,
210+ conversationHistory,
211+ url : 'opensearch-dashboard-test' ,
212+ } ;
213+
214+ const onToken = ( token : string ) => {
215+ setMessages ( prev => {
216+ const idx = prev . findIndex ( m => m . id === botMessageId ) ;
217+ if ( idx === - 1 ) return [ ...prev , { id : botMessageId , content : token , isUser : false , timestamp : new Date ( ) . toISOString ( ) } ] ;
218+ const updated = [ ...prev ] ;
219+ updated [ idx ] = { ...updated [ idx ] , content : updated [ idx ] . content + token } ;
220+ return updated ;
221+ } ) ;
222+ } ;
223+ const onButtons = ( buttons : ChoiceButton [ ] ) => {
224+ setMessages ( prev => {
225+ const idx = prev . findIndex ( m => m . id === botMessageId ) ;
226+ if ( idx === - 1 ) return prev ;
227+ const updated = [ ...prev ] ;
228+ updated [ idx ] = { ...updated [ idx ] , buttons } ;
229+ return updated ;
230+ } ) ;
231+ } ;
232+ const onComplete = ( ) => setIsLoading ( false ) ;
233+ const onError = ( error : string ) => {
234+ setIsLoading ( false ) ;
235+ toast . open ( { type : 'error' , title : t ( 'testProductionLLM.streamingErrorTitle' ) , message : error } ) ;
236+ } ;
237+
238+ try {
239+ await startStreaming ( payload , streamingOptions , onToken , onComplete , onError , onButtons ) ;
240+ } catch ( error ) {
241+ console . error ( '[Component] Failed to start streaming for button click:' , error ) ;
242+ setIsLoading ( false ) ;
243+ }
244+ } ;
245+
178246 const clearChat = ( ) => {
179247 setMessages ( [ ] ) ;
180248 stopStreaming ( ) ;
@@ -215,6 +283,20 @@ const TestProductionLLM: FC = () => {
215283 >
216284 < div className = "test-production-llm__message-content" >
217285 < MessageContent content = { msg . content } />
286+ { ! msg . isUser && msg . buttons && msg . buttons . length > 0 && (
287+ < div className = "mcq-buttons" >
288+ { msg . buttons . map ( ( btn ) => (
289+ < Button
290+ key = { btn . payload }
291+ onClick = { ( ) => handleButtonClick ( btn . title , btn . payload ) }
292+ disabled = { isLoading || isStreaming }
293+ appearance = "secondary"
294+ >
295+ { btn . title }
296+ </ Button >
297+ ) ) }
298+ </ div >
299+ ) }
218300 { msg . hasError && (
219301 < div className = "test-production-llm__message-error" >
220302 < span className = "test-production-llm__message-error-icon" > ⚠️</ span >
0 commit comments