@@ -61,33 +61,75 @@ public async IAsyncEnumerable<ChatCompletionResponse> StreamingChatCompletionsAs
61
61
using var reader = new StreamReader ( await httpResponseMessage . Content . ReadAsStreamAsync ( ) ) ;
62
62
63
63
var currentEvent = new SseEvent ( ) ;
64
+
64
65
while ( await reader . ReadLineAsync ( ) is { } line )
65
66
{
66
67
if ( ! string . IsNullOrEmpty ( line ) )
67
68
{
68
- currentEvent . Data = line . Substring ( "data:" . Length ) . Trim ( ) ;
69
+ if ( line . StartsWith ( "event:" ) )
70
+ {
71
+ currentEvent . EventType = line . Substring ( "event:" . Length ) . Trim ( ) ;
72
+ }
73
+ else if ( line . StartsWith ( "data:" ) )
74
+ {
75
+ currentEvent . Data = line . Substring ( "data:" . Length ) . Trim ( ) ;
76
+ }
69
77
}
70
- else
78
+ else // an empty line indicates the end of an event
71
79
{
72
- if ( currentEvent . Data == "[DONE]" )
73
- continue ;
80
+ if ( currentEvent . EventType == "content_block_start" && ! string . IsNullOrEmpty ( currentEvent . Data ) )
81
+ {
82
+ var dataBlock = JsonSerializer . Deserialize < DataBlock > ( currentEvent . Data ! ) ;
83
+ if ( dataBlock != null && dataBlock . ContentBlock ? . Type == "tool_use" )
84
+ {
85
+ currentEvent . ContentBlock = dataBlock . ContentBlock ;
86
+ }
87
+ }
74
88
75
- if ( currentEvent . Data != null )
89
+ if ( currentEvent . EventType is "message_start" or "content_block_delta" or "message_delta" && currentEvent . Data != null )
76
90
{
77
- yield return await JsonSerializer . DeserializeAsync < ChatCompletionResponse > (
91
+ var res = await JsonSerializer . DeserializeAsync < ChatCompletionResponse > (
78
92
new MemoryStream ( Encoding . UTF8 . GetBytes ( currentEvent . Data ) ) ,
79
- cancellationToken : cancellationToken ) ?? throw new Exception ( "Failed to deserialize response" ) ;
93
+ cancellationToken : cancellationToken ) ;
94
+
95
+ if ( res == null )
96
+ {
97
+ throw new Exception ( "Failed to deserialize response" ) ;
98
+ }
99
+
100
+ if ( res . Delta ? . Type == "input_json_delta" && ! string . IsNullOrEmpty ( res . Delta . PartialJson ) &&
101
+ currentEvent . ContentBlock != null )
102
+ {
103
+ currentEvent . ContentBlock . AppendDeltaParameters ( res . Delta . PartialJson ! ) ;
104
+ }
105
+ else if ( res . Delta is { StopReason : "tool_use" } && currentEvent . ContentBlock != null )
106
+ {
107
+ if ( res . Content == null )
108
+ {
109
+ res . Content = [ currentEvent . ContentBlock . CreateToolUseContent ( ) ] ;
110
+ }
111
+ else
112
+ {
113
+ res . Content . Add ( currentEvent . ContentBlock . CreateToolUseContent ( ) ) ;
114
+ }
115
+
116
+ currentEvent = new SseEvent ( ) ;
117
+ }
118
+
119
+ yield return res ;
80
120
}
81
- else if ( currentEvent . Data != null )
121
+ else if ( currentEvent . EventType == "error" && currentEvent . Data != null )
82
122
{
83
123
var res = await JsonSerializer . DeserializeAsync < ErrorResponse > (
84
124
new MemoryStream ( Encoding . UTF8 . GetBytes ( currentEvent . Data ) ) , cancellationToken : cancellationToken ) ;
85
125
86
126
throw new Exception ( res ? . Error ? . Message ) ;
87
127
}
88
128
89
- // Reset the current event for the next one
90
- currentEvent = new SseEvent ( ) ;
129
+ if ( currentEvent . ContentBlock == null )
130
+ {
131
+ currentEvent = new SseEvent ( ) ;
132
+ }
91
133
}
92
134
}
93
135
}
@@ -113,11 +155,50 @@ public void Dispose()
113
155
114
156
private struct SseEvent
115
157
{
158
+ public string EventType { get ; set ; }
116
159
public string ? Data { get ; set ; }
160
+ public ContentBlock ? ContentBlock { get ; set ; }
117
161
118
- public SseEvent ( string ? data = null )
162
+ public SseEvent ( string eventType , string ? data = null , ContentBlock ? contentBlock = null )
119
163
{
164
+ EventType = eventType ;
120
165
Data = data ;
166
+ ContentBlock = contentBlock ;
121
167
}
122
168
}
169
+
170
+ private class ContentBlock
171
+ {
172
+ [ JsonPropertyName ( "type" ) ]
173
+ public string ? Type { get ; set ; }
174
+
175
+ [ JsonPropertyName ( "id" ) ]
176
+ public string ? Id { get ; set ; }
177
+
178
+ [ JsonPropertyName ( "name" ) ]
179
+ public string ? Name { get ; set ; }
180
+
181
+ [ JsonPropertyName ( "input" ) ]
182
+ public object ? Input { get ; set ; }
183
+
184
+ public string ? parameters { get ; set ; }
185
+
186
+ public void AppendDeltaParameters ( string deltaParams )
187
+ {
188
+ StringBuilder sb = new StringBuilder ( parameters ) ;
189
+ sb . Append ( deltaParams ) ;
190
+ parameters = sb . ToString ( ) ;
191
+ }
192
+
193
+ public ToolUseContent CreateToolUseContent ( )
194
+ {
195
+ return new ToolUseContent { Id = Id , Name = Name , Input = parameters } ;
196
+ }
197
+ }
198
+
199
+ private class DataBlock
200
+ {
201
+ [ JsonPropertyName ( "content_block" ) ]
202
+ public ContentBlock ? ContentBlock { get ; set ; }
203
+ }
123
204
}
0 commit comments