Add per-job run logs and separate vision/text model selection.
Docker Release / build-and-push (push) Successful in 1m27s
Docker Release / release (push) Skipped

Capture pipeline stdout into job.log + API/UI so failed runs can be reviewed, and let users pick OpenRouter vision vs text models independently.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-31 14:56:48 -05:00
co-authored by Cursor
parent e30522af9a
commit a6b0c8fdfa
8 changed files with 586 additions and 44 deletions
+18 -3
View File
@@ -23,6 +23,9 @@ _clients: Dict[str, OpenAI] = {}
# call_json when routing a no-image (text) call. Module-global mirrors the
# set_stage/cost pattern (single-user tool).
_text_local = False
# Optional per-run model overrides from the UI (empty = use config defaults).
_vision_model_override: Optional[str] = None
_text_model_override: Optional[str] = None
def set_text_backend(local: bool) -> None:
@@ -30,6 +33,13 @@ def set_text_backend(local: bool) -> None:
global _text_local
_text_local = bool(local)
def set_model_overrides(vision: Optional[str] = None, text: Optional[str] = None) -> None:
"""Per-run OpenRouter model picks. None/blank clears back to config defaults."""
global _vision_model_override, _text_model_override
_vision_model_override = (vision or "").strip() or None
_text_model_override = (text or "").strip() or None
# --- per-job cost accounting -------------------------------------------------
# OpenRouter returns the real USD cost of each call when we request usage
# accounting. We accumulate it in a module-level counter; the runner resets it
@@ -167,12 +177,16 @@ def _resolve_backend(has_images: bool, model_override: Optional[str]) -> Dict[st
return {
"base_url": config.LOCAL_BASE_URL,
"api_key": config.LOCAL_API_KEY,
"model": model_override or config.LOCAL_TEXT_MODEL or config.TEXT_MODEL,
"model": (model_override or _text_model_override
or config.LOCAL_TEXT_MODEL or config.TEXT_MODEL),
"usage": False, # local has no OpenRouter usage accounting
"local": True,
}
# Vision, or text-on-OpenRouter (default / fallback).
default_model = config.MODEL if has_images else config.TEXT_MODEL
if has_images:
default_model = _vision_model_override or config.MODEL
else:
default_model = _text_model_override or config.TEXT_MODEL
return {
"base_url": config.AI_BASE_URL,
"api_key": config.AI_API_KEY,
@@ -340,7 +354,8 @@ def call_json(
_models["text_local"].add(be["model"])
_models["fallback_count"] += 1
be = {"base_url": config.AI_BASE_URL, "api_key": config.AI_API_KEY,
"model": config.TEXT_MODEL, "usage": True, "local": False}
"model": _text_model_override or config.TEXT_MODEL,
"usage": True, "local": False}
cache_key = None # don't cache fallback under the local-model key
fell_back = True
continue