Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

342 lines
13 KiB
Python

"""
High-value pure logic tests for graders.py.
Tests extract_answer_from_response(), DEFAULT_EVALUATION_CONFIG,
regex extraction patterns, and grade_single_result response parsing.
"""
import re
from unittest.mock import MagicMock, patch
from local_deep_research.benchmarks.graders import (
DEFAULT_EVALUATION_CONFIG,
extract_answer_from_response,
get_evaluation_llm,
grade_single_result,
)
class TestDefaultEvaluationConfig:
"""Tests for DEFAULT_EVALUATION_CONFIG dict values."""
def test_model_name_is_set(self):
assert "model_name" in DEFAULT_EVALUATION_CONFIG
assert (
DEFAULT_EVALUATION_CONFIG["model_name"]
== "anthropic/claude-3.7-sonnet"
)
def test_provider_is_openai_endpoint(self):
assert DEFAULT_EVALUATION_CONFIG["provider"] == "openai_endpoint"
def test_openai_endpoint_url(self):
assert (
DEFAULT_EVALUATION_CONFIG["openai_endpoint_url"]
== "https://openrouter.ai/api/v1"
)
def test_temperature_is_zero(self):
assert DEFAULT_EVALUATION_CONFIG["temperature"] == 0
def test_no_max_tokens_key(self):
"""max_tokens was intentionally removed per code comments."""
assert "max_tokens" not in DEFAULT_EVALUATION_CONFIG
def test_config_has_exactly_four_keys(self):
assert len(DEFAULT_EVALUATION_CONFIG) == 4
class TestExtractAnswerSimpleQA:
"""Tests for extract_answer_from_response with simpleqa dataset type."""
def test_basic_response_returned_as_is(self):
result = extract_answer_from_response("Paris is the capital of France.")
assert result["extracted_answer"] == "Paris is the capital of France."
assert result["confidence"] == "100"
def test_empty_response(self):
result = extract_answer_from_response("")
assert result["extracted_answer"] == ""
assert result["confidence"] == "100"
def test_citations_are_stripped(self):
result = extract_answer_from_response(
"The answer is 42[1] according to sources[23]."
)
assert "[1]" not in result["extracted_answer"]
assert "[23]" not in result["extracted_answer"]
assert "42" in result["extracted_answer"]
def test_multiple_citations_stripped(self):
result = extract_answer_from_response("[1][2][3] Some text [45]")
assert result["extracted_answer"] == " Some text "
def test_default_dataset_type_is_simpleqa(self):
result = extract_answer_from_response("Hello world")
assert result["extracted_answer"] == "Hello world"
def test_simpleqa_explicit_type(self):
result = extract_answer_from_response(
"Hello world", dataset_type="simpleqa"
)
assert result["extracted_answer"] == "Hello world"
def test_simpleqa_case_insensitive_type(self):
result = extract_answer_from_response("Hello", dataset_type="SimpleQA")
assert result["extracted_answer"] == "Hello"
def test_multiline_response(self):
text = "Line one\nLine two\nLine three"
result = extract_answer_from_response(text, dataset_type="simpleqa")
assert "Line one" in result["extracted_answer"]
assert "Line three" in result["extracted_answer"]
class TestExtractAnswerBrowseComp:
"""Tests for extract_answer_from_response with browsecomp dataset type."""
def test_exact_answer_extracted(self):
response = "Exact Answer: John Smith\nConfidence: 85%"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "John Smith"
assert result["confidence"] == "85"
def test_exact_answer_at_end_of_string(self):
response = "Exact Answer: The Eiffel Tower"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "The Eiffel Tower"
def test_no_exact_answer_returns_none_string(self):
response = "I don't know the answer."
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "None"
def test_no_confidence_defaults_to_100(self):
response = "Exact Answer: Something"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["confidence"] == "100"
def test_confidence_extraction(self):
response = "Exact Answer: X\nConfidence: 42%"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["confidence"] == "42"
def test_browsecomp_case_insensitive_type(self):
response = "Exact Answer: Yes\nConfidence: 90%"
result = extract_answer_from_response(
response, dataset_type="BrowseComp"
)
assert result["extracted_answer"] == "Yes"
def test_citations_stripped_before_extraction(self):
response = "Exact Answer: Paris[1]\nConfidence: 75%"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "Paris"
def test_empty_response_browsecomp(self):
result = extract_answer_from_response("", dataset_type="browsecomp")
assert result["extracted_answer"] == "None"
assert result["confidence"] == "100"
def test_exact_answer_with_extra_whitespace(self):
response = "Exact Answer: spaced out \nConfidence: 50%"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "spaced out"
def test_exact_answer_followed_by_more_text(self):
response = "Exact Answer: Alpha\nSome other line\nConfidence: 60%"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "Alpha"
assert result["confidence"] == "60"
class TestGradeSingleResultBrowseComp:
"""Tests for grade_single_result browsecomp regex parsing of grader LLM response."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_browsecomp_correct_yes(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="extracted_final_answer: Paris\nreasoning: Matches reference\ncorrect: yes\nconfidence: 95"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{
"problem": "Capital of France?",
"correct_answer": "Paris",
"response": "Paris",
},
dataset_type="browsecomp",
)
assert result["is_correct"] is True
assert result["extracted_by_grader"] == "Paris"
assert result["graded_confidence"] == "95"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_browsecomp_correct_no(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="extracted_final_answer: London\nreasoning: Wrong city\ncorrect: no\nconfidence: 10"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "Paris", "response": "London"},
dataset_type="browsecomp",
)
assert result["is_correct"] is False
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_browsecomp_no_correct_field_defaults_false(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="extracted_final_answer: Something\nreasoning: Unclear"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "A", "response": "R"},
dataset_type="browsecomp",
)
assert result["is_correct"] is False
assert result["graded_confidence"] == "100"
class TestGradeSingleResultSimpleQA:
"""Tests for grade_single_result simpleqa regex parsing of grader LLM response."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_simpleqa_correct_yes(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="Extracted Answer: 42\nReasoning: Matches exactly\nCorrect: Yes"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "42", "response": "42"},
dataset_type="simpleqa",
)
assert result["is_correct"] is True
assert result["extracted_by_grader"] == "42"
assert result["graded_confidence"] == "100"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_simpleqa_correct_no(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="Extracted Answer: 43\nReasoning: Off by one\nCorrect: No"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "42", "response": "43"},
dataset_type="simpleqa",
)
assert result["is_correct"] is False
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_simpleqa_no_extracted_answer_defaults_none(self, mock_get_llm):
mock_llm = MagicMock()
mock_llm.invoke.return_value = MagicMock(
content="Reasoning: Cannot determine\nCorrect: no"
)
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "A", "response": "R"},
dataset_type="simpleqa",
)
assert result["extracted_by_grader"] == "None"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_single_result_invoke_exception_returns_error(
self, mock_get_llm
):
"""When the LLM invoke raises, the try/except inside grade_single_result catches it."""
mock_llm = MagicMock()
mock_llm.invoke.side_effect = RuntimeError("LLM call failed")
mock_llm.chat_messages = True
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{"problem": "Q", "correct_answer": "A", "response": "R"},
)
assert result["is_correct"] is False
assert "grading_error" in result
assert "LLM call failed" in result["grading_error"]
assert result["graded_confidence"] == "0"
class TestCitationStrippingRegex:
"""Directly test the citation-stripping regex used in extract_answer_from_response."""
def test_single_digit_citation(self):
assert re.sub(r"\[\d+\]", "", "text[5]") == "text"
def test_multi_digit_citation(self):
assert re.sub(r"\[\d+\]", "", "text[123]more") == "textmore"
def test_no_citation(self):
assert (
re.sub(r"\[\d+\]", "", "no citations here") == "no citations here"
)
def test_non_numeric_brackets_preserved(self):
assert re.sub(r"\[\d+\]", "", "text[abc]") == "text[abc]"
class TestGetEvaluationLlm:
"""Tests for get_evaluation_llm configuration merging logic."""
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_default_config_used_when_no_custom(self, mock_get_llm):
mock_get_llm.return_value = MagicMock()
get_evaluation_llm()
mock_get_llm.assert_called_once()
call_kwargs = mock_get_llm.call_args[1]
assert call_kwargs["model_name"] == "anthropic/claude-3.7-sonnet"
assert call_kwargs["temperature"] == 0
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_custom_config_overrides_defaults(self, mock_get_llm):
mock_get_llm.return_value = MagicMock()
get_evaluation_llm(
custom_config={"model_name": "gpt-4", "temperature": 0.5}
)
call_kwargs = mock_get_llm.call_args[1]
assert call_kwargs["model_name"] == "gpt-4"
assert call_kwargs["temperature"] == 0.5
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_unsupported_params_filtered_out(self, mock_get_llm):
mock_get_llm.return_value = MagicMock()
get_evaluation_llm(custom_config={"max_tokens": 1000, "top_p": 0.9})
call_kwargs = mock_get_llm.call_args[1]
assert "max_tokens" not in call_kwargs
assert "top_p" not in call_kwargs