优化skill,添加特征树、编辑窗口
This commit is contained in:
@@ -231,7 +231,13 @@ def _validate_compiler_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
name
|
||||
for name in editable
|
||||
if not parameters[name].get("editable")
|
||||
or _parameter_reference_count(features, name) == 0
|
||||
or (
|
||||
_parameter_reference_count(features, name) == 0
|
||||
and _parameter_expression_reference_count(
|
||||
features, payload.get("expressions", {}), name
|
||||
)
|
||||
== 0
|
||||
)
|
||||
)
|
||||
if disconnected:
|
||||
raise DesignIRError(
|
||||
@@ -307,12 +313,15 @@ def migrate_designir_2_to_3(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
def compiler_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Return the stable flat representation consumed by both CAD backends."""
|
||||
version = payload.get("schema_version")
|
||||
if version is None and payload.get("designir_kind") == DESIGNIR_KIND:
|
||||
version = SCHEMA_VERSION
|
||||
if version == LEGACY_SCHEMA_VERSION:
|
||||
return _validate_compiler_payload(copy.deepcopy(payload))
|
||||
if version != SCHEMA_VERSION or payload.get("designir_kind") != DESIGNIR_KIND:
|
||||
raise DesignIRError(
|
||||
"DesignIR must be legacy 2.0 or independent_parametric_cad 3.0"
|
||||
)
|
||||
payload = _normalize_tool_authored_designir(payload)
|
||||
mode = payload.get("reconstruction_mode")
|
||||
if mode == "surface_parametric":
|
||||
raise DesignIRError(
|
||||
@@ -331,8 +340,16 @@ def compiler_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
semantic = payload.get("semantic_layer")
|
||||
if not isinstance(semantic, dict):
|
||||
raise DesignIRError("semantic_layer must be an object")
|
||||
semantic = _normalize_tool_authored_semantic_layer(payload, semantic)
|
||||
edit_interface = payload.get("edit_interface", {})
|
||||
validation = payload.get("validation_contract", {})
|
||||
validation = copy.deepcopy(payload.get("validation_contract", {}))
|
||||
if isinstance(validation, dict):
|
||||
validation["perturbations"] = _normalize_validation_list(
|
||||
validation.get("perturbations", [])
|
||||
)
|
||||
validation["invariants"] = _normalize_validation_list(
|
||||
validation.get("invariants", []), parameter_key="id"
|
||||
)
|
||||
flat = {
|
||||
"schema_version": LEGACY_SCHEMA_VERSION,
|
||||
"designir_kind": LEGACY_DESIGNIR_KIND,
|
||||
@@ -366,6 +383,462 @@ def compiler_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
return _validate_compiler_payload(flat)
|
||||
|
||||
|
||||
def _map_object_to_list(value: Any) -> Any:
|
||||
if isinstance(value, dict):
|
||||
return [
|
||||
{"id": str(key), **item}
|
||||
if isinstance(item, dict) and "id" not in item
|
||||
else item
|
||||
for key, item in value.items()
|
||||
]
|
||||
return value
|
||||
|
||||
|
||||
def _map_named_list_to_object(value: Any) -> Any:
|
||||
if not isinstance(value, list):
|
||||
return value
|
||||
mapped: dict[str, Any] = {}
|
||||
for index, item in enumerate(value):
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
name = item.get("name") or item.get("id") or f"item_{index + 1}"
|
||||
mapped[str(name)] = {
|
||||
key: child
|
||||
for key, child in item.items()
|
||||
if key not in {"name", "id"}
|
||||
}
|
||||
return mapped if mapped else value
|
||||
|
||||
|
||||
def _normalize_expression_object(value: Any) -> Any:
|
||||
if not isinstance(value, list):
|
||||
return value
|
||||
mapped: dict[str, Any] = {}
|
||||
for item in value:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
name = item.get("name") or item.get("id")
|
||||
expression = item.get("expression") or item.get("value")
|
||||
if name and expression is not None:
|
||||
mapped[str(name)] = expression
|
||||
return mapped if mapped else value
|
||||
|
||||
|
||||
def _normalize_semantic_parameter_names(value: Any) -> list[str]:
|
||||
if not isinstance(value, list):
|
||||
return []
|
||||
names = []
|
||||
for item in value:
|
||||
if isinstance(item, str):
|
||||
names.append(item)
|
||||
elif isinstance(item, dict):
|
||||
name = item.get("name") or item.get("id")
|
||||
if isinstance(name, str):
|
||||
names.append(name)
|
||||
return names
|
||||
|
||||
|
||||
def _semantic_parameter_metadata(value: Any) -> dict[str, dict[str, Any]]:
|
||||
if not isinstance(value, list):
|
||||
return {}
|
||||
metadata: dict[str, dict[str, Any]] = {}
|
||||
for item in value:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
name = item.get("name") or item.get("id")
|
||||
if isinstance(name, str):
|
||||
metadata[name] = item
|
||||
return metadata
|
||||
|
||||
|
||||
def _normalize_coordinate_system(value: Any) -> Any:
|
||||
if not isinstance(value, dict):
|
||||
return value
|
||||
axes = value.get("axes")
|
||||
if isinstance(axes, dict):
|
||||
normalized = dict(value)
|
||||
normalized.setdefault("x_axis", axes.get("x"))
|
||||
normalized.setdefault("y_axis", axes.get("y"))
|
||||
normalized.setdefault("z_axis", axes.get("z"))
|
||||
normalized.pop("axes", None)
|
||||
return normalized
|
||||
return value
|
||||
|
||||
|
||||
def _parameter_ref(name: str) -> dict[str, str]:
|
||||
return {"parameter": name}
|
||||
|
||||
|
||||
def _value_or_parameter_ref(parameters: dict[str, Any], name: str, fallback: Any) -> Any:
|
||||
return _parameter_ref(name) if name in parameters else fallback
|
||||
|
||||
|
||||
def _expression_or_value_ref(expressions: dict[str, Any], name: str, fallback: Any) -> Any:
|
||||
return {"expression": name} if name in expressions else fallback
|
||||
|
||||
|
||||
def _circle_profile(sketches: dict[str, Any], sketch_id: Any) -> dict[str, Any] | None:
|
||||
sketch = sketches.get(str(sketch_id))
|
||||
if not isinstance(sketch, dict):
|
||||
return None
|
||||
profile = sketch.get("profile")
|
||||
if isinstance(profile, dict) and profile.get("type") == "circle":
|
||||
return profile
|
||||
return None
|
||||
|
||||
|
||||
def _center3(profile: dict[str, Any], z: Any = 0) -> list[Any]:
|
||||
center = profile.get("center", [0, 0])
|
||||
if not isinstance(center, list):
|
||||
center = [0, 0]
|
||||
return [
|
||||
center[0] if len(center) > 0 else 0,
|
||||
center[1] if len(center) > 1 else 0,
|
||||
z,
|
||||
]
|
||||
|
||||
|
||||
def _diameter_from_profile(
|
||||
parameters: dict[str, Any],
|
||||
expressions: dict[str, Any],
|
||||
profile: dict[str, Any],
|
||||
*,
|
||||
diameter_parameter: str,
|
||||
radius_expression: str,
|
||||
) -> Any:
|
||||
if diameter_parameter in parameters:
|
||||
return _parameter_ref(diameter_parameter)
|
||||
if radius_expression in expressions:
|
||||
return {"expression": radius_expression}
|
||||
radius = profile.get("radius")
|
||||
if isinstance(radius, (int, float)):
|
||||
return float(radius) * 2.0
|
||||
return radius
|
||||
|
||||
|
||||
def _radius_from_profile(
|
||||
parameters: dict[str, Any],
|
||||
expressions: dict[str, Any],
|
||||
profile: dict[str, Any],
|
||||
*,
|
||||
radius_parameter: str,
|
||||
radius_expression: str,
|
||||
) -> Any:
|
||||
if radius_parameter in parameters:
|
||||
return _parameter_ref(radius_parameter)
|
||||
if radius_expression in expressions:
|
||||
return {"expression": radius_expression}
|
||||
return profile.get("radius")
|
||||
|
||||
|
||||
def _distance_xy(center: list[Any]) -> float | None:
|
||||
if len(center) < 2:
|
||||
return None
|
||||
try:
|
||||
return math.sqrt(float(center[0]) ** 2 + float(center[1]) ** 2)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _normalize_common_llm_feature_program(
|
||||
semantic: dict[str, Any],
|
||||
) -> list[dict[str, Any]] | None:
|
||||
raw_features = semantic.get("features")
|
||||
if not isinstance(raw_features, dict):
|
||||
return None
|
||||
sketches = semantic.get("sketches", {})
|
||||
if not isinstance(sketches, dict):
|
||||
return None
|
||||
patterns = semantic.get("patterns", {})
|
||||
if not isinstance(patterns, dict):
|
||||
patterns = {}
|
||||
parameters = semantic.get("parameters", {})
|
||||
if not isinstance(parameters, dict):
|
||||
parameters = {}
|
||||
expressions = semantic.get("expressions", {})
|
||||
if not isinstance(expressions, dict):
|
||||
expressions = {}
|
||||
|
||||
generated_bodies: list[dict[str, Any]] = []
|
||||
generated_cuts: list[dict[str, Any]] = []
|
||||
generated_patterns: list[dict[str, Any]] = []
|
||||
converted_pattern_sources: set[str] = set()
|
||||
host_feature_id: str | None = None
|
||||
|
||||
for pattern_id, pattern in patterns.items():
|
||||
if not isinstance(pattern, dict) or pattern.get("type") not in {
|
||||
"circular",
|
||||
"polar",
|
||||
}:
|
||||
continue
|
||||
source_feature_id = str(pattern.get("feature") or "")
|
||||
source_feature = raw_features.get(source_feature_id)
|
||||
if not isinstance(source_feature, dict):
|
||||
continue
|
||||
profile = _circle_profile(sketches, source_feature.get("sketch"))
|
||||
if profile is None:
|
||||
continue
|
||||
center = profile.get("center", [0, 0])
|
||||
pitch = _value_or_parameter_ref(
|
||||
parameters,
|
||||
"bolt_circle_diameter",
|
||||
None,
|
||||
)
|
||||
if pitch is None:
|
||||
radius = _distance_xy(center if isinstance(center, list) else [])
|
||||
pitch = radius * 2.0 if radius is not None else pattern.get("pitch_diameter")
|
||||
generated_patterns.append(
|
||||
{
|
||||
"id": str(pattern_id),
|
||||
"operation": "polar_hole_pattern",
|
||||
"count": _value_or_parameter_ref(
|
||||
parameters,
|
||||
"bolt_count",
|
||||
pattern.get("count", len(pattern.get("instances", [])) or 1),
|
||||
),
|
||||
"diameter": _diameter_from_profile(
|
||||
parameters,
|
||||
expressions,
|
||||
profile,
|
||||
diameter_parameter="bolt_hole_diameter",
|
||||
radius_expression="bolt_hole_radius",
|
||||
),
|
||||
"pitch_diameter": pitch,
|
||||
"axis": "primary_axis",
|
||||
"host": host_feature_id or "flange_body",
|
||||
}
|
||||
)
|
||||
converted_pattern_sources.add(source_feature_id)
|
||||
|
||||
for feature_id, feature in raw_features.items():
|
||||
if not isinstance(feature, dict) or feature_id in converted_pattern_sources:
|
||||
continue
|
||||
profile = _circle_profile(sketches, feature.get("sketch"))
|
||||
if profile is None:
|
||||
continue
|
||||
feature_type = str(feature.get("type") or "").lower()
|
||||
operation = str(feature.get("operation") or "").lower()
|
||||
if feature_type == "extrude" or operation == "new_body":
|
||||
generated_feature = {
|
||||
"id": str(feature_id),
|
||||
"operation": "extrude_circle",
|
||||
"radius": _radius_from_profile(
|
||||
parameters,
|
||||
expressions,
|
||||
profile,
|
||||
radius_parameter="flange_outer_radius",
|
||||
radius_expression="flange_outer_radius",
|
||||
),
|
||||
"height": _value_or_parameter_ref(
|
||||
parameters,
|
||||
"flange_thickness",
|
||||
feature.get("depth", feature.get("height")),
|
||||
),
|
||||
"axis": "primary_axis",
|
||||
"center": _center3(profile),
|
||||
}
|
||||
generated_bodies.append(generated_feature)
|
||||
host_feature_id = str(feature_id)
|
||||
elif feature_type == "cut_extrude" or operation == "cut":
|
||||
generated_cuts.append(
|
||||
{
|
||||
"id": str(feature_id),
|
||||
"operation": "through_hole",
|
||||
"diameter": _diameter_from_profile(
|
||||
parameters,
|
||||
expressions,
|
||||
profile,
|
||||
diameter_parameter="bore_diameter",
|
||||
radius_expression="bore_radius",
|
||||
),
|
||||
"axis": "primary_axis",
|
||||
"center": _center3(profile),
|
||||
"host": host_feature_id or "flange_body",
|
||||
}
|
||||
)
|
||||
|
||||
generated = generated_bodies + generated_cuts
|
||||
for pattern_feature in generated_patterns:
|
||||
if (
|
||||
pattern_feature.get("host") == "flange_body"
|
||||
and host_feature_id is not None
|
||||
):
|
||||
pattern_feature["host"] = host_feature_id
|
||||
generated.append(pattern_feature)
|
||||
|
||||
if not generated:
|
||||
return None
|
||||
return generated
|
||||
|
||||
|
||||
def _normalize_validation_list(value: Any, *, parameter_key: str = "parameter") -> Any:
|
||||
if isinstance(value, dict):
|
||||
rows = [
|
||||
(
|
||||
{
|
||||
parameter_key: str(key),
|
||||
**item,
|
||||
}
|
||||
if isinstance(item, dict)
|
||||
else {parameter_key: str(key), "description": item}
|
||||
)
|
||||
for key, item in value.items()
|
||||
]
|
||||
elif isinstance(value, list):
|
||||
rows = [
|
||||
copy.deepcopy(item)
|
||||
if isinstance(item, dict)
|
||||
else {"description": item}
|
||||
for item in value
|
||||
]
|
||||
else:
|
||||
return value
|
||||
if parameter_key == "parameter":
|
||||
for row in rows:
|
||||
row.setdefault(
|
||||
"expected_change",
|
||||
row.get("acceptance") or row.get("description") or "geometry_changes",
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def _normalize_feature_aliases(feature: dict[str, Any]) -> dict[str, Any]:
|
||||
normalized = copy.deepcopy(feature)
|
||||
operation = normalized.get("operation")
|
||||
if operation == "polar_hole_pattern":
|
||||
if "diameter" not in normalized and "hole_diameter" in normalized:
|
||||
normalized["diameter"] = normalized.pop("hole_diameter")
|
||||
reference = normalized.get("reference")
|
||||
if (
|
||||
"host" not in normalized
|
||||
and isinstance(reference, dict)
|
||||
and isinstance(reference.get("feature"), str)
|
||||
):
|
||||
normalized["host"] = reference["feature"]
|
||||
elif operation == "through_hole":
|
||||
reference = normalized.get("reference")
|
||||
if (
|
||||
"host" not in normalized
|
||||
and isinstance(reference, dict)
|
||||
and isinstance(reference.get("feature"), str)
|
||||
):
|
||||
normalized["host"] = reference["feature"]
|
||||
if operation in SUPPORTED_OPERATIONS:
|
||||
normalized.setdefault("axis", "primary_axis")
|
||||
return normalized
|
||||
|
||||
|
||||
def _normalize_feature_list_aliases(value: Any) -> Any:
|
||||
features = _map_object_to_list(value)
|
||||
if not isinstance(features, list):
|
||||
return features
|
||||
return [
|
||||
_normalize_feature_aliases(feature)
|
||||
if isinstance(feature, dict)
|
||||
else feature
|
||||
for feature in features
|
||||
]
|
||||
|
||||
|
||||
def _normalize_tool_authored_designir(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
normalized = copy.deepcopy(payload)
|
||||
normalized.setdefault("schema_version", SCHEMA_VERSION)
|
||||
if normalized.get("units") is None:
|
||||
normalized["units"] = "mm"
|
||||
semantic = normalized.get("semantic_layer")
|
||||
if isinstance(semantic, dict):
|
||||
normalized["semantic_layer"] = _normalize_tool_authored_semantic_layer(
|
||||
normalized, semantic
|
||||
)
|
||||
edit_interface = normalized.get("edit_interface")
|
||||
if isinstance(edit_interface, dict):
|
||||
edit_interface["semantic_parameters"] = _normalize_semantic_parameter_names(
|
||||
edit_interface.get("semantic_parameters", [])
|
||||
)
|
||||
validation = normalized.get("validation_contract")
|
||||
if isinstance(validation, dict):
|
||||
validation["perturbations"] = _normalize_validation_list(
|
||||
validation.get("perturbations", [])
|
||||
)
|
||||
validation["invariants"] = _normalize_validation_list(
|
||||
validation.get("invariants", []), parameter_key="id"
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
def _normalize_tool_authored_semantic_layer(
|
||||
payload: dict[str, Any], semantic: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
normalized = copy.deepcopy(semantic)
|
||||
if normalized.get("reconstruction_status") in {
|
||||
"independent",
|
||||
"fully_parametric",
|
||||
"fully_semantic_parametric",
|
||||
}:
|
||||
normalized["reconstruction_status"] = "ready"
|
||||
normalized["coordinate_system"] = _normalize_coordinate_system(
|
||||
normalized.get("coordinate_system")
|
||||
)
|
||||
coordinate = normalized.get("coordinate_system")
|
||||
if isinstance(coordinate, dict):
|
||||
coordinate.setdefault("origin", [0, 0, 0])
|
||||
coordinate.setdefault("x_axis", [1, 0, 0])
|
||||
coordinate.setdefault("y_axis", [0, 1, 0])
|
||||
coordinate.setdefault("z_axis", [0, 0, 1])
|
||||
normalized["parameters"] = _map_named_list_to_object(
|
||||
normalized.get("parameters", {})
|
||||
)
|
||||
normalized["expressions"] = _normalize_expression_object(
|
||||
normalized.get("expressions", {})
|
||||
)
|
||||
normalized["datums"] = _map_named_list_to_object(
|
||||
normalized.get("datums", {})
|
||||
)
|
||||
datums = normalized.setdefault("datums", {})
|
||||
if isinstance(datums, dict):
|
||||
datums.setdefault("primary_axis", {"kind": "axis", "axis": "z"})
|
||||
edit_interface = payload.get("edit_interface", {})
|
||||
editable_names = set(
|
||||
_normalize_semantic_parameter_names(
|
||||
edit_interface.get("semantic_parameters", [])
|
||||
)
|
||||
if isinstance(edit_interface, dict)
|
||||
else []
|
||||
)
|
||||
edit_metadata = (
|
||||
_semantic_parameter_metadata(edit_interface.get("semantic_parameters", []))
|
||||
if isinstance(edit_interface, dict)
|
||||
else {}
|
||||
)
|
||||
parameters = normalized.get("parameters")
|
||||
if isinstance(parameters, dict):
|
||||
for name, parameter in parameters.items():
|
||||
if isinstance(parameter, dict):
|
||||
parameter.setdefault("editable", name in editable_names)
|
||||
metadata = edit_metadata.get(name, {})
|
||||
if metadata.get("label") and not parameter.get("display_name"):
|
||||
parameter["display_name"] = metadata["label"]
|
||||
if metadata.get("range") and not parameter.get("range"):
|
||||
parameter["range"] = metadata["range"]
|
||||
converted_features = _normalize_common_llm_feature_program(normalized)
|
||||
if converted_features is not None:
|
||||
normalized["features"] = converted_features
|
||||
else:
|
||||
normalized["features"] = _normalize_feature_list_aliases(
|
||||
normalized.get("features", [])
|
||||
)
|
||||
normalized["sketches"] = _map_object_to_list(normalized.get("sketches", []))
|
||||
normalized["patterns"] = _map_object_to_list(normalized.get("patterns", []))
|
||||
normalized["attachments"] = _map_object_to_list(normalized.get("attachments", []))
|
||||
normalized["constraints"] = _normalize_validation_list(
|
||||
normalized.get("constraints", []), parameter_key="id"
|
||||
)
|
||||
normalized["construction_stages"] = _map_object_to_list(
|
||||
normalized.get("construction_stages", [])
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
def validate_designir(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Validate either supported version and return its compiler representation."""
|
||||
return compiler_payload(payload)
|
||||
@@ -423,7 +896,7 @@ def resolve_value(value: Any, values: dict[str, float], label: str) -> float:
|
||||
if isinstance(value, dict) and set(value) == {"expression"}:
|
||||
name = str(value["expression"])
|
||||
if name not in values:
|
||||
raise DesignIRError(f"{label} references unknown expression {name}")
|
||||
return _safe_expression(name, values)
|
||||
return values[name]
|
||||
raise DesignIRError(f"{label} must be a number, parameter, or expression")
|
||||
|
||||
@@ -578,10 +1051,9 @@ def build_shape(payload: dict[str, Any]) -> Part:
|
||||
|
||||
if shape is None:
|
||||
raise DesignIRError("DesignIR produced no geometry")
|
||||
result = Part(shape.wrapped)
|
||||
if not list(result.solids()) or result.volume <= 0:
|
||||
if not list(shape.solids()) or shape.volume <= 0:
|
||||
raise DesignIRError("DesignIR did not produce a valid solid")
|
||||
return result
|
||||
return shape
|
||||
|
||||
|
||||
def build_simplecad_shape(payload: dict[str, Any]) -> tuple[Any, str]:
|
||||
@@ -920,9 +1392,26 @@ def _distance(first: list[float], second: list[float]) -> float:
|
||||
return math.sqrt(sum((a - b) ** 2 for a, b in zip(first, second)))
|
||||
|
||||
|
||||
def _boolean_result_volume(value: Any) -> float:
|
||||
volume = getattr(value, "volume", None)
|
||||
if isinstance(volume, (int, float)):
|
||||
return float(volume)
|
||||
try:
|
||||
return sum(
|
||||
float(getattr(item, "volume", 0.0))
|
||||
for item in value
|
||||
)
|
||||
except TypeError as exc:
|
||||
raise DesignIRError(
|
||||
f"Boolean result has no measurable volume: {type(value).__name__}"
|
||||
) from exc
|
||||
|
||||
|
||||
def _symmetric_difference_volume(first: Part, second: Part) -> float:
|
||||
try:
|
||||
return float((first - second).volume + (second - first).volume)
|
||||
return _boolean_result_volume(first - second) + _boolean_result_volume(
|
||||
second - first
|
||||
)
|
||||
except Exception as exc: # OpenCascade failures become an explicit metric state.
|
||||
raise DesignIRError(f"Symmetric-difference boolean failed: {exc}") from exc
|
||||
|
||||
@@ -939,6 +1428,50 @@ def _parameter_reference_count(value: Any, parameter: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _expression_reference_names(value: Any) -> set[str]:
|
||||
if isinstance(value, dict):
|
||||
direct = {str(value["expression"])} if isinstance(value.get("expression"), str) else set()
|
||||
return direct.union(
|
||||
*(_expression_reference_names(child) for child in value.values())
|
||||
)
|
||||
if isinstance(value, list):
|
||||
names: set[str] = set()
|
||||
for child in value:
|
||||
names.update(_expression_reference_names(child))
|
||||
return names
|
||||
return set()
|
||||
|
||||
|
||||
def _expression_reference_values(value: Any) -> set[str]:
|
||||
if isinstance(value, dict):
|
||||
direct = {str(value["expression"])} if isinstance(value.get("expression"), str) else set()
|
||||
values = set(direct)
|
||||
for child in value.values():
|
||||
values.update(_expression_reference_values(child))
|
||||
return values
|
||||
if isinstance(value, list):
|
||||
values: set[str] = set()
|
||||
for child in value:
|
||||
values.update(_expression_reference_values(child))
|
||||
return values
|
||||
return set()
|
||||
|
||||
|
||||
def _parameter_expression_reference_count(
|
||||
value: Any, expressions: Any, parameter: str
|
||||
) -> int:
|
||||
if not isinstance(expressions, dict):
|
||||
expressions = {}
|
||||
count = 0
|
||||
for expression_value in _expression_reference_values(value):
|
||||
expression = expressions.get(expression_value, expression_value)
|
||||
if isinstance(expression, str):
|
||||
count += len(
|
||||
re.findall(rf"\b{re.escape(parameter)}\b", expression)
|
||||
)
|
||||
return count
|
||||
|
||||
|
||||
def acceptance_report(
|
||||
teacher_path: Path,
|
||||
rebuilt_path: Path,
|
||||
@@ -1186,7 +1719,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
migrated = (
|
||||
migrate_designir_2_to_3(original)
|
||||
if original.get("schema_version") == LEGACY_SCHEMA_VERSION
|
||||
else original
|
||||
else _normalize_tool_authored_designir(original)
|
||||
)
|
||||
compiler_payload(migrated)
|
||||
write_json(args.output, migrated)
|
||||
|
||||
Reference in New Issue
Block a user