优化skill,添加特征树、编辑窗口

This commit is contained in:
2026-08-07 17:44:12 +08:00
parent 35e18ca956
commit 5fbcf2c0b8
70 changed files with 13076 additions and 465 deletions
@@ -0,0 +1,152 @@
# SimpleCADAPI Agent Capability Guide
This document is the required backend capability brief for CAD Agent Studio
when considering the `simplecadapi` route. It is intentionally an Agent-facing
decision document, not a geometry template.
## Role
Use this route when the Agent will author backend-native SimpleCADAPI Python
source and the server will only execute, validate, normalize to DesignIR 3.0,
and publish artifacts.
SimpleCADAPI is strongest when its standard factories or replayable model graph
capabilities materially improve correctness, editability, or validation.
## Strong Fits
- The primary requested object is a supported standard mechanical family:
spur, helical, herringbone, ring, or bevel gears; racks; cycloidal discs;
ball bearings; bolts; nuts; roller-chain sprockets; planetary or cycloidal
reducers; joint actuator assemblies.
- The task benefits from `@model`, `ModelResult`, `capture_result`, model JSON,
graph replay, semantic tags, topology lineage, source mapping, QL inspection,
unit checking, tolerance chains, scene packages, or CAD translators.
- The task needs strict STEP/B-Rep diagnostics, target/candidate comparison,
multi-view diagnostics, or slice XOR diagnostics.
- The model is a reusable mechanical product family whose formulas should be a
deterministic SDK workflow rather than one-off source construction.
## Weak Fits
- The request is a custom machined part, flange, hub adapter, housing, bracket,
plate, or fixture where no SimpleCADAPI standard factory directly represents
the primary object.
- Standard part words appear only as feature context. Examples: bolt holes,
screw clearances, nut pockets, bearing seats, gear-mounting holes. These are
features of another part, not requests to generate the standard part itself.
- The requested shape needs freeform or multi-rail surface work outside the
documented SimpleCADAPI vocabulary.
- The current source of truth is an existing build123d source and the user did
not request backend conversion.
## Studio Documentation Discipline
For CAD Agent Studio backend selection, this document is sufficient. Decide
from the capability boundary here: what SimpleCADAPI is strong at, what it is
weak at, and whether the primary requested object matches those strengths.
Do not read SimpleCADAPI API manuals, SDK indexes, or per-function pages during
ordinary backend selection. The selection task only needs to know what the tool
can generate, not the exact API signatures.
Prefer standard-library functions only when the standard component is the
primary requested object and the factory actually covers it.
## Agent Source Contract
When choosing this backend, submit raw Python source to `generate_cad` with:
- `selectedBackend = "simplecadapi"`
- `sourceKind = "simplecadapi_python"`
- A script that accepts `--step`, `--metadata`, and `--model-json`
- STEP export written exactly to the `--step` path
- Model JSON written exactly to the `--model-json` path
- Metadata JSON written exactly to the `--metadata` path
- stdout JSON is optional diagnostic output; file artifacts are the source of
truth for execution success
The source should use one replayable `@model` entry point when model JSON or
graph replay is part of the task. Capture explicit outputs with
`capture_result(...)` and write the returned `ModelResult.model_json`.
Do not submit diagnostic, API-probing, topology-probing, radius-sweep, or smoke
test scripts as `nativeSource`. Do not use `inspect.signature(...)`, broad
`dir(...)` dumps, trial loops, or intentional exceptions to discover the SDK or
geometry at execution time. The source submitted to `generate_cad` must be the
final model generator for the user's part and must write the requested file
artifacts in one execution. If this compact reference is insufficient, report
the missing reference instead of using `generate_cad` as an exploration tool.
If exposing editable parameters, include a top-level block:
```python
# CAD_AGENT_PARAMETERS_START
PARAMETERS = {
"example": 1.0
}
# CAD_AGENT_PARAMETERS_END
```
Each editable metadata parameter must include `name`, `value`, `unit`,
`editable`, `binding_kind`, `parameter_path`, and `regenerate_adapter`.
Use `binding_kind = "python_constant"` for parameters bound to the
`PARAMETERS` block, or `model_graph_parameter` only when the parameter is
actually replayable through the model graph. Use
`regenerate_adapter = "simplecadapi"`.
## Minimal Native Source Reference
Use this compact API surface for Studio generation. It is included here so the
Agent can write production source without reading API indexes during backend
selection.
```python
import argparse
import json
from pathlib import Path
import simplecadapi as scad
@scad.model(graph_id="model")
def build_model():
shape = scad.std.gear.make_spur_gear_rsolid(
n_teeth=24,
module=2.0,
pressure_angle=20.0,
gear_height=8.0,
)
scad.capture_result(value=shape)
return shape
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--step", required=True)
parser.add_argument("--metadata", required=True)
parser.add_argument("--model-json", required=True)
args = parser.parse_args()
result = build_model()
Path(args.model_json).write_text(result.model_json, encoding="utf-8")
scad.export_step(shapes=result.value, filename=args.step)
Path(args.metadata).write_text(json.dumps({"backend": "simplecadapi"}), encoding="utf-8")
```
Replace the body of `build_model()` with the requested standard component or
workflow. Keep the CLI/output protocol unchanged.
Common standard gear factories:
- `scad.std.gear.make_spur_gear_rsolid(n_teeth: int, module: float, pressure_angle: float = 20.0, gear_height: float = 6.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25, backlash: float = 0.0)`
- `scad.std.gear.make_helical_gear_rsolid(n_teeth: int, module: float, pressure_angle: float = 20.0, helix_angle: float = 30.0, gear_height: float = 8.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25, backlash: float = 0.0)`
- `scad.std.gear.make_herringbone_gear_rsolid(n_teeth: int, module: float, pressure_angle: float = 20.0, helix_angle: float = 32.0, gear_height: float = 10.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25, backlash: float = 0.0)`
- `scad.std.gear.make_spur_ring_gear_rsolid(n_teeth: int, module: float, pressure_angle: float = 20.0, gear_height: float = 6.0, rim_thickness: float = 3.0, backlash: float = 0.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25)`
- `scad.std.gear.make_helical_ring_gear_rsolid(n_teeth: int, module: float, pressure_angle: float = 20.0, helix_angle: float = 25.0, gear_height: float = 8.0, rim_thickness: float = 3.0, backlash: float = 0.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25)`
- `scad.std.gear.make_straight_bevel_gear_rsolid(n_teeth: int, module: float, pitch_angle: float = 45.0, pressure_angle: float = 20.0, face_width: float = 8.0, *, addendum_factor: float = 1.0, clearance_factor: float = 0.25, backlash: float = 0.0)`
## Decision Rule
Choose SimpleCADAPI only when the primary requested model or required workflow
matches its documented standard factories or graph/semantic capabilities. Do
not select SimpleCADAPI merely because the prompt mentions a standard part word
inside another feature. For example, a wheel hub adapter with bolt holes is a
custom flange-like part unless the user asks to generate a bolt as the object.
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""Validate submitted Studio source against the imported SimpleCADAPI runtime.
This is a server-side guard only. Agent-facing API documentation remains the
original SimpleCADAPI skill and its API Markdown pages.
"""
from __future__ import annotations
import argparse
import ast
import importlib
import json
from pathlib import Path
from typing import Any
import simplecadapi as scad
STDLIB_MODULES = ("gear", "bearing", "chain", "fastener")
# Make the package's public ``scad.std.<module>`` paths available before AST
# resolution. Model source may use any of these documented standard factories.
for _module_name in STDLIB_MODULES:
importlib.import_module(f"simplecadapi.std.{_module_name}")
def resolve_simplecad_path(parts: list[str]) -> Any:
value: Any = scad
for part in parts:
value = getattr(value, part)
return value
class SourceValidator(ast.NodeVisitor):
def __init__(self) -> None:
self.package_aliases: set[str] = set()
self.imported_names: dict[str, tuple[str, list[str]]] = {}
self.errors: list[dict[str, object]] = []
def error(self, node: ast.AST, message: str) -> None:
self.errors.append({
"line": getattr(node, "lineno", None),
"column": getattr(node, "col_offset", None),
"message": message,
})
def visit_Import(self, node: ast.Import) -> None:
for alias in node.names:
if alias.name == "simplecadapi":
self.package_aliases.add(alias.asname or "simplecadapi")
self.generic_visit(node)
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
module = node.module or ""
if module == "simplecadapi" or module.startswith("simplecadapi."):
module_parts = module.split(".")[1:]
for alias in node.names:
if alias.name == "*":
self.error(node, "Wildcard imports from SimpleCADAPI are not allowed in submitted source.")
continue
try:
resolve_simplecad_path(module_parts + [alias.name])
except AttributeError:
self.error(alias, f"SimpleCADAPI import does not exist: {module}.{alias.name}")
self.imported_names[alias.asname or alias.name] = (module, module_parts + [alias.name])
self.generic_visit(node)
def attribute_path(self, node: ast.Attribute) -> tuple[str, list[str]] | None:
parts: list[str] = [node.attr]
current: ast.AST = node.value
while isinstance(current, ast.Attribute):
parts.append(current.attr)
current = current.value
if isinstance(current, ast.Name) and current.id in self.package_aliases:
return current.id, list(reversed(parts))
return None
def visit_Attribute(self, node: ast.Attribute) -> None:
resolved = self.attribute_path(node)
if resolved:
_, parts = resolved
try:
resolve_simplecad_path(parts)
except AttributeError:
self.error(node, f"SimpleCADAPI attribute does not exist: scad.{'.'.join(parts)}")
self.generic_visit(node)
def visit_Call(self, node: ast.Call) -> None:
if isinstance(node.func, ast.Name) and node.func.id in {"getattr", "setattr", "hasattr", "dir", "inspect"}:
self.error(node, "Dynamic or introspective SDK access is not allowed in submitted SimpleCADAPI source.")
self.generic_visit(node)
def validate_source(source_path: Path) -> dict[str, object]:
try:
source = source_path.read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(source_path))
except (OSError, SyntaxError) as exc:
return {"valid": False, "errors": [{"message": f"Cannot parse Python source: {exc}"}]}
validator = SourceValidator()
validator.visit(tree)
if not validator.package_aliases:
validator.errors.append({"message": "Submitted SimpleCADAPI source must import simplecadapi as scad."})
return {"valid": not validator.errors, "errors": validator.errors}
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--source", type=Path, required=True)
args = parser.parse_args()
print(json.dumps(validate_source(args.source), ensure_ascii=True))
if __name__ == "__main__":
main()