Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ After: the same request comes back with real token counts, so the dashboard show
**Please complete all items before asking a LiteLLM maintainer to review your PR**

- [ ] I have added meaningful tests
- [ ] The handful of test files covering my change pass locally, e.g. `uv run pytest tests/test_litellm/<your_test_file>.py -v`. Leave the suites (`make test-unit-*`, `make test-unit`) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
- [ ] The handful of test files covering my change pass locally, e.g. `uv run pytest tests/unit/<your_test_file>.py -v`. Leave the suites (`make test-unit-*`, `make test-unit`) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
- [ ] My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
- [ ] My PR's scope is as isolated as possible; it only solves 1 specific problem
- [ ] I have received a Greptile **Confidence Score of at least 4/5** before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment `@greptileai` to re-request a review after pushing changes)
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/test-rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ on:
- "litellm/ocr/**"
- "litellm/llms/base_llm/ocr/**"
- "litellm/llms/custom_httpx/llm_http_handler.py"
- "tests/test_litellm/ocr/**"
- "tests/test_litellm/conftest.py"
- "Makefile"
- ".cargo/**"
Expand Down Expand Up @@ -42,7 +41,6 @@ on:
- "litellm/ocr/**"
- "litellm/llms/base_llm/ocr/**"
- "litellm/llms/custom_httpx/llm_http_handler.py"
- "tests/test_litellm/ocr/**"
- "tests/test_litellm/conftest.py"
- "Makefile"
- ".cargo/**"
Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ jobs:

- shard: Vertex AI
artifact-name: llm-vertex-ai
test-path: "tests/test_litellm/llms/vertex_ai"
test-path: ""
unit-flag: llm-vertex-ai
workers: 1
reruns: 2
Expand All @@ -97,7 +97,7 @@ jobs:

- shard: All Other Providers
artifact-name: llm-other-providers
test-path: "tests/test_litellm/llms --ignore=tests/test_litellm/llms/vertex_ai"
test-path: ""
unit-flag: llm-other-providers
workers: 2
reruns: 2
Expand All @@ -107,9 +107,6 @@ jobs:
- shard: misc
artifact-name: misc
test-path: >-
tests/test_litellm/interactions
tests/test_litellm/ocr
tests/test_litellm/passthrough
tests/test_litellm/test_*.py
unit-flag: misc
workers: 2
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Never test structure of code only function of it

A test must only fail when litellm code changes. Never pin facts we don't own (a vendor's price, a third party's field, an upstream default, today's date) as literals or as "X must be absent"; assert the invariant our code guarantees instead, e.g. two rows agree, a value is within range, a field is derived from another. If an outside fact is truly load-bearing, cite its source and date next to the assertion so a reader can tell stale from broken

`tests/test_litellm/` mirrors `litellm/` in a parallel path (see `tests/test_litellm/readme.md`). Name tests `test_<filename>.py`, but always match the existing test file in the directory you touch — many provider dirs use longer descriptive names (e.g. `test_anthropic_chat_transformation.py`) to avoid ambiguity across sibling folders. For bug fixes, extend the existing mapped test file rather than creating a new one. Only create a new test file for a new feature (provider, endpoint, or transformation module) that has no mapped test yet, following that directory's naming convention (or `test_<filename>.py` if you're the first test there). One focused regression test beats many shallow ones
`tests/unit/` mirrors `litellm/` in a parallel path (see `tests/unit/AGENTS.md`). Name tests `test_<filename>.py`, but always match the existing test file in the directory you touch — many provider dirs use longer descriptive names (e.g. `test_anthropic_chat_transformation.py`) to avoid ambiguity across sibling folders. For bug fixes, extend the existing mapped test file rather than creating a new one. Only create a new test file for a new feature (provider, endpoint, or transformation module) that has no mapped test yet, following that directory's naming convention (or `test_<filename>.py` if you're the first test there). One focused regression test beats many shallow ones

