@@ -219,6 +219,7 @@ export function useChat({
219
219
sendExtraMessageFields,
220
220
experimental_onFunctionCall,
221
221
experimental_onToolCall,
222
+ experimental_maxAutomaticRoundtrips = 0 ,
222
223
streamMode,
223
224
onResponse,
224
225
onFinish,
@@ -230,6 +231,19 @@ export function useChat({
230
231
} : Omit < UseChatOptions , 'api' > & {
231
232
api ?: string | StreamingReactResponseAction ;
232
233
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 ;
233
247
} = { } ) : UseChatHelpers & {
234
248
experimental_addToolResult : ( {
235
249
toolCallId,
@@ -343,6 +357,23 @@ export function useChat({
343
357
} finally {
344
358
mutateLoading ( false ) ;
345
359
}
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
+ }
346
377
} ,
347
378
[
348
379
mutate ,
@@ -359,6 +390,7 @@ export function useChat({
359
390
sendExtraMessageFields ,
360
391
experimental_onFunctionCall ,
361
392
experimental_onToolCall ,
393
+ experimental_maxAutomaticRoundtrips ,
362
394
messagesRef ,
363
395
abortControllerRef ,
364
396
generateId ,
@@ -526,16 +558,38 @@ export function useChat({
526
558
527
559
// auto-submit when all tool calls in the last assistant message have results:
528
560
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 ) ) {
537
562
triggerRequest ( { messages : updatedMessages } ) ;
538
563
}
539
564
} ,
540
565
} ;
541
566
}
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