{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pip install pandas==2.2.*" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Processing: Comparator, LM393DR2G\n", "Processing: Digital Potentiometer, SPI, MCP4131-104E/SN\n", "Processing: Op-Amp, LM2904DR2G\n", "Processing: Op-Amp, LM324DT\n", "Processing: Op-Amp, LM358DR2G\n", "Processing: Op-Amp, LMV321IDBVR\n", "Processing: Op-Amp, MCP6002T-I/SN\n", "Processing: Op-Amp, NE5532DR\n", "Processing: Op-Amp, OP07CDR\n", "Processing: Op-Amp, TL072CDT\n", "Processing: Switch, CD4051BM96\n", "Processing: Switch, CD4052BM96\n", "Skipping autogen file: symbols\\JLCPCB-Capacitors.kicad_sym\n", "Processing: Battery Holder, CR2032\n", "Processing: Headphone Jack, 3.5mm\n", "Processing: Screw Terminal, 4Px5.08mm, 20A\n", "Processing: Spring Terminal, 2Px12mm, 15A\n", "Processing: Tactile Button, 160gf\n", "Processing: Crystal, 12MHz, 20pF\n", "Processing: Crystal, 16MHz, 9pF\n", "Processing: Crystal, 25MHz, 11pF\n", "Processing: Crystal, 32.768kHz, 13pF\n", "Processing: Crystal, 8MHz, 20pF, ±10ppm\n", "Processing: Crystal, 8MHz, 20pF, ±20ppm\n", "Processing: Bridge Rectifier, DB107S\n", "Processing: Bridge Rectifier, MB10S\n", "Processing: Package, BAT54TW\n", "Processing: Package, H5VU25U\n", "Processing: Package, H5VU25UC\n", "Processing: Package, H5VUT1B\n", "Processing: Package, H5VUT2U\n", "Processing: Package, H5VUT3UA\n", "Processing: Package, SMF05CT1G\n", "Processing: Package, SRV05-4-P-T7\n", "Processing: Schottky, BAT54AW\n", "Processing: Schottky, BAT54C,215\n", "Processing: Switching, BAV70-C68978\n", "Processing: Switching, BAV99,215\n", "Processing: TVS-Bi, H15VNT2B\n", "Processing: TVS-Bi, H24VNT2B\n", "Processing: TVS-Bi, H36VLT2B\n", "Processing: TVS-Bi, H3V3HT2B\n", "Processing: TVS-Bi, H4V5H22B\n", "Processing: TVS-Bi, H5VHT2B\n", "Processing: TVS-Bi, H5VNT2B\n", "Processing: TVS-Bi, H8VNT2B\n", "Processing: TVS-Bi, PSM712-LF-T7\n", "Processing: TVS-Bi, SM712\n", "Processing: TVS-Uni, H12VH22U\n", "Processing: TVS-Uni, H12VNT2U\n", "Processing: TVS-Uni, H5VHT2U\n", "Processing: TVS-Uni, H5VU13U\n", "Skipping autogen file: symbols\\JLCPCB-Diodes.kicad_sym\n", "Processing: ADC, ADS1015IDGS\n", "Processing: ADC, ADS1115IDGS\n", "Processing: Connector, USB-TYPE-C-16P\n", "Processing: Crystal, 40MHz, 15pF\n", "Processing: LED, WS2812B, 1615\n", "Processing: LED, WS2812B, 5050\n", "Processing: MCU, CH32V003F4U6\n", "Processing: MCU, CH32V003J4M6\n", "Processing: MCU, ESP-12F (ESP826 Module)\n", "Processing: MCU, ESP32-C3\n", "Processing: MCU, ESP32-C3FH4\n", "Processing: MCU, ESP32-S2\n", "Processing: MCU, ESP32-S3\n", "Processing: MCU, RP2040\n", "Processing: RF, Connector, U.FL\n", "Processing: RF, GPS, Antenna\n", "Processing: RF, Lora, SX1262IMLTRT\n", "Processing: Sensor, BME280\n", "Processing: Sensor, LTR-303\n", "Processing: Sensor, SCD40-D-R2\n", "Processing: USB-Serial, CH340G\n", "Processing: Gate, AND, 4 Channel, 74HC08D,653\n", "Processing: Inverter, 6 Channel, 74HC04D,653\n", "Processing: Inverter, 6 Channel, 74HC14D,653\n", "Processing: Level Shifter, 8 Channel, 74HC245D,653\n", "Processing: Level Shifter, 8 Channel, TSN74LVC4245APWR\n", "Processing: Real Time Clock, I2C, 0.3uA, PCF8563T/5,518\n", "Processing: Real Time Clock, SPI, 0.3uA, DS1302ZN+\n", "Processing: Shift Register, 1 In, 8 Out, 74HC595D,118\n", "Processing: Shift Register, 8 In, 1 Out, 74HC165D,653\n", "Processing: Timer, 555\n", "Skipping autogen file: symbols\\JLCPCB-Inductors.kicad_sym\n", "Processing: Digital Isolator, 1 Mbps, 2 Channel\n", "Processing: LCD Driver, HT1621B\n", "Processing: Transceiver, CAN, SN65HVD230DR\n", "Processing: Transceiver, CAN, TJA1050T\n", "Processing: Transceiver, RS232, MAX232ESE\n", "Processing: Transceiver, RS232, SP3232EEY-L/TR\n", "Processing: Transceiver, RS485/422, 10Mbps, SN75176BDR\n", "Processing: Transceiver, RS485/422, 10Mbps, SP3485EN-L/TR\n", "Processing: Transceiver, RS485/422, 10Mbps, SP485EEN-L/TR\n", "Processing: Transceiver, USB\n", "Processing: Mounting Hole, 3mm, Via Stiched\n", "Processing: Mousebites, Cosmetic, 3mm\n", "Processing: Mousebites, Mechanical, 3mm\n", "Processing: ATmega328P-AU\n", "Processing: STM32F030C8T6\n", "Processing: STM32F103C8T6\n", "Processing: STM8S003F3P6TR\n", "Processing: STM8S105K6T6C\n", "Processing: 16MByte, SPI\n", "Processing: 256Kbit, I2C\n", "Processing: 64Kbit, I2C\n", "Processing: Battery, Li-Ion, 0.5A, TP4057\n", "Processing: Battery, Li-Ion, 0.8A, TP4054\n", "Processing: Battery, Li-Ion, 1.0A, TP4056\n", "Processing: DC-DC, Boost, 1.8-6VIN, 0.4A, 28V-VOUT\n", "Processing: DC-DC, Buck, 3.5-28VIN, 3A, 0.8+VOUT\n", "Processing: DC-DC, Buck, 4-40VIN, 2A, 1.2+VOUT\n", "Processing: DC-DC, Buck, 4-40VIN, 2A, 5VOUT\n", "Processing: DC-DC, Buck, 4-40VIN, 3A, 5VOUT, (1)\n", "Processing: DC-DC, Buck, 4-40VIN, 3A, 5VOUT, (2)\n", "Processing: DC-DC, Buck, 6-36VIN, 3A, 1.2+VOUT\n", "Processing: DC-DC, Charge Pump, 12VIN, \n", "Processing: DC-DC, Driver\n", "Processing: Driver, Stepper Motor, A4984\n", "Processing: LDO, 3.3V, 0.1A\n", "Processing: LDO, 3.3V, 0.2A\n", "Processing: LDO, 3.3V, 1A\n", "Processing: LDO, 5V, 0.1A\n", "Processing: LDO, 5V, 0.2A\n", "Processing: LDO, 5V, 0.5A\n", "Processing: LDO, 5V, 1A\n", "Processing: Ref, 2-36V, 100mA, CJ431\n", "Skipping autogen file: symbols\\JLCPCB-Resistors.kicad_sym\n", "Processing: Darlington Transistor, 7 Channel, ULN2003ADR\n", "Processing: NMOS, 2 Channel, HJ8205\n", "Processing: NPN, 2 Channel, MMDT3904\n", "Processing: NPN, 2 Channel, MMDT5551\n", "Skipping autogen file: symbols\\JLCPCB-Transistors.kicad_sym\n", "Skipping autogen file: symbols\\JLCPCB-Variable-Resistors.kicad_sym\n" ] } ], "source": [ "import requests\n", "import os\n", "import re\n", "import json\n", "import pandas as pd\n", "\n", "\n", "def get_lcsc_numbers(csv_path: str = \"assessments.csv\") -> list[int]:\n", " df = pd.read_csv(csv_path)\n", " return df[\"lcsc\"].dropna().astype(int).tolist() # List of LCSCs\n", "\n", "\n", "def extract_assessment(content: str):\n", " try:\n", " json_str = re.search(r\"\\{.*\\}\", content, re.DOTALL).group(0)\n", " data = json.loads(json_str)\n", " return {\n", " \"pinswap_rating\": int(data.get(\"pinswap_rating\", 0)),\n", " \"overall_rating\": int(data.get(\"overall_rating\", 0)),\n", " \"reasoning\": data.get(\"reasoning\", \"\").strip(),\n", " }\n", " except Exception as e:\n", " print(f\"Failed to parse assessment: {e}\")\n", " return {\"pinswap_rating\": 0, \"overall_rating\": 0, \"reasoning\": \"Assessment parsing failed\"}\n", "\n", "\n", "def LLM_assess_for_pinswaps(symbol: str, api_key: str):\n", " try:\n", " prompt = f\"\"\"Analyze this KiCad symbol for pin swap errors and provide ratings:\n", " Return JSON format only: {{\"pinswap_rating\": 0-100, \"overall_rating\": 0-100, \"reasoning\": \"...\"}}\n", " Symbol:\n", " ```\n", " {symbol}\n", " ```\"\"\"\n", "\n", " response = requests.post(\n", " \"https://openrouter.ai/api/v1/chat/completions\",\n", " headers={\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"},\n", " json={\n", " \"model\": \"deepseek/deepseek-r1:free\",\n", " \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n", " \"temperature\": 0.05,\n", " },\n", " timeout=15,\n", " ).json()\n", "\n", " return extract_assessment(response[\"choices\"][0][\"message\"][\"content\"])\n", " except Exception as e:\n", " print(f\"Assessment failed: {e}\")\n", " print(response)\n", " return {\"pinswap_rating\": 0, \"overall_rating\": 0, \"reasoning\": \"Assessment request failed\"}\n", "\n", "\n", "def process_symbol_file(input_path: str, output_path: str, api_key: str) -> None:\n", " # results = {}\n", " lcsc_numbers = get_lcsc_numbers(output_path)\n", "\n", " try:\n", " with open(input_path, \"r\", encoding=\"utf-8\") as f:\n", " current_symbol = []\n", " lcsc = None\n", " name = None\n", "\n", " for line in f:\n", " if '(generator \"CDFER\"' in line:\n", " print(f\"Skipping autogen file: {input_path}\")\n", " return\n", "\n", " if '(symbol \"' in line and \"_\" not in line or line == \")\\n\":\n", " if name and lcsc and current_symbol:\n", " if lcsc not in lcsc_numbers:\n", " symbol_code = \"\".join(current_symbol)\n", " results = {}\n", " results[lcsc] = LLM_assess_for_pinswaps(symbol_code, api_key)\n", " if results[lcsc][\"reasoning\"] != \"Assessment parsing failed\":\n", " results[lcsc][\"name\"] = name\n", " results[lcsc][\"path\"] = input_path\n", " save_assessments(results, output_path)\n", " current_symbol.clear()\n", "\n", " if '(symbol \"' in line:\n", " name = re.search(r'\"([^\"]+)\"', line).group(1)\n", " print(f\"Processing: {name}\")\n", "\n", " if '(property \"LCSC\"' in line:\n", " lcsc = int(re.search(r\"C(\\d+)\", line).group(1))\n", "\n", " current_symbol.append(line)\n", "\n", " except Exception as e:\n", " print(f\"File processing failed for {input_path}: {e}\")\n", "\n", "\n", "def process_symbol_directory(dir_path: str, output_path: str, api_key: str) -> None:\n", " for filename in os.listdir(dir_path):\n", " if filename.endswith(\".kicad_sym\"):\n", " full_path = os.path.join(dir_path, filename)\n", " process_symbol_file(full_path, output_path, api_key)\n", "\n", " return\n", "\n", "\n", "# Reporting interface\n", "def save_assessments(results, csv_path: str = \"assessments.csv\") -> None:\n", " \"\"\"Save results to CSV with pandas, appending data and removing duplicates.\"\"\"\n", " # Convert results to DataFrame\n", " formatted_data = [\n", " {\n", " \"lcsc\": lcsc,\n", " \"Component\": data[\"name\"],\n", " \"Path\": data[\"path\"],\n", " \"SwapsRating\": data[\"pinswap_rating\"],\n", " \"TotalRating\": data[\"overall_rating\"],\n", " \"Comments\": data[\"reasoning\"].replace(\"\\n\", \" \").replace('\"', \"\"),\n", " }\n", " for lcsc, data in results.items()\n", " ]\n", "\n", " new_df = pd.DataFrame(formatted_data)\n", "\n", " # Append to existing data or create new file\n", " if os.path.exists(csv_path):\n", " existing_df = pd.read_csv(csv_path)\n", " combined_df = pd.concat([existing_df, new_df], ignore_index=True)\n", " # Keep last occurrence of each component\n", " combined_df = combined_df.drop_duplicates(subset=[\"Component\"], keep=\"last\")\n", " else:\n", " combined_df = new_df\n", "\n", " combined_df = combined_df.sort_values(by=\"SwapsRating\")\n", "\n", " # Save consolidated data\n", " combined_df.to_csv(csv_path, index=False)\n", " return\n", "\n", "\n", "# Usage examples\n", "if __name__ == \"__main__\":\n", " API_KEY = \"Put-Your-OpenRouter-Key-Here\"\n", "\n", " # Test single file\n", " # process_symbol_file(\"symbols/JLCPCB-Memory.kicad_sym\", \"pinswap.csv\", API_KEY)\n", "\n", " # Full directory scan\n", " process_symbol_directory(\"symbols\", \"pinswap.csv\", API_KEY)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.2" } }, "nbformat": 4, "nbformat_minor": 2 }