Merge branch 'main' into feature-python-foundry-agents

This commit is contained in:
Dmytro Struk
2025-11-12 07:53:38 -08:00
Unverified
24 changed files with 3820 additions and 3510 deletions
@@ -330,11 +330,19 @@ class AnthropicClient(BaseChatClient):
if content.has_top_level_media_type("image"):
a_content.append({
"type": "image",
"source": {"data": content.uri, "media_type": content.media_type},
"source": {
"data": content.get_data_bytes_as_str(),
"media_type": content.media_type,
"type": "base64",
},
})
else:
logger.debug(f"Ignoring unsupported data content media type: {content.media_type} for now")
case "uri":
if content.has_top_level_media_type("image"):
a_content.append({"type": "image", "source": {"type": "url", "url": content.uri}})
else:
logger.debug(f"Ignoring unsupported data content media type: {content.media_type} for now")
case "function_call":
a_content.append({
"type": "tool_use",
Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

@@ -1,5 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.
import os
from pathlib import Path
from typing import Annotated
from unittest.mock import MagicMock, patch
@@ -9,6 +10,7 @@ from agent_framework import (
ChatMessage,
ChatOptions,
ChatResponseUpdate,
DataContent,
FinishReason,
FunctionCallContent,
FunctionResultContent,
@@ -775,3 +777,31 @@ async def test_anthropic_client_integration_ordering() -> None:
assert response is not None
assert response.messages[0].text is not None
@pytest.mark.flaky
@skip_if_anthropic_integration_tests_disabled
async def test_anthropic_client_integration_images() -> None:
"""Integration test with images."""
client = AnthropicClient()
# get a image from the assets folder
image_path = Path(__file__).parent / "assets" / "sample_image.jpg"
with open(image_path, "rb") as img_file: # noqa [ASYNC230]
image_bytes = img_file.read()
messages = [
ChatMessage(
role=Role.USER,
contents=[
TextContent(text="Describe this image"),
DataContent(media_type="image/jpeg", data=image_bytes),
],
),
]
response = await client.get_response(messages=messages)
assert response is not None
assert response.messages[0].text is not None
assert "house" in response.messages[0].text.lower()
+26 -3
View File
@@ -897,6 +897,9 @@ class TextReasoningContent(BaseContent):
return self
TDataContent = TypeVar("TDataContent", bound="DataContent")
class DataContent(BaseContent):
"""Represents binary data content with an associated media type (also known as a MIME type).
@@ -1079,8 +1082,8 @@ class DataContent(BaseContent):
except Exception:
return "png" # Fallback if decoding fails
@classmethod
def create_data_uri_from_base64(cls, image_base64: str) -> tuple[str, str]:
@staticmethod
def create_data_uri_from_base64(image_base64: str) -> tuple[str, str]:
"""Create a data URI and media type from base64 image data.
Args:
@@ -1089,11 +1092,31 @@ class DataContent(BaseContent):
Returns:
Tuple of (data_uri, media_type)
"""
format_type = cls.detect_image_format_from_base64(image_base64)
format_type = DataContent.detect_image_format_from_base64(image_base64)
uri = f"data:image/{format_type};base64,{image_base64}"
media_type = f"image/{format_type}"
return uri, media_type
def get_data_bytes_as_str(self) -> str:
"""Extracts and returns the base64-encoded data from the data URI.
Returns:
The binary data as str.
"""
match = URI_PATTERN.match(self.uri)
if not match:
raise ValueError(f"Invalid data URI format: {self.uri}")
return match.group("base64_data")
def get_data_bytes(self) -> bytes:
"""Extracts and returns the binary data from the data URI.
Returns:
The binary data as bytes.
"""
base64_data = self.get_data_bytes_as_str()
return base64.b64decode(base64_data)
class UriContent(BaseContent):
"""Represents a URI content.
+1 -1
View File
@@ -56,7 +56,7 @@ math = [
[dependency-groups]
dev = [
"uv>=0.8.2,<0.9.0",
"uv",
"pre-commit >= 3.7",
"ruff>=0.11.8",
"pytest>=8.4.1",
+1 -1
View File
@@ -39,7 +39,7 @@ dependencies = [
[dependency-groups]
dev = [
"uv>=0.8.2,<0.10.0",
"uv>=0.9,<1.0.0",
"flit>=3.12.0",
"pre-commit >= 3.7",
"ruff>=0.11.8",
+3443 -3427
View File
File diff suppressed because it is too large Load Diff