chore: add vulture dead-code detection and remove unreachable code

Add vulture as the Python analogue of the frontend's knip: a make target
(lint-deadcode), [tool.vulture] config, and a committed allowlist baseline
(.vulture_allowlist.py) that ratchets like the existing ruff/basedpyright
budgets. lint-deadcode-update regenerates the baseline.

Remove the unreachable statements vulture surfaced at 100% confidence:
duplicate returns, dead branches after unconditional returns, a redundant
trailing pass, and a dead post-call logging block in the sagemaker handler.
All are provably unreachable, so behavior is unchanged.
This commit is contained in:
mateo-berri 2026-06-24 15:28:44 +00:00
parent 8bca05d311
commit d2e5d604e1
No known key found for this signature in database
13 changed files with 262 additions and 21 deletions

182
.vulture_allowlist.py Normal file
View file

@ -0,0 +1,182 @@
# Vulture allowlist: baseline of accepted dead-code findings.
#
# This is the Python analogue of the frontend's knip setup. `make lint-deadcode`
# runs vulture to surface unreachable code, dead branches, and unused symbols.
# Entries below are referenced so vulture treats them as used.
#
# Almost all of these are false positives that vulture cannot resolve statically:
# provider config classes assign their __init__ params via locals() + setattr, and
# many "unused variable" hits are method parameters required by a protocol/contract
# (e.g. __aexit__'s exc_val/exc_tb, abstract hook signatures, **kwargs forwarding).
#
# Regenerate after intentionally adding or removing such patterns with
# `make lint-deadcode-update`; everything below the marker is generated.
# === generated below; do not edit by hand ===
ssl_ca_certs # unused variable (litellm/_redis.py:129)
host_type # unused variable (litellm/caching/qdrant_semantic_cache.py:39)
logged_item # unused variable (litellm/integrations/custom_logger.py:354)
default_span # unused variable (litellm/integrations/opentelemetry.py:2841)
timeout_millis # unused variable (litellm/integrations/otel/plumbing/providers.py:90)
hugging_face # unused variable (litellm/litellm_core_utils/get_litellm_params.py:63)
replicate # unused variable (litellm/litellm_core_utils/get_litellm_params.py:64)
together_ai # unused variable (litellm/litellm_core_utils/get_litellm_params.py:65)
voice_name # unused variable (litellm/llms/azure/text_to_speech/transformation.py:199)
exc_tb # unused variable (litellm/llms/base.py:74)
exc_val # unused variable (litellm/llms/base.py:74)
create_file_request # unused variable (litellm/llms/base_llm/files/transformation.py:217)
maxTokens # unused variable (litellm/llms/bedrock/chat/converse_transformation.py:116)
stopSequences # unused variable (litellm/llms/bedrock/chat/converse_transformation.py:117)
topP # unused variable (litellm/llms/bedrock/chat/converse_transformation.py:119)
topK # unused variable (litellm/llms/bedrock/chat/converse_transformation.py:120)
search_queries_only # unused variable (litellm/llms/bedrock/chat/invoke_handler.py:112)
prompt_truncation # unused variable (litellm/llms/bedrock/chat/invoke_handler.py:118)
return_prompt # unused variable (litellm/llms/bedrock/chat/invoke_handler.py:122)
raw_prompting # unused variable (litellm/llms/bedrock/chat/invoke_handler.py:124)
maxTokens # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:41)
topP # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:43)
stopSequences # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:44)
frequencePenalty # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:45)
presencePenalty # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:46)
countPenalty # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_ai21_transformation.py:47)
return_likelihood # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_cohere_transformation.py:28)
maxTokenCount # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_llama_transformation.py:27)
topP # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_llama_transformation.py:29)
maxTokenCount # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_titan_transformation.py:31)
stopSequences # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_titan_transformation.py:32)
topP # unused variable (litellm/llms/bedrock/chat/invoke_transformations/amazon_titan_transformation.py:34)
max_tokens_to_sample # unused variable (litellm/llms/bedrock/chat/invoke_transformations/anthropic_claude2_transformation.py:32)
min_tokens # unused variable (litellm/llms/codestral/completion/transformation.py:27)
random_seed # unused variable (litellm/llms/codestral/completion/transformation.py:29)
prompt_truncation # unused variable (litellm/llms/cohere/chat/transformation.py:95)
connectors # unused variable (litellm/llms/cohere/chat/transformation.py:96)
search_queries_only # unused variable (litellm/llms/cohere/chat/transformation.py:97)
prompt_truncation # unused variable (litellm/llms/cohere/chat/v2_transformation.py:81)
connectors # unused variable (litellm/llms/cohere/chat/v2_transformation.py:82)
search_queries_only # unused variable (litellm/llms/cohere/chat/v2_transformation.py:83)
aimage_variation # unused variable (litellm/llms/custom_httpx/aiohttp_handler.py:590)
maximum_tokens # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:116)
minimum_tokens # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:117)
sequence_penalty # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:124)
sequence_penalty_min_length # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:125)
repetition_penalties_include_prompt # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:126)
repetition_penalties_include_completion # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:127)
use_multiplicative_presence_penalty # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:128)
use_multiplicative_frequency_penalty # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:129)
use_multiplicative_sequence_penalty # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:130)
penalty_bias # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:131)
penalty_exceptions_include_stop_sequences # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:132)
log_probs # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:136)
raw_completion # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:139)
disable_optimizations # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:140)
completion_bias_inclusion # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:141)
completion_bias_exclusion # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:142)
completion_bias_inclusion_first_token_only # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:143)
completion_bias_exclusion_first_token_only # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:144)
contextual_control_threshold # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:145)
control_log_additive # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:146)
default_max_tokens_to_sample # unused variable (litellm/llms/deprecated_providers/aleph_alpha.py:194)
prompt_truncate_len # unused variable (litellm/llms/fireworks_ai/chat/transformation.py:116)
context_length_exceeded_behavior # unused variable (litellm/llms/fireworks_ai/chat/transformation.py:117)
repetition_penalty # unused variable (litellm/llms/gigachat/chat/transformation.py:76)
profanity_check # unused variable (litellm/llms/gigachat/chat/transformation.py:77)
retrieval_method # unused variable (litellm/llms/gradient_ai/chat/transformation.py:33)
kb_filters # unused variable (litellm/llms/gradient_ai/chat/transformation.py:39)
filter_kb_content_by_query_metadata # unused variable (litellm/llms/gradient_ai/chat/transformation.py:40)
instruction_override # unused variable (litellm/llms/gradient_ai/chat/transformation.py:41)
include_functions_info # unused variable (litellm/llms/gradient_ai/chat/transformation.py:42)
include_retrieval_info # unused variable (litellm/llms/gradient_ai/chat/transformation.py:43)
include_guardrails_info # unused variable (litellm/llms/gradient_ai/chat/transformation.py:44)
provide_citations # unused variable (litellm/llms/gradient_ai/chat/transformation.py:45)
decoder_input_details # unused variable (litellm/llms/huggingface/embedding/transformation.py:66)
max_new_tokens # unused variable (litellm/llms/huggingface/embedding/transformation.py:68)
repetition_penalty # unused variable (litellm/llms/huggingface/embedding/transformation.py:69)
return_full_text # unused variable (litellm/llms/huggingface/embedding/transformation.py:70)
top_n_tokens # unused variable (litellm/llms/huggingface/embedding/transformation.py:74)
truncate # unused variable (litellm/llms/huggingface/embedding/transformation.py:76)
typical_p # unused variable (litellm/llms/huggingface/embedding/transformation.py:77)
watermark # unused variable (litellm/llms/huggingface/embedding/transformation.py:78)
repeat_penalty # unused variable (litellm/llms/lemonade/chat/transformation.py:41)
random_seed # unused variable (litellm/llms/mistral/chat/transformation.py:84)
safe_prompt # unused variable (litellm/llms/mistral/chat/transformation.py:85)
default_max_tokens_to_sample # unused variable (litellm/llms/nlp_cloud/chat/handler.py:29)
length_no_input # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:69)
end_sequence # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:70)
remove_end_sequence # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:71)
remove_input # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:72)
bad_words # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:73)
repetition_penalty # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:77)
num_beams # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:78)
num_return_sequences # unused variable (litellm/llms/nlp_cloud/chat/transformation.py:79)
truncate # unused variable (litellm/llms/nvidia_nim/embed.py:33)
enforce_content_headers # unused variable (litellm/llms/oci/common_utils.py:69)
mirostat # unused variable (litellm/llms/ollama/chat/transformation.py:109)
mirostat_eta # unused variable (litellm/llms/ollama/chat/transformation.py:110)
mirostat_tau # unused variable (litellm/llms/ollama/chat/transformation.py:111)
num_ctx # unused variable (litellm/llms/ollama/chat/transformation.py:112)
num_gqa # unused variable (litellm/llms/ollama/chat/transformation.py:113)
num_thread # unused variable (litellm/llms/ollama/chat/transformation.py:114)
repeat_last_n # unused variable (litellm/llms/ollama/chat/transformation.py:115)
repeat_penalty # unused variable (litellm/llms/ollama/chat/transformation.py:116)
tfs_z # unused variable (litellm/llms/ollama/chat/transformation.py:120)
num_predict # unused variable (litellm/llms/ollama/chat/transformation.py:121)
mirostat # unused variable (litellm/llms/ollama/completion/transformation.py:106)
mirostat_eta # unused variable (litellm/llms/ollama/completion/transformation.py:107)
mirostat_tau # unused variable (litellm/llms/ollama/completion/transformation.py:108)
num_ctx # unused variable (litellm/llms/ollama/completion/transformation.py:109)
num_gqa # unused variable (litellm/llms/ollama/completion/transformation.py:110)
num_gpu # unused variable (litellm/llms/ollama/completion/transformation.py:111)
num_thread # unused variable (litellm/llms/ollama/completion/transformation.py:112)
repeat_last_n # unused variable (litellm/llms/ollama/completion/transformation.py:113)
repeat_penalty # unused variable (litellm/llms/ollama/completion/transformation.py:114)
tfs_z # unused variable (litellm/llms/ollama/completion/transformation.py:118)
num_predict # unused variable (litellm/llms/ollama/completion/transformation.py:119)
default_max_tokens_to_sample # unused variable (litellm/llms/oobabooga/chat/oobabooga.py:27)
do_sample # unused variable (litellm/llms/petals/completion/transformation.py:55)
repetition_penalty # unused variable (litellm/llms/petals/completion/transformation.py:59)
decoder_input_details # unused variable (litellm/llms/predibase/chat/transformation.py:57)
max_new_tokens # unused variable (litellm/llms/predibase/chat/transformation.py:59)
repetition_penalty # unused variable (litellm/llms/predibase/chat/transformation.py:60)
return_full_text # unused variable (litellm/llms/predibase/chat/transformation.py:61)
truncate # unused variable (litellm/llms/predibase/chat/transformation.py:67)
typical_p # unused variable (litellm/llms/predibase/chat/transformation.py:68)
watermark # unused variable (litellm/llms/predibase/chat/transformation.py:69)
api_token # unused variable (litellm/llms/replicate/chat/handler.py:25)
api_token # unused variable (litellm/llms/replicate/chat/handler.py:73)
max_new_tokens # unused variable (litellm/llms/replicate/chat/transformation.py:68)
min_new_tokens # unused variable (litellm/llms/replicate/chat/transformation.py:69)
max_new_tokens # unused variable (litellm/llms/sagemaker/completion/transformation.py:48)
return_full_text # unused variable (litellm/llms/sagemaker/completion/transformation.py:52)
auto_truncate # unused variable (litellm/llms/vertex_ai/vertex_embeddings/transformation.py:38)
decoding_method # unused variable (litellm/llms/watsonx/completion/transformation.py:94)
max_new_tokens # unused variable (litellm/llms/watsonx/completion/transformation.py:96)
min_new_tokens # unused variable (litellm/llms/watsonx/completion/transformation.py:97)
length_penalty # unused variable (litellm/llms/watsonx/completion/transformation.py:98)
repetition_penalty # unused variable (litellm/llms/watsonx/completion/transformation.py:102)
truncate_input_tokens # unused variable (litellm/llms/watsonx/completion/transformation.py:103)
include_stop_sequences # unused variable (litellm/llms/watsonx/completion/transformation.py:104)
return_options # unused variable (litellm/llms/watsonx/completion/transformation.py:105)
random_seed # unused variable (litellm/llms/watsonx/completion/transformation.py:106)
caching # unused variable (litellm/main.py:6083)
caching # unused variable (litellm/main.py:6109)
caching # unused variable (litellm/main.py:6136)
text_type # unused variable (litellm/proxy/guardrails/guardrail_hooks/dynamoai/dynamoai.py:101)
current_master_key # unused variable (litellm/proxy/management_endpoints/key_management_endpoints.py:4163)
operation_context # unused variable (litellm/proxy/openai_files_endpoints/common_utils.py:268)
add_key # unused variable (litellm/proxy/proxy_cli.py:879)
test_async # unused variable (litellm/proxy/proxy_cli.py:896)
config_type # unused variable (litellm/proxy/proxy_server.py:15331)
preserve_fields # unused variable (litellm/proxy/utils.py:6548)
is_retry # unused variable (litellm/router.py:4388)
is_fallback # unused variable (litellm/router.py:4389)
is_retry # unused variable (litellm/router.py:4425)
is_fallback # unused variable (litellm/router.py:4426)
is_retry # unused variable (litellm/router.py:4532)
is_fallback # unused variable (litellm/router.py:4533)
imageConfig # unused variable (litellm/utils.py:3242)
# unsatisfiable 'if' condition (litellm/a2a_protocol/providers/base.py:61)
# redundant if-condition (litellm/llms/bedrock/chat/invoke_handler.py:185)
# unsatisfiable 'if' condition (litellm/proxy/common_request_processing.py:415)
max_tokens_to_sample # unused variable (litellm/llms/anthropic/completion/transformation.py:69)
max_new_tokens # unused variable (litellm/llms/petals/completion/transformation.py:52)
# redundant if-condition (litellm/proxy/litellm_pre_call_utils.py:1779)

