@@ -19,6 +19,9 @@ public static bool ShouldEmitStreamingEvents(this TurnToken token, bool? agentSe
1919
2020 public static bool ShouldEmitStreamingEvents ( bool ? turnTokenSetting , bool ? agentSetting )
2121 => turnTokenSetting ?? agentSetting ?? false ;
22+
23+ public static bool ShouldEmitStreamingEvents ( this HandoffState handoffState , bool ? agentSetting )
24+ => handoffState . TurnToken . ShouldEmitStreamingEvents ( agentSetting ) ;
2225}
2326
2427internal sealed class AIAgentHostExecutor : ChatProtocolExecutor
@@ -81,7 +84,11 @@ private ValueTask HandleUserInputResponseAsync(
8184 // resumes can be processed in one invocation.
8285 return this . ProcessTurnMessagesAsync ( async ( pendingMessages , ctx , ct ) =>
8386 {
84- pendingMessages . Add ( new ChatMessage ( ChatRole . User , [ response ] ) ) ;
87+ pendingMessages . Add ( new ChatMessage ( ChatRole . User , [ response ] )
88+ {
89+ CreatedAt = DateTimeOffset . UtcNow ,
90+ MessageId = Guid . NewGuid ( ) . ToString ( "N" ) ,
91+ } ) ;
8592
8693 await this . ContinueTurnAsync ( pendingMessages , ctx , this . _currentTurnEmitEvents ?? false , ct ) . ConfigureAwait ( false ) ;
8794
@@ -104,7 +111,12 @@ private ValueTask HandleFunctionResultAsync(
104111 // resumes can be processed in one invocation.
105112 return this . ProcessTurnMessagesAsync ( async ( pendingMessages , ctx , ct ) =>
106113 {
107- pendingMessages . Add ( new ChatMessage ( ChatRole . Tool , [ result ] ) ) ;
114+ pendingMessages . Add ( new ChatMessage ( ChatRole . Tool , [ result ] )
115+ {
116+ AuthorName = this . _agent . Name ?? this . _agent . Id ,
117+ CreatedAt = DateTimeOffset . UtcNow ,
118+ MessageId = Guid . NewGuid ( ) . ToString ( "N" ) ,
119+ } ) ;
108120
109121 await this . ContinueTurnAsync ( pendingMessages , ctx , this . _currentTurnEmitEvents ?? false , ct ) . ConfigureAwait ( false ) ;
110122
@@ -186,16 +198,13 @@ protected override ValueTask TakeTurnAsync(List<ChatMessage> messages, IWorkflow
186198 TurnExtensions . ShouldEmitStreamingEvents ( turnTokenSetting : emitEvents , this . _options . EmitAgentUpdateEvents ) ,
187199 cancellationToken ) ;
188200
189- private async ValueTask < AgentResponse > InvokeAgentAsync ( IEnumerable < ChatMessage > messages , IWorkflowContext context , bool emitEvents , CancellationToken cancellationToken = default )
201+ private async ValueTask < AgentResponse > InvokeAgentAsync ( IEnumerable < ChatMessage > messages , IWorkflowContext context , bool emitUpdateEvents , CancellationToken cancellationToken = default )
190202 {
191- #pragma warning disable MEAI001
192- Dictionary < string , ToolApprovalRequestContent > userInputRequests = new ( ) ;
193- Dictionary < string , FunctionCallContent > functionCalls = new ( ) ;
194203 AgentResponse response ;
204+ AIAgentUnservicedRequestsCollector collector = new ( this . _userInputHandler , this . _functionCallHandler ) ;
195205
196- if ( emitEvents )
206+ if ( emitUpdateEvents )
197207 {
198- #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
199208 // Run the agent in streaming mode only when agent run update events are to be emitted.
200209 IAsyncEnumerable < AgentResponseUpdate > agentStream = this . _agent . RunStreamingAsync (
201210 messages ,
@@ -206,7 +215,7 @@ await this.EnsureSessionAsync(context, cancellationToken).ConfigureAwait(false),
206215 await foreach ( AgentResponseUpdate update in agentStream . ConfigureAwait ( false ) )
207216 {
208217 await context . YieldOutputAsync ( update , cancellationToken ) . ConfigureAwait ( false ) ;
209- ExtractUnservicedRequests ( update . Contents ) ;
218+ collector . ProcessAgentResponseUpdate ( update ) ;
210219 updates . Add ( update ) ;
211220 }
212221
@@ -220,53 +229,16 @@ await this.EnsureSessionAsync(context, cancellationToken).ConfigureAwait(false),
220229 cancellationToken : cancellationToken )
221230 . ConfigureAwait ( false ) ;
222231
223- ExtractUnservicedRequests ( response . Messages . SelectMany ( message => message . Contents ) ) ;
232+ collector . ProcessAgentResponse ( response ) ;
224233 }
225234
226235 if ( this . _options . EmitAgentResponseEvents )
227236 {
228237 await context . YieldOutputAsync ( response , cancellationToken ) . ConfigureAwait ( false ) ;
229238 }
230239
231- if ( userInputRequests . Count > 0 || functionCalls . Count > 0 )
232- {
233- Task userInputTask = this . _userInputHandler ? . ProcessRequestContentsAsync ( userInputRequests , context , cancellationToken ) ?? Task . CompletedTask ;
234- Task functionCallTask = this . _functionCallHandler ? . ProcessRequestContentsAsync ( functionCalls , context , cancellationToken ) ?? Task . CompletedTask ;
235-
236- await Task . WhenAll ( userInputTask , functionCallTask )
237- . ConfigureAwait ( false ) ;
238- }
240+ await collector . SubmitAsync ( context , cancellationToken ) . ConfigureAwait ( false ) ;
239241
240242 return response ;
241-
242- void ExtractUnservicedRequests ( IEnumerable < AIContent > contents )
243- {
244- foreach ( AIContent content in contents )
245- {
246- if ( content is ToolApprovalRequestContent userInputRequest )
247- {
248- // It is an error to simultaneously have multiple outstanding user input requests with the same ID.
249- userInputRequests . Add ( userInputRequest . RequestId , userInputRequest ) ;
250- }
251- else if ( content is ToolApprovalResponseContent userInputResponse )
252- {
253- // If the set of messages somehow already has a corresponding user input response, remove it.
254- _ = userInputRequests . Remove ( userInputResponse . RequestId ) ;
255- }
256- else if ( content is FunctionCallContent functionCall )
257- {
258- // For function calls, we emit an event to notify the workflow.
259- //
260- // possibility 1: this will be handled inline by the agent abstraction
261- // possibility 2: this will not be handled inline by the agent abstraction
262- functionCalls . Add ( functionCall . CallId , functionCall ) ;
263- }
264- else if ( content is FunctionResultContent functionResult )
265- {
266- _ = functionCalls . Remove ( functionResult . CallId ) ;
267- }
268- }
269- }
270- #pragma warning restore MEAI001
271243 }
272244}
0 commit comments