Add reusable LLM parser profiles with multi-event extract.

Support kind=llm profiles (instruction/schema), optional multi-event posts via #eN URLs, and recover stale running/queued parse jobs after worker crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-13 20:22:38 +03:00
co-authored by Cursor
parent 5811ecb134
commit 3f9dc6643b
27 changed files with 1334 additions and 205 deletions
+17 -3
View File
@@ -87,7 +87,11 @@ def create_parse_job(payload: ParseJobCreate, db: Session = Depends(get_db)):
raise HTTPException(status_code=404, detail="Profile not found")
if not channel:
raise HTTPException(status_code=404, detail="Channel not found")
if not profile.heuristic_profile:
kind = (profile.kind or "heuristic").strip().lower()
if kind == "llm":
if not profile.llm_profile:
raise HTTPException(status_code=400, detail="LLM profile has no llm_profile")
elif not profile.heuristic_profile:
raise HTTPException(status_code=400, detail="Profile has no heuristic_profile")
if not channel.is_active:
raise HTTPException(status_code=400, detail="Channel is inactive")
@@ -146,7 +150,10 @@ def retry_parse_job(job_id: int, db: Session = Depends(get_db)):
job = db.query(ParseJob).filter(ParseJob.id == job_id).first()
if not job:
raise HTTPException(status_code=404, detail="Job not found")
if job.status in ("queued", "running"):
from ..services.job_stale import is_stale_job
if job.status in ("queued", "running") and not is_stale_job(job):
raise HTTPException(status_code=409, detail="Job is already running or queued")
job.status = "queued"
@@ -218,7 +225,14 @@ def delete_parse_job(job_id: int, db: Session = Depends(get_db)):
if not job:
raise HTTPException(status_code=404, detail="Job not found")
if job.status == "running":
raise HTTPException(status_code=409, detail="Cannot delete a running job")
from ..services.job_stale import is_stale_job
if not is_stale_job(job):
raise HTTPException(status_code=409, detail="Cannot delete a running job")
# Stale running — allow delete after marking failed for audit trail
job.status = "failed"
job.last_error = "Deleted while stale running"
db.commit()
db.delete(job)
db.commit()