Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6e44b5b
feat(anthropic): workload identity federation and pluggable identity …
mateo-berri Sep 5, 2026
cc5c6c0
chore: merge litellm_internal_staging into litellm_anthropic_wif_backend
mateo-berri Sep 5, 2026
b738e13
fix(anthropic): let batch-result downloads mint from deployment param…
mateo-berri Sep 5, 2026
3f36fe3
chore: merge litellm_internal_staging into litellm_anthropic_wif_backend
mateo-berri Sep 5, 2026
adc50b0
fix(types): move the WIF kwargs key sets to a leaf module so the kwar…
mateo-berri Sep 5, 2026
fd63ff1
test(anthropic): pin case-insensitive matching of WIF exchange-host a…
mateo-berri Sep 5, 2026
7930c8d
fix(anthropic): end workload identity federation errors without a per…
mateo-berri Sep 5, 2026
b414494
fix(proxy): decrypt stored litellm_params before the WIF write gate
mateo-berri Sep 5, 2026
2a4cf5a
fix(proxy): hide WIF secret references from /health output
mateo-berri Sep 5, 2026
9d681f5
fix(proxy): keep the proxy error shape on credential endpoint refusals
mateo-berri Sep 5, 2026
e0ee4b6
fix(proxy): hide identity token file paths from /health output
mateo-berri Sep 5, 2026
7a53a2b
fix(anthropic): rename the federation workspace param so Bedrock's an…
mateo-berri Sep 5, 2026
66b72db
fix(auth): share one exchanged token across workers reading the same …
mateo-berri Sep 6, 2026
779da8f
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 6, 2026
31f88a3
Merge remote-tracking branch 'origin/litellm_internal_staging' into l…
mateo-berri Sep 6, 2026
d817d90
fix: keep anthropic federation from being shadowed or leaked
mateo-berri Sep 6, 2026
44b9871
fix: unlink a staged token file a failed write leaves behind
mateo-berri Sep 6, 2026
7ef4380
refactor: move anthropic jwks derivation behind a provider-owned tagg…
mateo-berri Sep 6, 2026
7143170
fix: unlink the staged token file when its write fails at close
mateo-berri Sep 6, 2026
528caa4
fix(anthropic): close the staging descriptor before writing the share…
mateo-berri Sep 6, 2026
558fca6
fix(wif): judge federation writes by what they set, not what is stored
mateo-berri Sep 6, 2026
9116ad7
chore: merge litellm_internal_staging into litellm_anthropic_wif_backend
mateo-berri Sep 6, 2026
7b5ec07
fix(proxy): let a deployment write name a federated credential
mateo-berri Sep 6, 2026
a7e3b4c
refactor(proxy): derive health display policy from the federation key…
mateo-berri Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions litellm/batches/batch_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from litellm.litellm_core_utils.llm_cost_calc.utils import parse_prompt_tokens_details
from litellm.types.llms.openai import Batch
from litellm.types.utils import CallTypes, ModelInfo, Usage
from litellm.types.workload_identity import ANTHROPIC_WIF_KWARGS_KEYS
from litellm.utils import token_counter


Expand Down Expand Up @@ -528,6 +529,9 @@ def _extract_file_access_credentials(litellm_params: dict | None) -> dict:
"max_retries",
"_litellm_internal_model_credentials",
*AWS_CREDENTIAL_KWARGS_KEYS,
# A federated deployment holds no api_key, so without these the fetch that reads a
# finished batch's output has nothing to authenticate with and its cost is never billed.
*sorted(ANTHROPIC_WIF_KWARGS_KEYS),
Comment thread
greptile-apps[bot] marked this conversation as resolved.
)
for key in credential_keys:
if key in litellm_params:
Expand Down
2 changes: 2 additions & 0 deletions litellm/batches/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,15 @@ def _handle_retrieve_batch_providers_without_provider_config(
)
api_key = optional_params.api_key or litellm.api_key or litellm.azure_key or get_secret_str("ANTHROPIC_API_KEY")

batch_params: Final = dict(litellm_params) # mutable-ok: handler contract, copied not shared
response = anthropic_batches_instance.retrieve_batch(
_is_async=_is_async,
batch_id=batch_id,
api_base=api_base,
api_key=api_key,
timeout=timeout,
max_retries=optional_params.max_retries,
litellm_params=batch_params,
)
else:
raise litellm.exceptions.BadRequestError(
Expand Down
7 changes: 6 additions & 1 deletion litellm/litellm_core_utils/get_litellm_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from typing import Final

from litellm.llms.openai.data_residency import infer_openai_data_residency
from litellm.types.workload_identity import ANTHROPIC_WIF_KWARGS_KEYS, OPENAI_WIF_KWARGS_KEYS

AWS_CREDENTIAL_KWARGS_KEYS: Final = frozenset(
{
Expand All @@ -27,7 +28,9 @@
# Keys `completion()` forwards from its own kwargs into `get_litellm_params`,
# which are otherwise invisible to it because that call site passes explicit
# named arguments rather than `**kwargs`.
FORWARDED_KWARGS_KEYS: Final = AWS_CREDENTIAL_KWARGS_KEYS | frozenset({RUST_KWARG_KEY})
FORWARDED_KWARGS_KEYS: Final = (
AWS_CREDENTIAL_KWARGS_KEYS | ANTHROPIC_WIF_KWARGS_KEYS | OPENAI_WIF_KWARGS_KEYS | frozenset({RUST_KWARG_KEY})
)

# Pre-define optional kwargs keys as frozenset for O(1) lookups
# These are extracted from kwargs only if present, avoiding unnecessary .get() calls
Expand Down Expand Up @@ -65,6 +68,8 @@
}
)
| AWS_CREDENTIAL_KWARGS_KEYS
| ANTHROPIC_WIF_KWARGS_KEYS
| OPENAI_WIF_KWARGS_KEYS
)

