mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-03 07:23:33 +00:00
fix(anthropic): handle missing and null response content
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
a713ed6bab
commit
ce2b05d80e
2 changed files with 38 additions and 3 deletions
|
|
@ -179,6 +179,14 @@ NATIVE_OUTPUT_MAX_OPTIONAL_PARAMETERS: Final = 24
|
|||
NATIVE_OUTPUT_MAX_GRAMMAR_BRANCHES: Final = 2048
|
||||
|
||||
|
||||
class _ResponseContent(BaseModel):
|
||||
model_config = ConfigDict(frozen=True, from_attributes=True)
|
||||
content: object = None
|
||||
|
||||
|
||||
_RESPONSE_FIELDS: Final = TypeAdapter(_ResponseContent)
|
||||
|
||||
|
||||
def _tool_type(tool: object) -> str | None:
|
||||
match tool:
|
||||
case {"type": str() as tool_type}:
|
||||
|
|
@ -2593,7 +2601,8 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
web_search_results: list[object] | None = None
|
||||
tool_results: list[_AnthropicToolResultBlock] | None = None
|
||||
compaction_blocks: list[object] | None = None
|
||||
for idx, content in enumerate(completion_response["content"]):
|
||||
response_fields: Final = _RESPONSE_FIELDS.validate_python(completion_response)
|
||||
for idx, content in enumerate(completion_response["content"] if response_fields.content is not None else []):
|
||||
if content["type"] == "text":
|
||||
text_content += content["text"]
|
||||
## TOOL CALLING
|
||||
|
|
@ -2830,7 +2839,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
# Anthropic doesn't always include tool_search_requests in the usage object
|
||||
if tool_search_requests is None and completion_response is not None:
|
||||
tool_search_count = 0
|
||||
for content in completion_response.get("content", []):
|
||||
for content in completion_response.get("content") or []:
|
||||
if content.get("type") == "server_tool_use":
|
||||
tool_name = content.get("name", "")
|
||||
if "tool_search" in tool_name:
|
||||
|
|
@ -3092,7 +3101,9 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
_message = json_mode_message
|
||||
|
||||
model_response.choices[0].message = _message
|
||||
model_response.hidden_params["original_response"] = completion_response["content"]
|
||||
model_response.hidden_params["original_response"] = _RESPONSE_FIELDS.validate_python(
|
||||
completion_response
|
||||
).content
|
||||
model_response.choices[0].finish_reason = cast(
|
||||
OpenAIChatCompletionFinishReason,
|
||||
map_finish_reason(completion_response["stop_reason"]),
|
||||
|
|
|
|||
|
|
@ -96,6 +96,30 @@ def test_anthropic_json_mode_non_streaming_mixed_internal_and_user_tools():
|
|||
assert extra == '{"answer": 42}'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("completion_response", [{}, {"content": None}, {"content": []}])
|
||||
def test_calculate_usage_without_content_preserves_reported_tokens(completion_response: dict[str, object]) -> None:
|
||||
usage: Final = AnthropicConfig().calculate_usage(
|
||||
usage_object={"input_tokens": 12, "output_tokens": 3},
|
||||
reasoning_content=None,
|
||||
completion_response=completion_response,
|
||||
)
|
||||
assert (usage.prompt_tokens, usage.completion_tokens, usage.total_tokens) == (12, 3, 15)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("content_fields", [{}, {"content": None}, {"content": []}])
|
||||
def test_transform_response_without_content_preserves_reported_usage(content_fields: dict[str, object]) -> None:
|
||||
body: Final = {
|
||||
"id": "msg_empty",
|
||||
"model": "claude-opus-5-5",
|
||||
"stop_reason": "end_turn",
|
||||
"usage": {"input_tokens": 12, "output_tokens": 3},
|
||||
**content_fields,
|
||||
}
|
||||
response: Final = _transform_message_body(body)
|
||||
assert response.choices[0].message.content is None
|
||||
assert (response.usage.prompt_tokens, response.usage.completion_tokens, response.usage.total_tokens) == (12, 3, 15)
|
||||
|
||||
|
||||
def test_calculate_usage():
|
||||
"""
|
||||
Do not include cache_creation_input_tokens in the prompt_tokens
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue