Add job run logs, OpenRouter model picker, and discipline grouping.
- 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:
+23
-1
@@ -41,6 +41,27 @@ def health():
|
||||
"email_configured": bool(config.SMTP_HOST and config.SMTP_USER and config.SMTP_PASSWORD)}
|
||||
|
||||
|
||||
@app.get("/models")
|
||||
def list_models():
|
||||
"""Available OpenRouter models with per-1M-token pricing for the UI picker."""
|
||||
from backend.models import fetch_models
|
||||
models = fetch_models()
|
||||
if models is None:
|
||||
raise HTTPException(status_code=502,
|
||||
detail="Could not fetch the model list from OpenRouter")
|
||||
return {"models": models, "default": config.MODEL, "default_text": config.TEXT_MODEL}
|
||||
|
||||
|
||||
@app.get("/jobs/{job_id}/log")
|
||||
def job_log(job_id: str):
|
||||
"""The full captured stdout/stderr log of a job run (persists on disk)."""
|
||||
path = os.path.join(config.OUTPUT_DIR, job_id, "job.log")
|
||||
if not os.path.isfile(path):
|
||||
raise HTTPException(status_code=404, detail="Log not found for this job")
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
return Response(content=f.read(), media_type="text/plain")
|
||||
|
||||
|
||||
@app.post("/check")
|
||||
async def check(
|
||||
file: UploadFile = File(...),
|
||||
@@ -51,6 +72,7 @@ async def check(
|
||||
work_type: Optional[str] = Form(None),
|
||||
text_local: bool = Form(False),
|
||||
pipeline_mode: str = Form("classic"),
|
||||
model: Optional[str] = Form(None),
|
||||
):
|
||||
"""
|
||||
Accept a PDF, start a background conflict check, and return a job_id
|
||||
@@ -86,7 +108,7 @@ async def check(
|
||||
}
|
||||
job_id = create_job(tmp_path, source_filename=file.filename, email=email,
|
||||
project_input=project_input, text_local=text_local,
|
||||
pipeline_mode=pipeline_mode)
|
||||
pipeline_mode=pipeline_mode, model=model)
|
||||
return JSONResponse({
|
||||
"job_id": job_id,
|
||||
"status": "queued",
|
||||
|
||||
Reference in New Issue
Block a user