fix(langchain): search() crashes with TypeError when score is None (#5072)

Co-authored-by: Kartik <kartik.labhshetwar@mem0.ai>
This commit is contained in:
Oleg Ovcharuk
2026-06-11 20:04:06 +03:00
committed by GitHub
parent 32c8849044
commit b36847622d
4 changed files with 103 additions and 7 deletions
@@ -57,6 +57,9 @@ def test_search_vectors(langchain_instance):
assert results[0].payload == {"name": "vector1"}
assert results[1].id == "id2"
assert results[1].payload == {"name": "vector2"}
# scores must never be None — score_and_rank crashes on None < threshold
assert results[0].score == 1.0
assert results[1].score == 1.0
# Test search with filters
filters = {"name": "vector1"}
@@ -226,6 +229,56 @@ def test_list_with_exception(langchain_instance):
assert results == []
def test_search_score_is_never_none(langchain_instance):
"""Regression: similarity_search_by_vector returns Documents with no scores.
search() must not propagate None — score_and_rank crashes on None < threshold."""
from mem0.utils.scoring import score_and_rank
mock_docs = [Mock(metadata={"data": "mem A"}, id="id1"), Mock(metadata={"data": "mem B"}, id="id2")]
langchain_instance.client.similarity_search_by_vector.return_value = mock_docs
results = langchain_instance.search(query="test", vectors=[[0.1, 0.2]], top_k=5)
assert all(r.score is not None for r in results), "score must never be None"
# Fallback path: no scored method available, so 1.0 is assigned.
assert all(r.score == 1.0 for r in results)
# Verify the full pipeline does not raise TypeError
candidates = [{"id": r.id, "score": r.score, "payload": r.payload} for r in results]
ranked = score_and_rank(candidates, {}, {}, threshold=0.1, top_k=5)
assert len(ranked) == 2
def test_search_uses_scored_method_when_available(langchain_instance):
"""When a scored-by-vector method exists on the client, use it to get real scores."""
mock_docs = [Mock(metadata={"data": "mem A"}, id="id1"), Mock(metadata={"data": "mem B"}, id="id2")]
# Inject a non-spec method that returns (Document, float) pairs
langchain_instance.client.similarity_search_by_vector_with_relevance_scores = Mock(
return_value=[(mock_docs[0], 0.95), (mock_docs[1], 0.42)]
)
results = langchain_instance.search(query="test", vectors=[[0.1, 0.2]], top_k=5)
langchain_instance.client.similarity_search_by_vector_with_relevance_scores.assert_called_once()
langchain_instance.client.similarity_search_by_vector.assert_not_called()
assert results[0].score == pytest.approx(0.95)
assert results[1].score == pytest.approx(0.42)
def test_search_falls_back_when_scored_method_raises_not_implemented(langchain_instance):
"""If the scored method raises NotImplementedError, fall back to score=1.0."""
mock_docs = [Mock(metadata={"data": "mem A"}, id="id1")]
langchain_instance.client.similarity_search_by_vector_with_relevance_scores = Mock(
side_effect=NotImplementedError
)
langchain_instance.client.similarity_search_by_vector.return_value = mock_docs
results = langchain_instance.search(query="test", vectors=[[0.1, 0.2]], top_k=5)
langchain_instance.client.similarity_search_by_vector.assert_called_once()
assert results[0].score == 1.0
def test_update_wraps_vector_and_payload_in_lists(langchain_instance):
"""Regression test for Langchain update() type mismatch.