From 0fcc6a3529441ea2f2059083b58571d578c957e9 Mon Sep 17 00:00:00 2001 From: ArokyaMatthew Date: Tue, 26 May 2026 10:00:47 +0530 Subject: [PATCH 1/2] Add ReasoningResult confidence_tier/is_actionable computed fields + tests --- libs/schemas/reasoning.py | 20 +++++++----------- tests/test_reasoning_schema.py | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 tests/test_reasoning_schema.py diff --git a/libs/schemas/reasoning.py b/libs/schemas/reasoning.py index 5008cf3..15dee2d 100644 --- a/libs/schemas/reasoning.py +++ b/libs/schemas/reasoning.py @@ -1,7 +1,6 @@ from __future__ import annotations from typing import Literal, Optional -from pydantic import BaseModel, Field -import json +from pydantic import BaseModel, Field, computed_field class ReasoningResult(BaseModel): @@ -16,24 +15,21 @@ class ReasoningResult(BaseModel): severity_score: float = Field(0.0, ge=0.0, le=1.0) alert_id: Optional[str] = None - @property + @computed_field def confidence_tier(self) -> Literal["high", "medium", "low"]: - if self.confidence >= 0.75: return "high" - if self.confidence >= 0.50: return "medium" + if self.confidence >= 0.75: + return "high" + if self.confidence >= 0.50: + return "medium" return "low" - @property + @computed_field def is_actionable(self) -> bool: return self.label == "Suspicious" and self.confidence >= 0.65 - def model_dump_json(self, **kw) -> str: - d = self.model_dump(**kw) - d["confidence_tier"] = self.confidence_tier - d["is_actionable"] = self.is_actionable - return json.dumps(d, default=str) - class GroundingResult(BaseModel): grounded: bool invented_label: Optional[str] = None checked_caption: str = "" + diff --git a/tests/test_reasoning_schema.py b/tests/test_reasoning_schema.py new file mode 100644 index 0000000..e550d77 --- /dev/null +++ b/tests/test_reasoning_schema.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import pytest + +from libs.schemas.reasoning import ReasoningResult + + +@pytest.mark.parametrize( + "confidence, expected_tier", + [ + (0.49, "low"), + (0.50, "medium"), + (0.74, "medium"), + (0.75, "high"), + (1.0, "high"), + ], +) +def test_confidence_tier_boundaries(confidence: float, expected_tier: str) -> None: + result = ReasoningResult(track_id=1, label="Suspicious", confidence=confidence, reason="test") + + assert result.confidence_tier == expected_tier + + +def test_is_actionable_and_json_dump() -> None: + cases = [ + ("Suspicious", 0.65, True), + ("Suspicious", 0.64, False), + ("Normal", 0.99, False), + ("Normal", 0.65, False), + ] + + for label, confidence, expected_actionable in cases: + result = ReasoningResult(track_id=1, label=label, confidence=confidence, reason="test") + payload = result.model_dump(mode="json") + + assert result.is_actionable is expected_actionable + assert payload["confidence_tier"] == result.confidence_tier + assert payload["is_actionable"] is expected_actionable From 7d563e3e784c44992dc7f6245b19b2ee93e6861a Mon Sep 17 00:00:00 2001 From: ArokyaMatthew Date: Tue, 26 May 2026 10:08:27 +0530 Subject: [PATCH 2/2] Fix computed_field properties and clarify tests --- libs/schemas/reasoning.py | 5 +++-- tests/test_reasoning_schema.py | 25 ++++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/libs/schemas/reasoning.py b/libs/schemas/reasoning.py index 15dee2d..a658af7 100644 --- a/libs/schemas/reasoning.py +++ b/libs/schemas/reasoning.py @@ -16,6 +16,7 @@ class ReasoningResult(BaseModel): alert_id: Optional[str] = None @computed_field + @property def confidence_tier(self) -> Literal["high", "medium", "low"]: if self.confidence >= 0.75: return "high" @@ -24,6 +25,7 @@ def confidence_tier(self) -> Literal["high", "medium", "low"]: return "low" @computed_field + @property def is_actionable(self) -> bool: return self.label == "Suspicious" and self.confidence >= 0.65 @@ -31,5 +33,4 @@ def is_actionable(self) -> bool: class GroundingResult(BaseModel): grounded: bool invented_label: Optional[str] = None - checked_caption: str = "" - + checked_caption: str = "" \ No newline at end of file diff --git a/tests/test_reasoning_schema.py b/tests/test_reasoning_schema.py index e550d77..8b18763 100644 --- a/tests/test_reasoning_schema.py +++ b/tests/test_reasoning_schema.py @@ -1,7 +1,6 @@ from __future__ import annotations import pytest - from libs.schemas.reasoning import ReasoningResult @@ -17,22 +16,26 @@ ) def test_confidence_tier_boundaries(confidence: float, expected_tier: str) -> None: result = ReasoningResult(track_id=1, label="Suspicious", confidence=confidence, reason="test") - assert result.confidence_tier == expected_tier -def test_is_actionable_and_json_dump() -> None: - cases = [ +@pytest.mark.parametrize( + "label, confidence, expected", + [ ("Suspicious", 0.65, True), ("Suspicious", 0.64, False), ("Normal", 0.99, False), ("Normal", 0.65, False), - ] + ], +) +def test_is_actionable(label: str, confidence: float, expected: bool) -> None: + result = ReasoningResult(track_id=1, label=label, confidence=confidence, reason="test") + assert result.is_actionable is expected + - for label, confidence, expected_actionable in cases: - result = ReasoningResult(track_id=1, label=label, confidence=confidence, reason="test") - payload = result.model_dump(mode="json") +def test_model_dump_json_mode_includes_computed_fields() -> None: + result = ReasoningResult(track_id=1, label="Suspicious", confidence=0.88, reason="test") + payload = result.model_dump(mode="json") - assert result.is_actionable is expected_actionable - assert payload["confidence_tier"] == result.confidence_tier - assert payload["is_actionable"] is expected_actionable + assert payload["confidence_tier"] == "high" + assert payload["is_actionable"] is True \ No newline at end of file