View file

@ -7,6 +7,7 @@
info lint lint-dev format \
lint-basedpyright lint-basedpyright-budget-update \
lint-ruff-budget lint-ruff-budget-update lint-budget-update lint-gate \
lint-deadcode lint-deadcode-update \
install-dev install-proxy-dev install-test-deps install-hooks \
install-helm-unittest check-circular-imports check-import-safety
@ -31,6 +32,8 @@ help:
@echo " make lint-gate - Strict ruff gate in CI-parity mode (fetches staging, simulates the merge)"
@echo " make lint-ruff-budget-update - Re-capture per-rule baselines in ruff-strict-budget.json (ratchet)"
@echo " make lint-budget-update - Re-capture all ratchet budgets (ruff + basedpyright)"
@echo " make lint-deadcode - Find dead Python code (unreachable branches, unused symbols) with vulture"
@echo " make lint-deadcode-update - Re-capture the vulture allowlist baseline (.vulture_allowlist.py)"
@echo " make check-circular-imports - Check for circular imports"
@echo " make check-import-safety - Check import safety"
@echo " make test - Run all tests"
@ -148,6 +151,16 @@ lint-ruff-budget-update: install-dev
# Ratchet all budgets in one shot (ruff strict + basedpyright)
lint-budget-update: lint-ruff-budget-update lint-basedpyright-budget-update
# Dead-code detection (the Python analogue of the frontend's knip).
# Surfaces unreachable code, dead branches, and unused symbols. Config and the
# accepted-findings baseline live in pyproject.toml [tool.vulture] and
# .vulture_allowlist.py. On-demand, not wired into `make lint`.
lint-deadcode: install-dev
$(UV_RUN) vulture
lint-deadcode-update: install-dev
$(UV_RUN) python scripts/vulture_allowlist_update.py
check-circular-imports: install-dev
cd litellm && $(UV_RUN) python ../tests/documentation_tests/test_circular_imports.py && cd ..

