- codex/synthesis.py: Lead/Provenance dataclasses, find_connections/gaps/improvements, propose_conjectures (status=unverified, quarantined in leads/conjectures/), write_leads (grounded→leads/grounded/, conjectures→leads/conjectures/ HARD invariant) - codex/cli.py: synthesis leads/conjectures/report command group - codex/config.py: F-13 settings (leads_dir, synthesis_llm_*, synthesis_top_k, synthesis_min_grounded_ratio, synthesis_gap_min_coverage) - tests/synthesis/: 42 tests covering model, grounding, conjecture invariant, quarantine, CLI - spike/: F-13 spike script + output (live run attempted) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
"""Shared synthesis fixtures — keep DB/LLM/embed fully mocked."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
# Shared corpus fixture used across the test suite.
|
|
FIXTURE_CHUNKS: list[dict[str, Any]] = [
|
|
{
|
|
"id": 1,
|
|
"paper_id": "springborn-2008",
|
|
"ord": 16,
|
|
"content": (
|
|
"For an ideal tetrahedron with dihedral angles the hyperbolic volume is "
|
|
"V = L(gamma1) + L(gamma2) + L(gamma3). "
|
|
"The volume function V0 is strictly concave on the angle domain."
|
|
),
|
|
"bibkey": "Springborn2008",
|
|
},
|
|
{
|
|
"id": 2,
|
|
"paper_id": "bps-2015",
|
|
"ord": 3,
|
|
"content": (
|
|
"A discrete conformal map is defined by logarithmic scale factors u_i "
|
|
"at each vertex such that the edge lengths satisfy a compatibility condition."
|
|
),
|
|
"bibkey": "BobenkoPinkallSpringborn2015",
|
|
},
|
|
{
|
|
"id": 3,
|
|
"paper_id": "luo-2004",
|
|
"ord": 0,
|
|
"content": (
|
|
"The combinatorial Yamabe flow drives the edge lengths towards a target "
|
|
"curvature on a triangulated surface."
|
|
),
|
|
"bibkey": "Luo2004",
|
|
},
|
|
]
|
|
|
|
|
|
class StubLLM:
|
|
"""Deterministic mock LLM that returns a preset response."""
|
|
|
|
def __init__(self, response: str | list[str]) -> None:
|
|
if isinstance(response, str):
|
|
self._responses = [response]
|
|
else:
|
|
self._responses = list(response)
|
|
self._calls: list[tuple[str, str]] = []
|
|
|
|
def generate(self, prompt: str, model: str) -> str:
|
|
self._calls.append((prompt, model))
|
|
if not self._responses:
|
|
return ""
|
|
if len(self._responses) == 1:
|
|
return self._responses[0]
|
|
return self._responses.pop(0)
|
|
|
|
@property
|
|
def calls(self) -> list[tuple[str, str]]:
|
|
return list(self._calls)
|
|
|
|
|
|
@pytest.fixture
|
|
def stub_llm() -> StubLLM:
|
|
"""Default stub returning empty string (override per test as needed)."""
|
|
return StubLLM("")
|
|
|
|
|
|
@pytest.fixture
|
|
def fixture_chunks() -> list[dict[str, Any]]:
|
|
return [dict(c) for c in FIXTURE_CHUNKS]
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_retrieve(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Patch _retrieve_chunks to return deterministic fixture chunks (no DB)."""
|
|
monkeypatch.setattr(
|
|
"codex.synthesis._retrieve_chunks",
|
|
lambda queries, top_k: [dict(c) for c in FIXTURE_CHUNKS],
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_paper_titles(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Patch _list_paper_titles to return a deterministic three-paper corpus."""
|
|
monkeypatch.setattr(
|
|
"codex.synthesis._list_paper_titles",
|
|
lambda db_conn: [
|
|
("Springborn2008", "A variational principle for weighted Delaunay triangulations"),
|
|
(
|
|
"BobenkoPinkallSpringborn2015",
|
|
"Discrete conformal maps and ideal hyperbolic polyhedra",
|
|
),
|
|
("Luo2004", "Combinatorial Yamabe Flow on Surfaces"),
|
|
],
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_settings(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path:
|
|
"""Settings stub pointing leads_dir at tmp_path/leads."""
|
|
from codex.config import Settings
|
|
|
|
leads_path = tmp_path / "leads"
|
|
|
|
mock = MagicMock(spec=Settings)
|
|
mock.leads_dir = str(leads_path)
|
|
mock.synthesis_llm_model = "test-model"
|
|
mock.synthesis_llm_url = None
|
|
mock.ollama_base_url = "http://localhost:11434"
|
|
mock.synthesis_top_k = 6
|
|
mock.synthesis_min_grounded_ratio = 0.5
|
|
mock.synthesis_gap_min_coverage = 2
|
|
mock.wiki_dir = str(tmp_path / "wiki")
|
|
|
|
monkeypatch.setattr("codex.synthesis.get_settings", lambda: mock)
|
|
return leads_path
|