@@ -219,6 +219,7 @@ export function useChat({
219219 sendExtraMessageFields,
220220 experimental_onFunctionCall,
221221 experimental_onToolCall,
222+ experimental_maxAutomaticRoundtrips = 0 ,
222223 streamMode,
223224 onResponse,
224225 onFinish,
@@ -230,6 +231,19 @@ export function useChat({
230231} : Omit < UseChatOptions , 'api' > & {
231232 api ?: string | StreamingReactResponseAction ;
232233 key ?: string ;
234+ /**
235+ Maximal number of automatic roundtrips for tool calls.
236+
237+ An automatic tool call roundtrip is a call to the server with the
238+ tool call results when all tool calls in the last assistant
239+ message have results.
240+
241+ A maximum number is required to prevent infinite loops in the
242+ case of misconfigured tools.
243+
244+ By default, it's set to 0, which will disable the feature.
245+ */
246+ experimental_maxAutomaticRoundtrips ?: number ;
233247} = { } ) : UseChatHelpers & {
234248 experimental_addToolResult : ( {
235249 toolCallId,
@@ -343,6 +357,23 @@ export function useChat({
343357 } finally {
344358 mutateLoading ( false ) ;
345359 }
360+
361+ // auto-submit when all tool calls in the last assistant message have results:
362+ const messages = messagesRef . current ;
363+ const lastMessage = messages [ messages . length - 1 ] ;
364+ if (
365+ // ensure there is a last message:
366+ lastMessage != null &&
367+ // check if the feature is enabled:
368+ experimental_maxAutomaticRoundtrips > 0 &&
369+ // check that roundtrip is possible:
370+ isAssistantMessageWithCompletedToolCalls ( lastMessage ) &&
371+ // limit the number of automatic roundtrips:
372+ countTrailingAssistantMessages ( messages ) <=
373+ experimental_maxAutomaticRoundtrips
374+ ) {
375+ await triggerRequest ( { messages } ) ;
376+ }
346377 } ,
347378 [
348379 mutate ,
@@ -359,6 +390,7 @@ export function useChat({
359390 sendExtraMessageFields ,
360391 experimental_onFunctionCall ,
361392 experimental_onToolCall ,
393+ experimental_maxAutomaticRoundtrips ,
362394 messagesRef ,
363395 abortControllerRef ,
364396 generateId ,
@@ -526,16 +558,38 @@ export function useChat({
526558
527559 // auto-submit when all tool calls in the last assistant message have results:
528560 const lastMessage = updatedMessages [ updatedMessages . length - 1 ] ;
529- if (
530- lastMessage . role === 'assistant' &&
531- lastMessage . toolInvocations &&
532- lastMessage . toolInvocations . length > 0 &&
533- lastMessage . toolInvocations . every (
534- toolInvocation => 'result' in toolInvocation ,
535- )
536- ) {
561+ if ( isAssistantMessageWithCompletedToolCalls ( lastMessage ) ) {
537562 triggerRequest ( { messages : updatedMessages } ) ;
538563 }
539564 } ,
540565 } ;
541566}
567+
568+ /**
569+ Check if the message is an assistant message with completed tool calls.
570+ The message must have at least one tool invocation and all tool invocations
571+ must have a result.
572+ */
573+ function isAssistantMessageWithCompletedToolCalls ( message : Message ) {
574+ return (
575+ message . role === 'assistant' &&
576+ message . toolInvocations &&
577+ message . toolInvocations . length > 0 &&
578+ message . toolInvocations . every ( toolInvocation => 'result' in toolInvocation )
579+ ) ;
580+ }
581+
582+ /**
583+ Returns the number of trailing assistant messages in the array.
584+ */
585+ function countTrailingAssistantMessages ( messages : Message [ ] ) {
586+ let count = 0 ;
587+ for ( let i = messages . length - 1 ; i >= 0 ; i -- ) {
588+ if ( messages [ i ] . role === 'assistant' ) {
589+ count ++ ;
590+ } else {
591+ break ;
592+ }
593+ }
594+ return count ;
595+ }
0 commit comments