c56bef871b
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
Docker image release / Build base image (push) Has been cancelled
Sync docs with Docusaurus / sync (push) Has been cancelled
Tests / Check if changed (push) Has been cancelled
Tests / format (push) Has been cancelled
Tests / check-imports (push) Has been cancelled
Tests / Unit / macos-latest (push) Has been cancelled
Tests / Unit / ubuntu-latest (push) Has been cancelled
Tests / Unit / windows-latest (push) Has been cancelled
Tests / mypy (push) Has been cancelled
Tests / Integration / ubuntu-latest (push) Has been cancelled
Tests / Integration / macos-latest (push) Has been cancelled
Tests / Integration / windows-latest (push) Has been cancelled
Tests / notify-slack-on-failure (push) Has been cancelled
Tests / Mark tests as completed (push) Has been cancelled
166 lines
6.6 KiB
Python
166 lines
6.6 KiB
Python
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import random
|
|
|
|
import pytest
|
|
|
|
from haystack import Document
|
|
from haystack.components.samplers.top_p import TopPSampler
|
|
|
|
|
|
@pytest.fixture
|
|
def documents_with_score_field() -> list[Document]:
|
|
return [
|
|
Document(content="Sarajevo", meta={"similarity_score": 0.7}),
|
|
Document(content="Belgrade", meta={"similarity_score": 0.01}),
|
|
Document(content="Berlin", meta={"similarity_score": 0.001}),
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def documents_with_score() -> list[Document]:
|
|
return [
|
|
Document(content="Sarajevo", score=0.7),
|
|
Document(content="Belgrade", score=0.01),
|
|
Document(content="Berlin", score=0.001),
|
|
]
|
|
|
|
|
|
class TestTopPSampler:
|
|
def test_init_raises_value_error(self) -> None:
|
|
with pytest.raises(ValueError):
|
|
TopPSampler(top_p=2.0)
|
|
|
|
def test_run_raises_value_error(self, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=0.95)
|
|
with pytest.raises(ValueError):
|
|
sampler.run(documents=documents_with_score, top_p=2.0)
|
|
|
|
def test_run_score_field(self, documents_with_score_field: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=0.95, score_field="similarity_score")
|
|
docs = documents_with_score_field
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 2
|
|
assert docs[0].content == "Sarajevo"
|
|
assert docs[1].content == "Belgrade"
|
|
|
|
def test_run_score_field_missing_scores(self, caplog: pytest.LogCaptureFixture) -> None:
|
|
sampler = TopPSampler(top_p=1.0, score_field="similarity_score")
|
|
docs = [
|
|
Document(content="Sarajevo", meta={"similarity_score": 0.7}),
|
|
Document(content="Belgrade", meta={"similarity_score": 0.01}),
|
|
Document(content="Berlin", meta={"similarity_score": None}),
|
|
]
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 2
|
|
assert docs[0].content == "Sarajevo"
|
|
assert docs[1].content == "Belgrade"
|
|
assert "Score field" in caplog.text
|
|
|
|
def test_run(self, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=0.99)
|
|
docs = documents_with_score
|
|
random.shuffle(docs)
|
|
sorted_scores = sorted([doc.score for doc in docs], reverse=True)
|
|
|
|
# top_p = 0.99 will get the top 1 document
|
|
output = sampler.run(documents=docs)
|
|
docs_filtered = output["documents"]
|
|
assert len(docs_filtered) == 2
|
|
assert docs_filtered[0].content == "Sarajevo"
|
|
assert docs_filtered[1].content == "Belgrade"
|
|
|
|
assert [doc.score for doc in docs_filtered] == sorted_scores[:2]
|
|
|
|
def test_run_top_p_1(self, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=1.0)
|
|
docs = documents_with_score
|
|
random.shuffle(docs)
|
|
output = sampler.run(documents=docs)
|
|
docs_filtered = output["documents"]
|
|
assert len(docs_filtered) == len(docs)
|
|
assert docs_filtered[0].content == "Sarajevo"
|
|
assert [doc.score for doc in docs_filtered] == sorted([doc.score for doc in docs], reverse=True)
|
|
|
|
def test_run_top_p_0(self, caplog: pytest.LogCaptureFixture, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=0.0)
|
|
docs = documents_with_score
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 1
|
|
assert docs[0].content == "Sarajevo"
|
|
assert "Top-p sampling with p=" in caplog.text
|
|
|
|
def test_run_top_p_0_as_run_override(
|
|
self, caplog: pytest.LogCaptureFixture, documents_with_score: list[Document]
|
|
) -> None:
|
|
# top_p=0.0 passed to run() must not be silently replaced by the init value
|
|
sampler = TopPSampler(top_p=1.0)
|
|
docs = documents_with_score
|
|
output = sampler.run(documents=docs, top_p=0.0)
|
|
docs = output["documents"]
|
|
assert len(docs) == 1
|
|
assert docs[0].content == "Sarajevo"
|
|
assert "Top-p sampling with p=" in caplog.text
|
|
|
|
def test_run_with_integer_scores(self) -> None:
|
|
# integer scores must be treated like float scores, not as missing
|
|
sampler = TopPSampler(top_p=0.99)
|
|
docs = [
|
|
Document(content="Sarajevo", score=7),
|
|
Document(content="Belgrade", score=1),
|
|
Document(content="Berlin", score=-5),
|
|
]
|
|
output = sampler.run(documents=docs)
|
|
docs_filtered = output["documents"]
|
|
assert len(docs_filtered) < len(docs)
|
|
assert docs_filtered[0].content == "Sarajevo"
|
|
|
|
def test_run_with_boolean_scores_treated_as_missing(self, caplog: pytest.LogCaptureFixture) -> None:
|
|
sampler = TopPSampler(top_p=0.95)
|
|
docs = [Document(content="Sarajevo", score=True), Document(content="Belgrade", score=0.5)]
|
|
output = sampler.run(documents=docs)
|
|
docs_filtered = output["documents"]
|
|
assert docs_filtered == [docs[1]]
|
|
assert "Ensure all documents have a valid score value" in caplog.text
|
|
|
|
def test_run_returns_empty_list_no_documents(self) -> None:
|
|
sampler = TopPSampler()
|
|
output = sampler.run(documents=[])
|
|
assert output["documents"] == []
|
|
|
|
def test_run_no_score_field(self, caplog: pytest.LogCaptureFixture, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(top_p=0.95, score_field="similarity_score")
|
|
docs = documents_with_score
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 3
|
|
assert docs[0].content == "Sarajevo"
|
|
assert "Score field 'similarity_score' not found" in caplog.text
|
|
|
|
def test_run_missing_scores(self, caplog: pytest.LogCaptureFixture) -> None:
|
|
sampler = TopPSampler(top_p=0.95)
|
|
docs = [
|
|
Document(content="Sarajevo", score=0.7),
|
|
Document(content="Belgrade", score=0.01),
|
|
Document(content="Berlin", score=None),
|
|
]
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 1
|
|
assert docs[0].content == "Sarajevo"
|
|
assert "Ensure all documents have a valid score value" in caplog.text
|
|
|
|
def test_run_min_top_k(self, documents_with_score: list[Document]) -> None:
|
|
sampler = TopPSampler(min_top_k=2, top_p=0.2)
|
|
docs = documents_with_score
|
|
output = sampler.run(documents=docs)
|
|
docs = output["documents"]
|
|
assert len(docs) == 2
|
|
assert docs[0].content == "Sarajevo"
|
|
assert docs[1].content == "Belgrade"
|