fix(admin): resolve vendor/model-shaped ids in the availability routes #42
@@ -1392,7 +1392,7 @@ def build_router(
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.get("/api/models/{model_id}/{provider}")
|
||||
@router.get("/api/models/{model_id:path}/{provider}")
|
||||
def admin_model_detail(model_id: str, provider: str) -> dict:
|
||||
"""A single model's detail (same shape as one /api/models element)."""
|
||||
conn = _db_callable()
|
||||
@@ -1404,7 +1404,7 @@ def build_router(
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.post("/api/models/{model_id}/{provider}/availability")
|
||||
@router.post("/api/models/{model_id:path}/{provider}/availability")
|
||||
def admin_set_availability(
|
||||
model_id: str,
|
||||
provider: str,
|
||||
@@ -1502,7 +1502,7 @@ def build_router(
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.delete("/api/models/{model_id}/{provider}/availability")
|
||||
@router.delete("/api/models/{model_id:path}/{provider}/availability")
|
||||
def admin_delete_availability(model_id: str, provider: str) -> dict:
|
||||
"""Delete an admin availability override for a model.
|
||||
|
||||
|
||||
@@ -301,6 +301,67 @@ def test_delete_override_reverts_to_db_value(seeded_client):
|
||||
assert data["availability"] == "active"
|
||||
|
||||
|
||||
# --- vendor/model-shaped ids (OpenRouter's native id format) ---------------
|
||||
#
|
||||
# Every OpenRouter model id contains a slash (e.g. "google/lyria-3-pro-preview"),
|
||||
# and the browser dutifully encodeURIComponent()s it -- but Starlette decodes
|
||||
# %2F back into a literal '/' before matching a plain `str` path parameter
|
||||
# against the route, so the request splits into extra path segments and 404s.
|
||||
# Confirmed live: every admin availability-override click on an OpenRouter
|
||||
# model failed this way until model_id was declared `:path`.
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def slash_id_client(tmp_path, monkeypatch):
|
||||
"""A TestClient seeded with one vendor/model-shaped id."""
|
||||
conn = _make_db(tmp_path)
|
||||
_seed_models(conn, ("google/lyria-3-pro-preview",))
|
||||
conn.close()
|
||||
|
||||
monkeypatch.setattr(dispatcher.cfg.database, "path", str(tmp_path / "test.db"))
|
||||
monkeypatch.setattr(dispatcher.cfg.verification, "local_llm_enabled", False)
|
||||
monkeypatch.setattr(dispatcher.cfg.routing, "require_vision", False)
|
||||
monkeypatch.setenv("NEURALWATT_API_KEY", "test-key")
|
||||
|
||||
with TestClient(dispatcher.app) as client:
|
||||
yield client
|
||||
|
||||
|
||||
def test_model_detail_resolves_slash_containing_model_id(slash_id_client):
|
||||
"""GET /admin/api/models/{model_id}/{provider} works for a vendor/model id,
|
||||
sent the way a browser actually sends it: %2F for the embedded slash."""
|
||||
resp = slash_id_client.get(
|
||||
"/admin/api/models/google%2Flyria-3-pro-preview/neuralwatt"
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["model_id"] == "google/lyria-3-pro-preview"
|
||||
|
||||
|
||||
def test_post_override_resolves_slash_containing_model_id(slash_id_client):
|
||||
"""POST .../availability works for a vendor/model id (%2F-encoded)."""
|
||||
resp = slash_id_client.post(
|
||||
"/admin/api/models/google%2Flyria-3-pro-preview/neuralwatt/availability",
|
||||
json={"availability": "deprecated", "reason": "non-chat model"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["model_id"] == "google/lyria-3-pro-preview"
|
||||
assert data["effective_availability"] == "deprecated"
|
||||
|
||||
|
||||
def test_delete_override_resolves_slash_containing_model_id(slash_id_client):
|
||||
"""DELETE .../availability works for a vendor/model id (%2F-encoded)."""
|
||||
slash_id_client.post(
|
||||
"/admin/api/models/google%2Flyria-3-pro-preview/neuralwatt/availability",
|
||||
json={"availability": "deprecated", "reason": "test"},
|
||||
)
|
||||
resp = slash_id_client.delete(
|
||||
"/admin/api/models/google%2Flyria-3-pro-preview/neuralwatt/availability"
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["effective_availability"] == "active"
|
||||
|
||||
|
||||
def test_model_list_reflects_effective_availability(seeded_client):
|
||||
"""GET /admin/api/models includes effective_availability and is_overridden."""
|
||||
seeded_client.post(
|
||||
|
||||
Reference in New Issue
Block a user