优化生成流程

This commit is contained in:
2026-08-25 17:41:24 +08:00
parent 51575161dc
commit beaa5dd9fe
50 changed files with 4204 additions and 4261 deletions
+3 -9
View File
@@ -6,22 +6,19 @@ The package exposes the CDSL-only rebuild API used by the backend Agent.
from __future__ import annotations
from .convert_to_cdsl import convert_sw_json_to_cdsl, write_cdsl_outputs
from .llm_compiler import compile_cdsl
from .llm_engine import run_engine_plan
from .rebuild import compare_with_gold, compile_cdsl_to_pack, run_cdsl_only, run_engine, run_rebuild
from .semantic_validation import validate_semantic_cdsl
from .sketch_solver import SHAPE_GENERATORS, resolve_all_sketches, resolve_required_sketches
from .sketch_solver import CORE_SHAPE_GENERATORS, resolve_all_sketches, resolve_required_sketches
from .runtime import ALL_ATOMIC_IDS, EXECUTORS, RuntimeExecutionError, analyze_cdsl, rebuild_cdsl
from .design_intent import DesignIntentError, design_intent_from_cdsl, validate_design_intent, validate_intent_cdsl
# The package-level runtime contract is the session executor registry. The
# older llm_engine dispatcher remains available only for legacy engine packs.
SUPPORTED_ATOMIC_IDS = ALL_ATOMIC_IDS
SHAPE_GENERATORS = CORE_SHAPE_GENERATORS
__all__ = [
"convert_sw_json_to_cdsl",
"write_cdsl_outputs",
"compile_cdsl",
"compile_cdsl_to_pack",
"run_engine_plan",
@@ -31,6 +28,7 @@ __all__ = [
"compare_with_gold",
"resolve_all_sketches",
"resolve_required_sketches",
"CORE_SHAPE_GENERATORS",
"SHAPE_GENERATORS",
"SUPPORTED_ATOMIC_IDS",
"validate_semantic_cdsl",
@@ -39,10 +37,6 @@ __all__ = [
"rebuild_cdsl",
"analyze_cdsl",
"RuntimeExecutionError",
"DesignIntentError",
"validate_design_intent",
"validate_intent_cdsl",
"design_intent_from_cdsl",
]
__version__ = "1.0.0"
+2 -1
View File
@@ -13,6 +13,7 @@ from collections import Counter
from pathlib import Path
from typing import Any, Iterable
from cdsl_importer.legacy_profile_adapter import lower_legacy_profiles
from .semantic_validation import validate_semantic_cdsl
@@ -225,7 +226,7 @@ def analyze_document(
"failure_category": "preflight_blocked",
}
try:
cdsl = json.loads(path.read_text(encoding="utf-8"))
cdsl = lower_legacy_profiles(json.loads(path.read_text(encoding="utf-8")))
report["part_id"] = str(cdsl.get("part_id") or report["part_id"])
semantic = validate_semantic_cdsl(cdsl)
report["semantic_valid"] = True
@@ -5,7 +5,7 @@ from __future__ import annotations
import math
from typing import Any, Iterable
from build123d import Axis, Edge, Face, Plane, Solid, Vector, Wire, export_step
from build123d import Axis, Compound, Edge, Face, Plane, Solid, Vector, Wire, export_step
from .runtime_types import AxisSpec, HoleSpec, PlaneSpec, TopologyRecord, Vector3, canonical_plane_signature
@@ -201,13 +201,23 @@ class Build123dGeometryAdapter:
def revolve(face: Face, angle_deg: float, axis: AxisSpec) -> Solid:
return Solid.revolve(face, angle_deg, Build123dGeometryAdapter.axis(axis))
@staticmethod
def _body_shape(value: Any) -> Any:
"""Normalize boolean results, including disconnected ShapeList values."""
if hasattr(value, "bounding_box"):
return value
shapes = list(value)
if not shapes:
raise ValueError("Boolean operation produced no shapes")
return shapes[0] if len(shapes) == 1 else Compound(shapes)
@staticmethod
def fuse(body: Any | None, solid: Solid) -> Any:
return solid if body is None else body.fuse(solid)
return solid if body is None else Build123dGeometryAdapter._body_shape(body.fuse(solid))
@staticmethod
def cut(body: Any, tool: Any) -> Any:
return body.cut(tool)
return Build123dGeometryAdapter._body_shape(body.cut(tool))
@staticmethod
def sphere(radius_mm: float, center_mm: Vector3) -> Solid:
+4 -86
View File
@@ -258,24 +258,18 @@
"analyticSegment": {
"type": "object",
"properties": {
"type": {"enum": ["line", "arc", "circle", "bspline"]},
"type": {"enum": ["line", "arc", "circle"]},
"start": {"$ref": "#/$defs/point2"},
"end": {"$ref": "#/$defs/point2"},
"center": {"$ref": "#/$defs/point2"},
"radius_mm": {"$ref": "#/$defs/positive"},
"clockwise": {"type": "boolean"},
"degree": {"type": "integer", "minimum": 1},
"control_points": {"type": "array", "items": {"$ref": "#/$defs/point2"}},
"knots": {"type": "array", "items": {"$ref": "#/$defs/number"}},
"weights": {"type": "array", "items": {"$ref": "#/$defs/positive"}},
"periodic": {"type": "boolean"}
"clockwise": {"type": "boolean"}
},
"required": ["type"],
"allOf": [
{"if": {"properties": {"type": {"const": "line"}}}, "then": {"required": ["start", "end"]}},
{"if": {"properties": {"type": {"const": "arc"}}}, "then": {"required": ["start", "end", "center", "radius_mm"]}},
{"if": {"properties": {"type": {"const": "circle"}}}, "then": {"required": ["center", "radius_mm"]}},
{"if": {"properties": {"type": {"const": "bspline"}}}, "then": {"required": ["degree", "control_points", "knots"]}}
{"if": {"properties": {"type": {"const": "circle"}}}, "then": {"required": ["center", "radius_mm"]}}
],
"additionalProperties": false
},
@@ -334,87 +328,11 @@
{"if": {"properties": {"atomic_id": {"const": "hole_wizard"}}}, "then": {"properties": {"params": {"$ref": "#/$defs/holeWizardParams"}}}}
]
},
"rectangleBounds": {
"type": "object",
"properties": {"center": {"$ref": "#/$defs/point2"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "min_mm": {"$ref": "#/$defs/point2"}, "max_mm": {"$ref": "#/$defs/point2"}},
"allOf": [
{"oneOf": [
{"required": ["center", "width_mm", "height_mm"], "not": {"anyOf": [{"required": ["min_mm"]}, {"required": ["max_mm"]}]}},
{"required": ["min_mm", "max_mm"], "not": {"anyOf": [{"required": ["center"]}, {"required": ["width_mm"]}, {"required": ["height_mm"]}]}}
]}
]
},
"rectangleBoundary": {
"type": "object",
"properties": {"type": {"enum": ["rectangle", "rectangle_with_fillets"]}, "center": {"$ref": "#/$defs/point2"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "min_mm": {"$ref": "#/$defs/point2"}, "max_mm": {"$ref": "#/$defs/point2"}, "fillet_radius_mm": {"$ref": "#/$defs/positive"}},
"allOf": [{"$ref": "#/$defs/rectangleBounds"}],
"additionalProperties": false
},
"profile_type": {"enum": ["circle", "annulus", "circles", "circle_grid", "rectangle", "rectangle_with_circles", "rectangle_with_fillets", "obround", "polygon", "ibone", "rectangle_with_symmetric_notches", "revolve_chamfer", "revolve_chamfer_slanted", "circle_with_arc_notches", "circular_sector_slot", "circle_with_radial_tabs", "filleted_rect_side_slots", "d_shape", "partial_ring", "partial_ring_with_arc_island", "radial_slot", "arc_chain", "patterned_cutouts", "compound_patterned_cutouts", "analytic_contours"]},
"motif_type": {"enum": ["circle", "square", "rectangle", "obround", "cross", "d_shape_polygon", "regular_hexagon", "skew_hexagon", "triangle", "teardrop_polygon", "trapezoid", "annular_sector_polygon"]},
"layout_type": {"enum": ["ring", "angular", "concentric_rings", "disc_grid", "open_arc", "spiral", "cross_lines", "x_field", "twin_strips", "corner_clusters", "diamond_field"]},
"motif": {
"oneOf": [
{"type": "object", "properties": {"type": {"const": "circle"}, "radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "square"}, "width_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "rectangle"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "width_mm", "height_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "obround"}, "length_mm": {"$ref": "#/$defs/positive"}, "width_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "length_mm", "width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "cross"}, "size_mm": {"$ref": "#/$defs/positive"}, "arm_width_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "size_mm", "arm_width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "d_shape_polygon"}, "stem_length_mm": {"$ref": "#/$defs/positive"}, "nose_depth_mm": {"$ref": "#/$defs/positive"}, "half_height_mm": {"$ref": "#/$defs/positive"}, "arc_segments": {"$ref": "#/$defs/positiveInteger"}}, "required": ["type", "stem_length_mm", "nose_depth_mm", "half_height_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "regular_hexagon"}, "radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "skew_hexagon"}, "nominal_radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "nominal_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "triangle"}, "radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "teardrop_polygon"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "shoulder_fraction": {"type": "number", "exclusiveMinimum": 0, "exclusiveMaximum": 1}, "left_width_mm": {"$ref": "#/$defs/positive"}, "right_width_mm": {"$ref": "#/$defs/positive"}, "tip_height_mm": {"$ref": "#/$defs/positive"}, "bottom_depth_mm": {"$ref": "#/$defs/positive"}, "shoulder_height_mm": {"$ref": "#/$defs/positive"}}, "required": ["type"], "oneOf": [{"required": ["width_mm", "height_mm"], "not": {"anyOf": [{"required": ["left_width_mm"]}, {"required": ["right_width_mm"]}, {"required": ["tip_height_mm"]}, {"required": ["bottom_depth_mm"]}, {"required": ["shoulder_height_mm"]}]}}, {"required": ["left_width_mm", "right_width_mm", "tip_height_mm", "bottom_depth_mm", "shoulder_height_mm"], "not": {"anyOf": [{"required": ["width_mm"]}, {"required": ["height_mm"]}, {"required": ["shoulder_fraction"]}]}}], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "trapezoid"}, "bottom_width_mm": {"$ref": "#/$defs/positive"}, "top_width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "bottom_width_mm", "top_width_mm", "height_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "annular_sector_polygon"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "half_angle_deg": {"$ref": "#/$defs/positive"}, "arc_segments": {"$ref": "#/$defs/positiveInteger"}}, "required": ["type", "inner_radius_mm", "outer_radius_mm", "half_angle_deg"], "additionalProperties": false}
]
},
"layoutCommon": {
"type": "object",
"properties": {"orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}
},
"layout": {
"oneOf": [
{"type": "object", "properties": {"type": {"const": "ring"}, "radius_mm": {"$ref": "#/$defs/positive"}, "count": {"$ref": "#/$defs/positiveInteger"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "angle_step_deg": {"$ref": "#/$defs/number"}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm", "count"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "angular"}, "radius_mm": {"$ref": "#/$defs/number"}, "count": {"$ref": "#/$defs/positiveInteger"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "angle_step_deg": {"$ref": "#/$defs/number"}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "count"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "concentric_rings"}, "rings": {"type": "array", "minItems": 1, "items": {"type": "object", "properties": {"radius_mm": {"$ref": "#/$defs/positive"}, "count": {"$ref": "#/$defs/positiveInteger"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "angle_step_deg": {"$ref": "#/$defs/number"}}, "required": ["radius_mm", "count"], "additionalProperties": false}}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "rings"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "disc_grid"}, "count_x": {"$ref": "#/$defs/positiveInteger"}, "count_y": {"$ref": "#/$defs/positiveInteger"}, "spacing_x_mm": {"$ref": "#/$defs/positive"}, "spacing_y_mm": {"$ref": "#/$defs/positive"}, "center_mm": {"$ref": "#/$defs/point2"}, "max_center_radius_mm": {"$ref": "#/$defs/positive"}, "orientation_offset_deg": {"$ref": "#/$defs/number"}}, "required": ["type", "count_x", "count_y", "spacing_x_mm", "spacing_y_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "open_arc"}, "radius_mm": {"$ref": "#/$defs/positive"}, "count": {"$ref": "#/$defs/positiveInteger"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "end_angle_deg": {"$ref": "#/$defs/number"}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm", "count", "start_angle_deg", "end_angle_deg"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "spiral"}, "count": {"$ref": "#/$defs/positiveInteger"}, "start_radius_mm": {"$ref": "#/$defs/positive"}, "radius_step_mm": {"$ref": "#/$defs/number"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "angle_step_deg": {"$ref": "#/$defs/number"}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "count", "start_radius_mm", "radius_step_mm", "angle_step_deg"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "cross_lines"}, "count_per_axis": {"$ref": "#/$defs/positiveInteger"}, "spacing_mm": {"$ref": "#/$defs/positive"}, "orientation_offset_deg": {"$ref": "#/$defs/number"}}, "required": ["type", "count_per_axis", "spacing_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "x_field"}, "levels": {"$ref": "#/$defs/positiveInteger"}, "spacing_mm": {"$ref": "#/$defs/positive"}, "orientation": {"enum": ["fixed", "radial", "tangential", "snapped_radial", "diagonal_axes"]}, "orientation_offset_deg": {"$ref": "#/$defs/number"}, "orientation_snap_deg": {"$ref": "#/$defs/positive"}}, "required": ["type", "levels", "spacing_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "twin_strips"}, "x_offset_mm": {"$ref": "#/$defs/positive"}, "count_y": {"$ref": "#/$defs/positiveInteger"}, "y_start_mm": {"$ref": "#/$defs/number"}, "y_end_mm": {"$ref": "#/$defs/number"}, "orientation_offset_deg": {"$ref": "#/$defs/number"}}, "required": ["type", "x_offset_mm", "count_y", "y_start_mm", "y_end_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "corner_clusters"}, "levels_mm": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/positive"}}, "orientation_offset_deg": {"$ref": "#/$defs/number"}}, "required": ["type", "levels_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "diamond_field"}, "manhattan_radius": {"type": "integer", "minimum": 0}, "spacing_mm": {"$ref": "#/$defs/positive"}, "orientation_offset_deg": {"$ref": "#/$defs/number"}}, "required": ["type", "manhattan_radius", "spacing_mm"], "additionalProperties": false}
]
},
"pattern": {"type": "object", "properties": {"motif": {"$ref": "#/$defs/motif"}, "layout": {"$ref": "#/$defs/layout"}}, "required": ["motif", "layout"], "additionalProperties": false},
"profile_type": {"enum": ["circle", "polygon", "analytic_contours"]},
"profile": {
"oneOf": [
{"type": "object", "properties": {"type": {"const": "circle"}, "center": {"$ref": "#/$defs/point2"}, "radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "annulus"}, "center": {"$ref": "#/$defs/point2"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "inner_radius_mm", "outer_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "circles"}, "items": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "items"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "circle_grid"}, "radius_mm": {"$ref": "#/$defs/positive"}, "count_x": {"$ref": "#/$defs/positiveInteger"}, "count_y": {"$ref": "#/$defs/positiveInteger"}, "spacing_x_mm": {"$ref": "#/$defs/positive"}, "spacing_y_mm": {"$ref": "#/$defs/positive"}, "origin_mm": {"$ref": "#/$defs/point2"}, "center_mm": {"$ref": "#/$defs/point2"}}, "required": ["type", "radius_mm", "count_x", "count_y", "spacing_x_mm", "spacing_y_mm"], "additionalProperties": false, "oneOf": [{"required": ["origin_mm"], "not": {"required": ["center_mm"]}}, {"required": ["center_mm"], "not": {"required": ["origin_mm"]}}]},
{"type": "object", "properties": {"type": {"const": "rectangle"}, "center": {"$ref": "#/$defs/point2"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "min_mm": {"$ref": "#/$defs/point2"}, "max_mm": {"$ref": "#/$defs/point2"}}, "required": ["type"], "allOf": [{"$ref": "#/$defs/rectangleBounds"}], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "rectangle_with_circles"}, "boundary": {"$ref": "#/$defs/rectangleBoundary"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "boundary"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "rectangle_with_fillets"}, "center": {"$ref": "#/$defs/point2"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "min_mm": {"$ref": "#/$defs/point2"}, "max_mm": {"$ref": "#/$defs/point2"}, "fillet_radius_mm": {"$ref": "#/$defs/positive"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type"], "allOf": [{"$ref": "#/$defs/rectangleBounds"}], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "obround"}, "center": {"$ref": "#/$defs/point2"}, "length_mm": {"$ref": "#/$defs/positive"}, "width_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "length_mm", "width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "polygon"}, "vertices": {"type": "array", "minItems": 3, "items": {"$ref": "#/$defs/point2"}}}, "required": ["type", "vertices"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "ibone"}, "body_width_mm": {"$ref": "#/$defs/positive"}, "body_height_mm": {"$ref": "#/$defs/positive"}, "flange_width_mm": {"$ref": "#/$defs/positive"}, "flange_height_mm": {"$ref": "#/$defs/positive"}, "corner_radius_mm": {"$ref": "#/$defs/positive"}, "hole_radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["type", "body_width_mm", "body_height_mm", "flange_width_mm", "flange_height_mm", "corner_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "rectangle_with_symmetric_notches"}, "width_mm": {"$ref": "#/$defs/positive"}, "height_mm": {"$ref": "#/$defs/positive"}, "notch": {"type": "object", "properties": {"y_start": {"$ref": "#/$defs/number"}, "y_end": {"$ref": "#/$defs/number"}, "depth_mm": {"$ref": "#/$defs/positive"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "corner_radius_mm": {"$ref": "#/$defs/positive"}}, "required": ["y_start", "y_end", "depth_mm"], "additionalProperties": false}}, "required": ["type", "width_mm", "height_mm", "notch"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "revolve_chamfer"}, "axis_height_mm": {"$ref": "#/$defs/positive"}, "top_width_mm": {"$ref": "#/$defs/positive"}, "bottom_width_mm": {"$ref": "#/$defs/positive"}, "wall_inset_mm": {"$ref": "#/$defs/number"}, "step_inset_mm": {"$ref": "#/$defs/number"}, "on_axis_side": {"enum": ["left", "right"]}}, "required": ["type", "axis_height_mm", "top_width_mm", "bottom_width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "revolve_chamfer_slanted"}, "axis_height_mm": {"$ref": "#/$defs/positive"}, "top_width_mm": {"$ref": "#/$defs/positive"}, "wall_inset_mm": {"$ref": "#/$defs/number"}, "wall_height_mm": {"$ref": "#/$defs/positive"}, "wall_width_mm": {"$ref": "#/$defs/positive"}, "on_axis_side": {"enum": ["left", "right"]}}, "required": ["type", "axis_height_mm", "top_width_mm", "wall_height_mm", "wall_width_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "circle_with_arc_notches"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "notch_radius_mm": {"$ref": "#/$defs/positive"}, "notch_angles_deg": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/number"}}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "outer_radius_mm", "notch_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "circular_sector_slot"}, "arc_radius_mm": {"$ref": "#/$defs/positive"}, "slot_half_width_mm": {"$ref": "#/$defs/positive"}, "chord_half_mm": {"$ref": "#/$defs/positive"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "arc_radius_mm", "slot_half_width_mm", "chord_half_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "circle_with_radial_tabs"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "tab_u_half_mm": {"$ref": "#/$defs/positive"}, "tab_v_offset_mm": {"$ref": "#/$defs/number"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "outer_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "filleted_rect_side_slots"}, "half_width_mm": {"$ref": "#/$defs/positive"}, "half_height_mm": {"$ref": "#/$defs/positive"}, "corner_radius_mm": {"$ref": "#/$defs/positive"}, "slot_radius_mm": {"$ref": "#/$defs/positive"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "half_width_mm", "half_height_mm", "corner_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "d_shape"}, "radius_mm": {"$ref": "#/$defs/positive"}, "chord_sign": {"enum": ["left", "right"]}, "chord_x_mm": {"$ref": "#/$defs/positive"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "partial_ring"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "half_angle_deg": {"$ref": "#/$defs/positive"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "inner_radius_mm", "outer_radius_mm"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "partial_ring_with_arc_island"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "half_angle_deg": {"$ref": "#/$defs/positive"}, "island_radius_mm": {"$ref": "#/$defs/positive"}, "island_gap_mm": {"$ref": "#/$defs/positive"}, "center_angles_deg": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/number"}}, "replicas": {"type": "array", "minItems": 1, "items": {"type": "object", "properties": {"center_angle_deg": {"$ref": "#/$defs/number"}}, "required": ["center_angle_deg"], "additionalProperties": false}}}, "required": ["type", "inner_radius_mm", "outer_radius_mm", "island_radius_mm"], "additionalProperties": false, "oneOf": [{"required": ["center_angles_deg"], "not": {"required": ["replicas"]}}, {"required": ["replicas"], "not": {"required": ["center_angles_deg"]}}]},
{"type": "object", "properties": {"type": {"const": "radial_slot"}, "inner_radius_mm": {"$ref": "#/$defs/positive"}, "outer_radius_mm": {"$ref": "#/$defs/positive"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "end_angle_deg": {"$ref": "#/$defs/number"}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "inner_radius_mm", "outer_radius_mm", "start_angle_deg", "end_angle_deg"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "arc_chain"}, "arcs": {"type": "array", "minItems": 2, "items": {"type": "object", "properties": {"center": {"$ref": "#/$defs/point2"}, "radius_mm": {"$ref": "#/$defs/positive"}, "start_angle_deg": {"$ref": "#/$defs/number"}, "end_angle_deg": {"$ref": "#/$defs/number"}}, "required": ["radius_mm", "start_angle_deg", "end_angle_deg"], "additionalProperties": false}}, "circles": {"type": "array", "items": {"$ref": "#/$defs/circleItem"}}}, "required": ["type", "arcs"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "patterned_cutouts"}, "motif": {"$ref": "#/$defs/motif"}, "layout": {"$ref": "#/$defs/layout"}}, "required": ["type", "motif", "layout"], "additionalProperties": false},
{"type": "object", "properties": {"type": {"const": "compound_patterned_cutouts"}, "patterns": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/pattern"}}}, "required": ["type", "patterns"], "additionalProperties": false},
{"$ref": "#/$defs/analyticProfile"}
]
},
File diff suppressed because it is too large Load Diff
-331
View File
@@ -1,331 +0,0 @@
"""Validation for the semantic planning artifact between a request and CDSL."""
from __future__ import annotations
import json
from copy import deepcopy
from pathlib import Path
from typing import Any
from jsonschema import Draft202012Validator
from jsonschema.exceptions import SchemaError
class DesignIntentError(ValueError):
"""A plan-contract violation with a stable error code for the Agent."""
def __init__(self, code: str, message: str) -> None:
super().__init__(message)
self.code = code
def _schema_path(engine: Any) -> Path:
return Path(str(engine.__file__)).with_name("design_intent_schema.json")
def load_design_intent_schema(engine: Any) -> dict[str, Any]:
path = _schema_path(engine)
try:
schema = json.loads(path.read_text(encoding="utf-8"))
Draft202012Validator.check_schema(schema)
except (OSError, json.JSONDecodeError, SchemaError) as error:
raise RuntimeError("The local DesignIntent JSON Schema is unavailable or invalid") from error
return schema
def _location(error: Any) -> str:
return "$" + "".join(
f"[{item}]" if isinstance(item, int) else f".{item}"
for item in error.absolute_path
)
def _schema_validate(intent: dict[str, Any], engine: Any) -> None:
errors = sorted(
Draft202012Validator(load_design_intent_schema(engine)).iter_errors(intent),
key=lambda error: (list(error.absolute_path), error.message),
)
if errors:
error = errors[0]
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"DesignIntent schema violation at {_location(error)}: {error.message}",
)
def _runtime_capabilities(engine: Any) -> tuple[set[str], set[str], dict[str, dict[str, Any]]]:
try:
profile_schema = json.loads(Path(str(engine.__file__)).with_name("profile_schema.json").read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise RuntimeError("The local CDSL profile schema is unavailable or invalid") from error
atomic_ids = set(profile_schema.get("runtime_supported_atomic_ids") or getattr(engine, "SUPPORTED_ATOMIC_IDS", ()))
profiles = {
name
for name, contract in (profile_schema.get("profiles") or {}).items()
if isinstance(contract, dict) and contract.get("agent_allowed") is True
}
atomic_contracts = {
str(atomic_id): contract
for atomic_id, contract in (profile_schema.get("feature_atomic_ids") or {}).items()
if isinstance(contract, dict)
}
return {str(item) for item in atomic_ids}, {str(item) for item in profiles}, atomic_contracts
def _blocking(intent: dict[str, Any]) -> bool:
questions = intent.get("open_questions") or []
gaps = intent.get("capability_gaps") or []
return any(isinstance(item, dict) and item.get("blocking") is True for item in [*questions, *gaps])
def validate_design_intent(
intent: dict[str, Any],
engine: Any,
*,
current_revision_id: str = "",
) -> dict[str, Any]:
"""Validate DesignIntent syntax and semantic ordering against runtime capability."""
if not isinstance(intent, dict):
raise DesignIntentError("INVALID_DESIGN_INTENT", "DesignIntent must be a JSON object")
_schema_validate(intent, engine)
normalized = deepcopy(intent)
mode = str(normalized["mode"])
base_revision_id = str(normalized["base_revision_id"] or "")
if mode == "create" and base_revision_id:
raise DesignIntentError("INVALID_DESIGN_INTENT", "create DesignIntent must not set base_revision_id")
if mode == "revise":
if not base_revision_id:
raise DesignIntentError("INVALID_DESIGN_INTENT", "revise DesignIntent requires base_revision_id")
if current_revision_id and base_revision_id != current_revision_id:
raise DesignIntentError(
"DESIGN_INTENT_BASE_REVISION_MISMATCH",
f"revise DesignIntent base_revision_id must be {current_revision_id}, the current successful revision",
)
structures = normalized["structures"]
ids = [str(item["id"]) for item in structures]
feature_ids = [str(item["cdsl_feature_id"]) for item in structures]
if len(set(ids)) != len(ids):
raise DesignIntentError("INVALID_DESIGN_INTENT", "DesignIntent structure ids must be unique")
if len(set(feature_ids)) != len(feature_ids):
raise DesignIntentError("INVALID_DESIGN_INTENT", "DesignIntent cdsl_feature_id values must be unique")
feature_order = [str(item) for item in normalized["feature_order"]]
if len(set(feature_order)) != len(feature_order) or set(feature_order) != set(ids):
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
"feature_order must list every DesignIntent structure exactly once",
)
order = {structure_id: index for index, structure_id in enumerate(feature_order)}
atomic_ids, profile_types, atomic_contracts = _runtime_capabilities(engine)
for structure in structures:
structure_id = str(structure["id"])
for dependency in structure["depends_on"]:
dependency_id = str(dependency)
if dependency_id not in order:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Structure {structure_id} depends on an unknown structure {dependency_id}",
)
if order[dependency_id] >= order[structure_id]:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Structure {structure_id} must depend only on earlier feature_order entries",
)
strategy = structure["cdsl_strategy"]
atomic_id = str(strategy["atomic_id"])
if atomic_id not in atomic_ids:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Structure {structure_id} uses unsupported atomic_id {atomic_id}",
)
profile_type = str(strategy.get("profile_type") or "")
if atomic_contracts.get(atomic_id, {}).get("requires_sketch") and not profile_type:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Structure {structure_id} uses {atomic_id} and requires a profile_type",
)
if profile_type and profile_type not in profile_types:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Structure {structure_id} uses unsupported or agent-disallowed profile_type {profile_type}",
)
if normalized["status"] == "ready" and _blocking(normalized):
raise DesignIntentError(
"DESIGN_INTENT_BLOCKED",
"A DesignIntent with blocking questions or capability gaps must need clarification",
)
if normalized["status"] == "needs_clarification" and not _blocking(normalized):
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
"needs_clarification requires a blocking question or capability gap",
)
if normalized["status"] == "ready" and not structures:
raise DesignIntentError("INVALID_DESIGN_INTENT", "A ready DesignIntent needs at least one structure")
if normalized["status"] == "ready" and mode == "create" and not any(item["role"] == "base" for item in structures):
raise DesignIntentError("INVALID_DESIGN_INTENT", "A ready create DesignIntent needs a base structure")
nonblocking_gaps = [item for item in normalized["capability_gaps"] if not item["blocking"]]
if nonblocking_gaps and not normalized["assumptions"]:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
"Non-blocking capability gaps require an explicit approximation assumption",
)
for gap in normalized["capability_gaps"]:
if str(gap["structure_id"]) not in set(ids):
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"Capability gap references an unknown structure {gap['structure_id']}",
)
valid_expectation_ids = set(ids) | set(feature_ids)
for expectation in normalized["verification_expectations"]:
if expectation["type"] == "feature_count" and str(expectation["feature_id"]) not in valid_expectation_ids:
raise DesignIntentError(
"INVALID_DESIGN_INTENT",
f"feature_count expectation references an unknown structure or CDSL feature {expectation['feature_id']}",
)
return normalized
def _has_selector_evidence(value: Any, role: str) -> bool:
if isinstance(value, dict):
for key, child in value.items():
if role in {"selector", "selectors"} and key == "selectors" and isinstance(child, list) and child:
return True
if key == role and child not in (None, "", [], {}):
if not (isinstance(child, dict) and set(child) == {"unresolved"}):
return True
if _has_selector_evidence(child, role):
return True
elif isinstance(value, list):
return any(_has_selector_evidence(item, role) for item in value)
return False
def validate_intent_cdsl(
intent: dict[str, Any],
cdsl: dict[str, Any],
engine: Any,
*,
current_cdsl: dict[str, Any] | None = None,
) -> None:
"""Ensure an executable CDSL document faithfully realizes one accepted plan."""
normalized = validate_design_intent(intent, engine)
if normalized["status"] != "ready":
raise DesignIntentError("DESIGN_INTENT_BLOCKED", "CDSL generation is blocked until the DesignIntent is ready")
if not isinstance(cdsl, dict):
raise DesignIntentError("INTENT_CDSL_MISMATCH", "CDSL must be a JSON object")
features = cdsl.get("features")
sketches = (cdsl.get("geometry") or {}).get("sketches")
if not isinstance(features, list) or not isinstance(sketches, list):
raise DesignIntentError("INTENT_CDSL_MISMATCH", "CDSL must contain features and sketches")
feature_by_id = {str(feature.get("id") or ""): feature for feature in features if isinstance(feature, dict)}
if len(feature_by_id) != len(features):
raise DesignIntentError("INTENT_CDSL_MISMATCH", "CDSL features must have unique ids")
structure_by_id = {str(item["id"]): item for item in normalized["structures"]}
expected_feature_ids = [str(structure_by_id[item]["cdsl_feature_id"]) for item in normalized["feature_order"]]
if normalized["mode"] == "revise" and isinstance(current_cdsl, dict):
base_feature_ids = {
str(feature.get("id") or "")
for feature in current_cdsl.get("features") or []
if isinstance(feature, dict) and feature.get("id")
}
if not base_feature_ids.issubset(set(expected_feature_ids)):
missing = ", ".join(sorted(base_feature_ids - set(expected_feature_ids)))
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
f"revise DesignIntent must explicitly preserve current features: {missing}",
)
actual_feature_ids = [str(feature.get("id") or "") for feature in features]
if actual_feature_ids != expected_feature_ids:
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
"CDSL feature ids and order must exactly match DesignIntent feature_order",
)
sketch_by_id = {str(sketch.get("id") or ""): sketch for sketch in sketches if isinstance(sketch, dict)}
feature_for_structure = {
str(structure["id"]): feature_by_id[str(structure["cdsl_feature_id"])]
for structure in normalized["structures"]
}
for structure in normalized["structures"]:
structure_id = str(structure["id"])
feature = feature_for_structure[structure_id]
strategy = structure["cdsl_strategy"]
if feature.get("atomic_id") != strategy["atomic_id"]:
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
f"CDSL feature {feature.get('id')} atomic_id does not match structure {structure_id}",
)
expected_dependencies = {
str(structure_by_id[dependency]["cdsl_feature_id"])
for dependency in structure["depends_on"]
}
actual_dependencies = {str(item) for item in feature.get("depends_on") or []}
if not expected_dependencies.issubset(actual_dependencies):
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
f"CDSL feature {feature.get('id')} is missing planned dependencies",
)
profile_type = str(strategy.get("profile_type") or "")
if profile_type:
sketch_id = str(feature.get("sketch_id") or "")
sketch = sketch_by_id.get(sketch_id)
actual_profile_type = str(((sketch or {}).get("profile") or {}).get("type") or "")
if actual_profile_type != profile_type:
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
f"CDSL feature {feature.get('id')} must use profile_type {profile_type}",
)
for selector_role in strategy["selector_roles"]:
if not _has_selector_evidence(feature, str(selector_role)):
raise DesignIntentError(
"INTENT_CDSL_MISMATCH",
f"CDSL feature {feature.get('id')} is missing selector evidence for {selector_role}",
)
def design_intent_from_cdsl(cdsl: dict[str, Any], *, request: str, mode: str = "create", base_revision_id: str = "") -> dict[str, Any]:
"""Create a conservative audit-only plan for legacy revisions when needed."""
sketches = (cdsl.get("geometry") or {}).get("sketches") or []
profile_by_sketch_id = {
str(sketch.get("id") or ""): str((sketch.get("profile") or {}).get("type") or "")
for sketch in sketches
if isinstance(sketch, dict)
}
structures: list[dict[str, Any]] = []
for index, feature in enumerate(cdsl.get("features") or []):
if not isinstance(feature, dict):
continue
feature_id = str(feature.get("id") or f"feature_{index + 1}")
atomic_id = str(feature.get("atomic_id") or "")
profile_type = profile_by_sketch_id.get(str(feature.get("sketch_id") or ""), "")
strategy: dict[str, Any] = {
"atomic_id": atomic_id,
"parameter_roles": sorted(str(key) for key in (feature.get("params") or {}).keys()),
"selector_roles": [],
}
if profile_type:
strategy["profile_type"] = profile_type
structures.append({
"id": feature_id,
"cdsl_feature_id": feature_id,
"role": "base" if index == 0 else "subtractive" if "cut" in atomic_id or atomic_id.startswith("hole") else "additive",
"purpose": str(feature.get("name") or feature_id),
"depends_on": [str(item) for item in feature.get("depends_on") or []],
"cdsl_strategy": strategy,
})
return {
"schema": "cad.cdsl.design-intent.v1",
"schema_version": "1.0",
"mode": mode,
"request": request,
"base_revision_id": base_revision_id,
"structures": structures,
"feature_order": [item["id"] for item in structures],
"assumptions": ["Synthesized from a legacy CDSL revision."],
"open_questions": [],
"capability_gaps": [],
"verification_expectations": [],
"status": "ready",
}
@@ -1,124 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cdsl.local/schema/cad.cdsl.design-intent.v1",
"title": "CDSL design intent",
"type": "object",
"properties": {
"schema": {"const": "cad.cdsl.design-intent.v1"},
"schema_version": {"const": "1.0"},
"mode": {"enum": ["create", "revise"]},
"request": {"type": "string", "minLength": 1},
"base_revision_id": {"type": "string"},
"structures": {"type": "array", "items": {"$ref": "#/$defs/structure"}},
"feature_order": {"type": "array", "items": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"}},
"assumptions": {"type": "array", "items": {"type": "string", "minLength": 1}},
"open_questions": {"type": "array", "items": {"$ref": "#/$defs/openQuestion"}},
"capability_gaps": {"type": "array", "items": {"$ref": "#/$defs/capabilityGap"}},
"verification_expectations": {"type": "array", "items": {"$ref": "#/$defs/verificationExpectation"}},
"status": {"enum": ["ready", "needs_clarification"]},
"intent_id": {"type": "string", "pattern": "^intent_[a-z0-9]{12}$"},
"created_at": {"type": "string", "minLength": 1},
"part_skill_ids": {"type": "array", "items": {"type": "string", "minLength": 1}},
"part_skill_selection": {"type": "object"}
},
"required": [
"schema",
"schema_version",
"mode",
"request",
"base_revision_id",
"structures",
"feature_order",
"assumptions",
"open_questions",
"capability_gaps",
"verification_expectations",
"status"
],
"additionalProperties": false,
"$defs": {
"structure": {
"type": "object",
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"cdsl_feature_id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"role": {"enum": ["base", "reference", "additive", "subtractive", "dressup", "pattern"]},
"purpose": {"type": "string", "minLength": 1},
"depends_on": {"type": "array", "items": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"}},
"cdsl_strategy": {"$ref": "#/$defs/cdslStrategy"}
},
"required": ["id", "cdsl_feature_id", "role", "purpose", "depends_on", "cdsl_strategy"],
"additionalProperties": false
},
"cdslStrategy": {
"type": "object",
"properties": {
"atomic_id": {"type": "string", "minLength": 1},
"profile_type": {"type": "string", "minLength": 1},
"parameter_roles": {"type": "array", "items": {"type": "string", "minLength": 1}},
"selector_roles": {"type": "array", "items": {"type": "string", "minLength": 1}}
},
"required": ["atomic_id", "parameter_roles", "selector_roles"],
"additionalProperties": false
},
"openQuestion": {
"type": "object",
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"question": {"type": "string", "minLength": 1},
"blocking": {"type": "boolean"}
},
"required": ["id", "question", "blocking"],
"additionalProperties": false
},
"capabilityGap": {
"type": "object",
"properties": {
"code": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,100}$"},
"structure_id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"message": {"type": "string", "minLength": 1},
"blocking": {"type": "boolean"}
},
"required": ["code", "structure_id", "message", "blocking"],
"additionalProperties": false
},
"verificationExpectation": {
"oneOf": [
{
"type": "object",
"properties": {
"type": {"const": "feature_count"},
"feature_id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"expected": {"type": "integer", "minimum": 0}
},
"required": ["type", "feature_id", "expected"],
"additionalProperties": false
},
{
"type": "object",
"properties": {"type": {"const": "symmetry"}, "axis": {"enum": ["x", "y", "z"]}},
"required": ["type", "axis"],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"type": {"const": "bbox"},
"expected_mm": {
"type": "object",
"properties": {
"x": {"type": "number", "exclusiveMinimum": 0},
"y": {"type": "number", "exclusiveMinimum": 0},
"z": {"type": "number", "exclusiveMinimum": 0}
},
"required": ["x", "y", "z"],
"additionalProperties": false
}
},
"required": ["type", "expected_mm"],
"additionalProperties": false
}
]
}
}
}
@@ -1,349 +0,0 @@
"""将 output3 的程序化圆柱样本蒸馏为自足的短 CDSL。
这些样本的 SolidWorks 历史把每个切口存成独立草图/切除特征。本脚本按
文件名中的设计族选择一个可复用的 motif + layout 语义描述;不会复制
草图 entities、逐切口坐标、compiler_context 或任何编码后的几何。
"""
from __future__ import annotations
import argparse
import json
import re
from pathlib import Path
from typing import Any
SPECS: dict[str, dict[str, Any]] = {
"cylinder_001": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 16.0, "outer_radius_mm": 25.0, "half_angle_deg": 4.583662},
"layout": {"type": "angular", "count": 12, "start_angle_deg": 15.0, "orientation": "radial"},
},
"cylinder_002": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 8.0, "outer_radius_mm": 12.5, "half_angle_deg": 4.583662},
"layout": {"type": "angular", "count": 12, "start_angle_deg": 15.0, "orientation": "radial"},
},
"cylinder_003": {
"motif": {"type": "circle", "radius_mm": 2.4},
"layout": {"type": "ring", "radius_mm": 34.0, "count": 32},
},
"cylinder_004": {
"motif": {"type": "square", "width_mm": 3.3},
"layout": {"type": "ring", "radius_mm": 32.0, "count": 24},
},
"cylinder_005": {
"motif": {"type": "square", "width_mm": 4.242641},
"layout": {"type": "ring", "radius_mm": 32.0, "count": 24},
},
"cylinder_006": {
"motif": {"type": "circle", "radius_mm": 2.2},
"layout": {
"type": "disc_grid", "count_x": 7, "count_y": 7,
"spacing_x_mm": 10.0, "spacing_y_mm": 10.0,
"center_mm": [0.0, 0.0], "max_center_radius_mm": 36.1,
},
},
"cylinder_007": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 12.0, "outer_radius_mm": 20.0, "half_angle_deg": 4.583662},
"layout": {
"type": "concentric_rings", "orientation": "radial",
"rings": [
{"type": "angular", "count": 12, "start_angle_deg": 15.0},
{"type": "angular", "count": 24, "start_angle_deg": 7.5},
],
},
"ring_motifs": [
{"inner_radius_mm": 12.0, "outer_radius_mm": 20.0, "half_angle_deg": 4.583662, "count": 12, "start_angle_deg": 15.0},
{"inner_radius_mm": 28.0, "outer_radius_mm": 40.0, "half_angle_deg": 2.864789, "count": 24, "start_angle_deg": 7.5},
],
},
"cylinder_008": {
"motif": {"type": "obround", "length_mm": 7.0, "width_mm": 2.8},
"layout": {"type": "ring", "radius_mm": 34.0, "count": 24, "orientation": "radial"},
},
"cylinder_009": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 10.0, "outer_radius_mm": 42.0, "half_angle_deg": 3.437747},
"layout": {"type": "angular", "count": 12, "start_angle_deg": 15.0, "orientation": "radial"},
},
"cylinder_010": {
"motif": {"type": "circle", "radius_mm": 2.3},
"layout": {
"type": "concentric_rings",
"rings": [
{"radius_mm": 18.0, "count": 12, "start_angle_deg": 0.0},
{"radius_mm": 36.0, "count": 24, "start_angle_deg": 0.0},
],
},
},
"cylinder_011": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 10.0, "outer_radius_mm": 20.0, "half_angle_deg": 4.010705},
"layout": {"type": "angular", "count": 8, "start_angle_deg": 22.5, "orientation": "radial"},
},
"cylinder_012": {
"motif": {"type": "circle", "radius_mm": 1.9},
"layout": {"type": "ring", "radius_mm": 26.0, "count": 24},
},
"cylinder_013": {
"motif": {"type": "square", "width_mm": 2.7},
"layout": {"type": "ring", "radius_mm": 25.0, "count": 16},
},
"cylinder_014": {
"motif": {"type": "square", "width_mm": 3.535534},
"layout": {"type": "ring", "radius_mm": 25.0, "count": 16},
},
"cylinder_015": {
"motif": {"type": "circle", "radius_mm": 1.8},
"layout": {
"type": "disc_grid", "count_x": 6, "count_y": 5,
"spacing_x_mm": 9.0, "spacing_y_mm": 9.0,
"center_mm": [0.0, 0.0],
},
},
"cylinder_016": {
"motif": {"type": "d_shape_polygon", "stem_length_mm": 1.9, "nose_depth_mm": 1.9, "half_height_mm": 1.3775},
"layout": {
"type": "open_arc", "radius_mm": 35.0, "count": 18,
"start_angle_deg": 18.0, "end_angle_deg": 262.8,
},
},
"cylinder_017": {
"motif": {"type": "cross", "size_mm": 4.62, "arm_width_mm": 2.9568},
"layout": {
"type": "open_arc", "radius_mm": 31.0, "count": 14,
"start_angle_deg": 189.0, "end_angle_deg": 387.0,
},
},
"cylinder_018": {
"motif": {"type": "obround", "length_mm": 5.75, "width_mm": 2.3},
"layout": {"type": "ring", "radius_mm": 27.0, "count": 16, "orientation": "radial"},
},
"cylinder_019": {
"motif": {"type": "circle", "radius_mm": 1.8},
"layout": {
"type": "concentric_rings",
"rings": [
{"radius_mm": 14.0, "count": 8, "start_angle_deg": 0.0},
{"radius_mm": 30.0, "count": 16, "start_angle_deg": 0.0},
],
},
},
"cylinder_020": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 9.0, "outer_radius_mm": 36.0, "half_angle_deg": 3.151268},
"layout": {"type": "angular", "count": 10, "start_angle_deg": 18.0, "orientation": "radial"},
},
"cylinder_021": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 12.0, "outer_radius_mm": 20.0, "half_angle_deg": 4.010705},
"layout": {"type": "angular", "count": 8, "start_angle_deg": 22.5, "orientation": "radial"},
"ring_motifs": [
{"inner_radius_mm": 12.0, "outer_radius_mm": 20.0, "half_angle_deg": 4.010705, "count": 8, "start_angle_deg": 22.5},
{"inner_radius_mm": 28.0, "outer_radius_mm": 39.0, "half_angle_deg": 2.57831, "count": 16, "start_angle_deg": 11.25},
],
},
"cylinder_022": {
"motif": {"type": "skew_hexagon", "nominal_radius_mm": 1.875},
"layout": {
"type": "spiral", "count": 28, "start_radius_mm": 8.054,
"radius_step_mm": 0.851, "start_angle_deg": -0.105, "angle_step_deg": 33.2394,
"orientation": "snapped_radial", "orientation_snap_deg": 45.0,
"orientation_offset_deg": 0.0,
},
},
"cylinder_023": {
"motif": {"type": "skew_hexagon", "nominal_radius_mm": 1.9},
"layout": {
"type": "cross_lines", "count_per_axis": 6, "spacing_mm": 13.6,
"orientation_offset_deg": 0.0,
},
},
"cylinder_024": {
"motif": {"type": "triangle", "radius_mm": 2.3},
"layout": {
"type": "x_field", "levels": 5, "spacing_mm": 11.0,
"orientation": "diagonal_axes",
},
},
"cylinder_025": {
"motif": {
"type": "teardrop_polygon",
"left_width_mm": 1.116, "right_width_mm": 1.548,
"tip_height_mm": 2.79, "bottom_depth_mm": 1.476,
"shoulder_height_mm": 1.242,
},
"layout": {
"type": "twin_strips", "x_offset_mm": 15.0, "count_y": 12,
"y_start_mm": -34.0, "y_end_mm": 34.0,
},
},
"cylinder_026": {
"motif": {"type": "trapezoid", "bottom_width_mm": 4.32, "top_width_mm": 2.376, "height_mm": 3.24},
"layout": {
"type": "concentric_rings", "orientation": "radial",
"rings": [
{"radius_mm": 12.0, "count": 8, "start_angle_deg": 0.0},
{"radius_mm": 31.0, "count": 16, "start_angle_deg": 0.0},
],
},
},
"cylinder_027": {
"motif": {"type": "d_shape_polygon", "stem_length_mm": 1.8, "nose_depth_mm": 1.8, "half_height_mm": 1.305},
"layout": {"type": "corner_clusters", "levels_mm": [12.0, 18.5, 25.0]},
},
"cylinder_028": {
"motif": {"type": "square", "width_mm": 2.969848},
"layout": {"type": "diamond_field", "manhattan_radius": 3, "spacing_mm": 8.0},
},
"cylinder_029": {
"motif": {"type": "annular_sector_polygon", "inner_radius_mm": 11.0, "outer_radius_mm": 22.0, "half_angle_deg": 4.010705},
"layout": {"type": "angular", "count": 10, "start_angle_deg": 18.0, "orientation": "radial"},
},
}
def _pattern_sketches(spec: dict[str, Any]) -> list[dict[str, Any]]:
base = {
"id": "sketch_001",
"name": "草图1",
"workplane": {
"origin_mm": [0.0, 0.0, 0.0],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 1.0, 0.0],
"normal": [0.0, 0.0, 1.0],
},
"profile": {"type": "circle", "center": [0.0, 0.0], "radius_mm": 50.0},
}
cut_workplane = {
"origin_mm": [0.0, 0.0, 20.0],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 1.0, 0.0],
"normal": [0.0, 0.0, 1.0],
}
ring_motifs = spec.get("ring_motifs")
if not ring_motifs:
cut = {
"id": "sketch_002",
"name": "图案草图",
"workplane": cut_workplane,
"profile": {
"type": "patterned_cutouts",
"motif": spec["motif"],
"layout": spec["layout"],
},
}
return [base, cut]
# 两组扇区的内外半径不同,仍合并成一个切除特征;组合只包含
# 两个有名字的程序化子图案,不保存任何逐实例坐标。
cut = {
"id": "sketch_002",
"name": "双环图案草图",
"workplane": cut_workplane,
"profile": {
"type": "compound_patterned_cutouts",
"patterns": [
{
"motif": {
"type": "annular_sector_polygon",
"inner_radius_mm": item["inner_radius_mm"],
"outer_radius_mm": item["outer_radius_mm"],
"half_angle_deg": item["half_angle_deg"],
},
"layout": {
"type": "angular",
"count": item["count"],
"start_angle_deg": item["start_angle_deg"],
"orientation": "radial",
},
}
for item in ring_motifs
],
},
}
return [base, cut]
def make_cdsl(source: Path, spec: dict[str, Any]) -> dict[str, Any]:
part_id = source.name.removesuffix(".solidworks_evidence_v2.json")
return {
"schema": "cad.cdsl.llm.v1",
"schema_version": "1.0.0",
"kind": "part",
"part_id": part_id,
"features": [
{
"id": "f01",
"atomic_id": "extrude_add_blind",
"depends_on": [],
"name": "凸台-拉伸1",
"params": {"distance_mm": 20.0},
"sketch_id": "sketch_001",
},
{
"id": "f02",
"atomic_id": "extrude_cut_blind",
"depends_on": ["f01"],
"name": "程序化图案切除",
"params": {"distance_mm": 8.0, "reverse": True},
"sketch_id": "sketch_002",
},
],
"geometry": {"sketches": _pattern_sketches(spec)},
"meta": {
"source": source.name,
"parameterized_sketches": 2,
"profile_from_sketches": 0,
"notes": [
"CDSL-only drawable: repeated source sketches are represented by one procedural motif and layout",
"No compiler_context, source entities, per-instance coordinates, or encoded geometry",
],
},
}
def distill(input_dir: Path, output_dir: Path) -> list[Path]:
sources = sorted(input_dir.glob("cylinder_*.solidworks_evidence_v2.json"))
written: list[Path] = []
missing: list[str] = []
for source in sources:
key = source.name[:12]
spec = SPECS.get(key)
if spec is None:
missing.append(source.name)
continue
part_id = source.name.removesuffix(".solidworks_evidence_v2.json")
part_dir = output_dir / part_id
part_dir.mkdir(parents=True, exist_ok=True)
out = part_dir / f"{part_id}.cdsl.json"
text = json.dumps(make_cdsl(source, spec), ensure_ascii=False, indent=2)
# 与 b005/b006 一致:短标量数组保持单行,结构数组仍按层级展开。
scalar_array = re.compile(
r"\[\n(?P<body>(?:[ \t]+(?:-?\d+(?:\.\d+)?|true|false|null|\"[^\"\\n]*\"),?\n)+)[ \t]*\]"
)
def compact(match: re.Match[str]) -> str:
values = [line.strip().rstrip(",") for line in match.group("body").splitlines()]
inline = "[" + ", ".join(values) + "]"
return inline if len(inline) <= 100 else match.group(0)
text = scalar_array.sub(compact, text)
out.write_text(
text + "\n",
encoding="utf-8",
)
written.append(out)
if missing:
raise RuntimeError("No semantic specification for: " + ", ".join(missing))
return written
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input_dir", type=Path)
parser.add_argument("--out", type=Path, required=True)
args = parser.parse_args()
paths = distill(args.input_dir, args.out)
print(f"wrote {len(paths)} CDSL files to {args.out}")
if __name__ == "__main__":
main()
@@ -1,212 +0,0 @@
"""Deterministic GenerationSpec -> CDSL compilers.
The LLM supplies semantic parameters and feature intent. This module owns
the CDSL details so tool calls do not need to contain sketch IDs, selectors, or
runtime-specific parameter wrappers.
"""
from __future__ import annotations
import copy
from dataclasses import dataclass
from typing import Any, Protocol
from .generation_spec import GenerationSpecError, validate_generation_spec
def _workplane(z: float = 0.0, normal: list[float] | None = None) -> dict[str, list[float]]:
return {
"origin_mm": [0.0, 0.0, float(z)],
"x_dir": [1.0, 0.0, 0.0],
"y_dir": [0.0, 1.0, 0.0],
"normal": list(normal or [0.0, 0.0, 1.0]),
}
def _value(spec: dict[str, Any], name: str, default: float | int | str) -> Any:
parameter = (spec.get("parameters") or {}).get(name)
if isinstance(parameter, dict) and "value" in parameter:
return parameter["value"]
return default
def _number(spec: dict[str, Any], name: str, default: float) -> float:
value = _value(spec, name, default)
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise GenerationSpecError(f"Parameter {name} must be numeric", f"$.parameters.{name}.value")
number = float(value)
if not number == number or number in {float("inf"), float("-inf")}:
raise GenerationSpecError(f"Parameter {name} must be finite", f"$.parameters.{name}.value")
return number
def _integer(spec: dict[str, Any], name: str, default: int) -> int:
value = _number(spec, name, default)
if int(value) != value:
raise GenerationSpecError(f"Parameter {name} must be an integer", f"$.parameters.{name}.value")
return int(value)
def _positive(number: float, name: str) -> float:
if number <= 0:
raise GenerationSpecError(f"Parameter {name} must be greater than zero", f"$.parameters.{name}.value")
return number
def _acceptance(spec: dict[str, Any], generated: list[dict[str, Any]]) -> list[dict[str, Any]]:
existing = copy.deepcopy(spec.get("acceptance") or [])
known = {str(item.get("id")) for item in existing}
for item in generated:
if item["id"] not in known:
existing.append(item)
known.add(item["id"])
return existing
@dataclass(frozen=True)
class CompiledGeneration:
cdsl: dict[str, Any]
acceptance: list[dict[str, Any]]
provenance: dict[str, Any]
assumptions: list[str]
approximations: list[dict[str, Any]]
class Compiler(Protocol):
family: str
def compile(self, spec: dict[str, Any], references: list[dict[str, Any]]) -> CompiledGeneration:
...
class MountingPlateCompiler:
family = "mounting_plate"
def compile(self, spec: dict[str, Any], references: list[dict[str, Any]]) -> CompiledGeneration:
width = _positive(_number(spec, "width", 100.0), "width")
height = _positive(_number(spec, "height", 60.0), "height")
thickness = _positive(_number(spec, "thickness", 6.0), "thickness")
hole_diameter = _positive(_number(spec, "hole_diameter", 4.5), "hole_diameter")
hole_offset = _positive(_number(spec, "hole_edge_offset", 10.0), "hole_edge_offset")
hole_count = _integer(spec, "hole_count", 4)
counterbore_diameter = _positive(_number(spec, "counterbore_diameter", hole_diameter * 2), "counterbore_diameter")
counterbore_depth = _positive(_number(spec, "counterbore_depth", min(thickness / 2, 3.0)), "counterbore_depth")
slot_length = _positive(_number(spec, "slot_length", 20.0), "slot_length")
slot_width = _positive(_number(spec, "slot_width", 12.0), "slot_width")
if hole_count != 4:
raise GenerationSpecError("mounting_plate compiler currently requires four corner holes", "$.parameters.hole_count.value")
if counterbore_diameter <= hole_diameter:
raise GenerationSpecError("counterbore_diameter must exceed hole_diameter", "$.parameters.counterbore_diameter.value")
if counterbore_depth > thickness:
raise GenerationSpecError("counterbore_depth must not exceed thickness", "$.parameters.counterbore_depth.value")
if hole_offset * 2 >= min(width, height):
raise GenerationSpecError("hole_edge_offset leaves no usable plate area", "$.parameters.hole_edge_offset.value")
if slot_length < slot_width:
raise GenerationSpecError("slot_length must be at least slot_width", "$.parameters.slot_length.value")
centers = [
[-width / 2 + hole_offset, -height / 2 + hole_offset],
[width / 2 - hole_offset, -height / 2 + hole_offset],
[width / 2 - hole_offset, height / 2 - hole_offset],
[-width / 2 + hole_offset, height / 2 - hole_offset],
]
frame = _workplane(thickness)
cdsl = {
"schema": "cad.cdsl.llm.v1",
"schema_version": "1.1.0",
"kind": "part",
"part_id": "generation-spec-preflight",
"meta": {"unit": "mm", "name": str(spec["part"]["name"])},
"geometry": {"sketches": [
{"id": "base_plate", "workplane": _workplane(), "profile": {
"type": "rectangle", "center": [0.0, 0.0], "width_mm": width, "height_mm": height,
}},
{"id": "mounting_holes", "workplane": frame, "profile": {
"type": "circles", "items": [{"center": center, "radius_mm": hole_diameter / 2} for center in centers],
}},
{"id": "center_slot", "workplane": frame, "profile": {
"type": "obround", "center": [0.0, 0.0], "length_mm": slot_length, "width_mm": slot_width,
}},
]},
"features": [
{"id": "base_plate", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "base_plate", "params": {"distance_mm": thickness}},
{"id": "mounting_holes", "atomic_id": "extrude_cut_blind", "depends_on": ["base_plate"], "sketch_id": "mounting_holes", "params": {"distance_mm": thickness, "reverse": True}},
{"id": "mounting_counterbores", "atomic_id": "hole_counterbore", "depends_on": ["mounting_holes"], "sketch_id": "mounting_holes", "params": {
"diameter_mm": hole_diameter, "depth_mm": thickness, "counterbore_diameter_mm": counterbore_diameter,
"counterbore_depth_mm": counterbore_depth, "positions": [{"mm": [x, y, 0.0]} for x, y in centers], "host_face": {"frame": frame},
}},
{"id": "center_slot", "atomic_id": "extrude_cut_blind", "depends_on": ["mounting_counterbores"], "sketch_id": "center_slot", "params": {"distance_mm": thickness, "reverse": True}},
],
}
assumptions = [str(item) for item in spec.get("assumptions") or []]
if "hole_diameter" not in (spec.get("parameters") or {}):
assumptions.append("M4 clearance hole defaults to 4.5 mm")
if "counterbore_depth" not in (spec.get("parameters") or {}):
assumptions.append("Counterbore depth defaults to the lesser of 3 mm and half the plate thickness")
acceptance = _acceptance(spec, [
{"id": "overall_bbox", "type": "bbox", "expected": [width, height, thickness], "tolerance": 0.1, "severity": "blocking"},
{"id": "mounting_hole_count", "type": "feature_count", "feature": "mounting_holes", "expected": 4, "tolerance": 0, "severity": "blocking"},
{"id": "mounting_hole_diameter", "type": "hole_diameter", "feature": "mounting_holes", "expected": hole_diameter, "tolerance": 0.05, "severity": "blocking"},
{"id": "center_slot_size", "type": "overall_length", "feature": "center_slot", "expected": slot_length, "tolerance": 0.1, "severity": "blocking"},
])
provenance = {
"compiler": self.family,
"compiler_version": "1.0",
"references": references,
"feature_sources": {item["id"]: item["id"] for item in spec.get("features") or []},
"parameter_paths": {name: f"parameters.{name}.value" for name in (spec.get("parameters") or {})},
}
return CompiledGeneration(cdsl, acceptance, provenance, list(dict.fromkeys(assumptions)), list(spec.get("approximations") or []))
class FlangeSleeveCompiler:
family = "flange_sleeve"
def compile(self, spec: dict[str, Any], references: list[dict[str, Any]]) -> CompiledGeneration:
from app.services.flange_sleeve_template import build_flange_sleeve_cdsl, TEMPLATE_ID
aliases = {
"width": "flange_width_mm", "height": "flange_height_mm", "thickness": "flange_thickness_mm",
"tube_outer_diameter": "tube_outer_diameter_mm", "bore_diameter": "bore_diameter_mm",
"hole_diameter": "mount_hole_diameter_mm", "counterbore_diameter": "mount_counterbore_diameter_mm",
"counterbore_depth": "mount_counterbore_depth_mm", "hole_offset_x": "mount_hole_u_mm", "hole_offset_y": "mount_hole_v_mm",
}
plan: dict[str, Any] = {"template": TEMPLATE_ID, "name": spec["part"]["name"]}
for source, target in aliases.items():
if source in (spec.get("parameters") or {}):
plan[target] = _value(spec, source, 0)
cdsl, values = build_flange_sleeve_cdsl(plan)
acceptance = _acceptance(spec, [
{"id": "flange_bbox", "type": "bbox", "expected": [float(values["flange_width_mm"]), float(values["flange_height_mm"]), float(values["flange_thickness_mm"])], "tolerance": 0.1, "severity": "blocking"},
])
provenance = {"compiler": self.family, "compiler_version": "1.0", "template_id": TEMPLATE_ID, "references": references}
assumptions = list(dict.fromkeys([*map(str, spec.get("assumptions") or []), "Flange sleeve dimensions use the verified parameterized template defaults for omitted fields."]))
return CompiledGeneration(cdsl, acceptance, provenance, assumptions, list(spec.get("approximations") or []))
COMPILERS: dict[str, Compiler] = {
"mounting_plate": MountingPlateCompiler(),
"flange_sleeve": FlangeSleeveCompiler(),
}
def compiler_for(family: str) -> Compiler:
try:
return COMPILERS[family]
except KeyError as error:
raise GenerationSpecError(f"No GenerationSpec compiler registered for part family: {family}", "$.part.family") from error
def compile_generation_spec(spec: dict[str, Any], references: list[dict[str, Any]] | None = None) -> dict[str, Any]:
normalized = validate_generation_spec(spec)
compiler = compiler_for(str(normalized["part"]["family"]))
compiled = compiler.compile(normalized, references or [])
return {
"spec": normalized,
"cdsl": compiled.cdsl,
"acceptance": compiled.acceptance,
"provenance": compiled.provenance,
"assumptions": compiled.assumptions,
"approximations": compiled.approximations,
}
@@ -1,177 +0,0 @@
"""Validation and normalization for the semantic GenerationSpec contract."""
from __future__ import annotations
import copy
import json
from functools import lru_cache
from pathlib import Path
from typing import Any
from jsonschema import Draft202012Validator
KNOWN_PART_FAMILIES = frozenset({
"mounting_plate",
"flange",
"flange_sleeve",
"simple_shaft",
"bearing_housing",
"mounting_bracket",
"hex_nut",
"slotted_plate",
})
KNOWN_FEATURE_KINDS = frozenset({
"base_extrusion",
"base_revolve",
"boss",
"through_hole",
"blind_hole",
"counterbored_hole",
"countersunk_hole",
"hole_pattern",
"counterbored_hole_pattern",
"obround_cut",
"pocket",
"revolve_profile",
"coaxial_bore",
"fillet",
"chamfer",
})
class GenerationSpecError(ValueError):
"""A stable, user-repairable GenerationSpec validation error."""
def __init__(self, message: str, path: str = "$") -> None:
super().__init__(message)
self.path = path
@lru_cache(maxsize=1)
def generation_spec_schema() -> dict[str, Any]:
path = Path(__file__).with_name("generation_spec_schema.json")
schema = json.loads(path.read_text(encoding="utf-8"))
Draft202012Validator.check_schema(schema)
return schema
def _schema_error(spec: dict[str, Any]) -> GenerationSpecError | None:
errors = sorted(
Draft202012Validator(generation_spec_schema()).iter_errors(spec),
key=lambda error: (list(error.absolute_path), error.message),
)
if not errors:
return None
error = errors[0]
location = "$" + "".join(
f"[{item}]" if isinstance(item, int) else f".{item}"
for item in error.absolute_path
)
return GenerationSpecError(error.message, location)
def _parameter_value(spec: dict[str, Any], name: str) -> Any:
value = (spec.get("parameters") or {}).get(name)
if not isinstance(value, dict):
raise GenerationSpecError(f"Unknown parameter: {name}", f"$.parameters.{name}")
return value.get("value")
def _numeric(value: Any, path: str) -> float:
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise GenerationSpecError("Expected a finite numeric value", path)
number = float(value)
if number != number or number in {float("inf"), float("-inf")}:
raise GenerationSpecError("Expected a finite numeric value", path)
return number
def _check_constraints(spec: dict[str, Any]) -> None:
for index, constraint in enumerate(spec.get("constraints") or []):
path = f"$.constraints[{index}]"
kind = constraint.get("type")
if kind in {"less_than", "less_equal", "greater_than", "greater_equal", "equal"}:
left = _numeric(_parameter_value(spec, str(constraint.get("left") or "")), f"{path}.left")
right_name = constraint.get("right")
right = _numeric(_parameter_value(spec, str(right_name)), f"{path}.right") if right_name else _numeric(constraint.get("value"), f"{path}.value")
ok = {
"less_than": left < right,
"less_equal": left <= right,
"greater_than": left > right,
"greater_equal": left >= right,
"equal": abs(left - right) <= 1e-9,
}[kind]
if not ok:
raise GenerationSpecError(constraint.get("message") or f"Constraint {kind} failed", path)
def _check_graph(spec: dict[str, Any]) -> None:
features = spec.get("features") or []
ids = [str(item.get("id")) for item in features]
if len(ids) != len(set(ids)):
raise GenerationSpecError("Feature ids must be unique", "$.features")
known: set[str] = set()
for index, feature in enumerate(features):
feature_id = str(feature.get("id"))
kind = str(feature.get("kind"))
if kind not in KNOWN_FEATURE_KINDS:
raise GenerationSpecError(f"Unsupported feature kind: {kind}", f"$.features[{index}].kind")
for dependency in feature.get("depends_on") or []:
if dependency not in known:
raise GenerationSpecError(
f"Feature {feature_id} has a forward or missing dependency: {dependency}",
f"$.features[{index}].depends_on",
)
known.add(feature_id)
acceptance_ids = {str(item.get("id")) for item in spec.get("acceptance") or []}
if len(acceptance_ids) != len(spec.get("acceptance") or []):
raise GenerationSpecError("Acceptance ids must be unique", "$.acceptance")
for index, item in enumerate(spec.get("acceptance") or []):
feature = item.get("feature")
if feature and feature not in known:
raise GenerationSpecError(f"Acceptance refers to missing feature: {feature}", f"$.acceptance[{index}].feature")
def normalize_generation_spec(spec: dict[str, Any], *, request: str = "") -> dict[str, Any]:
if not isinstance(spec, dict):
raise GenerationSpecError("GenerationSpec must be a JSON object")
normalized = copy.deepcopy(spec)
normalized.setdefault("schema", "cad.generation-spec.v1")
normalized.setdefault("schema_version", "1.0")
normalized.setdefault("mode", "create")
normalized.setdefault("base_revision_id", "")
normalized.setdefault("parameters", {})
normalized.setdefault("features", [])
normalized.setdefault("constraints", [])
normalized.setdefault("acceptance", [])
normalized.setdefault("assumptions", [])
normalized.setdefault("approximations", [])
normalized.setdefault("references", [])
normalized.setdefault("patch_intent", request)
return normalized
def validate_generation_spec(
spec: dict[str, Any],
*,
known_families: set[str] | frozenset[str] = KNOWN_PART_FAMILIES,
) -> dict[str, Any]:
normalized = normalize_generation_spec(spec)
error = _schema_error(normalized)
if error:
raise error
family = str(normalized["part"]["family"])
if family not in known_families:
raise GenerationSpecError(f"Unsupported part family: {family}", "$.part.family")
for name, parameter in normalized["parameters"].items():
source = parameter["source"]
if source == "user" and not parameter["locked"]:
raise GenerationSpecError("User parameters must be locked", f"$.parameters.{name}.locked")
if source == "image_estimate" and not parameter.get("assumption"):
raise GenerationSpecError("Image estimates require an assumption", f"$.parameters.{name}.assumption")
_check_graph(normalized)
_check_constraints(normalized)
return normalized
@@ -1,100 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://cdsl.local/schema/cad.generation-spec.v1",
"title": "CDSL GenerationSpec",
"type": "object",
"additionalProperties": false,
"properties": {
"schema": {"const": "cad.generation-spec.v1"},
"schema_version": {"const": "1.0"},
"mode": {"enum": ["create", "revise"]},
"base_revision_id": {"type": "string"},
"patch_intent": {"type": "string"},
"part": {"$ref": "#/$defs/part"},
"parameters": {"type": "object", "additionalProperties": {"$ref": "#/$defs/parameter"}},
"features": {"type": "array", "items": {"$ref": "#/$defs/feature"}},
"constraints": {"type": "array", "items": {"$ref": "#/$defs/constraint"}},
"acceptance": {"type": "array", "items": {"$ref": "#/$defs/acceptance"}},
"assumptions": {"type": "array", "items": {"type": "string", "minLength": 1}},
"approximations": {"type": "array", "items": {"$ref": "#/$defs/approximation"}},
"references": {"type": "array", "items": {"type": "object"}}
},
"required": ["schema", "schema_version", "mode", "base_revision_id", "part", "parameters", "features", "constraints", "acceptance", "assumptions", "approximations", "references"],
"$defs": {
"part": {
"type": "object",
"additionalProperties": false,
"properties": {
"family": {"type": "string", "minLength": 1},
"name": {"type": "string", "minLength": 1},
"units": {"const": "mm"},
"coordinate_system": {"type": "string", "minLength": 1}
},
"required": ["family", "name", "units", "coordinate_system"]
},
"parameter": {
"type": "object",
"additionalProperties": false,
"properties": {
"value": {},
"unit": {"enum": ["mm", "deg", "rad", "count", ""]},
"source": {"enum": ["user", "technical_drawing", "image_estimate", "template_default", "derived_standard", "assumption"]},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"locked": {"type": "boolean"},
"assumption": {"type": "string"}
},
"required": ["value", "unit", "source", "confidence", "locked", "assumption"]
},
"feature": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"kind": {"type": "string", "minLength": 1},
"depends_on": {"type": "array", "items": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"}},
"parameters": {"type": "object"},
"semantic_role": {"type": "string"}
},
"required": ["id", "kind", "depends_on", "parameters"]
},
"constraint": {
"type": "object",
"additionalProperties": false,
"properties": {
"type": {"enum": ["less_than", "less_equal", "greater_than", "greater_equal", "equal", "inside_boundary", "symmetric", "same_axis", "distance", "count"]},
"left": {"type": "string"},
"right": {"type": "string"},
"value": {},
"feature": {"type": "string"},
"margin": {"type": "number"},
"ratio": {"type": "number"},
"message": {"type": "string"}
},
"required": ["type"]
},
"acceptance": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$"},
"type": {"enum": ["bbox", "solid_count", "feature_count", "hole_diameter", "hole_center", "hole_spacing", "wall_thickness", "overall_length", "overall_diameter", "coaxiality", "symmetry", "through_condition", "blind_depth"]},
"feature": {"type": "string"},
"expected": {},
"tolerance": {"type": "number", "minimum": 0},
"severity": {"enum": ["blocking", "warning", "informational"]}
},
"required": ["id", "type", "expected", "tolerance", "severity"]
},
"approximation": {
"type": "object",
"additionalProperties": false,
"properties": {
"request": {"type": "string", "minLength": 1},
"status": {"enum": ["approximated", "expanded", "omitted", "blocked"]},
"translation": {"type": "string", "minLength": 1},
"reason": {"type": "string", "minLength": 1}
},
"required": ["request", "status", "translation", "reason"]
}
}
}
+5 -1
View File
@@ -11,6 +11,7 @@ from pathlib import Path
from typing import Any
from .capabilities import sketch_ids_required_by_contract
from cdsl_importer.legacy_profile_adapter import lower_legacy_profiles
from .semantic_validation import validate_semantic_cdsl
from .sketch_solver import resolve_required_sketches
@@ -21,7 +22,7 @@ P3_ATOMIC_IDS = frozenset({
})
P4_ATOMIC_IDS = P3_ATOMIC_IDS | frozenset({"hole_wizard"})
P6_ATOMIC_IDS = P4_ATOMIC_IDS | frozenset({"pattern_linear", "pattern_mirror"})
P3_PROFILE_TYPES = frozenset({"analytic_contours", "circle", "circles", "annulus"})
P3_PROFILE_TYPES = frozenset({"analytic_contours", "circle", "polygon"})
# Static pool membership asks whether the exported history is an extrude/
# revolve history. Whether a first cut has a preceding active body remains a
# runtime preflight question, not a reason to erase it from the input pool.
@@ -66,6 +67,9 @@ def is_static_phase_ready(cdsl: dict[str, Any], phase: str) -> bool:
allowed = _PHASE_ATOMIC_IDS.get(phase)
if allowed is None:
raise ValueError(f"Unknown CDSL runtime phase {phase!r}")
# Corpus pools retain historical profiles on disk but evaluate them after
# the importer compatibility lowering used by the batch path.
cdsl = lower_legacy_profiles(cdsl)
semantic = validate_semantic_cdsl(cdsl)
if semantic["unresolved"]:
return False
+3 -187
View File
@@ -2,10 +2,10 @@
"schema": "cdsl.engine.schema.v1",
"schema_version": "1.3.0",
"cdsl_json_schema_file": "cdsl_schema.json",
"maintenance_rule": "The semantic CDSL contract is a superset of the current runtime. runtime_supported_atomic_ids and runtime_supported_profiles must stay synchronized with runtime.py EXECUTORS, sketch_solver.py SHAPE_GENERATORS, and the package-level capability tests. Legacy llm_compiler.py and llm_engine.py are not the CDSL-only runtime contract.",
"maintenance_rule": "The CDSL-only runtime contract is limited to direct generic profiles and runtime.py EXECUTORS. Legacy macro profiles are importer compatibility syntax and must be lowered by cdsl_importer.legacy_profile_adapter before generic runtime validation.",
"coordinate_convention": "All profile dimensions use millimetres. Two-dimensional points are [u, v] in the sketch workplane.",
"runtime_supported_atomic_ids": ["extrude_add_blind", "extrude_add_two_sided", "extrude_cut_blind", "revolve_add", "revolve_cut", "hole_blind", "hole_countersink", "hole_counterbore", "sphere_add", "reference_plane", "reference_axis", "hole_wizard", "fillet", "chamfer", "pattern_linear", "pattern_mirror"],
"runtime_supported_profiles": ["circle", "annulus", "circles", "circle_grid", "rectangle", "rectangle_with_circles", "rectangle_with_fillets", "obround", "polygon", "ibone", "rectangle_with_symmetric_notches", "revolve_chamfer", "revolve_chamfer_slanted", "circle_with_arc_notches", "circular_sector_slot", "circle_with_radial_tabs", "filleted_rect_side_slots", "d_shape", "partial_ring", "partial_ring_with_arc_island", "arc_chain", "radial_slot", "patterned_cutouts", "compound_patterned_cutouts", "analytic_contours", "complex_arc_shape", "unknown_shape"],
"runtime_supported_profiles": ["circle", "polygon", "analytic_contours"],
"feature_atomic_ids": {
"extrude_add_blind": {"summary": "Add the closed profile by one signed extrusion distance.", "required_params": ["distance_mm"], "optional_params": ["reverse"], "requires_sketch": true},
"extrude_add_two_sided": {"summary": "Add the closed profile with independently captured forward and reverse terminations.", "required_params": ["distance_mm", "reverse_distance_mm"], "optional_params": ["reverse", "end_condition", "reverse_end_condition"], "requires_sketch": true},
@@ -26,202 +26,18 @@
},
"profiles": {
"circle": {
"agent_allowed": true,
"summary": "A single circular closed profile.",
"required": ["radius_mm"],
"optional": ["center"],
"constraints": ["radius_mm > 0", "center defaults to [0, 0]"]
},
"annulus": {
"agent_allowed": true,
"summary": "A concentric ring.",
"required": ["inner_radius_mm", "outer_radius_mm"],
"optional": ["center"],
"constraints": ["0 < inner_radius_mm < outer_radius_mm"]
},
"circles": {
"agent_allowed": true,
"summary": "A non-empty list of circles.",
"required": ["items"],
"item_schema": {"required": ["radius_mm"], "optional": ["center"]}
},
"circle_grid": {
"agent_allowed": true,
"summary": "A rectangular grid of equal circles.",
"required": ["radius_mm", "count_x", "count_y", "spacing_x_mm", "spacing_y_mm"],
"optional": ["origin_mm", "center_mm"],
"constraints": ["radius_mm > 0", "count_x and count_y are integers >= 1", "use origin_mm or center_mm, not both"]
},
"rectangle": {
"agent_allowed": true,
"summary": "An axis-aligned rectangle.",
"one_of": [["center", "width_mm", "height_mm"], ["min_mm", "max_mm"]],
"constraints": ["width_mm > 0 and height_mm > 0 when using center"]
},
"rectangle_with_circles": {
"agent_allowed": true,
"summary": "A rectangle or filleted rectangle with optional internal circles.",
"required": ["boundary"],
"optional": ["circles"],
"nested": {"boundary": "rectangle schema plus optional type=rectangle|rectangle_with_fillets and fillet_radius_mm"}
},
"rectangle_with_fillets": {
"agent_allowed": true,
"summary": "An axis-aligned rectangle with corner fillets and optional internal circles.",
"one_of": [["center", "width_mm", "height_mm"], ["min_mm", "max_mm"]],
"optional": ["fillet_radius_mm", "circles"],
"constraints": ["fillet_radius_mm defaults to 0"]
},
"obround": {
"agent_allowed": true,
"summary": "A horizontal capsule/slot.",
"required": ["length_mm", "width_mm"],
"optional": ["center"],
"constraints": ["length_mm > 0", "width_mm > 0", "center defaults to [0, 0]"]
},
"polygon": {
"agent_allowed": true,
"summary": "A closed straight-edge polygon.",
"required": ["vertices"],
"constraints": ["vertices contains at least three [u, v] points"]
},
"ibone": {
"agent_allowed": true,
"summary": "I-shaped lug with optional four holes.",
"required": ["body_width_mm", "body_height_mm", "flange_width_mm", "flange_height_mm", "corner_radius_mm"],
"optional": ["hole_radius_mm"]
},
"rectangle_with_symmetric_notches": {
"agent_allowed": true,
"summary": "A rectangular plate with four symmetric side notches.",
"required": ["width_mm", "height_mm", "notch"],
"nested": {"notch": {"required": ["y_start", "y_end", "depth_mm"], "optional": ["inner_radius_mm", "corner_radius_mm"]}}
},
"revolve_chamfer": {
"agent_allowed": true,
"summary": "Five-edge trapezoid profile for a revolve cut.",
"required": ["axis_height_mm", "top_width_mm", "bottom_width_mm"],
"optional": ["wall_inset_mm", "step_inset_mm", "on_axis_side"],
"constraints": ["on_axis_side is left or right"]
},
"revolve_chamfer_slanted": {
"agent_allowed": true,
"summary": "Five-edge slanted profile for a revolve cut.",
"required": ["axis_height_mm", "top_width_mm", "wall_height_mm", "wall_width_mm"],
"optional": ["wall_inset_mm", "on_axis_side"],
"constraints": ["on_axis_side is left or right"]
},
"circle_with_arc_notches": {
"agent_allowed": true,
"summary": "A circular boundary with arc-shaped notches.",
"required": ["outer_radius_mm", "notch_radius_mm"],
"optional": ["notch_angles_deg", "circles"],
"constraints": ["notch_angles_deg defaults to [0, 90, 180, 270]"]
},
"circular_sector_slot": {
"agent_allowed": true,
"summary": "A circular-sector boundary with a central rectangular slot.",
"required": ["arc_radius_mm", "slot_half_width_mm", "chord_half_mm"],
"optional": ["circles"]
},
"circle_with_radial_tabs": {
"agent_allowed": true,
"summary": "A circle with two mirrored radial tabs.",
"required": ["outer_radius_mm"],
"optional": ["tab_u_half_mm", "tab_v_offset_mm", "circles"]
},
"filleted_rect_side_slots": {
"agent_allowed": true,
"summary": "A filleted rectangle with one semicircular slot at each side centre.",
"required": ["half_width_mm", "half_height_mm", "corner_radius_mm"],
"optional": ["slot_radius_mm", "circles"]
},
"d_shape": {
"agent_allowed": true,
"summary": "A D-shaped boundary made from one chord and one arc.",
"required": ["radius_mm"],
"optional": ["chord_sign", "chord_x_mm", "circles"],
"constraints": ["chord_sign is left or right"]
},
"partial_ring": {
"agent_allowed": true,
"summary": "An annular sector.",
"required": ["inner_radius_mm", "outer_radius_mm"],
"optional": ["half_angle_deg", "circles"],
"constraints": ["0 < inner_radius_mm < outer_radius_mm"]
},
"partial_ring_with_arc_island": {
"agent_allowed": true,
"summary": "One or more annular sectors with an arc island.",
"required": ["inner_radius_mm", "outer_radius_mm", "island_radius_mm"],
"optional": ["half_angle_deg", "island_gap_mm", "center_angles_deg", "replicas"],
"nested": {"replicas": {"required": ["center_angle_deg"]}}
},
"arc_chain": {
"agent_allowed": true,
"summary": "A closed chain of two or more arcs, commonly used for revolve sections.",
"required": ["arcs"],
"optional": ["circles"],
"item_schema": {"required": ["radius_mm", "start_angle_deg", "end_angle_deg"], "optional": ["center"]},
"constraints": ["arcs contains at least two endpoint-connected arcs"]
},
"radial_slot": {
"agent_allowed": true,
"summary": "A rounded annular sector slot.",
"required": ["inner_radius_mm", "outer_radius_mm", "start_angle_deg", "end_angle_deg"],
"optional": ["circles"],
"constraints": ["0 < inner_radius_mm < outer_radius_mm"]
},
"patterned_cutouts": {
"agent_allowed": true,
"summary": "One procedural cutout motif repeated by one procedural layout.",
"required": ["motif", "layout"],
"nested": {"motif": "See motif_types", "layout": "See layout_types"}
},
"compound_patterned_cutouts": {
"agent_allowed": true,
"summary": "Multiple procedural motif/layout pairs merged into one cutout sketch.",
"required": ["patterns"],
"nested": {"patterns": "Non-empty list of {motif, layout}; see patterned_cutouts."}
},
"complex_arc_shape": {
"agent_allowed": false,
"summary": "Legacy compiler-context fallback only. Never generate it."
},
"analytic_contours": {
"agent_allowed": false,
"summary": "Exact analytic line, arc and circle contours emitted by the Evidence v2 converter. The runtime resolves them into closed regions without compiler_context; B-spline currently produces an explicit unsupported diagnostic."
},
"unknown_shape": {
"agent_allowed": false,
"summary": "Legacy compiler-context fallback only. Never generate it."
"summary": "Closed direct line, arc and circle contours. B-splines are unsupported."
}
},
"motif_types": {
"circle": ["radius_mm"],
"square": ["width_mm"],
"rectangle": ["width_mm", "height_mm"],
"obround": ["length_mm", "width_mm"],
"cross": ["size_mm", "arm_width_mm"],
"d_shape_polygon": ["stem_length_mm", "nose_depth_mm", "half_height_mm"],
"regular_hexagon": ["radius_mm"],
"skew_hexagon": ["nominal_radius_mm"],
"triangle": ["radius_mm"],
"teardrop_polygon": ["width_mm", "height_mm"],
"trapezoid": ["bottom_width_mm", "top_width_mm", "height_mm"],
"annular_sector_polygon": ["inner_radius_mm", "outer_radius_mm", "half_angle_deg"]
},
"layout_types": {
"ring": ["radius_mm", "count"],
"angular": ["count"],
"concentric_rings": ["rings"],
"disc_grid": ["count_x", "count_y", "spacing_x_mm", "spacing_y_mm"],
"open_arc": ["radius_mm", "count", "start_angle_deg", "end_angle_deg"],
"spiral": ["count", "start_radius_mm", "radius_step_mm", "angle_step_deg"],
"cross_lines": ["count_per_axis", "spacing_mm"],
"x_field": ["levels", "spacing_mm"],
"twin_strips": ["x_offset_mm", "count_y", "y_start_mm", "y_end_mm"],
"corner_clusters": ["levels_mm"],
"diamond_field": ["manhattan_radius", "spacing_mm"]
}
}
+10 -5
View File
@@ -15,25 +15,30 @@ from pathlib import Path
from typing import Any
try:
from .sketch_solver import SHAPE_GENERATORS, resolve_all_sketches
from cdsl_importer.legacy_profile_adapter import lower_legacy_profiles
from .sketch_solver import SHAPE_GENERATORS, resolve_all_sketches
from .llm_compiler import compile_cdsl
from .llm_engine import run_engine_plan
from .translator import generate_build123d_code, normalize_to_ir
except ImportError: # 允许直接 python rebuild.py
from sketch_solver import SHAPE_GENERATORS, resolve_all_sketches
from cdsl_importer.legacy_profile_adapter import lower_legacy_profiles
from sketch_solver import SHAPE_GENERATORS, resolve_all_sketches
from llm_compiler import compile_cdsl
from llm_engine import run_engine_plan
from translator import generate_build123d_code, normalize_to_ir
def run_rebuild(cdsl: dict[str, Any], out_step: Path, ctx_file: Path | None = None, gold_step: Path | None = None,
force_exact: bool = False) -> dict[str, Any]:
def run_rebuild(cdsl: dict[str, Any], out_step: Path, ctx_file: Path | None = None, gold_step: Path | None = None,
force_exact: bool = False) -> dict[str, Any]:
"""主重建入口。
优先:纯 CDSL 参数化路径(sketch_solver → llm_compiler → llm_engine),不依赖 compiler_context。
回退:CDSL + compiler_context 的 translator 路径。
"""
sketches = cdsl.get("geometry", {}).get("sketches", [])
# Compatibility entry point only: new CDSL-only runtime calls do not use
# macro profiles and therefore never invoke this adapter.
cdsl = lower_legacy_profiles(cdsl)
sketches = cdsl.get("geometry", {}).get("sketches", [])
all_drawable = bool(sketches) and all(
_sketch_is_cdsl_drawable(s) for s in sketches
)
+3 -3
View File
@@ -15,7 +15,7 @@ from .runtime_types import (
RuntimeDiagnostic, SelectorResolution, TopologyRecord, TopologyRegistry,
vector_add, vector_cross, vector_dot, vector_scale, vector_subtract, vector_unit,
)
from .sketch_solver import SHAPE_GENERATORS, resolve_required_sketches
from .sketch_solver import CORE_SHAPE_GENERATORS, resolve_required_sketches
ALL_ATOMIC_IDS = frozenset({
@@ -758,7 +758,7 @@ def analyze_cdsl(cdsl: dict[str, Any]):
resolved = resolve_required_sketches(
deepcopy(cdsl), sketch_ids_required_by_contract(cdsl), errors=sketch_errors,
)
analyzer = CapabilityAnalyzer(atomic_ids=EXECUTORS, profile_types=SHAPE_GENERATORS)
analyzer = CapabilityAnalyzer(atomic_ids=EXECUTORS, profile_types=CORE_SHAPE_GENERATORS)
return analyzer.analyze(resolved, sketch_errors=sketch_errors)
@@ -768,7 +768,7 @@ def rebuild_cdsl(cdsl: dict[str, Any], out_step: Path, *, strict: bool = True) -
resolved = resolve_required_sketches(
deepcopy(cdsl), sketch_ids_required_by_contract(cdsl), errors=sketch_errors,
)
analysis = CapabilityAnalyzer(atomic_ids=EXECUTORS, profile_types=SHAPE_GENERATORS).analyze(
analysis = CapabilityAnalyzer(atomic_ids=EXECUTORS, profile_types=CORE_SHAPE_GENERATORS).analyze(
resolved, sketch_errors=sketch_errors,
)
if strict and not analysis.runtime_eligible:
File diff suppressed because it is too large Load Diff