11using System . Runtime . CompilerServices ;
2+ using System . Text ;
23using System . Text . Json ;
34using Microsoft . Extensions . AI ;
45
@@ -55,6 +56,8 @@ async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync
5556 yield break ;
5657 }
5758
59+ var toolCallBuilders = new Dictionary < int , ( string Id , string Name , StringBuilder Args ) > ( ) ;
60+
5861 foreach ( var streamMsg in streamMessages )
5962 {
6063 foreach ( var choice in streamMsg . Choices )
@@ -63,11 +66,56 @@ async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync
6366 {
6467 ResponseId = streamMsg . Id ,
6568 Role = ChatRole . Assistant ,
66- FinishReason = ToFinishReason ( choice . FinishReason ) ,
6769 RawRepresentation = streamMsg ,
6870 } ;
6971
70- ProcessStreamingDelta ( update , choice . Delta ) ;
72+ var delta = choice . Delta ;
73+ if ( delta . IsValue2 && delta . Value2 is { } contentDelta )
74+ {
75+ if ( ! string . IsNullOrEmpty ( contentDelta . Content ) )
76+ {
77+ update . Contents . Add ( new TextContent ( contentDelta . Content )
78+ {
79+ RawRepresentation = contentDelta ,
80+ } ) ;
81+ }
82+ }
83+ else if ( delta . IsValue3 && delta . Value3 is { } toolCallsFirst )
84+ {
85+ foreach ( var toolCall in toolCallsFirst . ToolCalls )
86+ {
87+ toolCallBuilders [ toolCall . Index ] = (
88+ Id : toolCall . Id ,
89+ Name : toolCall . Function . Name ,
90+ Args : new StringBuilder ( ) ) ;
91+ }
92+ }
93+ else if ( delta . IsValue4 && delta . Value4 is { } toolCallsDelta )
94+ {
95+ foreach ( var toolCall in toolCallsDelta . ToolCalls )
96+ {
97+ if ( ! string . IsNullOrEmpty ( toolCall . Function . Arguments ) &&
98+ toolCallBuilders . TryGetValue ( toolCall . Index , out var existing ) )
99+ {
100+ existing . Args . Append ( toolCall . Function . Arguments ) ;
101+ }
102+ }
103+ }
104+
105+ if ( choice . FinishReason is not null )
106+ {
107+ update . FinishReason = ToFinishReason ( choice . FinishReason ) ;
108+ if ( toolCallBuilders . Count > 0 )
109+ {
110+ foreach ( var ( _, builder ) in toolCallBuilders )
111+ {
112+ update . Contents . Add ( new FunctionCallContent (
113+ builder . Id , builder . Name ,
114+ ParseArguments ( builder . Args . ToString ( ) ) ) ) ;
115+ }
116+ toolCallBuilders . Clear ( ) ;
117+ }
118+ }
71119
72120 yield return update ;
73121 }
@@ -87,50 +135,6 @@ async IAsyncEnumerable<ChatResponseUpdate> IChatClient.GetStreamingResponseAsync
87135 }
88136 }
89137
90- private static void ProcessStreamingDelta (
91- ChatResponseUpdate update ,
92- AnyOf < ChatStreamingFirstDelta , ChatStreamingContentDelta , ChatStreamingToolCallsFirstDelta , ChatStreamingToolCallsDelta > delta )
93- {
94- if ( delta . IsValue2 && delta . Value2 is { } contentDelta )
95- {
96- if ( ! string . IsNullOrEmpty ( contentDelta . Content ) )
97- {
98- update . Contents . Add ( new TextContent ( contentDelta . Content )
99- {
100- RawRepresentation = contentDelta ,
101- } ) ;
102- }
103- }
104- else if ( delta . IsValue3 && delta . Value3 is { } toolCallsFirst )
105- {
106- foreach ( var toolCall in toolCallsFirst . ToolCalls )
107- {
108- update . Contents . Add ( new FunctionCallContent (
109- callId : toolCall . Id ,
110- name : toolCall . Function . Name ,
111- arguments : null )
112- {
113- RawRepresentation = toolCall ,
114- } ) ;
115- }
116- }
117- else if ( delta . IsValue4 && delta . Value4 is { } toolCallsDelta )
118- {
119- foreach ( var toolCall in toolCallsDelta . ToolCalls )
120- {
121- if ( ! string . IsNullOrEmpty ( toolCall . Function . Arguments ) )
122- {
123- update . Contents . Add ( new FunctionCallContent (
124- callId : null ,
125- name : null ,
126- arguments : ParseArguments ( toolCall . Function . Arguments ) )
127- {
128- RawRepresentation = toolCall ,
129- } ) ;
130- }
131- }
132- }
133- }
134138
135139 private ChatRequest CreateChatRequest (
136140 IEnumerable < ChatMessage > messages ,
0 commit comments