Add interval-aware polling and a 'cycle' command for scheduling

- poll_due_sources(): polls only sources whose last successful poll is older
  than their poll_interval_minutes (or never polled), finally giving that
  config field meaning.
- classify gains only_unclassified to spend the LLM solely on new (heuristic)
  articles, so a frequent scheduled run stays cheap.
- 'cycle' command runs poll-due -> classify-new -> rebuild-today's-brief, with
  each step non-fatal so a down model endpoint or empty day never aborts it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-05-30 14:13:00 +00:00
parent 2f4bdf2d00
commit 2414fd3ccb
3 changed files with 92 additions and 9 deletions
+13 -2
View File
@@ -200,8 +200,11 @@ def classify_articles(
limit: int,
include_rejected: bool = False,
dry_run: bool = False,
only_unclassified: bool = False,
) -> list[tuple[int, dict]]:
rows = _classification_candidates(conn, limit=limit, include_rejected=include_rejected)
rows = _classification_candidates(
conn, limit=limit, include_rejected=include_rejected, only_unclassified=only_unclassified
)
results = []
for row in rows:
try:
@@ -297,8 +300,16 @@ def _classification_candidates(
conn: sqlite3.Connection,
limit: int,
include_rejected: bool,
only_unclassified: bool = False,
) -> list[sqlite3.Row]:
where = "" if include_rejected else "WHERE s.accepted = 1 OR s.constructive_score >= 4"
filters = []
if not include_rejected:
filters.append("(s.accepted = 1 OR s.constructive_score >= 4)")
if only_unclassified:
# Articles still carrying the fast heuristic score, i.e. not yet judged
# by the model. Lets a scheduled cycle only spend the LLM on new items.
filters.append("s.model_name LIKE 'heuristic-%'")
where = ("WHERE " + " AND ".join(filters)) if filters else ""
return conn.execute(
f"""
SELECT