summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-28 17:39:32 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-28 17:39:32 +0900
commitb4f3c99a7d458d32517077ef77b0e5676020b9a2 (patch)
tree5c0db0afffef74e1bc28cf049f94fdb746dd1187
parent9e430700cc58adbed5baf110961c08cbcb6a7135 (diff)
モデル名によってthinkingトグルがグレーアウトする不具合を修正 モデル名にスラッシュが含まれている場合にStarlette側でエスケープされる問題を回避
-rw-r--r--src/ollama_web/app.py7
-rw-r--r--tests/test_models.py54
2 files changed, 57 insertions, 4 deletions
diff --git a/src/ollama_web/app.py b/src/ollama_web/app.py
index 701cc3c..eeb3bae 100644
--- a/src/ollama_web/app.py
+++ b/src/ollama_web/app.py
@@ -38,6 +38,7 @@ class NoCacheStaticFiles(StaticFiles):
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
return response
+
_STATIC_DIR = Path(__file__).resolve().parent / "static"
@@ -47,9 +48,7 @@ def create_app() -> Starlette:
log_path = Path(settings.data_dir).resolve().parent / "server.log"
log_path.parent.mkdir(parents=True, exist_ok=True)
handler = logging.FileHandler(str(log_path), encoding="utf-8")
- handler.setFormatter(
- logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
- )
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
root = logging.getLogger()
root.setLevel(logging.INFO)
root.addHandler(handler)
@@ -63,7 +62,7 @@ def create_app() -> Starlette:
Route("/api/chat", chat_route.chat, methods=["POST"], name="chat"),
Route("/api/models", models_route.get_models, methods=["GET"], name="models"),
Route(
- "/api/models/{model}/capabilities",
+ "/api/models/{model:path}/capabilities",
models_route.get_model_capabilities,
methods=["GET"],
name="model_capabilities",
diff --git a/tests/test_models.py b/tests/test_models.py
new file mode 100644
index 0000000..8cc2738
--- /dev/null
+++ b/tests/test_models.py
@@ -0,0 +1,54 @@
+"""Tests for model listing and capability routes."""
+
+from __future__ import annotations
+
+from starlette.testclient import TestClient
+
+from ollama_web.app import create_app
+from ollama_web.config import settings
+
+
+def _login_client(client: TestClient) -> str:
+ """Authenticate the test client and return the CSRF token."""
+ login = client.post("/api/auth/login", json={"pin": str(settings.pin)})
+ return str(login.headers["x-csrf-token"])
+
+
+def test_model_capabilities_route_supports_slash_in_model_name() -> None:
+ """HuggingFace-style model names contain slashes and must reach the API.
+
+ Starlette's default path converter does not allow ``/`` inside a segment,
+ so the route uses the ``path`` converter. Encoding ``%2F`` must decode to
+ the full model name, not split the URL into multiple segments.
+ """
+ with TestClient(create_app()) as client:
+ csrf = _login_client(client)
+
+ slash_model = "hf.co/hotdogs/gemma-4-E4B-it-ultra-uncensored-heretic-GGUF:Q5_K_M"
+ encoded = slash_model.replace("/", "%2F").replace(":", "%3A")
+ response = client.get(
+ f"/api/models/{encoded}/capabilities",
+ headers={"X-CSRF-Token": csrf},
+ )
+ assert response.status_code == 200
+ payload = response.json()
+ assert "capabilities" in payload
+ assert all(
+ cap in [c.lower() for c in payload["capabilities"]]
+ for cap in ("completion", "vision", "tools", "thinking")
+ )
+
+
+def test_model_capabilities_route_still_works_without_slashes() -> None:
+ """Model names without slashes (e.g. cloud aliases) must keep working."""
+ with TestClient(create_app()) as client:
+ csrf = _login_client(client)
+
+ response = client.get(
+ "/api/models/glm-5.2%3Acloud/capabilities",
+ headers={"X-CSRF-Token": csrf},
+ )
+ assert response.status_code == 200
+ payload = response.json()
+ assert "capabilities" in payload
+ assert "completion" in [c.lower() for c in payload["capabilities"]]