Native DeepSeek datasheet extraction in periscope/src (Fase C3).

Pipeline and LCSC resolve call datasheet_extract; inherited extraction.py
stays in dependency as fallback. No Anthropic Console skill ids.
This commit is contained in:
2026-09-20 15:26:21 +02:00
parent a96ee2e88a
commit 5c0c5184fb
11 changed files with 1351 additions and 18 deletions
+31
View File
@@ -0,0 +1,31 @@
"""Native datasheet_extract vs inherited extraction — prove before switching pipeline."""
from __future__ import annotations
import ast
from pathlib import Path
from backend.services.datasheet_extract import _coerce_abs_max
from backend.services.extraction import _coerce_abs_max as inherited_coerce
def test_coerce_abs_max_matches_inherited():
raw = [
{"parameter": "Vin", "min": 0, "max": 6, "unit": "V", "source_page": 4},
"not-a-dict",
]
assert _coerce_abs_max(raw) == inherited_coerce(raw)
assert _coerce_abs_max(None) == inherited_coerce(None)
def test_native_extract_has_no_anthropic_import():
path = Path(__import__("backend.services.datasheet_extract", fromlist=["x"]).__file__)
tree = ast.parse(path.read_text())
imported = []
for node in ast.walk(tree):
if isinstance(node, ast.ImportFrom) and node.module:
imported.append(node.module)
elif isinstance(node, ast.Import):
imported.extend(a.name for a in node.names)
assert not any("anthropic" in (m or "") for m in imported)
assert "backend.services.llm.factory" in imported
+4 -4
View File
@@ -86,12 +86,12 @@ def test_auto_resolve_skips_llm_when_catalog_parses(monkeypatch):
import asyncio
from pathlib import Path
from backend.services.extraction import auto_resolve_specs
from backend.services.datasheet_extract import auto_resolve_specs
async def boom(*_a, **_k):
raise AssertionError("LLM must not run")
monkeypatch.setattr("backend.services.extraction.call_with_fallback", boom)
monkeypatch.setattr("backend.services.datasheet_extract.call_with_fallback", boom)
tax = TAXONOMY
model = asyncio.run(
auto_resolve_specs(
@@ -110,12 +110,12 @@ def test_auto_resolve_use_llm_false_on_ferrite(monkeypatch):
import asyncio
from pathlib import Path
from backend.services.extraction import auto_resolve_specs
from backend.services.datasheet_extract import auto_resolve_specs
async def boom(*_a, **_k):
raise AssertionError("LLM must not run")
monkeypatch.setattr("backend.services.extraction.call_with_fallback", boom)
monkeypatch.setattr("backend.services.datasheet_extract.call_with_fallback", boom)
tax = TAXONOMY
model = asyncio.run(
auto_resolve_specs(
+4 -4
View File
@@ -52,12 +52,12 @@ def test_ambiguous_string_returns_none():
def test_resolve_from_value_skips_llm_when_parseable(monkeypatch):
from backend.services.extraction import resolve_from_value
from backend.services.datasheet_extract import resolve_from_value
async def boom(*_a, **_k):
raise AssertionError("LLM must not run")
monkeypatch.setattr("backend.services.extraction.call_with_fallback", boom)
monkeypatch.setattr("backend.services.datasheet_extract.call_with_fallback", boom)
tax = TAXONOMY
model = asyncio.run(
resolve_from_value(
@@ -71,12 +71,12 @@ def test_resolve_from_value_skips_llm_when_parseable(monkeypatch):
def test_resolve_from_value_placeholder_no_llm(monkeypatch):
from backend.services.extraction import resolve_from_value
from backend.services.datasheet_extract import resolve_from_value
async def boom(*_a, **_k):
raise AssertionError("LLM must not run")
monkeypatch.setattr("backend.services.extraction.call_with_fallback", boom)
monkeypatch.setattr("backend.services.datasheet_extract.call_with_fallback", boom)
tax = TAXONOMY
with pytest.raises(ValueError, match="Placeholder"):
asyncio.run(
+1 -1
View File
@@ -4,7 +4,7 @@ from __future__ import annotations
from pathlib import Path
from backend.services.extraction import _coerce_abs_max
from backend.services.datasheet_extract import _coerce_abs_max
from backend.services.llm.pdf_ingest import (
extract_pdf_text,
make_text_pdf,
+1 -1
View File
@@ -452,7 +452,7 @@ async def test_lcsc_resolve_passive_success_and_cached(tmp_path, monkeypatch):
from backend.config import settings
from backend.periscopex.models import CapacitorSpecs, ComponentModel
from backend.services import purple_parts
from backend.services import extraction as extraction_svc
from backend.services import datasheet_extract as extraction_svc
# Billing modules are absent in the open-source repo; the endpoint under
# test only charges when billing is enabled, so skip there.