Merge main: dual model dropdowns + richer job logs, adapted for agent-mode.
- llm.py: set_model_overrides(vision, text) replaces the single job override;
UI picks still beat per-call agent model args, but never name the hybrid
local model (avoids main's hybrid footgun); local->cloud fallback uses the
text pick.
- jobs.py: timestamped line-split tee (job_log.py), in-memory log + log_tail
polls, full log on terminal states (done/error/needs_review/finalization_error),
log-only disk recovery, error email links to the run log, and failed runs now
append the full traceback to job.log. Keeps pipeline_mode, job.json, and the
review gate.
- models.py: vision/text split via architecture modalities, pricing kept;
/models returns {vision, text, defaults}; /check takes vision_model/text_model
(replacing model); /health adds text_model. models_catalog.py dropped.
- UI: two priced dropdowns (OpenRouter compute only) + live run-log panel.
- Tests updated for dual overrides and the /models shape; new coverage for
traceback capture and local-model immunity.
This commit is contained in:
@@ -42,7 +42,9 @@ from backend.pipeline.risk import score_and_prioritize
|
||||
from backend.pipeline.rfi import generate_rfis
|
||||
from backend.pipeline.report import build_report, to_markdown
|
||||
from backend.pipeline._stage import validate_issue
|
||||
from backend.llm import reset_cost, get_cost, set_stage, set_text_backend
|
||||
from backend.llm import (
|
||||
reset_cost, get_cost, set_stage, set_text_backend, set_model_overrides,
|
||||
)
|
||||
|
||||
|
||||
def run_pipeline(
|
||||
@@ -52,6 +54,8 @@ def run_pipeline(
|
||||
project_input: Optional[Dict] = None,
|
||||
source_name: Optional[str] = None,
|
||||
text_local: bool = False,
|
||||
vision_model: Optional[str] = None,
|
||||
text_model: Optional[str] = None,
|
||||
) -> Dict:
|
||||
"""
|
||||
Run the full QAQC pipeline on one PDF and return the report dict.
|
||||
@@ -59,6 +63,9 @@ def run_pipeline(
|
||||
project_input: optional intake fields (project_name, address, occupancy,
|
||||
work_type). Cover-sheet-derived values fill any gaps; intake fields win.
|
||||
|
||||
vision_model / text_model: optional per-run OpenRouter (or local text)
|
||||
model overrides from the UI. Blank/None keeps config defaults.
|
||||
|
||||
If out_dir is given, writes conflicts.json, report.md, and the intermediate
|
||||
artifacts (assertions.json, clusters.json, and one json per QAQC stage).
|
||||
"""
|
||||
@@ -70,7 +77,29 @@ def run_pipeline(
|
||||
|
||||
reset_cost()
|
||||
set_text_backend(text_local)
|
||||
set_model_overrides(vision_model, text_model)
|
||||
if vision_model or text_model:
|
||||
print(f"[Runner] model overrides: vision={vision_model or '(default)'} "
|
||||
f"text={text_model or '(default)'}")
|
||||
|
||||
try:
|
||||
return _run_stages(
|
||||
pdf_path, out_dir, stage, project_input, source_name, text_local,
|
||||
)
|
||||
finally:
|
||||
# Don't leak per-run picks into a later overlapping/CLI call.
|
||||
set_model_overrides(None, None)
|
||||
set_text_backend(False)
|
||||
|
||||
|
||||
def _run_stages(
|
||||
pdf_path: str,
|
||||
out_dir: Optional[str],
|
||||
stage: Callable[[str], None],
|
||||
project_input: Optional[Dict],
|
||||
source_name: Optional[str],
|
||||
text_local: bool,
|
||||
) -> Dict:
|
||||
stage("PDF -> images")
|
||||
pages = convert_pdf_to_images(pdf_path)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user