From 500d0ddea696ef212323e69fe5df2b7b52e4e37c Mon Sep 17 00:00:00 2001 From: "exe.dev user" Date: Thu, 6 Aug 2026 06:10:53 +0000 Subject: [PATCH] Clean up harness model validation --- backend/druks/harnesses/registry.py | 4 ++-- backend/druks/harnesses/subprocess.py | 4 ++-- backend/druks/user_settings/routes.py | 7 ++----- backend/tests/test_harness_model_routing.py | 16 +++++++++++++--- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/druks/harnesses/registry.py b/backend/druks/harnesses/registry.py index 9098cb74..9b96c7b1 100644 --- a/backend/druks/harnesses/registry.py +++ b/backend/druks/harnesses/registry.py @@ -1,5 +1,3 @@ -from druks.user_settings.models import HarnessSettings - from .base import Harness from .exceptions import HarnessError @@ -24,6 +22,8 @@ def get_harness_for_model(model: str) -> type[Harness]: A miss raises loudly; namespace-shaped models do not route until a fetch stores them on the harness settings row. """ + from druks.user_settings.models import HarnessSettings + for row in HarnessSettings.all(): if model == row.name or any(m["id"] == model for m in row.allowed_models): return row.harness diff --git a/backend/druks/harnesses/subprocess.py b/backend/druks/harnesses/subprocess.py index 15a8093d..8956ef34 100644 --- a/backend/druks/harnesses/subprocess.py +++ b/backend/druks/harnesses/subprocess.py @@ -16,9 +16,9 @@ def read_result_json(output_path: Path, *, name: str) -> dict[str, Any]: except FileNotFoundError as error: raise HarnessInvalidOutputError(f"{name} did not write result JSON.") from error - if text[:3] == "```": + if text.startswith("```"): lines = text.splitlines() - if lines and lines[0][:3] == "```": + if lines and lines[0].startswith("```"): lines = lines[1:] if lines and lines[-1].strip() == "```": lines = lines[:-1] diff --git a/backend/druks/user_settings/routes.py b/backend/druks/user_settings/routes.py index 74cec9de..3f1fe41c 100644 --- a/backend/druks/user_settings/routes.py +++ b/backend/druks/user_settings/routes.py @@ -71,16 +71,13 @@ async def update_harness_settings( if "model" in updates: try: resolved = get_harness_for_model(updates["model"]) + if resolved.name != harness.name: + raise HarnessError except HarnessError as exc: raise HTTPException( status_code=422, detail=f"{updates['model']!r} is not a {harness.name} model.", ) from exc - if resolved.name != harness.name: - raise HTTPException( - status_code=422, - detail=f"{updates['model']!r} is not a {harness.name} model.", - ) _validate_effort(updates.get("effort")) _validate_timeout(updates.get("timeout")) if updates: diff --git a/backend/tests/test_harness_model_routing.py b/backend/tests/test_harness_model_routing.py index b11e7f50..192edc38 100644 --- a/backend/tests/test_harness_model_routing.py +++ b/backend/tests/test_harness_model_routing.py @@ -44,15 +44,25 @@ def test_settings_reject_model_missing_from_lists_returns_422(druks_db): assert error.value.detail == "No installed harness runs model 'llama-3-70b'." -async def test_settings_reject_model_from_another_harness_returns_422(druks_db): +@pytest.mark.parametrize( + ("model", "detail"), + [ + ("gpt-5.5", "'gpt-5.5' is not a claude model."), + ("llama-3-70b", "'llama-3-70b' is not a claude model."), + ], +) +async def test_settings_reject_invalid_model_returns_422(druks_db, model, detail): account = Account.get_or_create("op@example.com") + settings = HarnessSettings.require("claude") + original_model = settings.model with pytest.raises(HTTPException) as error: await update_harness_settings( name="claude", - body=HarnessUpdate(model="gpt-5.5"), + body=HarnessUpdate(model=model), account=account, ) assert error.value.status_code == 422 - assert "is not a claude model" in error.value.detail + assert error.value.detail == detail + assert settings.model == original_model