- Job logs: each job's stdout/stderr is teed into outputs/<id>/job.log
(survives restarts) and served at GET /jobs/{id}/log as text/plain, so
full run logs can be shared for debugging and refinement.
- Model picker: GET /models proxies OpenRouter's public model list with
per-1M-token pricing (1h cache, 502 on failure); the UI shows a model
dropdown with costs when OpenRouter compute is selected, and the pick
overrides vision+text models for that job (Classic and Agent modes).
- Conflicts in the report view are grouped by discipline pair
(collapsible sections, severity-ordered within groups) instead of one
flat severity-only list.
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
from fastapi.testclient import TestClient
|
|
|
|
import backend.models as models
|
|
from backend import config
|
|
from backend.main import app
|
|
|
|
_PAYLOAD = {
|
|
"data": [
|
|
{
|
|
"id": "openai/gpt-4o",
|
|
"name": "GPT-4o",
|
|
"pricing": {"prompt": "0.0000025", "completion": "0.00001"},
|
|
"context_length": 128000,
|
|
},
|
|
{
|
|
"id": "google/gemini-2.5-pro",
|
|
"name": "Gemini 2.5 Pro",
|
|
"pricing": {"prompt": "0.00000125", "completion": "0.00001"},
|
|
"context_length": 1000000,
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
def _reset_cache():
|
|
models._cache["models"] = None
|
|
models._cache["at"] = 0.0
|
|
|
|
|
|
def test_models_endpoint_normalizes_pricing(monkeypatch):
|
|
_reset_cache()
|
|
monkeypatch.setattr(models, "_fetch_openrouter_models", lambda: _PAYLOAD["data"])
|
|
client = TestClient(app)
|
|
response = client.get("/models")
|
|
assert response.status_code == 200
|
|
body = response.json()
|
|
assert body["default"] == config.MODEL
|
|
assert body["default_text"] == config.TEXT_MODEL
|
|
by_id = {m["id"]: m for m in body["models"]}
|
|
assert by_id["openai/gpt-4o"]["prompt_usd_per_mtok"] == 2.5
|
|
assert by_id["openai/gpt-4o"]["completion_usd_per_mtok"] == 10.0
|
|
assert by_id["openai/gpt-4o"]["context_length"] == 128000
|
|
|
|
|
|
def test_models_endpoint_caches(monkeypatch):
|
|
_reset_cache()
|
|
calls = []
|
|
|
|
def fake_fetch():
|
|
calls.append(1)
|
|
return _PAYLOAD["data"]
|
|
|
|
monkeypatch.setattr(models, "_fetch_openrouter_models", fake_fetch)
|
|
client = TestClient(app)
|
|
assert client.get("/models").status_code == 200
|
|
assert client.get("/models").status_code == 200
|
|
assert len(calls) == 1
|
|
|
|
|
|
def test_models_endpoint_502_on_fetch_failure(monkeypatch):
|
|
_reset_cache()
|
|
monkeypatch.setattr(models, "_fetch_openrouter_models", lambda: None)
|
|
client = TestClient(app)
|
|
assert client.get("/models").status_code == 502
|