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
+34 -8
View File
@@ -15,6 +15,10 @@ class TelegramSourceConfig(BaseModel):
extract_schema: dict[str, str] | None = None
instruction: str | None = None
heuristic_profile: dict | None = None
# Post-extract gate for extract_mode=llm (empty = only is_event filter)
required_fields: list[str] = Field(default_factory=list)
# Split one post into N events when extract_mode=llm (opt-in)
multi_event: bool = False
sample_post: str | None = None # audit / re-generate; not required at runtime
@field_validator("channel")
@@ -22,15 +26,37 @@ class TelegramSourceConfig(BaseModel):
def strip_channel(cls, value: str) -> str:
return value.strip()
@model_validator(mode="after")
def profile_requires_rules(self) -> "TelegramSourceConfig":
if self.extract_mode != "profile":
return self
if not self.heuristic_profile:
raise ValueError("heuristic_profile required when extract_mode=profile")
from contracts.heuristic_profile import HeuristicProfile
@field_validator("required_fields")
@classmethod
def known_required(cls, value: list[str]) -> list[str]:
from contracts.heuristic_profile import TARGET_FIELDS
HeuristicProfile.model_validate(self.heuristic_profile)
seen: list[str] = []
unknown: list[str] = []
for name in value:
if name not in TARGET_FIELDS:
unknown.append(name)
continue
if name not in seen:
seen.append(name)
if unknown:
raise ValueError(f"Unknown required_fields: {sorted(set(unknown))}")
return seen
@model_validator(mode="after")
def validate_extract_mode(self) -> "TelegramSourceConfig":
if self.extract_mode == "profile":
if not self.heuristic_profile:
raise ValueError("heuristic_profile required when extract_mode=profile")
from contracts.heuristic_profile import HeuristicProfile
HeuristicProfile.model_validate(self.heuristic_profile)
elif self.extract_mode == "llm" and self.extract_schema is not None:
from contracts.heuristic_profile import TARGET_FIELDS
unknown = set(self.extract_schema) - set(TARGET_FIELDS)
if unknown:
raise ValueError(f"Unknown extract_schema fields: {sorted(unknown)}")
return self