litellm/tests/base_sdk_tests/check_base_sdk_install.py

144 lines
4.8 KiB
Python

"""Smoke-check that a base ``pip install litellm`` (no extras) is importable and usable.
Run against a virtualenv that has the built wheel installed with no extras, using
that venv's own interpreter and nothing else. Deliberately stdlib-only: pytest would
pull ``packaging``, ``pluggy`` and ``iniconfig`` into the environment and could mask
the very class of undeclared-dependency bug this guards against.
"""
import importlib.util
import sys
import traceback
from collections.abc import Callable
EXTRAS_ONLY_MODULES = ("fastapi", "uvicorn", "keyring", "mcp", "mcp_types", "httpx2", "httpcore2")
def _require(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def check_environment_is_base_only() -> str:
present = tuple(name for name in EXTRAS_ONLY_MODULES if importlib.util.find_spec(name) is not None)
_require(
not present,
f"{', '.join(present)} installed, so this environment is not base-only and the run proves nothing",
)
return f"no extras-only packages present ({', '.join(EXTRAS_ONLY_MODULES)})"
def check_import() -> str:
from importlib.metadata import version
import litellm
_require(bool(litellm.__file__), "litellm has no __file__")
return f"imported litellm {version('litellm')}"
def check_completion() -> str:
import litellm
response = litellm.completion(
model="gpt-4o",
messages=[{"role": "user", "content": "ping"}],
mock_response="pong",
)
content = response.choices[0].message.content
_require(content == "pong", f"mock completion returned {content!r}")
return "mock completion round-trips"
def check_embedding() -> str:
import litellm
response = litellm.embedding(
model="text-embedding-3-small",
input=["ping"],
mock_response=[[0.1, 0.2]],
)
_require(len(response.data) == 1, f"mock embedding returned {len(response.data)} rows")
return "mock embedding round-trips"
def check_bundled_model_metadata() -> str:
import litellm
max_input_tokens = litellm.get_model_info("gpt-4o")["max_input_tokens"]
_require(
isinstance(max_input_tokens, int) and max_input_tokens > 0,
f"get_model_info returned max_input_tokens={max_input_tokens!r}",
)
prompt_cost, completion_cost = litellm.cost_per_token(model="gpt-4o", prompt_tokens=1000, completion_tokens=1000)
_require(
prompt_cost > 0 and completion_cost > 0,
f"cost_per_token returned ({prompt_cost}, {completion_cost})",
)
return f"bundled pricing metadata readable (gpt-4o max_input_tokens={max_input_tokens})"
def check_token_counter() -> str:
import litellm
count = litellm.token_counter(model="gpt-4o", text="hello world")
_require(count > 0, f"token_counter returned {count!r}")
return f"token_counter returned {count}"
def check_bedrock_credential_resolution() -> str:
import os
from unittest import mock
from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
non_aws_environ = {k: v for k, v in os.environ.items() if not k.startswith("AWS_")}
with mock.patch.dict(os.environ, non_aws_environ, clear=True):
credentials = BaseAWSLLM().get_credentials(
aws_access_key_id="AKIA-fake-base-sdk-check",
aws_secret_access_key="fake-secret",
aws_region_name="us-east-1",
)
_require(
credentials.access_key == "AKIA-fake-base-sdk-check",
f"get_credentials returned access_key={credentials.access_key!r}",
)
return "bedrock credential resolution works (boto3 ships with the base SDK)"
CHECKS: tuple[tuple[str, Callable[[], str]], ...] = (
("environment is base-only", check_environment_is_base_only),
("import litellm", check_import),
("chat completion", check_completion),
("embedding", check_embedding),
("bundled model metadata", check_bundled_model_metadata),
("token counter", check_token_counter),
("bedrock credential resolution", check_bedrock_credential_resolution),
)
def _run(check: Callable[[], str]) -> tuple[bool, str]:
try:
return True, check()
except Exception:
return False, traceback.format_exc()
def main() -> int:
print(f"base SDK smoke check on {sys.executable}")
for label, check in CHECKS:
passed, detail = _run(check)
if not passed:
print(f"FAIL {label}:\n{detail}")
print(f"A base `pip install litellm` is broken at: {label}")
print("Something needed at import or call time is missing from [project].dependencies")
print("in pyproject.toml. Declaring it only in an extra is what causes this.")
return 1
print(f"PASS {label}: {detail}")
print(f"\nall {len(CHECKS)} checks passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())