Skip to content

Commit aede742

Browse files
Merge pull request #14818 from akshoop/akshoop/fastuuid-dep-make-optional
Fix: make `fastuuid` an optional dependency for `proxy`, graceful fallback to stdlib `uuid`
2 parents d59dec3 + ab2bd2a commit aede742

File tree

11 files changed

+137
-12
lines changed

11 files changed

+137
-12
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,7 @@ jobs:
15211521
- run: python ./tests/code_coverage_tests/prevent_key_leaks_in_exceptions.py
15221522
- run: python ./tests/code_coverage_tests/check_unsafe_enterprise_import.py
15231523
- run: python ./tests/code_coverage_tests/ban_copy_deepcopy_kwargs.py
1524+
- run: python ./tests/code_coverage_tests/check_fastuuid_usage.py
15241525
- run: helm lint ./deploy/charts/litellm-helm
15251526

15261527
db_migration_disable_update_check:

litellm/_uuid.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
Internal unified UUID helper.
3+
4+
Tries to use fastuuid (performance) and falls back to stdlib uuid if unavailable.
5+
"""
6+
7+
FASTUUID_AVAILABLE = False
8+
9+
try:
10+
import fastuuid as _uuid # type: ignore
11+
12+
FASTUUID_AVAILABLE = True
13+
except Exception: # pragma: no cover - fallback path
14+
import uuid as _uuid # type: ignore
15+
16+
17+
# Expose a module-like alias so callers can use: uuid.uuid4()
18+
uuid = _uuid
19+
20+
21+
def uuid4():
22+
"""Return a UUID4 using the selected backend."""
23+
return uuid.uuid4()

litellm/litellm_core_utils/litellm_logging.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
cast,
2727
)
2828

29-
import fastuuid as uuid
3029
from httpx import Response
3130
from pydantic import BaseModel
3231

@@ -38,6 +37,7 @@
3837
turn_off_message_logging,
3938
)
4039
from litellm._logging import _is_debugging_on, verbose_logger
40+
from litellm._uuid import uuid
4141
from litellm.batches.batch_utils import _handle_completed_batch
4242
from litellm.caching.caching import DualCache, InMemoryCache
4343
from litellm.caching.caching_handler import LLMCachingHandler

litellm/llms/bedrock/common_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ def generate_unique_job_name(self, model: str, prefix: str = "litellm") -> str:
774774
Returns:
775775
Unique job name (≤ 63 characters for Bedrock compatibility)
776776
"""
777-
import fastuuid as uuid
777+
from litellm._uuid import uuid
778778
unique_id = str(uuid.uuid4())[:8]
779779
# Format: {prefix}-batch-{model}-{uuid}
780780
# Example: litellm-batch-claude-266c398e

litellm/proxy/common_request_processing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
Union,
1515
)
1616

17-
import fastuuid as uuid
1817
import httpx
1918
import orjson
2019
from fastapi import HTTPException, Request, status
2120
from fastapi.responses import Response, StreamingResponse
2221

2322
import litellm
2423
from litellm._logging import verbose_proxy_logger
24+
from litellm._uuid import uuid
2525
from litellm.constants import (
2626
DD_TRACER_STREAMING_CHUNK_YIELD_RESOURCE,
2727
STREAM_SSE_DATA_PREFIX,

litellm/types/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
Union,
1414
)
1515

16-
import fastuuid as uuid
1716
from aiohttp import FormData
1817
from openai._models import BaseModel as OpenAIObject
1918
from openai.types.audio.transcription_create_params import FileTypes # type: ignore
@@ -33,6 +32,7 @@
3332
from typing_extensions import Callable, Dict, Required, TypedDict, override
3433

