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:
@@ -0,0 +1,36 @@
|
||||
"""Stale parse-job recovery helpers (running/queued left behind after worker crash)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..models import ParseJob
|
||||
|
||||
# Jobs stuck in running/queued longer than this are considered abandoned.
|
||||
STALE_JOB_SECONDS = int(os.getenv("STALE_JOB_SECONDS", "900"))
|
||||
|
||||
|
||||
def _aware(dt: datetime | None) -> datetime | None:
|
||||
if dt is None:
|
||||
return None
|
||||
if dt.tzinfo is None:
|
||||
return dt.replace(tzinfo=timezone.utc)
|
||||
return dt
|
||||
|
||||
|
||||
def job_anchor_time(job: ParseJob) -> datetime | None:
|
||||
"""Best available timestamp for staleness (prefer last_run_at)."""
|
||||
return _aware(job.last_run_at) or _aware(getattr(job, "created_at", None))
|
||||
|
||||
|
||||
def is_stale_job(job: ParseJob, now: datetime | None = None, *, ttl: int | None = None) -> bool:
|
||||
if job.status not in ("running", "queued"):
|
||||
return False
|
||||
now = now or datetime.now(timezone.utc)
|
||||
anchor = job_anchor_time(job)
|
||||
if anchor is None:
|
||||
# No timestamp — treat long-lived running as stale immediately for recovery
|
||||
return job.status == "running"
|
||||
limit = ttl if ttl is not None else STALE_JOB_SECONDS
|
||||
return (now - anchor).total_seconds() >= limit
|
||||
Reference in New Issue
Block a user