Add job run logs, OpenRouter model picker, and discipline grouping.
Docker Release / build-and-push (push) Successful in 55s
Docker Release / release (push) Skipped

- 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.
This commit is contained in:
John Wilganowski
2026-07-28 21:23:42 +00:00
parent 4ecc7c5cef
commit afa1089311
8 changed files with 441 additions and 55 deletions
+13 -2
View File
@@ -24,6 +24,16 @@ _clients: Dict[str, OpenAI] = {}
# set_stage/cost pattern (single-user tool).
_text_local = False
# Per-job model override (user picked a model in the UI). Same module-global
# pattern: set by the job runner before the pipeline starts, cleared after.
_model_override: Optional[str] = None
def set_model_override(model: Optional[str]) -> None:
"""Override the model for all OpenRouter calls (vision + text), or None to clear."""
global _model_override
_model_override = (model or "").strip() or None
def set_text_backend(local: bool) -> None:
"""Choose whether text (no-image) calls go to the local endpoint this run."""
@@ -171,12 +181,13 @@ def _resolve_backend(has_images: bool, model_override: Optional[str]) -> Dict[st
"usage": False, # local has no OpenRouter usage accounting
"local": True,
}
# Vision, or text-on-OpenRouter (default / fallback).
# Vision, or text-on-OpenRouter (default / fallback). A per-job override
# (user's UI model pick) wins over per-call and env defaults.
default_model = config.MODEL if has_images else config.TEXT_MODEL
return {
"base_url": config.AI_BASE_URL,
"api_key": config.AI_API_KEY,
"model": model_override or default_model,
"model": _model_override or model_override or default_model,
"usage": True,
"local": False,
}