Fix OCR zero credit and Bedrock support checks

This commit is contained in:
Cursor Agent 2026-05-01 18:37:04 +00:00
parent 87803d541d
commit 33b1f1237b
No known key found for this signature in database
6 changed files with 102 additions and 6 deletions

View file

@ -1812,7 +1812,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

View file

@ -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:

View file

@ -516,7 +516,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)

View file

@ -2,6 +2,7 @@ import asyncio
import json
import os
import sys
from unittest.mock import patch
import pytest
@ -440,6 +441,31 @@ def test_output_config_removed_from_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.

View file

@ -668,6 +668,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_strips_output_config_with_output_format():
"""
When both output_config and output_format are present, output_format
@ -931,7 +965,10 @@ async def test_promote_message_start_cache_when_message_stop_omits_cache_fields(
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
"usage": {"input_tokens": 10, "output_tokens": 181},
}
yield {"type": "message_stop", "usage": {"input_tokens": 10, "output_tokens": 181}}
yield {
"type": "message_stop",
"usage": {"input_tokens": 10, "output_tokens": 181},
}
merged: list[dict] = []
async for chunk in cfg._promote_message_stop_usage(_stream()):
@ -985,7 +1022,11 @@ async def test_unified_bedrock_messages_cache_on_start_only_never_negative_cost(
},
},
}
yield {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}
yield {
"type": "content_block_start",
"index": 0,
"content_block": {"type": "text", "text": ""},
}
yield {
"type": "content_block_delta",
"index": 0,
@ -997,7 +1038,10 @@ async def test_unified_bedrock_messages_cache_on_start_only_never_negative_cost(
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
"usage": {"output_tokens": 181, "input_tokens": 10},
}
yield {"type": "message_stop", "usage": {"input_tokens": 10, "output_tokens": 181}}
yield {
"type": "message_stop",
"usage": {"input_tokens": 10, "output_tokens": 181},
}
logging_obj = LiteLLMLoggingObj(
model="bedrock/anthropic.claude-3-5-sonnet-20240620-v1:0",

View file

@ -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,