fix(router): reject a blank Jev api_key so it cannot pair with a caller-chosen api_base

This commit is contained in:
mateo-berri 2026-09-17 16:38:54 -07:00
parent 7f581f6bc7
commit 1e7e5b695f
3 changed files with 24 additions and 3 deletions

View file

@ -697,6 +697,13 @@ class JevClassifierConfig(BaseModel):
raise ValueError("jev_classifier_config.instructions must be non-empty; omit it to use the default")
return value
@field_validator("api_key")
@classmethod
def _reject_blank_api_key(cls, value: str | None) -> str | None:
if value is not None and not value.strip():
raise ValueError("jev_classifier_config.api_key must be non-empty; omit it to use TYPESAFE_API_KEY")
return value
@model_validator(mode="after")
def _keep_the_environment_key_on_the_environment_base(self) -> "JevClassifierConfig":
if self.api_base is not None and self.api_key is None:

View file

@ -137,6 +137,7 @@ def test_tier_config_is_normalized_and_unknown_router_extras_are_rejected() -> N
({"api_base": "https://collector.invalid"}, "jev_classifier_config"),
({"api_key": "sk-member"}, "api_key"),
({"api_base": "https://collector.invalid", "api_key": "sk-member"}, "api_key"),
({"api_base": "https://collector.invalid", "api_key": ""}, "jev_classifier_config.api_key"),
],
)
def test_members_cannot_move_the_jev_classifier_off_the_proxys_typesafe_account(

View file

@ -47,10 +47,23 @@ def test_jev_instructions_reject_blank_values() -> None:
JevClassifierConfig(instructions=" \t")
def test_jev_api_base_without_its_own_key_is_rejected_so_the_environment_key_stays_home() -> None:
with pytest.raises(ValueError, match=r"api_base requires jev_classifier_config\.api_key"):
@pytest.mark.parametrize(
("missing_key", "rejection"),
[
({}, r"api_base requires jev_classifier_config\.api_key"),
({"api_key": ""}, r"api_key must be non-empty"),
({"api_key": " "}, r"api_key must be non-empty"),
],
)
def test_jev_api_base_without_its_own_key_is_rejected_so_the_environment_key_stays_home(
missing_key: Mapping[str, str], rejection: str
) -> None:
with pytest.raises(ValueError, match=rejection):
ComplexityRouterConfig.model_validate(
{"classifier_type": "jev", "jev_classifier_config": {"api_base": "https://collector.invalid"}}
{
"classifier_type": "jev",
"jev_classifier_config": {"api_base": "https://collector.invalid", **missing_key},
}
)
paired: Final = JevClassifierConfig(api_base="https://eu.typesafe.invalid", api_key="sk-own")
assert (paired.api_base, paired.api_key) == ("https://eu.typesafe.invalid", "sk-own")