Bisect provider-rejected batches in triage and deep assessment
DeepSeek's content filter rejects a whole request (400 "Content Exists Risk") when one article trips it, which cost the other articles in the batch their assessment and retried them every run. New curate/batch.rs runs both stages through a bisecting runner: a rejected batch is split until the offending article is isolated, that article is retried once on the editor provider when it is a different one, and a still-rejected article is recorded as a provider_rejected assessment row so it is not retried for assessment_reuse_days. Cache reuse accepts rows from either configured model. Each stage logs reused/requested/rejected counts, the curation: line shows rejections when non-zero, explain prints them, and the llm_assess span reports the deep-set size. Implemented by a Claude agent from an orchestrator brief; verified fmt/clippy(-W dead_code)/test green (354 lib tests). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1rCLQeKBgnBo3oTgHuTMe
This commit is contained in:
@@ -494,4 +494,49 @@ mod tests {
|
||||
.expect("thin row");
|
||||
assert_eq!(reason, "recently_rejected");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn provider_rejected_rows_do_not_mark_an_article_recently_rejected() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let db = Db::open_and_migrate(&dir.path().join("hygiene.db"))
|
||||
.await
|
||||
.expect("db");
|
||||
sqlx::query(
|
||||
"INSERT INTO articles (id, canonical_url, title, first_seen) VALUES
|
||||
(1, 'https://example.com/1', 'Refused', '2026-09-02T00:00:00Z')",
|
||||
)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("article");
|
||||
sqlx::query(
|
||||
"INSERT INTO article_assessments
|
||||
(article_id, stage, model, prompt_version, score, fit, kind, rationale, assessed_at)
|
||||
VALUES (1, 'triage', 'model', 1, NULL, NULL, 'provider_rejected',
|
||||
'deepseek: Content Exists Risk', '2026-09-02T04:00:00Z'),
|
||||
(1, 'deep', 'model', 1, NULL, NULL, 'provider_rejected',
|
||||
'deepseek: Content Exists Risk', '2026-09-02T04:00:00Z')",
|
||||
)
|
||||
.execute(db.pool())
|
||||
.await
|
||||
.expect("rejection rows");
|
||||
let run_id = db
|
||||
.start_run(
|
||||
"2026-09-02".parse().expect("date"),
|
||||
"2026-09-02T05:30:00Z".parse().expect("timestamp"),
|
||||
)
|
||||
.await
|
||||
.expect("run");
|
||||
let eligible = hygiene(
|
||||
&db,
|
||||
run_id,
|
||||
vec![article(1, "Refused", 500)],
|
||||
"2026-09-02".parse().expect("date"),
|
||||
&CurationConfig::default(),
|
||||
"2026-09-02T05:30:00Z".parse().expect("timestamp"),
|
||||
)
|
||||
.await
|
||||
.expect("hygiene");
|
||||
assert_eq!(eligible.len(), 1, "a NULL score is not a low score");
|
||||
assert_eq!(eligible[0].article.id, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user