mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-03 07:23:33 +00:00
fix(anthropic): run the advisor loop for an anthropic deployment on a Foundry host
The advisor interceptor treated every anthropic deployment as native, so a Foundry-hosted one received the advisor_20260301 tool and Foundry answered 400. Interceptors now get the deployment's beta row from resolve_beta_provider, so that deployment runs litellm's advisor loop the same way an azure_ai deployment does
This commit is contained in:
parent
55385e483f
commit
083990f6d7
3 changed files with 49 additions and 4 deletions
|
|
@ -15,7 +15,7 @@ from typing import Final, cast
|
|||
from pydantic import TypeAdapter
|
||||
|
||||
import litellm
|
||||
from litellm.anthropic_beta_headers_manager import aload_beta_headers_config
|
||||
from litellm.anthropic_beta_headers_manager import aload_beta_headers_config, resolve_beta_provider
|
||||
from litellm.constants import PROVIDERS_THAT_AUTHENTICATE_ON_PROVIDER_INFO
|
||||
from litellm.integrations.anthropic_cache_control_hook import AnthropicCacheControlHook
|
||||
from litellm.litellm_core_utils.core_helpers import as_str_mapping
|
||||
|
|
@ -540,11 +540,16 @@ async def anthropic_messages(
|
|||
if short_circuit_response is not None:
|
||||
return short_circuit_response
|
||||
|
||||
serving_provider: Final = (
|
||||
resolve_beta_provider(custom_llm_provider, AnthropicModelInfo.get_api_base(api_base))
|
||||
if custom_llm_provider
|
||||
else None
|
||||
)
|
||||
# Run registered MessagesInterceptors (e.g. advisor orchestration loop).
|
||||
# Named params on `anthropic_messages` are bound to locals, not `**kwargs`,
|
||||
# so forward them explicitly — otherwise interceptor sub-calls drop them.
|
||||
for interceptor in get_messages_interceptors():
|
||||
if interceptor.can_handle(tools, custom_llm_provider):
|
||||
if interceptor.can_handle(tools, serving_provider):
|
||||
return await interceptor.handle(
|
||||
model=model,
|
||||
messages=messages,
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ _interceptors = [
|
|||
|
||||
### `AdvisorOrchestrationHandler`
|
||||
|
||||
Handles `advisor_20260301` tool for providers that don't support it natively (all non-Anthropic providers for now).
|
||||
Handles `advisor_20260301` tool for providers that don't support it natively (every provider but Anthropic itself, including an `anthropic/` deployment on an Azure AI Foundry host).
|
||||
|
||||
**Triggers when:** `advisor_20260301` is in `tools` AND `custom_llm_provider` is not in `ADVISOR_NATIVE_PROVIDERS`.
|
||||
**Triggers when:** `advisor_20260301` is in `tools` AND the deployment's beta row (`resolve_beta_provider`, which maps an `anthropic` deployment on a Foundry host to `azure_ai`) is not in `ADVISOR_NATIVE_PROVIDERS`.
|
||||
|
||||
**What it does:**
|
||||
- Translates the advisor tool to a regular function tool the provider understands.
|
||||
|
|
|
|||
|
|
@ -190,6 +190,46 @@ async def test_anthropic_provider_bypasses_interceptor():
|
|||
assert any("Native anthropic" in b.get("text", "") for b in text_blocks)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"api_base",
|
||||
["https://res.services.ai.azure.com/anthropic", "https://res.cognitiveservices.azure.com/anthropic"],
|
||||
)
|
||||
async def test_anthropic_deployment_on_foundry_runs_the_advisor_loop(api_base):
|
||||
from litellm.llms.anthropic.messages.native.handler import (
|
||||
anthropic_messages,
|
||||
)
|
||||
|
||||
sent_tool_types = []
|
||||
|
||||
async def mock_handler(model, messages, tools, stream, max_tokens, **kwargs):
|
||||
sent_tool_types.append([t.get("type") for t in tools or []])
|
||||
if len(sent_tool_types) == 1:
|
||||
return _advisor_call_resp()
|
||||
if len(sent_tool_types) == 2:
|
||||
return _text_resp("Use trial division.", model="claude-opus-4-6")
|
||||
return _text_resp("def is_prime(n): ...")
|
||||
|
||||
with patch(
|
||||
"litellm.llms.anthropic.messages.native.interceptors.advisor._call_messages_handler",
|
||||
side_effect=mock_handler,
|
||||
):
|
||||
result = await anthropic_messages(
|
||||
model="claude-sonnet-4-6",
|
||||
messages=MESSAGES,
|
||||
tools=[ADVISOR_TOOL],
|
||||
stream=False,
|
||||
max_tokens=512,
|
||||
custom_llm_provider="anthropic",
|
||||
api_base=api_base,
|
||||
)
|
||||
|
||||
assert len(sent_tool_types) == 3
|
||||
assert all("advisor_20260301" not in types for types in sent_tool_types)
|
||||
assert isinstance(result, dict)
|
||||
assert not [b for b in result.get("content", []) if b.get("type") == "tool_use" and b.get("name") == "advisor"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Regression: top-level named params must be forwarded into executor sub-call
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue