优化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
@@ -15,6 +15,14 @@ MODULE = importlib.util.module_from_spec(SPEC)
assert SPEC.loader
SPEC.loader.exec_module(MODULE)
FEATURE_TREE_SCRIPT = ROOT / "scripts" / "feature_tree.py"
FEATURE_TREE_SPEC = importlib.util.spec_from_file_location(
"feature_tree", FEATURE_TREE_SCRIPT
)
FEATURE_TREE = importlib.util.module_from_spec(FEATURE_TREE_SPEC)
assert FEATURE_TREE_SPEC.loader
FEATURE_TREE_SPEC.loader.exec_module(FEATURE_TREE)
class DesignIRPipelineTests(unittest.TestCase):
def payload(self) -> dict:
@@ -66,6 +74,257 @@ class DesignIRPipelineTests(unittest.TestCase):
MODULE.compile_designir(hybrid, rebuilt)
self.assertTrue(rebuilt.is_file())
def test_feature_tree_is_derived_from_semantic_designir(self) -> None:
migrated = MODULE.migrate_designir_2_to_3(self.payload())
tree = FEATURE_TREE.generate_feature_tree(
migrated,
designir_path="flange.designir.json",
step_path="flange.step",
backend="build123d",
compiled=True,
validated=True,
)
self.assertEqual("solidworks_feature_manager", tree["tree_kind"])
self.assertEqual("1.0", tree["schema_version"])
serialized = json.dumps(tree)
self.assertNotIn('"designir_3"', serialized)
self.assertNotIn('"source_designir_feature"', serialized)
self.assertNotIn('"manufacturing"', serialized)
self.assertEqual(
"authored_semantic",
tree["source"]["history_kind"],
)
self.assertEqual(
"valid",
tree["validation"]["structure"]["status"],
)
nodes = {
node["id"]: node
for node in tree["feature_manager"]["nodes"]
}
self.assertIn("sw:front_plane", nodes)
self.assertIn("sketch:base_flange", nodes)
self.assertIn("feature:base_flange", nodes)
self.assertIn("feature:central_bore", nodes)
self.assertEqual(
"BossExtrude",
nodes["feature:base_flange"]["solidworks_type"],
)
self.assertEqual(
"BossExtrude",
nodes["feature:base_flange"]["definition"]["feature_type"],
)
self.assertTrue(nodes["feature:base_flange"]["dimensions"])
self.assertIn(
"feature:central_bore",
nodes["feature:base_flange"]["references"]["child_features"],
)
self.assertEqual(
"CutExtrude",
nodes["feature:central_bore"]["solidworks_type"],
)
self.assertEqual(
"ThroughAll",
nodes["feature:central_bore"]["definition"]["end_condition"],
)
self.assertEqual(
[],
nodes["feature:central_bore"]["selection_sets"]["selected_edges"],
)
self.assertFalse(nodes["feature:central_bore"]["rebuild"]["suppressed"])
self.assertIn(
"凸台-拉伸",
nodes["feature:base_flange"]["display_name"],
)
self.assertEqual(
["bore_diameter"],
[
parameter["binding_id"]
for parameter in nodes["feature:central_bore"]["parameters"]
],
)
replay_designir = FEATURE_TREE.designir_from_feature_tree(tree)
self.assertEqual("3.0", replay_designir["schema_version"])
self.assertEqual(
"feature_manager_tree_replay",
replay_designir["authoring_mode"],
)
with tempfile.TemporaryDirectory() as temporary:
original_step = Path(temporary) / "original.step"
replay_step = Path(temporary) / "replayed.step"
original = MODULE.compile_designir(migrated, original_step)
result = FEATURE_TREE.compile_feature_tree(tree, replay_step)
self.assertTrue(replay_step.is_file())
self.assertEqual("feature_manager_native", result["replay_kind"])
for key in ("solid_count", "face_count", "edge_count"):
self.assertEqual(original["facts"][key], result["facts"][key])
self.assertEqual(original["facts"]["size_mm"], result["facts"]["size_mm"])
self.assertAlmostEqual(
original["facts"]["volume_mm3"],
result["facts"]["volume_mm3"],
places=7,
)
def test_llm_flange_variant_normalizes_and_compiles(self) -> None:
payload = {
"designir_kind": "independent_parametric_cad",
"reconstruction_mode": "fully_semantic_parametric",
"authoring_mode": "semantic_feature_program",
"semantic_layer": {
"reconstruction_status": "ready",
"coordinate_system": {
"origin": [0, 0, 0],
"x_axis": [1, 0, 0],
"y_axis": [0, 1, 0],
"z_axis": [0, 0, 1],
},
"datums": [
{
"id": "datum_xy",
"plane": {"normal": [0, 0, 1], "offset": 0},
}
],
"parameters": [
{
"name": "flange_outer_diameter",
"value": 120,
"unit": "mm",
"editable": True,
},
{
"name": "flange_thickness",
"value": 16,
"unit": "mm",
"editable": True,
},
{
"name": "bore_diameter",
"value": 40,
"unit": "mm",
"editable": True,
},
{
"name": "bolt_circle_diameter",
"value": 90,
"unit": "mm",
"editable": True,
},
{
"name": "bolt_hole_diameter",
"value": 11,
"unit": "mm",
"editable": True,
},
{
"name": "bolt_count",
"value": 4,
"unit": "count",
"editable": True,
},
],
"expressions": [
{
"name": "flange_outer_radius",
"expression": "flange_outer_diameter / 2",
}
],
"sketches": [],
"constraints": [],
"features": [
{
"id": "feat_flange_body",
"operation": "extrude_circle",
"radius": {
"expression": "flange_outer_diameter / 2"
},
"height": {"parameter": "flange_thickness"},
},
{
"id": "feat_central_bore",
"operation": "through_hole",
"diameter": {"parameter": "bore_diameter"},
"reference": {"feature": "feat_flange_body"},
},
{
"id": "feat_bolt_holes",
"operation": "polar_hole_pattern",
"count": {"parameter": "bolt_count"},
"pitch_diameter": {
"parameter": "bolt_circle_diameter"
},
"hole_diameter": {
"parameter": "bolt_hole_diameter"
},
"reference": {"feature": "feat_flange_body"},
},
],
"patterns": [],
"attachments": [],
"construction_stages": [],
},
"edit_interface": {
"semantic_parameters": [
{"name": "flange_outer_diameter", "label": "法兰外径"},
{"name": "flange_thickness", "label": "法兰厚度"},
{"name": "bore_diameter", "label": "中心孔径"},
{"name": "bolt_circle_diameter", "label": "分布圆直径"},
{"name": "bolt_hole_diameter", "label": "螺栓孔径"},
{"name": "bolt_count", "label": "螺栓孔数量"},
],
"surface_parameter_groups": [],
"modification_levels": ["semantic_feature"],
"preserved_interfaces": [],
},
"validation_contract": {
"source_independence": True,
"geometry_checks": [],
"edit_checks": [],
"invariants": [],
"perturbations": [
{"parameter": "flange_outer_diameter", "delta": 10}
],
"thresholds": {},
},
"backend_hint": "build123d",
}
normalized = MODULE._normalize_tool_authored_designir(payload)
self.assertEqual("3.0", normalized["schema_version"])
self.assertEqual("mm", normalized["units"])
self.assertIsInstance(
normalized["semantic_layer"]["parameters"], dict
)
self.assertEqual(
["flange_outer_diameter", "flange_thickness", "bore_diameter",
"bolt_circle_diameter", "bolt_hole_diameter", "bolt_count"],
normalized["edit_interface"]["semantic_parameters"],
)
compiled = MODULE.validate_designir(payload)
self.assertEqual(
{"parameter": "bolt_hole_diameter"},
compiled["features"][2]["diameter"],
)
self.assertEqual("feat_flange_body", compiled["features"][1]["host"])
with tempfile.TemporaryDirectory() as temporary:
output = Path(temporary) / "flange.step"
result = MODULE.compile_designir(payload, output)
self.assertEqual(1, result["facts"]["solid_count"])
self.assertEqual([120.0, 120.0, 16.0], result["facts"]["size_mm"])
tree = FEATURE_TREE.generate_feature_tree(
normalized,
designir_path="flange.designir.json",
step_path="flange.step",
backend="build123d",
compiled=True,
validated=True,
)
serialized = json.dumps(tree)
self.assertNotIn('"designir_3"', serialized)
self.assertNotIn('"source_designir_feature"', serialized)
replay_step = Path(temporary) / "flange-replayed.step"
replay = FEATURE_TREE.compile_feature_tree(tree, replay_step)
self.assertEqual("feature_manager_native", replay["replay_kind"])
self.assertEqual(result["facts"]["size_mm"], replay["facts"]["size_mm"])
def test_teacher_dependency_is_rejected(self) -> None:
payload = self.payload()
payload["source_step"] = "teacher.step"