Skip to content

Conversation

@Sameerlite
Copy link
Collaborator

@Sameerlite Sameerlite commented Nov 20, 2025

Title

Litellm anthropic image url support

Relevant issues

Fixes #12899
Fixes #16760

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • I have added a screenshot of my new test passing locally
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

Type

🆕 New Feature
🐛 Bug Fix

Changes

Updated the code to handle image urls for anthropic

@vercel
Copy link

vercel bot commented Nov 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
litellm Ready Ready Preview Comment Nov 20, 2025 3:55pm

assert '"bytes"' in json_str or '"bytes":' in json_str
elif model.startswith("anthropic/"):
# Direct Anthropic models should pass URLs directly
assert "https://upload.wikimedia.org" in json_str

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

The string
https://upload.wikimedia.org
may be at an arbitrary position in the sanitized URL.

Copilot Autofix

AI 1 day ago

To avoid relying on substring checks that are prone to false positives, the test should deserialize the json_str payload, locate the relevant URL by traversing the structured data, and then assert that the expected host appears in the correct field. This can be achieved by parsing the JSON string, walking the nested structure to find the field(s) where the URL is expected, and then verifying that the expected hostname (e.g., upload.wikimedia.org) is present.

Concretely:

  • Parse json_str using json.loads.
  • Write a helper function to recursively search for values of "url" fields in the JSON and collect them.
  • For the relevant test branches (e.g., where URLs should appear), assert that one of these "url" fields contains the expected domain and scheme, using url parsing (e.g., with urllib.parse.urlparse), and verify the hostname matches exactly.
  • Use in or string comparison only as needed on the exact field value, not the entire serialized payload.

Required steps:

  • After obtaining json_str, parse to a dictionary.
  • Add helper function to extract all "url" fields.
  • In the relevant test branches, iterate and parse the extracted URLs, and assert on the hostname.

Suggested changeset 1
tests/test_litellm/test_main.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/test_litellm/test_main.py b/tests/test_litellm/test_main.py
--- a/tests/test_litellm/test_main.py
+++ b/tests/test_litellm/test_main.py
@@ -197,7 +197,22 @@
         # bedrock/invoke models use Anthropic messages API which supports URLs
         if model.startswith("bedrock/invoke/"):
             # bedrock/invoke uses Anthropic messages API format, which supports URLs
-            assert "https://upload.wikimedia.org" in json_str
+            # Parse the JSON and verify that the "url" field contains the right host
+            data_obj = json.loads(json_str)
+            def find_urls(obj):
+                urls = []
+                if isinstance(obj, dict):
+                    for k, v in obj.items():
+                        if k == "url" and isinstance(v, str):
+                            urls.append(v)
+                        else:
+                            urls.extend(find_urls(v))
+                elif isinstance(obj, list):
+                    for item in obj:
+                        urls.extend(find_urls(item))
+                return urls
+            urls = find_urls(data_obj)
+            assert any(urllib.parse.urlparse(u).hostname == "upload.wikimedia.org" for u in urls)
             # For Anthropic, URL references use "url" type, not base64
             assert '"type":"url"' in json_str or '"type": "url"' in json_str
         elif model.startswith("bedrock/"):
@@ -208,7 +223,22 @@
             assert '"bytes"' in json_str or '"bytes":' in json_str
         elif model.startswith("anthropic/"):
             # Direct Anthropic models should pass URLs directly
-            assert "https://upload.wikimedia.org" in json_str
+            # Parse the JSON and verify that the "url" field contains the right host
+            data_obj = json.loads(json_str)
+            def find_urls(obj):
+                urls = []
+                if isinstance(obj, dict):
+                    for k, v in obj.items():
+                        if k == "url" and isinstance(v, str):
+                            urls.append(v)
+                        else:
+                            urls.extend(find_urls(v))
+                elif isinstance(obj, list):
+                    for item in obj:
+                        urls.extend(find_urls(item))
+                return urls
+            urls = find_urls(data_obj)
+            assert any(urllib.parse.urlparse(u).hostname == "upload.wikimedia.org" for u in urls)
             # For Anthropic, URL references use "url" type, not base64
             assert '"type":"url"' in json_str or '"type": "url"' in json_str
         else:
EOF
@@ -197,7 +197,22 @@
# bedrock/invoke models use Anthropic messages API which supports URLs
if model.startswith("bedrock/invoke/"):
# bedrock/invoke uses Anthropic messages API format, which supports URLs
assert "https://upload.wikimedia.org" in json_str
# Parse the JSON and verify that the "url" field contains the right host
data_obj = json.loads(json_str)
def find_urls(obj):
urls = []
if isinstance(obj, dict):
for k, v in obj.items():
if k == "url" and isinstance(v, str):
urls.append(v)
else:
urls.extend(find_urls(v))
elif isinstance(obj, list):
for item in obj:
urls.extend(find_urls(item))
return urls
urls = find_urls(data_obj)
assert any(urllib.parse.urlparse(u).hostname == "upload.wikimedia.org" for u in urls)
# For Anthropic, URL references use "url" type, not base64
assert '"type":"url"' in json_str or '"type": "url"' in json_str
elif model.startswith("bedrock/"):
@@ -208,7 +223,22 @@
assert '"bytes"' in json_str or '"bytes":' in json_str
elif model.startswith("anthropic/"):
# Direct Anthropic models should pass URLs directly
assert "https://upload.wikimedia.org" in json_str
# Parse the JSON and verify that the "url" field contains the right host
data_obj = json.loads(json_str)
def find_urls(obj):
urls = []
if isinstance(obj, dict):
for k, v in obj.items():
if k == "url" and isinstance(v, str):
urls.append(v)
else:
urls.extend(find_urls(v))
elif isinstance(obj, list):
for item in obj:
urls.extend(find_urls(item))
return urls
urls = find_urls(data_obj)
assert any(urllib.parse.urlparse(u).hostname == "upload.wikimedia.org" for u in urls)
# For Anthropic, URL references use "url" type, not base64
assert '"type":"url"' in json_str or '"type": "url"' in json_str
else:
Copilot is powered by AI and may make mistakes. Always verify output.
@Sameerlite Sameerlite changed the base branch from main to litellm_sameer_nov_3 November 20, 2025 15:59
@Sameerlite Sameerlite marked this pull request as ready for review November 20, 2025 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Anthropic models request use URL-referenced image [Bug]: Images URLs are converted to Base64 for Anthropic provider

2 participants