Pinscope open-source core
Agentic schematic validation: datasheet extraction via Claude Console Skills, netlist/BOM design graph, per-IC direct datasheet review with page citations, capacitor derating, Next.js report UI. Extracted from the Pinscope cloud codebase. Auth and billing live in the private gateway repo behind stable seams (billing_hook.py, adapter files listed in CLAUDE.md).
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
---
|
||||
skill_name: extract-specs
|
||||
description: Extract pin table, package info, and electrical specifications from a discrete/simple component datasheet PDF. Returns structured data via the save_specs tool.
|
||||
---
|
||||
|
||||
# Extract Component Specifications & Pin Table
|
||||
|
||||
Extract the pin table, package info, and key electrical specifications from a component datasheet and return them as structured JSON via the `save_specs` tool.
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Read the datasheet PDF
|
||||
|
||||
The datasheet PDF is provided in the user message. Focus on these sections:
|
||||
- **Pin configuration / pin assignment table** — Pin number, pin name, description
|
||||
- **Package information** — Pin count, package type
|
||||
- **Electrical characteristics** — The primary source of parameter values
|
||||
- **Absolute maximum ratings** — Maximum voltage, current, and power limits
|
||||
|
||||
### 2. Identify the component subtype
|
||||
|
||||
The system prompt provides a list of taxonomy subtypes. Choose the best match for this component. If none match, propose a new subtype following the dotted naming convention.
|
||||
|
||||
### 3. Extract the pin table
|
||||
|
||||
For every pin on the component, extract:
|
||||
- `number` (int or str) — The pin number as printed in the datasheet
|
||||
- `name` (str) — The pin name exactly as printed (e.g., `"A"` for anode, `"K"` for cathode, `"G"` for gate)
|
||||
- `description` (str or null) — A brief description if the datasheet provides one
|
||||
- `functions` (list[str] or null) — Alternate functions if the pin supports them
|
||||
|
||||
Rules for pin extraction:
|
||||
- Include ALL pins — including pad/tab/exposed pad pins
|
||||
- Use pin names verbatim from the datasheet — do not rename or normalize
|
||||
- Pay careful attention to pin numbering — off-by-one errors break downstream validation
|
||||
- For multi-pin packages (e.g., SOT-23 transistor), ensure the pin assignment matches the specific package variant
|
||||
|
||||
### 4. Extract package info
|
||||
|
||||
Decode the MPN and package details:
|
||||
- `base_family` (str) — The base part family (e.g., `"BAT54"` from `"BAT54S"`)
|
||||
- `package` (str) — Package name (e.g., `"SOT-23"`, `"SOD-123"`, `"TO-220"`)
|
||||
- `pin_count` (int) — Number of pins
|
||||
- `description` (str) — Human-readable decoding of the full MPN
|
||||
|
||||
### 5. Extract specifications
|
||||
|
||||
The system prompt contains a "PARAMETERS TO EXTRACT" section listing the **ONLY** parameters you should extract. These are the standardized parameters for this component type that are useful for schematic validation.
|
||||
|
||||
**CRITICAL: Extract ONLY the parameters listed in "PARAMETERS TO EXTRACT".** Do not add any other parameters, even if they appear in the datasheet. Parameters like contact material, insulator material, processing temperature, orientation, mounting type, plating, etc. are NOT useful for schematic validation and MUST be excluded.
|
||||
|
||||
For each listed parameter:
|
||||
|
||||
- **Search systematically**: Check electrical characteristics tables, absolute maximum ratings, and application notes
|
||||
- **Prefer typical operating values** where available, but note maximums for rating parameters
|
||||
- **Use SPICE multiplier prefixes** for all values with units: `T`=1e12, `G`=1e9, `M`=1e6, `k`=1e3, `m`=1e-3, `u`=1e-6, `n`=1e-9, `p`=1e-12. Pick the multiplier that gives the most readable number.
|
||||
- Good: `"30V"`, `"240mV"`, `"500mA"`, `"47mohm"`, `"18pF"`, `"8MHz"`, `"10nC"`
|
||||
- Bad: `"0.24V"`, `"0.5A"`, `"0.047ohm"`, `"0.000000000018F"`, `"8000000Hz"`
|
||||
- **Always include the unit** with the multiplier in the value string
|
||||
- **Use numeric values** only when the parameter is inherently unitless (e.g., turns ratio, pin count, hFE)
|
||||
- **Use null** for parameters that are not applicable to this component or not found in the datasheet
|
||||
|
||||
Rules:
|
||||
- Extract from the datasheet only — do not infer or calculate values
|
||||
- If a parameter has different values at different conditions, use the value at the most common/standard condition
|
||||
- For parameters with min/typ/max, prefer typical; include all in the string if they matter (e.g., `"550mV typ, 850mV max"`)
|
||||
- **ONLY use parameter names from the "PARAMETERS TO EXTRACT" list** — any extra keys will be discarded
|
||||
|
||||
### 6. Call save_specs
|
||||
|
||||
Call the `save_specs` tool with:
|
||||
- `component_subtype`: The dotted taxonomy path (e.g., `"discrete.diode.schottky"`)
|
||||
- `component_subtype_description`: A brief description if this is a new subtype
|
||||
- `package_info`: Package details (base_family, package, pin_count, description)
|
||||
- `pintable`: Array of pin objects (number, name, description, functions)
|
||||
- `values`: An object mapping parameter names to their extracted values
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"component_subtype": {
|
||||
"type": "string",
|
||||
"description": "Dotted taxonomy path, e.g. discrete.diode.schottky, connector.usb",
|
||||
"pattern": "^[a-z][a-z0-9_]+(\\.[a-z][a-z0-9_]+)*$"
|
||||
},
|
||||
"component_subtype_description": {
|
||||
"type": "string",
|
||||
"description": "Brief description of the component subtype. Used when this is a new taxonomy entry."
|
||||
},
|
||||
"package_info": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"base_family": {"type": "string"},
|
||||
"package": {"type": "string"},
|
||||
"pin_count": {"type": "integer"},
|
||||
"description": {"type": "string"}
|
||||
},
|
||||
"required": ["base_family", "package", "pin_count"]
|
||||
},
|
||||
"pintable": {
|
||||
"type": "array",
|
||||
"description": "Pin table for the component. Include ALL pins.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"number": {},
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"functions": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"required": ["number", "name"]
|
||||
}
|
||||
},
|
||||
"values": {
|
||||
"type": "object",
|
||||
"description": "Extracted parameter values keyed ONLY by parameter names from the PARAMETERS TO EXTRACT list. Use SPICE multiplier prefixes (k, M, m, u, n, p) with units. Use null for missing/inapplicable parameters. Do NOT add parameters not in the list.",
|
||||
"additionalProperties": {
|
||||
"type": ["string", "number", "null"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["component_subtype", "component_subtype_description", "package_info", "pintable", "values"]
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate extraction output against the specs schema."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCHEMA_PATH = Path(__file__).parent / "schema.json"
|
||||
|
||||
|
||||
def validate(data: dict) -> list[str]:
|
||||
"""Return list of validation errors (empty = valid)."""
|
||||
errors = []
|
||||
schema = json.loads(SCHEMA_PATH.read_text())
|
||||
|
||||
for field in schema.get("required", []):
|
||||
if field not in data:
|
||||
errors.append(f"Missing required field: {field}")
|
||||
|
||||
if "component_subtype" in data:
|
||||
st = data["component_subtype"]
|
||||
if not isinstance(st, str) or "." not in st:
|
||||
errors.append(f"component_subtype must be dotted path, got: {st!r}")
|
||||
|
||||
if "package_info" in data:
|
||||
pkg = data["package_info"]
|
||||
for f in ["base_family", "package", "pin_count"]:
|
||||
if f not in pkg:
|
||||
errors.append(f"package_info missing required field: {f}")
|
||||
if "pin_count" in pkg and not isinstance(pkg["pin_count"], int):
|
||||
errors.append(f"package_info.pin_count must be integer, got: {type(pkg['pin_count']).__name__}")
|
||||
|
||||
if "pintable" in data:
|
||||
pins = data["pintable"]
|
||||
if not isinstance(pins, list) or len(pins) == 0:
|
||||
errors.append("pintable must be a non-empty array")
|
||||
else:
|
||||
numbers = []
|
||||
for i, pin in enumerate(pins):
|
||||
if "number" not in pin:
|
||||
errors.append(f"pintable[{i}] missing required field: number")
|
||||
if "name" not in pin:
|
||||
errors.append(f"pintable[{i}] missing required field: name")
|
||||
if "number" in pin:
|
||||
numbers.append(pin["number"])
|
||||
dupes = [n for n in set(numbers) if numbers.count(n) > 1]
|
||||
if dupes:
|
||||
errors.append(f"Duplicate pin numbers: {dupes}")
|
||||
|
||||
if "values" in data:
|
||||
values = data["values"]
|
||||
if not isinstance(values, dict):
|
||||
errors.append(f"values must be an object, got: {type(values).__name__}")
|
||||
else:
|
||||
for k, v in values.items():
|
||||
if v is not None and not isinstance(v, (str, int, float)):
|
||||
errors.append(f"values[{k!r}] must be string, number, or null, got: {type(v).__name__}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 validate.py '<json string>'")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
data = json.loads(sys.argv[1])
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"INVALID JSON: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
errors = validate(data)
|
||||
if errors:
|
||||
print("VALIDATION FAILED:")
|
||||
for err in errors:
|
||||
print(f" - {err}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("VALIDATION PASSED")
|
||||
Reference in New Issue
Block a user