@@ -136,6 +136,7 @@ public async Task SupportsToolsProvidedByAdditionalTools(bool provideOptions)
136136
137137 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
138138
139+ ResetPlanFunctionCallStates ( plan ) ;
139140 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
140141 }
141142
@@ -169,6 +170,7 @@ public async Task PrefersToolsProvidedByChatOptions()
169170
170171 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
171172
173+ ResetPlanFunctionCallStates ( plan ) ;
172174 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
173175 }
174176
@@ -219,6 +221,7 @@ public async Task SupportsMultipleFunctionCallsPerRequestAsync(bool concurrentIn
219221
220222 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
221223
224+ ResetPlanFunctionCallStates ( plan ) ;
222225 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
223226 }
224227
@@ -267,6 +270,7 @@ public async Task ParallelFunctionCallsMayBeInvokedConcurrentlyAsync()
267270
268271 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
269272
273+ ResetPlanFunctionCallStates ( plan ) ;
270274 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
271275 }
272276
@@ -308,6 +312,7 @@ public async Task ConcurrentInvocationOfParallelCallsDisabledByDefaultAsync()
308312
309313 await InvokeAndAssertAsync ( options , plan ) ;
310314
315+ ResetPlanFunctionCallStates ( plan ) ;
311316 await InvokeAndAssertStreamingAsync ( options , plan ) ;
312317 }
313318
@@ -351,6 +356,7 @@ public async Task FunctionInvokerDelegateOverridesHandlingAsync()
351356
352357 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
353358
359+ ResetPlanFunctionCallStates ( plan ) ;
354360 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
355361 }
356362
@@ -562,6 +568,7 @@ public async Task KeepsFunctionCallingContent()
562568#pragma warning disable SA1005 , S125
563569 Validate ( await InvokeAndAssertAsync ( options , plan ) ) ;
564570
571+ ResetPlanFunctionCallStates ( plan ) ;
565572 Validate ( await InvokeAndAssertStreamingAsync ( options , plan ) ) ;
566573
567574 static void Validate ( List < ChatMessage > finalChat )
@@ -597,6 +604,7 @@ public async Task ExceptionDetailsOnlyReportedWhenRequestedAsync(bool detailedEr
597604
598605 await InvokeAndAssertAsync ( options , plan , configurePipeline : configure ) ;
599606
607+ ResetPlanFunctionCallStates ( plan ) ;
600608 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configure ) ;
601609 }
602610
@@ -1077,6 +1085,7 @@ public async Task FunctionInvocations_InvokedOnOriginalSynchronizationContext()
10771085 . UseFunctionInvocation ( configure : c => { c . AllowConcurrentInvocation = true ; c . IncludeDetailedErrors = true ; } ) ;
10781086
10791087 await InvokeAndAssertAsync ( options , plan , configurePipeline : configurePipeline ) ;
1088+ ResetPlanFunctionCallStates ( plan ) ;
10801089 await InvokeAndAssertStreamingAsync ( options , plan , configurePipeline : configurePipeline ) ;
10811090 }
10821091
@@ -1110,6 +1119,7 @@ public async Task TerminateOnUnknownCalls_ControlsBehaviorForUnknownFunctions(bo
11101119 ] ;
11111120
11121121 await InvokeAndAssertAsync ( options , planForContinue , configurePipeline : configure ) ;
1122+ ResetPlanFunctionCallStates ( planForContinue ) ;
11131123 await InvokeAndAssertStreamingAsync ( options , planForContinue , configurePipeline : configure ) ;
11141124 }
11151125 else
@@ -1621,6 +1631,9 @@ private static async Task<List<ChatMessage>> InvokeAndAssertAsync(
16211631 {
16221632 Assert . NotEmpty ( plan ) ;
16231633
1634+ // Reset InvocationRequired for all FunctionCallContent in the plan to allow reuse
1635+ ResetPlanFunctionCallStates ( plan ) ;
1636+
16241637 configurePipeline ??= static b => b . UseFunctionInvocation ( ) ;
16251638
16261639 using CancellationTokenSource cts = new ( ) ;
@@ -1691,6 +1704,9 @@ private static async Task<List<ChatMessage>> InvokeAndAssertStreamingAsync(
16911704 {
16921705 Assert . NotEmpty ( plan ) ;
16931706
1707+ // Reset InvocationRequired for all FunctionCallContent in the plan to allow reuse
1708+ ResetPlanFunctionCallStates ( plan ) ;
1709+
16941710 configurePipeline ??= static b => b . UseFunctionInvocation ( ) ;
16951711
16961712 using CancellationTokenSource cts = new ( ) ;
@@ -1731,4 +1747,22 @@ private static async IAsyncEnumerable<T> YieldAsync<T>(params IEnumerable<T> ite
17311747 yield return item ;
17321748 }
17331749 }
1750+
1751+ /// <summary>
1752+ /// Resets InvocationRequired to true for all FunctionCallContent in the plan.
1753+ /// This is needed when reusing a plan across multiple test invocations.
1754+ /// </summary>
1755+ private static void ResetPlanFunctionCallStates ( List < ChatMessage > plan )
1756+ {
1757+ foreach ( var message in plan )
1758+ {
1759+ foreach ( var content in message . Contents )
1760+ {
1761+ if ( content is FunctionCallContent fcc )
1762+ {
1763+ fcc . InvocationRequired = true ;
1764+ }
1765+ }
1766+ }
1767+ }
17341768}
0 commit comments