@@ -343,8 +343,8 @@ def test_reasoning_model_requirements(model_name):
343343 lm = dspy .LM (
344344 model = model_name ,
345345 )
346- assert lm .kwargs ["temperature" ] == None
347- assert lm .kwargs ["max_completion_tokens" ] == None
346+ assert lm .kwargs ["temperature" ] is None
347+ assert lm .kwargs ["max_completion_tokens" ] is None
348348
349349
350350def test_dump_state ():
@@ -633,3 +633,127 @@ def test_api_key_not_saved_in_json():
633633 assert saved_state ["lm" ]["model" ] == "openai/gpt-4o-mini"
634634 assert saved_state ["lm" ]["temperature" ] == 1.0
635635 assert saved_state ["lm" ]["max_tokens" ] == 100
636+
637+
638+ def test_responses_api_converts_images_correctly ():
639+ from dspy .clients .lm import _convert_chat_request_to_responses_request
640+
641+ # Test with base64 image
642+ request_with_base64_image = {
643+ "model" : "openai/gpt-5-mini" ,
644+ "messages" : [
645+ {
646+ "role" : "user" ,
647+ "content" : [
648+ {"type" : "text" , "text" : "What's in this image?" },
649+ {
650+ "type" : "image_url" ,
651+ "image_url" : {
652+ "url" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
653+ }
654+ }
655+ ]
656+ }
657+ ]
658+ }
659+
660+ result = _convert_chat_request_to_responses_request (request_with_base64_image )
661+
662+ assert "input" in result
663+ assert len (result ["input" ]) == 1
664+ assert result ["input" ][0 ]["role" ] == "user"
665+
666+ content = result ["input" ][0 ]["content" ]
667+ assert len (content ) == 2
668+
669+ # First item should be text converted to input_text format
670+ assert content [0 ]["type" ] == "input_text"
671+ assert content [0 ]["text" ] == "What's in this image?"
672+
673+ # Second item should be converted to input_image format
674+ assert content [1 ]["type" ] == "input_image"
675+ assert content [1 ]["image_url" ] == "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
676+
677+ # Test with URL image
678+ request_with_url_image = {
679+ "model" : "openai/gpt-5-mini" ,
680+ "messages" : [
681+ {
682+ "role" : "user" ,
683+ "content" : [
684+ {
685+ "type" : "image_url" ,
686+ "image_url" : {
687+ "url" : "https://example.com/image.jpg"
688+ }
689+ }
690+ ]
691+ }
692+ ]
693+ }
694+
695+ result = _convert_chat_request_to_responses_request (request_with_url_image )
696+
697+ content = result ["input" ][0 ]["content" ]
698+ assert len (content ) == 1
699+ assert content [0 ]["type" ] == "input_image"
700+ assert content [0 ]["image_url" ] == "https://example.com/image.jpg"
701+
702+
703+ def test_responses_api_with_image_input ():
704+ api_response = make_response (
705+ output_blocks = [
706+ ResponseOutputMessage (
707+ ** {
708+ "id" : "msg_1" ,
709+ "type" : "message" ,
710+ "role" : "assistant" ,
711+ "status" : "completed" ,
712+ "content" : [
713+ {"type" : "output_text" , "text" : "This is a test answer with image input." , "annotations" : []}
714+ ],
715+ },
716+ ),
717+ ]
718+ )
719+
720+ with mock .patch ("litellm.responses" , autospec = True , return_value = api_response ) as dspy_responses :
721+ lm = dspy .LM (
722+ model = "openai/gpt-5-mini" ,
723+ model_type = "responses" ,
724+ cache = False ,
725+ temperature = 1.0 ,
726+ max_tokens = 16000 ,
727+ )
728+
729+ # Test with messages containing an image
730+ messages = [
731+ {
732+ "role" : "user" ,
733+ "content" : [
734+ {"type" : "text" , "text" : "Describe this image" },
735+ {
736+ "type" : "image_url" ,
737+ "image_url" : {
738+ "url" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
739+ }
740+ }
741+ ]
742+ }
743+ ]
744+
745+ lm_result = lm (messages = messages )
746+
747+ assert lm_result == [{"text" : "This is a test answer with image input." }]
748+
749+ dspy_responses .assert_called_once ()
750+ call_args = dspy_responses .call_args .kwargs
751+
752+ # Verify the request was converted correctly
753+ assert "input" in call_args
754+ content = call_args ["input" ][0 ]["content" ]
755+
756+ # Check that image was converted to input_image format
757+ image_content = [c for c in content if c .get ("type" ) == "input_image" ]
758+ assert len (image_content ) == 1
759+ assert image_content [0 ]["image_url" ] == "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
0 commit comments