diff --git a/litellm-rust/crates/python-bridge/src/routes/chat_completions/mod.rs b/litellm-rust/crates/python-bridge/src/routes/chat_completions/mod.rs index 00ca5e092a8..d78d141f0f2 100644 --- a/litellm-rust/crates/python-bridge/src/routes/chat_completions/mod.rs +++ b/litellm-rust/crates/python-bridge/src/routes/chat_completions/mod.rs @@ -23,7 +23,7 @@ fn run_chat_completions( ) -> PyResult> { let host = InferenceHost::new( call.view()?, - "litellm.rust_bridge.chat_completions.route_host", + "litellm.rust_bridge.host.routes.chat_completions", ); run_inference::( py, diff --git a/litellm-rust/crates/python-bridge/src/routes/messages/host.rs b/litellm-rust/crates/python-bridge/src/routes/messages/host.rs index 008f1bde763..1add8fa485f 100644 --- a/litellm-rust/crates/python-bridge/src/routes/messages/host.rs +++ b/litellm-rust/crates/python-bridge/src/routes/messages/host.rs @@ -26,7 +26,7 @@ use crate::{ routes::RequestView, }; -const ROUTE_HOST_MODULE: &str = "litellm.rust_bridge.messages.route_host"; +const ROUTE_HOST_MODULE: &str = "litellm.rust_bridge.host.routes.messages"; const REQUEST_ERROR_MARKER: &str = "messages_request_error"; const BODY_FIELDS: [&str; 22] = [ diff --git a/litellm-rust/crates/python-bridge/src/routes/ocr/host.rs b/litellm-rust/crates/python-bridge/src/routes/ocr/host.rs index 4c8469cc019..fc1b70392e2 100644 --- a/litellm-rust/crates/python-bridge/src/routes/ocr/host.rs +++ b/litellm-rust/crates/python-bridge/src/routes/ocr/host.rs @@ -77,7 +77,7 @@ impl OcrPythonHost { _ => "", }; let mapped = py - .import("litellm.rust_bridge.ocr.route_host") + .import("litellm.rust_bridge.host.routes.ocr") .and_then(|module| module.getattr("map_failure")) .and_then(|map| map.call1((error.value(py), self.view.request(py)?, provider))) .and_then(|mapped| mapped.extract::>().map_err(PyErr::from)); @@ -115,7 +115,7 @@ impl PythonBinding for OcrPythonHost { py: Python<'_>, response: LiteLLMOcrResponse, ) -> PyResult> { - public_response(py, "litellm.rust_bridge.ocr.route_host", &response) + public_response(py, "litellm.rust_bridge.host.routes.ocr", &response) } fn encode_stream_head( diff --git a/litellm-rust/crates/python-bridge/src/routes/responses/mod.rs b/litellm-rust/crates/python-bridge/src/routes/responses/mod.rs index 1bf24813103..984f1e74df2 100644 --- a/litellm-rust/crates/python-bridge/src/routes/responses/mod.rs +++ b/litellm-rust/crates/python-bridge/src/routes/responses/mod.rs @@ -21,7 +21,7 @@ use super::{ }; use crate::errors::RustBridgeDeclined; -const ROUTE_HOST_MODULE: &str = "litellm.rust_bridge.responses.route_host"; +const ROUTE_HOST_MODULE: &str = "litellm.rust_bridge.host.routes.responses"; fn run_responses(py: Python<'_>, call: NativeCall<'_>, asynchronous: bool) -> PyResult> { let resolved = call.resolved()?; diff --git a/litellm/rust_bridge/host/AGENTS.md b/litellm/rust_bridge/host/AGENTS.md index 44d2909a834..9b751a1ada2 100644 --- a/litellm/rust_bridge/host/AGENTS.md +++ b/litellm/rust_bridge/host/AGENTS.md @@ -8,4 +8,4 @@ Python state Rust needs but does not own, behind one module per question. Rust i - The module path and function signature are a contract. The Rust side pins the path in a `const MODULE` in the crate that asks (`python-bridge/src/routes/messages/host.rs`, `python-bridge/src/secrets/`, `callbacks-legacy-python/src/python.rs`); change both sides together and keep `tests/unit/rust_bridge/host/` green - Inputs and outputs use values and frozen snapshots. Retain caller-owned clients and caches as opaque references when identity is required; Rust never discovers them through module globals - Moving a question into Rust means replacing the function body with a native call, not adding a second resolver. The Python signature stays -- A `*/route_host.py` module is not this: it projects one route's kwargs and maps that route's failures. Shared questions belong here +- `routes/.py` holds the questions one route asks, under the same function names on every route (`response`, `map_failure`, `connection_defaults`), so Rust names only the module. A question two routes ask goes in a top-level module here instead diff --git a/tests/unit/rust_bridge/chat_completions/__init__.py b/litellm/rust_bridge/host/routes/__init__.py similarity index 100% rename from tests/unit/rust_bridge/chat_completions/__init__.py rename to litellm/rust_bridge/host/routes/__init__.py diff --git a/litellm/rust_bridge/chat_completions/route_host.py b/litellm/rust_bridge/host/routes/chat_completions.py similarity index 100% rename from litellm/rust_bridge/chat_completions/route_host.py rename to litellm/rust_bridge/host/routes/chat_completions.py diff --git a/litellm/rust_bridge/messages/route_host.py b/litellm/rust_bridge/host/routes/messages.py similarity index 100% rename from litellm/rust_bridge/messages/route_host.py rename to litellm/rust_bridge/host/routes/messages.py diff --git a/litellm/rust_bridge/ocr/route_host.py b/litellm/rust_bridge/host/routes/ocr.py similarity index 100% rename from litellm/rust_bridge/ocr/route_host.py rename to litellm/rust_bridge/host/routes/ocr.py diff --git a/litellm/rust_bridge/responses/route_host.py b/litellm/rust_bridge/host/routes/responses.py similarity index 100% rename from litellm/rust_bridge/responses/route_host.py rename to litellm/rust_bridge/host/routes/responses.py diff --git a/tests/unit/rust_bridge/AGENTS.md b/tests/unit/rust_bridge/AGENTS.md index ab88d6b49ce..64c9fbbadd3 100644 --- a/tests/unit/rust_bridge/AGENTS.md +++ b/tests/unit/rust_bridge/AGENTS.md @@ -6,4 +6,4 @@ Call each path directly with an explicit decision instead. The Python path is th Rollout policy itself, meaning which rule matches and what `LITELLM_RUST` changes, belongs in `test_catalog.py`, `test_configuration.py` and `test_dispatch.py`, tested against rules the test builds rather than the shipped `catalog.RULES` -Before adding a test here, ask whether it checks something Rust cannot. A `route_host.py` module is the Python half of a native route: it projects Python-only state (the cost map, `litellm.*` settings, request kwargs) into the plain values the Rust side consumes, and maps native failures back onto public exceptions. Those projections are what belongs here, because a wrong key or an ignored provider prefix ships the wrong value to Rust and no Rust test sees it. `messages/test_route_host.py` shows the shape. Behavior that lives in Rust (a request transform given its inputs, header assembly, stream relay) is tested in the crate, and the route end to end is tested against a recording server in `tests/test_litellm_rust/`. A test that only re-checks a Python helper the route host happens to call is a duplicate of that helper's own test and should not be added +Before adding a test here, ask whether it checks something Rust cannot. A `host/routes/.py` module is the Python half of a native route: it projects Python-only state (the cost map, `litellm.*` settings, request kwargs) into the plain values the Rust side consumes, and maps native failures back onto public exceptions. Those projections are what belongs here, because a wrong key or an ignored provider prefix ships the wrong value to Rust and no Rust test sees it. `host/routes/test_messages.py` shows the shape. Behavior that lives in Rust (a request transform given its inputs, header assembly, stream relay) is tested in the crate, and the route end to end is tested against a recording server in `tests/test_litellm_rust/`. A test that only re-checks a Python helper the route host happens to call is a duplicate of that helper's own test and should not be added diff --git a/tests/unit/rust_bridge/responses/__init__.py b/tests/unit/rust_bridge/host/routes/__init__.py similarity index 100% rename from tests/unit/rust_bridge/responses/__init__.py rename to tests/unit/rust_bridge/host/routes/__init__.py diff --git a/tests/unit/rust_bridge/chat_completions/test_route_host.py b/tests/unit/rust_bridge/host/routes/test_chat_completions.py similarity index 95% rename from tests/unit/rust_bridge/chat_completions/test_route_host.py rename to tests/unit/rust_bridge/host/routes/test_chat_completions.py index 402c032f340..3496791f5a0 100644 --- a/tests/unit/rust_bridge/chat_completions/test_route_host.py +++ b/tests/unit/rust_bridge/host/routes/test_chat_completions.py @@ -4,7 +4,7 @@ from typing import Final import pytest import litellm -from litellm.rust_bridge.chat_completions.route_host import connection_defaults, response +from litellm.rust_bridge.host.routes.chat_completions import connection_defaults, response from litellm.types.utils import ModelResponse diff --git a/tests/unit/rust_bridge/messages/test_route_host.py b/tests/unit/rust_bridge/host/routes/test_messages.py similarity index 85% rename from tests/unit/rust_bridge/messages/test_route_host.py rename to tests/unit/rust_bridge/host/routes/test_messages.py index 43a17e5e593..abf0e055e73 100644 --- a/tests/unit/rust_bridge/messages/test_route_host.py +++ b/tests/unit/rust_bridge/host/routes/test_messages.py @@ -5,8 +5,8 @@ import pytest import litellm from litellm.constants import DEFAULT_REQUEST_TIMEOUT_SECONDS -from litellm.rust_bridge.messages import route_host -from litellm.rust_bridge.messages.route_host import response +from litellm.rust_bridge.host.routes import messages as messages_host +from litellm.rust_bridge.host.routes.messages import response def test_response_is_a_detached_public_messages_dict() -> None: @@ -36,7 +36,7 @@ def test_settings_project_caller_configuration_without_resolving_a_model(monkeyp monkeypatch.setattr(litellm, "request_timeout", 1800) monkeypatch.setattr(litellm, "request_timeout_explicitly_set", True) - projected: Final = route_host.settings({"drop_params": "true", "additional_drop_params": ["metadata.user_id"]}) + projected: Final = messages_host.settings({"drop_params": "true", "additional_drop_params": ["metadata.user_id"]}) assert projected == { "drop_params": True, @@ -60,7 +60,7 @@ def test_request_timeout_carries_only_a_configured_global_timeout( monkeypatch.setattr(litellm, "request_timeout", timeout) monkeypatch.setattr(litellm, "request_timeout_explicitly_set", explicitly_set) - assert route_host.settings({})["request_timeout"] == expected + assert messages_host.settings({})["request_timeout"] == expected @pytest.mark.parametrize( @@ -78,7 +78,7 @@ def test_drop_params_merges_the_global_flag_with_the_request( ) -> None: monkeypatch.setattr(litellm, "drop_params", global_flag) - assert route_host.settings(kwargs)["drop_params"] is expected + assert messages_host.settings(kwargs)["drop_params"] is expected @pytest.mark.parametrize( @@ -90,7 +90,7 @@ def test_drop_params_merges_the_global_flag_with_the_request( ], ) def test_additional_drop_params_keep_only_string_paths(configured: object, expected: tuple[str, ...]) -> None: - settings: Final = route_host.settings({"additional_drop_params": configured}) + settings: Final = messages_host.settings({"additional_drop_params": configured}) assert settings["additional_drop_params"] == expected @@ -117,19 +117,19 @@ def test_native_request_rejections_map_to_the_public_400() -> None: rejected: Final = ValueError("claude-sonnet-5 does not support top_k=5") rejected.messages_request_error = True # pyright: ignore[reportAttributeAccessIssue] # marker the native host sets - mapped: Final = route_host.map_failure(rejected, request.resolved, "anthropic") + mapped: Final = messages_host.map_failure(rejected, request.resolved, "anthropic") assert isinstance(mapped, litellm.BadRequestError) assert mapped.status_code == 400 assert "does not support top_k=5" in mapped.message assert mapped.model == "claude-sonnet-5" assert not isinstance( - route_host.map_failure(ValueError("plain"), request.resolved, "anthropic"), litellm.BadRequestError + messages_host.map_failure(ValueError("plain"), request.resolved, "anthropic"), litellm.BadRequestError ) def test_stream_hidden_params_projects_upstream_headers_the_way_the_python_handler_does() -> None: - hidden: Final = route_host.stream_hidden_params( + hidden: Final = messages_host.stream_hidden_params( (("request-id", "req_upstream_123"), ("x-ratelimit-remaining-requests", "41")) ) diff --git a/tests/unit/rust_bridge/ocr/test_route_host.py b/tests/unit/rust_bridge/host/routes/test_ocr.py similarity index 95% rename from tests/unit/rust_bridge/ocr/test_route_host.py rename to tests/unit/rust_bridge/host/routes/test_ocr.py index 879edb951fc..90040f55ef3 100644 --- a/tests/unit/rust_bridge/ocr/test_route_host.py +++ b/tests/unit/rust_bridge/host/routes/test_ocr.py @@ -3,8 +3,8 @@ from typing import Final import pytest import litellm -from litellm.rust_bridge.ocr.route_host import UpstreamFailure, map_failure -from litellm.rust_bridge.ocr.route_host import response as build_ocr_response +from litellm.rust_bridge.host.routes.ocr import UpstreamFailure, map_failure +from litellm.rust_bridge.host.routes.ocr import response as build_ocr_response from litellm.rust_bridge.public_call import NativeCall REQUEST: Final = NativeCall( diff --git a/tests/unit/rust_bridge/responses/test_route_host.py b/tests/unit/rust_bridge/host/routes/test_responses.py similarity index 97% rename from tests/unit/rust_bridge/responses/test_route_host.py rename to tests/unit/rust_bridge/host/routes/test_responses.py index d93ceb12ec9..99e3726c03a 100644 --- a/tests/unit/rust_bridge/responses/test_route_host.py +++ b/tests/unit/rust_bridge/host/routes/test_responses.py @@ -5,7 +5,7 @@ import pytest from pydantic import ValidationError import litellm -from litellm.rust_bridge.responses.route_host import connection_defaults, map_failure, response +from litellm.rust_bridge.host.routes.responses import connection_defaults, map_failure, response from litellm.types.llms.openai import ResponsesAPIResponse