mfcli 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mfcli/.env.example +72 -0
- mfcli/__init__.py +0 -0
- mfcli/agents/__init__.py +0 -0
- mfcli/agents/controller/__init__.py +0 -0
- mfcli/agents/controller/agent.py +19 -0
- mfcli/agents/controller/config.yaml +27 -0
- mfcli/agents/controller/tools.py +42 -0
- mfcli/agents/tools/general.py +118 -0
- mfcli/alembic/env.py +61 -0
- mfcli/alembic/script.py.mako +28 -0
- mfcli/alembic/versions/6ccc0c7c397c_added_fields_to_pdf_parts_model.py +39 -0
- mfcli/alembic/versions/769019ef4870_added_gemini_file_path_to_pdf_part_model.py +33 -0
- mfcli/alembic/versions/7a2e3a779fdc_added_functional_block_and_component_.py +54 -0
- mfcli/alembic/versions/7d5adb2a47a7_added_pdf_parts_model.py +41 -0
- mfcli/alembic/versions/7fcb7d6a5836_init.py +167 -0
- mfcli/alembic/versions/e0f2b5765c72_added_cascade_delete_for_models_that_.py +32 -0
- mfcli/alembic.ini +147 -0
- mfcli/cli/__init__.py +0 -0
- mfcli/cli/dependencies.py +59 -0
- mfcli/cli/main.py +200 -0
- mfcli/client/__init__.py +0 -0
- mfcli/client/chroma_db.py +184 -0
- mfcli/client/docling.py +44 -0
- mfcli/client/gemini.py +252 -0
- mfcli/client/llama_parse.py +38 -0
- mfcli/client/vector_db.py +93 -0
- mfcli/constants/__init__.py +0 -0
- mfcli/constants/base_enum.py +18 -0
- mfcli/constants/directory_names.py +1 -0
- mfcli/constants/file_types.py +189 -0
- mfcli/constants/gemini.py +1 -0
- mfcli/constants/openai.py +6 -0
- mfcli/constants/pipeline_run_status.py +3 -0
- mfcli/crud/__init__.py +0 -0
- mfcli/crud/file.py +42 -0
- mfcli/crud/functional_blocks.py +26 -0
- mfcli/crud/netlist.py +18 -0
- mfcli/crud/pipeline_run.py +17 -0
- mfcli/crud/project.py +144 -0
- mfcli/digikey/__init__.py +0 -0
- mfcli/digikey/digikey.py +105 -0
- mfcli/main.py +5 -0
- mfcli/mcp/__init__.py +0 -0
- mfcli/mcp/configs/cline_mcp_settings.json +11 -0
- mfcli/mcp/configs/mfcli.mcp.json +7 -0
- mfcli/mcp/mcp_instance.py +6 -0
- mfcli/mcp/server.py +37 -0
- mfcli/mcp/state_manager.py +51 -0
- mfcli/mcp/tools/__init__.py +0 -0
- mfcli/mcp/tools/query_knowledgebase.py +108 -0
- mfcli/models/__init__.py +10 -0
- mfcli/models/base.py +10 -0
- mfcli/models/bom.py +71 -0
- mfcli/models/datasheet.py +10 -0
- mfcli/models/debug_setup.py +64 -0
- mfcli/models/file.py +43 -0
- mfcli/models/file_docket.py +94 -0
- mfcli/models/file_metadata.py +19 -0
- mfcli/models/functional_blocks.py +94 -0
- mfcli/models/llm_response.py +5 -0
- mfcli/models/mcu.py +97 -0
- mfcli/models/mcu_errata.py +26 -0
- mfcli/models/netlist.py +59 -0
- mfcli/models/pdf_parts.py +25 -0
- mfcli/models/pipeline_run.py +34 -0
- mfcli/models/project.py +27 -0
- mfcli/models/project_metadata.py +15 -0
- mfcli/pipeline/__init__.py +0 -0
- mfcli/pipeline/analysis/__init__.py +0 -0
- mfcli/pipeline/analysis/bom_netlist_mapper.py +28 -0
- mfcli/pipeline/analysis/generators/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/bom/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/bom/bom.py +74 -0
- mfcli/pipeline/analysis/generators/debug_setup/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/debug_setup/debug_setup.py +71 -0
- mfcli/pipeline/analysis/generators/debug_setup/instructions.py +150 -0
- mfcli/pipeline/analysis/generators/functional_blocks/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/functional_blocks/functional_blocks.py +93 -0
- mfcli/pipeline/analysis/generators/functional_blocks/instructions.py +34 -0
- mfcli/pipeline/analysis/generators/functional_blocks/validator.py +94 -0
- mfcli/pipeline/analysis/generators/generator.py +258 -0
- mfcli/pipeline/analysis/generators/generator_base.py +18 -0
- mfcli/pipeline/analysis/generators/mcu/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/mcu/instructions.py +156 -0
- mfcli/pipeline/analysis/generators/mcu/mcu.py +84 -0
- mfcli/pipeline/analysis/generators/mcu_errata/__init__.py +1 -0
- mfcli/pipeline/analysis/generators/mcu_errata/instructions.py +77 -0
- mfcli/pipeline/analysis/generators/mcu_errata/mcu_errata.py +95 -0
- mfcli/pipeline/analysis/generators/summary/__init__.py +0 -0
- mfcli/pipeline/analysis/generators/summary/summary.py +47 -0
- mfcli/pipeline/classifier.py +93 -0
- mfcli/pipeline/data_enricher.py +15 -0
- mfcli/pipeline/extractor.py +34 -0
- mfcli/pipeline/extractors/__init__.py +0 -0
- mfcli/pipeline/extractors/pdf.py +12 -0
- mfcli/pipeline/parser.py +120 -0
- mfcli/pipeline/parsers/__init__.py +0 -0
- mfcli/pipeline/parsers/netlist/__init__.py +0 -0
- mfcli/pipeline/parsers/netlist/edif.py +93 -0
- mfcli/pipeline/parsers/netlist/kicad_legacy_net.py +326 -0
- mfcli/pipeline/parsers/netlist/kicad_spice.py +135 -0
- mfcli/pipeline/parsers/netlist/pads.py +185 -0
- mfcli/pipeline/parsers/netlist/protel.py +166 -0
- mfcli/pipeline/parsers/netlist/protel_detector.py +29 -0
- mfcli/pipeline/pipeline.py +470 -0
- mfcli/pipeline/preprocessors/__init__.py +0 -0
- mfcli/pipeline/preprocessors/user_guide.py +127 -0
- mfcli/pipeline/run_context.py +32 -0
- mfcli/pipeline/schema_mapper.py +89 -0
- mfcli/pipeline/sub_classifier.py +115 -0
- mfcli/utils/__init__.py +0 -0
- mfcli/utils/cline_rules.py +256 -0
- mfcli/utils/config.py +33 -0
- mfcli/utils/configurator.py +324 -0
- mfcli/utils/data_cleaner.py +114 -0
- mfcli/utils/datasheet_vectorizer.py +283 -0
- mfcli/utils/directory_manager.py +116 -0
- mfcli/utils/file_upload.py +298 -0
- mfcli/utils/files.py +16 -0
- mfcli/utils/http_requests.py +54 -0
- mfcli/utils/kb_lister.py +89 -0
- mfcli/utils/kb_remover.py +173 -0
- mfcli/utils/logger.py +28 -0
- mfcli/utils/mcp_configurator.py +394 -0
- mfcli/utils/migrations.py +18 -0
- mfcli/utils/orm.py +43 -0
- mfcli/utils/pdf_splitter.py +63 -0
- mfcli/utils/pre_uninstall.py +167 -0
- mfcli/utils/query_service.py +22 -0
- mfcli/utils/system_check.py +306 -0
- mfcli/utils/tools.py +98 -0
- mfcli/utils/vectorizer.py +28 -0
- mfcli-0.2.1.dist-info/METADATA +956 -0
- mfcli-0.2.1.dist-info/RECORD +138 -0
- mfcli-0.2.1.dist-info/WHEEL +5 -0
- mfcli-0.2.1.dist-info/entry_points.txt +4 -0
- mfcli-0.2.1.dist-info/licenses/LICENSE +21 -0
- mfcli-0.2.1.dist-info/top_level.txt +1 -0
mfcli/models/mcu.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MCUData(BaseModel):
|
|
7
|
+
"""Complete MCU cheat sheet for firmware boot essentials."""
|
|
8
|
+
mcu_name: str = Field(..., description="MCU part number")
|
|
9
|
+
mcu_family: str = Field("", description="MCU family or series")
|
|
10
|
+
architecture: str = Field("", description="CPU architecture (e.g., ARM Cortex-M4, RISC-V, AVR)")
|
|
11
|
+
core_frequency_max: str = Field("", description="Maximum core frequency")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MemoryRegion(BaseModel):
|
|
15
|
+
"""Represents a memory region in the MCU."""
|
|
16
|
+
name: str = Field(..., description="Memory region name (e.g., FLASH, RAM, ROM)")
|
|
17
|
+
start_address: str = Field(..., description="Start address in hex (e.g., 0x08000000)")
|
|
18
|
+
size: str = Field(..., description="Size in bytes or with unit (e.g., 128KB, 0x20000)")
|
|
19
|
+
type: str = Field(..., description="Type: flash, ram, rom, peripheral, reserved")
|
|
20
|
+
description: str = Field("", description="Additional details about this region")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MemoryMap(BaseModel):
|
|
24
|
+
memory_map: List[MemoryRegion] = Field(default_factory=list, description="Memory regions for linker script")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ClockConfiguration(BaseModel):
|
|
28
|
+
"""Represents clock configuration details."""
|
|
29
|
+
clock_source: str = Field(..., description="Clock source (e.g., HSI, HSE, PLL, Internal RC)")
|
|
30
|
+
frequency_range: str = Field("", description="Frequency range or typical frequency")
|
|
31
|
+
configuration_steps: List[str] = Field(default_factory=list, description="Steps to configure this clock")
|
|
32
|
+
registers: List[str] = Field(default_factory=list, description="Key registers to configure")
|
|
33
|
+
notes: str = Field("", description="Important notes about clock configuration")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ClockConfig(BaseModel):
|
|
37
|
+
clock_config: ClockConfiguration = Field(..., description="Represents clock configuration details.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class InterruptVector(BaseModel):
|
|
41
|
+
"""Represents an interrupt vector entry."""
|
|
42
|
+
vector_number: int = Field(..., description="Interrupt vector number or position")
|
|
43
|
+
name: str = Field(..., description="Interrupt name")
|
|
44
|
+
address: str = Field("", description="Vector table address in hex")
|
|
45
|
+
priority: str = Field("", description="Default or configurable priority")
|
|
46
|
+
description: str = Field("", description="What triggers this interrupt")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InterruptVectors(BaseModel):
|
|
50
|
+
interrupt_vectors: List[InterruptVector] = Field(..., description="Represents interrupt vector entities.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class StartupRequirements(BaseModel):
|
|
54
|
+
"""Represents startup and boot requirements."""
|
|
55
|
+
boot_mode: str = Field("", description="Boot mode or boot source selection")
|
|
56
|
+
startup_code_location: str = Field("", description="Where startup code should be placed")
|
|
57
|
+
vector_table_offset: str = Field("", description="Vector table offset register (VTOR)")
|
|
58
|
+
stack_pointer_init: str = Field("", description="How to initialize stack pointer")
|
|
59
|
+
minimum_init_sequence: List[str] = Field(default_factory=list, description="Minimum steps to boot")
|
|
60
|
+
watchdog_handling: str = Field("", description="How to handle watchdog timer at startup")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class StartupRequirementsModel(BaseModel):
|
|
64
|
+
startup_requirements: StartupRequirements = Field(..., description="Represents startup and boot requirements.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PeripheralInfo(BaseModel):
|
|
68
|
+
"""Represents a peripheral module."""
|
|
69
|
+
name: str = Field(..., description="Peripheral name (e.g., GPIO, UART0, Timer1)")
|
|
70
|
+
base_address: str = Field("", description="Base address in hex")
|
|
71
|
+
clock_enable: str = Field("", description="How to enable clock for this peripheral")
|
|
72
|
+
initialization_steps: List[str] = Field(default_factory=list, description="Basic init steps")
|
|
73
|
+
key_registers: List[str] = Field(default_factory=list, description="Important registers")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class EssentialPeripherals(BaseModel):
|
|
77
|
+
essential_peripherals: List[PeripheralInfo] = Field(..., description="Represents essential peripherals")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class MCULinkerScriptInfo(BaseModel):
|
|
81
|
+
linker_script_notes: str = Field("", description="Notes for creating linker script")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class MCUHelloWorldSteps(BaseModel):
|
|
85
|
+
hello_world_steps: List[str] = Field(default_factory=list, description="Steps to create a hello world app")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class MCUDebuggingInterface(BaseModel):
|
|
89
|
+
debugging_interface: str = Field("", description="Debug interface (JTAG, SWD, etc.)")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class MCUProgrammingInterface(BaseModel):
|
|
93
|
+
programming_interface: str = Field("", description="Programming interface and method")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class MCUDatasheetReferences(BaseModel):
|
|
97
|
+
datasheet_references: List[str] = Field(default_factory=list, description="Key datasheet sections/pages")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import List, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ErrataIDs(BaseModel):
|
|
7
|
+
ids: list[str]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ErrataTopLevelSummary(BaseModel):
|
|
11
|
+
errata_document: str = Field(..., description="Errata document name (e.g., Silicon Errata Rev 1.2 - March 2024)")
|
|
12
|
+
mcu_name: str = Field(..., description="MCU name (e.g., MSPM0L1306)")
|
|
13
|
+
recommendations: list[str] = Field(default_factory=list, description="Top-level firmware recommendations")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ErrataItem(BaseModel):
|
|
17
|
+
"""Individual errata issue."""
|
|
18
|
+
errata_id: str = Field(..., description="Errata ID/number (e.g., 'I2C_01', 'ADV0123')")
|
|
19
|
+
title: str = Field(..., description="Brief title of the issue")
|
|
20
|
+
affected_modules: List[str] = Field(default_factory=list, description="Affected peripherals/modules")
|
|
21
|
+
severity: Literal['Critical', 'Major', 'Minor']
|
|
22
|
+
description: str = Field(..., description="What the issue is")
|
|
23
|
+
conditions: str = Field("", description="When/how the issue occurs")
|
|
24
|
+
firmware_workaround: str = Field("", description="Firmware workaround or mitigation")
|
|
25
|
+
impact: str = Field("", description="Impact on firmware operation")
|
|
26
|
+
affected_revisions: List[str] = Field(default_factory=list, description="Silicon revisions affected")
|
mfcli/models/netlist.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from sqlalchemy import Column, TIMESTAMP, func
|
|
6
|
+
from sqlmodel import Field, Relationship, SQLModel
|
|
7
|
+
|
|
8
|
+
from mfcli.models.bom import BOM
|
|
9
|
+
from mfcli.models.pipeline_run import PipelineRun
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Pin(BaseModel):
|
|
13
|
+
"""Represents a single pin connection."""
|
|
14
|
+
pin: str = Field(..., description="Pin number or name")
|
|
15
|
+
net: str = Field(..., description="Net name this pin connects to")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Component(BaseModel):
|
|
19
|
+
"""Represents a component with its pins and connections."""
|
|
20
|
+
ref_des: str = Field(..., description="Reference designator (C1, R5, U2, etc.)")
|
|
21
|
+
part_number: str = Field(..., description="Part number or footprint")
|
|
22
|
+
pins: List[Pin] = Field(default_factory=list, description="List of pin connections")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NetlistSchema(BaseModel):
|
|
26
|
+
"""Top-level schema for parsed netlist."""
|
|
27
|
+
components: List[Component] = Field(default_factory=list, description="List of components")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Netlist(SQLModel, table=True):
|
|
31
|
+
__tablename__ = "netlists"
|
|
32
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
33
|
+
pipeline_run_id: int = Field(foreign_key='pipeline_runs.id', index=True, nullable=False, ondelete="CASCADE")
|
|
34
|
+
pipeline_run: Optional["PipelineRun"] = Relationship()
|
|
35
|
+
components: List["NetlistComponent"] = Relationship(back_populates='netlist', cascade_delete=True)
|
|
36
|
+
created_at: datetime = Field(
|
|
37
|
+
sa_column=Column(TIMESTAMP(timezone=True), server_default=func.now(), nullable=False)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NetlistComponent(SQLModel, table=True):
|
|
42
|
+
__tablename__ = "netlist_components"
|
|
43
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
44
|
+
netlist_id: int = Field(foreign_key='netlists.id', index=True, nullable=False, ondelete="CASCADE")
|
|
45
|
+
netlist: Optional["Netlist"] = Relationship(back_populates='components')
|
|
46
|
+
pins: List["NetlistPin"] = Relationship(back_populates='netlist_component', cascade_delete=True)
|
|
47
|
+
ref_des: str = Field(..., max_length=100, nullable=False)
|
|
48
|
+
part_number: str = Field(max_length=255, nullable=False, index=True)
|
|
49
|
+
bom_entry_id: Optional[int] = Field(foreign_key='bom.id', index=True, ondelete='CASCADE')
|
|
50
|
+
bom_entry: Optional["BOM"] = Relationship()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class NetlistPin(SQLModel, table=True):
|
|
54
|
+
__tablename__ = "netlist_pins"
|
|
55
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
56
|
+
netlist_component_id: int = Field(foreign_key='netlist_components.id', index=True, nullable=False, ondelete="CASCADE")
|
|
57
|
+
netlist_component: NetlistComponent = Relationship()
|
|
58
|
+
pin: str = Field(..., max_length=100, nullable=False)
|
|
59
|
+
net: str = Field(..., max_length=100, nullable=False)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from sqlalchemy import Column, Integer, ForeignKey
|
|
4
|
+
from sqlmodel import SQLModel, Field, Relationship
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PDFPart(SQLModel, table=True):
|
|
8
|
+
__tablename__ = "pdf_parts"
|
|
9
|
+
|
|
10
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
11
|
+
file_id: int = Field(
|
|
12
|
+
sa_column=Column(
|
|
13
|
+
Integer,
|
|
14
|
+
ForeignKey("files.id", ondelete="CASCADE"),
|
|
15
|
+
nullable=False,
|
|
16
|
+
index=True
|
|
17
|
+
)
|
|
18
|
+
)
|
|
19
|
+
file: Optional["File"] = Relationship(back_populates='pdf_parts')
|
|
20
|
+
path: str = Field(max_length=600, nullable=False)
|
|
21
|
+
gemini_file_id: str = Field(max_length=40, nullable=True)
|
|
22
|
+
start_page: int = Field(nullable=True)
|
|
23
|
+
end_page: int = Field(nullable=True)
|
|
24
|
+
title: str = Field(nullable=True)
|
|
25
|
+
section_no: int = Field(nullable=True)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import Integer, func, Text, Column, DateTime, ForeignKey
|
|
5
|
+
from sqlmodel import SQLModel, Field, Relationship
|
|
6
|
+
|
|
7
|
+
from mfcli.constants.pipeline_run_status import PIPELINE_STATUS_IN_PROGRESS
|
|
8
|
+
from mfcli.models.project import Project
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PipelineRun(SQLModel, table=True):
|
|
12
|
+
__tablename__ = "pipeline_runs"
|
|
13
|
+
|
|
14
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
15
|
+
project_id: int = Field(
|
|
16
|
+
sa_column=Column(
|
|
17
|
+
Integer,
|
|
18
|
+
ForeignKey("projects.id", ondelete="CASCADE"),
|
|
19
|
+
nullable=True,
|
|
20
|
+
index=True
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
project: Optional["Project"] = Relationship(back_populates='runs')
|
|
24
|
+
status: int = Field(
|
|
25
|
+
sa_column=Column(Integer, nullable=False, server_default=str(PIPELINE_STATUS_IN_PROGRESS))
|
|
26
|
+
)
|
|
27
|
+
errors: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
|
|
28
|
+
|
|
29
|
+
created_at: datetime = Field(
|
|
30
|
+
sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
|
31
|
+
)
|
|
32
|
+
updated_at: datetime = Field(
|
|
33
|
+
sa_column=Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
|
|
34
|
+
)
|
mfcli/models/project.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import Column, DateTime, func
|
|
5
|
+
from sqlmodel import SQLModel, Field, Relationship
|
|
6
|
+
|
|
7
|
+
project_name_regex = r"^[A-Za-z0-9_-]+$"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Project(SQLModel, table=True):
|
|
11
|
+
__tablename__ = "projects"
|
|
12
|
+
|
|
13
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
14
|
+
runs: List["PipelineRun"] = Relationship(back_populates='project', cascade_delete=True)
|
|
15
|
+
name: str = Field(
|
|
16
|
+
nullable=False,
|
|
17
|
+
unique=True,
|
|
18
|
+
index=True,
|
|
19
|
+
min_length=3,
|
|
20
|
+
max_length=45,
|
|
21
|
+
regex=project_name_regex
|
|
22
|
+
)
|
|
23
|
+
index_id: str = Field(index=True, unique=True)
|
|
24
|
+
repo_dir: str = Field(index=True, unique=True)
|
|
25
|
+
created_at: datetime = Field(
|
|
26
|
+
sa_column=Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
|
27
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class VectorizeCheatSheetsConfig(BaseModel):
|
|
5
|
+
vectorize_errata: bool = Field(default=False)
|
|
6
|
+
vectorize_mcu: bool = Field(default=False)
|
|
7
|
+
vectorize_debug_setup: bool = Field(default=False)
|
|
8
|
+
vectorize_functional_blocks: bool = Field(default=False)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProjectConfig(BaseModel):
|
|
12
|
+
name: str
|
|
13
|
+
vectorize_hw_files: bool = Field(default=True)
|
|
14
|
+
vectorize_datasheets: bool = Field(default=True)
|
|
15
|
+
vectorize_cheat_sheets_config: VectorizeCheatSheetsConfig = Field(default_factory=VectorizeCheatSheetsConfig)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from mfcli.models.bom import BOM
|
|
2
|
+
from mfcli.models.file import File
|
|
3
|
+
from mfcli.models.netlist import NetlistComponent, Netlist
|
|
4
|
+
from mfcli.utils.logger import get_logger
|
|
5
|
+
from mfcli.utils.orm import Session
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def map_netlist_to_bom_entries(db: Session, pipeline_run_id: int):
|
|
11
|
+
logger.debug(f"Mapping netlist to BOM entries for pipeline: {pipeline_run_id}")
|
|
12
|
+
components: list[NetlistComponent] = (
|
|
13
|
+
db.query(NetlistComponent)
|
|
14
|
+
.join(NetlistComponent.netlist)
|
|
15
|
+
.filter(Netlist.pipeline_run_id == pipeline_run_id)
|
|
16
|
+
.all()
|
|
17
|
+
)
|
|
18
|
+
results = (
|
|
19
|
+
db.query(BOM.id, BOM.value)
|
|
20
|
+
.join(BOM.file)
|
|
21
|
+
.filter(File.pipeline_run_id == pipeline_run_id)
|
|
22
|
+
.all()
|
|
23
|
+
)
|
|
24
|
+
bom_value_id_map = {result[1]: result[0] for result in results}
|
|
25
|
+
for component in components:
|
|
26
|
+
if bom_value_id_map.get(component.part_number):
|
|
27
|
+
component.bom_entry_id = bom_value_id_map[component.part_number]
|
|
28
|
+
logger.debug(f"All netlist entries mapped: {pipeline_run_id}")
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from asyncio import Semaphore
|
|
3
|
+
from typing import Dict, Type, List
|
|
4
|
+
|
|
5
|
+
from google.genai.types import File
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from mfcli.client.gemini import Gemini
|
|
9
|
+
from mfcli.models.bom import BOMSchema
|
|
10
|
+
from mfcli.utils.logger import get_logger
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
system_instructions = (
|
|
15
|
+
"""
|
|
16
|
+
You are the BOM Generator agent. Your role is to extract components from PDF schematics
|
|
17
|
+
and generate a Bill of Materials (BOM) in CSV format.
|
|
18
|
+
"""
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
identify_bom_entries_instructions = (
|
|
22
|
+
"""
|
|
23
|
+
You will return a list of names for Bill of Materials (BOM) entries from this schematic file.
|
|
24
|
+
"""
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
generate_bom_entry_instructions = (
|
|
28
|
+
"""
|
|
29
|
+
Analyze the schematic and extract BOM entry data for component: {}:
|
|
30
|
+
- Reference designators (R1, C1, U1, etc.)
|
|
31
|
+
- Component values (10kΩ, 100nF, etc.)
|
|
32
|
+
- Part numbers and manufacturers (if visible)
|
|
33
|
+
- Footprints/packages (if visible)
|
|
34
|
+
- Any other available information
|
|
35
|
+
"""
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BOMEntryNames(BaseModel):
|
|
40
|
+
names: List[str] = Field(default_factory=list, description="BOM entry names")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class BOMGenerator:
|
|
44
|
+
def __init__(self, gemini: Gemini):
|
|
45
|
+
self._gemini = gemini
|
|
46
|
+
self._sem = Semaphore(5)
|
|
47
|
+
|
|
48
|
+
async def _generate(self, file: File, model: Type[BaseModel], instructions: str) -> BaseModel:
|
|
49
|
+
async with self._sem:
|
|
50
|
+
return await self._gemini.generate(
|
|
51
|
+
prompt=instructions,
|
|
52
|
+
instructions=system_instructions,
|
|
53
|
+
response_model=model,
|
|
54
|
+
files=[file]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
async def _get_bom_entry_names(self, file: File) -> List[str]:
|
|
58
|
+
response = await self._generate(file, BOMEntryNames, identify_bom_entries_instructions)
|
|
59
|
+
return response.names
|
|
60
|
+
|
|
61
|
+
async def generate(self, file: File) -> List[Dict]:
|
|
62
|
+
bom_entry_names = await self._get_bom_entry_names(file)
|
|
63
|
+
tasks = []
|
|
64
|
+
for name in bom_entry_names:
|
|
65
|
+
bom_entry_instructions = generate_bom_entry_instructions.format(name)
|
|
66
|
+
tasks.append(self._generate(file, BOMSchema, bom_entry_instructions))
|
|
67
|
+
results: List[BOMSchema | Exception] = await asyncio.gather(*tasks, return_exceptions=True)
|
|
68
|
+
response_data = []
|
|
69
|
+
for result in results:
|
|
70
|
+
if isinstance(result, Exception):
|
|
71
|
+
logger.warn(f"There was an error generating BOM entry data: {result}")
|
|
72
|
+
continue
|
|
73
|
+
response_data.append(result.model_dump())
|
|
74
|
+
return response_data
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from asyncio import Semaphore
|
|
3
|
+
from typing import Dict, Type, List
|
|
4
|
+
|
|
5
|
+
from google.genai.types import File as GeminiFile
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from mfcli.models.debug_setup import (
|
|
9
|
+
DSTargetMCU,
|
|
10
|
+
DebugInterface,
|
|
11
|
+
DebugTool,
|
|
12
|
+
DriverInfo,
|
|
13
|
+
VSCodeLaunchConfig,
|
|
14
|
+
DSAdditionalTools,
|
|
15
|
+
DSTroubleshootingGuide,
|
|
16
|
+
DSQuickStartGuide
|
|
17
|
+
)
|
|
18
|
+
from mfcli.models.file import File
|
|
19
|
+
from mfcli.pipeline.analysis.generators.debug_setup.instructions import (
|
|
20
|
+
ds_mcu_instructions,
|
|
21
|
+
ds_debug_int_instructions,
|
|
22
|
+
ds_recommended_tool_instructions,
|
|
23
|
+
ds_drivers_instructions,
|
|
24
|
+
ds_vscode_launch_instructions,
|
|
25
|
+
ds_add_tools_instructions,
|
|
26
|
+
ds_troubleshooting_instructions,
|
|
27
|
+
ds_quick_start_guide_instructions,
|
|
28
|
+
ds_system_instructions
|
|
29
|
+
)
|
|
30
|
+
from mfcli.pipeline.analysis.generators.generator_base import GeneratorBase
|
|
31
|
+
from mfcli.pipeline.run_context import PipelineRunContext
|
|
32
|
+
from mfcli.utils.logger import get_logger
|
|
33
|
+
|
|
34
|
+
logger = get_logger(__name__)
|
|
35
|
+
|
|
36
|
+
DSModelInstructionsMap = {
|
|
37
|
+
DSTargetMCU: ds_mcu_instructions,
|
|
38
|
+
DebugInterface: ds_debug_int_instructions,
|
|
39
|
+
DebugTool: ds_recommended_tool_instructions,
|
|
40
|
+
DriverInfo: ds_drivers_instructions,
|
|
41
|
+
VSCodeLaunchConfig: ds_vscode_launch_instructions,
|
|
42
|
+
DSAdditionalTools: ds_add_tools_instructions,
|
|
43
|
+
DSTroubleshootingGuide: ds_troubleshooting_instructions,
|
|
44
|
+
DSQuickStartGuide: ds_quick_start_guide_instructions
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DSCheatSheetGenerator(GeneratorBase):
|
|
49
|
+
def __init__(self, context: PipelineRunContext, db_file: File, uploads: List[GeminiFile]):
|
|
50
|
+
super().__init__(context, db_file, uploads)
|
|
51
|
+
self._sem = Semaphore(5)
|
|
52
|
+
|
|
53
|
+
async def _extract_ds_data(self, model: Type[BaseModel], instructions: str) -> BaseModel:
|
|
54
|
+
return await self._context.gemini.generate(
|
|
55
|
+
prompt=instructions,
|
|
56
|
+
instructions=ds_system_instructions,
|
|
57
|
+
response_model=model,
|
|
58
|
+
files=self._uploads
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def generate(self) -> Dict:
|
|
62
|
+
tasks = []
|
|
63
|
+
for model, instructions in DSModelInstructionsMap.items():
|
|
64
|
+
tasks.append(self._extract_ds_data(model, instructions))
|
|
65
|
+
results: list[BaseModel] = await asyncio.gather(*tasks)
|
|
66
|
+
response_dict = {}
|
|
67
|
+
for result in results:
|
|
68
|
+
response_dict.update(result.model_dump())
|
|
69
|
+
return {
|
|
70
|
+
"debug_setup": response_dict
|
|
71
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
ds_system_instructions = (
|
|
2
|
+
"""
|
|
3
|
+
You are the Debug Setup Cheat Sheet Generator agent. Your role is to analyze schematics
|
|
4
|
+
to extract debug interface information and provide comprehensive setup guidance including:
|
|
5
|
+
- Recommended JTAG/SWD tools based on the MCU and interface
|
|
6
|
+
- Driver installation for Windows and Linux
|
|
7
|
+
- VS Code launch.json configuration
|
|
8
|
+
- GDB server setup
|
|
9
|
+
- Quick start guide
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Analyze the schematic to identify debug interface:
|
|
13
|
+
Look for:
|
|
14
|
+
- Debug connector (J-TAG, SWD, ICSP headers)
|
|
15
|
+
- Pin labels (SWDIO, SWCLK, TMS, TCK, TDI, TDO, etc.)
|
|
16
|
+
- Target MCU identification
|
|
17
|
+
- Voltage levels
|
|
18
|
+
- Connector type and pinout
|
|
19
|
+
|
|
20
|
+
Determine recommended debug tool based on MCU:
|
|
21
|
+
|
|
22
|
+
Common mappings:
|
|
23
|
+
- STM32 → ST-Link V3 (official), J-Link (professional)
|
|
24
|
+
- NXP LPC/Kinetis → LPC-Link2, J-Link
|
|
25
|
+
- Nordic nRF → J-Link, nRF DK
|
|
26
|
+
- TI MSP430/MSPM0 → MSP-FET, XDS110
|
|
27
|
+
- Microchip PIC/AVR → PICkit, ICD, MPLAB Snap
|
|
28
|
+
- ARM Cortex-M (generic) → CMSIS-DAP, J-Link
|
|
29
|
+
- ESP32 → ESP-Prog, J-Link
|
|
30
|
+
- RISC-V → J-Link, OpenOCD compatible probes
|
|
31
|
+
"""
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
ds_mcu_instructions = (
|
|
35
|
+
"""
|
|
36
|
+
Extract information systematically:
|
|
37
|
+
|
|
38
|
+
TARGET MCU:
|
|
39
|
+
- Identify the MCU from schematic
|
|
40
|
+
- Note the architecture (ARM Cortex-M4, AVR, etc.)
|
|
41
|
+
"""
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
ds_debug_int_instructions = (
|
|
45
|
+
"""
|
|
46
|
+
Extract information systematically:
|
|
47
|
+
|
|
48
|
+
DEBUG INTERFACE:
|
|
49
|
+
- interface_type: JTAG, SWD, ICSP, PDI, UPDI, etc.
|
|
50
|
+
- pin_count: Number of pins on debug connector
|
|
51
|
+
- pin_mapping: Map pin numbers to signals
|
|
52
|
+
Example: {"1": "VCC", "2": "SWDIO", "3": "GND", "4": "SWCLK"}
|
|
53
|
+
- connector_type: Physical connector (10-pin Cortex, 20-pin JTAG, etc.)
|
|
54
|
+
- voltage_level: Target voltage (typically 3.3V or 5V)
|
|
55
|
+
"""
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
ds_recommended_tool_instructions = (
|
|
59
|
+
"""
|
|
60
|
+
Extract information systematically:
|
|
61
|
+
|
|
62
|
+
RECOMMENDED TOOL:
|
|
63
|
+
- name: Primary tool recommendation (e.g., "J-Link", "ST-Link V3")
|
|
64
|
+
- model: Specific model if applicable
|
|
65
|
+
- purchase_links: Where to buy (Digikey, Mouser, official sites)
|
|
66
|
+
- compatibility_notes: Why this tool is recommended
|
|
67
|
+
- alternative_tools: Other compatible options
|
|
68
|
+
"""
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
ds_drivers_instructions = (
|
|
72
|
+
"""
|
|
73
|
+
Extract information systematically:
|
|
74
|
+
|
|
75
|
+
1. DRIVERS (Windows):
|
|
76
|
+
- download_url: Where to download drivers
|
|
77
|
+
- installation_steps: Step-by-step installation for Windows
|
|
78
|
+
Example:
|
|
79
|
+
1. Download from [URL]
|
|
80
|
+
2. Run installer
|
|
81
|
+
3. Connect debugger
|
|
82
|
+
4. Verify in Device Manager
|
|
83
|
+
|
|
84
|
+
2. DRIVERS (Linux):
|
|
85
|
+
- package_name: Package to install (if available)
|
|
86
|
+
- installation_steps: Commands to install
|
|
87
|
+
Example for J-Link:
|
|
88
|
+
1. Download from segger.com
|
|
89
|
+
2. sudo dpkg -i JLink_*.deb
|
|
90
|
+
3. Add udev rules
|
|
91
|
+
4. sudo usermod -a -G plugdev $USER
|
|
92
|
+
|
|
93
|
+
3. GDB SERVER:
|
|
94
|
+
- server_name: GDB server to use (JLinkGDBServer, OpenOCD, pyOCD, etc.)
|
|
95
|
+
- command_line: How to start server
|
|
96
|
+
Example: "JLinkGDBServer -device STM32F407VG -if SWD -speed 4000"
|
|
97
|
+
- config_file: Configuration if needed (OpenOCD .cfg)
|
|
98
|
+
- port: GDB port (default 3333)
|
|
99
|
+
"""
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
ds_vscode_launch_instructions = (
|
|
103
|
+
"""
|
|
104
|
+
Extract information systematically:
|
|
105
|
+
|
|
106
|
+
VS CODE LAUNCH.JSON:
|
|
107
|
+
- configuration_name: Name for the config (e.g., "Debug with J-Link")
|
|
108
|
+
- configuration_type: cortex-debug, cppdbg, etc.
|
|
109
|
+
- launch_json: Complete configuration object for .vscode/launch.json
|
|
110
|
+
- extensions_required: VS Code extensions needed
|
|
111
|
+
Common: "marus25.cortex-debug", "ms-vscode.cpptools"
|
|
112
|
+
- setup_notes: Any additional VS Code setup
|
|
113
|
+
"""
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
ds_add_tools_instructions = (
|
|
117
|
+
"""
|
|
118
|
+
Extract information systematically:
|
|
119
|
+
|
|
120
|
+
ADDITIONAL TOOLS:
|
|
121
|
+
- List helpful tools: OpenOCD, pyOCD, gdb-multiarch, etc.
|
|
122
|
+
"""
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
ds_troubleshooting_instructions = (
|
|
126
|
+
"""
|
|
127
|
+
Extract information systematically:
|
|
128
|
+
|
|
129
|
+
TROUBLESHOOTING:
|
|
130
|
+
- Common issues and solutions
|
|
131
|
+
- Connection problems
|
|
132
|
+
- Driver issues
|
|
133
|
+
- Permission issues (Linux)
|
|
134
|
+
"""
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
ds_quick_start_guide_instructions = (
|
|
138
|
+
"""
|
|
139
|
+
Extract information systematically:
|
|
140
|
+
|
|
141
|
+
QUICK START GUIDE:
|
|
142
|
+
Step-by-step instructions to get debugging working:
|
|
143
|
+
1. Install drivers
|
|
144
|
+
2. Connect debugger to target
|
|
145
|
+
3. Install VS Code extensions
|
|
146
|
+
4. Create/copy launch.json
|
|
147
|
+
5. Build project
|
|
148
|
+
6. Start debugging (F5)
|
|
149
|
+
"""
|
|
150
|
+
)
|
|
File without changes
|