docs: Comprehensive documentation update for Dashboard V3 and Canvas

## Documentation Updates
- DASHBOARD.md: Updated to V3.0 with Canvas V3 features, file browser, introspection
- DASHBOARD_IMPLEMENTATION_STATUS.md: Marked Canvas V3 features as COMPLETE
- CANVAS.md: New comprehensive guide for Canvas Builder V3 with all features
- CLAUDE.md: Added dashboard quick reference and Canvas V3 features

## Canvas V3 Features Documented
- File Browser: Browse studies directory for model files
- Model Introspection: Auto-discover expressions, solver type, dependencies
- One-Click Add: Add expressions as design variables instantly
- Claude Bug Fixes: WebSocket reconnection, SQL errors resolved
- Health Check: /api/health endpoint for monitoring

## Backend Services
- NX introspection service with expression discovery
- File browser API with type filtering
- Claude session management improvements
- Context builder enhancements

## Frontend Components
- FileBrowser: Modal for file selection with search
- IntrospectionPanel: View discovered model information
- ExpressionSelector: Dropdown for design variable configuration
- Improved chat hooks with reconnection logic

## Plan Documents
- Added RALPH_LOOP_CANVAS_V2/V3 implementation records
- Added ATOMIZER_DASHBOARD_V2_MASTER_PLAN
- Added investigation and sync documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-16 20:48:58 -05:00
parent 1c7c7aff05
commit ac5e9b4054
23 changed files with 10860 additions and 773 deletions

View File

