Files
wehub-resource-sync 7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

405 lines
14 KiB
Python

"""
Tests for uncovered code paths in graders.py.
Targets:
- extract_answer_from_response: browsecomp mode, missing matches
- grade_single_result: various LLM interfaces (chat_messages, content attr, callable)
- grade_results / _grade_results_inner: browsecomp extraction, progress callbacks, error handling
- human_evaluation: non-interactive mode
"""
import json
from unittest.mock import Mock, patch
class TestExtractAnswerFromResponse:
"""Tests for extract_answer_from_response."""
def test_simpleqa_returns_full_response(self):
"""SimpleQA mode returns full response as extracted answer."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
result = extract_answer_from_response(
"The answer is 42 [1] with citations [2]", "simpleqa"
)
assert "42" in result["extracted_answer"]
assert result["confidence"] == "100"
# Citations should be stripped
assert "[1]" not in result["extracted_answer"]
assert "[2]" not in result["extracted_answer"]
def test_browsecomp_extracts_answer_and_confidence(self):
"""BrowseComp mode extracts answer and confidence from structured response."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "Exact Answer: The Great Wall of China\nConfidence: 85%"
result = extract_answer_from_response(response, "browsecomp")
assert result["extracted_answer"] == "The Great Wall of China"
assert result["confidence"] == "85"
def test_browsecomp_missing_answer_field(self):
"""BrowseComp mode returns 'None' when answer field is missing."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
result = extract_answer_from_response(
"Some unstructured response", "browsecomp"
)
assert result["extracted_answer"] == "None"
def test_browsecomp_missing_confidence(self):
"""BrowseComp mode defaults to 100 when confidence is missing."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
result = extract_answer_from_response(
"Exact Answer: Something\nNo confidence here", "browsecomp"
)
assert result["confidence"] == "100"
def test_citations_stripped_from_response(self):
"""Citation markers like [1], [23] are removed."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
result = extract_answer_from_response(
"The answer [1] is 42 [23] exactly.", "simpleqa"
)
assert "[1]" not in result["extracted_answer"]
assert "[23]" not in result["extracted_answer"]
assert "42" in result["extracted_answer"]
class TestGradeSingleResult:
"""Tests for grade_single_result."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
@patch("local_deep_research.utilities.resource_utils.safe_close")
def test_grade_single_result_with_content_attr(
self, mock_close, mock_get_llm
):
"""grade_single_result handles LLM response with .content attribute."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = Mock()
mock_response = Mock()
mock_response.content = (
"Extracted Answer: 42\nReasoning: Matches exactly\nCorrect: yes"
)
mock_llm.invoke.return_value = mock_response
mock_llm.chat_messages = None # Has invoke but not chat_messages
# Remove chat_messages to trigger the else branch
del mock_llm.chat_messages
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{
"problem": "What is 6 * 7?",
"correct_answer": "42",
"response": "The answer is 42",
},
dataset_type="simpleqa",
)
assert result["is_correct"] is True
assert result["extracted_by_grader"] == "42"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
@patch("local_deep_research.utilities.resource_utils.safe_close")
def test_grade_single_result_browsecomp(self, mock_close, mock_get_llm):
"""grade_single_result handles browsecomp extraction format."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = Mock()
mock_response = Mock()
mock_response.content = (
"extracted_final_answer: The Great Wall\n"
"reasoning: The response correctly identifies the structure\n\n"
"correct: yes\n"
"confidence: 90"
)
mock_llm.invoke.return_value = mock_response
del mock_llm.chat_messages
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{
"problem": "What is the longest wall?",
"correct_answer": "The Great Wall",
"response": "The Great Wall of China",
},
dataset_type="browsecomp",
)
assert result["is_correct"] is True
assert result["extracted_by_grader"] == "The Great Wall"
assert result["graded_confidence"] == "90"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
@patch("local_deep_research.utilities.resource_utils.safe_close")
def test_grade_single_result_callable_fallback(
self, mock_close, mock_get_llm
):
"""grade_single_result falls back to calling LLM as callable."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = Mock()
# Remove invoke to trigger callable fallback
del mock_llm.invoke
mock_llm.return_value = (
"Extracted Answer: 42\nReasoning: ok\nCorrect: yes"
)
mock_get_llm.return_value = mock_llm
result = grade_single_result(
{
"problem": "What?",
"correct_answer": "42",
"response": "42",
},
)
assert result["is_correct"] is True
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
@patch("local_deep_research.utilities.resource_utils.safe_close")
def test_grade_single_result_exception(self, mock_close, mock_get_llm):
"""grade_single_result handles exceptions gracefully."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = Mock()
mock_llm.invoke.side_effect = Exception("LLM failed")
del mock_llm.chat_messages
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 failed" in result["grading_error"]
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
@patch("local_deep_research.utilities.resource_utils.safe_close")
def test_grade_single_result_no_correct_match(
self, mock_close, mock_get_llm
):
"""grade_single_result defaults to incorrect when no 'Correct:' match."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = Mock()
mock_response = Mock()
mock_response.content = "Some response without the expected format"
mock_llm.invoke.return_value = mock_response
del mock_llm.chat_messages
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 result["extracted_by_grader"] == "None"
class TestHumanEvaluation:
"""Tests for human_evaluation non-interactive mode."""
def test_non_interactive_mode(self, tmp_path):
"""human_evaluation in non-interactive mode marks all as incorrect."""
from local_deep_research.benchmarks.graders import human_evaluation
# Create input file
input_file = tmp_path / "results.jsonl"
results = [
{
"problem": "Q1",
"correct_answer": "A1",
"response": "R1",
"extracted_answer": "E1",
},
{
"problem": "Q2",
"correct_answer": "A2",
"response": "R2",
"extracted_answer": "E2",
},
]
input_file.write_text("\n".join(json.dumps(r) for r in results))
output_file = tmp_path / "graded.jsonl"
graded = human_evaluation(
str(input_file), str(output_file), interactive=False
)
assert len(graded) == 2
assert all(r["is_correct"] is False for r in graded)
assert all(r["human_evaluation"] is True for r in graded)
assert all(
r["reasoning"] == "Non-interactive evaluation" for r in graded
)
# Check output file was written
assert output_file.exists()
lines = output_file.read_text().strip().split("\n")
assert len(lines) == 2
def test_non_interactive_empty_results(self, tmp_path):
"""human_evaluation handles empty results file."""
from local_deep_research.benchmarks.graders import human_evaluation
input_file = tmp_path / "empty.jsonl"
input_file.write_text("")
output_file = tmp_path / "graded.jsonl"
graded = human_evaluation(
str(input_file), str(output_file), interactive=False
)
assert len(graded) == 0
class TestGradeResultsInner:
"""Tests for _grade_results_inner."""
def test_grade_results_with_progress_callback(self, tmp_path):
"""_grade_results_inner calls progress callback correctly."""
from local_deep_research.benchmarks.graders import _grade_results_inner
# Create input file
input_file = tmp_path / "results.jsonl"
input_file.write_text(
json.dumps(
{
"problem": "Q1",
"correct_answer": "42",
"response": "42",
}
)
)
output_file = tmp_path / "graded.jsonl"
mock_llm = Mock()
mock_response = Mock()
mock_response.content = (
"Extracted Answer: 42\nReasoning: ok\nCorrect: yes"
)
mock_llm.invoke.return_value = mock_response
del mock_llm.chat_messages
callback_calls = []
def progress_callback(idx, total, data):
callback_calls.append((idx, total, data))
result = _grade_results_inner(
mock_llm,
str(input_file),
str(output_file),
"simpleqa",
progress_callback,
)
assert len(result) == 1
assert result[0]["is_correct"] is True
# Should have at least 2 callbacks: grading + graded
assert len(callback_calls) >= 2
def test_grade_results_error_handling(self, tmp_path):
"""_grade_results_inner handles grading errors per result."""
from local_deep_research.benchmarks.graders import _grade_results_inner
input_file = tmp_path / "results.jsonl"
input_file.write_text(
json.dumps({"problem": "Q", "correct_answer": "A", "response": "R"})
)
output_file = tmp_path / "graded.jsonl"
mock_llm = Mock()
mock_llm.invoke.side_effect = Exception("LLM error")
del mock_llm.chat_messages
result = _grade_results_inner(
mock_llm, str(input_file), str(output_file), "simpleqa", None
)
assert len(result) == 1
assert "grading_error" in result[0]
def test_grade_results_browsecomp_extraction(self, tmp_path):
"""_grade_results_inner uses browsecomp extraction format."""
from local_deep_research.benchmarks.graders import _grade_results_inner
input_file = tmp_path / "results.jsonl"
input_file.write_text(
json.dumps(
{
"problem": "What is X?",
"correct_answer": "Y",
"response": "Y is correct",
}
)
)
output_file = tmp_path / "graded.jsonl"
mock_llm = Mock()
mock_response = Mock()
mock_response.content = (
"extracted_final_answer: Y\n"
"reasoning: Exact match\n\n"
"correct: yes\n"
"confidence: 95"
)
mock_llm.invoke.return_value = mock_response
del mock_llm.chat_messages
result = _grade_results_inner(
mock_llm, str(input_file), str(output_file), "browsecomp", None
)
assert len(result) == 1
assert result[0]["is_correct"] is True
assert result[0]["graded_confidence"] == "95"
def test_grade_results_removes_existing_output(self, tmp_path):
"""_grade_results_inner removes existing output file before writing."""
from local_deep_research.benchmarks.graders import _grade_results_inner
input_file = tmp_path / "results.jsonl"
input_file.write_text(
json.dumps({"problem": "Q", "correct_answer": "A", "response": "R"})
)
output_file = tmp_path / "graded.jsonl"
output_file.write_text("old data\n")
mock_llm = Mock()
mock_response = Mock()
mock_response.content = (
"Extracted Answer: A\nReasoning: ok\nCorrect: yes"
)
mock_llm.invoke.return_value = mock_response
del mock_llm.chat_messages
_grade_results_inner(
mock_llm, str(input_file), str(output_file), "simpleqa", None
)
# Output should not contain old data
content = output_file.read_text()
assert "old data" not in content