End-to-end tests belong in `tests/e2e/` and must follow the harness conventions documented in that directory's `AGENTS.md`

Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ Conventions to follow when touching this layer:
| Column vs. field names | Where a model field differs from its DB column (for example `org_id` maps to the `organization_id` column), the repository translates in both directions rather than relying on Pydantic to guess. |
| Array mutations | Adds use Prisma's atomic `push` (`add_member`, `add_admin`, `add_models`) to avoid read-modify-write races. Removals fall back to read-modify-write because Prisma has no atomic array remove. |

To add a new entity, define the model under `litellm/models/`, re-export it from `proxy/_types.py` if existing code imports it from there, and add a repository under `litellm/repositories/` (subclass `BaseRepository` for plain CRUD, or add bespoke methods when the entity needs encryption, archiving, or atomic array updates). Mirror the tests in `tests/test_litellm/repositories/`.
To add a new entity, define the model under `litellm/models/`, re-export it from `proxy/_types.py` if existing code imports it from there, and add a repository under `litellm/repositories/` (subclass `BaseRepository` for plain CRUD, or add bespoke methods when the entity needs encryption, archiving, or atomic array updates). Mirror the tests in `tests/unit/repositories/`.

---

Expand Down
14 changes: 7 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Here are the core requirements for any PR submitted to LiteLLM:
- [ ] **Add testing** - Adding at least 1 test is a hard requirement - [see details](#adding-testing)
- [ ] **Ensure your PR passes all checks**:
- [ ] [Linting / Formatting](#running-linting-and-formatting-checks) - `make lint`
- [ ] [The tests covering your change](#running-unit-tests) pass, e.g. `uv run pytest tests/test_litellm/<your_test_file>.py -v`. CI runs the full unit test matrix, so you don't need to run the whole suite locally
- [ ] [The tests covering your change](#running-unit-tests) pass, e.g. `uv run pytest tests/unit/<your_test_file>.py -v`. CI runs the full unit test matrix, so you don't need to run the whole suite locally

#### UI PRs

Expand Down Expand Up @@ -72,7 +72,7 @@ make format
make lint

# Run the tests covering your change (CI runs the full suite)
uv run pytest tests/test_litellm/<your_test_file>.py -v
uv run pytest tests/unit/<your_test_file>.py -v

# Commit your changes (must follow Conventional Commits — see above)
git add .
Expand All @@ -88,18 +88,18 @@ git push origin feature/your-feature

### Where to Add Tests

Add your tests to the [`tests/test_litellm/` directory](https://github.com/BerriAI/litellm/tree/main/tests/test_litellm).
Add your tests to the [`tests/unit/` directory](https://github.com/BerriAI/litellm/tree/main/tests/unit).

- This directory mirrors the structure of the `litellm/` directory
- **Only add mocked tests** - no real LLM API calls in this directory
- For integration tests with real APIs, use the appropriate test directories

### File Naming Convention

The `tests/test_litellm/` directory follows the same structure as `litellm/`:
The `tests/unit/` directory follows the same structure as `litellm/`:

- `litellm/proxy/caching_routes.py` → `tests/test_litellm/proxy/test_caching_routes.py`
- `litellm/utils.py` → `tests/test_litellm/test_utils.py`
- `litellm/utils.py` → `tests/unit/test_utils.py`

### Example Test

Expand All @@ -125,10 +125,10 @@ def test_your_feature():

Run the tests covering your change:
```bash
uv run pytest tests/test_litellm/test_your_file.py -v
uv run pytest tests/unit/test_your_file.py -v
```

`tests/test_litellm` holds thousands of tests, so running all of it locally takes a long time. CI runs it as a parallel matrix (`make test-unit-llms`, `make test-unit-proxy-core`, and the other `test-unit-*` targets) on beefier boxes, so if, for whatever reason, you must run the whole suite, it's better to rely on CI to do that.
`tests/unit` holds thousands of tests, so running all of it locally takes a long time. CI runs it as a parallel matrix (`make test-unit-llms`, `make test-unit-proxy-core`, and the other `test-unit-*` targets) on beefier boxes, so if, for whatever reason, you must run the whole suite, it's better to rely on CI to do that.

If you're running broader test suites, proxy tests, or anything that touches PostgreSQL-backed fixtures/plugins, install the full local test environment first:

Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ help:
@echo " make check-circular-imports - Check for circular imports"
@echo " make check-import-safety - Check import safety"
@echo " make test - Run all tests"
@echo " make test-unit - Run unit tests (tests/test_litellm)"
@echo " make test-unit - Run unit tests (tests/unit and tests/test_litellm)"
@echo " make test-unit-llms - Run LLM provider tests (~225 files)"
@echo " make test-unit-proxy-guardrails - Run proxy guardrails+mgmt tests (~51 files)"
@echo " make test-unit-proxy-core - Run proxy auth+client+db+hooks tests (~52 files)"
Expand Down Expand Up @@ -310,7 +310,7 @@ test: install-test-deps
$(UV_RUN) pytest tests/

test-unit: install-test-deps
$(UV_RUN) pytest tests/test_litellm -x -vv -n 4
$(UV_RUN) pytest tests/unit tests/test_litellm -x -vv -n 4

# Matrix test targets (matching CI workflow groups)
test-unit-llms: install-test-deps
Expand All @@ -332,7 +332,7 @@ test-unit-core-utils: install-test-deps
$(UV_RUN) pytest tests/unit/litellm_core_utils --tb=short -vv -n 2 --durations=20

test-unit-other: install-test-deps
$(UV_RUN) pytest tests/unit/caching tests/unit/responses tests/unit/secret_managers tests/unit/vector_stores tests/unit/a2a_protocol tests/test_litellm/anthropic_interface tests/unit/completion_extras tests/unit/containers tests/unit/enterprise tests/unit/experimental_mcp_client tests/unit/google_genai tests/unit/images tests/unit/interactions tests/test_litellm/interactions tests/test_litellm/passthrough tests/unit/router_strategy tests/unit/router_utils tests/unit/types --tb=short -vv -n 4 --durations=20
$(UV_RUN) pytest tests/unit/caching tests/unit/responses tests/unit/secret_managers tests/unit/vector_stores tests/unit/a2a_protocol tests/unit/completion_extras tests/unit/containers tests/unit/enterprise tests/unit/experimental_mcp_client tests/unit/google_genai tests/unit/images tests/unit/interactions tests/unit/router_strategy tests/unit/router_utils tests/unit/types --tb=short -vv -n 4 --durations=20

test-unit-root: install-test-deps
$(UV_RUN) pytest tests/unit/test_*.py tests/test_litellm/test_*.py --tb=short -vv -n 4 --durations=20
Expand Down
2 changes: 1 addition & 1 deletion litellm/containers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ Run the container API tests:

```bash
cd /Users/ishaanjaffer/github/litellm
python -m pytest tests/test_litellm/containers/ -v
python -m pytest tests/unit/containers/ -v
```

Test via proxy:
Expand Down
2 changes: 1 addition & 1 deletion tests/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

To make it easier to contribute and map what behavior is tested,

we've started mapping the litellm directory in `tests/test_litellm`
we've started mapping the litellm directory in `tests/unit`

This folder can only run mock tests.
3 changes: 1 addition & 2 deletions tests/integration/sandbox/test_e2b_sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
e2b code execution sandbox - end-to-end integration tests.

These tests make REAL HTTP calls to the e2b API and are skipped automatically
unless E2B_API_KEY is set. Mock-only unit tests live in
tests/test_litellm/sandbox/test_e2b_sandbox.py.
unless E2B_API_KEY is set.

Run only these tests:
pytest tests/integration/sandbox/test_e2b_sandbox.py -v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import os

from tests.test_litellm.interactions.base_interactions_test import (
from tests.llm_translation.interactions.base_interactions_test import (
BaseInteractionsTest,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Per OpenAPI spec: https://ai.google.dev/static/api/interactions.openapi.json

Run with: pytest tests/test_litellm/interactions/test_google_interactions_integration.py -v
Run with: pytest tests/llm_translation/interactions/test_google_interactions_integration.py -v
"""

import asyncio
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import os

from tests.test_litellm.interactions.base_interactions_test import (
from tests.llm_translation.interactions.base_interactions_test import (
BaseInteractionsTest,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

Run with:
cd /path/to/litellm
python tests/test_litellm/llms/databricks/test_databricks_e2e.py
python tests/llm_translation/test_databricks_e2e.py
Comment thread
greptile-apps[bot] marked this conversation as resolved.

Config Options:
TEST_AUTH_METHOD=oauth # Test OAuth M2M authentication
Expand All @@ -69,12 +69,12 @@
# These are E2E tests that require real Databricks credentials
pytestmark = pytest.mark.skip(
reason="E2E tests require real Databricks credentials. Run directly with: "
"python tests/test_litellm/llms/databricks/test_databricks_e2e.py"
"python tests/llm_translation/test_databricks_e2e.py"
)

# Add the litellm package to path
sys.path.insert(
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
)

# Config file path - can be overridden with DATABRICKS_TEST_CONFIG env var
Expand Down
5 changes: 1 addition & 4 deletions tests/local_testing/test_handler_gc_does_not_close_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@
test, and the test then passes on a broken handler. They hold weak references
instead, which the refcount does not count.

These live here rather than under ``tests/test_litellm/`` because they need a
real connection pool: a mocked transport goes on yielding chunks after its
client is closed, so the very teardown under test is what a mock cannot
reproduce. The server is a hermetic, credential-free ``ThreadingHTTPServer`` on
The server is a hermetic, credential-free ``ThreadingHTTPServer`` on
an ephemeral loopback port, and needs no network access beyond it.

Related: https://github.com/BerriAI/litellm/issues/24929
Expand Down
Empty file.
Empty file.
1 change: 0 additions & 1 deletion tests/test_litellm/llms/vertex_ai/__init__.py

This file was deleted.

2 changes: 0 additions & 2 deletions tests/test_litellm/log.txt

This file was deleted.

Empty file removed tests/test_litellm/ocr/__init__.py
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion tests/unit/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Green if `send_batched` drops every row. pydantic doubles in 12 of 203 files, fa
## Where it goes

`tests/unit/<path>` mirrors `litellm/<path>`, so a changed file selects its tests by path, not a mapping
file. Empty today; new unit tests go here. The examples above live in `tests/test_litellm`
file. New unit tests go here

## Writing it so a human can read it

Expand Down
16 changes: 8 additions & 8 deletions tests/unit/integrations/dotprompt/test_prompt_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_prompt_manager_initialization():
# Test with the existing prompts directory
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

# Should have loaded at least the sample prompts
Expand Down Expand Up @@ -56,7 +56,7 @@ def test_render_simple_template():
"""Test rendering a simple template with variables."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

# Test sample_prompt rendering
Expand All @@ -72,7 +72,7 @@ def test_render_chat_prompt():
"""Test rendering the chat prompt with conditional content."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

# Test with system context
Expand All @@ -98,7 +98,7 @@ def test_render_coding_assistant():
"""Test rendering the coding assistant prompt with complex logic."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

rendered = manager.render(
Expand Down Expand Up @@ -159,7 +159,7 @@ def test_prompt_not_found():
"""Test error handling for non-existent prompts."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

with pytest.raises(KeyError, match="Prompt 'nonexistent' not found"):
Expand All @@ -170,7 +170,7 @@ def test_list_prompts():
"""Test listing available prompts."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

prompts = manager.list_prompts()
Expand All @@ -184,7 +184,7 @@ def test_get_prompt_metadata():
"""Test retrieving prompt metadata."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

metadata = manager.get_prompt_metadata("sample_prompt")
Expand Down Expand Up @@ -221,7 +221,7 @@ def test_add_prompt_programmatically():
"""Test adding prompts programmatically."""
prompt_dir = Path(
__file__
).parent # Current directory when running from tests/test_litellm/prompts
).parent
manager = PromptManager(prompt_directory=str(prompt_dir))

initial_count = len(manager.prompts)
Expand Down
Loading
Loading