@@ -25,6 +25,7 @@ class ContextBuilder:
mode: Literal["user", "power"],
study_id: Optional[str] = None,
conversation_history: Optional[List[Dict[str, Any]]] = None,
canvas_state: Optional[Dict[str, Any]] = None,
) -> str:
"""
Build full system prompt with context.
@@ -33,12 +34,17 @@ class ContextBuilder:
mode: "user" for safe operations, "power" for full access
study_id: Optional study name to provide context for
conversation_history: Optional recent messages for continuity
canvas_state: Optional canvas state (nodes, edges) from the UI
Returns:
Complete system prompt string
"""
parts = [self._base_context(mode)]
# Canvas context takes priority - if user is working on a canvas, include it
if canvas_state:
parts.append(self._canvas_context(canvas_state))
if study_id:
parts.append(self._study_context(study_id))
else:
@@ -200,6 +206,166 @@ Important guidelines:
return context
def _canvas_context(self, canvas_state: Dict[str, Any]) -> str:
"""
Build context from canvas state (nodes and edges).
This is CRITICAL for Claude to understand the current workflow
being built in the Canvas UI.
"""
context = "# Current Canvas State\n\n"
context += "**You are assisting the user with a Canvas Builder workflow.**\n"
context += "The canvas represents an optimization pipeline being configured visually.\n\n"
nodes = canvas_state.get("nodes", [])
edges = canvas_state.get("edges", [])
study_name = canvas_state.get("studyName", "Untitled")
study_path = canvas_state.get("studyPath", None)
context += f"**Study Name**: {study_name}\n"
if study_path:
context += f"**Study Path**: {study_path}\n"
context += "\n"
# Group nodes by type
node_types = {}
for node in nodes:
node_type = node.get("type", "unknown")
if node_type not in node_types:
node_types[node_type] = []
node_types[node_type].append(node)
# Model node
if "model" in node_types:
model = node_types["model"][0]
data = model.get("data", {})
context += "## Model\n"
context += f"- **Label**: {data.get('label', 'Model')}\n"
context += f"- **File Path**: {data.get('filePath', 'Not set')}\n"
context += f"- **File Type**: {data.get('fileType', 'Not set')}\n\n"
# Solver node
if "solver" in node_types:
solver = node_types["solver"][0]
data = solver.get("data", {})
context += "## Solver\n"
context += f"- **Type**: {data.get('solverType', 'Not set')}\n\n"
# Design variables
if "designVar" in node_types:
context += "## Design Variables\n\n"
context += "| Name | Expression | Min | Max | Baseline | Unit | Enabled |\n"
context += "|------|------------|-----|-----|----------|------|---------|\n"
for dv in node_types["designVar"]:
data = dv.get("data", {})
name = data.get("label", "?")
expr = data.get("expressionName", data.get("label", "?"))
min_val = data.get("minValue", "?")
max_val = data.get("maxValue", "?")
baseline = data.get("baseline", "-")
unit = data.get("unit", "-")
enabled = "" if data.get("enabled", True) else ""
context += f"| {name} | {expr} | {min_val} | {max_val} | {baseline} | {unit} | {enabled} |\n"
context += "\n"
# Extractors
if "extractor" in node_types:
context += "## Extractors\n\n"
for ext in node_types["extractor"]:
data = ext.get("data", {})
context += f"### {data.get('extractorName', data.get('label', 'Extractor'))}\n"
context += f"- **ID**: {data.get('extractorId', 'Not set')}\n"
context += f"- **Type**: {data.get('extractorType', 'Not set')}\n"
if data.get("extractMethod"):
context += f"- **Method**: {data.get('extractMethod')}\n"
if data.get("innerRadius"):
context += f"- **Inner Radius**: {data.get('innerRadius')}\n"
if data.get("nModes"):
context += f"- **Zernike Modes**: {data.get('nModes')}\n"
if data.get("subcases"):
context += f"- **Subcases**: {data.get('subcases')}\n"
if data.get("config"):
config = data.get("config", {})
if config.get("subcaseLabels"):
context += f"- **Subcase Labels**: {config.get('subcaseLabels')}\n"
if config.get("referenceSubcase"):
context += f"- **Reference Subcase**: {config.get('referenceSubcase')}\n"
context += "\n"
# Objectives
if "objective" in node_types:
context += "## Objectives\n\n"
context += "| Name | Direction | Weight | Penalty |\n"
context += "|------|-----------|--------|---------|\n"
for obj in node_types["objective"]:
data = obj.get("data", {})
name = data.get("name", data.get("label", "?"))
direction = data.get("direction", "minimize")
weight = data.get("weight", 1)
penalty = data.get("penaltyWeight", "-")
context += f"| {name} | {direction} | {weight} | {penalty} |\n"
context += "\n"
# Constraints
if "constraint" in node_types:
context += "## Constraints\n\n"
context += "| Name | Operator | Value |\n"
context += "|------|----------|-------|\n"
for con in node_types["constraint"]:
data = con.get("data", {})
name = data.get("name", data.get("label", "?"))
operator = data.get("operator", "?")
value = data.get("value", "?")
context += f"| {name} | {operator} | {value} |\n"
context += "\n"
# Algorithm
if "algorithm" in node_types:
algo = node_types["algorithm"][0]
data = algo.get("data", {})
context += "## Algorithm\n"
context += f"- **Method**: {data.get('method', 'Not set')}\n"
context += f"- **Max Trials**: {data.get('maxTrials', 'Not set')}\n"
if data.get("sigma0"):
context += f"- **CMA-ES Sigma0**: {data.get('sigma0')}\n"
if data.get("restartStrategy"):
context += f"- **Restart Strategy**: {data.get('restartStrategy')}\n"
context += "\n"
# Surrogate
if "surrogate" in node_types:
sur = node_types["surrogate"][0]
data = sur.get("data", {})
context += "## Surrogate\n"
context += f"- **Enabled**: {data.get('enabled', False)}\n"
context += f"- **Type**: {data.get('modelType', 'Not set')}\n"
context += f"- **Min Trials**: {data.get('minTrials', 'Not set')}\n\n"
# Edge connections summary
context += "## Connections\n\n"
context += f"Total edges: {len(edges)}\n"
context += "Flow: Design Variables → Model → Solver → Extractors → Objectives/Constraints → Algorithm\n\n"
# Canvas modification instructions
context += """## Canvas Modification Tools
When the user asks to modify the canvas (add/remove nodes, change values), use these MCP tools:
- `canvas_add_node` - Add a new node (designVar, extractor, objective, constraint)
- `canvas_update_node` - Update node properties (bounds, weights, names)
- `canvas_remove_node` - Remove a node from the canvas
- `canvas_connect_nodes` - Create an edge between nodes
**Example user requests you can handle:**
- "Add a design variable called hole_diameter with range 5-15 mm" → Use canvas_add_node
- "Change the weight of wfe_40_20 to 8" → Use canvas_update_node
- "Remove the constraint node" → Use canvas_remove_node
- "Connect the new extractor to the objective" → Use canvas_connect_nodes
Always respond with confirmation of changes made to the canvas.
"""
return context
def _mode_instructions(self, mode: str) -> str:
"""Mode-specific instructions"""
if mode == "power":