@@ -38,6 +38,16 @@ class StreamingState:
38
38
function_calls : dict [int , ResponseFunctionToolCall ] = field (default_factory = dict )
39
39
40
40
41
+ class SequenceNumber :
42
+ def __init__ (self ):
43
+ self ._sequence_number = 0
44
+
45
+ def get_and_increment (self ) -> int :
46
+ num = self ._sequence_number
47
+ self ._sequence_number += 1
48
+ return num
49
+
50
+
41
51
class ChatCmplStreamHandler :
42
52
@classmethod
43
53
async def handle_stream (
@@ -47,13 +57,14 @@ async def handle_stream(
47
57
) -> AsyncIterator [TResponseStreamEvent ]:
48
58
usage : CompletionUsage | None = None
49
59
state = StreamingState ()
50
-
60
+ sequence_number = SequenceNumber ()
51
61
async for chunk in stream :
52
62
if not state .started :
53
63
state .started = True
54
64
yield ResponseCreatedEvent (
55
65
response = response ,
56
66
type = "response.created" ,
67
+ sequence_number = sequence_number .get_and_increment (),
57
68
)
58
69
59
70
# This is always set by the OpenAI API, but not by others e.g. LiteLLM
@@ -89,6 +100,7 @@ async def handle_stream(
89
100
item = assistant_item ,
90
101
output_index = 0 ,
91
102
type = "response.output_item.added" ,
103
+ sequence_number = sequence_number .get_and_increment (),
92
104
)
93
105
yield ResponseContentPartAddedEvent (
94
106
content_index = state .text_content_index_and_output [0 ],
@@ -100,6 +112,7 @@ async def handle_stream(
100
112
annotations = [],
101
113
),
102
114
type = "response.content_part.added" ,
115
+ sequence_number = sequence_number .get_and_increment (),
103
116
)
104
117
# Emit the delta for this segment of content
105
118
yield ResponseTextDeltaEvent (
@@ -108,6 +121,7 @@ async def handle_stream(
108
121
item_id = FAKE_RESPONSES_ID ,
109
122
output_index = 0 ,
110
123
type = "response.output_text.delta" ,
124
+ sequence_number = sequence_number .get_and_increment (),
111
125
)
112
126
# Accumulate the text into the response part
113
127
state .text_content_index_and_output [1 ].text += delta .content
@@ -134,6 +148,7 @@ async def handle_stream(
134
148
item = assistant_item ,
135
149
output_index = 0 ,
136
150
type = "response.output_item.added" ,
151
+ sequence_number = sequence_number .get_and_increment (),
137
152
)
138
153
yield ResponseContentPartAddedEvent (
139
154
content_index = state .refusal_content_index_and_output [0 ],
@@ -145,6 +160,7 @@ async def handle_stream(
145
160
annotations = [],
146
161
),
147
162
type = "response.content_part.added" ,
163
+ sequence_number = sequence_number .get_and_increment (),
148
164
)
149
165
# Emit the delta for this segment of refusal
150
166
yield ResponseRefusalDeltaEvent (
@@ -153,6 +169,7 @@ async def handle_stream(
153
169
item_id = FAKE_RESPONSES_ID ,
154
170
output_index = 0 ,
155
171
type = "response.refusal.delta" ,
172
+ sequence_number = sequence_number .get_and_increment (),
156
173
)
157
174
# Accumulate the refusal string in the output part
158
175
state .refusal_content_index_and_output [1 ].refusal += delta .refusal
@@ -190,6 +207,7 @@ async def handle_stream(
190
207
output_index = 0 ,
191
208
part = state .text_content_index_and_output [1 ],
192
209
type = "response.content_part.done" ,
210
+ sequence_number = sequence_number .get_and_increment (),
193
211
)
194
212
195
213
if state .refusal_content_index_and_output :
@@ -201,6 +219,7 @@ async def handle_stream(
201
219
output_index = 0 ,
202
220
part = state .refusal_content_index_and_output [1 ],
203
221
type = "response.content_part.done" ,
222
+ sequence_number = sequence_number .get_and_increment (),
204
223
)
205
224
206
225
# Actually send events for the function calls
@@ -216,13 +235,15 @@ async def handle_stream(
216
235
),
217
236
output_index = function_call_starting_index ,
218
237
type = "response.output_item.added" ,
238
+ sequence_number = sequence_number .get_and_increment (),
219
239
)
220
240
# Then, yield the args
221
241
yield ResponseFunctionCallArgumentsDeltaEvent (
222
242
delta = function_call .arguments ,
223
243
item_id = FAKE_RESPONSES_ID ,
224
244
output_index = function_call_starting_index ,
225
245
type = "response.function_call_arguments.delta" ,
246
+ sequence_number = sequence_number .get_and_increment (),
226
247
)
227
248
# Finally, the ResponseOutputItemDone
228
249
yield ResponseOutputItemDoneEvent (
@@ -235,6 +256,7 @@ async def handle_stream(
235
256
),
236
257
output_index = function_call_starting_index ,
237
258
type = "response.output_item.done" ,
259
+ sequence_number = sequence_number .get_and_increment (),
238
260
)
239
261
240
262
# Finally, send the Response completed event
@@ -258,6 +280,7 @@ async def handle_stream(
258
280
item = assistant_msg ,
259
281
output_index = 0 ,
260
282
type = "response.output_item.done" ,
283
+ sequence_number = sequence_number .get_and_increment (),
261
284
)
262
285
263
286
for function_call in state .function_calls .values ():
@@ -289,4 +312,5 @@ async def handle_stream(
289
312
yield ResponseCompletedEvent (
290
313
response = final_response ,
291
314
type = "response.completed" ,
315
+ sequence_number = sequence_number .get_and_increment (),
292
316
)
0 commit comments