Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/druks/harnesses/registry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from druks.user_settings.models import HarnessSettings

from .base import Harness
from .exceptions import HarnessError

Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions backend/druks/harnesses/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
7 changes: 2 additions & 5 deletions backend/druks/user_settings/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 13 additions & 3 deletions backend/tests/test_harness_model_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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