mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-05 07:37:50 +00:00
Merge c54da063f2 into f1795e6d44
This commit is contained in:
commit
59442b273c
5 changed files with 73 additions and 4 deletions
|
|
@ -124,12 +124,14 @@ pub enum AnthropicBeta {
|
|||
ToolSearchTool20251019,
|
||||
#[strum(serialize = "inline-tools-2026-09-15")]
|
||||
InlineTools20260915,
|
||||
#[strum(serialize = "server-side-fallback-2026-07-01")]
|
||||
ServerSideFallback20260701,
|
||||
#[strum(default, transparent)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl AnthropicBeta {
|
||||
pub const KNOWN: [Self; 40] = [
|
||||
pub const KNOWN: [Self; 41] = [
|
||||
Self::Oauth20250420,
|
||||
Self::WebFetch20250910,
|
||||
Self::WebSearch20250305,
|
||||
|
|
@ -170,6 +172,7 @@ impl AnthropicBeta {
|
|||
Self::ToolExamples20251029,
|
||||
Self::ToolSearchTool20251019,
|
||||
Self::InlineTools20260915,
|
||||
Self::ServerSideFallback20260701,
|
||||
];
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
|
|
@ -241,7 +244,7 @@ impl AnthropicBeta {
|
|||
| Self::ThinkingBindingControls20260801 => &[],
|
||||
Self::DangerousToolUse20260903 => &[BetaProvider::BedrockConverse],
|
||||
Self::Effort20251124 => &[BetaProvider::VertexAi],
|
||||
Self::FastMode20260201 | Self::Oauth20250420 => &[
|
||||
Self::FastMode20260201 | Self::Oauth20250420 | Self::ServerSideFallback20260701 => &[
|
||||
BetaProvider::AzureAi,
|
||||
BetaProvider::BedrockConverse,
|
||||
BetaProvider::Bedrock,
|
||||
|
|
@ -441,7 +444,8 @@ mod tests {
|
|||
AnthropicBeta::TokenEfficientTools20250219,
|
||||
AnthropicBeta::ToolExamples20251029,
|
||||
AnthropicBeta::ToolSearchTool20251019,
|
||||
AnthropicBeta::InlineTools20260915
|
||||
AnthropicBeta::InlineTools20260915,
|
||||
AnthropicBeta::ServerSideFallback20260701
|
||||
)]
|
||||
beta: AnthropicBeta,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@
|
|||
"mid-conversation-output-config-2026-07-01": "mid-conversation-output-config-2026-07-01",
|
||||
"thinking-display-updates-2026-08-18": "thinking-display-updates-2026-08-18",
|
||||
"mid-conversation-tool-changes-2026-07-01": "mid-conversation-tool-changes-2026-07-01",
|
||||
"inline-tools-2026-09-15": "inline-tools-2026-09-15"
|
||||
"inline-tools-2026-09-15": "inline-tools-2026-09-15",
|
||||
"server-side-fallback-2026-07-01": "server-side-fallback-2026-07-01"
|
||||
},
|
||||
"azure_ai": {
|
||||
"advisor-tool-2026-03-01": null,
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ from litellm.types.utils import (
|
|||
)
|
||||
from litellm.types.utils import Message as LitellmMessage
|
||||
from litellm.utils import (
|
||||
EXTRA_BODY_ROUTING_KEYS,
|
||||
ModelResponse,
|
||||
Usage,
|
||||
add_dummy_tool,
|
||||
|
|
@ -165,6 +166,7 @@ _DEFER_LOADING_ADAPTER: Final = TypeAdapter(bool)
|
|||
_TOOL_FIELDS_ADAPTER: Final = TypeAdapter(Mapping[str, object])
|
||||
_TOOLS_ADAPTER: Final = TypeAdapter(list[object])
|
||||
_LITELLM_PARAMS_ADAPTER: Final = TypeAdapter(Mapping[str, object])
|
||||
_EXTRA_BODY_ADAPTER: Final = TypeAdapter(Mapping[str, object])
|
||||
_NATIVE_OUTPUT_STRING_FORMATS: Final = frozenset(
|
||||
("date-time", "time", "date", "duration", "email", "hostname", "uri", "ipv4", "ipv6", "uuid")
|
||||
)
|
||||
|
|
@ -2418,6 +2420,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
request_params: Final = cast( # cast-ok: optional_params is the untyped dict of the shared transform signature
|
||||
dict[str, object], optional_params
|
||||
)
|
||||
extra_body: Final = _EXTRA_BODY_ADAPTER.validate_python(request_params.pop("extra_body", None) or {})
|
||||
marked_tools: Final = tools_marked_for_a_tool_config_point(
|
||||
request_params.get("tools"), request_params.pop("cache_control_injection_points", None)
|
||||
)
|
||||
|
|
@ -2455,6 +2458,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
|
|||
"messages": anthropic_messages,
|
||||
**optional_params,
|
||||
**({} if marked_tools is None else {"tools": list(marked_tools)}),
|
||||
**{k: v for k, v in extra_body.items() if k not in EXTRA_BODY_ROUTING_KEYS},
|
||||
}
|
||||
|
||||
self._apply_output_config(data=data, model=model, optional_params=optional_params)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,47 @@ def test_anthropic_completion_does_not_send_deployment_default_limits():
|
|||
assert "default_api_key_tpm_limit" not in request_body
|
||||
|
||||
|
||||
def test_anthropic_completion_sends_extra_body_fields_top_level_without_redirecting_model():
|
||||
captured_requests: list[httpx.Request] = []
|
||||
|
||||
def respond(request: httpx.Request) -> httpx.Response:
|
||||
captured_requests.append(request)
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"id": "msg_extra_body",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"model": "claude-sonnet-5-5",
|
||||
"content": [{"type": "text", "text": "Hello"}],
|
||||
"stop_reason": "end_turn",
|
||||
"stop_sequence": None,
|
||||
"usage": {"input_tokens": 1, "output_tokens": 1},
|
||||
},
|
||||
)
|
||||
|
||||
client = HTTPHandler(client=httpx.Client(transport=httpx.MockTransport(respond)))
|
||||
try:
|
||||
litellm.completion(
|
||||
model="anthropic/claude-sonnet-5-5",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
api_key="test-key",
|
||||
client=client,
|
||||
max_tokens=16,
|
||||
extra_body={"fallbacks": [{"model": "claude-opus-5-5"}], "model": "claude-opus-5-5"},
|
||||
)
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
request_body = json.loads(captured_requests[0].content)
|
||||
assert request_body == {
|
||||
"model": "claude-sonnet-5-5",
|
||||
"messages": [{"role": "user", "content": [{"type": "text", "text": "Hello"}]}],
|
||||
"max_tokens": 16,
|
||||
"fallbacks": [{"model": "claude-opus-5-5"}],
|
||||
}, request_body
|
||||
|
||||
|
||||
def test_anthropic_tool_history_without_tools_injects_a_dummy_tool(
|
||||
respx_mock: respx.MockRouter, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
|
|
|
|||
|
|
@ -474,6 +474,25 @@ class TestAnthropicBetaHeadersFiltering:
|
|||
|
||||
assert filtered == ["inline-tools-2026-09-15"]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("provider", "expected"),
|
||||
[
|
||||
("anthropic", ["server-side-fallback-2026-07-01"]),
|
||||
("vertex_ai", []),
|
||||
("bedrock", []),
|
||||
],
|
||||
)
|
||||
def test_server_side_fallback_forwarded_only_to_the_claude_api(self, provider: str, expected: list[str]) -> None:
|
||||
"""Anthropic rejects the `fallbacks` request field with 400 "fallbacks: Extra inputs are not permitted" unless
|
||||
this beta is sent, and documents it for the Claude API only
|
||||
(https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback, fetched 2026-10-26)"""
|
||||
filtered: Final = filter_and_transform_beta_headers(
|
||||
beta_headers=["server-side-fallback-2026-07-01"],
|
||||
provider=provider,
|
||||
)
|
||||
|
||||
assert filtered == expected
|
||||
|
||||
def test_null_value_headers_filtered(self):
|
||||
"""Test that headers with null values are always filtered out."""
|
||||
for provider in [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue