diff --git a/src/dakera/models.py b/src/dakera/models.py index fc699ea..6bdca49 100644 --- a/src/dakera/models.py +++ b/src/dakera/models.py @@ -699,6 +699,11 @@ class RecalledMemory: memory_type: str importance: float score: float + """Ranking score — equals smart_score when present, then weighted_score, then raw score.""" + smart_score: float | None = None + """Raw smart_score from the server (the primary ranking key).""" + weighted_score: float | None = None + """Raw weighted_score from the server.""" metadata: dict[str, Any] | None = None created_at: str | None = None depth: int | None = None @@ -706,12 +711,22 @@ class RecalledMemory: @classmethod def from_dict(cls, data: dict[str, Any]) -> "RecalledMemory": + smart_score = data.get("smart_score") + weighted_score = data.get("weighted_score") + if smart_score is not None: + score = smart_score + elif weighted_score is not None: + score = weighted_score + else: + score = data.get("score", 0.0) return cls( id=data["id"], content=data["content"], memory_type=data.get("memory_type", "episodic"), importance=data.get("importance", 0.5), - score=data.get("score", 0.0), + score=score, + smart_score=smart_score, + weighted_score=weighted_score, metadata=data.get("metadata"), created_at=data.get("created_at"), depth=data.get("depth"), @@ -733,9 +748,13 @@ class RecallResponse: @classmethod def _normalize_memory(cls, m: dict[str, Any]) -> dict[str, Any]: - """Flatten nested {memory: {...}, score: ...} into a single dict.""" + """Flatten nested {memory: {...}, score, weighted_score, smart_score} into a single dict.""" if "memory" in m and isinstance(m["memory"], dict): flat = {**m["memory"], "score": m.get("score", 0.0)} + if "smart_score" in m: + flat["smart_score"] = m["smart_score"] + if "weighted_score" in m: + flat["weighted_score"] = m["weighted_score"] return flat return m diff --git a/tests/test_smart_score.py b/tests/test_smart_score.py new file mode 100644 index 0000000..085db39 --- /dev/null +++ b/tests/test_smart_score.py @@ -0,0 +1,89 @@ +"""Tests for RecalledMemory.score priority: smart_score > weighted_score > raw score.""" + +import pytest + +from dakera.models import RecalledMemory, RecallResponse + + +def test_smart_score_takes_priority(): + data = { + "id": "m1", "content": "test", "memory_type": "episodic", "importance": 0.8, + "score": 0.5, "weighted_score": 0.7, "smart_score": 0.9, + } + m = RecalledMemory.from_dict(data) + assert m.score == pytest.approx(0.9), ".score must equal smart_score when present" + assert m.smart_score == pytest.approx(0.9) + assert m.weighted_score == pytest.approx(0.7) + + +def test_weighted_score_fallback(): + data = { + "id": "m2", "content": "test", "memory_type": "episodic", "importance": 0.8, + "score": 0.5, "weighted_score": 0.7, + } + m = RecalledMemory.from_dict(data) + assert m.score == pytest.approx(0.7), ".score must equal weighted_score when smart_score absent" + assert m.smart_score is None + assert m.weighted_score == pytest.approx(0.7) + + +def test_raw_score_fallback(): + data = { + "id": "m3", "content": "test", "memory_type": "episodic", "importance": 0.8, + "score": 0.55, + } + m = RecalledMemory.from_dict(data) + assert m.score == pytest.approx(0.55), ( + ".score must equal raw score when neither smart_score nor weighted_score" + ) + assert m.smart_score is None + assert m.weighted_score is None + + +def test_recall_response_normalize_forwards_smart_score(): + raw = { + "memories": [ + { + "memory": { + "id": "m4", + "content": "nested", + "memory_type": "episodic", + "importance": 0.9, + "created_at": "2026-01-01T00:00:00Z", + "tags": [], + }, + "score": 0.4, + "weighted_score": 0.6, + "smart_score": 0.85, + } + ] + } + resp = RecallResponse.from_dict(raw) + assert len(resp.memories) == 1 + m = resp.memories[0] + assert m.score == pytest.approx(0.85) + assert m.smart_score == pytest.approx(0.85) + assert m.weighted_score == pytest.approx(0.6) + + +def test_recall_response_normalize_without_smart_score(): + raw = { + "memories": [ + { + "memory": { + "id": "m5", + "content": "nested2", + "memory_type": "semantic", + "importance": 0.7, + "created_at": "2026-01-01T00:00:00Z", + "tags": [], + }, + "score": 0.3, + "weighted_score": 0.55, + } + ] + } + resp = RecallResponse.from_dict(raw) + m = resp.memories[0] + assert m.score == pytest.approx(0.55) + assert m.smart_score is None