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

746 lines
26 KiB
Python

"""
Tests for benchmarks/graders.py
Tests cover:
- extract_answer_from_response function
- grade_single_result with mocked LLM
- get_evaluation_llm configuration
"""
from unittest.mock import Mock, patch, MagicMock
class TestExtractAnswerFromResponse:
"""Tests for the extract_answer_from_response function."""
def test_browsecomp_extracts_exact_answer(self):
"""Test extraction of exact answer from BrowseComp response."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = """
Based on my research, I found the following information.
Exact Answer: 42
Confidence: 95%
"""
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "42"
assert result["confidence"] == "95"
def test_browsecomp_missing_answer_returns_none(self):
"""Test handling of missing answer in BrowseComp response."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "Some response without the expected format"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "None"
assert result["confidence"] == "100"
def test_browsecomp_missing_confidence_defaults(self):
"""Test that missing confidence defaults to 100."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "Exact Answer: Paris"
result = extract_answer_from_response(
response, dataset_type="browsecomp"
)
assert result["extracted_answer"] == "Paris"
assert result["confidence"] == "100"
def test_simpleqa_returns_full_response(self):
"""Test that SimpleQA returns the full response as the answer."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "The capital of France is Paris."
result = extract_answer_from_response(response, dataset_type="simpleqa")
assert result["extracted_answer"] == response
assert result["confidence"] == "100"
def test_removes_citations_from_response(self):
"""Test that citations are removed from the response."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "The answer is 42 [1] according to the source [2][3]."
result = extract_answer_from_response(response, dataset_type="simpleqa")
assert "[1]" not in result["extracted_answer"]
assert "[2]" not in result["extracted_answer"]
assert "[3]" not in result["extracted_answer"]
assert "42" in result["extracted_answer"]
def test_browsecomp_case_insensitive(self):
"""Test that dataset type matching is case insensitive."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "Exact Answer: test\nConfidence: 80%"
result = extract_answer_from_response(
response, dataset_type="BROWSECOMP"
)
assert result["extracted_answer"] == "test"
assert result["confidence"] == "80"
class TestGetEvaluationLLM:
"""Tests for the get_evaluation_llm function."""
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_uses_default_config(self, mock_get_llm):
"""Test that default config is used."""
from local_deep_research.benchmarks.graders import (
get_evaluation_llm,
DEFAULT_EVALUATION_CONFIG,
)
mock_get_llm.return_value = Mock()
get_evaluation_llm()
mock_get_llm.assert_called_once()
call_kwargs = mock_get_llm.call_args[1]
assert (
call_kwargs["model_name"] == DEFAULT_EVALUATION_CONFIG["model_name"]
)
assert (
call_kwargs["temperature"]
== DEFAULT_EVALUATION_CONFIG["temperature"]
)
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_custom_config_overrides_defaults(self, mock_get_llm):
"""Test that custom config overrides defaults."""
from local_deep_research.benchmarks.graders import get_evaluation_llm
mock_get_llm.return_value = Mock()
custom_config = {
"model_name": "custom-model",
"temperature": 0.5,
}
get_evaluation_llm(custom_config=custom_config)
call_kwargs = mock_get_llm.call_args[1]
assert call_kwargs["model_name"] == "custom-model"
assert call_kwargs["temperature"] == 0.5
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_filters_unsupported_params(self, mock_get_llm):
"""Test that unsupported parameters are filtered out."""
from local_deep_research.benchmarks.graders import get_evaluation_llm
mock_get_llm.return_value = Mock()
custom_config = {
"model_name": "test-model",
"unsupported_param": "value",
"max_tokens": 1000, # Not supported by LDR's get_llm
}
get_evaluation_llm(custom_config=custom_config)
call_kwargs = mock_get_llm.call_args[1]
assert "unsupported_param" not in call_kwargs
assert "max_tokens" not in call_kwargs
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_extracts_api_key_from_settings_snapshot(self, mock_get_llm):
"""Test that API key is extracted from settings snapshot."""
from local_deep_research.benchmarks.graders import get_evaluation_llm
mock_get_llm.return_value = Mock()
settings_snapshot = {
"llm.openai_endpoint.api_key": {"value": "test-api-key"}
}
get_evaluation_llm(settings_snapshot=settings_snapshot)
# Should not raise and should call get_llm
mock_get_llm.assert_called_once()
class TestGradeSingleResult:
"""Tests for the grade_single_result function."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grades_correctly(self, mock_get_eval_llm):
"""Test that grade_single_result grades correctly."""
from local_deep_research.benchmarks.graders import grade_single_result
# Mock the LLM response
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = """
Extracted Answer: Paris
Reasoning: The model correctly identified Paris as the capital of France.
Correct: yes
"""
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "What is the capital of France?",
"correct_answer": "Paris",
"response": "The capital of France is Paris.",
}
graded = grade_single_result(result_data, dataset_type="simpleqa")
assert graded["is_correct"] is True
assert "Paris" in graded["extracted_by_grader"]
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_handles_grading_error(self, mock_get_eval_llm):
"""Test that grade_single_result handles errors gracefully."""
from local_deep_research.benchmarks.graders import grade_single_result
# Mock the LLM to raise an error during invoke (inside the try block)
mock_llm = MagicMock()
mock_llm.invoke.side_effect = Exception("LLM invoke error")
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "test",
"correct_answer": "answer",
"response": "response",
}
graded = grade_single_result(result_data)
assert graded["is_correct"] is False
assert "grading_error" in graded
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_browsecomp_grading_format(self, mock_get_eval_llm):
"""Test BrowseComp-specific grading format extraction."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = """
extracted_final_answer: 42
reasoning: The model found the correct answer by analyzing the data.
correct: yes
confidence: 95
"""
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "What is the answer?",
"correct_answer": "42",
"response": "The answer is 42.",
}
graded = grade_single_result(result_data, dataset_type="browsecomp")
assert graded["is_correct"] is True
assert graded["extracted_by_grader"] == "42"
assert graded["graded_confidence"] == "95"
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grading_with_no_judgment(self, mock_get_eval_llm):
"""Test grading when LLM doesn't provide clear judgment."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = "Some response without proper format"
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "test",
"correct_answer": "answer",
"response": "response",
}
graded = grade_single_result(result_data, dataset_type="simpleqa")
# Should default to False when no clear judgment
assert graded["is_correct"] is False
assert graded["extracted_by_grader"] == "None"
class TestGradeResults:
"""Tests for grade_results function (batch grading)."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_results_processes_all_items(self, mock_get_eval_llm):
"""Test that grade_results processes all items in file."""
import tempfile
import json
from local_deep_research.benchmarks.graders import grade_results
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = """
Extracted Answer: test
Reasoning: Test reasoning
Correct: yes
"""
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
with tempfile.TemporaryDirectory() as tmpdir:
# Create input file
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
for i in range(3):
f.write(
json.dumps(
{
"problem": f"Question {i}",
"correct_answer": f"Answer {i}",
"response": f"Response {i}",
}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
results = grade_results(input_file, output_file)
assert len(results) == 3
assert all(r["is_correct"] for r in results)
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_results_invokes_progress_callback(self, mock_get_eval_llm):
"""Test that progress callback is invoked during grading."""
import tempfile
import json
from local_deep_research.benchmarks.graders import grade_results
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = (
"Extracted Answer: test\nReasoning: test\nCorrect: yes"
)
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
callback_invocations = []
def progress_callback(idx, total, data):
callback_invocations.append(
{"idx": idx, "total": total, "data": data}
)
with tempfile.TemporaryDirectory() as tmpdir:
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
f.write(
json.dumps(
{
"problem": "Q",
"correct_answer": "A",
"response": "R",
}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
grade_results(
input_file, output_file, progress_callback=progress_callback
)
# Should have multiple invocations (grading and graded)
assert len(callback_invocations) >= 2
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_results_handles_errors_gracefully(self, mock_get_eval_llm):
"""Test that grade_results handles individual grading errors."""
import tempfile
import json
from local_deep_research.benchmarks.graders import grade_results
mock_llm = MagicMock()
# First call succeeds, second fails
mock_response = MagicMock()
mock_response.content = "Extracted Answer: test\nCorrect: yes"
mock_llm.invoke.side_effect = [
mock_response,
Exception("Grading error"),
]
mock_get_eval_llm.return_value = mock_llm
with tempfile.TemporaryDirectory() as tmpdir:
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
for i in range(2):
f.write(
json.dumps(
{
"problem": f"Q{i}",
"correct_answer": f"A{i}",
"response": f"R{i}",
}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
results = grade_results(input_file, output_file)
# Should have both results (one success, one error)
assert len(results) == 2
# First should be correct
assert results[0]["is_correct"] is True
# Second should have error
assert "grading_error" in results[1]
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_results_writes_output_file(self, mock_get_eval_llm):
"""Test that grade_results writes to output file."""
import tempfile
import json
from local_deep_research.benchmarks.graders import grade_results
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = "Extracted Answer: test\nCorrect: yes"
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
with tempfile.TemporaryDirectory() as tmpdir:
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
f.write(
json.dumps(
{"problem": "Q", "correct_answer": "A", "response": "R"}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
grade_results(input_file, output_file)
# Output file should exist
with open(output_file, "r") as f:
lines = f.readlines()
assert len(lines) == 1
result = json.loads(lines[0])
assert "is_correct" in result
class TestHumanEvaluation:
"""Tests for human_evaluation function."""
def test_human_evaluation_noninteractive_mode(self):
"""Test human evaluation in non-interactive mode."""
import tempfile
import json
from local_deep_research.benchmarks.graders import human_evaluation
with tempfile.TemporaryDirectory() as tmpdir:
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
f.write(
json.dumps(
{
"problem": "What is 2+2?",
"correct_answer": "4",
"response": "The answer is 4.",
"extracted_answer": "4",
}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
results = human_evaluation(
input_file, output_file, interactive=False
)
assert len(results) == 1
# Non-interactive defaults to is_correct=False
assert results[0]["is_correct"] is False
assert results[0]["human_evaluation"] is True
def test_human_evaluation_writes_output(self):
"""Test that human evaluation writes to output file."""
import tempfile
import json
from local_deep_research.benchmarks.graders import human_evaluation
with tempfile.TemporaryDirectory() as tmpdir:
input_file = f"{tmpdir}/input.jsonl"
with open(input_file, "w") as f:
f.write(
json.dumps(
{
"problem": "Q",
"correct_answer": "A",
"response": "R",
}
)
+ "\n"
)
output_file = f"{tmpdir}/output.jsonl"
human_evaluation(input_file, output_file, interactive=False)
with open(output_file, "r") as f:
lines = f.readlines()
assert len(lines) == 1
result = json.loads(lines[0])
assert "human_evaluation" in result
assert result["human_evaluation"] is True
class TestGradeSingleResultEdgeCases:
"""Edge case tests for grade_single_result."""
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_with_empty_response(self, mock_get_eval_llm):
"""Test grading with empty model response."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = ""
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "Question",
"correct_answer": "Answer",
"response": "",
}
graded = grade_single_result(result_data)
assert graded["is_correct"] is False
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_with_llm_no_invoke(self, mock_get_eval_llm):
"""Test grading when LLM doesn't have invoke method."""
from local_deep_research.benchmarks.graders import grade_single_result
# Create LLM without invoke method but with close for cleanup
mock_llm = MagicMock(spec=["close"])
mock_llm.__call__ = MagicMock(
return_value="Extracted Answer: test\nCorrect: yes"
)
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "Q",
"correct_answer": "A",
"response": "R",
}
graded = grade_single_result(result_data)
# Should still work via fallback
assert "is_correct" in graded
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_with_chat_messages_attribute(self, mock_get_eval_llm):
"""Test grading with LLM that has chat_messages attribute."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_llm.chat_messages = True # Has this attribute
mock_response = MagicMock()
mock_response.content = "Extracted Answer: test\nCorrect: yes"
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "Q",
"correct_answer": "A",
"response": "R",
}
graded = grade_single_result(result_data)
assert graded["is_correct"] is True
# Should have called invoke with HumanMessage
mock_llm.invoke.assert_called_once()
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_simpleqa_correct_no(self, mock_get_eval_llm):
"""Test SimpleQA grading with 'no' judgment."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = """
Extracted Answer: wrong answer
Reasoning: The model's answer is incorrect.
Correct: no
"""
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
result_data = {
"problem": "What is 2+2?",
"correct_answer": "4",
"response": "The answer is 5.",
}
graded = grade_single_result(result_data, dataset_type="simpleqa")
assert graded["is_correct"] is False
@patch("local_deep_research.benchmarks.graders.get_evaluation_llm")
def test_grade_preserves_settings_snapshot(self, mock_get_eval_llm):
"""Test that settings_snapshot is passed to get_evaluation_llm."""
from local_deep_research.benchmarks.graders import grade_single_result
mock_llm = MagicMock()
mock_response = MagicMock()
mock_response.content = "Extracted Answer: test\nCorrect: yes"
mock_llm.invoke.return_value = mock_response
mock_get_eval_llm.return_value = mock_llm
settings_snapshot = {"llm.api_key": "test-key"}
result_data = {
"problem": "Q",
"correct_answer": "A",
"response": "R",
}
grade_single_result(result_data, settings_snapshot=settings_snapshot)
# Verify settings_snapshot was passed
mock_get_eval_llm.assert_called_once()
call_args = mock_get_eval_llm.call_args
assert (
call_args[0][1] == settings_snapshot
or call_args[1].get("settings_snapshot") == settings_snapshot
)
class TestExtractAnswerEdgeCases:
"""Edge case tests for extract_answer_from_response."""
def test_extract_handles_multiline_answer(self):
"""Test extraction of multiline answers."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = """Based on my research:
Exact Answer: This is a
multiline answer
Confidence: 90%
"""
result = extract_answer_from_response(response, "browsecomp")
# Should capture first line after "Exact Answer:"
assert "This is a" in result["extracted_answer"]
def test_extract_handles_special_characters(self):
"""Test extraction handles special characters."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "The answer is: $100 (USD) [according to source]."
result = extract_answer_from_response(response, "simpleqa")
# Only numeric citations like [1], [2] are removed
# Text-based brackets like [according to source] are preserved
assert "$100" in result["extracted_answer"]
def test_extract_empty_response(self):
"""Test extraction with empty response."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
result = extract_answer_from_response("", "simpleqa")
assert result["extracted_answer"] == ""
assert result["confidence"] == "100"
def test_extract_browsecomp_no_exact_answer(self):
"""Test BrowseComp extraction without 'Exact Answer' marker."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "The value is 42."
result = extract_answer_from_response(response, "browsecomp")
assert result["extracted_answer"] == "None"
def test_extract_removes_multiple_citations(self):
"""Test that multiple citations are all removed."""
from local_deep_research.benchmarks.graders import (
extract_answer_from_response,
)
response = "First point [1], second point [2], third point [3][4][5]."
result = extract_answer_from_response(response, "simpleqa")
assert "[1]" not in result["extracted_answer"]
assert "[2]" not in result["extracted_answer"]
assert "[5]" not in result["extracted_answer"]
class TestSettingsSnapshotPropagation:
"""get_evaluation_llm must thread settings_snapshot into get_llm.
Regression (PR #4300 review): the snapshot was used only to read the
api_key and never passed to get_llm. Combined with the new snapshot-less
fail-closed LLM gate, a cloud grader (openai) was refused with
PolicyDeniedError because get_llm saw no snapshot to evaluate policy.
"""
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_snapshot_forwarded_to_get_llm(self, mock_get_llm):
from local_deep_research.benchmarks.graders import get_evaluation_llm
mock_get_llm.return_value = Mock()
snapshot = {"policy.egress_scope": "both", "llm.provider": "openai"}
get_evaluation_llm(settings_snapshot=snapshot)
call_kwargs = mock_get_llm.call_args[1]
assert "settings_snapshot" in call_kwargs, (
"get_evaluation_llm must forward settings_snapshot to get_llm"
)
assert call_kwargs["settings_snapshot"] is snapshot
@patch("local_deep_research.benchmarks.graders.get_llm")
def test_none_snapshot_still_forwarded(self, mock_get_llm):
from local_deep_research.benchmarks.graders import get_evaluation_llm
mock_get_llm.return_value = Mock()
get_evaluation_llm()
call_kwargs = mock_get_llm.call_args[1]
# Explicitly forwarded (as None) — get_llm's own gate then decides.
assert call_kwargs.get("settings_snapshot") is None