refactor(evidence): unify formulas with typed evidence

This commit is contained in:
2026-08-25 01:23:04 +02:00
parent d5d65f3659
commit cc30148b69
11 changed files with 342 additions and 38 deletions
+82
View File
@@ -5,8 +5,16 @@ Completes the formula layer beyond the store: the `auto` status the spec require
and the guarantee that load_evidence_dir does NOT choke on *.sql.md formula files when
they live under the evidence root.
"""
import json
from types import SimpleNamespace
from typer.testing import CliRunner
from tht.cli import app, search_cmd
from tht.evidence import formula_store
from tht.evidence.formula_store import ConceptFormula, save_formula, search_formulas
from tht.evidence.model import EvidenceDoc, load_evidence_dir
from tht.evidence.search import EvidenceSearchOutcome
def test_status_auto_is_valid():
@@ -35,3 +43,77 @@ def test_load_evidence_dir_skips_formula_files(tmp_path):
ids = [d.id for d in docs]
assert ids == ["ev1"] # the .sql.md formula file is skipped, no crash
assert all(isinstance(d, EvidenceDoc) for d in docs)
def test_unreviewed_legacy_formulas_cannot_become_curated_evidence():
for status in ("auto", "draft"):
formula = ConceptFormula(
concept="fascia pediatrica",
columns=["clinical.patient.birth_date"],
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
status=status,
)
assert formula_store.legacy_formula_to_curated(
formula, legacy_path="formulas/pediatric-1.sql.md",
) is None
def test_formula_search_uses_typed_evidence_with_a_formula_constraint():
class Searcher:
vector_generation = "gen:" + "a" * 32
def __init__(self):
self.calls = []
def search(self, embedding, **kwargs):
self.calls.append((embedding, kwargs))
return [SimpleNamespace(
id="fragment:formula", similarity=0.9, content="formula excerpt",
title="Fascia pediatrica", metadata={
"evidence_id": "evidence:fascia-pediatrica",
"evidence_kind": "formula",
"document_id": "doc:formula",
"ordinal": 0,
"source_uri": "file:///curated/formula/fascia-pediatrica.md",
"provenance": {},
},
)]
class Embedder:
def embed_query(self, query):
return [0.25]
searcher = Searcher()
outcome = search_cmd.search_formula_evidence(
"fascia pediatrica", searcher=searcher, embedder=Embedder(), top=3,
)
assert outcome.status == "available"
assert [result.evidence_id for result in outcome.results] == ["evidence:fascia-pediatrica"]
assert searcher.calls[0][1]["metadata_filter"]["required_kinds"] == ["formula"]
def test_formula_search_json_is_pristine_while_human_output_warns_about_legacy_store(monkeypatch):
monkeypatch.setattr(search_cmd, "_load_config_or_exit", lambda _path: SimpleNamespace(embeddings=object()))
monkeypatch.setattr(search_cmd, "workspace_id_for_config", lambda _cfg, _path: "workspace-a")
monkeypatch.setattr(search_cmd, "search_formula_evidence", lambda *args, **kwargs: EvidenceSearchOutcome(
"available", "gen:" + "a" * 32,
))
monkeypatch.setattr("tht.cli.vector_cmd.require_vector_cfg", lambda _cfg: None)
monkeypatch.setattr("tht.cli.vector_cmd.open_searcher", lambda _cfg: object())
monkeypatch.setattr("tht.cli.vector_cmd.make_embedder", lambda _cfg: object())
monkeypatch.setattr("tht.evidence.active_searcher", lambda *args, **kwargs: object())
monkeypatch.setattr("tht.evidence.validate_corpus_workspace", lambda _cfg, _workspace: None)
json_result = CliRunner().invoke(
app, ["search", "find", "fascia pediatrica", "--kind", "formula", "--top", "1", "--json"],
)
human_result = CliRunner().invoke(
app, ["search", "find", "fascia pediatrica", "--kind", "formula", "--top", "1"],
)
assert json_result.exit_code == 0
assert json.loads(json_result.stdout) == []
assert "ATTENZIONE" not in json_result.stdout
assert human_result.exit_code == 0
assert "ATTENZIONE" in human_result.output