Files
cdsl-cad/backend/engine/cdsl_engine/selector_capabilities.py
T
likang 994d06aaea feat(selector): 增加离线候选遍历与严格回放验证 Demo
- 新增 selector_candidate_demo,移除 provenance intent 后枚举候选 selector
- 对候选分支执行有界重建与严格 STEP 比较
- 仅在候选遍历完整且唯一 strict 通过时生成 selector 映射记录
- 增加 selector 候选搜索、预算限制和记录生成的测试
- 保持生产 selector resolver 不受 Demo 逻辑影响
- 更新 CADFS 能力台账,记录 IMPRINT 派生 profile 的 lineage selector 缺口
2026-09-10 15:12:57 +08:00

174 lines
6.6 KiB
Python

"""Verified FeatureScript query semantics available to the selector resolver.
This is intentionally a small, explicit allow-list. A numeric source version
only identifies a FeatureScript release; it does not establish that this
runtime has verified a query family's source semantics for that release.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class SelectorQueryCapability:
query_family: str
featurescript_version: str
standard_library: str
standard_library_version: str
source: str
contract: str
# These entries correspond to the direct builder contracts exercised by the
# CADFS 1511 corpus. New versions and query families must be registered with
# their source/API evidence before the runtime accepts them.
_CAPABILITIES = {
("CAP_FACE", "1511"): SelectorQueryCapability(
"CAP_FACE",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history",
"direct extrude cap operation role",
),
("CAP_EDGE", "1511"): SelectorQueryCapability(
"CAP_EDGE",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history",
"direct prism source edge to qualified start/end cap edge kernel lineage",
),
("OFFSET_FACE", "1511"): SelectorQueryCapability(
"OFFSET_FACE",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history",
"shell offset-face operation role with true dependency qualification",
),
# These entries authorize the deliberately narrow direct-prism lineage
# path. The capability matrix keeps the broader query families explicitly
# partial; all other generator and lifecycle combinations remain rejected.
("SWEPT_FACE", "1511"): SelectorQueryCapability(
"SWEPT_FACE",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history",
"kernel-lineage resolver contract only",
),
("SWEPT_EDGE", "1511"): SelectorQueryCapability(
"SWEPT_EDGE",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history",
"kernel-lineage resolver contract only",
),
("SWEPT_BODY", "1511"): SelectorQueryCapability(
"SWEPT_BODY",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history; one-item qUnion identity",
"active direct-new-body member selected by its producing operation",
),
("INTERSECT", "1511"): SelectorQueryCapability(
"INTERSECT",
"1511",
"onshape/std/geometry.fs",
"1511.0",
"CADFS FeatureScript 1511 exported query history; BRepAlgoAPI boolean Generated(face) and SectionEdges() exact handles",
"two source-qualified boolean input faces to one final section edge kernel lineage",
),
}
def selector_query_capability(intent: dict[str, Any]) -> SelectorQueryCapability | None:
"""Return an explicitly verified query capability for ``intent`` only."""
source_query = intent.get("source_query")
if not isinstance(source_query, dict):
return None
family = intent.get("query_family")
version = source_query.get("featurescript_version")
if not isinstance(family, str) or not isinstance(version, str):
return None
capability = _CAPABILITIES.get((family, version))
if capability is None:
return None
if (
source_query.get("standard_library") != capability.standard_library
or source_query.get("standard_library_version") != capability.standard_library_version
):
return None
return capability
def known_selector_query_versions(query_family: str) -> tuple[str, ...]:
"""Expose registered versions for deterministic unsupported diagnostics."""
return tuple(sorted(version for family, version in _CAPABILITIES if family == query_family))
def known_selector_query_standard_library_versions(query_family: str) -> tuple[tuple[str, str], ...]:
"""Expose exact direct imports that back a registered query contract."""
return tuple(sorted({
(capability.standard_library, capability.standard_library_version)
for (family, _version), capability in _CAPABILITIES.items()
if family == query_family
}))
def is_direct_blind_extrude_cap_output_role(
selector: dict[str, Any],
producer: dict[str, Any] | None,
sketches: dict[str, dict[str, Any]],
) -> bool:
"""Whether a selector names one cap from the direct prism contract.
This is the consumer-side mirror of the lowerer's CAP_FACE constructor.
It deliberately describes a narrow builder contract, rather than treating
an output-role string as general permission to select arbitrary topology.
The runtime still has to prove the exact role in its active snapshot.
"""
if not isinstance(selector, dict) or not isinstance(producer, dict):
return False
intent = selector.get("selector_intent")
if (
selector.get("kind") != "face"
or selector.get("output_role") not in {"extrude.start", "extrude.end"}
or selector.get("source") != "runtime_snapshot"
or selector.get("output_role_source") is not None
or any(selector.get(key) is not None for key in ("stable_id", "snapshot_id", "geometry", "binding_feature_id"))
or not isinstance(intent, dict)
or intent.get("query_family") != "CAP_FACE"
or intent.get("evidence") != "operation_role"
or intent.get("output_role") != selector.get("output_role")
):
return False
policy = intent.get("derivation_policy")
if not isinstance(policy, dict) or policy.get("multiplicity") != "one" or set(policy.get("allowed") or ()) != {"boundary", "continuation"}:
return False
params = producer.get("params") or {}
if (
producer.get("atomic_id") != "extrude_add_blind"
or params.get("result_mode") != "new_body"
or (params.get("end_condition") or {}).get("type") != "blind"
):
return False
if params.get("draft") is None:
return True
sketch = sketches.get(str(producer.get("sketch_id") or "")) or {}
profile = sketch.get("profile") or {}
if profile.get("type") == "circle":
return True
contours = profile.get("contours")
return (
profile.get("type") == "analytic_contours"
and isinstance(contours, list)
and len(contours) == 1
and bool((contours[0] or {}).get("closed"))
)