|
43 | 43 | from sentry_sdk.integrations.openai import ( |
44 | 44 | OpenAIIntegration, |
45 | 45 | _calculate_token_usage, |
| 46 | + _convert_message_parts, |
46 | 47 | ) |
47 | 48 | from sentry_sdk.ai.utils import MAX_GEN_AI_MESSAGE_BYTES |
48 | 49 | from sentry_sdk._types import AnnotatedValue |
@@ -1509,6 +1510,77 @@ def test_openai_message_role_mapping(sentry_init, capture_events): |
1509 | 1510 | assert "ai" not in roles |
1510 | 1511 |
|
1511 | 1512 |
|
| 1513 | +def test_convert_message_parts_image_url_to_blob(): |
| 1514 | + """Test that OpenAI image_url message parts are correctly converted to blob format""" |
| 1515 | + messages = [ |
| 1516 | + { |
| 1517 | + "role": "user", |
| 1518 | + "content": [ |
| 1519 | + { |
| 1520 | + "text": "How many ponies do you see in the image?", |
| 1521 | + "type": "text", |
| 1522 | + }, |
| 1523 | + { |
| 1524 | + "type": "image_url", |
| 1525 | + "image_url": { |
| 1526 | + "url": "data:image/jpeg;base64,/9j/4AAQSkZJRg==", |
| 1527 | + "detail": "high", |
| 1528 | + }, |
| 1529 | + }, |
| 1530 | + ], |
| 1531 | + } |
| 1532 | + ] |
| 1533 | + |
| 1534 | + converted = _convert_message_parts(messages) |
| 1535 | + |
| 1536 | + assert len(converted) == 1 |
| 1537 | + assert converted[0]["role"] == "user" |
| 1538 | + assert isinstance(converted[0]["content"], list) |
| 1539 | + assert len(converted[0]["content"]) == 2 |
| 1540 | + |
| 1541 | + # First item (text) should remain unchanged |
| 1542 | + assert converted[0]["content"][0] == { |
| 1543 | + "text": "How many ponies do you see in the image?", |
| 1544 | + "type": "text", |
| 1545 | + } |
| 1546 | + |
| 1547 | + # Second item (image_url) should be converted to blob format |
| 1548 | + blob_item = converted[0]["content"][1] |
| 1549 | + assert blob_item["type"] == "blob" |
| 1550 | + assert blob_item["modality"] == "image" |
| 1551 | + assert blob_item["mime_type"] == "data:image/jpeg" |
| 1552 | + assert blob_item["content"] == "/9j/4AAQSkZJRg==" |
| 1553 | + # Verify the original image_url structure is replaced |
| 1554 | + assert "image_url" not in blob_item |
| 1555 | + |
| 1556 | + |
| 1557 | +def test_convert_message_parts_image_url_to_uri(): |
| 1558 | + """Test that OpenAI image_url with non-data URLs are converted to uri format""" |
| 1559 | + messages = [ |
| 1560 | + { |
| 1561 | + "role": "user", |
| 1562 | + "content": [ |
| 1563 | + { |
| 1564 | + "type": "image_url", |
| 1565 | + "image_url": { |
| 1566 | + "url": "https://example.com/image.jpg", |
| 1567 | + "detail": "low", |
| 1568 | + }, |
| 1569 | + }, |
| 1570 | + ], |
| 1571 | + } |
| 1572 | + ] |
| 1573 | + |
| 1574 | + converted = _convert_message_parts(messages) |
| 1575 | + |
| 1576 | + assert len(converted) == 1 |
| 1577 | + uri_item = converted[0]["content"][0] |
| 1578 | + assert uri_item["type"] == "uri" |
| 1579 | + assert uri_item["uri"] == "https://example.com/image.jpg" |
| 1580 | + # Verify the original image_url structure is replaced |
| 1581 | + assert "image_url" not in uri_item |
| 1582 | + |
| 1583 | + |
1512 | 1584 | def test_openai_message_truncation(sentry_init, capture_events): |
1513 | 1585 | """Test that large messages are truncated properly in OpenAI integration.""" |
1514 | 1586 | sentry_init( |
|
0 commit comments