8.9 KiB
Canvas Reasoner: Auto-Inferring Workflow Connections
Overview
The CanvasReasonerActivity uses LLM reasoning to automatically suggest connections between workflow activities when users drop new nodes onto the canvas. It analyzes input/output compatibility and detects connection problems.
Connection Logic
How It Works
-
Analyze Node Schemas
- Get each activity's input/output fields from knowledge base
- Activities are classified as:
- Generators: No inputs, has outputs (e.g., API call, trigger)
- Processors: Has inputs and outputs (e.g., analyze code, security scan)
- Sinks/Terminals: Has inputs, no outputs (e.g., notification, approval)
-
LLM Reasoning
- Pass all nodes + their schemas to reasoning model
- Ask LLM to suggest edges based on:
- Type compatibility (string→string, object→object)
- Logical execution order
- Data flow requirements
- Common workflow patterns
-
Validate Suggestions
- Check all suggested edges exist in node map
- Skip self-loops
- Remove duplicates
-
Compatibility Checking
- For each suggested edge:
source → target - Verify source produces outputs
- Verify target accepts inputs
- Check output/input type compatibility
- Flag incompatible connections
- For each suggested edge:
-
Identify Issues
- Collect all incompatible edges
- Identify disconnected nodes (no edges in/out)
- Generate user alerts for problems
Connection Impossibility Detection
Why Connections Fail
-
Missing Outputs
NotifyStatusActivity → AnalyzeCodeActivity ⚠️ NotifyStatusActivity produces no outputs Reason: Notification is terminal activity (sink) Solution: Add an intermediate processor that has outputs -
Missing Inputs
CloneRepoActivity → ApproveWorkflowActivity ⚠️ ApproveWorkflowActivity accepts no inputs Reason: Approval is a terminal activity (sink) Solution: ApproveWorkflowActivity only works as final step -
Type Mismatch
LLMInferenceActivity (output: string) → DeploymentPreCheckActivity (input: object) ⚠️ String output cannot satisfy object input requirement Reason: Incompatible data types Solution: Use LLM transformation node to convert string→object -
Semantic Incompatibility
NotifyStatusActivity → CloneRepoActivity ⚠️ No logical connection between these activities Reason: Notification cannot be input to clone operation Solution: Ensure data flow makes semantic sense
Incompatibility Data Structure
{
"incompatible_edges": [
{
"source": "node-1",
"target": "node-2",
"reason": "Source activity produces no outputs",
"source_needs": "any output",
"target_needs": "path, depth",
"suggestion": "Use LLM transformation to map outputs to inputs"
}
],
"disconnected_nodes": ["node-5", "node-8"],
"user_alerts": [
"⚠️ node-1 → node-2: Source activity produces no outputs. Use LLM transformation to map outputs to inputs",
"🔌 Node 'NotifyStatus-1' has no connections. Consider adding edges or removing it."
]
}
User Alerts
Alert Types
-
Incompatibility Warnings (⚠️)
⚠️ source → target: reason. suggestion.- Highlighted in red on canvas
- Shows in error sidebar
- Prevents workflow execution until fixed
-
Disconnection Warnings (🔌)
🔌 Node 'label' has no connections. Consider adding edges or removing it.- Highlighted in yellow
- Nodes with no input/output edges
- May be valid (first step, last step) or indicate design error
-
Type Mismatch Info (ℹ️)
ℹ️ To connect source → target, use transformer to map: {source_outputs} → {target_inputs}- Suggestion to use intermediate LLM node
- Provides mapping information
Frontend Integration
Canvas UI Feedback
When CanvasReasonerActivity returns incompatibilities:
-
Visual Markers
- Incompatible suggested edges: ❌ red dashed line (don't auto-add)
- Disconnected nodes: ⚠️ yellow border
-
Sidebar Alerts
🚨 Connection Issues (3) ⚠️ CloneRepo → ApproveWorkflow Reason: ApproveWorkflow is terminal (no outputs) Suggestion: Place ApproveWorkflow at end of workflow ⚠️ LLMInference → DeploymentPreCheck Reason: Type mismatch (string ≠ object) Suggestion: Add LLM transformation node 🔌 SecurityScan-1 has no incoming edges Suggestion: Connect CloneRepo → SecurityScan -
User Actions
- ✅ Accept suggestions (green edges)
- ❌ Reject incompatible edges
- 🔧 Add transformer nodes
- 🗑️ Remove disconnected nodes
API Response Example
{
"suggested_edges": [
{"source": "clone-1", "target": "analyze-1"},
{"source": "analyze-1", "target": "security-1"},
{"source": "security-1", "target": "report-1"}
],
"reasoning": "Standard code review workflow: clone → analyze → scan → report",
"confidence": 0.92,
"incompatible_edges": [
{
"source": "report-1",
"target": "approve-1",
"reason": "ReportGenerator has no outputs (terminal activity)",
"suggestion": "ApproveWorkflow can only be a final step"
}
],
"disconnected_nodes": [],
"user_alerts": [
"⚠️ report-1 → approve-1: ReportGenerator has no outputs (terminal activity). ApproveWorkflow can only be a final step"
]
}
Knowledge Base Schema
Each activity in activity_knowledge_base.json defines:
{
"name": "CloneRepoActivity",
"inputs": {
"repo": {"type": "string", "required": true},
"branch": {"type": "string", "required": false}
},
"outputs": {
"path": {"type": "string"},
"commit": {"type": "string"}
}
}
Classification Rules
- Generator (0 inputs): trigger, API call, schedule
- Processor (1+ inputs, 1+ outputs): analysis, transformation, scan
- Sink (1+ inputs, 0 outputs): notification, approval, archive
- Bypass (0 inputs, 0 outputs): rare - usually error
Common Patterns
✅ Valid Chains
CloneRepo → Analyze → SecurityScan → Report
(generator) → (processor) → (processor) → (sink)
Trigger → LLMInference → Decision → (Branch: Notify OR Approve)
(gen) → (processor) → (processor) → (sink)
❌ Invalid Chains
Notify → CloneRepo ❌
(sink) → (generator) - backward flow
CloneRepo → CloneRepo → Analyze ❌
self-loop - no benefit
Analyze → Approve → Notify ❌
Approve is terminal (sink), can't output to Notify
Edge Cases
Multiple Outputs → Single Input
SecurityScan → Report
SecurityScan outputs: [issues, metrics, severity]
Report inputs: [report_data]
LLM must infer: bundle all outputs into single report_data object
Confidence: 0.7 (requires transformation)
Terminal Activities
- ApproveWorkflowActivity: Must be last (blocks workflow)
- NotifyStatusActivity: Can be mid-workflow (async notify)
- ArchiveResultsActivity: Should be last (persistence)
Data Transformation
When source outputs don't match target inputs:
# User can insert transformer node:
LLMInference → [LLMTransformer] → DeploymentPreCheck
# Transformer:
# - Input: LLMInference.output (string)
# - Output: DeploymentPreCheck.requirements (object)
# - Action: Call LLM to convert format
Testing Incompatibility Detection
Test Case 1: Terminal Activity as Source
source := db.WorkflowNode{ID: "n1", Type: "notify-status", Label: "Notify"}
target := db.WorkflowNode{ID: "n2", Type: "clone-repo", Label: "Clone"}
warnings := CheckConnectionCompatibility(source, target)
// Should warn: NotifyStatusActivity produces no outputs
Test Case 2: Type Mismatch
source := db.WorkflowNode{ID: "n1", Type: "llm-inference", ...}
target := db.WorkflowNode{ID: "n2", Type: "deployment-check", ...}
warnings := CheckConnectionCompatibility(source, target)
// Should warn: string output ≠ object input
Test Case 3: Disconnected Node
nodes := []db.WorkflowNode{n1, n2, n3}
edges := []db.WorkflowEdge{{Source: "n1", Target: "n2"}}
disconnected := IdentifyDisconnectedNodes(nodes, edges)
// Should return ["n3"]
Future Enhancements
-
Automatic Transformer Insertion
- Detect incompatibilities
- Auto-suggest LLM transformer nodes
- Chain transformers if needed
-
Confidence Scoring
- Increase when types match perfectly
- Decrease for semantic mismatches
- Factor in activity dependencies
-
Learning from History
- Track successful workflows
- Remember user edits to suggestions
- Improve LLM prompts over time
-
Multi-Path Analysis
- Suggest multiple connection topologies
- Show cost/efficiency of each
- Rank by execution time/cost
-
Dry-Run Validation
- Execute suggested workflow in simulation
- Catch runtime errors early
- Show data flow through each node