View file

@ -378,7 +378,6 @@ class QdrantSemanticCache(BaseCache):
else:
# cache miss !
return None
pass
async def async_set_cache(self, key, value, **kwargs):
from litellm._uuid import uuid
@ -486,7 +485,6 @@ class QdrantSemanticCache(BaseCache):
else:
# cache miss !
return None
pass
async def _collection_info(self):
return self.collection_info

View file

@ -4527,7 +4527,6 @@ def _init_custom_logger_compatible_class(
f"[Non-Blocking Error] Error initializing custom logger: {e}"
)
return None
return None
def _maybe_construct_otel_v2(

View file

@ -720,7 +720,6 @@ class CustomStreamWrapper:
"prompt_tokens": parsed_response.get("input_token_count", 0),
"completion_tokens": parsed_response.get("generated_token_count", 0),
}
return {"text": "", "is_finished": False}
except Exception as e:
raise e

View file

@ -386,14 +386,6 @@ class SagemakerLLM(BaseAWSLLM):
return completion_stream
# LOGGING
logging_obj.post_call(
input=[],
api_key="",
original_response="first stream response received",
additional_args={"complete_input_dict": data},
)
except httpx.HTTPStatusError as err:
error_code = err.response.status_code
raise SagemakerError(status_code=error_code, message=err.response.text)

