10
10
OutputPipelineStep ,
11
11
)
12
12
from codegate .types .common import Delta , ModelResponse , StreamingChoices
13
+ from codegate .types .openai import (
14
+ ChatCompletionRequest ,
15
+ ChoiceDelta ,
16
+ MessageDelta ,
17
+ StreamingChatCompletion ,
18
+ )
13
19
14
20
15
21
class MockOutputPipelineStep (OutputPipelineStep ):
@@ -26,30 +32,37 @@ def name(self) -> str:
26
32
27
33
async def process_chunk (
28
34
self ,
29
- chunk : ModelResponse ,
35
+ chunk : StreamingChatCompletion ,
30
36
context : OutputPipelineContext ,
31
37
input_context : PipelineContext = None ,
32
- ) -> list [ModelResponse ]:
38
+ ) -> list [StreamingChatCompletion ]:
33
39
if self ._should_pause :
34
40
return []
35
41
36
- if self ._modify_content and chunk .choices [0 ].delta .content :
42
+ if next (chunk .get_content (), None ) is None :
43
+ return [chunk ] # short-circuit
44
+
45
+ content = next (chunk .get_content ())
46
+ if content .get_text () is None or content .get_text () == "" :
47
+ return [chunk ] # short-circuit
48
+
49
+ if self ._modify_content :
37
50
# Append step name to content to track modifications
38
- modified_content = f"{ chunk . choices [ 0 ]. delta . content } _{ self .name } "
39
- chunk . choices [ 0 ]. delta . content = modified_content
51
+ modified_content = f"{ content . get_text () } _{ self .name } "
52
+ content . set_text ( modified_content )
40
53
41
54
return [chunk ]
42
55
43
56
44
- def create_model_response (content : str , id : str = "test" ) -> ModelResponse :
45
- """Helper to create test ModelResponse objects"""
46
- return ModelResponse (
57
+ def create_model_response (content : str , id : str = "test" ) -> StreamingChatCompletion :
58
+ """Helper to create test StreamingChatCompletion objects"""
59
+ return StreamingChatCompletion (
47
60
id = id ,
48
61
choices = [
49
- StreamingChoices (
62
+ ChoiceDelta (
50
63
finish_reason = None ,
51
64
index = 0 ,
52
- delta = Delta (content = content , role = "assistant" ),
65
+ delta = MessageDelta (content = content , role = "assistant" ),
53
66
logprobs = None ,
54
67
)
55
68
],
@@ -64,7 +77,7 @@ class MockContext:
64
77
def __init__ (self ):
65
78
self .sensitive = False
66
79
67
- def add_output (self , chunk : ModelResponse ):
80
+ def add_output (self , chunk : StreamingChatCompletion ):
68
81
pass
69
82
70
83
@@ -157,10 +170,23 @@ async def mock_stream():
157
170
async for chunk in instance .process_stream (mock_stream ()):
158
171
chunks .append (chunk )
159
172
173
+ # NOTE: this test ensured that buffered chunks were flushed at
174
+ # the end of the pipeline. This was possible as long as the
175
+ # current implementation assumed that all messages were
176
+ # equivalent and position was not relevant.
177
+ #
178
+ # This is not the case for Anthropic, whose protocol is much
179
+ # more structured than that of the others.
180
+ #
181
+ # We're not there yet to ensure that such a protocol is not
182
+ # broken in face of messages being arbitrarily retained at
183
+ # each pipeline step, so we decided to treat a clogged
184
+ # pipelines as a bug.
185
+
160
186
# Should get one chunk at the end with all buffered content
161
- assert len (chunks ) == 1
187
+ assert len (chunks ) == 0
162
188
# Content should be buffered and combined
163
- assert chunks [0 ].choices [0 ].delta .content == "hello world"
189
+ # assert chunks[0].choices[0].delta.content == "hello world"
164
190
# Buffer should be cleared after flush
165
191
assert len (instance ._context .buffer ) == 0
166
192
@@ -180,19 +206,19 @@ def name(self) -> str:
180
206
181
207
async def process_chunk (
182
208
self ,
183
- chunk : ModelResponse ,
209
+ chunk : StreamingChatCompletion ,
184
210
context : OutputPipelineContext ,
185
211
input_context : PipelineContext = None ,
186
- ) -> List [ModelResponse ]:
212
+ ) -> List [StreamingChatCompletion ]:
187
213
# Replace 'world' with 'moon' in buffered content
188
214
content = "" .join (context .buffer )
189
215
if "world" in content :
190
216
content = content .replace ("world" , "moon" )
191
217
chunk .choices = [
192
- StreamingChoices (
218
+ ChoiceDelta (
193
219
finish_reason = None ,
194
220
index = 0 ,
195
- delta = Delta (content = content , role = "assistant" ),
221
+ delta = MessageDelta (content = content , role = "assistant" ),
196
222
logprobs = None ,
197
223
)
198
224
]
@@ -274,10 +300,10 @@ def name(self) -> str:
274
300
275
301
async def process_chunk (
276
302
self ,
277
- chunk : ModelResponse ,
303
+ chunk : StreamingChatCompletion ,
278
304
context : OutputPipelineContext ,
279
305
input_context : PipelineContext = None ,
280
- ) -> List [ModelResponse ]:
306
+ ) -> List [StreamingChatCompletion ]:
281
307
assert input_context .metadata ["test" ] == "value"
282
308
return [chunk ]
283
309
@@ -308,8 +334,6 @@ async def mock_stream():
308
334
async for chunk in instance .process_stream (mock_stream ()):
309
335
chunks .append (chunk )
310
336
311
- # Should get one chunk with combined buffer content
312
- assert len (chunks ) == 1
313
- assert chunks [0 ].choices [0 ].delta .content == "HelloWorld"
314
- # Buffer should be cleared after flush
315
- assert len (instance ._context .buffer ) == 0
337
+ # We do not flush messages anymore, this should be treated as
338
+ # a bug of the pipeline rather than and edge case.
339
+ assert len (chunks ) == 0
0 commit comments