LeroyDyer's picture
Update app.py
109dec0 verified
from dataclasses import asdict, dataclass, field
import os
import pickle
from typing import Dict, List, Optional, Any
import gradio as gr
import json
import tempfile
import asyncio
import uuid
from dataclasses import dataclass, asdict
from typing import List, Dict, Optional, Any
from openai import AsyncOpenAI
#================================================================================
# Unified Hierarchical Component definitions for UML and Object-Oriented Design
# as well as agentic design
COMPONENT_HIERARCHY = {
"HIGH_LEVEL": {
"CLASS": {
"description": "Class diagrams and object-oriented design elements",
"color": "#4CAF50",
"icon": "📋",
"shape": "rect",
"sub_components": ["ATTRIBUTE", "METHOD", "RELATIONSHIP", "INHERITANCE", "COMPOSITION", "AGGREGATION"]
},
"USECASE": {
"description": "Use case diagrams and system interactions",
"color": "#2196F3",
"icon": "🎯",
"shape": "ellipse",
"sub_components": ["ACTOR", "USECASE", "INCLUDE", "EXTEND", "SYSTEM_BOUNDARY"]
},
"SEQUENCE": {
"description": "Sequence diagrams and message flows",
"color": "#FF9800",
"icon": "⚡",
"shape": "rect",
"sub_components": ["LIFELINE", "MESSAGE", "ACTIVATION", "ALT_FRAME", "LOOP_FRAME"]
},
"ACTIVITY": {
"description": "Activity diagrams and workflow processes",
"color": "#00BCD4",
"icon": "🔄",
"shape": "diamond",
"sub_components": ["ACTION", "DECISION", "MERGE", "FORK", "JOIN", "START_NODE", "END_NODE"]
},
"STATE": {
"description": "State machine diagrams and object states",
"color": "#9C27B0",
"icon": "🔄",
"shape": "ellipse",
"sub_components": ["STATE", "TRANSITION", "ENTRY_ACTION", "EXIT_ACTION", "INTERNAL_ACTION"]
},
"OBJECT": {
"description": "Object diagrams and runtime instances",
"color": "#795548",
"icon": "📦",
"shape": "rect",
"sub_components": ["INSTANCE", "LINK", "OBJECT_ATTRIBUTE", "OBJECT_VALUE"]
},
"COMPONENT": {
"description": "Component diagrams and system architecture",
"color": "#009688",
"icon": "🧩",
"shape": "rect",
"sub_components": ["COMPONENT", "INTERFACE", "PORT", "ARTIFACT", "DEPENDENCY"]
},
"DEPLOYMENT": {
"description": "Deployment diagrams and physical architecture",
"color": "#FF5722",
"icon": "🌐",
"shape": "rect",
"sub_components": ["NODE", "ARTIFACT", "DEVICE", "EXECUTION_ENVIRONMENT", "COMMUNICATION_PATH"]
},
"PACKAGE": {
"description": "Package diagrams and module organization",
"color": "#E91E63",
"icon": "📦",
"shape": "folder",
"sub_components": ["PACKAGE", "SUBPACKAGE", "PACKAGE_DEPENDENCY", "IMPORT", "ACCESS"]
},
"SYSTEM": {
"description": "Top-level system architecture containing all components",
"color": "#333333",
"icon": "🌐",
"shape": "folder",
"sub_components": ["AGENT", "USER", "TOOL", "DATA", "PROCESSOR", "ROUTER", "INFRASTRUCTURE", "CONFIG"]
},
"AGENT": {
"description": "Autonomous reasoning and decision-making units",
"color": "#4CAF50",
"icon": "🤖",
"shape": "rect",
"sub_components": ["REASONING_AGENT", "ACTION_AGENT", "PLANNER_AGENT", "REACT_AGENT", "MULTI_AGENT"]
},
"USER": {
"description": "User interaction points and interfaces",
"color": "#9C27B0",
"icon": "👤",
"shape": "ellipse",
"sub_components": ["USER_INPUT", "USER_OUTPUT", "MULTIMODAL_INTERFACE"]
},
"TOOL": {
"description": "External functions and capabilities",
"color": "#795548",
"icon": "🔧",
"shape": "hexagon",
"sub_components": ["MCP_TOOL", "API_TOOL", "LOCAL_TOOL", "AGENT_TOOL", "FUNCTION_TOOL"]
},
"DATA": {
"description": "Data sources and storage systems",
"color": "#009688",
"icon": "💾",
"shape": "cylinder",
"sub_components": ["KNOWLEDGE_BASE", "VECTOR_DB", "DOCUMENT_STORE", "CACHE", "MEMORY"]
},
"PROCESSOR": {
"description": "Data processing and transformation units",
"color": "#2196F3",
"icon": "⚙️",
"shape": "rect",
"sub_components": ["QUERY_PROCESSOR", "CONTENT_RETRIEVAL", "PROMPT_TEMPLATE", "RESPONSE_FORMATTER"]
},
"ROUTER": {
"description": "Decision points and workflow routing",
"color": "#FF9800",
"icon": "🎯",
"shape": "diamond",
"sub_components": ["INTENT_DISCOVERY", "MODEL_SELECTOR", "WORKFLOW_ROUTER", "VALIDATOR"]
},
"INFRASTRUCTURE": {
"description": "System infrastructure and services",
"color": "#FF5722",
"icon": "🌐",
"shape": "rect",
"sub_components": ["PROVIDER", "MONITOR", "FALLBACK", "ORCHESTRATOR"]
},
}
}
# Complete Component Information with Configuration
COMPONENT_INFO = {
# ===================================
# CLASS: Class diagram elements
# ===================================
"CLASS": {
"description": "Class with attributes and methods",
"color": "#4CAF50",
"icon": "📋",
"shape": "rect",
"config_fields": {
"name": {"type": "text", "label": "Class Name", "default": "MyClass"},
"visibility": {"type": "dropdown", "label": "Visibility", "choices": ["public", "private", "protected"], "default": "public"},
"stereotype": {"type": "text", "label": "Stereotype (optional)", "default": ""},
"abstract": {"type": "boolean", "label": "Abstract Class", "default": False},
"documentation": {"type": "textarea", "label": "Documentation", "default": "Class description"},
}
},
"ATTRIBUTE": {
"shape": "rect",
"color": "#4CAF50",
"icon": "🏷️",
"description": "Class attribute with visibility and type",
"config_fields": {
"name": {"type": "text", "label": "Attribute Name", "default": "myAttribute"},
"type": {"type": "text", "label": "Type", "default": "String"},
"visibility": {"type": "dropdown", "label": "Visibility", "choices": ["+", "-", "#"], "default": "+"},
"static": {"type": "boolean", "label": "Static", "default": False},
"final": {"type": "boolean", "label": "Final/Constant", "default": False},
"default_value": {"type": "text", "label": "Default Value", "default": ""},
}
},
"METHOD": {
"shape": "rect",
"color": "#4CAF50",
"icon": "⚙️",
"description": "Class method with parameters and return type",
"config_fields": {
"name": {"type": "text", "label": "Method Name", "default": "myMethod"},
"return_type": {"type": "text", "label": "Return Type", "default": "void"},
"visibility": {"type": "dropdown", "label": "Visibility", "choices": ["+", "-", "#"], "default": "+"},
"static": {"type": "boolean", "label": "Static", "default": False},
"abstract": {"type": "boolean", "label": "Abstract", "default": False},
"parameters": {"type": "text", "label": "Parameters (comma-separated)", "default": ""},
}
},
"INHERITANCE": {
"shape": "line",
"color": "#4CAF50",
"icon": "🡅",
"description": "Inheritance relationship (generalization)",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Inheritance"},
"stereotype": {"type": "dropdown", "label": "Stereotype", "choices": ["<<extend>>", "<<implement>>", ""], "default": ""},
}
},
"COMPOSITION": {
"shape": "line",
"color": "#4CAF50",
"icon": "🔗",
"description": "Composition relationship (strong ownership)",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Composition"},
"multiplicity_source": {"type": "text", "label": "Source Multiplicity", "default": "1"},
"multiplicity_target": {"type": "text", "label": "Target Multiplicity", "default": "1..*"},
}
},
"AGGREGATION": {
"shape": "line",
"color": "#4CAF50",
"icon": "🔗",
"description": "Aggregation relationship (weak ownership)",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Aggregation"},
"multiplicity_source": {"type": "text", "label": "Source Multiplicity", "default": "0..1"},
"multiplicity_target": {"type": "text", "label": "Target Multiplicity", "default": "0..*"},
}
},
"ASSOCIATION": {
"shape": "line",
"color": "#4CAF50",
"icon": "🔗",
"description": "Association relationship between classes",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Association"},
"direction": {"type": "dropdown", "label": "Direction", "choices": ["bidirectional", "source_to_target", "target_to_source"], "default": "bidirectional"},
"multiplicity_source": {"type": "text", "label": "Source Multiplicity", "default": "1"},
"multiplicity_target": {"type": "text", "label": "Target Multiplicity", "default": "1"},
}
},
# ===================================
# USECASE: Use case diagram elements
# ===================================
"ACTOR": {
"shape": "ellipse",
"color": "#2196F3",
"icon": "👤",
"description": "External entity that interacts with the system",
"config_fields": {
"name": {"type": "text", "label": "Actor Name", "default": "User"},
"stereotype": {"type": "text", "label": "Stereotype (optional)", "default": ""},
"description": {"type": "textarea", "label": "Description", "default": "Actor description"},
}
},
"USECASE": {
"shape": "ellipse",
"color": "#2196F3",
"icon": "🎯",
"description": "Specific functionality provided by the system",
"config_fields": {
"name": {"type": "text", "label": "Use Case Name", "default": "Login"},
"description": {"type": "textarea", "label": "Description", "default": "Use case description"},
"preconditions": {"type": "textarea", "label": "Preconditions", "default": ""},
"postconditions": {"type": "textarea", "label": "Postconditions", "default": ""},
}
},
"INCLUDE": {
"shape": "line",
"color": "#2196F3",
"icon": "➡️",
"description": "Include relationship between use cases",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Include"},
"stereotype": {"type": "text", "label": "<<include>>", "default": "<<include>>"},
}
},
"EXTEND": {
"shape": "line",
"color": "#2196F3",
"icon": "➡️",
"description": "Extend relationship between use cases",
"config_fields": {
"name": {"type": "text", "label": "Relationship Name", "default": "Extend"},
"stereotype": {"type": "text", "label": "<<extend>>", "default": "<<extend>>"},
"extension_point": {"type": "text", "label": "Extension Point", "default": ""},
}
},
"SYSTEM_BOUNDARY": {
"shape": "rect",
"color": "#2196F3",
"icon": "🏗️",
"description": "Boundary of the system being modeled",
"config_fields": {
"name": {"type": "text", "label": "System Name", "default": "MySystem"},
"description": {"type": "textarea", "label": "System Description", "default": "System boundary"},
}
},
# ===================================
# SEQUENCE: Sequence diagram elements
# ===================================
"LIFELINE": {
"shape": "rect",
"color": "#FF9800",
"icon": "⁞",
"description": "Lifeline representing an object instance",
"config_fields": {
"name": {"type": "text", "label": "Object Name", "default": "object1"},
"class_name": {"type": "text", "label": "Class Name", "default": "MyClass"},
"stereotype": {"type": "text", "label": "Stereotype (optional)", "default": ""},
}
},
"MESSAGE": {
"shape": "line",
"color": "#FF9800",
"icon": "➡️",
"description": "Message sent between lifelines",
"config_fields": {
"name": {"type": "text", "label": "Message Name", "default": "request()"},
"message_type": {"type": "dropdown", "label": "Type", "choices": ["synchronous", "asynchronous", "return"], "default": "synchronous"},
"stereotype": {"type": "text", "label": "Stereotype (optional)", "default": ""},
}
},
"ACTIVATION": {
"shape": "rect",
"color": "#FF9800",
"icon": "║",
"description": "Activation box showing object is active",
"config_fields": {
"name": {"type": "text", "label": "Activation Name", "default": "Active"},
"duration": {"type": "number", "label": "Duration (time units)", "default": 10},
}
},
"ALT_FRAME": {
"shape": "rect",
"color": "#FF9800",
"icon": "⎇",
"description": "Alternative frame for conditional flows",
"config_fields": {
"name": {"type": "text", "label": "Frame Name", "default": "alt"},
"condition": {"type": "text", "label": "Condition", "default": "[condition]"},
"description": {"type": "textarea", "label": "Description", "default": "Alternative flow"},
}
},
"LOOP_FRAME": {
"shape": "rect",
"color": "#FF9800",
"icon": "↻",
"description": "Loop frame for repetitive flows",
"config_fields": {
"name": {"type": "text", "label": "Frame Name", "default": "loop"},
"condition": {"type": "text", "label": "Condition", "default": "[loop condition]"},
"description": {"type": "textarea", "label": "Description", "default": "Loop flow"},
}
},
# ===================================
# ACTIVITY: Activity diagram elements
# ===================================
"ACTION": {
"shape": "rect",
"color": "#00BCD4",
"icon": "⚡",
"description": "Action or activity node",
"config_fields": {
"name": {"type": "text", "label": "Action Name", "default": "Do Something"},
"description": {"type": "textarea", "label": "Description", "default": "Action description"},
"cost": {"type": "number", "label": "Cost/Time", "default": 0},
}
},
"DECISION": {
"shape": "diamond",
"color": "#00BCD4",
"icon": "❓",
"description": "Decision point with multiple outgoing flows",
"config_fields": {
"name": {"type": "text", "label": "Decision Name", "default": "Decision"},
"description": {"type": "textarea", "label": "Description", "default": "Decision point"},
}
},
"MERGE": {
"shape": "diamond",
"color": "#00BCD4",
"icon": "🡇",
"description": "Merge point combining multiple flows",
"config_fields": {
"name": {"type": "text", "label": "Merge Name", "default": "Merge"},
"description": {"type": "textarea", "label": "Description", "default": "Merge point"},
}
},
"FORK": {
"shape": "rect",
"color": "#00BCD4",
"icon": "🡇",
"description": "Fork point for parallel flows",
"config_fields": {
"name": {"type": "text", "label": "Fork Name", "default": "Fork"},
"description": {"type": "textarea", "label": "Description", "default": "Parallel fork"},
}
},
"JOIN": {
"shape": "rect",
"color": "#00BCD4",
"icon": "🡅",
"description": "Join point for parallel flows",
"config_fields": {
"name": {"type": "text", "label": "Join Name", "default": "Join"},
"description": {"type": "textarea", "label": "Description", "default": "Parallel join"},
}
},
"START_NODE": {
"shape": "circle",
"color": "#00BCD4",
"icon": "🟢",
"description": "Start node of activity diagram",
"config_fields": {
"name": {"type": "text", "label": "Start", "default": "Start"},
}
},
"END_NODE": {
"shape": "circle",
"color": "#00BCD4",
"icon": "🔴",
"description": "End node of activity diagram",
"config_fields": {
"name": {"type": "text", "label": "End", "default": "End"},
}
},
# ===================================
# STATE: State machine elements
# ===================================
"STATE": {
"shape": "rect",
"color": "#9C27B0",
"icon": "🔄",
"description": "State in a state machine",
"config_fields": {
"name": {"type": "text", "label": "State Name", "default": "InitialState"},
"entry_action": {"type": "textarea", "label": "Entry Action", "default": ""},
"exit_action": {"type": "textarea", "label": "Exit Action", "default": ""},
"internal_action": {"type": "textarea", "label": "Internal Action", "default": ""},
}
},
"TRANSITION": {
"shape": "line",
"color": "#9C27B0",
"icon": "➡️",
"description": "Transition between states",
"config_fields": {
"name": {"type": "text", "label": "Transition Name", "default": "Transition"},
"trigger": {"type": "text", "label": "Trigger", "default": "event"},
"guard_condition": {"type": "text", "label": "Guard Condition", "default": "[condition]"},
"action": {"type": "text", "label": "Action", "default": ""},
}
},
# ===================================
# OBJECT: Object diagram elements
# ===================================
"INSTANCE": {
"shape": "rect",
"color": "#795548",
"icon": "📦",
"description": "Object instance in runtime",
"config_fields": {
"name": {"type": "text", "label": "Instance Name", "default": "object1"},
"class_name": {"type": "text", "label": "Class Name", "default": "MyClass"},
"values": {"type": "textarea", "label": "Attribute Values", "default": "attr1=value1"},
}
},
"LINK": {
"shape": "line",
"color": "#795548",
"icon": "🔗",
"description": "Link between object instances",
"config_fields": {
"name": {"type": "text", "label": "Link Name", "default": "Link"},
"association": {"type": "text", "label": "Association Name", "default": ""},
}
},
# ===================================
# COMPONENT: Component diagram elements
# ===================================
"COMPONENT": {
"shape": "rect",
"color": "#009688",
"icon": "🧩",
"description": "Software component with interfaces",
"config_fields": {
"name": {"type": "text", "label": "Component Name", "default": "MyComponent"},
"type": {"type": "text", "label": "Component Type", "default": "Library"},
"version": {"type": "text", "label": "Version", "default": "1.0"},
}
},
"INTERFACE": {
"shape": "ellipse",
"color": "#009688",
"icon": "🔌",
"description": "Component interface",
"config_fields": {
"name": {"type": "text", "label": "Interface Name", "default": "MyInterface"},
"type": {"type": "dropdown", "label": "Type", "choices": ["provided", "required"], "default": "provided"},
}
},
"DEPENDENCY": {
"shape": "line",
"color": "#009688",
"icon": "➡️",
"description": "Dependency relationship between components",
"config_fields": {
"name": {"type": "text", "label": "Dependency Name", "default": "Dependency"},
"stereotype": {"type": "text", "label": "Stereotype", "default": "<<use>>"},
}
},
# ===================================
# DEPLOYMENT: Deployment diagram elements
# ===================================
"NODE": {
"shape": "rect",
"color": "#FF5722",
"icon": "🖥️",
"description": "Physical node or device",
"config_fields": {
"name": {"type": "text", "label": "Node Name", "default": "Server"},
"node_type": {"type": "text", "label": "Node Type", "default": "Device"},
"hardware": {"type": "text", "label": "Hardware", "default": "x86_64"},
}
},
"ARTIFACT": {
"shape": "rect",
"color": "#FF5722",
"icon": "📦",
"description": "Deployable software artifact",
"config_fields": {
"name": {"type": "text", "label": "Artifact Name", "default": "Application.jar"},
"artifact_type": {"type": "text", "label": "Type", "default": "File"},
"version": {"type": "text", "label": "Version", "default": "1.0"},
}
},
"COMMUNICATION_PATH": {
"shape": "line",
"color": "#FF5722",
"icon": "📡",
"description": "Communication path between nodes",
"config_fields": {
"name": {"type": "text", "label": "Path Name", "default": "Network"},
"protocol": {"type": "text", "label": "Protocol", "default": "TCP/IP"},
}
},
# ===================================
# PACKAGE: Package diagram elements
# ===================================
"PACKAGE": {
"shape": "folder",
"color": "#E91E63",
"icon": "📦",
"description": "Package containing other elements",
"config_fields": {
"name": {"type": "text", "label": "Package Name", "default": "MyPackage"},
"namespace": {"type": "text", "label": "Namespace", "default": "com.example"},
"description": {"type": "textarea", "label": "Description", "default": "Package description"},
}
},
"PACKAGE_DEPENDENCY": {
"shape": "line",
"color": "#E91E63",
"icon": "➡️",
"description": "Dependency between packages",
"config_fields": {
"name": {"type": "text", "label": "Dependency Name", "default": "Dependency"},
"stereotype": {"type": "text", "label": "Stereotype", "default": "<<import>>"},
}
},
# SYSTEM
"SYSTEM": {
"description": "Top-level system architecture containing all components",
"color": "#333333",
"icon": "🌐",
"shape": "folder",
"config_fields": {
"name": {"type": "text", "label": "System Name", "default": "AgenticSystem"},
"description": {"type": "textarea", "label": "System Description", "default": "Multi-agent AI system"},
"version": {"type": "text", "label": "System Version", "default": "1.0.0"},
}
},
# AGENT
"AGENT": {
"description": "Autonomous reasoning and decision-making units",
"color": "#4CAF50",
"icon": "🤖",
"shape": "rect",
"config_fields": {
"name": {"type": "text", "label": "Agent Name", "default": "MyAgent"},
"role": {"type": "textarea", "label": "Role Description", "default": "Assistant"},
"system_prompt": {"type": "textarea", "label": "System Prompt", "default": "You are a helpful assistant"},
}
},
"REASONING_AGENT": {
"shape": "rect",
"color": "#4CAF50",
"icon": "🧠",
"description": "Performs complex reasoning tasks using chain-of-thought approaches",
"config_fields": {
"name": {"type": "text", "label": "Agent Name", "default": "ReasoningAgent"},
"reasoning_type": {"type": "dropdown", "label": "Reasoning Type", "choices": ["chain_of_thought", "tree_of_thought"], "default": "chain_of_thought"},
"max_steps": {"type": "number", "label": "Max Reasoning Steps", "default": 5},
}
},
"ACTION_AGENT": {
"shape": "rect",
"color": "#4CAF50",
"icon": "⚡",
"description": "Executes actions using available tools",
"config_fields": {
"name": {"type": "text", "label": "Agent Name", "default": "ActionAgent"},
"retry_attempts": {"type": "number", "label": "Retry Attempts", "default": 3},
"timeout_seconds": {"type": "number", "label": "Timeout Seconds", "default": 30},
}
},
"PLANNER_AGENT": {
"shape": "rect",
"color": "#4CAF50",
"icon": "📋",
"description": "Creates multi-step plans to achieve goals",
"config_fields": {
"name": {"type": "text", "label": "Agent Name", "default": "PlannerAgent"},
"max_steps": {"type": "number", "label": "Max Plan Steps", "default": 20},
}
},
"REACT_AGENT": {
"shape": "rect",
"color": "#4CAF50",
"icon": "🔄",
"description": "Implements ReAct (Reason + Act) framework",
"config_fields": {
"name": {"type": "text", "label": "Agent Name", "default": "ReActAgent"},
"max_iterations": {"type": "number", "label": "Max Iterations", "default": 10},
}
},
"MULTI_AGENT": {
"shape": "rect",
"color": "#4CAF50",
"icon": "👥",
"description": "Coordinates multiple specialized agents",
"config_fields": {
"name": {"type": "text", "label": "System Name", "default": "MultiAgentSystem"},
"max_concurrent_agents": {"type": "number", "label": "Max Concurrent Agents", "default": 10},
}
},
# USER
"USER": {
"description": "User interaction points and interfaces",
"color": "#9C27B0",
"icon": "👤",
"shape": "ellipse",
"config_fields": {
"name": {"type": "text", "label": "User Interface", "default": "UserInterface"},
}
},
"USER_INPUT": {
"shape": "ellipse",
"color": "#9C27B0",
"icon": "⌨️",
"description": "Accepts and validates user input",
"config_fields": {
"name": {"type": "text", "label": "Input Name", "default": "UserInput"},
"input_types": {"type": "dropdown", "label": "Input Types", "choices": ["text", "voice", "gesture"], "default": "text"},
"max_length": {"type": "number", "label": "Max Input Length", "default": 1000},
}
},
"USER_OUTPUT": {
"shape": "ellipse",
"color": "#9C27B0",
"icon": "🔊",
"description": "Formats responses for user consumption",
"config_fields": {
"name": {"type": "text", "label": "Output Name", "default": "UserOutput"},
"output_format": {"type": "dropdown", "label": "Output Format", "choices": ["text", "audio", "visual"], "default": "text"},
}
},
"MULTIMODAL_INTERFACE": {
"shape": "ellipse",
"color": "#9C27B0",
"icon": "🖼️",
"description": "Handles multiple input/output modalities",
"config_fields": {
"name": {"type": "text", "label": "Interface Name", "default": "MultimodalInterface"},
"supported_modalities": {"type": "dropdown", "label": "Modalities", "choices": ["text", "image", "audio", "video"], "default": "text"},
}
},
# TOOL - FIXED: All tools now have proper config_fields
"TOOL": {
"description": "External functions and capabilities",
"color": "#795548",
"icon": "🔧",
"shape": "hexagon",
"config_fields": {
"name": {"type": "text", "label": "Tool Name", "default": "MyTool"},
"description": {"type": "textarea", "label": "Tool Description", "default": "Tool description"},
}
},
"MCP_TOOL": {
"shape": "hexagon",
"color": "#795548",
"icon": "🔌",
"description": "Model Context Protocol server interface",
"config_fields": {
"name": {"type": "text", "label": "Tool Name", "default": "MCPTool"},
"server_command": {"type": "text", "label": "Server Command", "default": "npx"},
"server_args": {"type": "text", "label": "Server Arguments", "default": ""},
}
},
"API_TOOL": {
"shape": "hexagon",
"color": "#795548",
"icon": "🔗",
"description": "Wraps external REST/gRPC APIs",
"config_fields": {
"name": {"type": "text", "label": "API Tool Name", "default": "APITool"},
"base_url": {"type": "text", "label": "Base URL", "default": "https://api.example.com"},
"timeout": {"type": "number", "label": "Timeout (seconds)", "default": 30},
}
},
"LOCAL_TOOL": {
"shape": "hexagon",
"color": "#795548",
"icon": "💻",
"description": "Locally executed utility functions",
"config_fields": {
"name": {"type": "text", "label": "Local Tool Name", "default": "LocalTool"},
"allowed_operations": {"type": "dropdown", "label": "Operations", "choices": ["file", "math", "system"], "default": "file"},
}
},
"AGENT_TOOL": {
"shape": "hexagon",
"color": "#795548",
"icon": "🛠️",
"description": "Allows one agent to act as a tool",
"config_fields": {
"name": {"type": "text", "label": "Agent Tool Name", "default": "AgentTool"},
"max_concurrent_calls": {"type": "number", "label": "Max Concurrent Calls", "default": 5},
}
},
"FUNCTION_TOOL": {
"shape": "hexagon",
"color": "#795548",
"icon": "🧮",
"description": "Generic callable function",
"config_fields": {
"name": {"type": "text", "label": "Function Tool Name", "default": "FunctionTool"},
"max_params": {"type": "number", "label": "Max Parameters", "default": 10},
}
},
# DATA
"DATA": {
"description": "Data sources and storage systems",
"color": "#009688",
"icon": "💾",
"shape": "cylinder",
"config_fields": {
"name": {"type": "text", "label": "Data Store", "default": "DataStore"},
}
},
"KNOWLEDGE_BASE": {
"shape": "cylinder",
"color": "#009688",
"icon": "📘",
"description": "Curated domain-specific knowledge",
"config_fields": {
"name": {"type": "text", "label": "Knowledge Base Name", "default": "MyKnowledgeBase"},
"max_facts": {"type": "number", "label": "Max Facts", "default": 10000},
}
},
"VECTOR_DB": {
"shape": "cylinder",
"color": "#009688",
"icon": "🔍",
"description": "Embedding-based semantic search database",
"config_fields": {
"name": {"type": "text", "label": "Vector DB Name", "default": "MyVectorDB"},
"embedding_model": {"type": "text", "label": "Embedding Model", "default": "all-MiniLM-L6-v2"},
"max_documents": {"type": "number", "label": "Max Documents", "default": 10000},
}
},
"DOCUMENT_STORE": {
"shape": "cylinder",
"color": "#009688",
"icon": "🗂️",
"description": "Raw document repository",
"config_fields": {
"name": {"type": "text", "label": "Document Store Name", "default": "DocumentStore"},
"supported_formats": {"type": "dropdown", "label": "Supported Formats", "choices": [".pdf", ".txt", ".docx"], "default": ".pdf"},
}
},
"CACHE": {
"shape": "cylinder",
"color": "#009688",
"icon": "⏱️",
"description": "Temporary fast-access storage",
"config_fields": {
"name": {"type": "text", "label": "Cache Name", "default": "ResponseCache"},
"max_size": {"type": "number", "label": "Max Cache Size", "default": 1000},
"ttl_seconds": {"type": "number", "label": "TTL (seconds)", "default": 3600},
}
},
"MEMORY": {
"shape": "cylinder",
"color": "#009588",
"icon": "🧠",
"description": "Short-term context memory",
"config_fields": {
"name": {"type": "text", "label": "Memory Name", "default": "SessionMemory"},
"max_context_length": {"type": "number", "label": "Max Context Length", "default": 2000},
}
},
# PROCESSOR
"PROCESSOR": {
"description": "Data processing and transformation units",
"color": "#2196F3",
"icon": "⚙️",
"shape": "rect",
"config_fields": {
"name": {"type": "text", "label": "Processor", "default": "DataProcessor"},
}
},
"QUERY_PROCESSOR": {
"shape": "rect",
"color": "#2196F3",
"icon": "🔎",
"description": "Parses and enriches queries",
"config_fields": {
"name": {"type": "text", "label": "Processor Name", "default": "QueryProcessor"},
"max_query_length": {"type": "number", "label": "Max Query Length", "default": 1000},
}
},
"CONTENT_RETRIEVAL": {
"shape": "rect",
"color": "#2196F3",
"icon": "📤",
"description": "Fetches relevant content from data stores",
"config_fields": {
"name": {"type": "text", "label": "Retrieval Name", "default": "ContentRetrieval"},
"top_k": {"type": "number", "label": "Top K Results", "default": 5},
}
},
"PROMPT_TEMPLATE": {
"shape": "rect",
"color": "#2196F3",
"icon": "📝",
"description": "Template-based prompt construction",
"config_fields": {
"name": {"type": "text", "label": "Template Name", "default": "MyPromptTemplate"},
"template_content": {"type": "textarea", "label": "Template Content", "default": "You are a helpful assistant. User: {query}"},
}
},
"RESPONSE_FORMATTER": {
"shape": "rect",
"color": "#2196F3",
"icon": "📄",
"description": "Structures final output",
"config_fields": {
"name": {"type": "text", "label": "Formatter Name", "default": "ResponseFormatter"},
"default_format": {"type": "dropdown", "label": "Default Format", "choices": ["json", "xml", "markdown", "text"], "default": "json"},
}
},
# ROUTER
"ROUTER": {
"description": "Decision points and workflow routing",
"color": "#FF9800",
"icon": "🎯",
"shape": "diamond",
"config_fields": {
"name": {"type": "text", "label": "Router", "default": "WorkflowRouter"},
}
},
"INTENT_DISCOVERY": {
"shape": "diamond",
"color": "#FF9800",
"icon": "🎯",
"description": "Identifies user intent",
"config_fields": {
"name": {"type": "text", "label": "Intent Discovery Name", "default": "IntentDiscovery"},
"fallback_intent": {"type": "text", "label": "Fallback Intent", "default": "unknown"},
}
},
"MODEL_SELECTOR": {
"shape": "diamond",
"color": "#FF9800",
"icon": "🧠",
"description": "Selects appropriate model",
"config_fields": {
"name": {"type": "text", "label": "Model Selector Name", "default": "ModelSelector"},
"selection_strategy": {"type": "dropdown", "label": "Selection Strategy", "choices": ["performance", "cost", "latency"], "default": "performance"},
}
},
"WORKFLOW_ROUTER": {
"shape": "diamond",
"color": "#FF9800",
"icon": "🔄",
"description": "Routes requests through workflows",
"config_fields": {
"name": {"type": "text", "label": "Workflow Router Name", "default": "WorkflowRouter"},
"max_concurrent_workflows": {"type": "number", "label": "Max Concurrent", "default": 100},
}
},
"VALIDATOR": {
"shape": "diamond",
"color": "#FF9800",
"icon": "✅",
"description": "Validates inputs and outputs",
"config_fields": {
"name": {"type": "text", "label": "Validator Name", "default": "DataValidator"},
"error_handling": {"type": "dropdown", "label": "Error Handling", "choices": ["strict", "lenient", "adaptive"], "default": "adaptive"},
}
},
# INFRASTRUCTURE
"INFRASTRUCTURE": {
"description": "System infrastructure and services",
"color": "#FF5722",
"icon": "🌐",
"shape": "rect",
"config_fields": {
"name": {"type": "text", "label": "Infrastructure", "default": "SystemInfrastructure"},
}
},
"PROVIDER": {
"shape": "rect",
"color": "#FF5722",
"icon": "🌐",
"description": "API connection to LLM service",
"config_fields": {
"name": {"type": "text", "label": "Provider Name", "default": "LocalLM"},
"provider_type": {"type": "dropdown", "label": "Provider Type", "choices": ["openai", "anthropic", "local"], "default": "local"},
"base_url": {"type": "text", "label": "Base URL", "default": "http://localhost:1234/v1"},
"model": {"type": "text", "label": "Model", "default": "qwen3-0.6b"},
}
},
"MONITOR": {
"shape": "rect",
"color": "#FF5722",
"icon": "📊",
"description": "Tracks system performance",
"config_fields": {
"name": {"type": "text", "label": "Monitor Name", "default": "SystemMonitor"},
"metrics_retention_hours": {"type": "number", "label": "Metrics Retention (hours)", "default": 24},
}
},
"FALLBACK": {
"shape": "rect",
"color": "#FF5722",
"icon": "🔄",
"description": "Provides alternative execution paths",
"config_fields": {
"name": {"type": "text", "label": "Fallback Name", "default": "FallbackHandler"},
"max_fallback_attempts": {"type": "number", "label": "Max Attempts", "default": 3},
}
},
"ORCHESTRATOR": {
"shape": "rect",
"color": "#FF5722",
"icon": "🎬",
"description": "Coordinates complex multi-step processes",
"config_fields": {
"name": {"type": "text", "label": "Orchestrator Name", "default": "WorkflowOrchestrator"},
"max_workflow_steps": {"type": "number", "label": "Max Steps", "default": 100},
}
},
# CONFIG
"CONFIG": {
"shape": "document",
"color": "#607D8B",
"icon": "⚙️",
"description": "System configuration management",
"config_fields": {
"name": {"type": "text", "label": "Config Name", "default": "SystemConfig"},
"environment": {"type": "dropdown", "label": "Environment", "choices": ["development", "staging", "production"], "default": "development"},
}
},
}
# Enhanced Example workflows
EXAMPLE_WORKFLOWS = {
"E-Commerce Class Diagram": {
"description": "Object-oriented design for e-commerce system",
"nodes": [
{"id": "user", "type": "CLASS", "x": 100, "y": 100},
{"id": "product", "type": "CLASS", "x": 300, "y": 100},
{"id": "order", "type": "CLASS", "x": 500, "y": 100},
{"id": "payment", "type": "CLASS", "x": 700, "y": 100},
{"id": "cart", "type": "CLASS", "x": 300, "y": 300},
{"id": "inventory", "type": "CLASS", "x": 500, "y": 300}
],
"connections": [
{"from": "user", "to": "order", "type": "AGGREGATION"},
{"from": "order", "to": "product", "type": "COMPOSITION"},
{"from": "order", "to": "payment", "type": "AGGREGATION"},
{"from": "user", "to": "cart", "type": "AGGREGATION"},
{"from": "cart", "to": "product", "type": "AGGREGATION"},
{"from": "product", "to": "inventory", "type": "AGGREGATION"}
]
},
"User Authentication Use Case": {
"description": "Use case diagram for user authentication system",
"nodes": [
{"id": "user", "type": "ACTOR", "x": 50, "y": 200},
{"id": "admin", "type": "ACTOR", "x": 50, "y": 300},
{"id": "login", "type": "USECASE", "x": 250, "y": 150},
{"id": "register", "type": "USECASE", "x": 250, "y": 250},
{"id": "reset_password", "type": "USECASE", "x": 250, "y": 350},
{"id": "admin_panel", "type": "USECASE", "x": 450, "y": 300},
{"id": "system_boundary", "type": "SYSTEM_BOUNDARY", "x": 150, "y": 100}
],
"connections": [
{"from": "user", "to": "login"},
{"from": "user", "to": "register"},
{"from": "user", "to": "reset_password"},
{"from": "admin", "to": "admin_panel"},
{"from": "admin", "to": "login"},
{"from": "login", "to": "reset_password", "type": "EXTEND"}
]
},
"Order Processing Sequence": {
"description": "Sequence diagram for order processing workflow",
"nodes": [
{"id": "customer", "type": "LIFELINE", "x": 100, "y": 50},
{"id": "ui", "type": "LIFELINE", "x": 250, "y": 50},
{"id": "order_service", "type": "LIFELINE", "x": 400, "y": 50},
{"id": "payment_service", "type": "LIFELINE", "x": 550, "y": 50},
{"id": "inventory_service", "type": "LIFELINE", "x": 700, "y": 50}
],
"connections": [
{"from": "customer", "to": "ui", "type": "MESSAGE", "label": "placeOrder()"},
{"from": "ui", "to": "order_service", "type": "MESSAGE", "label": "createOrder()"},
{"from": "order_service", "to": "inventory_service", "type": "MESSAGE", "label": "checkAvailability()"},
{"from": "inventory_service", "to": "order_service", "type": "MESSAGE", "label": "availabilityConfirmed()"},
{"from": "order_service", "to": "payment_service", "type": "MESSAGE", "label": "processPayment()"},
{"from": "payment_service", "to": "order_service", "type": "MESSAGE", "label": "paymentConfirmed()"},
{"from": "order_service", "to": "ui", "type": "MESSAGE", "label": "orderConfirmed()"},
{"from": "ui", "to": "customer", "type": "MESSAGE", "label": "showConfirmation()"}
]
},
"User State Machine": {
"description": "State machine for user account states",
"nodes": [
{"id": "pending", "type": "STATE", "x": 100, "y": 100},
{"id": "active", "type": "STATE", "x": 300, "y": 100},
{"id": "suspended", "type": "STATE", "x": 500, "y": 100},
{"id": "deleted", "type": "STATE", "x": 300, "y": 300},
{"id": "start", "type": "START_NODE", "x": 50, "y": 100},
{"id": "end", "type": "END_NODE", "x": 300, "y": 400}
],
"connections": [
{"from": "start", "to": "pending"},
{"from": "pending", "to": "active", "type": "TRANSITION", "label": "approveAccount()"},
{"from": "active", "to": "suspended", "type": "TRANSITION", "label": "suspendAccount()"},
{"from": "suspended", "to": "active", "type": "TRANSITION", "label": "activateAccount()"},
{"from": "active", "to": "deleted", "type": "TRANSITION", "label": "deleteAccount()"},
{"from": "suspended", "to": "deleted", "type": "TRANSITION", "label": "deleteAccount()"},
{"from": "deleted", "to": "end"}
]
},
"Document Management Activity": {
"description": "Activity diagram for document management process",
"nodes": [
{"id": "start", "type": "START_NODE", "x": 200, "y": 50},
{"id": "upload", "type": "ACTION", "x": 200, "y": 150},
{"id": "validate", "type": "DECISION", "x": 200, "y": 250},
{"id": "process", "type": "ACTION", "x": 200, "y": 350},
{"id": "store", "type": "ACTION", "x": 200, "y": 450},
{"id": "notify", "type": "ACTION", "x": 200, "y": 550},
{"id": "end", "type": "END_NODE", "x": 200, "y": 650},
{"id": "error", "type": "ACTION", "x": 400, "y": 350}
],
"connections": [
{"from": "start", "to": "upload"},
{"from": "upload", "to": "validate"},
{"from": "validate", "to": "process", "label": "[valid]"},
{"from": "validate", "to": "error", "label": "[invalid]"},
{"from": "error", "to": "end"},
{"from": "process", "to": "store"},
{"from": "store", "to": "notify"},
{"from": "notify", "to": "end"}
]
},
"Simple Chat Agent": {
"description": "Basic conversational agent with single LLM call",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 150, "y": 200},
{"id": "agent_1", "type": "REASONING_AGENT", "x": 400, "y": 200},
{"id": "provider_1", "type": "PROVIDER", "x": 650, "y": 200},
{"id": "output_1", "type": "USER_OUTPUT", "x": 900, "y": 200}
],
"connections": [
{"from": "user_1", "to": "agent_1"},
{"from": "agent_1", "to": "provider_1"},
{"from": "provider_1", "to": "output_1"}
]
},
"Intent-Driven Routing": {
"description": "Routes to specialized agents based on user intent",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 150, "y": 300},
{"id": "intent_1", "type": "INTENT_DISCOVERY", "x": 400, "y": 300},
{"id": "agent_1", "type": "REASONING_AGENT", "x": 650, "y": 150},
{"id": "agent_2", "type": "ACTION_AGENT", "x": 650, "y": 450},
{"id": "output_1", "type": "USER_OUTPUT", "x": 900, "y": 300}
],
"connections": [
{"from": "user_1", "to": "intent_1"},
{"from": "intent_1", "to": "agent_1"},
{"from": "intent_1", "to": "agent_2"},
{"from": "agent_1", "to": "output_1"},
{"from": "agent_2", "to": "output_1"}
]
},
"RAG Pipeline": {
"description": "Retrieval-Augmented Generation with context",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 100, "y": 250},
{"id": "query_1", "type": "QUERY_PROCESSOR", "x": 250, "y": 250},
{"id": "content_1", "type": "CONTENT_RETRIEVAL", "x": 400, "y": 250},
{"id": "prompt_1", "type": "PROMPT_TEMPLATE", "x": 550, "y": 250},
{"id": "agent_1", "type": "REASONING_AGENT", "x": 700, "y": 250},
{"id": "output_1", "type": "USER_OUTPUT", "x": 900, "y": 250}
],
"connections": [
{"from": "user_1", "to": "query_1"},
{"from": "query_1", "to": "content_1"},
{"from": "content_1", "to": "prompt_1"},
{"from": "prompt_1", "to": "agent_1"},
{"from": "agent_1", "to": "output_1"}
]
},
"Multi-Agent with Tools": {
"description": "Coordinated agents with tool access and validation",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 100, "y": 300},
{"id": "intent_1", "type": "INTENT_DISCOVERY", "x": 280, "y": 300},
{"id": "agent_1", "type": "REASONING_AGENT", "x": 460, "y": 150},
{"id": "agent_2", "type": "ACTION_AGENT", "x": 460, "y": 450},
{"id": "tool_1", "type": "MCP_TOOL", "x": 640, "y": 150},
{"id": "tool_2", "type": "API_TOOL", "x": 640, "y": 450},
{"id": "validator_1", "type": "VALIDATOR", "x": 820, "y": 300},
{"id": "output_1", "type": "USER_OUTPUT", "x": 980, "y": 300}
],
"connections": [
{"from": "user_1", "to": "intent_1"},
{"from": "intent_1", "to": "agent_1"},
{"from": "intent_1", "to": "agent_2"},
{"from": "agent_1", "to": "tool_1"},
{"from": "agent_2", "to": "tool_2"},
{"from": "tool_1", "to": "validator_1"},
{"from": "tool_2", "to": "validator_1"},
{"from": "validator_1", "to": "output_1"}
]
},
"Advanced RAG with Cache": {
"description": "Enhanced RAG with caching and monitoring",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 100, "y": 200},
{"id": "query_1", "type": "QUERY_PROCESSOR", "x": 250, "y": 200},
{"id": "cache_1", "type": "CACHE", "x": 400, "y": 100},
{"id": "knowledge_1", "type": "KNOWLEDGE_BASE", "x": 400, "y": 300},
{"id": "prompt_1", "type": "PROMPT_TEMPLATE", "x": 550, "y": 200},
{"id": "agent_1", "type": "REASONING_AGENT", "x": 700, "y": 200},
{"id": "monitor_1", "type": "MONITOR", "x": 850, "y": 100},
{"id": "output_1", "type": "USER_OUTPUT", "x": 850, "y": 300}
],
"connections": [
{"from": "user_1", "to": "query_1"},
{"from": "query_1", "to": "cache_1"},
{"from": "query_1", "to": "knowledge_1"},
{"from": "cache_1", "to": "prompt_1"},
{"from": "knowledge_1", "to": "prompt_1"},
{"from": "prompt_1", "to": "agent_1"},
{"from": "agent_1", "to": "monitor_1"},
{"from": "agent_1", "to": "output_1"}
]
},
"MCP Tool Agent": {
"description": "Agent using MCP tools for extended capabilities",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 100, "y": 250},
{"id": "agent_1", "type": "REACT_AGENT", "x": 300, "y": 250},
{"id": "mcp_tool_1", "type": "MCP_TOOL", "x": 500, "y": 150},
{"id": "mcp_tool_2", "type": "MCP_TOOL", "x": 500, "y": 350},
{"id": "memory_1", "type": "MEMORY", "x": 700, "y": 250},
{"id": "output_1", "type": "USER_OUTPUT", "x": 900, "y": 250}
],
"connections": [
{"from": "user_1", "to": "agent_1"},
{"from": "agent_1", "to": "mcp_tool_1"},
{"from": "agent_1", "to": "mcp_tool_2"},
{"from": "mcp_tool_1", "to": "agent_1"},
{"from": "mcp_tool_2", "to": "agent_1"},
{"from": "agent_1", "to": "memory_1"},
{"from": "agent_1", "to": "output_1"}
]
},
"Multi-Agent Planning System": {
"description": "Complex planning with multiple specialized agents",
"nodes": [
{"id": "user_1", "type": "USER_INPUT", "x": 100, "y": 300},
{"id": "planner_1", "type": "PLANNER_AGENT", "x": 300, "y": 300},
{"id": "reasoning_1", "type": "REASONING_AGENT", "x": 500, "y": 150},
{"id": "action_1", "type": "ACTION_AGENT", "x": 500, "y": 300},
{"id": "multi_1", "type": "MULTI_AGENT", "x": 500, "y": 450},
{"id": "orchestrator_1", "type": "ORCHESTRATOR", "x": 700, "y": 300},
{"id": "output_1", "type": "USER_OUTPUT", "x": 900, "y": 300}
],
"connections": [
{"from": "user_1", "to": "planner_1"},
{"from": "planner_1", "to": "orchestrator_1"},
{"from": "orchestrator_1", "to": "reasoning_1"},
{"from": "orchestrator_1", "to": "action_1"},
{"from": "orchestrator_1", "to": "multi_1"},
{"from": "reasoning_1", "to": "orchestrator_1"},
{"from": "action_1", "to": "orchestrator_1"},
{"from": "multi_1", "to": "orchestrator_1"},
{"from": "orchestrator_1", "to": "output_1"}
]
}
}
#================================================================================
# Combine all components
ALL_COMPONENTS = {}
for category, components in COMPONENT_HIERARCHY["HIGH_LEVEL"].items():
ALL_COMPONENTS[category] = components
for sub_comp in components.get('sub_components', []):
if sub_comp in COMPONENT_INFO:
ALL_COMPONENTS[sub_comp] = COMPONENT_INFO[sub_comp]
for comp_name, comp_info in COMPONENT_INFO.items():
if comp_name not in ALL_COMPONENTS:
ALL_COMPONENTS[comp_name] = comp_info
# CRITICAL FIX: Ensure ALL components have config_fields
for comp_name, comp_info in ALL_COMPONENTS.items():
if 'config_fields' not in comp_info:
comp_info['config_fields'] = {
"name": {"type": "text", "label": "Component Name", "default": comp_name}
}
#================================================================================
@dataclass
class ComponentData:
type: str
shape: str
color: str
icon: str
description: str
config: Dict[str, Any] = field(default_factory=dict)
@dataclass
class AgentNode:
id: str
type: str
x: int
y: int
label: str = ""
config: Dict[str, Any] = field(default_factory=dict)
component_data: ComponentData = field(default_factory=lambda: ComponentData("", "", "", "", ""))
class Connection:
def __init__(self, from_node: str, to_node: str):
self.from_node = from_node
self.to_node = to_node
class WorkflowDesigner:
def __init__(self):
self.nodes: Dict[str, AgentNode] = {}
self.connections: List[Connection] = []
self.node_counter = 0
self.selected_node: Optional[str] = None
def select_node(self, node_id: str) -> None:
self.selected_node = node_id if node_id in self.nodes else None
def move_selected_node(self, dx: int, dy: int) -> None:
if self.selected_node and self.selected_node in self.nodes:
node = self.nodes[self.selected_node]
node.x = max(0, node.x + dx)
node.y = max(0, node.y + dy)
def add_node(self, node_type: str, config: Dict[str, Any] = None) -> AgentNode:
self.node_counter += 1
node_id = f"{node_type}_{self.node_counter}"
comp_info = ALL_COMPONENTS.get(node_type, {
"shape": "rect",
"color": "#666666",
"icon": "❓",
"description": "Unknown component type",
"config_fields": {"name": {"type": "text", "label": "Name", "default": node_id}}
})
label = config.get("name", node_id) if config else node_id
col = len(self.nodes) % 4
row = len(self.nodes) // 4
x_pos = 150 + (col * 300)
y_pos = 150 + (row * 200)
component_data = ComponentData(
type=node_type,
shape=comp_info["shape"],
color=comp_info["color"],
icon=comp_info["icon"],
description=comp_info["description"],
config=config or {}
)
node = AgentNode(
id=node_id,
type=node_type,
x=x_pos,
y=y_pos,
label=label,
config=config or {},
component_data=component_data
)
self.nodes[node_id] = node
self.selected_node = node_id
return node
def delete_node(self, node_id: str):
if node_id in self.nodes:
self.connections = [
conn for conn in self.connections
if conn.from_node != node_id and conn.to_node != node_id
]
del self.nodes[node_id]
if self.selected_node == node_id:
self.selected_node = None
def add_connection(self, from_node: str, to_node: str):
if from_node in self.nodes and to_node in self.nodes and from_node != to_node:
existing = any(
conn.from_node == from_node and conn.to_node == to_node
for conn in self.connections
)
if not existing:
self.connections.append(Connection(from_node, to_node))
def load_example(self, example_name: str):
if example_name not in EXAMPLE_WORKFLOWS:
return
example = EXAMPLE_WORKFLOWS[example_name]
self.nodes.clear()
self.connections.clear()
for node_data in example["nodes"]:
node_type = node_data["type"]
comp_info = ALL_COMPONENTS.get(node_type, {
"shape": "rect",
"color": "#666666",
"icon": "❓",
"description": "Unknown component type"
})
component_data = ComponentData(
type=node_type,
shape=comp_info["shape"],
color=comp_info["color"],
icon=comp_info["icon"],
description=comp_info["description"]
)
node = AgentNode(
id=node_data["id"],
type=node_type,
x=node_data["x"],
y=node_data["y"],
label=node_data["id"],
component_data=component_data
)
self.nodes[node.id] = node
for conn_data in example["connections"]:
conn = Connection(
from_node=conn_data["from"],
to_node=conn_data["to"]
)
self.connections.append(conn)
if self.nodes:
self.selected_node = list(self.nodes.keys())[0]
def get_workflow_json(self) -> Dict[str, Any]:
return {
"metadata": {
"total_nodes": len(self.nodes),
"total_connections": len(self.connections),
"selected_node": self.selected_node,
},
"nodes": [
{
"id": node.id,
"type": node.type,
"label": node.label,
"position": {"x": node.x, "y": node.y},
"config": node.config,
"component_data": {
"type": node.component_data.type,
"shape": node.component_data.shape,
"color": node.component_data.color,
"icon": node.component_data.icon,
"description": node.component_data.description
}
}
for node in self.nodes.values()
],
"connections": [
{"from": conn.from_node, "to": conn.to_node}
for conn in self.connections
]
}
def update_node_config(self, node_id: str, config: Dict[str, Any]):
if node_id in self.nodes:
self.nodes[node_id].config = config
self.nodes[node_id].label = config.get("name", node_id)
def render_svg(self) -> str:
if not self.nodes:
return '''
<svg width="1200" height="600" style="border-radius: 12px;">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
<rect width="1200" height="600" fill="url(#bg)"/>
<text x="600" y="280" text-anchor="middle" fill="white" font-size="32" font-weight="bold">🚀 Start Building Your Workflow</text>
<text x="600" y="320" text-anchor="middle" fill="white" font-size="18" opacity="0.9">Add components from the library</text>
</svg>
'''
width = 1200
height = max(600, max([n.y for n in self.nodes.values()], default=0) + 200)
svg_parts = [
f'<svg width="{width}" height="{height}" style="border-radius: 12px;">',
'<defs>',
'<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">',
'<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />',
'<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />',
'</linearGradient>',
'<marker id="arrowhead" markerWidth="12" markerHeight="12" refX="11" refY="3" orient="auto">',
'<polygon points="0 0, 12 3, 0 6" fill="white" opacity="0.9"/>',
'</marker>',
'<filter id="shadow">',
'<feDropShadow dx="0" dy="4" stdDeviation="4" flood-opacity="0.3"/>',
'</filter>',
'<filter id="selected-glow">',
'<feGaussianBlur stdDeviation="5" result="coloredBlur"/>',
'<feMerge><feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/></feMerge>',
'</filter>',
'</defs>',
'<rect width="100%" height="100%" fill="url(#bg)"/>'
]
# Draw connections
for conn in self.connections:
if conn.from_node in self.nodes and conn.to_node in self.nodes:
from_node = self.nodes[conn.from_node]
to_node = self.nodes[conn.to_node]
from_x = from_node.x + 85
from_y = from_node.y + 60
to_x = to_node.x + 15
to_y = to_node.y + 60
mid_x = (from_x + to_x) / 2
svg_parts.append(
f'<path d="M {from_x} {from_y} C {mid_x} {from_y}, {mid_x} {to_y}, {to_x} {to_y}" '
f'stroke="white" stroke-width="3" fill="none" opacity="0.8" marker-end="url(#arrowhead)"/>'
)
# Draw nodes
for node in self.nodes.values():
comp_data = node.component_data
cx = node.x + 85
cy = node.y + 60
is_selected = (node.id == self.selected_node)
selection_glow = 'filter="url(#selected-glow)"' if is_selected else 'filter="url(#shadow)"'
selection_stroke = "6" if is_selected else "4"
# Node background
if comp_data.shape == "ellipse":
svg_parts.append(
f'<ellipse cx="{cx}" cy="{cy}" rx="80" ry="50" '
f'fill="white" stroke="{comp_data.color}" stroke-width="{selection_stroke}" {selection_glow} '
f'class="node" id="node_{node.id}" style="cursor: move;"/>'
)
elif comp_data.shape == "cylinder":
svg_parts.append(
f'<ellipse cx="{cx}" cy="{cy-35}" rx="70" ry="18" '
f'fill="white" stroke="{comp_data.color}" stroke-width="3"/>'
)
svg_parts.append(
f'<rect x="{cx-70}" y="{cy-35}" width="140" height="70" '
f'fill="white" stroke="none"/>'
)
svg_parts.append(
f'<line x1="{cx-70}" y1="{cy-35}" x2="{cx-70}" y2="{cy+35}" '
f'stroke="{comp_data.color}" stroke-width="3"/>'
)
svg_parts.append(
f'<line x1="{cx+70}" y1="{cy-35}" x2="{cx+70}" y2="{cy+35}" '
f'stroke="{comp_data.color}" stroke-width="3"/>'
)
svg_parts.append(
f'<ellipse cx="{cx}" cy="{cy+35}" rx="70" ry="18" '
f'fill="white" stroke="{comp_data.color}" stroke-width="3" {selection_glow} '
f'class="node" id="node_{node.id}" style="cursor: move;"/>'
)
elif comp_data.shape == "diamond":
size = 60
points = f"{cx},{cy-size} {cx+size},{cy} {cx},{cy+size} {cx-size},{cy}"
svg_parts.append(
f'<polygon points="{points}" '
f'fill="white" stroke="{comp_data.color}" stroke-width="{selection_stroke}" {selection_glow} '
f'class="node" id="node_{node.id}" style="cursor: move;"/>'
)
elif comp_data.shape == "hexagon":
size = 50
points = f"{cx-size},{cy-30} {cx-size},{cy+30} {cx},{cy+size} {cx+size},{cy+30} {cx+size},{cy-30} {cx},{cy-size}"
svg_parts.append(
f'<polygon points="{points}" '
f'fill="white" stroke="{comp_data.color}" stroke-width="{selection_stroke}" {selection_glow} '
f'class="node" id="node_{node.id}" style="cursor: move;"/>'
)
else: # rect, document, folder
svg_parts.append(
f'<rect x="{cx-80}" y="{cy-45}" width="160" height="90" rx="12" '
f'fill="white" stroke="{comp_data.color}" stroke-width="{selection_stroke}" {selection_glow} '
f'class="node" id="node_{node.id}" style="cursor: move;"/>'
)
# Icon
svg_parts.append(
f'<text x="{cx}" y="{cy-10}" text-anchor="middle" font-size="36">{comp_data.icon}</text>'
)
# Label
label_display = node.label[:15] + "..." if len(node.label) > 15 else node.label
svg_parts.append(
f'<text x="{cx}" y="{cy+25}" text-anchor="middle" '
f'fill="#333" font-size="13" font-weight="600">{label_display}</text>'
)
svg_parts.append('</svg>')
return '\n'.join(svg_parts)
class WorkflowReporter:
def __init__(self):
try:
self.client = AsyncOpenAI(base_url="http://localhost:1234/v1", api_key="lm-studio")
except Exception as e:
print("LM Studio client init failed:", e)
async def generate_report(self, workflow_json: str) -> str:
prompt = f"""
Generate a comprehensive system design report based on the following workflow:
{workflow_json}
The report should include a detailed repost and system breif with full examples and implimentations where possible and explanaion of requirement in cases where the workflow is complexed and need further deconstruction, as well as example usages :
1. A high-level system overview
2. User stories for each component or connection expetation
3. Use case briefs for each component interaction and component relationship
4. Pseudocode for the implementation for each component and for the overall workflow
5. Component responsibilities and interfaces
6. Data flow description and example use-cases
"""
try:
response = await self.client.chat.completions.create(
model="leroydyer/qwen/qwen3-0.6b-q4_k_m.gguf",
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
max_tokens=2048
)
return response.choices[0].message.content
except Exception as e:
return f"Error generating report: {str(e)}"
#================================================================================
workflow = WorkflowDesigner()
reporter = WorkflowReporter()
#================================================================================
#================================================================================
# Original Fully working model !
#================================================================================
def create_workflow_ui():
"""Create a unified interface that works properly"""
with gr.Blocks(title="Agent Workflow Designer", theme=gr.themes.Soft(), css="""
.component-library { max-height: 70vh; overflow-y: auto; }
.canvas-container { border: 2px solid #e0e0e0; border-radius: 12px; padding: 10px; }
.config-panel { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0; }
""") as demo:
gr.Markdown("# 🎓 Agentic System Workflow Designer")
gr.Markdown("**Educational tool for planning and understanding agent architectures**")
# State variables
selected_node_state = gr.State(None)
pending_component_type = gr.State(None)
with gr.Row(equal_height=False):
# Left Sidebar - Component Library
with gr.Column(scale=1, min_width=300):
gr.Markdown("## 📚 Component Library")
with gr.Tabs() as library_tabs:
# High-level categories tab
with gr.TabItem("Categories"):
component_buttons = []
for category, info in COMPONENT_HIERARCHY["HIGH_LEVEL"].items():
with gr.Accordion(f"{info['icon']} {category}", open=False):
gr.Markdown(f"*{info['description']}*")
# Main category button
btn = gr.Button(
f"{info['icon']} Add {category}",
size="sm",
variant="primary"
)
component_buttons.append((btn, category))
# Sub-components
if info.get('sub_components'):
gr.Markdown("**Specialized types:**")
for sub_comp in info['sub_components']:
if sub_comp in COMPONENT_INFO:
sub_info = COMPONENT_INFO[sub_comp]
sub_btn = gr.Button(
f"{sub_info['icon']} {sub_comp.replace('_', ' ').title()}",
size="sm"
)
component_buttons.append((sub_btn, sub_comp))
# All components tab
with gr.TabItem("All Components"):
for comp_type, comp_info in COMPONENT_INFO.items():
btn = gr.Button(
f"{comp_info['icon']} {comp_type.replace('_', ' ').title()}",
size="sm",
variant="secondary"
)
component_buttons.append((btn, comp_type))
gr.Markdown("---")
gr.Markdown("## 📋 Examples")
example_dropdown = gr.Dropdown(
choices=list(EXAMPLE_WORKFLOWS.keys()),
label="Load Example Workflow",
interactive=True
)
load_example_btn = gr.Button("📥 Load Example", variant="secondary")
gr.Markdown("---")
with gr.Row():
clear_btn = gr.Button("🗑️ Clear All", variant="stop")
download_btn = gr.Button("💾 Export", variant="primary")
# Center - Canvas and Configuration
with gr.Column(scale=3):
gr.Markdown("## 🎨 Workflow Canvas")
canvas = gr.HTML(label="Workflow Visualization")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 🎯 Selected Node")
selected_node_info = gr.Markdown("No node selected")
with gr.Row():
select_prev_btn = gr.Button("⬅️ Prev", size="sm")
select_next_btn = gr.Button("➡️ Next", size="sm")
deselect_btn = gr.Button("❌ Deselect", size="sm")
gr.Markdown("**Move Selected:**")
with gr.Row():
move_left_btn = gr.Button("⬅️", size="sm")
move_up_btn = gr.Button("⬆️", size="sm")
move_down_btn = gr.Button("⬇️", size="sm")
move_right_btn = gr.Button("➡️", size="sm")
delete_btn = gr.Button("🗑️ Delete Selected", variant="stop", size="sm")
with gr.Column(scale=2):
gr.Markdown("### ⚙️ Node Configuration")
config_display = gr.JSON(
label="Current Configuration",
)
# Dynamic configuration form
config_form = gr.Column(visible=False)
config_inputs = {}
with config_form:
gr.Markdown("#### Edit Configuration")
for comp_type, comp_info in COMPONENT_INFO.items():
if 'config_fields' in comp_info:
for field_name, field_config in comp_info['config_fields'].items():
input_key = f"{comp_type}_{field_name}"
if field_config['type'] == 'text':
config_inputs[input_key] = gr.Textbox(
label=field_config['label'],
value=field_config.get('default', ''),
visible=False
)
elif field_config['type'] == 'textarea':
config_inputs[input_key] = gr.Textbox(
label=field_config['label'],
value=field_config.get('default', ''),
lines=3,
visible=False
)
elif field_config['type'] == 'number':
config_inputs[input_key] = gr.Number(
label=field_config['label'],
value=field_config.get('default', 0),
visible=False
)
elif field_config['type'] == 'slider':
config_inputs[input_key] = gr.Slider(
label=field_config['label'],
minimum=field_config.get('min', 0),
maximum=field_config.get('max', 1),
step=field_config.get('step', 0.1),
value=field_config.get('default', 0),
visible=False
)
elif field_config['type'] == 'dropdown':
config_inputs[input_key] = gr.Dropdown(
label=field_config['label'],
choices=field_config['choices'],
value=field_config.get('default'),
visible=False
)
with gr.Row():
save_config_btn = gr.Button("💾 Save Config", variant="primary")
cancel_config_btn = gr.Button("❌ Cancel", variant="secondary")
with gr.Row():
gr.Markdown("---")
gr.Markdown("## 🔗 Connections")
with gr.Row():
from_node = gr.Dropdown(label="From", choices=[], interactive=True, scale=2)
to_node = gr.Dropdown(label="To", choices=[], interactive=True, scale=2)
connect_btn = gr.Button("➡️ Connect Nodes", variant="secondary")
# Right Sidebar - Information and Export
with gr.Column(scale=1, min_width=300):
gr.Markdown("## 📊 Workflow Information")
with gr.Accordion("📋 Workflow JSON", open=False):
workflow_json = gr.JSON(label="Complete Workflow Data")
with gr.Accordion("📝 Component Info", open=True):
component_info = gr.Markdown("Select a component to see details")
gr.Markdown("---")
gr.Markdown("## 📄 System Report")
report_btn = gr.Button("📊 Generate Report", variant="primary")
report_output = gr.Textbox(
label="Design Report",
lines=10,
max_lines=15,
interactive=False
)
download_report_btn = gr.Button("📥 Download Report", variant="secondary")
gr.Markdown("---")
download_files = gr.Files(label="📥 Download Files")
# Core state management functions
def get_full_state():
"""Get complete application state"""
svg = workflow.render_svg()
node_choices = list(workflow.nodes.keys())
wf_json = workflow.get_workflow_json()
# Selected node info
selected_info = "**No node selected**"
comp_info_text = "Select a component to see its description and configuration options"
config_data = {}
if workflow.selected_node and workflow.selected_node in workflow.nodes:
node = workflow.nodes[workflow.selected_node]
comp_info = ALL_COMPONENTS.get(node.type, {})
selected_info = f"**Selected:** `{node.label}`\n\n**Type:** {node.type}\n**Position:** ({node.x}, {node.y})"
comp_info_text = f"### {comp_info.get('icon', '❓')} {node.type}\n\n{comp_info.get('description', 'No description available')}"
config_data = node.config
return (
svg, # canvas
gr.Dropdown(choices=node_choices), # from_node
gr.Dropdown(choices=node_choices), # to_node
selected_info, # selected_node_info
config_data, # config_display
wf_json, # workflow_json
comp_info_text, # component_info
gr.update(visible=False), # config_form
)
def add_node_simple(node_type):
"""Add node with default configuration"""
default_config = {}
if node_type in COMPONENT_INFO and 'config_fields' in COMPONENT_INFO[node_type]:
for field_name, field_config in COMPONENT_INFO[node_type]['config_fields'].items():
default_config[field_name] = field_config.get('default', '')
default_config['name'] = f"{node_type}_{workflow.node_counter + 1}"
workflow.add_node(node_type, default_config)
return get_full_state()
def select_node_handler(node_id):
"""Handle node selection"""
if node_id:
workflow.select_node(node_id)
return get_full_state()
def navigate_nodes(direction):
"""Navigate between nodes"""
if workflow.nodes:
node_ids = list(workflow.nodes.keys())
if not workflow.selected_node:
workflow.selected_node = node_ids[0]
else:
current_idx = node_ids.index(workflow.selected_node)
if direction == 'next':
new_idx = (current_idx + 1) % len(node_ids)
else: # prev
new_idx = (current_idx - 1) % len(node_ids)
workflow.selected_node = node_ids[new_idx]
return get_full_state()
def move_node_handler(dx, dy):
"""Move selected node"""
if workflow.selected_node:
workflow.move_selected_node(dx, dy)
return get_full_state()
def connect_nodes_handler(from_node, to_node):
"""Connect two nodes"""
if from_node and to_node and from_node != to_node:
workflow.add_connection(from_node, to_node)
return get_full_state()
def delete_selected_handler():
"""Delete selected node"""
if workflow.selected_node:
workflow.delete_node(workflow.selected_node)
return get_full_state()
def load_example_handler(example_name):
"""Load example workflow"""
if example_name:
workflow.load_example(example_name)
return get_full_state()
def clear_workflow_handler():
"""Clear entire workflow"""
workflow.nodes.clear()
workflow.connections.clear()
workflow.node_counter = 0
workflow.selected_node = None
return get_full_state()
def show_config_form():
"""Show configuration form for selected node"""
if not workflow.selected_node:
return get_full_state()
node = workflow.nodes[workflow.selected_node]
updates = list(get_full_state())
updates[-1] = gr.update(visible=True) # Show config form
# Show relevant config inputs
for input_key in config_inputs:
comp_type, field_name = input_key.split('_', 1)
if comp_type == node.type:
updates.append(gr.update(visible=True, value=node.config.get(field_name, '')))
else:
updates.append(gr.update(visible=False))
return tuple(updates)
def save_config_handler(*config_values):
"""Save configuration for selected node"""
if not workflow.selected_node:
return get_full_state()
node = workflow.nodes[workflow.selected_node]
new_config = {}
# Build config from visible inputs
for idx, (input_key, input_comp) in enumerate(config_inputs.items()):
comp_type, field_name = input_key.split('_', 1)
if comp_type == node.type:
new_config[field_name] = config_values[idx]
workflow.update_node_config(workflow.selected_node, new_config)
updates = list(get_full_state())
updates[-1] = gr.update(visible=False) # Hide config form
return tuple(updates)
# Connect component buttons
for btn, comp_type in component_buttons:
btn.click(
lambda ct=comp_type: add_node_simple(ct),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
# Connect navigation and interaction
select_prev_btn.click(
lambda: navigate_nodes('prev'),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
select_next_btn.click(
lambda: navigate_nodes('next'),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
# Movement buttons
movement_buttons = [move_left_btn, move_right_btn, move_up_btn, move_down_btn]
movements = [(-20, 0), (20, 0), (0, -20), (0, 20)]
for btn, (dx, dy) in zip(movement_buttons, movements):
btn.click(
lambda dx=dx, dy=dy: move_node_handler(dx, dy),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
# Other interactions
connect_btn.click(
connect_nodes_handler,
inputs=[from_node, to_node],
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
delete_btn.click(
delete_selected_handler,
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
load_example_btn.click(
load_example_handler,
inputs=[example_dropdown],
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
clear_btn.click(
clear_workflow_handler,
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
]
)
# Configuration form
save_config_btn.click(
save_config_handler,
inputs=list(config_inputs.values()),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
] + list(config_inputs.values())
)
cancel_config_btn.click(
lambda: get_full_state() + tuple([gr.update(visible=False)] * len(config_inputs)),
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form
] + list(config_inputs.values())
)
# Double-click to edit config
def handle_canvas_double_click():
return show_config_form()
# Download functions
def download_workflow():
wf_json = workflow.get_workflow_json()
json_path = tempfile.mktemp(suffix="_workflow.json")
with open(json_path, "w", encoding="utf-8") as f:
json.dump(wf_json, f, indent=2)
svg_path = tempfile.mktemp(suffix="_workflow.svg")
with open(svg_path, "w", encoding="utf-8") as f:
f.write(workflow.render_svg())
return [json_path, svg_path]
def generate_report():
wf_data = workflow.get_workflow_json()
json_str = json.dumps(wf_data, indent=2)
try:
report = asyncio.run(reporter.generate_report(json_str))
except Exception as e:
report = f"Report generation failed: {str(e)}\n\nPlease ensure LM Studio is running with a model loaded."
return report
def download_report_func():
report_text = generate_report()
report_path = tempfile.mktemp(suffix="_report.txt")
with open(report_path, "w", encoding="utf-8") as f:
f.write(f"Agentic Workflow Design Report\n")
f.write(f"Generated: {__import__('datetime').datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
f.write(report_text)
return [report_path]
download_btn.click(download_workflow, outputs=[download_files])
report_btn.click(generate_report, outputs=[report_output])
download_report_btn.click(download_report_func, outputs=[download_files])
# JavaScript for node selection
js_code = '''
<script>
window.gradio_api = function(type, data) {
if (type === 'select_node') {
const event = new CustomEvent('gradio_node_select', { detail: data });
document.dispatchEvent(event);
}
};
document.addEventListener('gradio_node_select', (e) => {
// This will be handled by Gradio's event system
console.log('Node selected:', e.detail);
});
</script>
'''
def init_app():
state = get_full_state()
return state + (js_code,)
demo.load(
init_app,
outputs=[
canvas, from_node, to_node, selected_node_info,
config_display, workflow_json, component_info, config_form,
gr.HTML(visible=False) # For JS code
]
)
return demo
#================================================================================
#================================================================================
# FULL VERSION
def create_main_workflow_ui():
"""Create unified interface with proper modal popup"""
with gr.Blocks(title="Agent Workflow Designer", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🎓 Agentic System Workflow Designer")
gr.Markdown("**Educational tool for planning agent architectures**")
# State variables
pending_component_type = gr.State("")
editing_node_id = gr.State("")
#================================================================================
# Left Sidebar
with gr.Sidebar(open=True,label = "Components",width=600, position = "right"):
gr.Markdown("## 📚 Component Library")
with gr.Tabs():
with gr.TabItem("Categories"):
component_buttons = []
for category, info in COMPONENT_HIERARCHY["HIGH_LEVEL"].items():
with gr.TabItem(f"{info['icon']} {category}"):
gr.Markdown(f"*{info['description']}*")
btn = gr.Button(
f"{info['icon']} Add {category}",
size="sm",
variant="primary"
)
component_buttons.append((btn, category))
if info.get('sub_components'):
gr.Markdown("**Specialized:**")
for sub_comp in info['sub_components']:
if sub_comp in COMPONENT_INFO:
sub_info = COMPONENT_INFO[sub_comp]
sub_btn = gr.Button(
f"{sub_info['icon']} {sub_comp.replace('_', ' ').title()}",
size="sm"
)
component_buttons.append((sub_btn, sub_comp))
with gr.TabItem("All"):
for comp_type, comp_info in COMPONENT_INFO.items():
btn = gr.Button(
f"{comp_info['icon']} {comp_type.replace('_', ' ').title()}",
size="sm",
variant="secondary"
)
component_buttons.append((btn, comp_type))
gr.Markdown("---")
example_dropdown = gr.Dropdown(
choices=list(EXAMPLE_WORKFLOWS.keys()),
label="Load Example",
interactive=True
)
load_example_btn = gr.Button("📥 Load", variant="secondary")
gr.Markdown("---")
with gr.Row():
clear_btn = gr.Button("🗑️ Clear", variant="stop")
download_btn = gr.Button("💾 Export", variant="primary")
# Center
#================================================================================
with gr.Column():
# Config Modal -
with gr.Column(visible=False, elem_classes=["modal-overlay"]) as config_modal:
with gr.Row():
gr.Markdown("") # Left spacer
# Modal content
with gr.Column(scale=2, min_width=400, elem_classes=["modal-content"]):
with gr.Row():
config_modal_title = gr.Markdown("### ⚙️ Configure Component")
close_btn = gr.Button("❌", size="sm", elem_classes=["close-btn"])
config_inputs = []
for i in range(8): # Reasonable number of fields
config_input = gr.Textbox(
label=f"Field {i}",
visible=False,
interactive=True
)
config_inputs.append(config_input)
with gr.Row():
save_new_btn = gr.Button("✅ Add Component", variant="primary")
save_edit_btn = gr.Button("✅ Save Changes", variant="primary", visible=False)
cancel_btn = gr.Button("❌ Cancel", variant="secondary")
gr.Markdown("") # Right spacer
#================================================================================
# Canvas -
with gr.Column(scale=3,min_width=600):
gr.Markdown("## 🎨 Canvas")
canvas = gr.HTML(min_height=600,label="Workflow")
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### 🎯 Selected")
selected_node_info = gr.Markdown("No node selected")
with gr.Row():
from_node = gr.Dropdown(label="From", choices=[], interactive=True)
to_node = gr.Dropdown(label="To", choices=[], interactive=True)
connect_btn = gr.Button("➡️ Connect", variant="secondary")
with gr.Row():
select_prev_btn = gr.Button("⬅️", size="sm")
select_next_btn = gr.Button("➡️", size="sm")
gr.Markdown("**Move:**")
with gr.Row():
move_left_btn = gr.Button("⬅️", size="sm")
move_up_btn = gr.Button("⬆️", size="sm")
move_down_btn = gr.Button("⬇️", size="sm")
move_right_btn = gr.Button("➡️", size="sm")
delete_btn = gr.Button("🗑️ Delete", variant="stop", size="sm")
edit_config_btn = gr.Button("⚙️ Config", variant="primary", size="sm")
with gr.Column(scale=1):
gr.Markdown("### ⚙️ Configuration")
config_display = gr.JSON(label="Current Config")
gr.Markdown("---")
with gr.Row():
gr.Markdown("---")
gr.Markdown("## 🔗 Connections")
#================================================================================
# Right Sidebar
with gr.Sidebar(width=600,position = "left",open = False):
gr.Markdown("## 📊 Info")
with gr.Accordion("📋 JSON", open=True):
workflow_json = gr.JSON(label="Workflow Data")
with gr.Accordion("📝 Component", open=True):
component_info = gr.Markdown("Select a component")
gr.Markdown("---")
gr.Markdown("## 📄 Report")
report_btn = gr.Button("📊 Generate", variant="primary")
report_output = gr.Textbox(label="Report", lines=10, interactive=False)
gr.Markdown("---")
download_files = gr.Files(label="📥 Downloads")
#================================================================================
# Add custom CSS for modal styling
demo.css = """
.modal-overlay {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100vw !important;
height: 100vh !important;
background-color: rgba(0,0,0,0.5) !important;
z-index: 1000 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.modal-content {
background: white !important;
padding: 20px !important;
border-radius: 10px !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.3) !important;
max-height: 80vh !important;
overflow-y: auto !important;
border: 1px solid #ccc !important;
}
.close-btn {
margin-left: auto !important;
margin-bottom: 10px !important;
}
"""
#================================================================================
# Helper functions
def get_full_state():
svg = workflow.render_svg()
node_choices = list(workflow.nodes.keys())
wf_json = workflow.get_workflow_json()
selected_info = "**No node selected**"
comp_info_text = "Select a component"
config_data = {}
if workflow.selected_node and workflow.selected_node in workflow.nodes:
node = workflow.nodes[workflow.selected_node]
comp_info = ALL_COMPONENTS.get(node.type, {})
selected_info = f"**Selected:** `{node.label}`\n**Type:** {node.type}"
comp_info_text = f"### {comp_info.get('icon', '❓')} {node.type}\n\n{comp_info.get('description', 'No description')}"
config_data = node.config
return (
svg,
gr.Dropdown(choices=node_choices),
gr.Dropdown(choices=node_choices),
selected_info,
config_data,
wf_json,
comp_info_text,
)
def show_config_modal(comp_type, is_editing=False, existing_config=None):
"""FIXED: Proper modal display with correct field setup"""
if not comp_type:
return [gr.update(visible=False), "", comp_type, ""] + [gr.update(visible=False)] * 8 + [gr.update(visible=False), gr.update(visible=False)]
# Get component info
comp_info = COMPONENT_INFO.get(comp_type, {
"icon": "❓",
"config_fields": {"name": {"type": "text", "label": "Name", "default": comp_type}}
})
action = "Edit" if is_editing else "Add"
title = f"### {comp_info.get('icon', '❓')} {action} {comp_type.replace('_', ' ').title()}"
updates = [
gr.update(visible=True), # modal
title,
comp_type,
existing_config.get('id', '') if existing_config else "",
]
# Get config fields
field_configs = comp_info.get('config_fields', {})
field_names = list(field_configs.keys())
# Update inputs (max 8)
for i in range(8):
if i < len(field_names):
field_name = field_names[i]
field_config = field_configs[field_name]
existing_value = existing_config.get(field_name, field_config.get('default', '')) if existing_config else field_config.get('default', '')
updates.append(gr.update(
label=field_config['label'],
value=str(existing_value),
placeholder=field_config.get('placeholder', ''),
visible=True,
interactive=True
))
else:
updates.append(gr.update(visible=False))
# Button visibility
updates.append(gr.update(visible=not is_editing))
updates.append(gr.update(visible=is_editing))
return updates
def save_and_add(comp_type, *field_values):
"""Save new component configuration"""
if not comp_type:
return get_full_state() + (gr.update(visible=False),)
comp_info = COMPONENT_INFO.get(comp_type, {})
config = {}
if 'config_fields' in comp_info:
field_names = list(comp_info['config_fields'].keys())
for i, field_name in enumerate(field_names):
if i < len(field_values) and field_values[i] is not None and field_values[i] != "":
config[field_name] = field_values[i]
# Ensure name
if 'name' not in config or not config['name']:
config['name'] = f"{comp_type}_{workflow.node_counter + 1}"
workflow.add_node(comp_type, config)
return get_full_state() + (gr.update(visible=False),)
def save_edit(node_id, comp_type, *field_values):
"""Save edited component configuration"""
if not node_id or node_id not in workflow.nodes:
return get_full_state() + (gr.update(visible=False),)
comp_info = COMPONENT_INFO.get(comp_type, {})
config = {}
if 'config_fields' in comp_info:
field_names = list(comp_info['config_fields'].keys())
for i, field_name in enumerate(field_names):
if i < len(field_values) and field_values[i] is not None and field_values[i] != "":
config[field_name] = field_values[i]
workflow.update_node_config(node_id, config)
return get_full_state() + (gr.update(visible=False),)
def close_modal():
"""Close modal and reset state"""
return [gr.update(visible=False), "", ""]
#================================================================================
# Connect component buttons
for btn, comp_type in component_buttons:
btn.click(
lambda ct=comp_type: show_config_modal(ct, is_editing=False),
outputs=[config_modal, config_modal_title, pending_component_type, editing_node_id] +
config_inputs + [save_new_btn, save_edit_btn]
)
# Edit button
def show_edit():
if not workflow.selected_node or workflow.selected_node not in workflow.nodes:
return [gr.update(visible=False)] + [gr.update()] * (3 + 8 + 2)
node = workflow.nodes[workflow.selected_node]
existing_config = node.config.copy()
existing_config['id'] = node.id
return show_config_modal(node.type, is_editing=True, existing_config=existing_config)
edit_config_btn.click(show_edit, outputs=[config_modal, config_modal_title, pending_component_type, editing_node_id] + config_inputs + [save_new_btn, save_edit_btn])
# Save buttons
save_new_btn.click(save_and_add, inputs=[pending_component_type] + config_inputs,
outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info, config_modal])
save_edit_btn.click(save_edit, inputs=[editing_node_id, pending_component_type] + config_inputs,
outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info, config_modal])
# Close modal actions
cancel_btn.click(close_modal, outputs=[config_modal, pending_component_type, editing_node_id])
close_btn.click(close_modal, outputs=[config_modal, pending_component_type, editing_node_id])
#================================================================================
# Navigation and other interactions remain the same...
def navigate(direction):
if workflow.nodes:
node_ids = list(workflow.nodes.keys())
if not workflow.selected_node:
workflow.selected_node = node_ids[0]
else:
idx = node_ids.index(workflow.selected_node)
idx = (idx + (1 if direction == 'next' else -1)) % len(node_ids)
workflow.selected_node = node_ids[idx]
return get_full_state()
select_prev_btn.click(lambda: navigate('prev'), outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
select_next_btn.click(lambda: navigate('next'), outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
# Movement
for btn, dx, dy in [(move_left_btn, -20, 0), (move_right_btn, 20, 0), (move_up_btn, 0, -20), (move_down_btn, 0, 20)]:
btn.click(lambda dx=dx, dy=dy: (workflow.move_selected_node(dx, dy) or get_full_state()),
outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
#================================================================================
# Other actions
connect_btn.click(lambda f, t: (workflow.add_connection(f, t) or get_full_state()) if f and t else get_full_state(),
inputs=[from_node, to_node], outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
delete_btn.click(lambda: (workflow.delete_node(workflow.selected_node) or get_full_state()) if workflow.selected_node else get_full_state(),
outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
load_example_btn.click(lambda e: (workflow.load_example(e) or get_full_state()) if e else get_full_state(),
inputs=[example_dropdown], outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
clear_btn.click(lambda: (workflow.nodes.clear(), workflow.connections.clear(), setattr(workflow, 'node_counter', 0), setattr(workflow, 'selected_node', None), get_full_state())[-1],
outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
#================================================================================
# Export
def export():
wf = workflow.get_workflow_json()
json_path = tempfile.mktemp(suffix="_workflow.json")
with open(json_path, "w") as f:
json.dump(wf, f, indent=2)
svg_path = tempfile.mktemp(suffix="_workflow.svg")
with open(svg_path, "w") as f:
try:
f.write(workflow.render_svg())
except:
pass
return [json_path, svg_path]
download_btn.click(export, outputs=[download_files])
def gen_report():
try:
wf = json.dumps(workflow.get_workflow_json(), indent=2)
return asyncio.run(reporter.generate_report(wf))
except Exception as e:
return f"Error: {str(e)}\n\nEnsure LM Studio is running."
report_btn.click(gen_report, outputs=[report_output])
#================================================================================
# Initialize
demo.load(get_full_state, outputs=[canvas, from_node, to_node, selected_node_info, config_display, workflow_json, component_info])
return demo
#================================================================================
if __name__ == "__main__":
demo = create_main_workflow_ui()
demo.launch(share=True)