diff --git a/backend/README.md b/backend/README.md index e445d238..d2b5371d 100644 --- a/backend/README.md +++ b/backend/README.md @@ -10,39 +10,63 @@ The backend owns the application API and CAD generation workflow: Expected development entrypoint: `app.main:app`, served by Uvicorn. -## Incremental Generation Configuration +## Reasoning Effort -Incremental generation is enabled by default. It requires a separately -configured vision-capable review model and the Python OpenCascade/Pillow -technical renderer; a run -fails instead of skipping visual review when either is unavailable. +The backend uses the Chat Completions API. Configure a provider's reasoning +budget with `CDSL__REASONING_EFFORT`; for the current OpenAI setup: ```dotenv -# Authoring provider/model must already be configured as usual. -CDSL_INCREMENTAL_GENERATION=1 +CDSL_OPENAI_REASONING_EFFORT=medium +``` +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 # CDSL__VISION_MODELS setting. It is intentionally not inferred # from the authoring model. -CDSL_REVIEW_PROVIDER=openai -CDSL_REVIEW_MODEL=gpt-4.1-mini -CDSL_OPENAI_VISION_MODELS=gpt-4.1-mini +CDSL_REVIEW_PROVIDER=deepseek +CDSL_REVIEW_MODEL=deepseek-v4-flash-vision-exp +CDSL_DEEPSEEK_VISION_MODELS=deepseek-v4-flash-vision-exp # Install Python rendering dependencies. The renderer reads the revision STEP # file and creates canonical images without a browser or GPU driver. pip install -r requirements.txt -# Optional per-node retry budgets. -CDSL_NODE_AUTHORING_ATTEMPTS=2 -CDSL_NODE_REPAIR_ATTEMPTS=2 -CDSL_NODE_REPLAN_ATTEMPTS=1 +# Limits apply to the current checkpoint head, never to total task complexity. +CDSL_AGENT_TOOL_CALLS_PER_CYCLE=12 +CDSL_AGENT_CANDIDATE_ATTEMPTS_PER_HEAD=3 +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 reports are available only after the task reaches `COMPLETED`. -The generation plan contains semantic node IDs only. The backend derives the -unique CDSL feature and sketch IDs from each node, then writes them during -fragment materialization. This keeps naming and topology ownership stable -without requiring the authoring model to reproduce internal identifiers. +The backend assigns feature and sketch IDs, appends causal dependencies and +expands only opaque current-snapshot selector tokens. It does not compile +geometry templates or correct workplanes, profiles, sizes, directions or +boolean semantics authored by the model. Failed candidates remain auditable +but never become revisions. diff --git a/backend/engine/cdsl_engine/build123d_adapter.py b/backend/engine/cdsl_engine/build123d_adapter.py index 343caaaf..562293b4 100644 --- a/backend/engine/cdsl_engine/build123d_adapter.py +++ b/backend/engine/cdsl_engine/build123d_adapter.py @@ -399,9 +399,14 @@ class Build123dGeometryAdapter: def body_geometry(body: Any) -> dict[str, Any]: # 汇总主体基本几何信息:包围盒与体积。 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 { "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), + "solid_count": len(solids), } @staticmethod diff --git a/backend/engine/cdsl_engine/runtime.py b/backend/engine/cdsl_engine/runtime.py index 5aaae5c8..ec1d5964 100644 --- a/backend/engine/cdsl_engine/runtime.py +++ b/backend/engine/cdsl_engine/runtime.py @@ -549,9 +549,13 @@ def _execute_hole(node: FeaturePlanNode, session: ExecutionSession, *, wizard: b positions_are_local = False # 3. 解析孔规格 HoleSpec(直径、深度、类型等,wizard 模式提供额外默认值)。 spec = HoleSpec.from_feature(node.atomic_id, node.params, wizard=wizard) - # 4. 确定孔轴向:默认沿宿主面法向,但需保证指向主体内部(按主体中心与面原点的相对位置取反)。 - normal = host.normal - inward = normal if vector_dot(vector_subtract(session.adapter.body_center(session.body), host.origin_mm), normal) >= 0 else vector_scale(normal, -1) + # 4. A host-face normal is an outward B-rep orientation, so its inverse + # always enters the material. Inferring direction from the global body + # 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. 生成孔切除工具:按孔规格、起始位置、内方向及“贯穿到主体底面”的深度构造工具实体。 tool = session.adapter.hole_tool( spec,