@@ -561,11 +561,13 @@ async def test_stream(openai_client, model_id, model, agenerator, alist):
561561 tru_events = await alist (response )
562562 exp_events = [
563563 {"messageStart" : {"role" : "assistant" }},
564- {"contentBlockStart" : {"start" : {}}},
564+ {"contentBlockStart" : {"start" : {}}}, # reasoning_content starts
565565 {"contentBlockDelta" : {"delta" : {"reasoningContent" : {"text" : "\n I'm thinking" }}}},
566+ {"contentBlockStop" : {}}, # reasoning_content ends
567+ {"contentBlockStart" : {"start" : {}}}, # text starts
566568 {"contentBlockDelta" : {"delta" : {"text" : "I'll calculate" }}},
567569 {"contentBlockDelta" : {"delta" : {"text" : "that for you" }}},
568- {"contentBlockStop" : {}},
570+ {"contentBlockStop" : {}}, # text ends
569571 {
570572 "contentBlockStart" : {
571573 "start" : {
@@ -631,9 +633,7 @@ async def test_stream_empty(openai_client, model_id, model, agenerator, alist):
631633 tru_events = await alist (response )
632634 exp_events = [
633635 {"messageStart" : {"role" : "assistant" }},
634- {"contentBlockStart" : {"start" : {}}},
635- {"contentBlockStop" : {}},
636- {"messageStop" : {"stopReason" : "end_turn" }},
636+ {"messageStop" : {"stopReason" : "end_turn" }}, # No content blocks when no content
637637 ]
638638
639639 assert len (tru_events ) == len (exp_events )
@@ -678,10 +678,10 @@ async def test_stream_with_empty_choices(openai_client, model, agenerator, alist
678678 tru_events = await alist (response )
679679 exp_events = [
680680 {"messageStart" : {"role" : "assistant" }},
681- {"contentBlockStart" : {"start" : {}}},
681+ {"contentBlockStart" : {"start" : {}}}, # text content starts
682682 {"contentBlockDelta" : {"delta" : {"text" : "content" }}},
683683 {"contentBlockDelta" : {"delta" : {"text" : "content" }}},
684- {"contentBlockStop" : {}},
684+ {"contentBlockStop" : {}}, # text content ends
685685 {"messageStop" : {"stopReason" : "end_turn" }},
686686 {
687687 "metadata" : {
@@ -756,6 +756,74 @@ def test_tool_choice_none_no_warning(model, messages, captured_warnings):
756756 assert len (captured_warnings ) == 0
757757
758758
759+ @pytest .mark .parametrize (
760+ "new_data_type, prev_data_type, expected_chunks, expected_data_type" ,
761+ [
762+ ("text" , None , [{"contentBlockStart" : {"start" : {}}}], "text" ),
763+ (
764+ "reasoning_content" ,
765+ "text" ,
766+ [{"contentBlockStop" : {}}, {"contentBlockStart" : {"start" : {}}}],
767+ "reasoning_content" ,
768+ ),
769+ ("text" , "text" , [], "text" ),
770+ ],
771+ )
772+ def test__stream_switch_content (model , new_data_type , prev_data_type , expected_chunks , expected_data_type ):
773+ """Test _stream_switch_content method for content type switching."""
774+ chunks , data_type = model ._stream_switch_content (new_data_type , prev_data_type )
775+ assert chunks == expected_chunks
776+ assert data_type == expected_data_type
777+
778+
779+ def test_format_request_messages_excludes_reasoning_content ():
780+ """Test that reasoningContent is excluded from formatted messages."""
781+ messages = [
782+ {
783+ "content" : [
784+ {"text" : "Hello" },
785+ {"reasoningContent" : {"reasoningText" : {"text" : "excluded" }}},
786+ ],
787+ "role" : "user" ,
788+ },
789+ ]
790+
791+ tru_result = OpenAIModel .format_request_messages (messages )
792+
793+ # Only text content should be included
794+ exp_result = [
795+ {
796+ "content" : [{"text" : "Hello" , "type" : "text" }],
797+ "role" : "user" ,
798+ },
799+ ]
800+ assert tru_result == exp_result
801+
802+
803+ @pytest .mark .asyncio
804+ async def test_structured_output_context_overflow_exception (openai_client , model , messages , test_output_model_cls ):
805+ """Test that structured output also handles context overflow properly."""
806+ # Create a mock OpenAI BadRequestError with context_length_exceeded code
807+ mock_error = openai .BadRequestError (
808+ message = "This model's maximum context length is 4096 tokens. However, your messages resulted in 5000 tokens." ,
809+ response = unittest .mock .MagicMock (),
810+ body = {"error" : {"code" : "context_length_exceeded" }},
811+ )
812+ mock_error .code = "context_length_exceeded"
813+
814+ # Configure the mock client to raise the context overflow error
815+ openai_client .beta .chat .completions .parse .side_effect = mock_error
816+
817+ # Test that the structured_output method converts the error properly
818+ with pytest .raises (ContextWindowOverflowException ) as exc_info :
819+ async for _ in model .structured_output (test_output_model_cls , messages ):
820+ pass
821+
822+ # Verify the exception message contains the original error
823+ assert "maximum context length" in str (exc_info .value )
824+ assert exc_info .value .__cause__ == mock_error
825+
826+
759827@pytest .mark .asyncio
760828async def test_stream_context_overflow_exception (openai_client , model , messages ):
761829 """Test that OpenAI context overflow errors are properly converted to ContextWindowOverflowException."""
@@ -803,30 +871,6 @@ async def test_stream_other_bad_request_errors_passthrough(openai_client, model,
803871 assert exc_info .value == mock_error
804872
805873
806- @pytest .mark .asyncio
807- async def test_structured_output_context_overflow_exception (openai_client , model , messages , test_output_model_cls ):
808- """Test that structured output also handles context overflow properly."""
809- # Create a mock OpenAI BadRequestError with context_length_exceeded code
810- mock_error = openai .BadRequestError (
811- message = "This model's maximum context length is 4096 tokens. However, your messages resulted in 5000 tokens." ,
812- response = unittest .mock .MagicMock (),
813- body = {"error" : {"code" : "context_length_exceeded" }},
814- )
815- mock_error .code = "context_length_exceeded"
816-
817- # Configure the mock client to raise the context overflow error
818- openai_client .beta .chat .completions .parse .side_effect = mock_error
819-
820- # Test that the structured_output method converts the error properly
821- with pytest .raises (ContextWindowOverflowException ) as exc_info :
822- async for _ in model .structured_output (test_output_model_cls , messages ):
823- pass
824-
825- # Verify the exception message contains the original error
826- assert "maximum context length" in str (exc_info .value )
827- assert exc_info .value .__cause__ == mock_error
828-
829-
830874@pytest .mark .asyncio
831875async def test_stream_rate_limit_as_throttle (openai_client , model , messages ):
832876 """Test that all rate limit errors are converted to ModelThrottledException."""
0 commit comments