Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix linting issues in image conversion code
Co-authored-by: TomeHirata <[email protected]>
  • Loading branch information
Copilot and TomeHirata committed Oct 29, 2025
commit ac2709b8fead9e87a1aee2c01789664ced2c08fd
10 changes: 5 additions & 5 deletions dspy/clients/lm.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,19 +485,19 @@ def _convert_chat_request_to_responses_request(request: dict[str, Any]):
def _convert_content_item_to_responses_format(item: dict[str, Any]) -> dict[str, Any]:
"""
Convert a content item from Chat API format to Responses API format.

For images, converts from:
{"type": "image_url", "image_url": {"url": "..."}}
To:
{"type": "input_image", "source": {"type": "url", "url": "..."}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Does this match the documented interface?
https://platform.openai.com/docs/api-reference/responses/create?lang=curl
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right - I was using the wrong format. Fixed in 6da81b3 to match the OpenAI documentation. The Responses API expects {"type": "input_image", "image_url": "..."} with image_url as a direct property, not a nested source object.

or:
{"type": "input_image", "source": {"type": "base64", "media_type": "...", "data": "..."}}

For text and other types, passes through as-is (already in correct format).
"""
if item.get("type") == "image_url":
image_url = item.get("image_url", {}).get("url", "")

# Check if it's a base64 data URI
if image_url.startswith("data:"):
# Extract media type and base64 data
Expand All @@ -515,7 +515,7 @@ def _convert_content_item_to_responses_format(item: dict[str, Any]) -> dict[str,
"data": data,
}
}

# Otherwise treat as URL
return {
"type": "input_image",
Expand All @@ -524,7 +524,7 @@ def _convert_content_item_to_responses_format(item: dict[str, Any]) -> dict[str,
"url": image_url,
}
}

# For non-image items, return as-is
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot I this we also need to convert text item from type: text to type: input_text. See https://platform.openai.com/docs/api-reference/responses

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed in 0055def - now converting text items from type: "text" to type: "input_text" to match the OpenAI Responses API format.

return item

Expand Down
30 changes: 15 additions & 15 deletions tests/clients/test_lm.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ def test_reasoning_model_requirements(model_name):
lm = dspy.LM(
model=model_name,
)
assert lm.kwargs["temperature"] == None
assert lm.kwargs["max_completion_tokens"] == None
assert lm.kwargs["temperature"] is None
assert lm.kwargs["max_completion_tokens"] is None


def test_dump_state():
Expand Down Expand Up @@ -638,7 +638,7 @@ def test_api_key_not_saved_in_json():
def test_responses_api_converts_images_correctly():
"""Test that image_url format is converted to input_image format for Responses API."""
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot can we remove docstring for the test cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 43c8fc9

from dspy.clients.lm import _convert_chat_request_to_responses_request

# Test with base64 image
request_with_base64_image = {
"model": "openai/gpt-5-mini",
Expand All @@ -657,26 +657,26 @@ def test_responses_api_converts_images_correctly():
}
]
}

result = _convert_chat_request_to_responses_request(request_with_base64_image)

assert "input" in result
assert len(result["input"]) == 1
assert result["input"][0]["role"] == "user"

content = result["input"][0]["content"]
assert len(content) == 2

# First item should be text (passed through as-is since it's already in correct format)
assert content[0]["type"] == "text"
assert content[0]["text"] == "What's in this image?"

# Second item should be converted to input_image format
assert content[1]["type"] == "input_image"
assert content[1]["source"]["type"] == "base64"
assert content[1]["source"]["media_type"] == "image/png"
assert content[1]["source"]["data"] == "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="

# Test with URL image
request_with_url_image = {
"model": "openai/gpt-5-mini",
Expand All @@ -694,9 +694,9 @@ def test_responses_api_converts_images_correctly():
}
]
}

result = _convert_chat_request_to_responses_request(request_with_url_image)

content = result["input"][0]["content"]
assert len(content) == 1
assert content[0]["type"] == "input_image"
Expand Down Expand Up @@ -730,7 +730,7 @@ def test_responses_api_with_image_input():
temperature=1.0,
max_tokens=16000,
)

# Test with messages containing an image
messages = [
{
Expand All @@ -746,18 +746,18 @@ def test_responses_api_with_image_input():
]
}
]

lm_result = lm(messages=messages)

assert lm_result == [{"text": "This is a test answer with image input."}]

dspy_responses.assert_called_once()
call_args = dspy_responses.call_args.kwargs

# Verify the request was converted correctly
assert "input" in call_args
content = call_args["input"][0]["content"]

# Check that image was converted to input_image format
image_content = [c for c in content if c.get("type") == "input_image"]
assert len(image_content) == 1
Expand Down