Native DeepSeek review loop in periscope/src (Fase C2).
Live review_ic_async comes from review_session; PinScope validate.py and validation_tools.py stay in dependency as _inherited_review_ic_async fallback.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""Prove native review parse/tools match inherited PinScope before switching the live loop."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from backend.periscopex.constraints_lookup import match_constraints
|
||||
from backend.periscopex.finding_engine import complete_finding
|
||||
from backend.periscopex.models import DesignGraph
|
||||
from backend.periscopex.review_parse import parse_submit_review
|
||||
from backend.periscopex.validate import _load_datasheets, _match_constraints, _parse_review
|
||||
from backend.periscopex.validation_tools import execute_tool as inherited_execute
|
||||
from backend.periscopex.review_tools import execute_tool as native_execute
|
||||
from tests.paths import SIMPLE_PROJECT
|
||||
|
||||
|
||||
_PAYLOAD = {
|
||||
"findings": [
|
||||
{
|
||||
"finding": "FB5 floating",
|
||||
"why": "FB5 is NC; DCDC5 SW is loaded.",
|
||||
"status": "ERROR",
|
||||
"source_page": 12,
|
||||
"source_quote": "Connect FB5 to the output sense node.",
|
||||
"recommendation": "Connect FB5.",
|
||||
},
|
||||
{
|
||||
"finding": "no quote",
|
||||
"why": "maybe missing cap",
|
||||
"status": "ERROR",
|
||||
"source_page": 3,
|
||||
"source_quote": "",
|
||||
},
|
||||
],
|
||||
"checked_areas": ["power", "decoupling"],
|
||||
}
|
||||
|
||||
|
||||
def test_native_parse_matches_inherited():
|
||||
inherited = _parse_review(_PAYLOAD, "U16", "AXP2101")
|
||||
native = parse_submit_review(_PAYLOAD, "U16", "AXP2101")
|
||||
assert len(native.findings) == len(inherited.findings)
|
||||
assert native.checked_areas == inherited.checked_areas
|
||||
for a, b in zip(native.findings, inherited.findings, strict=True):
|
||||
assert a.model_dump() == b.model_dump()
|
||||
|
||||
|
||||
def test_native_llm_finding_is_review_and_recommended_not_error():
|
||||
native = parse_submit_review(_PAYLOAD, "U16", "AXP2101")
|
||||
f = native.findings[0]
|
||||
complete_finding(f)
|
||||
assert f.finding_class == "REVIEW"
|
||||
assert f.facts == "FB5 floating"
|
||||
assert f.requirement
|
||||
assert f.status != "ERROR"
|
||||
f2 = native.findings[1]
|
||||
complete_finding(f2)
|
||||
assert f2.status != "ERROR"
|
||||
assert f2.why.startswith("Unverified:")
|
||||
|
||||
|
||||
def test_match_constraints_matches_inherited():
|
||||
datasheets = _load_datasheets(SIMPLE_PROJECT / "extracted")
|
||||
if not datasheets:
|
||||
datasheets = _load_datasheets(SIMPLE_PROJECT / "datasheets" / "extracted")
|
||||
mpns = list(datasheets)[:5] or ["MSPM0G3507SPTR"]
|
||||
for mpn in mpns:
|
||||
assert match_constraints(mpn, datasheets) == _match_constraints(mpn, datasheets)
|
||||
assert match_constraints(None, datasheets) is None
|
||||
|
||||
|
||||
def test_native_graph_tools_match_inherited_on_simple_project():
|
||||
graph = DesignGraph.model_validate(
|
||||
json.loads((SIMPLE_PROJECT / "design_graph.json").read_text())
|
||||
)
|
||||
cmap = {}
|
||||
native_txt, _ = native_execute(
|
||||
graph, cmap, "get_net_for_pin", {"designator": "U3", "pin": "1"},
|
||||
)
|
||||
inherited_txt, _ = inherited_execute(
|
||||
graph, cmap, "get_net_for_pin", {"designator": "U3", "pin": "1"},
|
||||
)
|
||||
assert native_txt == inherited_txt
|
||||
n2, _ = native_execute(
|
||||
graph, cmap, "find_connected_components",
|
||||
{"designator": "U3", "pin": "1", "designator_filter": "C"},
|
||||
)
|
||||
i2, _ = inherited_execute(
|
||||
graph, cmap, "find_connected_components",
|
||||
{"designator": "U3", "pin": "1", "designator_filter": "C"},
|
||||
)
|
||||
assert n2 == i2
|
||||
|
||||
|
||||
def test_native_session_does_not_import_pinscope_loop():
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
import backend.services.review_session as rs
|
||||
|
||||
tree = ast.parse(Path(rs.__file__).read_text())
|
||||
imported = [
|
||||
node.module
|
||||
for node in ast.walk(tree)
|
||||
if isinstance(node, ast.ImportFrom) and node.module
|
||||
]
|
||||
assert "backend.periscopex.validate" not in imported
|
||||
assert "backend.periscopex.validation_tools" not in imported
|
||||
assert "backend.services.llm" in imported
|
||||
assert "backend.periscopex.review_tools" in imported
|
||||
assert "backend.periscopex.review_parse" in imported
|
||||
|
||||
from backend.services import validation as val
|
||||
import backend.services.review_session as rs
|
||||
assert val.review_ic_async is rs.review_ic_async
|
||||
@@ -18,6 +18,7 @@ import pytest
|
||||
from pypdf import PdfWriter
|
||||
|
||||
from backend.periscopex.utils import safe_mpn
|
||||
from backend.services import review_session as review_session
|
||||
from backend.services import validation as val
|
||||
from backend.services.llm.types import Completion, ToolCall, Usage
|
||||
from backend.services.storage import LocalStorageBackend
|
||||
@@ -124,6 +125,7 @@ async def test_no_tool_calls_triggers_forced_submit_next_turn(workspace, monkeyp
|
||||
return await body(_Provider(), "fake-model")
|
||||
|
||||
monkeypatch.setattr(val, "call_with_fallback", fake_cwf)
|
||||
monkeypatch.setattr(review_session, "call_with_fallback", fake_cwf)
|
||||
|
||||
orig = val.review_ic_async
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import pytest
|
||||
from pypdf import PdfWriter
|
||||
|
||||
from backend.periscopex.utils import safe_mpn
|
||||
from backend.services import review_session as review_session
|
||||
from backend.services import validation as val
|
||||
from backend.services.llm.types import Completion, ToolCall, Usage
|
||||
from backend.services.storage import LocalStorageBackend
|
||||
@@ -136,6 +137,7 @@ async def _run(ws, monkeypatch, before_ic):
|
||||
return await body(prov_cls(), "fake-model")
|
||||
|
||||
monkeypatch.setattr(val, "call_with_fallback", fake_cwf)
|
||||
monkeypatch.setattr(review_session, "call_with_fallback", fake_cwf)
|
||||
|
||||
# The fake session needs the current ic_ref; thread it through by wrapping
|
||||
# review_ic_async to set the class attr before delegating.
|
||||
|
||||
Reference in New Issue
Block a user