Skip to content

Commit 47ead84

Browse files
moonbox3CopilotCopilot
authored
Python: Support detail field in OpenAI Chat API image_url payload (#4756)
* Support detail field in OpenAI image_url payload (#4616) Include the optional 'detail' field from Content.additional_properties when building image_url payloads for the OpenAI Chat API, matching the existing pattern used for 'filename' in document file payloads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply pre-commit auto-fixes * Remove reproduction report Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify detail extraction from additional_properties (#4616) - Remove unnecessary hasattr check; additional_properties is always initialized as a dict on Content instances. - Use 'is not None' instead of truthy check to be more precise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Apply pre-commit auto-fixes * Remove detail allowlist in chat client to align with responses client Replace the strict allowlist check ('low', 'high', 'auto') with an isinstance(detail, str) check so that any valid string detail value is passed through to OpenAI. This aligns the chat client behavior with the responses client, which passes detail through unconditionally. Also add test coverage for: - Future/unknown string detail values being passed through - Data URI images (covering the 'data' branch of the match) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4c287c2 commit 47ead84

2 files changed

Lines changed: 98 additions & 1 deletion

File tree

python/packages/core/agent_framework/openai/_chat_client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,9 +713,13 @@ def _prepare_content_for_openai(self, content: Content) -> dict[str, Any]:
713713
"content": content.result if content.result is not None else "",
714714
}
715715
case "data" | "uri" if content.has_top_level_media_type("image"):
716+
image_url_obj: dict[str, Any] = {"url": content.uri}
717+
detail = content.additional_properties.get("detail")
718+
if isinstance(detail, str):
719+
image_url_obj["detail"] = detail
716720
return {
717721
"type": "image_url",
718-
"image_url": {"url": content.uri},
722+
"image_url": image_url_obj,
719723
}
720724
case "data" | "uri" if content.has_top_level_media_type("audio"):
721725
if content.media_type and "wav" in content.media_type:

python/packages/core/tests/openai/test_openai_chat_client.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,99 @@ def test_prepare_content_for_openai_data_content_image(
462462
assert result["input_audio"]["format"] == "mp3"
463463

464464

465+
def test_prepare_content_for_openai_image_url_detail(
466+
openai_unit_test_env: dict[str, str],
467+
) -> None:
468+
"""Test _prepare_content_for_openai includes the detail field in image_url when specified."""
469+
client = OpenAIChatClient()
470+
471+
# Test image with detail set to "high"
472+
image_with_detail = Content.from_uri(
473+
uri="https://example.com/image.png",
474+
media_type="image/png",
475+
additional_properties={"detail": "high"},
476+
)
477+
478+
result = client._prepare_content_for_openai(image_with_detail) # type: ignore
479+
480+
assert result["type"] == "image_url"
481+
assert result["image_url"]["url"] == "https://example.com/image.png"
482+
assert result["image_url"]["detail"] == "high"
483+
484+
# Test image with detail set to "low"
485+
image_low_detail = Content.from_uri(
486+
uri="https://example.com/image.png",
487+
media_type="image/png",
488+
additional_properties={"detail": "low"},
489+
)
490+
491+
result = client._prepare_content_for_openai(image_low_detail) # type: ignore
492+
493+
assert result["image_url"]["detail"] == "low"
494+
495+
# Test image with detail set to "auto"
496+
image_auto_detail = Content.from_uri(
497+
uri="https://example.com/image.png",
498+
media_type="image/png",
499+
additional_properties={"detail": "auto"},
500+
)
501+
502+
result = client._prepare_content_for_openai(image_auto_detail) # type: ignore
503+
504+
assert result["image_url"]["detail"] == "auto"
505+
506+
# Test image without detail should not include it
507+
image_no_detail = Content.from_uri(
508+
uri="https://example.com/image.png",
509+
media_type="image/png",
510+
)
511+
512+
result = client._prepare_content_for_openai(image_no_detail) # type: ignore
513+
514+
assert result["type"] == "image_url"
515+
assert result["image_url"]["url"] == "https://example.com/image.png"
516+
assert "detail" not in result["image_url"]
517+
518+
# Test image with a future/unknown string detail value should pass it through
519+
image_future_detail = Content.from_uri(
520+
uri="https://example.com/image.png",
521+
media_type="image/png",
522+
additional_properties={"detail": "ultra"},
523+
)
524+
525+
result = client._prepare_content_for_openai(image_future_detail) # type: ignore
526+
527+
assert result["type"] == "image_url"
528+
assert result["image_url"]["url"] == "https://example.com/image.png"
529+
assert result["image_url"]["detail"] == "ultra"
530+
531+
# Test image with data URI should include detail
532+
image_data_uri = Content.from_uri(
533+
uri="data:image/png;base64,iVBORw0KGgo",
534+
media_type="image/png",
535+
additional_properties={"detail": "high"},
536+
)
537+
538+
result = client._prepare_content_for_openai(image_data_uri) # type: ignore
539+
540+
assert result["type"] == "image_url"
541+
assert result["image_url"]["url"] == "data:image/png;base64,iVBORw0KGgo"
542+
assert result["image_url"]["detail"] == "high"
543+
544+
# Test image with non-string detail value should not include it
545+
image_non_string_detail = Content.from_uri(
546+
uri="https://example.com/image.png",
547+
media_type="image/png",
548+
additional_properties={"detail": 123},
549+
)
550+
551+
result = client._prepare_content_for_openai(image_non_string_detail) # type: ignore
552+
553+
assert result["type"] == "image_url"
554+
assert result["image_url"]["url"] == "https://example.com/image.png"
555+
assert "detail" not in result["image_url"]
556+
557+
465558
def test_prepare_content_for_openai_document_file_mapping(
466559
openai_unit_test_env: dict[str, str],
467560
) -> None:

0 commit comments

Comments
 (0)