Reject WROOM pintables taken from the bare ESP32 die map.
Module pad 1 is GND; extracting ANT/XTAL as pin 1 made every ground look like a shorted antenna. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -148,8 +148,12 @@ async def run_skill_locally(
|
||||
|
||||
errors: list[str] = []
|
||||
if validator is not None:
|
||||
check = dict(payload)
|
||||
mpn_hint = re.search(r"MPN:\s*(\S+)", user_text or "", re.I)
|
||||
if mpn_hint and "mpn" not in check:
|
||||
check["mpn"] = mpn_hint.group(1).rstrip(".,;")
|
||||
try:
|
||||
errors = list(validator(payload) or [])
|
||||
errors = list(validator(check) or [])
|
||||
except Exception as exc:
|
||||
log.warning(
|
||||
"Skill %s validate.py raised: %s", skill_name, exc,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"default_model_version": "1.5.0",
|
||||
"default_model_version": "1.6.0",
|
||||
"extract-pintable": {
|
||||
"skill_id": "skill_01VMWPZuvuZAe4LmLbmsNWNY",
|
||||
"latest_version": "1784798970179642",
|
||||
|
||||
@@ -30,6 +30,9 @@ Rules for pin extraction:
|
||||
- For multiplexed pins, put the primary name in `name` and alternates in `functions`
|
||||
- If the datasheet has separate tables for different packages, extract for the package matching the MPN
|
||||
- Pay careful attention to pin numbering — off-by-one errors here break everything downstream
|
||||
- **Modules vs bare die (critical).** MPNs containing `WROOM`, `WROVER`, `MODULE`, `MOD-`, or `SIP` are *modules*. Extract the **module landing-pad table** (connector pins the schematic uses). Do **not** extract the SoC/QFN ball map from a nested chip chapter or a sibling chip-only PDF.
|
||||
- Espressif WROOM: pin 1 is GND (often a group of GND pads). Pin 1 named `ANT`, `CHIP_PU`, or `XTAL_*` means you grabbed the bare ESP32 die table — that will mark every module GND as “antenna shorted” and is invalid.
|
||||
- Crystal, RF antenna, and flash on a WROOM module are **inside the can**; they must not appear as schematic pin numbers.
|
||||
|
||||
### 3. Extract package info
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"""Validate extraction output against the pintable schema."""
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -47,6 +48,24 @@ def validate(data: dict) -> list[str]:
|
||||
if dupes:
|
||||
errors.append(f"Duplicate pin numbers: {dupes}")
|
||||
|
||||
names = {
|
||||
str(p.get("number")): str(p.get("name") or "").upper()
|
||||
for p in pins if "number" in p
|
||||
}
|
||||
pin1 = names.get("1", "")
|
||||
looks_like_rf_die = bool(
|
||||
re.search(r"\bANT\b|^CHIP_PU$|^XTAL", pin1)
|
||||
and any("XTAL" in n for n in names.values())
|
||||
)
|
||||
mpn = str(data.get("mpn") or "")
|
||||
is_module_mpn = bool(re.search(r"WROOM|WROVER|\bMODULE\b|\bSIP\b", mpn, re.I))
|
||||
if looks_like_rf_die and is_module_mpn:
|
||||
errors.append(
|
||||
"Pin 1 looks like a bare RF SoC ball (ANT/CHIP_PU) with XTAL "
|
||||
"pins in the table. Module footprints (WROOM) use pad 1 = GND; "
|
||||
"extract the module landing-pad table, not the die map."
|
||||
)
|
||||
|
||||
if "absolute_maximum_ratings" in data:
|
||||
ratings = data["absolute_maximum_ratings"]
|
||||
if ratings is not None and not isinstance(ratings, list):
|
||||
|
||||
@@ -212,6 +212,7 @@ def test_local_skills_load():
|
||||
validate = load_skill_validator("extract-pintable")
|
||||
assert validate is not None
|
||||
errors = validate({
|
||||
"mpn": "MSPM0G3507SPTR",
|
||||
"component_subtype": "ic.mcu",
|
||||
"component_subtype_description": "MCU",
|
||||
"package_info": {"base_family": "MSPM0", "package": "LQFP-48", "pin_count": 2},
|
||||
@@ -223,6 +224,23 @@ def test_local_skills_load():
|
||||
assert errors == []
|
||||
|
||||
|
||||
def test_wroom_rejects_bare_soc_pin1_ant():
|
||||
from backend.services.llm.local_skill import load_skill_validator
|
||||
validate = load_skill_validator("extract-pintable")
|
||||
errors = validate({
|
||||
"mpn": "ESP32-S31-WROOM-3",
|
||||
"component_subtype": "ic.mcu",
|
||||
"package_info": {"base_family": "ESP32-S31", "package": "module", "pin_count": 2},
|
||||
"pintable": [
|
||||
{"number": 1, "name": "ANT"},
|
||||
{"number": 2, "name": "CHIP_PU"},
|
||||
{"number": 78, "name": "XTAL_N"},
|
||||
{"number": 79, "name": "XTAL_P"},
|
||||
],
|
||||
})
|
||||
assert any("module" in e.lower() or "pad" in e.lower() or "SoC" in e or "WROOM" in e for e in errors)
|
||||
|
||||
|
||||
def test_factory_routes_deepseek(monkeypatch):
|
||||
monkeypatch.setattr(settings, "deepseek_api_key", "sk-test")
|
||||
from backend.services.llm.factory import get_provider_by_name
|
||||
|
||||
Reference in New Issue
Block a user