@@ -304,6 +304,63 @@ def test__human_assistant_format() -> None:
304304 {"chunk" : {"bytes" : b'"[DONE]"' }},
305305]
306306
307+ MOCK_STREAMING_RESPONSE_QWEN = [
308+ {
309+ "chunk" : {
310+ "bytes" : b'{"choices": [{"delta": {"content": "", "role": "assistant"}, '
311+ b'"finish_reason": null, "index": 0}], '
312+ b'"created": 1759875373, '
313+ b'"id": "chatcmpl-a069cbda08ce4599afae798c4d2de095", '
314+ b'"model": "qwen.qwen3-32b-v1:0", '
315+ b'"object": "chat.completion.chunk", '
316+ b'"service_tier": "auto"}'
317+ }
318+ },
319+ {
320+ "chunk" : {
321+ "bytes" : b'{"choices": [{"delta": {"content": "Hello. \\ nGoodbye."}, '
322+ b'"finish_reason": "stop", "index": 0}], '
323+ b'"created": 1759875373, '
324+ b'"id": "chatcmpl-a069cbda08ce4599afae798c4d2de095", '
325+ b'"model": "qwen.qwen3-32b-v1:0", '
326+ b'"object": "chat.completion.chunk", '
327+ b'"service_tier": "auto", '
328+ b'"amazon-bedrock-invocationMetrics": {'
329+ b'"inputTokenCount": 35, "outputTokenCount": 7, '
330+ b'"invocationLatency": 225, "firstByteLatency": 191}}'
331+ }
332+ },
333+ ]
334+
335+ MOCK_STREAMING_RESPONSE_OPENAI = [
336+ {
337+ "chunk" : {
338+ "bytes" : b'{"choices": [{"delta": {"content": "Hello."}, '
339+ b'"finish_reason": null, "index": 0}], '
340+ b'"created": 1759813667, '
341+ b'"id": "chatcmpl-fa6fb768b71046eeb3880cbb4a1b07c1", '
342+ b'"model": "openai.gpt-oss-20b-1:0", '
343+ b'"object": "chat.completion.chunk", "service_tier": "auto"}'
344+ }
345+ },
346+ {
347+ "chunk" : {
348+ "bytes" : b'{"choices": [{"delta": {}, '
349+ b'"finish_reason": "stop", "index": 0}],'
350+ b' "created": 1759813667, '
351+ b'"id": "chatcmpl-fa6fb768b71046eeb3880cbb4a1b07c1", '
352+ b'"model": "openai.gpt-oss-20b-1:0", '
353+ b'"object": "chat.completion.chunk", '
354+ b'"service_tier": "auto", '
355+ b'"amazon-bedrock-invocationMetrics": {'
356+ b'"inputTokenCount": 84, '
357+ b'"outputTokenCount": 87, '
358+ b'"invocationLatency": 3981, '
359+ b'"firstByteLatency": 3615}}'
360+ }
361+ },
362+ ]
363+
307364
308365async def async_gen_mock_streaming_response () -> AsyncGenerator [Dict , None ]:
309366 for item in MOCK_STREAMING_RESPONSE :
@@ -421,6 +478,56 @@ def writer_streaming_response():
421478 return response
422479
423480
481+ @pytest .fixture
482+ def qwen_response ():
483+ body = MagicMock ()
484+ body .read .return_value = json .dumps (
485+ {"choices" : [{"message" : {"content" : "This is the Qwen output text." }}]}
486+ ).encode ()
487+ response = dict (
488+ body = body ,
489+ ResponseMetadata = {
490+ "HTTPHeaders" : {
491+ "x-amzn-bedrock-input-token-count" : "35" ,
492+ "x-amzn-bedrock-output-token-count" : "42" ,
493+ }
494+ },
495+ )
496+
497+ return response
498+
499+
500+ @pytest .fixture
501+ def qwen_streaming_response ():
502+ response = dict (body = MOCK_STREAMING_RESPONSE_QWEN )
503+ return response
504+
505+
506+ @pytest .fixture
507+ def openai_response ():
508+ body = MagicMock ()
509+ body .read .return_value = json .dumps (
510+ {"choices" : [{"message" : {"content" : "This is the OpenAI output text." }}]}
511+ ).encode ()
512+ response = dict (
513+ body = body ,
514+ ResponseMetadata = {
515+ "HTTPHeaders" : {
516+ "x-amzn-bedrock-input-token-count" : "85" ,
517+ "x-amzn-bedrock-output-token-count" : "80" ,
518+ }
519+ },
520+ )
521+
522+ return response
523+
524+
525+ @pytest .fixture
526+ def openai_streaming_response ():
527+ response = dict (body = MOCK_STREAMING_RESPONSE_OPENAI )
528+ return response
529+
530+
424531@pytest .fixture
425532def cohere_response ():
426533 body = MagicMock ()
@@ -556,6 +663,48 @@ def test_prepare_output_stream_for_writer(writer_streaming_response) -> None:
556663 assert results [1 ] == "lo."
557664
558665
666+ def test_prepare_output_for_qwen (qwen_response ):
667+ result = LLMInputOutputAdapter .prepare_output ("qwen" , qwen_response )
668+ assert result ["text" ] == "This is the Qwen output text."
669+ assert result ["usage" ]["prompt_tokens" ] == 35
670+ assert result ["usage" ]["completion_tokens" ] == 42
671+ assert result ["usage" ]["total_tokens" ] == 77
672+ assert result ["stop_reason" ] is None
673+
674+
675+ def test_prepare_output_stream_for_qwen (qwen_streaming_response ) -> None :
676+ results = [
677+ chunk .text
678+ for chunk in LLMInputOutputAdapter .prepare_output_stream (
679+ "qwen" , qwen_streaming_response
680+ )
681+ ]
682+
683+ assert results [0 ] == ""
684+ assert results [1 ] == "Hello. \n Goodbye."
685+
686+
687+ def test_prepare_output_for_openai (openai_response ):
688+ result = LLMInputOutputAdapter .prepare_output ("openai" , openai_response )
689+ assert result ["text" ] == "This is the OpenAI output text."
690+ assert result ["usage" ]["prompt_tokens" ] == 85
691+ assert result ["usage" ]["completion_tokens" ] == 80
692+ assert result ["usage" ]["total_tokens" ] == 165
693+ assert result ["stop_reason" ] is None
694+
695+
696+ def test_prepare_output_stream_for_openai (openai_streaming_response ) -> None :
697+ results = [
698+ chunk .text
699+ for chunk in LLMInputOutputAdapter .prepare_output_stream (
700+ "openai" , openai_streaming_response
701+ )
702+ ]
703+
704+ assert results [0 ] == "Hello."
705+ assert results [1 ] == ""
706+
707+
559708def test_prepare_output_for_cohere (cohere_response ):
560709 result = LLMInputOutputAdapter .prepare_output ("cohere" , cohere_response )
561710 assert result ["text" ] == "This is the Cohere output text."
0 commit comments