添加cadfs_to_cdsl代码,优化cad生成

This commit is contained in:
2026-09-03 10:40:51 +08:00
parent f3eb5dff54
commit c25b73c71e
34 changed files with 3586 additions and 147 deletions
+39 -6
View File
@@ -7,6 +7,7 @@ from fastapi import FastAPI, File, HTTPException, UploadFile
from fastapi.responses import JSONResponse, StreamingResponse
from app.models.contracts import ChatRequest, ConversationPatch
from app.cad_agent.domain.feature_plan import FeaturePlan, FeatureScheduler
from app.services.agent_service import AgentService
from app.services.library import CdslLibrary
from app.services.storage import WorkspaceStore, safe_conversation_id, safe_task_id
@@ -158,12 +159,27 @@ async def read_task(task_id: str) -> JSONResponse:
requirements_path = agent.v3.artifacts.artifact_path(safe_id, state.requirements_document_path) if state is not None and state.requirements_document_path else None
task["requirements_markdown"] = requirements_path.read_text(encoding="utf-8") if requirements_path and requirements_path.is_file() else None
target_path = agent.v3.artifacts.artifact_path(safe_id, state.completion_target_path) if state is not None and state.completion_target_path else None
plan_path = agent.v3.artifacts.artifact_path(safe_id, state.modeling_plan_path) if state is not None and state.modeling_plan_path else None
plan_path = agent.v3.artifacts.artifact_path(safe_id, state.feature_plan_path) if state is not None and state.feature_plan_path else None
result_path = agent.v3.artifacts.task_dir(safe_id) / "completion-result.md"
task["completion_target_markdown"] = target_path.read_text(encoding="utf-8") if target_path and target_path.is_file() else None
task["completion_target_path"] = state.completion_target_path if target_path and target_path.is_file() else ""
task["modeling_plan_markdown"] = plan_path.read_text(encoding="utf-8") if plan_path and plan_path.is_file() else None
task["modeling_plan_path"] = state.modeling_plan_path if plan_path and plan_path.is_file() else ""
task["feature_plan"] = agent.v3.artifacts.read_json(safe_id, state.feature_plan_path) if state is not None and state.feature_plan_path else None
task["feature_plan_path"] = state.feature_plan_path if plan_path and plan_path.is_file() else ""
task["feature_plan_hash"] = state.feature_plan_hash if state is not None else ""
if isinstance(task["feature_plan"], dict):
plan = FeaturePlan.model_validate(task["feature_plan"])
statuses = FeatureScheduler(plan, agent.v3.repository.ledger_events(safe_id)).statuses()
node_evidence = {str(item.get("node_id") or ""): item for item in task.get("feature_nodes") or () if isinstance(item, dict)}
task["feature_nodes"] = [
{
"node_id": str(node.get("node_id") or ""), "intent": str(node.get("intent") or ""),
"atomic_id": str(node.get("atomic_id") or ""), "priority": node.get("priority"),
"depends_on": node.get("depends_on") or [], "claim_ids": node.get("claim_ids") or [],
"status": statuses.get(str(node.get("node_id") or ""), "pending"),
**node_evidence.get(str(node.get("node_id") or ""), {}),
}
for node in task["feature_plan"].get("nodes") or () if isinstance(node, dict)
]
task["completion_result_markdown"] = result_path.read_text(encoding="utf-8") if result_path.is_file() else None
task["completion_result_path"] = "completion-result.md" if result_path.is_file() else ""
task["usage"] = agent.v3.repository.usage_summary(safe_id)
@@ -179,7 +195,7 @@ def _claim_summary(contract: dict[str, Any] | None, ledger: Any) -> list[dict[st
"""
latest: dict[str, dict[str, Any]] = {}
for event in reversed(ledger if isinstance(ledger, list) else []):
if not isinstance(event, dict) or event.get("event") not in {"accepted", "completed"}:
if not isinstance(event, dict) or event.get("event") not in {"accepted", "completed", "feature_node_verified", "final_visual_reviewed"}:
continue
values = event.get("claim_results")
if not isinstance(values, list):
@@ -291,8 +307,25 @@ async def read_artifact(task_id: str, artifact_path: str) -> StreamingResponse:
raise HTTPException(status_code=403, detail="Only final delivery artifacts are downloadable")
return FileResponse(path, filename=path.name)
# A running task may render its current checkpoint in the browser, but
# cannot expose any other checkpoint artifact or a failed-task preview.
v32_checkpoint_paths = {
f"revisions/{revision_id}/model.cdsl.json",
f"revisions/{revision_id}/model.step",
f"revisions/{revision_id}/model.glb",
f"revisions/{revision_id}/rebuild-report.json",
}
verified_revisions = {
str(item.get("revision_id") or "")
for item in task.get("revisions") or ()
if isinstance(item, dict) and item.get("status") == "success"
}
# Each v3.2 node revision is immutable and manifest-published. Make those
# checkpoints inspectable from the DAG while keeping every staging input,
# rejected candidate and arbitrary task artifact private.
if task.get("schema_version") == "3.2" and revision_id in verified_revisions and artifact_path in v32_checkpoint_paths:
return FileResponse(path, media_type="model/gltf-binary" if path.suffix == ".glb" else None, headers={"Content-Disposition": "inline" if path.suffix == ".glb" else f"attachment; filename={path.name}"})
# A legacy running task may render its active checkpoint in the browser,
# but cannot expose any other checkpoint artifact or failed-task preview.
if (
str(task.get("lifecycle") or "") != "running"
or revision_id != active_revision