# Backward-compatible alias for existing imports/tests.
Expand Down
18 changes: 11 additions & 7 deletions litellm/llms/anthropic/batches/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ async def aretrieve_batch(
timeout: float | httpx.Timeout,
max_retries: int | None,
logging_obj: LiteLLMLoggingObj | None = None,
litellm_params: dict | None = None, # mutable-ok: handed straight to validate_environment
) -> LiteLLMBatch:
"""
Async: Retrieve a batch from Anthropic.
Expand All @@ -60,9 +61,7 @@ async def aretrieve_batch(
# Resolve API credentials
api_base = api_base or self.anthropic_model_info.get_api_base(api_base)
api_key = api_key or self.anthropic_model_info.get_api_key()

if not api_key:
raise ValueError("Missing Anthropic API Key")
resolved_litellm_params: Final = litellm_params if litellm_params is not None else {}

# Create a minimal logging object if not provided
if logging_obj is None:
Expand All @@ -85,16 +84,18 @@ async def aretrieve_batch(
api_base=api_base,
batch_id=batch_id,
optional_params={},
litellm_params={},
litellm_params=resolved_litellm_params,
)

# Validate environment and get headers
headers: Final = self.provider_config.validate_environment(
# Validate environment and get headers. Offloaded to a worker thread: a WIF token
# exchange here would otherwise block the event loop.
headers: Final = await asyncio.to_thread(
self.provider_config.validate_environment,
headers={},
model="",
messages=[],
optional_params={},
litellm_params={},
litellm_params=resolved_litellm_params,
api_key=api_key,
api_base=api_base,
)
Expand Down Expand Up @@ -130,6 +131,7 @@ def retrieve_batch(
timeout: float | httpx.Timeout,
max_retries: int | None,
logging_obj: LiteLLMLoggingObj | None = None,
litellm_params: dict | None = None, # mutable-ok: handed straight to validate_environment
) -> LiteLLMBatch | Coroutine[Any, Any, LiteLLMBatch]:
"""
Retrieve a batch from Anthropic.
Expand All @@ -154,6 +156,7 @@ def retrieve_batch(
timeout=timeout,
max_retries=max_retries,
logging_obj=logging_obj,
litellm_params=litellm_params,
)
else:
return asyncio.run(
Expand All @@ -164,5 +167,6 @@ def retrieve_batch(
timeout=timeout,
max_retries=max_retries,
logging_obj=logging_obj,
litellm_params=litellm_params,
)
)
28 changes: 18 additions & 10 deletions litellm/llms/anthropic/batches/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from litellm.types.llms.openai import AllMessageValues, CreateBatchRequest
from litellm.types.utils import LiteLLMBatch, LlmProviders, ModelResponse

from ..common_utils import merge_anthropic_beta_headers, without_caller_credential_headers

if TYPE_CHECKING:
import tiktoken

Expand Down Expand Up @@ -70,24 +72,30 @@ def validate_environment(
api_base: str | None = None,
) -> dict:
"""Validate and prepare environment-specific headers and parameters."""
if api_base is None and isinstance(litellm_params, dict):
api_base = litellm_params.get("api_base")
auth_header: Final = self.anthropic_model_info.get_auth_header(api_key, api_base)
params_mapping: Final = litellm_params if isinstance(litellm_params, dict) else None
if api_base is None and params_mapping is not None:
api_base = params_mapping.get("api_base")
auth_header: Final = self.anthropic_model_info.get_auth_header(
api_key, api_base, litellm_params=params_mapping, allow_workload_identity=True
)
if auth_header is None:
raise ValueError(
"Missing Anthropic API Key - A call is being made to anthropic but no key is set either in the environment variables or via params"
)
_headers: Final = {
merged_beta: Final = merge_anthropic_beta_headers(
merge_anthropic_beta_headers(headers.get("anthropic-beta"), auth_header.get("anthropic-beta")),
"message-batches-2024-09-24",
)
# The deployment's own credential is applied below, so a caller-supplied one must not
# ride along: without this a minted federation Bearer travels beside the caller's x-api-key.
return {
**without_caller_credential_headers(headers),
"accept": "application/json",
"anthropic-version": "2023-06-01",
"content-type": "application/json",
**auth_header,
"anthropic-beta": merged_beta,
}
_headers.update(auth_header)
# Add beta header for message batches
if "anthropic-beta" not in headers:
headers["anthropic-beta"] = "message-batches-2024-09-24"
headers.update(_headers)
return headers

def get_complete_batch_url(
self,
Expand Down
4 changes: 3 additions & 1 deletion litellm/llms/anthropic/chat/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import time
from collections.abc import Callable, Mapping, Sequence
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Final, NoReturn, cast
from typing import TYPE_CHECKING, Any, ClassVar, Final, NoReturn, cast

import httpx
from pydantic import ValidationError
Expand Down Expand Up @@ -284,6 +284,8 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
to pass metadata to anthropic, it's {"user_id": "any-relevant-information"}
"""

_workload_identity_eligible: ClassVar[bool] = True

max_tokens: int | None = None
stop_sequences: list | None = None
temperature: int | None = None
Expand Down
Loading
Loading