Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1fa4206
fix(core_helpers.py): return None, instead of raising kwargs is None …
Oct 29, 2024
d866b12
docs(cost_tracking.md): cleanup doc
Oct 29, 2024
f147e33
fix(vertex_and_google_ai_studio.py): handle function call with no par…
Oct 29, 2024
dd309f8
test(test_router_timeout.py): add test for router timeout + retry logic
Oct 29, 2024
ba49202
test: update test to use module level values
Oct 29, 2024
149dd18
(fix) Prometheus - Log Postgres DB latency, status on prometheus (#6…
ishaan-jaff Oct 29, 2024
0dc957c
docs clarify vertex vs gemini
ishaan-jaff Oct 29, 2024
37f5feb
(router_strategy/) ensure all async functions use async cache methods…
ishaan-jaff Oct 29, 2024
947af8c
(fix) proxy - fix when `STORE_MODEL_IN_DB` should be set (#6492)
ishaan-jaff Oct 29, 2024
17695e1
(fix) `PrometheusServicesLogger` `_get_metric` should return metric i…
ishaan-jaff Oct 29, 2024
fe811a5
bump: version 1.51.0 → 1.51.1
ishaan-jaff Oct 29, 2024
74d7216
Add `azure/gpt-4o-mini-2024-07-18` to model_prices_and_context_window…
xingyaoww Oct 29, 2024
3b385fb
Update utils.py (#6468)
vibhanshu-ob Oct 29, 2024
6b991e7
(perf) Litellm redis router fix - ~100ms improvement (#6483)
Oct 29, 2024
9d25552
refactor(prometheus.py): move to using standard logging payload for r…
Oct 30, 2024
50ff8c3
fix: fix linting error
Oct 30, 2024
c72ea56
fix(redis_cache.py): make sure ttl is always int (handle float values)
Oct 30, 2024
3cec40a
fix: fix linting error
Oct 30, 2024
498d2d9
test: update test
Oct 30, 2024
669cda3
fix: fix linting error
Oct 30, 2024
5569838
Merge branch 'main' into litellm_dev_10_29_2024
Oct 30, 2024
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: 1 addition & 3 deletions docs/my-website/docs/proxy/cost_tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,7 @@ Output from script

:::info

Customer This is the value of `user_id` passed when calling [`/key/generate`](https://litellm-api.up.railway.app/#/key%20management/generate_key_fn_key_generate_post)

[this is `user` passed to `/chat/completions` request](#how-to-track-spend-with-litellm)
Customer [this is `user` passed to `/chat/completions` request](#how-to-track-spend-with-litellm)
- [LiteLLM API key](virtual_keys.md)


Expand Down
8 changes: 6 additions & 2 deletions litellm/caching/base_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ def __init__(self, default_ttl: int = 60):
self.default_ttl = default_ttl

def get_ttl(self, **kwargs) -> Optional[int]:
if kwargs.get("ttl") is not None:
return kwargs.get("ttl")
kwargs_ttl: Optional[int] = kwargs.get("ttl")
if kwargs_ttl is not None:
try:
return int(kwargs_ttl)
except ValueError:
return self.default_ttl
return self.default_ttl

def set_cache(self, key, value, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions litellm/caching/redis_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ async def async_set_cache(self, key, value, **kwargs):
print_verbose(
f"Set ASYNC Redis Cache: key: {key}\nValue {value}\nttl={ttl}"
)

try:
if not hasattr(redis_client, "set"):
raise Exception(
Expand Down
36 changes: 18 additions & 18 deletions litellm/integrations/prometheus.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,9 +849,13 @@ def set_llm_deployment_success_metrics(
):
try:
verbose_logger.debug("setting remaining tokens requests metric")
standard_logging_payload: StandardLoggingPayload = request_kwargs.get(
"standard_logging_object", {}
standard_logging_payload: Optional[StandardLoggingPayload] = (
request_kwargs.get("standard_logging_object")
)

if standard_logging_payload is None:
return

model_group = standard_logging_payload["model_group"]
api_base = standard_logging_payload["api_base"]
_response_headers = request_kwargs.get("response_headers")
Expand All @@ -862,22 +866,18 @@ def set_llm_deployment_success_metrics(
_model_info = _metadata.get("model_info") or {}
model_id = _model_info.get("id", None)

remaining_requests = None
remaining_tokens = None
# OpenAI / OpenAI Compatible headers
if (
_response_headers
and "x-ratelimit-remaining-requests" in _response_headers
):
remaining_requests = _response_headers["x-ratelimit-remaining-requests"]
if (
_response_headers
and "x-ratelimit-remaining-tokens" in _response_headers
):
remaining_tokens = _response_headers["x-ratelimit-remaining-tokens"]
verbose_logger.debug(
f"remaining requests: {remaining_requests}, remaining tokens: {remaining_tokens}"
)
remaining_requests: Optional[int] = None
remaining_tokens: Optional[int] = None
if additional_headers := standard_logging_payload["hidden_params"][
"additional_headers"
]:
# OpenAI / OpenAI Compatible headers
remaining_requests = additional_headers.get(
"x_ratelimit_remaining_requests", None
)
remaining_tokens = additional_headers.get(
"x_ratelimit_remaining_tokens", None
)

if remaining_requests:
"""
Expand Down
2 changes: 1 addition & 1 deletion litellm/litellm_core_utils/core_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def _get_parent_otel_span_from_kwargs(
) -> Union[Span, None]:
try:
if kwargs is None:
raise ValueError("kwargs is None")
return None
litellm_params = kwargs.get("litellm_params")
_metadata = kwargs.get("metadata") or {}
if "litellm_parent_otel_span" in _metadata:
Expand Down
67 changes: 52 additions & 15 deletions litellm/litellm_core_utils/litellm_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
ImageResponse,
ModelResponse,
StandardCallbackDynamicParams,
StandardLoggingAdditionalHeaders,
StandardLoggingHiddenParams,
StandardLoggingMetadata,
StandardLoggingModelCostFailureDebugInformation,
Expand Down Expand Up @@ -2640,6 +2641,52 @@ def get_final_response_obj(

return final_response_obj

@staticmethod
def get_additional_headers(
additiona_headers: Optional[dict],
) -> Optional[StandardLoggingAdditionalHeaders]:

if additiona_headers is None:
return None

additional_logging_headers: StandardLoggingAdditionalHeaders = {}

for key in StandardLoggingAdditionalHeaders.__annotations__.keys():
_key = key.lower()
_key = _key.replace("_", "-")
if _key in additiona_headers:
try:
additional_logging_headers[key] = int(additiona_headers[_key]) # type: ignore
except (ValueError, TypeError):
verbose_logger.debug(
f"Could not convert {additiona_headers[_key]} to int for key {key}."

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information

This expression logs [sensitive data (secret)](1) as clear text. This expression logs [sensitive data (secret)](2) as clear text. This expression logs [sensitive data (secret)](3) as clear text. This expression logs [sensitive data (secret)](4) as clear text. This expression logs [sensitive data (secret)](5) as clear text. This expression logs [sensitive data (secret)](6) as clear text. This expression logs [sensitive data (secret)](7) as clear text. This expression logs [sensitive data (secret)](8) as clear text. This expression logs [sensitive data (secret)](9) as clear text. This expression logs [sensitive data (secret)](10) as clear text. This expression logs [sensitive data (secret)](11) as clear text. This expression logs [sensitive data (secret)](12) as clear text. This expression logs [sensitive data (secret)](13) as clear text. This expression logs [sensitive data (secret)](14) as clear text. This expression logs [sensitive data (secret)](15) as clear text. This expression logs [sensitive data (secret)](16) as clear text. This expression logs [sensitive data (secret)](17) as clear text. This expression logs [sensitive data (secret)](18) as clear text. This expression logs [sensitive data (secret)](19) as clear text. This expression logs [sensitive data (secret)](20) as clear text. This expression logs [sensitive data (secret)](21) as clear text. This expression logs [sensitive data (secret)](22) as clear text. This expression logs [sensitive data (secret)](23) as clear text. This expression logs [sensitive data (secret)](24) as clear text. This expression logs [sensitive data (secret)](25) as clear text. This expression logs [sensitive data (secret)](26) as clear text. This expression logs [sensitive data (secret)](27) as clear text. This expression logs [sensitive data (secret)](28) as clear text. This expression logs [sensitive data (secret)](29) as clear text. This expression logs [sensitive data (secret)](30) as clear text. This expression logs [sensitive data (secret)](31) as clear text. This expression logs [sensitive data (secret)](32) as clear text. This expression logs [sensitive data (secret)](33) as clear text. This expression logs [sensitive data (secret)](34) as clear text. This expression logs [sensitive data (secret)](35) as clear text. This expression logs [sensitive data (secret)](36) as clear text. This expression logs [sensitive data (secret)](37) as clear text. This expression logs [sensitive data (secret)](38) as clear text. This expression logs [sensitive data (secret)](39) as clear text. This expression logs [sensitive data (secret)](40) as clear text. This expression logs [sensitive data (secret)](41) as clear text. This expression logs [sensitive data (secret)](42) as clear text. This expression logs [sensitive data (secret)](43) as clear text. This expression logs [sensitive data (secret)](44) as clear text. This expression logs [sensitive data (secret)](45) as clear text. This expression logs [sensitive data (secret)](46) as clear text. This expression logs [sensitive data (secret)](47) as clear text. This expression logs [sensitive data (secret)](48) as clear text. This expression logs [sensitive data (secret)](49) as clear text. This expression logs [sensitive data (secret)](50) as clear text. This expression logs [sensitive data (secret)](51) as clear text. This expression logs [sensitive data (secret)](52) as clear text. This expression logs [sensitive data (secret)](53) as clear text. This expression logs [sensitive data (secret)](54) as clear text. This expression logs [sensitive data (secret)](55) as clear text. This expression logs [sensitive data (secret)](56) as clear text. This expression logs [sensitive data (secret)](57) as clear text. This expression logs [sensitive data (secret)](58) as clear text. This expression logs [sensitive data (secret)](59) as clear text. This expression logs [sensitive data (secret)](60) as clear text. This expression logs [sensitive data (secret)](61) as clear text. This expression logs [sensitive data (secret)](62) as clear text. This expressi

Copilot Autofix

AI almost 2 years ago

To fix the problem, we should avoid logging the actual value of additiona_headers[_key] when a conversion error occurs. Instead, we can log a generic message indicating that a conversion error happened without including the sensitive data. This approach maintains the usefulness of the log message for debugging while protecting sensitive information.

  • Modify the log message on line 2662 to exclude the actual value of additiona_headers[_key].
  • Ensure that the log message still provides enough context to understand that a conversion error occurred.
Suggested changeset 1
litellm/litellm_core_utils/litellm_logging.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py
--- a/litellm/litellm_core_utils/litellm_logging.py
+++ b/litellm/litellm_core_utils/litellm_logging.py
@@ -2661,3 +2661,3 @@
                     verbose_logger.debug(
-                        f"Could not convert {additiona_headers[_key]} to int for key {key}."
+                        f"Could not convert value for key {key} to int."
                     )
EOF
@@ -2661,3 +2661,3 @@
verbose_logger.debug(
f"Could not convert {additiona_headers[_key]} to int for key {key}."
f"Could not convert value for key {key} to int."
)
Copilot is powered by AI and may make mistakes. Always verify output.
)
return additional_logging_headers

@staticmethod
def get_hidden_params(
hidden_params: Optional[dict],
) -> StandardLoggingHiddenParams:
clean_hidden_params = StandardLoggingHiddenParams(
model_id=None,
cache_key=None,
api_base=None,
response_cost=None,
additional_headers=None,
)
if hidden_params is not None:
for key in StandardLoggingHiddenParams.__annotations__.keys():
if key in hidden_params:
if key == "additional_headers":
clean_hidden_params["additional_headers"] = (
StandardLoggingPayloadSetup.get_additional_headers(
hidden_params[key]
)
)
else:
clean_hidden_params[key] = hidden_params[key] # type: ignore
return clean_hidden_params


def get_standard_logging_object_payload(
kwargs: Optional[dict],
Expand Down Expand Up @@ -2671,7 +2718,9 @@ def get_standard_logging_object_payload(
if response_headers is not None:
hidden_params = dict(
StandardLoggingHiddenParams(
additional_headers=dict(response_headers),
additional_headers=StandardLoggingPayloadSetup.get_additional_headers(
dict(response_headers)
),
model_id=None,
cache_key=None,
api_base=None,
Expand Down Expand Up @@ -2712,21 +2761,9 @@ def get_standard_logging_object_payload(
)
)
# clean up litellm hidden params
clean_hidden_params = StandardLoggingHiddenParams(
model_id=None,
cache_key=None,
api_base=None,
response_cost=None,
additional_headers=None,
clean_hidden_params = StandardLoggingPayloadSetup.get_hidden_params(
hidden_params
)
if hidden_params is not None:
clean_hidden_params = StandardLoggingHiddenParams(
**{ # type: ignore
key: hidden_params[key]
for key in StandardLoggingHiddenParams.__annotations__.keys()
if key in hidden_params
}
)
# clean up litellm metadata
clean_metadata = StandardLoggingPayloadSetup.get_standard_logging_metadata(
metadata=metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,13 @@ def _map_function(self, value: List[dict]) -> List[Tools]:
elif openai_function_object is not None:
gtool_func_declaration = FunctionDeclaration(
name=openai_function_object["name"],
description=openai_function_object.get("description", ""),
parameters=openai_function_object.get("parameters", {}),
)
_description = openai_function_object.get("description", None)
_parameters = openai_function_object.get("parameters", None)
if _description is not None:
gtool_func_declaration["description"] = _description
if _parameters is not None:
gtool_func_declaration["parameters"] = _parameters
gtool_func_declarations.append(gtool_func_declaration)
else:
# assume it's a provider-specific param
Expand Down
2 changes: 1 addition & 1 deletion litellm/proxy/_new_secret_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ model_list:

litellm_settings:
fallbacks: [{ "claude-3-5-sonnet-20240620": ["claude-3-5-sonnet-aihubmix"] }]
callbacks: ["otel"]
callbacks: ["otel", "prometheus"]

router_settings:
routing_strategy: latency-based-routing
Expand Down
1 change: 1 addition & 0 deletions litellm/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5255,6 +5255,7 @@ async def async_get_available_deployment(
parent_otel_span=parent_otel_span,
)
raise exception

verbose_router_logger.info(
f"get_available_deployment for model: {model}, Selected deployment: {self.print_deployment(deployment)} for model: {model}"
)
Expand Down
2 changes: 2 additions & 0 deletions litellm/router_utils/handle_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ async def send_llm_exception_alert(
)



async def async_raise_no_deployment_exception(
litellm_router_instance: LitellmRouter, model: str, parent_otel_span: Optional[Span]
):
Expand All @@ -73,6 +74,7 @@ async def async_raise_no_deployment_exception(
verbose_router_logger.info(
f"get_available_deployment for model: {model}, No deployment available"
)

model_ids = litellm_router_instance.get_model_ids(model_name=model)
_cooldown_time = litellm_router_instance.cooldown_cache.get_min_cooldown(
model_ids=model_ids, parent_otel_span=parent_otel_span
Expand Down
9 changes: 8 additions & 1 deletion litellm/types/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1433,12 +1433,19 @@ class StandardLoggingMetadata(StandardLoggingUserAPIKeyMetadata):
requester_metadata: Optional[dict]


class StandardLoggingAdditionalHeaders(TypedDict, total=False):
x_ratelimit_limit_requests: int
x_ratelimit_limit_tokens: int
x_ratelimit_remaining_requests: int
x_ratelimit_remaining_tokens: int


class StandardLoggingHiddenParams(TypedDict):
model_id: Optional[str]
cache_key: Optional[str]
api_base: Optional[str]
response_cost: Optional[str]
additional_headers: Optional[dict]
additional_headers: Optional[StandardLoggingAdditionalHeaders]


class StandardLoggingModelInformation(TypedDict):
Expand Down
1 change: 1 addition & 0 deletions tests/llm_translation/test_optional_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ def test_unmapped_vertex_anthropic_model():
assert "max_retries" not in optional_params



@pytest.mark.parametrize("provider", ["anthropic", "vertex_ai"])
def test_anthropic_parallel_tool_calls(provider):
optional_params = get_optional_params(
Expand Down
63 changes: 62 additions & 1 deletion tests/llm_translation/test_vertex.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
sys.path.insert(
0, os.path.abspath("../..")
) # Adds the parent directory to the system path

import pytest
import litellm
from litellm import get_optional_params


def test_completion_pydantic_obj_2():
Expand Down Expand Up @@ -117,3 +118,63 @@ def test_build_vertex_schema():
assert new_schema["type"] == schema["type"]
assert new_schema["properties"] == schema["properties"]
assert "required" in new_schema and new_schema["required"] == schema["required"]


@pytest.mark.parametrize(
"tools, key",
[
([{"googleSearchRetrieval": {}}], "googleSearchRetrieval"),
([{"code_execution": {}}], "code_execution"),
],
)
def test_vertex_tool_params(tools, key):

optional_params = get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
tools=tools,
)
print(optional_params)
assert optional_params["tools"][0][key] == {}


@pytest.mark.parametrize(
"tool, expect_parameters",
[
(
{
"name": "test_function",
"description": "test_function_description",
"parameters": {
"type": "object",
"properties": {"test_param": {"type": "string"}},
},
},
True,
),
(
{
"name": "test_function",
},
False,
),
],
)
def test_vertex_function_translation(tool, expect_parameters):
"""
If param not set, don't set it in the request
"""

tools = [tool]
optional_params = get_optional_params(
model="gemini-1.5-pro",
custom_llm_provider="vertex_ai",
tools=tools,
)
print(optional_params)
if expect_parameters:
assert "parameters" in optional_params["tools"][0]["function_declarations"][0]
else:
assert (
"parameters" not in optional_params["tools"][0]["function_declarations"][0]
)
4 changes: 2 additions & 2 deletions tests/local_testing/test_caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ async def test_embedding_caching_redis_ttl():
type="redis",
host="dummy_host",
password="dummy_password",
default_in_redis_ttl=2.5,
default_in_redis_ttl=2,
)

inputs = [
Expand All @@ -635,7 +635,7 @@ async def test_embedding_caching_redis_ttl():
print(f"redis pipeline set args: {args}")
print(f"redis pipeline set kwargs: {kwargs}")
assert kwargs.get("ex") == datetime.timedelta(
seconds=2.5
seconds=2
) # Check if TTL is set to 2.5 seconds


Expand Down
31 changes: 31 additions & 0 deletions tests/local_testing/test_function_calling.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,34 @@ def test_passing_tool_result_as_list():
print(resp)

assert resp.usage.prompt_tokens_details.cached_tokens > 0


def test_function_calling_with_gemini():
litellm.set_verbose = True
resp = litellm.completion(
model="gemini/gemini-1.5-pro-002",
messages=[
{
"content": [
{
"type": "text",
"text": "You are a helpful assistant that can interact with a computer to solve tasks.\n<IMPORTANT>\n* If user provides a path, you should NOT assume it's relative to the current working directory. Instead, you should explore the file system to find the file before working on it.\n</IMPORTANT>\n",
}
],
"role": "system",
},
{
"content": [{"type": "text", "text": "Hey, how's it going?"}],
"role": "user",
},
],
tools=[
{
"type": "function",
"function": {
"name": "finish",
"description": "Finish the interaction when the task is complete OR if the assistant cannot proceed further with the task.",
},
},
],
)
Loading