mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
Fix credential name tag injection by moving to Router level
The previous approach tried to inject litellm_credential_name as a tag in get_standard_logging_object_payload, but the credential name was never available in litellm_params because the Logging object is created by the proxy BEFORE the Router selects a deployment. The credential name only exists in the deployment's litellm_params, which is resolved later. This fix injects the credential name as a tag in Router._update_kwargs_with_deployment(), right alongside the existing deployment-level tags mechanism. This ensures the credential name flows through the normal metadata.tags pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8c5d48348c
commit
e08989dd8f
6 changed files with 87 additions and 214 deletions
|
|
@ -32,7 +32,6 @@ _OPTIONAL_KWARGS_KEYS = frozenset({
|
|||
"aws_bedrock_runtime_endpoint",
|
||||
"tpm",
|
||||
"rpm",
|
||||
"litellm_credential_name",
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5076,11 +5076,6 @@ def get_standard_logging_object_payload(
|
|||
litellm_params=litellm_params, proxy_server_request=proxy_server_request
|
||||
)
|
||||
|
||||
# Inject credential name as tag for spend tracking
|
||||
credential_name = litellm_params.get("litellm_credential_name")
|
||||
if credential_name and credential_name not in request_tags:
|
||||
request_tags.append(credential_name)
|
||||
|
||||
# cleanup timestamps
|
||||
(
|
||||
start_time_float,
|
||||
|
|
|
|||
|
|
@ -2019,6 +2019,16 @@ class Router:
|
|||
merged_tags.append(tag)
|
||||
kwargs[metadata_variable_name]["tags"] = merged_tags
|
||||
|
||||
## CREDENTIAL NAME AS TAG
|
||||
credential_name = deployment.get("litellm_params", {}).get(
|
||||
"litellm_credential_name"
|
||||
)
|
||||
if credential_name:
|
||||
existing_tags = kwargs[metadata_variable_name].get("tags") or []
|
||||
if credential_name not in existing_tags:
|
||||
existing_tags.append(credential_name)
|
||||
kwargs[metadata_variable_name]["tags"] = existing_tags
|
||||
|
||||
kwargs["model_info"] = model_info
|
||||
|
||||
kwargs["timeout"] = self._get_timeout(
|
||||
|
|
|
|||
|
|
@ -126,12 +126,3 @@ class TestGetLitellmParamsExplicitFields:
|
|||
result = get_litellm_params(no_log=True)
|
||||
assert result["no-log"] is True
|
||||
|
||||
def test_litellm_credential_name_captured(self):
|
||||
"""litellm_credential_name should be captured via _OPTIONAL_KWARGS_KEYS."""
|
||||
result = get_litellm_params(litellm_credential_name="my-credential")
|
||||
assert result["litellm_credential_name"] == "my-credential"
|
||||
|
||||
def test_litellm_credential_name_absent(self):
|
||||
"""When litellm_credential_name is not passed, it should not appear."""
|
||||
result = get_litellm_params()
|
||||
assert "litellm_credential_name" not in result
|
||||
|
|
|
|||
|
|
@ -1357,202 +1357,3 @@ def test_get_error_information_error_code_priority():
|
|||
assert result["error_class"] == "NoCodeException"
|
||||
|
||||
|
||||
def test_credential_name_injected_as_tag():
|
||||
"""
|
||||
Test that litellm_credential_name from litellm_params is injected into
|
||||
request_tags in the standard logging payload.
|
||||
|
||||
In the real flow, litellm_credential_name is captured into litellm_params
|
||||
by get_litellm_params() via _OPTIONAL_KWARGS_KEYS.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
get_standard_logging_object_payload,
|
||||
Logging,
|
||||
)
|
||||
from datetime import datetime
|
||||
|
||||
logging_obj = Logging(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=False,
|
||||
call_type="completion",
|
||||
start_time=datetime.now(),
|
||||
litellm_call_id="test-cred-tag",
|
||||
function_id="test-function",
|
||||
)
|
||||
|
||||
mock_response = {
|
||||
"id": "chatcmpl-123",
|
||||
"object": "chat.completion",
|
||||
"model": "gpt-4o",
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 30,
|
||||
},
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "Hi!"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-4o",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"response_cost": 0.001,
|
||||
"custom_llm_provider": "openai",
|
||||
"litellm_params": {
|
||||
"litellm_credential_name": "my-openai-credential",
|
||||
},
|
||||
}
|
||||
|
||||
start_time = datetime.now()
|
||||
end_time = datetime.now()
|
||||
|
||||
payload = get_standard_logging_object_payload(
|
||||
kwargs=kwargs,
|
||||
init_response_obj=mock_response,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
logging_obj=logging_obj,
|
||||
status="success",
|
||||
)
|
||||
|
||||
assert payload is not None
|
||||
assert "my-openai-credential" in payload["request_tags"]
|
||||
|
||||
|
||||
def test_credential_name_not_injected_when_absent():
|
||||
"""
|
||||
Test that when litellm_credential_name is not in litellm_params,
|
||||
request_tags are unchanged.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
get_standard_logging_object_payload,
|
||||
Logging,
|
||||
)
|
||||
from datetime import datetime
|
||||
|
||||
logging_obj = Logging(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=False,
|
||||
call_type="completion",
|
||||
start_time=datetime.now(),
|
||||
litellm_call_id="test-no-cred",
|
||||
function_id="test-function",
|
||||
)
|
||||
|
||||
mock_response = {
|
||||
"id": "chatcmpl-456",
|
||||
"object": "chat.completion",
|
||||
"model": "gpt-4o",
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 30,
|
||||
},
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "Hi!"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-4o",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"response_cost": 0.001,
|
||||
"custom_llm_provider": "openai",
|
||||
}
|
||||
|
||||
start_time = datetime.now()
|
||||
end_time = datetime.now()
|
||||
|
||||
payload = get_standard_logging_object_payload(
|
||||
kwargs=kwargs,
|
||||
init_response_obj=mock_response,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
logging_obj=logging_obj,
|
||||
status="success",
|
||||
)
|
||||
|
||||
assert payload is not None
|
||||
# No credential-related tags should be present
|
||||
for tag in payload["request_tags"]:
|
||||
assert tag.startswith("User-Agent:")
|
||||
|
||||
|
||||
def test_credential_name_not_duplicated_in_tags():
|
||||
"""
|
||||
Test that if the credential name already exists in the tags list,
|
||||
it is not duplicated.
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
get_standard_logging_object_payload,
|
||||
Logging,
|
||||
)
|
||||
from datetime import datetime
|
||||
|
||||
logging_obj = Logging(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello"}],
|
||||
stream=False,
|
||||
call_type="completion",
|
||||
start_time=datetime.now(),
|
||||
litellm_call_id="test-dup-cred",
|
||||
function_id="test-function",
|
||||
)
|
||||
|
||||
mock_response = {
|
||||
"id": "chatcmpl-789",
|
||||
"object": "chat.completion",
|
||||
"model": "gpt-4o",
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 20,
|
||||
"total_tokens": 30,
|
||||
},
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"message": {"role": "assistant", "content": "Hi!"},
|
||||
"finish_reason": "stop",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
kwargs = {
|
||||
"model": "gpt-4o",
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"response_cost": 0.001,
|
||||
"custom_llm_provider": "openai",
|
||||
"litellm_params": {
|
||||
"litellm_credential_name": "my-openai-credential",
|
||||
"metadata": {"tags": ["my-openai-credential", "other-tag"]},
|
||||
},
|
||||
}
|
||||
|
||||
start_time = datetime.now()
|
||||
end_time = datetime.now()
|
||||
|
||||
payload = get_standard_logging_object_payload(
|
||||
kwargs=kwargs,
|
||||
init_response_obj=mock_response,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
logging_obj=logging_obj,
|
||||
status="success",
|
||||
)
|
||||
|
||||
assert payload is not None
|
||||
credential_count = payload["request_tags"].count("my-openai-credential")
|
||||
assert credential_count == 1, (
|
||||
f"Expected credential name once, found {credential_count} times"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2186,3 +2186,80 @@ def test_update_kwargs_with_deployment_merge_tools_request_overrides_tool_choice
|
|||
|
||||
# Request tool_choice should be preserved (merged tools still applied)
|
||||
assert kwargs["tool_choice"] == "none"
|
||||
|
||||
|
||||
def test_credential_name_injected_as_tag():
|
||||
"""
|
||||
Test that litellm_credential_name from deployment litellm_params
|
||||
is injected as a tag into metadata during _update_kwargs_with_deployment.
|
||||
"""
|
||||
router = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "xai-model",
|
||||
"litellm_params": {
|
||||
"model": "xai/grok-4-1-fast",
|
||||
"litellm_credential_name": "xAI",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
kwargs: dict = {"metadata": {"tags": ["A.101"]}}
|
||||
deployment = router.get_deployment_by_model_group_name(
|
||||
model_group_name="xai-model"
|
||||
)
|
||||
router._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs)
|
||||
|
||||
assert "xAI" in kwargs["metadata"]["tags"]
|
||||
assert "A.101" in kwargs["metadata"]["tags"]
|
||||
|
||||
|
||||
def test_credential_name_not_duplicated_in_tags():
|
||||
"""
|
||||
Test that if the credential name already exists in the tags list,
|
||||
it is not duplicated.
|
||||
"""
|
||||
router = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "xai-model",
|
||||
"litellm_params": {
|
||||
"model": "xai/grok-4-1-fast",
|
||||
"litellm_credential_name": "xAI",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
kwargs: dict = {"metadata": {"tags": ["xAI", "A.101"]}}
|
||||
deployment = router.get_deployment_by_model_group_name(
|
||||
model_group_name="xai-model"
|
||||
)
|
||||
router._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs)
|
||||
|
||||
assert kwargs["metadata"]["tags"].count("xAI") == 1
|
||||
|
||||
|
||||
def test_credential_name_not_injected_when_absent():
|
||||
"""
|
||||
Test that when no litellm_credential_name is set, tags are unchanged.
|
||||
"""
|
||||
router = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "gpt-model",
|
||||
"litellm_params": {
|
||||
"model": "gpt-4o",
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
kwargs: dict = {"metadata": {"tags": ["A.101"]}}
|
||||
deployment = router.get_deployment_by_model_group_name(
|
||||
model_group_name="gpt-model"
|
||||
)
|
||||
router._update_kwargs_with_deployment(deployment=deployment, kwargs=kwargs)
|
||||
|
||||
assert kwargs["metadata"]["tags"] == ["A.101"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue