Files
hkuds--deeptutor/tests/agents/research/test_pipeline_partial_failure.py
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

117 lines
3.9 KiB
Python

"""Deep Research must not report success when a research block fails (issue #595).
A subtopic that raises (or exhausts its iteration budget without finishing) is
backfilled with empty knowledge so the surviving subtopics still yield a report.
The fix keeps that resilience but makes the shortfall explicit in the result
envelope (``metadata.partial`` / ``failed_block_count`` / ``failed_block_titles``)
instead of returning a clean-success shape with missing evidence.
"""
from __future__ import annotations
import types
from unittest.mock import patch
import pytest
from deeptutor.agents.research.pipeline import ResearchedBlock, ResearchPipeline, SubTopicItem
from deeptutor.core.context import UnifiedContext
from deeptutor.core.stream_bus import StreamBus
pytestmark = pytest.mark.asyncio
class _FakeLLM:
binding = "openai"
model = "gpt-x"
api_key = "k"
base_url = "u"
api_version = None
extra_headers: dict = {}
reasoning_effort = None
class _FakeRegistry:
def build_openai_schemas(self, _names):
return []
def build_prompt_text(self, _names, **_kwargs):
return "- none"
def get(self, _name):
return None
def get_enabled(self, _names):
return []
def _make_pipeline() -> ResearchPipeline:
with (
patch("deeptutor.agents.research.pipeline.get_llm_config", lambda: _FakeLLM()),
patch("deeptutor.agents.research.pipeline.get_tool_registry", lambda: _FakeRegistry()),
):
return ResearchPipeline(language="en", runtime_config={"queue": {"max_length": 5}})
async def _run(pipeline: ResearchPipeline) -> dict:
async def fake_emit(*_args, **_kwargs):
return None
with patch("deeptutor.agents.research.pipeline.emit_capability_result", fake_emit):
return await pipeline._run_inner(
context=UnifiedContext(session_id="s1", user_message="research this"),
topic="Research topic",
image_attachments=[],
confirmed_outline=[SubTopicItem(title="A"), SubTopicItem(title="B")],
stream=StreamBus(),
client=None,
)
async def test_failed_block_marks_result_partial() -> None:
pipeline = _make_pipeline()
async def fake_research_block(self, *, block, queue, citations, topic, context, stream, client):
queue.mark_researching(block.block_id)
if block.block_id == "block_1":
raise RuntimeError("synthetic block failure")
queue.mark_completed(block.block_id)
return ResearchedBlock(block=block, knowledge=f"knowledge for {block.block_id}")
async def fake_write_report(self, *, topic, blocks, citations, stream, client):
return "REPORT_OK"
pipeline._research_block = types.MethodType(fake_research_block, pipeline)
pipeline._write_report = types.MethodType(fake_write_report, pipeline)
result = await _run(pipeline)
meta = result["metadata"]
assert result["response"] == "REPORT_OK" # surviving evidence still produces a report
assert meta["partial"] is True
assert meta["failed_block_count"] == 1
assert meta["failed_block_titles"] == ["A"]
assert meta["block_count"] == 2
async def test_all_blocks_complete_is_not_partial() -> None:
pipeline = _make_pipeline()
async def fake_research_block(self, *, block, queue, citations, topic, context, stream, client):
queue.mark_researching(block.block_id)
queue.mark_completed(block.block_id)
return ResearchedBlock(block=block, knowledge=f"knowledge for {block.block_id}")
async def fake_write_report(self, *, topic, blocks, citations, stream, client):
return "REPORT_OK"
pipeline._research_block = types.MethodType(fake_research_block, pipeline)
pipeline._write_report = types.MethodType(fake_write_report, pipeline)
result = await _run(pipeline)
meta = result["metadata"]
assert meta["partial"] is False
assert meta["failed_block_count"] == 0
assert meta["failed_block_titles"] == []