View file

@ -1115,8 +1115,6 @@ class ModelManagementAuthChecks:
else:
return True
return True
#### [BETA] - This is a beta endpoint, format might change based on user feedback. - https://github.com/BerriAI/litellm/issues/964
@router.post(

View file

@ -381,7 +381,6 @@ async def test_policies_and_guardrails(
)
results.append(item)
return ApplyPoliciesListResult(results=results)
raise ValueError("Either inputs or inputs_list must be provided")
except Exception as e:
raise handle_exception_on_proxy(e)

View file

@ -2487,8 +2487,6 @@ async def view_spend_logs(
)
return data
return None
except Exception as e:
if isinstance(e, HTTPException):
raise ProxyException(

View file

@ -4766,8 +4766,6 @@ class Router:
)
response = await self.async_function_with_fallbacks(**kwargs)
return response
return response
except Exception as e:
asyncio.create_task(
send_llm_exception_alert(

View file

@ -254,8 +254,6 @@ def _should_cooldown_deployment(
original_exception=original_exception,
)
return False
def _set_cooldown_deployments(
litellm_router_instance: LitellmRouter,

View file

@ -184,6 +184,7 @@ dev = [
"pytest-timeout==2.4.0",
"vcrpy==8.2.1",
"pytest-recording==0.13.4",
"vulture==2.16",
]
proxy-dev = [
"prisma==0.11.0",
@ -269,6 +270,12 @@ source-exclude = [
[tool.isort]
profile = "black"
[tool.vulture]
paths = ["litellm", ".vulture_allowlist.py"]
exclude = ["litellm/types/", "litellm/proxy/_experimental/", "litellm/proxy/example_config_yaml/"]
min_confidence = 100
sort_by_size = true
[tool.commitizen]
version = "1.91.0"
version_files = [

View file

@ -0,0 +1,60 @@
"""Regenerate the vulture allowlist baseline (.vulture_allowlist.py).
Mirrors the ruff/basedpyright budget-update ratchet: re-capture the set of
currently accepted dead-code findings so `make lint-deadcode` only surfaces new
ones. The hand-written header is preserved; everything below it is regenerated
from `vulture --make-whitelist`.
"""
from __future__ import annotations
import subprocess
import sys
import tomllib
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
ALLOWLIST = REPO_ROOT / ".vulture_allowlist.py"
MARKER = "# === generated below; do not edit by hand ==="
def _read_header() -> str:
text = ALLOWLIST.read_text()
end = text.find(MARKER)
if end == -1:
raise SystemExit(f"Could not find marker {MARKER!r} in {ALLOWLIST}")
return text[: end + len(MARKER)] + "\n\n"
def _vulture_config() -> tuple[tuple[str, ...], tuple[str, ...]]:
config = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text())["tool"][
"vulture"
]
paths = tuple(p for p in config["paths"] if p != ALLOWLIST.name)
exclude = tuple(config.get("exclude", ()))
return paths, exclude
def main() -> int:
header = _read_header()
paths, exclude = _vulture_config()
cmd = [
"vulture",
*paths,
"--min-confidence",
"100",
"--make-whitelist",
]
if exclude:
cmd += ["--exclude", ",".join(exclude)]
result = subprocess.run(cmd, cwd=REPO_ROOT, capture_output=True, text=True)
if result.returncode not in (0, 3):
sys.stderr.write(result.stderr)
return result.returncode
ALLOWLIST.write_text(header + result.stdout)
print(f"Updated {ALLOWLIST.relative_to(REPO_ROOT)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())