Durability pass: tests, clearer diversity/classify behavior, Calm Filters foundation
- Add pytest suite (34 tests) covering scoring thresholds, dedup clustering + representative selection + time window, brief source/category diversity, avoid-term phrase matching, and text canonicalization/truncation. - Rewrite _select_diverse with an explicit, tested contract (best-first, one per source, backfill, then inject a second category by evicting the lowest-ranked pick). - classify_articles now returns attempted/succeeded/skipped (ClassifyReport) so silent model failures are visible in both the cycle and classify output. - Fix clean_text truncation to stay within max_len (ellipsis no longer overshoots). - New filters.py: canonical FilterPrefs shape (include/mute topics+flavors, avoid_terms, pauses) and pure word/phrase-boundary matching engine seeding Calm Filters. Not yet wired into the API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-4
@@ -144,20 +144,24 @@ def main() -> None:
|
||||
elif args.command == "classify":
|
||||
init_db(conn)
|
||||
client = llm_client_from_args(args)
|
||||
results = classify_articles(
|
||||
report = classify_articles(
|
||||
conn,
|
||||
client,
|
||||
limit=args.limit,
|
||||
include_rejected=args.include_rejected,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
for article_id, scores in results:
|
||||
for article_id, scores in report.results:
|
||||
accepted = "yes" if scores["accepted"] else "no"
|
||||
print(
|
||||
f"[{article_id}] accepted={accepted} {scores['topic']}/{scores['flavor']} "
|
||||
f"reason={scores['reason_code']}"
|
||||
)
|
||||
print(f" {scores['reason_text']}")
|
||||
print(
|
||||
f"classify: attempted={report.attempted} succeeded={report.succeeded} "
|
||||
f"skipped={report.skipped}"
|
||||
)
|
||||
if args.dry_run:
|
||||
print("Dry run only; database was not updated.")
|
||||
elif args.command == "cycle":
|
||||
@@ -294,7 +298,7 @@ def _run_cycle_locked(conn: sqlite3.Connection, args: argparse.Namespace) -> Non
|
||||
print(f" classify {done}/{total} (article {article_id})", flush=True)
|
||||
|
||||
try:
|
||||
results = classify_articles(
|
||||
report = classify_articles(
|
||||
conn,
|
||||
client,
|
||||
limit=args.classify_limit,
|
||||
@@ -302,7 +306,11 @@ def _run_cycle_locked(conn: sqlite3.Connection, args: argparse.Namespace) -> Non
|
||||
only_unclassified=True,
|
||||
progress=_progress,
|
||||
)
|
||||
print(f"classify: {len(results)} new article(s) scored by {client.model}", flush=True)
|
||||
print(
|
||||
f"classify: attempted={report.attempted} succeeded={report.succeeded} "
|
||||
f"skipped={report.skipped} (model {client.model})",
|
||||
flush=True,
|
||||
)
|
||||
except Exception as exc: # endpoint down, timeout, etc. — keep going
|
||||
print(f"classify: skipped ({exc})", flush=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user