From 005f67bff324bf0b7b6bcddfaf8b65485da42ec0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 May 2026 18:37:04 +0000 Subject: [PATCH] Fix OCR zero credit and Bedrock support checks --- litellm/cost_calculator.py | 2 +- .../anthropic_claude3_transformation.py | 2 +- .../anthropic_claude3_transformation.py | 2 +- ...ations_anthropic_claude3_transformation.py | 26 ++++++++++++++ .../test_anthropic_claude3_transformation.py | 34 +++++++++++++++++++ tests/test_litellm/llms/reducto/test_cost.py | 26 ++++++++++++++ 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 898972fa2db..b61da0717aa 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -1890,7 +1890,7 @@ def ocr_cost( cost_per_credit = None if model_info is not None: cost_per_credit = model_info.get("ocr_cost_per_credit") - if credits is not None and cost_per_credit: + if credits is not None and cost_per_credit is not None: return cost_per_credit * credits, 0.0 pages_processed = response.usage_info.pages_processed diff --git a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py index 9e8467fe394..02f0579c845 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude3_transformation.py @@ -171,7 +171,7 @@ class AmazonAnthropicClaudeConfig(AmazonInvokeConfig, AnthropicConfig): anthropic_request.pop("stream", None) anthropic_request.pop("output_format", None) if not _supports_factory( - model=model, custom_llm_provider=None, key="supports_output_config" + model=model, custom_llm_provider="bedrock", key="supports_output_config" ): anthropic_request.pop("output_config", None) if "anthropic_version" not in anthropic_request: diff --git a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py index 0c7461bebf8..a17b158f4a0 100644 --- a/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py +++ b/litellm/llms/bedrock/messages/invoke_transformations/anthropic_claude3_transformation.py @@ -562,7 +562,7 @@ class AmazonAnthropicClaudeMessagesConfig( # but older models do not — strip it to avoid request rejection. # Ref: https://github.com/BerriAI/litellm/issues/22797 if not _supports_factory( - model=model, custom_llm_provider=None, key="supports_output_config" + model=model, custom_llm_provider="bedrock", key="supports_output_config" ): anthropic_messages_request.pop("output_config", None) diff --git a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py index 4495e3f4101..b2e254901f4 100644 --- a/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py @@ -2,6 +2,7 @@ import asyncio import json import os import sys +from unittest.mock import patch import pytest @@ -429,6 +430,31 @@ def test_output_config_forwarded_for_bedrock_chat_invoke_request(): assert result["max_tokens"] == 100 +def test_bedrock_chat_invoke_checks_output_config_support_with_bedrock_provider(): + config = AmazonAnthropicClaudeConfig() + messages = [{"role": "user", "content": "test"}] + optional_params = {"max_tokens": 100, "output_config": {"effort": "high"}} + + with patch( + "litellm.llms.bedrock.chat.invoke_transformations.anthropic_claude3_transformation._supports_factory", + return_value=True, + ) as mock_supports_factory: + result = config.transform_request( + model="us.anthropic.claude-opus-4-7", + messages=messages, + optional_params=optional_params, + litellm_params={}, + headers={}, + ) + + mock_supports_factory.assert_called_once_with( + model="us.anthropic.claude-opus-4-7", + custom_llm_provider="bedrock", + key="supports_output_config", + ) + assert result["output_config"] == {"effort": "high"} + + def test_output_format_removed_from_bedrock_invoke_request(): """ Test that output_format parameter is removed from Bedrock Invoke requests. diff --git a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py index 44964c5d822..6a52b5d9707 100644 --- a/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py +++ b/tests/test_litellm/llms/bedrock/messages/invoke_transformations/test_anthropic_claude3_transformation.py @@ -666,6 +666,40 @@ def test_bedrock_messages_preserves_output_config_for_claude_4_6(): assert result.get("max_tokens") == 4096 +def test_bedrock_messages_checks_output_config_support_with_bedrock_provider(): + from unittest.mock import patch + + from litellm.types.router import GenericLiteLLMParams + + cfg = AmazonAnthropicClaudeMessagesConfig() + messages = [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}] + optional_params = { + "max_tokens": 4096, + "output_config": { + "effort": "high", + }, + } + + with patch( + "litellm.llms.bedrock.messages.invoke_transformations.anthropic_claude3_transformation._supports_factory", + return_value=True, + ) as mock_supports_factory: + result = cfg.transform_anthropic_messages_request( + model="us.anthropic.claude-opus-4-7", + messages=messages, + anthropic_messages_optional_request_params=optional_params, + litellm_params=GenericLiteLLMParams(), + headers={}, + ) + + mock_supports_factory.assert_called_with( + model="us.anthropic.claude-opus-4-7", + custom_llm_provider="bedrock", + key="supports_output_config", + ) + assert result["output_config"] == {"effort": "high"} + + def test_bedrock_messages_forwards_output_config(): """Bedrock Invoke /v1/messages forwards ``output_config`` for supported models.""" from unittest.mock import patch diff --git a/tests/test_litellm/llms/reducto/test_cost.py b/tests/test_litellm/llms/reducto/test_cost.py index bbc65f90531..bb89100d020 100644 --- a/tests/test_litellm/llms/reducto/test_cost.py +++ b/tests/test_litellm/llms/reducto/test_cost.py @@ -27,6 +27,32 @@ def test_ocr_cost_prefers_credit_pricing_when_pages_processed_is_none(monkeypatc assert cost == 0.03 +def test_ocr_cost_prefers_zero_credit_pricing_over_page_pricing(monkeypatch): + monkeypatch.setattr( + litellm, + "get_model_info", + lambda model, custom_llm_provider=None: { + "ocr_cost_per_credit": 0.0, + "ocr_cost_per_page": 0.5, + }, + ) + + response = OCRResponse( + pages=[OCRPage(index=0, markdown="free credit priced")], + model="parse-v3", + usage_info=OCRUsageInfo(pages_processed=2, credits=10), + ) + + cost = completion_cost( + completion_response=response, + model="reducto/parse-v3", + custom_llm_provider="reducto", + call_type="ocr", + ) + + assert cost == 0.0 + + def test_ocr_cost_falls_back_to_page_pricing(monkeypatch): monkeypatch.setattr( litellm,