3534
import litellm
35+
from litellm._uuid import uuid
3636
from litellm.types.llms.base import (
3737
BaseLiteLLMOpenAIResponseObject,
3838
LiteLLMPydanticObjectBase,

litellm/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040

4141
import aiohttp
4242
import dotenv
43-
import fastuuid as uuid
4443
import httpx
4544
import openai
4645
import tiktoken
@@ -59,6 +58,7 @@
5958
import litellm.litellm_core_utils.json_validation_rule
6059
import litellm.llms
6160
import litellm.llms.gemini
61+
from litellm._uuid import uuid
6262
from litellm.caching._internal_lru_cache import lru_cache_wrapper
6363
from litellm.caching.caching import DualCache
6464
from litellm.caching.caching_handler import CachingHandlerResponse, LLMCachingHandler

poetry.lock

Lines changed: 7 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ Documentation = "https://docs.litellm.ai"
2020

2121
[tool.poetry.dependencies]
2222
python = ">=3.8.1,<4.0, !=3.9.7"
23-
fastuuid = ">=0.12.0"
2423
httpx = ">=0.23.0"
2524
openai = ">=1.99.5"
2625
python-dotenv = ">=0.2.0"
@@ -34,6 +33,7 @@ pydantic = "^2.5.0"
3433
jsonschema = "^4.22.0"
3534
pondpond = "^1.4.1"
3635
numpydoc = {version = "*", optional = true} # used in utils.py
36+
fastuuid = {version = ">=0.12.0", optional = true}
3737

3838
uvicorn = {version = "^0.29.0", optional = true}
3939
uvloop = {version = "^0.21.0", optional = true, markers="sys_platform != 'win32'"}
@@ -93,6 +93,7 @@ proxy = [
9393
"litellm-enterprise",
9494
"rich",
9595
"polars",
96+
"fastuuid",
9697
]
9798

9899
extra_proxy = [
@@ -115,6 +116,7 @@ semantic-router = ["semantic-router"]
115116

116117
mlflow = ["mlflow"]
117118

119+
118120
[tool.isort]
119121
profile = "black"
120122

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import ast
2+
import os
3+
from typing import List, Dict, Any
4+
5+
6+
ALLOWED_FILE = os.path.normpath("litellm/_uuid.py")
7+
8+
9+
def _to_module_path(relative_path: str) -> str:
10+
module = os.path.splitext(relative_path)[0].replace(os.sep, ".")
11+
if module.endswith(".__init__"):
12+
return module[: -len(".__init__")]
13+
return module
14+
15+
16+
def _find_fastuuid_imports_in_file(
17+
file_path: str, base_dir: str
18+
) -> List[Dict[str, Any]]:
19+
results: List[Dict[str, Any]] = []
20+
try:
21+
with open(file_path, "r", encoding="utf-8") as f:
22+
source = f.read()
23+
tree = ast.parse(source, filename=file_path)
24+
except Exception:
25+
return results
26+
27+
relative = os.path.normpath(os.path.relpath(file_path, base_dir))
28+
if relative == ALLOWED_FILE:
29+
return results
30+
31+
module = _to_module_path(relative)
32+
for node in ast.walk(tree):
33+
if isinstance(node, ast.Import):
34+
for alias in node.names:
35+
if alias.name == "fastuuid":
36+
results.append(
37+
{
38+
"file": relative,
39+
"line": getattr(node, "lineno", 0),
40+
"import": f"import {alias.name}",
41+
"module": module,
42+
}
43+
)
44+
elif isinstance(node, ast.ImportFrom) and node.module == "fastuuid":
45+
names = ", ".join([a.name for a in node.names])
46+
results.append(
47+
{
48+
"file": relative,
49+
"line": getattr(node, "lineno", 0),
50+
"import": f"from fastuuid import {names}",
51+
"module": module,
52+
}
53+
)
54+
55+
return results
56+
57+
58+
def scan_directory_for_fastuuid(base_dir: str) -> List[Dict[str, Any]]:
59+
violations: List[Dict[str, Any]] = []
60+
scan_root = os.path.join(base_dir, "litellm")
61+
for root, _, files in os.walk(scan_root):
62+
for filename in files:
63+
if filename.endswith(".py"):
64+
file_path = os.path.join(root, filename)
65+
violations.extend(_find_fastuuid_imports_in_file(file_path, base_dir))
66+
return violations
67+
68+
69+
def main() -> None:
70+
base_dir = "." # tests run from repo root in CI
71+
violations = scan_directory_for_fastuuid(base_dir)
72+
if violations:
73+
print(
74+
"\n🚨 fastuuid must only be imported inside litellm/_uuid.py. Found violations:"
75+
)
76+
for v in violations:
77+
print(f"* {v['module']} ({v['file']}:{v['line']}) -> {v['import']}")
78+
print("\n")
79+
raise Exception(
80+
"Found fastuuid imports outside litellm/_uuid.py. Use litellm._uuid.uuid or litellm._uuid.uuid4 instead."
81+
)
82+
else:
83+
print("✅ No invalid fastuuid imports found.")
84+
85+
86+
if __name__ == "__main__":
87+
main()

0 commit comments

Comments
 (0)