fix(rust_bridge): let LITELLM_RUST win over litellm.rust() for optional tiers

Parse the switch with pydantic TypeAdapter(bool) so 1/true/yes/on and 0/false/no/off all work, and treat an unparseable value as unset instead of off. PYTHON_ONLY and RUST_REQUIRED still ignore both switches

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Yujong Lee 2026-09-16 19:45:11 +00:00
parent 358d767c9e
commit f9d423827f
4 changed files with 73 additions and 37 deletions

View file

@ -4,10 +4,11 @@ import os
from enum import Enum, auto
from typing import Final
from pydantic import TypeAdapter, ValidationError
from typing_extensions import assert_never
_TRUE_ENV_VALUES: Final = frozenset({"1", "true", "yes", "on"})
_GLOBAL_ENV_NAME: Final = "LITELLM_RUST"
_ENV_BOOL: Final = TypeAdapter(bool)
class Rollout(Enum):
@ -34,7 +35,10 @@ _CONFIGURATION: Final = _RustConfiguration()
def _parse_env_bool(value: str | None) -> bool | None:
if value is None:
return None
return value.strip().lower() in _TRUE_ENV_VALUES
try:
return _ENV_BOOL.validate_python(value.strip())
except ValidationError:
return None
def decide(
@ -50,10 +54,10 @@ def decide(
return Decision.RUST_REQUIRED
case Rollout.RUST_OPT_IN | Rollout.RUST_OPT_OUT:
switch: Final = (
process_override
if process_override is not None
else environment_override
environment_override
if environment_override is not None
else process_override
if process_override is not None
else rollout is Rollout.RUST_OPT_OUT
)
return Decision.RUST_WITH_FALLBACK if switch else Decision.PYTHON
@ -80,6 +84,7 @@ def reset_rust_configuration() -> None:
def rust(enabled: bool | None) -> None:
"""Set the process override for optional Rust paths.
``PYTHON_ONLY`` and ``RUST_REQUIRED`` routes in the catalog ignore this switch.
``PYTHON_ONLY`` and ``RUST_REQUIRED`` routes in the catalog ignore this switch,
and an explicit ``LITELLM_RUST`` environment value wins over it.
"""
_CONFIGURATION.override = enabled

View file

@ -33,12 +33,14 @@ Decision: Final = configuration.Decision
(Rollout.RUST_REQUIRED, False, False, Decision.RUST_REQUIRED),
(Rollout.RUST_OPT_IN, None, None, Decision.PYTHON),
(Rollout.RUST_OPT_IN, None, True, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_IN, True, False, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_IN, False, True, Decision.PYTHON),
(Rollout.RUST_OPT_IN, True, None, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_IN, True, False, Decision.PYTHON),
(Rollout.RUST_OPT_IN, False, True, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_OUT, None, None, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_OUT, None, False, Decision.PYTHON),
(Rollout.RUST_OPT_OUT, False, True, Decision.PYTHON),
(Rollout.RUST_OPT_OUT, True, False, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_OUT, False, None, Decision.PYTHON),
(Rollout.RUST_OPT_OUT, False, True, Decision.RUST_WITH_FALLBACK),
(Rollout.RUST_OPT_OUT, True, False, Decision.PYTHON),
),
)
def test_decide_precedence(
@ -68,50 +70,53 @@ def test_opt_out_route_configuration(
expected: Final = (
Decision.RUST_WITH_FALLBACK
if process is True or (process is None and environment not in frozenset({"0", "off"}))
if environment == "1" or (environment is None and process is not False)
else Decision.PYTHON
)
assert configuration.decision(Rollout.RUST_OPT_OUT) is expected
def test_process_override_wins_over_environment(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_RUST", "0")
@pytest.mark.parametrize(
("environment", "process", "expected"),
(
*((value, True, False) for value in ("0", "false", "False", "no", "off", "f", "n", " 0 ")),
*((value, False, True) for value in ("1", "true", "TRUE", "yes", "on", "t", "y", " 1 ")),
),
)
def test_environment_wins_over_process_override(
monkeypatch: pytest.MonkeyPatch, environment: str, process: bool, expected: bool
) -> None:
monkeypatch.setenv("LITELLM_RUST", environment)
configuration.rust(process)
assert configuration.rust_enabled() is expected
def test_process_override_applies_when_environment_is_unset() -> None:
configuration.rust(True)
assert configuration.rust_enabled() is True
def test_global_environment_accepts_explicit_false(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_RUST", "off")
assert configuration.rust_enabled() is False
@pytest.mark.parametrize("value", ("", " ", "sometimes", "2"))
def test_invalid_environment_value_disables_rust(monkeypatch: pytest.MonkeyPatch, value: str) -> None:
def test_invalid_environment_value_is_ignored(monkeypatch: pytest.MonkeyPatch, value: str) -> None:
monkeypatch.setenv("LITELLM_RUST", value)
assert configuration.rust_enabled() is False
def test_process_override_and_reset_apply_to_existing_threads(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_RUST", "1")
with ThreadPoolExecutor(max_workers=1) as executor:
assert executor.submit(configuration.rust_enabled).result() is True
configuration.rust(False)
assert executor.submit(configuration.rust_enabled).result() is False
configuration.reset_rust_configuration()
assert executor.submit(configuration.rust_enabled).result() is True
def test_explicit_override_precedes_invalid_environment(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("LITELLM_RUST", "sometimes")
assert configuration.decision(Rollout.RUST_OPT_OUT) is Decision.RUST_WITH_FALLBACK
configuration.rust(True)
assert configuration.rust_enabled() is True
def test_process_override_and_reset_apply_to_existing_threads() -> None:
with ThreadPoolExecutor(max_workers=1) as executor:
assert executor.submit(configuration.rust_enabled).result() is False
configuration.rust(True)
assert executor.submit(configuration.rust_enabled).result() is True
configuration.reset_rust_configuration()
assert executor.submit(configuration.rust_enabled).result() is False
@pytest.mark.parametrize(("value", "expected"), (("1", "True"), ("0", "False")))
def test_environment_controls_startup(value: str, expected: str) -> None:
environment: Final = {**os.environ, "LITELLM_RUST": value}

View file

@ -143,7 +143,7 @@ def test_public_missing_required_argument_error_does_not_depend_on_native_select
@pytest.mark.asyncio
@pytest.mark.parametrize("asynchronous", [False, True])
@pytest.mark.parametrize("enabled", [False, None])
@pytest.mark.parametrize("enabled", [False, True, None])
async def test_environment_opt_out_never_loads_native(
monkeypatch: pytest.MonkeyPatch, asynchronous: bool, enabled: bool | None
) -> None:

View file

@ -114,6 +114,32 @@ def test_environment_switch_enables_opt_in_route(monkeypatch: pytest.MonkeyPatch
assert calls.calls == (RUST,)
@pytest.mark.parametrize(
("rollout", "environment", "switch", "expected"),
(
(Rollout.RUST_OPT_IN, "0", True, (PYTHON,)),
(Rollout.RUST_OPT_OUT, "0", True, (PYTHON,)),
(Rollout.RUST_OPT_IN, "1", False, (RUST,)),
(Rollout.RUST_OPT_OUT, "1", False, (RUST,)),
(Rollout.RUST_REQUIRED, "0", False, (RUST,)),
(Rollout.PYTHON_ONLY, "1", True, (PYTHON,)),
),
)
def test_environment_switch_wins_over_process_switch(
monkeypatch: pytest.MonkeyPatch,
rollout: Rollout,
environment: str,
switch: bool,
expected: tuple[str, ...],
) -> None:
calls: Final = recorder()
monkeypatch.setenv("LITELLM_RUST", environment)
configuration.rust(switch)
assert run(rollout, calls) == expected[-1]
assert calls.calls == expected
def test_context_outside_rule_stays_on_python() -> None:
calls: Final = recorder()
configuration.rust(True)