fix research result handoff and synthesis provenance

This commit is contained in:
faligam
2026-09-07 13:21:33 +02:00
parent 7b279dceea
commit dc285d80bd
5 changed files with 152 additions and 23 deletions

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
import json
from uuid import uuid4
import httpx
@@ -18,6 +19,8 @@ from nsct.providers.metrics import ProviderMetrics
from nsct.providers.searxng import SearXNGProvider
from nsct.crawler.normalize import NormalizedDocument
from nsct.api.rest_research import DEPTH_CONFIGS
from nsct.models.claim import Claim, ClaimType
from nsct.stages.stage9_synthesis import _build_evidence_package_json
def _config(*, searxng_base_url: str | None = "http://searxng:8080") -> AppSettings:
@@ -206,6 +209,59 @@ def test_depth_time_budgets_allow_local_35b_inference() -> None:
assert DEPTH_CONFIGS["deep"].max_research_duration_seconds == 7_200
def test_synthesis_package_keeps_stage5_uuid_claim_provenance() -> None:
"""Stage 9 must accept the actual, UUID-bearing Stage 5 claim model."""
source_id = uuid4()
claim = Claim(
research_run_id=uuid4(),
source_id=source_id,
claim_text="Eine überprüfbare Behauptung.",
evidence_span="Die belegende Passage.",
claim_type=ClaimType.CLAIM,
source_url="https://example.org/source",
)
package = json.loads(_build_evidence_package_json([claim.model_dump()], [], "Thema"))
assert package["evidence"][0]["claim_id"] == str(claim.id)
assert package["evidence"][0]["source_id"] == str(source_id)
assert package["sources"][0]["source_id"] == str(source_id)
def test_orchestrator_synthesis_returns_a_report_for_uuid_claims() -> None:
"""The production hand-off to Stage 9 must not trigger UUID JSON errors."""
async def run() -> None:
orchestrator = ResearchOrchestrator(_config(), uuid4(), "Testthema")
await orchestrator.start()
orchestrator._state_machine = StateMachine(ResearchRunState.COMPARING)
orchestrator._claims = [
Claim(
research_run_id=uuid4(),
source_id=uuid4(),
claim_text="Eine überprüfbare Behauptung.",
evidence_span="Die belegende Passage.",
claim_type=ClaimType.CLAIM,
source_url="https://example.org/source",
)
]
provider = Mock()
provider.complete = AsyncMock(return_value=json.dumps({
"summary": "Neutraler Bericht.",
"confident_findings": [],
"uncertain_areas": [],
"contradictions": [],
}))
orchestrator._llm_provider = provider
response = await orchestrator._step_synthesizing()
assert response["success"] is True
assert response["report"]["summary"] == "Neutraler Bericht."
provider.complete.assert_awaited_once()
asyncio.run(run())
def test_start_does_not_charge_a_phantom_planner_request() -> None:
async def run() -> None:
orchestrator = ResearchOrchestrator(_config(), uuid4(), "test")

View File

@@ -334,7 +334,18 @@ def test_detail_endpoints_serialize_pipeline_uuid_identifiers(client: TestClient
}
],
evidence_scores=[{"claim_id": claim_id, "research_run_id": uuid4()}],
report={"summary": "Zusammenfassung", "findings": [], "methodology": "Methodik"},
report={
"summary": "Zusammenfassung",
"confident_findings": [
{
"claim_text": "Eine überprüfbare Behauptung.",
"confidence": 0.9,
"source_id": source_id,
}
],
"uncertain_areas": [],
"methodology": "Methodik",
},
)
sources = client.get(f"/v1/research/{research_id}/sources")
@@ -348,6 +359,8 @@ def test_detail_endpoints_serialize_pipeline_uuid_identifiers(client: TestClient
assert claims.json()["claims"][0]["source_id"] == str(source_id)
assert evidence.json()["evidence"][0]["claim_id"] == str(claim_id)
assert report.json()["methodology"] == "Methodik"
assert report.json()["findings"][0]["text"] == "Eine überprüfbare Behauptung."
assert report.json()["findings"][0]["source_ids"] == [str(source_id)]
# ---------------------------------------------------------------------------