Ganjihong #7
+43
-19
@@ -10,39 +10,63 @@ The backend owns the application API and CAD generation workflow:
|
|||||||
|
|
||||||
Expected development entrypoint: `app.main:app`, served by Uvicorn.
|
Expected development entrypoint: `app.main:app`, served by Uvicorn.
|
||||||
|
|
||||||
## Incremental Generation Configuration
|
## Reasoning Effort
|
||||||
|
|
||||||
Incremental generation is enabled by default. It requires a separately
|
The backend uses the Chat Completions API. Configure a provider's reasoning
|
||||||
configured vision-capable review model and the Python OpenCascade/Pillow
|
budget with `CDSL_<PROVIDER>_REASONING_EFFORT`; for the current OpenAI setup:
|
||||||
technical renderer; a run
|
|
||||||
fails instead of skipping visual review when either is unavailable.
|
|
||||||
|
|
||||||
```dotenv
|
```dotenv
|
||||||
# Authoring provider/model must already be configured as usual.
|
CDSL_OPENAI_REASONING_EFFORT=medium
|
||||||
CDSL_INCREMENTAL_GENERATION=1
|
```
|
||||||
|
|
||||||
|
Use `low`, `medium`, or `high` according to the latency/cost versus quality
|
||||||
|
tradeoff. The setting is sent as Chat Completions' `reasoning_effort` field to
|
||||||
|
authoring, streaming, and visual-review requests. Leave it empty to use the
|
||||||
|
provider/model default. The selected OpenAI-compatible endpoint must support
|
||||||
|
the requested value.
|
||||||
|
|
||||||
|
## Autonomous CDSL Agent Configuration
|
||||||
|
|
||||||
|
The autonomous agent writes one frozen free-form `requirements.md`, then
|
||||||
|
observes, measures, renders and appends one CDSL feature at a time. Its author
|
||||||
|
uses normal function calls; no provider strict JSON Schema capability or
|
||||||
|
complete modelling DAG is required. Candidate fragments are rebuilt in a
|
||||||
|
staging directory through `cdsl_only` before a checkpoint can be committed.
|
||||||
|
|
||||||
|
Final publication requires a separately configured vision-capable review model
|
||||||
|
and the Python OpenCascade/Pillow technical renderer. The agent may build and
|
||||||
|
inspect intermediate checkpoints without image review; a final run fails
|
||||||
|
closed if its independent review configuration is unavailable.
|
||||||
|
|
||||||
|
```dotenv
|
||||||
# Must name one configured provider and one model listed in that provider's
|
# Must name one configured provider and one model listed in that provider's
|
||||||
# CDSL_<PROVIDER>_VISION_MODELS setting. It is intentionally not inferred
|
# CDSL_<PROVIDER>_VISION_MODELS setting. It is intentionally not inferred
|
||||||
# from the authoring model.
|
# from the authoring model.
|
||||||
CDSL_REVIEW_PROVIDER=openai
|
CDSL_REVIEW_PROVIDER=deepseek
|
||||||
CDSL_REVIEW_MODEL=gpt-4.1-mini
|
CDSL_REVIEW_MODEL=deepseek-v4-flash-vision-exp
|
||||||
CDSL_OPENAI_VISION_MODELS=gpt-4.1-mini
|
CDSL_DEEPSEEK_VISION_MODELS=deepseek-v4-flash-vision-exp
|
||||||
|
|
||||||
# Install Python rendering dependencies. The renderer reads the revision STEP
|
# Install Python rendering dependencies. The renderer reads the revision STEP
|
||||||
# file and creates canonical images without a browser or GPU driver.
|
# file and creates canonical images without a browser or GPU driver.
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
# Optional per-node retry budgets.
|
# Limits apply to the current checkpoint head, never to total task complexity.
|
||||||
CDSL_NODE_AUTHORING_ATTEMPTS=2
|
CDSL_AGENT_TOOL_CALLS_PER_CYCLE=12
|
||||||
CDSL_NODE_REPAIR_ATTEMPTS=2
|
CDSL_AGENT_CANDIDATE_ATTEMPTS_PER_HEAD=3
|
||||||
CDSL_NODE_REPLAN_ATTEMPTS=1
|
CDSL_AGENT_CONSECUTIVE_NO_PROGRESS_LIMIT=6
|
||||||
|
CDSL_AGENT_MAX_FEATURES_PER_FRAGMENT=6
|
||||||
|
CDSL_AGENT_CONTEXT_CHAR_LIMIT=24000
|
||||||
|
CDSL_AGENT_RENDER_CACHE=true
|
||||||
```
|
```
|
||||||
|
|
||||||
Every checkpoint is rebuilt from its fully materialized CDSL through the
|
The author chooses each coherent 1-6 feature batch. Every rebuilt batch is
|
||||||
|
rendered and independently reviewed before it can become a checkpoint; only
|
||||||
|
an accepted reviewer verdict advances the working model. Every checkpoint is rebuilt from its fully materialized CDSL through the
|
||||||
`cdsl_only` runtime. Checkpoint GLB files are preview-only; STEP, CDSL, and
|
`cdsl_only` runtime. Checkpoint GLB files are preview-only; STEP, CDSL, and
|
||||||
reports are available only after the task reaches `COMPLETED`.
|
reports are available only after the task reaches `COMPLETED`.
|
||||||
|
|
||||||
The generation plan contains semantic node IDs only. The backend derives the
|
The backend assigns feature and sketch IDs, appends causal dependencies and
|
||||||
unique CDSL feature and sketch IDs from each node, then writes them during
|
expands only opaque current-snapshot selector tokens. It does not compile
|
||||||
fragment materialization. This keeps naming and topology ownership stable
|
geometry templates or correct workplanes, profiles, sizes, directions or
|
||||||
without requiring the authoring model to reproduce internal identifiers.
|
boolean semantics authored by the model. Failed candidates remain auditable
|
||||||
|
but never become revisions.
|
||||||
|
|||||||
@@ -399,9 +399,14 @@ class Build123dGeometryAdapter:
|
|||||||
def body_geometry(body: Any) -> dict[str, Any]:
|
def body_geometry(body: Any) -> dict[str, Any]:
|
||||||
# 汇总主体基本几何信息:包围盒与体积。
|
# 汇总主体基本几何信息:包围盒与体积。
|
||||||
bbox = body.bounding_box()
|
bbox = body.bounding_box()
|
||||||
|
# A feature history can contain several body IDs while still ending in
|
||||||
|
# one connected solid (for example, a base extrusion followed by hole
|
||||||
|
# cuts). Count the current OCC result, never feature history entries.
|
||||||
|
solids = list(body.solids()) if hasattr(body, "solids") else [body]
|
||||||
return {
|
return {
|
||||||
"bbox_mm": [bbox.min.X, bbox.min.Y, bbox.min.Z, bbox.max.X, bbox.max.Y, bbox.max.Z],
|
"bbox_mm": [bbox.min.X, bbox.min.Y, bbox.min.Z, bbox.max.X, bbox.max.Y, bbox.max.Z],
|
||||||
"volume_mm3": float(body.volume),
|
"volume_mm3": float(body.volume),
|
||||||
|
"solid_count": len(solids),
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -7,22 +7,22 @@
|
|||||||
"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_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", "polygon", "analytic_contours"],
|
"runtime_supported_profiles": ["circle", "polygon", "analytic_contours"],
|
||||||
"feature_atomic_ids": {
|
"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_blind": {"summary": "Add the closed profile by one signed extrusion distance.", "required_params": ["distance_mm"], "optional_params": ["reverse"], "requires_sketch": true, "produces_body": 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},
|
"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, "produces_body": true},
|
||||||
"extrude_cut_blind": {"summary": "Remove the closed profile by one signed extrusion distance.", "required_params": ["distance_mm"], "optional_params": ["reverse"], "requires_sketch": true},
|
"extrude_cut_blind": {"summary": "Remove the closed profile by one signed extrusion distance.", "required_params": ["distance_mm"], "optional_params": ["reverse"], "requires_sketch": true},
|
||||||
"revolve_add": {"summary": "Add the closed profile by revolving it around an axis.", "required_params": ["angle_deg", "axis"], "optional_params": ["reverse"], "requires_sketch": true},
|
"revolve_add": {"summary": "Add the closed profile by revolving it around an axis.", "required_params": ["angle_deg", "axis"], "optional_params": ["reverse"], "requires_sketch": true, "produces_body": true},
|
||||||
"revolve_cut": {"summary": "Remove the closed profile by revolving it around an axis.", "required_params": ["angle_deg", "axis"], "optional_params": ["reverse"], "requires_sketch": true},
|
"revolve_cut": {"summary": "Remove the closed profile by revolving it around an axis.", "required_params": ["angle_deg", "axis"], "optional_params": ["reverse"], "requires_sketch": true},
|
||||||
"hole_blind": {"summary": "Cut one or more blind cylindrical holes in the current body.", "required_params": ["diameter_mm", "depth_mm", "positions", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face must provide a strict selector or an explicit frame.", "requires_sketch": true},
|
"hole_blind": {"summary": "Cut one or more blind cylindrical holes in the current body.", "required_params": ["diameter_mm", "depth_mm", "positions", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face is injected from exactly one face selector token.", "requires_sketch": false},
|
||||||
"hole_countersink": {"summary": "Cut one or more blind holes with countersink dimensions.", "required_params": ["diameter_mm", "depth_mm", "positions", "countersink_diameter_mm", "countersink_angle_rad", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face must provide a strict selector or an explicit frame.", "requires_sketch": true},
|
"hole_countersink": {"summary": "Cut one or more blind holes with countersink dimensions.", "required_params": ["diameter_mm", "depth_mm", "positions", "countersink_diameter_mm", "countersink_angle_rad", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face is injected from exactly one face selector token.", "requires_sketch": false},
|
||||||
"hole_counterbore": {"summary": "Cut one or more blind holes with counterbore dimensions.", "required_params": ["diameter_mm", "depth_mm", "positions", "counterbore_diameter_mm", "counterbore_depth_mm", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face must provide a strict selector or an explicit frame.", "requires_sketch": true},
|
"hole_counterbore": {"summary": "Cut one or more blind holes with counterbore dimensions.", "required_params": ["diameter_mm", "depth_mm", "positions", "counterbore_diameter_mm", "counterbore_depth_mm", "host_face"], "optional_params": ["drill_angle_rad"], "position_format": "positions is a non-empty array of objects: [{\"mm\":[u_mm,v_mm,w_mm]}]. A bare coordinate array is invalid. host_face is injected from exactly one face selector token.", "requires_sketch": false},
|
||||||
"sphere_add": {"summary": "Add one spherical solid at an explicit model-space center.", "required_params": ["radius_mm", "center_mm"], "optional_params": [], "requires_sketch": true},
|
"sphere_add": {"summary": "Add one spherical solid at an explicit model-space center.", "required_params": ["radius_mm", "center_mm"], "optional_params": [], "requires_sketch": true, "produces_body": true},
|
||||||
"fillet": {"summary": "Apply a radius to selected edges or faces.", "required_params": ["radius_mm"], "optional_params": ["tangent_propagation"], "requires_sketch": false},
|
"fillet": {"summary": "Apply a radius to selected edges or faces.", "required_params": ["radius_mm"], "optional_params": ["tangent_propagation"], "requires_sketch": false, "selector_slot": {"path": "feature.selectors", "min_items": 1, "max_items": 64}},
|
||||||
"chamfer": {"summary": "Apply an equal-distance or angle-distance chamfer to selected edges or faces.", "required_params": ["distance_mm"], "optional_params": ["distance_2_mm", "angle_rad"], "requires_sketch": false},
|
"chamfer": {"summary": "Apply an equal-distance or angle-distance chamfer to selected edges or faces.", "required_params": ["distance_mm"], "optional_params": ["distance_2_mm", "angle_rad"], "requires_sketch": false, "selector_slot": {"path": "feature.selectors", "min_items": 1, "max_items": 64}},
|
||||||
"pattern_linear": {"summary": "Repeat source features along one or two directions.", "required_params": ["source_feature_ids", "direction_1", "spacing_1_mm", "pattern_count_1"], "optional_params": ["direction_2", "spacing_2_mm", "pattern_count_2"], "requires_sketch": false},
|
"pattern_linear": {"summary": "Repeat source features along one or two directions.", "required_params": ["source_feature_ids", "direction_1", "spacing_1_mm", "pattern_count_1"], "optional_params": ["direction_2", "spacing_2_mm", "pattern_count_2"], "requires_sketch": false},
|
||||||
"pattern_mirror": {"summary": "Mirror source features about a selected plane.", "required_params": ["source_feature_ids", "mirror_plane"], "optional_params": [], "requires_sketch": false},
|
"pattern_mirror": {"summary": "Mirror source features about a selected plane.", "required_params": ["source_feature_ids", "mirror_plane"], "optional_params": [], "requires_sketch": false, "selector_slot": {"path": "params.mirror_plane", "min_items": 1, "max_items": 1}},
|
||||||
"reference_plane": {"summary": "A named reference plane used by sketches or patterns.", "required_params": ["plane"], "optional_params": [], "requires_sketch": false},
|
"reference_plane": {"summary": "A named reference plane used by sketches or patterns.", "required_params": ["plane"], "optional_params": [], "requires_sketch": false},
|
||||||
"reference_axis": {"summary": "A named reference axis used by revolve or pattern features.", "required_params": ["axis"], "optional_params": [], "requires_sketch": false},
|
"reference_axis": {"summary": "A named reference axis used by revolve or pattern features.", "required_params": ["axis"], "optional_params": [], "requires_sketch": false},
|
||||||
"hole_wizard": {"summary": "A SolidWorks Hole Wizard feature including its typed dimensional contract and placement selectors.", "required_params": ["hole_type", "diameter_mm", "depth_mm"], "optional_params": ["positions", "host_face", "thread", "countersink", "counterbore"], "requires_sketch": false}
|
"hole_wizard": {"summary": "A SolidWorks Hole Wizard feature including its typed dimensional contract and placement selectors.", "required_params": ["hole_type", "diameter_mm", "depth_mm"], "optional_params": ["positions", "host_face", "thread", "countersink", "counterbore"], "requires_sketch": false, "selector_slot": {"path": "params.host_face", "min_items": 1, "max_items": 1}}
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"circle": {
|
"circle": {
|
||||||
|
|||||||
@@ -549,9 +549,13 @@ def _execute_hole(node: FeaturePlanNode, session: ExecutionSession, *, wizard: b
|
|||||||
positions_are_local = False
|
positions_are_local = False
|
||||||
# 3. 解析孔规格 HoleSpec(直径、深度、类型等,wizard 模式提供额外默认值)。
|
# 3. 解析孔规格 HoleSpec(直径、深度、类型等,wizard 模式提供额外默认值)。
|
||||||
spec = HoleSpec.from_feature(node.atomic_id, node.params, wizard=wizard)
|
spec = HoleSpec.from_feature(node.atomic_id, node.params, wizard=wizard)
|
||||||
# 4. 确定孔轴向:默认沿宿主面法向,但需保证指向主体内部(按主体中心与面原点的相对位置取反)。
|
# 4. A host-face normal is an outward B-rep orientation, so its inverse
|
||||||
normal = host.normal
|
# always enters the material. Inferring direction from the global body
|
||||||
inward = normal if vector_dot(vector_subtract(session.adapter.body_center(session.body), host.origin_mm), normal) >= 0 else vector_scale(normal, -1)
|
# centre fails for concave or multi-leg parts: for example, the top face
|
||||||
|
# of an L bracket can sit below the whole body's centre and the old rule
|
||||||
|
# drilled outward, producing a no-op feature reported as successful.
|
||||||
|
# The selected topology face is the local, authoritative orientation.
|
||||||
|
inward = vector_scale(host.normal, -1)
|
||||||
# 5. 生成孔切除工具:按孔规格、起始位置、内方向及“贯穿到主体底面”的深度构造工具实体。
|
# 5. 生成孔切除工具:按孔规格、起始位置、内方向及“贯穿到主体底面”的深度构造工具实体。
|
||||||
tool = session.adapter.hole_tool(
|
tool = session.adapter.hole_tool(
|
||||||
spec,
|
spec,
|
||||||
|
|||||||
Reference in New Issue
Block a user