From 707c4f743443237c1c453df06f1f4dd28e9833df Mon Sep 17 00:00:00 2001 From: musingfox Date: Tue, 3 Nov 2026 21:54:51 +0800 Subject: [PATCH] fix(scoring): treat a null scores list as a reply with no entries A reply of {"scores": null} made `for entry in entries` raise TypeError, so score_in_batches logged a stack trace and marked the run degraded instead of the "Scored none of N articles: the reply had 0 entries" warning PR #128 added for a reply with no scores. `or []` gives null the same path as a missing key. No model has been seen sending null; the case came from the PR #128 review. Co-Authored-By: Claude Opus 5.5 --- src/cyris/service_layer/scoring.py | 2 +- tests/test_scoring.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cyris/service_layer/scoring.py b/src/cyris/service_layer/scoring.py index 9e6bb27e..8d2a4bd6 100644 --- a/src/cyris/service_layer/scoring.py +++ b/src/cyris/service_layer/scoring.py @@ -92,7 +92,7 @@ async def score_articles_batch( result: dict[str, tuple[float, str]] = {} url_to_tags: dict[str, list[str]] = {} picks: dict[str, AreaPick] = {} - entries = data.get("scores", []) + entries = data.get("scores") or [] for entry in entries: article_id = entry["id"] score = float(entry["score"]) diff --git a/tests/test_scoring.py b/tests/test_scoring.py index 1542289e..68cf5d92 100644 --- a/tests/test_scoring.py +++ b/tests/test_scoring.py @@ -296,8 +296,8 @@ async def test_custom_snippet_length_in_batch(self, sample_articles): @pytest.mark.parametrize( "reply", - [{}, {"scores": [{"id": 9, "score": 80}]}], - ids=["no scores key", "unknown ids"], + [{}, {"scores": None}, {"scores": [{"id": 9, "score": 80}]}], + ids=["no scores key", "null scores", "unknown ids"], ) async def test_a_batch_scoring_nothing_warns(self, sample_articles, reply, caplog): from cyris.service_layer.scoring import score_articles_batch @@ -310,7 +310,7 @@ async def test_a_batch_scoring_nothing_warns(self, sample_articles, reply, caplo assert scores == {} (record,) = [r for r in caplog.records if r.levelname == "WARNING"] - assert f"{len(reply.get('scores', []))} entries" in record.getMessage() + assert f"{len(reply.get('scores') or [])} entries" in record.getMessage() async def test_a_batch_scoring_some_does_not_warn(self, sample_articles, caplog): from cyris.service_layer.scoring import score_articles_batch