litellm/tests/test_litellm_rust/AGENTS.md
Yujong Lee ca21e2572f
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
refactor(rust-bridge): decide native routing in the catalog, drop the bypass predicates
2026-10-19 09:38:54 -07:00

4.2 KiB

Invariants

  • Outside the dispatch tests below, this directory tests the Rust path and nothing else. Treat the Python implementation of every route as already removed. Never call it on purpose, never run one request through both engines, and never compare Python and Rust outputs, headers, cache keys or logs
  • A test that would still pass with Python serving the call does not belong here. Every test except the dispatch tests asserts the call was served natively with support.routing.assert_served_natively
  • Dispatch tests check which path a public call takes under given rules. They are each route folder's test_dispatch.py, plus test_dispatch.py and test_public_entries.py at the top of this directory
    • They pass rules= to a dispatch directly or wrap the whole test in one support.routing.routed(...) block, and they stub the native bindings
    • They may call Python, since the choice between the two paths is what they test
    • They live here until they can run in tests/unit/ without the compiled extension
  • Select the Rust path with support.routing.native_route(...), which prepends RUST_REQUIRED rules to catalog.RULES and restores them on exit. Each route folder's conftest.py applies it to every test through an autouse fixture. A test outside a route folder wraps its whole body in one native_route(...) block, and a child interpreter script assigns catalog.RULES once at its top. Never select a backend by reading or setting LITELLM_RUST, calling litellm.rust(...) or rebinding catalog.RULES any other way. The rollout in litellm/rust_bridge/catalog.py is a product decision, and a test here must pass no matter what it says, including for a route marked PYTHON_ONLY
  • catalog.RULES is static at runtime and native_route(...) swaps a module global, so open it once per test and never per call. Two overlapping blocks, such as one per task inside an asyncio.gather, restore out of order and leave the wrong rules behind for later tests
  • No test here depends on the rollout switch. Only no rule, PYTHON_ONLY and RUST_REQUIRED pick a path without it, so those are the only rollouts a test here uses, and Rust with Python fallback is not reachable from this directory. What LITELLM_RUST and litellm.rust(...) mean is tested in tests/unit/rust_bridge/test_configuration.py, mostly through decide, which takes both as plain values
  • A test lives here only when it cannot be written in Rust. That means it needs the Python public API, Python-owned state such as litellm.cache and callbacks, or the process boundary (fork, threads, event loops). Anything checkable from the Rust side goes in the crate's own tests
  • The wire is the local recording server. No test here reaches a real provider
  • Never parametrize one test across routes. Each route folder owns its tests, its server fixture, its request data and its call. The one exception is test_public_entries.py, whose subject is that every route serves its public call forms the same way
  • Call _native directly only for a case the public API cannot reach

Layout

Each route folder (ocr/, messages/, responses/, chat_completions/) uses the same file names, one concern per file

  • test_dispatch.py the call reaches the native route, and which path the rules pick for each public call
  • test_requests.py Python inputs that reach the wire
  • test_failures.py errors raised to the caller and rejection before provider IO
  • test_settings.py globals, env, named credentials and their precedence
  • test_streaming.py stream relay, early close and stream-time logging
  • test_callbacks.py the logger hook contract
  • test_body_hooks.py handler body hooks such as interceptors and agentic loops
  • test_lifecycle.py cancellation, concurrency, context and nested calls
  • test_cache.py the configured cache seen from this route

embeddings/ holds only test_dispatch.py so far. At the top, test_dispatch.py tests PublicDispatch with stub routes and test_public_entries.py runs every route's public call forms through one table

Route data and the route's call live in <route>/data.py, and <route>/conftest.py owns the server fixture and the autouse native fixture. support/ holds only route-neutral helpers