diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,2459 +1,2551 @@ -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 -import base64 - -# Complete Hierarchical Component definitions with Implementation Details -COMPONENT_INFO = { - "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: Autonomous reasoning units - # =================================== - "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"] - }, - - "REASONING_AGENT": { - "shape": "rect", - "color": "#4CAF50", - "icon": "🧠", - "description": [ - "• Performs complex reasoning tasks", - "• Uses chain-of-thought or tree-of-thought", - "• Can break down complex problems", - "• Maintains reasoning traces" - ], - "implementation": { - "python_snippet": """ -class ReasoningAgent: - def __init__(self, model, tools=None): - self.model = model - self.tools = tools or [] - self.reasoning_history = [] - - async def process(self, query, context=None): - # Chain-of-thought reasoning - reasoning_steps = await self.generate_reasoning_steps(query, context) - self.reasoning_history.extend(reasoning_steps) - - # Final answer generation - answer = await self.synthesize_answer(reasoning_steps) - return answer - - async def generate_reasoning_steps(self, query, context): - prompt = f\"\"\"Analyze this problem step by step: - Query: {query} - Context: {context} - - Break down your reasoning:\"\"\" - return await self.model.generate(prompt) - """, - "prompt_template": """ -You are a reasoning agent. Analyze the user's query step by step: - -Query: {user_input} -Context: {context} - -Please: -1. Break down the problem into logical steps -2. Consider different perspectives -3. Evaluate evidence and constraints -4. Synthesize a comprehensive answer - -Reasoning steps: - """, - "dependencies": ["openai", "langchain", "pydantic"], - "config": { - "model": "gpt-4", - "temperature": 0.1, - "max_tokens": 2000 - } - } - }, - - "ACTION_AGENT": { - "shape": "rect", - "color": "#4CAF50", - "icon": "⚡", - "description": [ - "• Executes actions using available tools", - "• Monitors action outcomes", - "• Handles errors and retries", - "• Updates state after actions" - ], - "implementation": { - "python_snippet": """ -class ActionAgent: - def __init__(self, tools, model): - self.tools = {tool.name: tool for tool in tools} - self.model = model - - async def execute_action(self, action_request): - tool_name, parameters = self.parse_action(action_request) - if tool_name in self.tools: - return await self.tools[tool_name].execute(parameters) - else: - raise ValueError(f"Unknown tool: {tool_name}") - - def parse_action(self, action_request): - # Parse action from model response - return action_request['tool'], action_request['parameters'] - """, - "dependencies": ["pydantic", "asyncio"], - "config": { - "retry_attempts": 3, - "timeout_seconds": 30 - } - } - }, - - "PLANNER_AGENT": { - "shape": "rect", - "color": "#4CAF50", - "icon": "📋", - "description": [ - "• Creates multi-step plans to achieve goals", - "• Decomposes complex tasks", - "• Optimizes execution order", - "• Monitors plan progress" - ], - "implementation": { - "python_snippet": """ -class PlannerAgent: - def __init__(self, model): - self.model = model - self.plans = [] - - async def create_plan(self, goal, context): - plan_prompt = f\"\"\"Create a step-by-step plan to achieve: - Goal: {goal} - Context: {context} - - Return a list of actionable steps:\"\"\" - plan_steps = await self.model.generate(plan_prompt) - plan = Plan(steps=plan_steps, goal=goal) - self.plans.append(plan) - return plan - """, - "dependencies": ["pydantic"], - "config": { - "max_steps": 20, - "planning_temperature": 0.3 - } - } - }, - - "REACT_AGENT": { - "shape": "rect", - "color": "#4CAF50", - "icon": "🔄", - "description": [ - "• Implements ReAct (Reason + Act) framework", - "• Alternates reasoning and action steps", - "• Maintains conversation history", - "• Handles tool interactions" - ], - "implementation": { - "python_snippet": """ -class ReActAgent: - def __init__(self, model, tools): - self.model = model - self.tools = tools - self.conversation_history = [] - - async def step(self, input_text): - # Generate thought - thought = await self.generate_thought(input_text) - - # Decide on action - action = await self.decide_action(thought) - - # Execute action if needed - if action: - observation = await self.execute_action(action) - return {"thought": thought, "action": action, "observation": observation} - else: - return {"thought": thought, "answer": await self.generate_answer(thought)} - """, - "dependencies": ["asyncio", "langchain"], - "config": { - "max_iterations": 10, - "react_temperature": 0.7 - } - } - }, - - "MULTI_AGENT": { - "shape": "rect", - "color": "#4CAF50", - "icon": "👥", - "description": [ - "• Coordinates multiple specialized agents", - "• Manages agent communication", - "• Distributes tasks among agents", - "• Aggregates results from agents" - ], - "implementation": { - "python_snippet": """ -class MultiAgentSystem: - def __init__(self, agents, orchestrator): - self.agents = {agent.name: agent for agent in agents} - self.orchestrator = orchestrator - - async def coordinate(self, task): - # Assign task to appropriate agents - agent_assignments = await self.orchestrator.assign(task) - - # Execute in parallel - results = await asyncio.gather(*[ - self.agents[agent_name].process(subtask) - for agent_name, subtask in agent_assignments.items() - ]) - - return self.orchestrator.aggregate(results) - """, - "dependencies": ["asyncio", "concurrent.futures"], - "config": { - "max_concurrent_agents": 10, - "communication_protocol": "message_queue" - } - } - }, - - # =================================== - # USER: Interaction interfaces - # =================================== - "USER": { - "description": "User interaction points and interfaces", - "color": "#9C27B0", - "icon": "👤", - "shape": "ellipse", - "sub_components": ["USER_INPUT", "USER_OUTPUT", "MULTIMODAL_INTERFACE"] - }, - - "USER_INPUT": { - "shape": "ellipse", - "color": "#9C27B0", - "icon": "⌨️", - "description": [ - "• Accepts text, voice, or gesture input", - "• Validates and sanitizes input", - "• Converts to structured format", - "• Handles multiple input channels" - ], - "implementation": { - "python_snippet": """ -class UserInputHandler: - def __init__(self): - self.input_validators = { - 'text': self.validate_text, - 'voice': self.validate_voice, - 'gesture': self.validate_gesture - } - - async def process_input(self, input_type, raw_input): - validator = self.input_validators.get(input_type) - if validator: - return await validator(raw_input) - else: - raise ValueError(f"Unsupported input type: {input_type}") - - async def validate_text(self, text): - # Sanitize and structure text input - return {"type": "text", "content": text.strip()} - """, - "dependencies": ["validators", "pydantic"], - "config": { - "max_input_length": 10000, - "allowed_input_types": ["text", "voice", "gesture"] - } - } - }, - - "USER_OUTPUT": { - "shape": "ellipse", - "color": "#9C27B0", - "icon": "🔊", - "description": [ - "• Formats responses for user consumption", - "• Supports multiple output formats", - "• Handles accessibility features", - "• Manages response timing" - ], - "implementation": { - "python_snippet": """ -class UserOutputHandler: - def __init__(self): - self.formatters = { - 'text': self.format_text, - 'audio': self.format_audio, - 'visual': self.format_visual - } - - async def deliver_response(self, response_data, output_format): - formatter = self.formatters.get(output_format) - if formatter: - formatted_response = await formatter(response_data) - return await self.send_to_user(formatted_response) - - async def format_text(self, data): - # Format response as structured text - return {"format": "text", "content": data} - """, - "dependencies": ["jinja2", "markdown"], - "config": { - "default_format": "text", - "max_response_length": 5000 - } - } - }, - - "MULTIMODAL_INTERFACE": { - "shape": "ellipse", - "color": "#9C27B0", - "icon": "🖼️", - "description": [ - "• Handles multiple input/output modalities", - "• Integrates text, image, audio, video", - "• Manages modality conversion", - "• Supports rich media responses" - ], - "implementation": { - "python_snippet": """ -class MultimodalInterface: - def __init__(self): - self.input_processors = { - 'image': ImageProcessor(), - 'audio': AudioProcessor(), - 'text': TextProcessor() - } - self.output_formatters = { - 'rich_text': RichTextFormatter(), - 'multimedia': MultimediaFormatter() - } - - async def process_multimodal_input(self, inputs): - processed_inputs = {} - for input_type, input_data in inputs.items(): - processor = self.input_processors.get(input_type) - if processor: - processed_inputs[input_type] = await processor.process(input_data) - return processed_inputs - """, - "dependencies": ["pillow", "pyaudio", "opencv-python"], - "config": { - "supported_modalities": ["text", "image", "audio", "video"], - "max_file_size_mb": 50 - } - } - }, - - # =================================== - # TOOL: External functions and capabilities - # =================================== - "TOOL": { - "description": "External functions and capabilities", - "color": "#795548", - "icon": "🔧", - "shape": "hexagon", - "sub_components": ["MCP_TOOL", "API_TOOL", "LOCAL_TOOL", "AGENT_TOOL", "FUNCTION_TOOL"] - }, - - "MCP_TOOL": { - "shape": "hexagon", - "color": "#795548", - "icon": "🔌", - "description": [ - "• Model Context Protocol server", - "• Standardized tool interface", - "• Dynamic tool discovery", - "• Secure resource access" - ], - "implementation": { - "python_snippet": """ -# MCP Server implementation -from mcp import MCPServer, Tool - -class FileSystemTool: - @Tool - async def read_file(self, path: str) -> str: - \"\"\"Read content from a file\"\"\" - with open(path, 'r') as f: - return f.read() - - @Tool - async def write_file(self, path: str, content: str) -> str: - \"\"\"Write content to a file\"\"\" - with open(path, 'w') as f: - f.write(content) - return f"Written to {path}" - -# MCP Client usage -async def use_mcp_tool(agent, tool_name, parameters): - result = await agent.use_tool(tool_name, parameters) - return result - """, - "protocol_spec": { - "version": "1.0", - "transport": ["stdio", "sse"], - "authentication": ["none", "bearer"] - }, - "example_tools": ["filesystem", "calculator", "web_search", "database"] - } - }, - - "API_TOOL": { - "shape": "hexagon", - "color": "#795548", - "icon": "🔗", - "description": [ - "• Wraps external REST/gRPC APIs", - "• Handles authentication and rate limits", - "• Manages request/response mapping", - "• Provides error handling and retries" - ], - "implementation": { - "python_snippet": """ -class APITool: - def __init__(self, base_url, auth_token=None, rate_limit=10): - self.base_url = base_url - self.auth_token = auth_token - self.rate_limit = rate_limit - self.session = aiohttp.ClientSession() - - async def call(self, endpoint, method='GET', data=None): - headers = {"Authorization": f"Bearer {self.auth_token}"} if self.auth_token else {} - url = f"{self.base_url}/{endpoint}" - - async with self.session.request(method, url, json=data, headers=headers) as response: - return await response.json() - """, - "dependencies": ["aiohttp", "requests"], - "config": { - "timeout": 30, - "max_retries": 3, - "retry_delay": 1.0 - } - } - }, - - "LOCAL_TOOL": { - "shape": "hexagon", - "color": "#795548", - "icon": "💻", - "description": [ - "• Locally executed utility functions", - "• File operations, math calculations", - "• System utilities and helpers", - "• Fast execution without network calls" - ], - "implementation": { - "python_snippet": """ -class LocalTool: - @staticmethod - async def file_operations(action, **kwargs): - if action == 'read': - with open(kwargs['path'], 'r') as f: - return f.read() - elif action == 'write': - with open(kwargs['path'], 'w') as f: - f.write(kwargs['content']) - return f"File written to {kwargs['path']}" - - @staticmethod - async def math_operations(operation, **kwargs): - if operation == 'add': - return kwargs['a'] + kwargs['b'] - elif operation == 'multiply': - return kwargs['a'] * kwargs['b'] - """, - "dependencies": ["os", "math"], - "config": { - "max_execution_time": 5.0, - "allowed_operations": ["file", "math", "system"] - } - } - }, - - "AGENT_TOOL": { - "shape": "hexagon", - "color": "#795548", - "icon": "🛠️", - "description": [ - "• Allows one agent to act as a tool for another", - "• Wraps agent functionality for external use", - "• Handles agent-to-agent communication", - "• Manages agent state and context" - ], - "implementation": { - "python_snippet": """ -class AgentTool: - def __init__(self, agent): - self.agent = agent - - async def execute(self, query, context=None): - # Wrap agent execution as a tool call - result = await self.agent.process(query, context) - return { - "result": result, - "agent_name": self.agent.name, - "execution_time": time.time() - } - """, - "dependencies": ["asyncio", "time"], - "config": { - "max_concurrent_calls": 5, - "timeout_seconds": 60 - } - } - }, - - "FUNCTION_TOOL": { - "shape": "hexagon", - "color": "#795548", - "icon": "🧮", - "description": [ - "• Generic callable function exposed to agents", - "• Wraps Python functions for tool use", - "• Handles parameter validation", - "• Provides type safety and documentation" - ], - "implementation": { - "python_snippet": """ -from pydantic import BaseModel, create_model - -class FunctionTool: - def __init__(self, func, description, param_schema=None): - self.func = func - self.description = description - self.param_schema = param_schema or self._infer_schema(func) - - async def execute(self, **kwargs): - validated_params = self.param_schema(**kwargs) - return await self.func(**validated_params.dict()) - - def _infer_schema(self, func): - # Infer schema from function signature - sig = inspect.signature(func) - fields = {} - for name, param in sig.parameters.items(): - fields[name] = (param.annotation, param.default if param.default != param.empty else ...) - return create_model(f"{func.__name__}Params", **fields) - """, - "dependencies": ["pydantic", "inspect"], - "config": { - "max_params": 10, - "validation_enabled": True - } - } - }, - - # =================================== - # DATA: Storage and knowledge systems - # =================================== - "DATA": { - "description": "Data sources and storage systems", - "color": "#009688", - "icon": "💾", - "shape": "cylinder", - "sub_components": ["KNOWLEDGE_BASE", "VECTOR_DB", "DOCUMENT_STORE", "CACHE", "MEMORY"] - }, - - "KNOWLEDGE_BASE": { - "shape": "cylinder", - "color": "#009688", - "icon": "📘", - "description": [ - "• Curated domain-specific facts and rules", - "• Structured knowledge representation", - "• Supports inference and reasoning", - "• Maintains consistency and accuracy" - ], - "implementation": { - "python_snippet": """ -class KnowledgeBase: - def __init__(self, storage_backend): - self.storage = storage_backend - self.index = {} - - async def query(self, query_text, context=None): - # Query knowledge base with optional context - results = await self.storage.search(query_text) - return self._format_results(results) - - async def update(self, fact, metadata=None): - # Add or update knowledge fact - await self.storage.insert(fact, metadata) - self._update_index(fact) - """, - "dependencies": ["sqlite3", "nltk"], - "config": { - "max_facts": 100000, - "update_frequency": "daily" - } - } - }, - - "VECTOR_DB": { - "shape": "cylinder", - "color": "#009688", - "icon": "🔍", - "description": [ - "• Embedding-based database for semantic search", - "• Stores vector representations of text", - "• Enables similarity-based retrieval", - "• Supports semantic understanding" - ], - "implementation": { - "python_snippet": """ -import numpy as np -from sentence_transformers import SentenceTransformer - -class VectorDB: - def __init__(self, embedding_model="all-MiniLM-L6-v2"): - self.model = SentenceTransformer(embedding_model) - self.vectors = {} - self.metadata = {} - - async def add_document(self, doc_id, text, metadata=None): - embedding = self.model.encode(text) - self.vectors[doc_id] = embedding - self.metadata[doc_id] = metadata or {} - - async def search(self, query, top_k=5): - query_embedding = self.model.encode(query) - similarities = [] - for doc_id, vector in self.vectors.items(): - similarity = np.dot(query_embedding, vector) / ( - np.linalg.norm(query_embedding) * np.linalg.norm(vector) - ) - similarities.append((doc_id, similarity)) - - return sorted(similarities, key=lambda x: x[1], reverse=True)[:top_k] - """, - "dependencies": ["sentence-transformers", "numpy"], - "config": { - "embedding_model": "all-MiniLM-L6-v2", - "max_documents": 10000 - } - } - }, - - "DOCUMENT_STORE": { - "shape": "cylinder", - "color": "#009688", - "icon": "🗂️", - "description": [ - "• Raw document repository (PDFs, web pages, etc.)", - "• Handles various document formats", - "• Provides document parsing and extraction", - "• Manages document lifecycle and metadata" - ], - "implementation": { - "python_snippet": """ -class DocumentStore: - def __init__(self, storage_path): - self.storage_path = storage_path - self.parsers = { - '.pdf': self._parse_pdf, - '.txt': self._parse_text, - '.docx': self._parse_docx - } - - async def store_document(self, filename, content): - # Parse and store document with metadata - ext = os.path.splitext(filename)[1].lower() - parser = self.parsers.get(ext) - if parser: - parsed_content = await parser(content) - # Store in database with metadata - return await self._save_to_db(filename, parsed_content) - - async def _parse_pdf(self, content): - # Extract text from PDF - import PyPDF2 - pdf_reader = PyPDF2.PdfReader(content) - text = "" - for page in pdf_reader.pages: - text += page.extract_text() - return text - """, - "dependencies": ["PyPDF2", "python-docx"], - "config": { - "supported_formats": [".pdf", ".txt", ".docx", ".html"], - "max_file_size_mb": 100 - } - } - }, - - "CACHE": { - "shape": "cylinder", - "color": "#009688", - "icon": "⏱️", - "description": [ - "• Temporary fast-access storage for responses or embeddings", - "• Implements LRU or TTL eviction policies", - "• Reduces computation and API costs", - "• Improves response times" - ], - "implementation": { - "python_snippet": """ -import time -from collections import OrderedDict - -class Cache: - def __init__(self, max_size=1000, ttl_seconds=3600): - self.cache = OrderedDict() - self.max_size = max_size - self.ttl = ttl_seconds - - async def get(self, key): - if key in self.cache: - value, timestamp = self.cache[key] - if time.time() - timestamp < self.ttl: - return value - else: - del self.cache[key] - return None - - async def set(self, key, value): - if len(self.cache) >= self.max_size: - self.cache.popitem(last=False) - self.cache[key] = (value, time.time()) - """, - "dependencies": ["time", "collections"], - "config": { - "max_size": 1000, - "ttl_seconds": 3600, - "eviction_policy": "lru" - } - } - }, - - "MEMORY": { - "shape": "cylinder", - "color": "#009688", - "icon": "🧠", - "description": [ - "• Short-term context memory (conversation history, scratchpad)", - "• Maintains session state and context", - "• Supports conversation continuity", - "• Manages memory lifecycle" - ], - "implementation": { - "python_snippet": """ -class Memory: - def __init__(self, max_context_length=2000): - self.conversation_history = [] - self.scratchpad = {} - self.max_context_length = max_context_length - - async def add_interaction(self, user_input, agent_response): - interaction = { - "timestamp": time.time(), - "user": user_input, - "agent": agent_response - } - self.conversation_history.append(interaction) - self._trim_history() - - def _trim_history(self): - # Trim history to maintain context length - total_length = sum(len(str(item)) for item in self.conversation_history) - while total_length > self.max_context_length and len(self.conversation_history) > 1: - removed = self.conversation_history.pop(0) - total_length -= len(str(removed)) - """, - "dependencies": ["time"], - "config": { - "max_context_length": 2000, - "history_retention_hours": 24 - } - } - }, - - # =================================== - # PROCESSOR: Data processing units - # =================================== - "PROCESSOR": { - "description": "Data processing and transformation units", - "color": "#2196F3", - "icon": "⚙️", - "shape": "rect", - "sub_components": ["QUERY_PROCESSOR", "CONTENT_RETRIEVAL", "PROMPT_TEMPLATE", "RESPONSE_FORMATTER"] - }, - - "QUERY_PROCESSOR": { - "shape": "rect", - "color": "#2196F3", - "icon": "🔎", - "description": [ - "• Parses and enriches incoming queries", - "• Extracts intent and entities", - "• Normalizes query structure", - "• Handles query validation" - ], - "implementation": { - "python_snippet": """ -class QueryProcessor: - def __init__(self): - self.intent_classifier = IntentClassifier() - self.entity_extractor = EntityExtractor() - - async def process_query(self, query_text): - # Classify intent and extract entities - intent = await self.intent_classifier.classify(query_text) - entities = await self.entity_extractor.extract(query_text) - - return { - "original_query": query_text, - "intent": intent, - "entities": entities, - "processed_query": self._normalize_query(query_text, entities) - } - - def _normalize_query(self, query, entities): - # Normalize query for downstream processing - normalized = query - for entity, value in entities.items(): - normalized = normalized.replace(value, f"[{entity}]") - return normalized - """, - "dependencies": ["spacy", "transformers"], - "config": { - "max_query_length": 1000, - "confidence_threshold": 0.7 - } - } - }, - - "CONTENT_RETRIEVAL": { - "shape": "rect", - "color": "#2196F3", - "icon": "📤", - "description": [ - "• Fetches relevant content from data stores", - "• Implements semantic and keyword search", - "• Ranks and filters retrieved content", - "• Handles multi-source retrieval" - ], - "implementation": { - "python_snippet": """ -class ContentRetrieval: - def __init__(self, data_sources): - self.data_sources = data_sources - - async def retrieve(self, query, top_k=5, sources=None): - all_results = [] - - for source_name, source in self.data_sources.items(): - if sources is None or source_name in sources: - results = await source.search(query, top_k) - all_results.extend(results) - - # Rank and deduplicate results - ranked_results = self._rank_results(all_results, query) - return ranked_results[:top_k] - - def _rank_results(self, results, query): - # Implement ranking algorithm - return sorted(results, key=lambda x: x.get('relevance_score', 0), reverse=True) - """, - "dependencies": ["numpy", "scikit-learn"], - "config": { - "top_k": 5, - "max_sources": 10, - "relevance_threshold": 0.5 - } - } - }, - - "PROMPT_TEMPLATE": { - "shape": "rect", - "color": "#2196F3", - "icon": "📝", - "description": [ - "• Template-based prompt construction", - "• Supports variable substitution", - "• Handles different prompt formats", - "• Manages prompt versioning" - ], - "implementation": { - "python_snippet": """ -from jinja2 import Template - -class PromptTemplate: - def __init__(self, template_string): - self.template = Template(template_string) - - async def format(self, **kwargs): - return self.template.render(**kwargs) - - @classmethod - def load_from_file(cls, file_path): - with open(file_path, 'r') as f: - template_string = f.read() - return cls(template_string) - - def validate_variables(self, required_vars): - # Validate that all required variables are provided - pass - """, - "dependencies": ["jinja2"], - "config": { - "default_template": "You are a helpful assistant. User: {query}", - "max_template_length": 5000 - } - } - }, - - "RESPONSE_FORMATTER": { - "shape": "rect", - "color": "#2196F3", - "icon": "📄", - "description": [ - "• Structures final output (JSON, XML, markdown, etc.)", - "• Applies formatting rules and styles", - "• Validates response structure", - "• Supports multiple output formats" - ], - "implementation": { - "python_snippet": """ -class ResponseFormatter: - def __init__(self): - self.formatters = { - 'json': self._format_json, - 'xml': self._format_xml, - 'markdown': self._format_markdown, - 'text': self._format_text - } - - async def format(self, data, format_type='json'): - formatter = self.formatters.get(format_type) - if formatter: - return formatter(data) - else: - raise ValueError(f"Unsupported format: {format_type}") - - def _format_json(self, data): - import json - return json.dumps(data, indent=2) - """, - "dependencies": ["json", "xml.etree.ElementTree"], - "config": { - "default_format": "json", - "max_output_length": 10000 - } - } - }, - - # =================================== - # ROUTER: Decision points and workflow routing - # =================================== - "ROUTER": { - "description": "Decision points and workflow routing", - "color": "#FF9800", - "icon": "🎯", - "shape": "diamond", - "sub_components": ["INTENT_DISCOVERY", "MODEL_SELECTOR", "WORKFLOW_ROUTER", "VALIDATOR"] - }, - - "INTENT_DISCOVERY": { - "shape": "diamond", - "color": "#FF9800", - "icon": "🎯", - "description": [ - "• Identifies user intent from input", - "• Uses machine learning classification", - "• Handles intent confidence scoring", - "• Supports intent hierarchy" - ], - "implementation": { - "python_snippet": """ -class IntentDiscovery: - def __init__(self, model_path): - self.model = self.load_model(model_path) - - async def discover_intent(self, text): - # Classify intent using trained model - predictions = await self.model.predict(text) - top_intent = max(predictions, key=predictions.get) - confidence = predictions[top_intent] - - return { - "intent": top_intent, - "confidence": confidence, - "all_predictions": predictions - } - """, - "dependencies": ["transformers", "torch"], - "config": { - "confidence_threshold": 0.8, - "fallback_intent": "unknown" - } - } - }, - - "MODEL_SELECTOR": { - "shape": "diamond", - "color": "#FF9800", - "icon": "🧠", - "description": [ - "• Selects appropriate model based on task", - "• Considers task complexity and cost", - "• Handles model availability and load", - "• Supports A/B testing of models" - ], - "implementation": { - "python_snippet": """ -class ModelSelector: - def __init__(self, models): - self.models = models - self.model_performance = {} - - async def select_model(self, task_description, context=None): - # Select best model based on task requirements - suitable_models = self._filter_suitable_models(task_description) - - # Choose based on performance metrics and availability - best_model = self._select_best_model(suitable_models) - return best_model - - def _filter_suitable_models(self, task_description): - # Filter models based on task compatibility - return [model for model in self.models if model.can_handle(task_description)] - """, - "dependencies": ["numpy"], - "config": { - "selection_strategy": "performance_weighted", - "max_model_candidates": 5 - } - } - }, - - "WORKFLOW_ROUTER": { - "shape": "diamond", - "color": "#FF9800", - "icon": "🔄", - "description": [ - "• Routes requests through appropriate workflows", - "• Manages workflow state and transitions", - "• Handles parallel and sequential execution", - "• Supports workflow versioning" - ], - "implementation": { - "python_snippet": """ -class WorkflowRouter: - def __init__(self, workflows): - self.workflows = workflows - self.current_executions = {} - - async def route(self, request, workflow_name=None): - if workflow_name: - workflow = self.workflows.get(workflow_name) - else: - workflow = await self._auto_select_workflow(request) - - execution_id = str(uuid.uuid4()) - self.current_executions[execution_id] = workflow - - result = await workflow.execute(request) - del self.current_executions[execution_id] - - return result - """, - "dependencies": ["uuid", "asyncio"], - "config": { - "max_concurrent_workflows": 100, - "workflow_timeout": 300 - } - } - }, - - "VALIDATOR": { - "shape": "diamond", - "color": "#FF9800", - "icon": "✅", - "description": [ - "• Validates inputs, outputs, and intermediate results", - "• Implements schema and business rule validation", - "• Handles data quality checks", - "• Provides validation feedback" - ], - "implementation": { - "python_snippet": """ -from pydantic import BaseModel, ValidationError - -class Validator: - def __init__(self, schema_class: BaseModel): - self.schema_class = schema_class - - async def validate(self, data): - try: - validated_data = self.schema_class(**data) - return { - "valid": True, - "data": validated_data.dict(), - "errors": [] - } - except ValidationError as e: - return { - "valid": False, - "data": None, - "errors": e.errors() - } - """, - "dependencies": ["pydantic"], - "config": { - "strict_validation": True, - "validation_timeout": 10 - } - } - }, - - # =================================== - # INFRASTRUCTURE: System services - # =================================== - "INFRASTRUCTURE": { - "description": "System infrastructure and services", - "color": "#FF5722", - "icon": "🌐", - "shape": "rect", - "sub_components": ["PROVIDER", "MONITOR", "FALLBACK", "ORCHESTRATOR"] - }, - - "PROVIDER": { - "shape": "rect", - "color": "#FF5722", - "icon": "🌐", - "description": [ - "• API connection to LLM service", - "• Manages authentication and rate limits", - "• Handles retries and error recovery", - "• Tracks usage and costs" - ], - "implementation": { - "python_snippet": """ -class LLMProvider: - def __init__(self, base_url: str, api_key: str = None, model: str = "default"): - self.base_url = base_url - self.api_key = api_key - self.model = model - self.client = AsyncOpenAI(base_url=base_url, api_key=api_key) - self.usage_tracker = UsageTracker() - - async def generate(self, prompt: str, **kwargs) -> str: - try: - response = await self.client.chat.completions.create( - model=self.model, - messages=[{"role": "user", "content": prompt}], - **kwargs - ) - self.usage_tracker.record_usage(response.usage) - return response.choices[0].message.content - except Exception as e: - raise ProviderError(f"Generation failed: {e}") - - def get_cost_estimate(self) -> float: - return self.usage_tracker.calculate_cost() - """, - "supported_providers": { - "openai": {"base_url": "https://api.openai.com/v1", "models": ["gpt-4", "gpt-3.5-turbo"]}, - "anthropic": {"base_url": "https://api.anthropic.com/v1", "models": ["claude-3", "claude-2"]}, - "local": {"base_url": "http://localhost:1234/v1", "models": ["local-model"]}, - "azure": {"base_url": "https://your-resource.openai.azure.com/", "models": ["gpt-4", "gpt-35-turbo"]} - }, - "config_template": { - "base_url": "https://api.openai.com/v1", - "api_key": "your-api-key-here", - "model": "gpt-4", - "max_retries": 3, - "timeout": 30 - } - } - }, - - "MONITOR": { - "shape": "rect", - "color": "#FF5722", - "icon": "📊", - "description": [ - "• Tracks system performance and metrics", - "• Monitors resource usage and errors", - "• Provides health checks and alerts", - "• Supports logging and analytics" - ], - "implementation": { - "python_snippet": """ -import time -import logging -from collections import defaultdict - -class Monitor: - def __init__(self): - self.metrics = defaultdict(list) - self.logger = logging.getLogger(__name__) - - async def record_metric(self, name, value, timestamp=None): - if timestamp is None: - timestamp = time.time() - self.metrics[name].append((timestamp, value)) - - async def get_health_status(self): - recent_errors = [m for m in self.metrics['errors'] if time.time() - m[0] < 300] - avg_response_time = self._calculate_avg_time('response_time', 300) - - return { - "status": "healthy" if len(recent_errors) == 0 else "degraded", - "recent_errors": len(recent_errors), - "avg_response_time": avg_response_time - } - """, - "dependencies": ["logging", "time"], - "config": { - "metrics_retention_hours": 24, - "alert_thresholds": {"error_rate": 0.05, "response_time": 5.0} - } - } - }, - - "FALLBACK": { - "shape": "rect", - "color": "#FF5722", - "icon": "🔄", - "description": [ - "• Provides alternative execution paths", - "• Handles primary system failures", - "• Implements graceful degradation", - "• Maintains service availability" - ], - "implementation": { - "python_snippet": """ -class FallbackHandler: - def __init__(self, primary_handler, fallback_handlers): - self.primary = primary_handler - self.fallbacks = fallback_handlers - - async def execute_with_fallback(self, *args, **kwargs): - try: - return await self.primary(*args, **kwargs) - except PrimaryError as e: - self.logger.warning(f"Primary failed: {e}, trying fallbacks") - - for fallback in self.fallbacks: - try: - return await fallback(*args, **kwargs) - except FallbackError: - continue - - raise ServiceUnavailableError("All fallbacks exhausted") - """, - "dependencies": ["logging"], - "config": { - "max_fallback_attempts": 3, - "fallback_timeout": 10 - } - } - }, - - "ORCHESTRATOR": { - "shape": "rect", - "color": "#FF5722", - "icon": "🎬", - "description": [ - "• Coordinates complex multi-step processes", - "• Manages component interactions", - "• Handles state and error propagation", - "• Supports distributed execution" - ], - "implementation": { - "python_snippet": """ -class Orchestrator: - def __init__(self, components): - self.components = components - self.state = {} - - async def orchestrate(self, workflow_definition, input_data): - current_state = input_data.copy() - - for step in workflow_definition.steps: - component = self.components[step.component] - step_result = await component.execute(current_state, step.config) - current_state.update(step_result) - - return current_state - """, - "dependencies": ["asyncio"], - "config": { - "max_workflow_steps": 100, - "step_timeout": 60 - } - } - } -} - -# Hierarchical Component definitions -COMPONENT_HIERARCHY = { - "HIGH_LEVEL": { - "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"] - } - } -} - -# Enhanced Example workflows -EXAMPLE_WORKFLOWS = { - "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"} - ] - } -} - -@dataclass -class ComponentData: - """Complete component information""" - type: str - shape: str - color: str - icon: str - description: List[str] - category: Optional[str] = None - sub_category: Optional[str] = None - -@dataclass -class AgentNode: - id: str - type: str - x: int - y: int - component_data: ComponentData = field(default_factory=lambda: ComponentData("", "", "", "", [])) - -@dataclass -class Connection: - from_node: str - to_node: str - -class CustomNodeManager: - def __init__(self, storage_path: str = "custom_nodes.pkl"): - self.storage_path = storage_path - self.custom_nodes: Dict[str, Dict[str, Any]] = {} - self.load_custom_nodes() - - def load_custom_nodes(self): - """Load custom nodes from storage""" - if os.path.exists(self.storage_path): - try: - with open(self.storage_path, 'rb') as f: - self.custom_nodes = pickle.load(f) - except Exception as e: - print(f"Error loading custom nodes: {e}") - self.custom_nodes = {} - - def save_custom_nodes(self): - """Save custom nodes to storage""" - try: - with open(self.storage_path, 'wb') as f: - pickle.dump(self.custom_nodes, f) - except Exception as e: - print(f"Error saving custom nodes: {e}") - - def create_custom_node(self, name: str, config: Dict[str, Any]): - """Create a new custom node""" - node_id = f"custom_{name.lower().replace(' ', '_')}" - self.custom_nodes[node_id] = { - "id": node_id, - "name": name, - "type": "CUSTOM", - "config": config, - "created_at": __import__('datetime').datetime.now().isoformat() - } - self.save_custom_nodes() - return node_id - - def get_custom_node_info(self, node_id: str) -> Dict[str, Any]: - """Get information for a custom node""" - return self.custom_nodes.get(node_id, {}) - - def delete_custom_node(self, node_id: str): - """Delete a custom node""" - if node_id in self.custom_nodes: - del self.custom_nodes[node_id] - self.save_custom_nodes() - -# Initialize custom node manager -custom_node_manager = CustomNodeManager() - -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: - """Select a node and deselect others""" - self.selected_node = node_id if node_id in self.nodes else None - - def move_selected_node(self, dx: int, dy: int) -> None: - """Move selected node by delta""" - 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_custom_node(self, custom_config: Dict[str, Any]) -> AgentNode: - """Add a custom node to the workflow""" - self.node_counter += 1 - node_id = f"custom_{self.node_counter}" - - # Create custom node configuration - custom_node_config = { - "shape": custom_config.get("shape", "rect"), - "color": custom_config.get("color", "#666666"), - "icon": custom_config.get("icon", "🔧"), - "description": custom_config.get("description", ["Custom node"]), - "implementation": custom_config.get("implementation", {}) - } - - # Add to COMPONENT_INFO for rendering - COMPONENT_INFO[node_id] = custom_node_config - - col = len(self.nodes) % 3 - row = len(self.nodes) // 3 - x_pos = 200 + (col * 350) - y_pos = 150 + (row * 200) - - node = AgentNode( - id=node_id, - type=node_id, # Use node_id as type for custom nodes - x=x_pos, - y=y_pos - ) - - self.nodes[node_id] = node - self.selected_node = node_id - return node - - def get_workflow_json(self) -> Dict[str, Any]: - """Get complete workflow data including component implementations""" - nodes_data = [] - for node in self.nodes.values(): - node_info = COMPONENT_INFO.get(node.type, {}) - nodes_data.append({ - "id": node.id, - "type": node.type, - "x": node.x, - "y": node.y, - "component_info": node_info, - "implementation": node_info.get("implementation", {}) - }) - - return { - "nodes": nodes_data, - "connections": [asdict(c) for c in self.connections], - "selected_node": self.selected_node, - "metadata": { - "total_nodes": len(self.nodes), - "total_connections": len(self.connections), - "generated_at": __import__('datetime').datetime.now().isoformat() - } - } - - def add_node(self, node_type: str) -> AgentNode: - self.node_counter += 1 - node_id = f"{node_type}_{self.node_counter}" - - col = len(self.nodes) % 3 - row = len(self.nodes) // 3 - x_pos = 200 + (col * 350) - y_pos = 150 + (row * 200) - - # Get complete component information - component_info = COMPONENT_INFO.get(node_type, { - "shape": "rect", - "color": "#666666", - "icon": "❓", - "description": ["Unknown component type"] - }) - - # Create component data with full information - component_data = ComponentData( - type=node_type, - shape=component_info["shape"], - color=component_info["color"], - icon=component_info["icon"], - description=component_info["description"], - category=self._find_component_category(node_type), - sub_category=self._find_component_sub_category(node_type) - ) - - node = AgentNode( - id=node_id, - type=node_type, - x=x_pos, - y=y_pos, - component_data=component_data - ) - - self.nodes[node_id] = node - self.selected_node = node_id - return node - - def _find_component_category(self, node_type: str) -> Optional[str]: - """Find which high-level category this component belongs to""" - - for category, components in COMPONENT_HIERARCHY["HIGH_LEVEL"].items(): - if node_type == category or node_type in components.get('sub_components', []): - return category - return None - - def _find_component_sub_category(self, node_type: str) -> Optional[str]: - """Determine if this is a high-level or sub-component""" - - for category, components in COMPONENT_HIERARCHY["HIGH_LEVEL"].items(): - if node_type == category: - return "HIGH_LEVEL" - elif node_type in components.get('sub_components', []): - return "SUB_COMPONENT" - return None - - 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"] - - # Get complete component information for example nodes too - component_info = COMPONENT_INFO.get(node_type, { - "shape": "rect", - "color": "#666666", - "icon": "❓", - "description": ["Unknown component type"] - }) - - component_data = ComponentData( - type=node_type, - shape=component_info["shape"], - color=component_info["color"], - icon=component_info["icon"], - description=component_info["description"], - category=self._find_component_category(node_type), - sub_category=self._find_component_sub_category(node_type) - ) - - node = AgentNode( - id=node_data["id"], - type=node_type, - x=node_data["x"], - y=node_data["y"], - 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]: - """Get complete workflow data including full component information""" - return { - "metadata": { - "total_nodes": len(self.nodes), - "total_connections": len(self.connections), - "selected_node": self.selected_node, - "generated_with": "Agent Workflow Designer" - }, - "nodes": [ - { - "id": node.id, - "type": node.type, - "position": {"x": node.x, "y": node.y}, - "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, - "category": node.component_data.category, - "sub_category": node.component_data.sub_category - } - } - for node in self.nodes.values() - ], - "connections": [ - { - "from": conn.from_node, - "to": conn.to_node - } - for conn in self.connections - ] - } - - def render_svg(self) -> str: - """Render workflow as beautiful SVG with selection support""" - if not self.nodes: - return ''' - - - - - - - - - 🚀 Start Building Your Workflow - Add components from the library on the left - - ''' - - width = 1200 - height = max(600, max([n.y for n in self.nodes.values()], default=0) + 200) - - svg_parts = [ - f'', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '', - '' - ] - - # Draw connections with glow - 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 - - # Glow path - svg_parts.append( - f'' - ) - - # Main path - svg_parts.append( - f'' - ) - - # Draw nodes with selection support - for node in self.nodes.values(): - # Use the stored component data instead of looking it up - shape = node.component_data.shape - color = node.component_data.color - icon = node.component_data.icon - - cx = node.x + 85 - cy = node.y + 60 - label = node.id.replace("_", " ").title() - - 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 with selection highlight - if shape == "ellipse": - svg_parts.append( - f'' - ) - elif shape == "diamond": - size = 70 - points = f"{cx},{cy-size} {cx+size},{cy} {cx},{cy+size} {cx-size},{cy}" - svg_parts.append( - f'' - ) - elif shape == "hexagon": - w, h = 70, 50 - points = f"{cx-w},{cy-h/2} {cx-w/2},{cy-h} {cx+w/2},{cy-h} {cx+w},{cy-h/2} {cx+w},{cy+h/2} {cx+w/2},{cy+h} {cx-w/2},{cy+h} {cx-w},{cy+h/2}" - svg_parts.append( - f'' - ) - elif shape == "cylinder": - svg_parts.append( - f'' - ) - svg_parts.append( - f'' - ) - svg_parts.append( - f'' - ) - svg_parts.append( - f'' - ) - svg_parts.append( - f'' - ) - else: # rect - svg_parts.append( - f'' - ) - - # Icon - svg_parts.append( - f'{icon}' - ) - - # Label - svg_parts.append( - f'{label}' - ) - - # Add JavaScript for drag and drop - svg_parts.append(''' - - ''') - - svg_parts.append('') - return '\n'.join(svg_parts) - -workflow = WorkflowDesigner() - -# Report generation class -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)}" - -# Initialize reporter -reporter = WorkflowReporter() - -def create_workflow_ui(): - 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 and understanding agent architectures**") - - # Hidden components for JavaScript communication - select_node_trigger = gr.Textbox(visible=False) - move_node_trigger = gr.Textbox(visible=False) - - # Define all UI components first - with gr.Row(): - # Left Sidebar - Component Library - with gr.Column(scale=1): - gr.Markdown("## 📚 Component Library") - - # Store component buttons for later connection - component_buttons = [] - - # High-level component accordions - for category, components in COMPONENT_HIERARCHY["HIGH_LEVEL"].items(): - with gr.Accordion(f"{components['icon']} {category}", open=False): - # High-level component button - high_level_btn = gr.Button( - f"{components['icon']} {category}", - size="sm", - variant="primary" - ) - component_buttons.append((high_level_btn, category)) - - # Sub-components - if components['sub_components']: - gr.Markdown("**Sub-components:**") - for sub_comp in components['sub_components']: - 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)) - - gr.Markdown("---") - gr.Markdown("## 🔗 Connect Nodes") - 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") - - 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") - - gr.Markdown("---") - with gr.Row(): - download_json_btn = gr.Button("💾 Download JSON", variant="primary", size="sm") - download_svg_btn = gr.Button("🖼️ Download SVG", variant="primary", size="sm") - clear_btn = gr.Button("🗑️ Clear All", variant="stop", size="sm") - - # Output for multiple downloadable files - download_files = gr.Files(label="📥 Download Files", visible=True) - - # Center - Canvas - with gr.Column(scale=3): - gr.Markdown("## 🎨 Workflow Canvas") - gr.Markdown("**💡 Tip:** Click nodes to select, then drag or use arrow keys") - canvas = gr.HTML() - - gr.Markdown("## 📖 Component Information") - component_info = gr.Markdown("Select a component to see its description") - - # Right Sidebar - Movement Controls - with gr.Column(scale=1): - gr.Markdown("## 🎯 Selection & Movement") - - gr.Markdown("**Navigation:**") - 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("**Selected Node:**") - selected_node_info = gr.Markdown("No node selected") - - 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") - - gr.Markdown("**Movement Modes:**") - with gr.Row(): - move_fine_btn = gr.Button("🎯 Fine (5px)", size="sm") - move_coarse_btn = gr.Button("🚀 Coarse (50px)", size="sm") - - gr.Markdown("---") - with gr.Accordion("🗑️ Delete Selected", open=False): - delete_selected_btn = gr.Button("❌ Delete Selected Node", variant="stop", size="sm") - - gr.Markdown("---") - with gr.Accordion("📊 Workflow Data", open=False): - json_output = gr.Code(language="json", label="Workflow JSON", lines=10) - - gr.Markdown("---") - with gr.Accordion("📋 Generate Report", open=False): - report_btn = gr.Button("📄 Generate System Report", variant="primary") - report_output = gr.Textbox(label="System Design Report", lines=15, interactive=False) - download_report_btn = gr.Button("📝 Download Report", variant="secondary", size="sm") - - # Now define all the handler functions after UI components are defined - def get_full_state(): - svg = workflow.render_svg() - node_choices = list(workflow.nodes.keys()) - workflow_json = json.dumps({ - "nodes": [asdict(n) for n in workflow.nodes.values()], - "connections": [asdict(c) for c in workflow.connections], - "selected_node": workflow.selected_node - }, indent=2) - - selected_info = "**No node selected**" - comp_info = "Select a component to see its description" - - if workflow.selected_node and workflow.selected_node in workflow.nodes: - node = workflow.nodes[workflow.selected_node] - info = COMPONENT_INFO[node.type] - selected_info = f"**Selected:** `{node.id}` ({info['icon']} {node.type.replace('_', ' ').title()}) at position ({node.x}, {node.y})" - comp_info = f"### {node.type.replace('_', ' ').title()} {info['icon']}\n\n" + "\n".join(info["description"]) - - return svg, workflow_json, node_choices, selected_info, comp_info - - def add_node_handler(node_type): - node = workflow.add_node(node_type) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, gr.Dropdown(choices=choices), gr.Dropdown(choices=choices), selected_info, comp_info - - # Connect all component buttons - for btn, comp_type in component_buttons: - btn.click( - lambda ct=comp_type: add_node_handler(ct), - outputs=[canvas, json_output, from_node, to_node, selected_node_info, component_info] - ) - - # Selection handlers - def select_node_handler(node_id): - if node_id: - workflow.select_node(node_id) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - return workflow.render_svg(), "", "No node selected", "Select a component to see its description" - - def select_next_node(): - 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) - next_idx = (current_idx + 1) % len(node_ids) - workflow.selected_node = node_ids[next_idx] - - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - - def select_prev_node(): - if workflow.nodes: - node_ids = list(workflow.nodes.keys()) - if not workflow.selected_node: - workflow.selected_node = node_ids[-1] - else: - current_idx = node_ids.index(workflow.selected_node) - prev_idx = (current_idx - 1) % len(node_ids) - workflow.selected_node = node_ids[prev_idx] - - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - - def deselect_all(): - workflow.selected_node = None - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - - # Delete selected node - def delete_selected_node(): - if workflow.selected_node and workflow.selected_node in workflow.nodes: - workflow.connections = [ - c for c in workflow.connections - if c.from_node != workflow.selected_node and c.to_node != workflow.selected_node - ] - del workflow.nodes[workflow.selected_node] - workflow.selected_node = None - - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, gr.Dropdown(choices=choices), gr.Dropdown(choices=choices), selected_info, comp_info - - # Movement handlers - def move_selected_node(dx, dy): - if workflow.selected_node: - workflow.move_selected_node(dx, dy) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - return workflow.render_svg(), "", "No node selected", component_info.value - - # Connect selection events - select_node_trigger.change( - select_node_handler, - inputs=[select_node_trigger], - outputs=[canvas, json_output, selected_node_info, component_info] - ) - - select_next_btn.click(select_next_node, outputs=[canvas, json_output, selected_node_info, component_info]) - select_prev_btn.click(select_prev_node, outputs=[canvas, json_output, selected_node_info, component_info]) - deselect_btn.click(deselect_all, outputs=[canvas, json_output, selected_node_info, component_info]) - - # Movement buttons - move_left_btn.click(lambda: move_selected_node(-20, 0), outputs=[canvas, json_output, selected_node_info, component_info]) - move_right_btn.click(lambda: move_selected_node(20, 0), outputs=[canvas, json_output, selected_node_info, component_info]) - move_up_btn.click(lambda: move_selected_node(0, -20), outputs=[canvas, json_output, selected_node_info, component_info]) - move_down_btn.click(lambda: move_selected_node(0, 20), outputs=[canvas, json_output, selected_node_info, component_info]) - move_fine_btn.click(lambda: move_selected_node(-5, 0), outputs=[canvas, json_output, selected_node_info, component_info]) - move_coarse_btn.click(lambda: move_selected_node(-50, 0), outputs=[canvas, json_output, selected_node_info, component_info]) - - # Delete button - delete_selected_btn.click( - delete_selected_node, - outputs=[canvas, json_output, from_node, to_node, selected_node_info, component_info] - ) - - # Drag handler - def handle_node_drag(move_data): - try: - data = json.loads(move_data) - node_id = data.get('node_id') - dx = data.get('dx', 0) - dy = data.get('dy', 0) - if node_id and node_id in workflow.nodes: - workflow.select_node(node_id) - workflow.move_selected_node(dx, dy) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info, comp_info - except Exception as e: - print("Drag error:", e) - return workflow.render_svg(), "", "Drag completed", component_info.value - - move_node_trigger.change( - handle_node_drag, - inputs=[move_node_trigger], - outputs=[canvas, json_output, selected_node_info, component_info] - ) - - # Connection handler - def connect_nodes(from_n, to_n): - if from_n and to_n and from_n != to_n: - existing = [c for c in workflow.connections if c.from_node == from_n and c.to_node == to_n] - if not existing: - workflow.connections.append(Connection(from_node=from_n, to_node=to_n)) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - return svg, wf_json, selected_info - return workflow.render_svg(), "", selected_node_info.value - - connect_btn.click(connect_nodes, inputs=[from_node, to_node], outputs=[canvas, json_output, selected_node_info]) - - # Example loading - def load_example_handler(example_name): - if example_name: - workflow.load_example(example_name) - svg, wf_json, choices, selected_info, comp_info = get_full_state() - desc = EXAMPLE_WORKFLOWS[example_name]["description"] - info_text = f"### {example_name}\n\n{desc}" - return svg, wf_json, gr.Dropdown(choices=choices), gr.Dropdown(choices=choices), selected_info, info_text - return workflow.render_svg(), "", gr.Dropdown(choices=[]), gr.Dropdown(choices=[]), "No node selected", "Select a component to see its description" - - load_example_btn.click( - load_example_handler, - inputs=[example_dropdown], - outputs=[canvas, json_output, from_node, to_node, selected_node_info, component_info] - ) - - # Unified download handler (returns list of files) - def download_all_files(): - file_list = [] - fid = str(uuid.uuid4()) - - # JSON - json_data = { ... } - json_path = tempfile.mktemp(suffix=f"_{fid}.json") - with open(json_path, "w", encoding="utf-8") as f: - json.dump(json_data, f, indent=2) - file_list.append(json_path) - - # SVG - svg_path = tempfile.mktemp(suffix=f"_{fid}.svg") - with open(svg_path, "w", encoding="utf-8") as f: - f.write(workflow.render_svg()) - file_list.append(svg_path) - - return file_list - # In your download_json function, replace: - def download_json(): - fid = str(uuid.uuid4()) - # Use the new method that includes full component data - json_data = workflow.get_workflow_json() - json_path = tempfile.mktemp(suffix=f"_{fid}.json") - with open(json_path, "w", encoding="utf-8") as f: - json.dump(json_data, f, indent=2) - return [json_path] - # Download SVG only - def download_svg(): - fid = str(uuid.uuid4()) - svg_content = workflow.render_svg() - svg_path = tempfile.mktemp(suffix=f"_{fid}.svg") - with open(svg_path, "w", encoding="utf-8") as f: # ←← KEY CHANGE: encoding="utf-8" - f.write(svg_content) - return [svg_path] - # Report generation (sync wrapper) - def sync_generate_report(): - workflow_data = { - "nodes": [asdict(n) for n in workflow.nodes.values()], - "connections": [asdict(c) for c in workflow.connections], - "selected_node": workflow.selected_node - } - json_str = json.dumps(workflow_data, indent=2) - try: - report = asyncio.run(reporter.generate_report(json_str)) - except Exception as e: - report = f"Failed to generate report: {e}" - return report - - def download_report(): - report_text = sync_generate_report() - fid = str(uuid.uuid4()) - txt_path = tempfile.mktemp(suffix=f"_report_{fid}.txt") - with open(txt_path, "w", encoding="utf-8") as f: # ←← - f.write(f"Agentic Workflow Design Report\nGenerated on: {str(__import__('datetime').datetime.now())}\n\n") - f.write(report_text) - return [txt_path] - - - # Attach handlers - download_json_btn.click(download_json, outputs=[download_files]) - download_svg_btn.click(download_svg, outputs=[download_files]) - report_btn.click(sync_generate_report, outputs=[report_output]) - download_report_btn.click(download_report, outputs=[download_files]) - - # Clear handler - def clear_all(): - workflow.nodes.clear() - workflow.connections.clear() - workflow.node_counter = 0 - workflow.selected_node = None - svg = workflow.render_svg() - return ( - svg, - "{}", - gr.Dropdown(choices=[]), - gr.Dropdown(choices=[]), - "No node selected", - "Canvas cleared. Ready to build!" - ) - - clear_btn.click(clear_all, outputs=[canvas, json_output, from_node, to_node, selected_node_info, component_info]) - - # Initialize with JavaScript support - def init_app(): - svg = workflow.render_svg() - js_code = ''' - - ''' - return svg + js_code - - demo.load(init_app, outputs=[canvas]) - - return demo - - -if __name__ == "__main__": - demo = create_workflow_ui() - demo.launch() +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 +#================================================================================ +## UML TOOLOX ! +# Unified Hierarchical Component definitions for UML and Object-Oriented 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"] + } + } +} +# 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": ["<>", "<>", ""], "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": "<>", "default": "<>"}, + } + }, + + "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": "<>", "default": "<>"}, + "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": "<>"}, + } + }, + + # =================================== + # 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": "<>"}, + } + } +} +# 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"} + ] + } +} +#================================================================================ +#================================================================================ +## AGENTIC TOOLBOX - FIXED VERSION +#================================================================================ + +# Unified Hierarchical Component definitions +COMPONENT_HIERARCHY = { + "HIGH_LEVEL": { + "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 - ENSURED ALL HAVE config_fields +COMPONENT_INFO = { + # 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"}, + } + }, +} + +# Example workflows +EXAMPLE_WORKFLOWS = { + "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 ''' + + + + + + + + + 🚀 Start Building Your Workflow + Add components from the library + + ''' + + width = 1200 + height = max(600, max([n.y for n in self.nodes.values()], default=0) + 200) + + svg_parts = [ + f'', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '', + '' + ] + + # 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'' + ) + + # 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'' + ) + elif comp_data.shape == "cylinder": + svg_parts.append( + f'' + ) + svg_parts.append( + f'' + ) + svg_parts.append( + f'' + ) + svg_parts.append( + f'' + ) + svg_parts.append( + f'' + ) + 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'' + ) + 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'' + ) + else: # rect, document, folder + svg_parts.append( + f'' + ) + + # Icon + svg_parts.append( + f'{comp_data.icon}' + ) + + # Label + label_display = node.label[:15] + "..." if len(node.label) > 15 else node.label + svg_parts.append( + f'{label_display}' + ) + + svg_parts.append('') + 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("## 🔗 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") + + 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") + + # 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 = ''' + + ''' + + 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("") + + with gr.Row(equal_height=False): + # Left Sidebar + with gr.Column(scale=1, min_width=300): + gr.Markdown("## 📚 Component Library") + + with gr.Tabs(): + 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']}*") + + 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("---") + gr.Markdown("## 🔗 Connections") + 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") + + 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(scale=3): + gr.Markdown("## 🎨 Canvas") + canvas = gr.HTML(label="Workflow") + + with gr.Row(): + with gr.Column(scale=1): + gr.Markdown("### 🎯 Selected") + selected_node_info = gr.Markdown("No node selected") + + 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=2): + gr.Markdown("### ⚙️ Configuration") + config_display = gr.JSON(label="Current Config") + + # Right Sidebar + with gr.Column(scale=1, min_width=300): + gr.Markdown("## 📊 Info") + + with gr.Accordion("📋 JSON", open=False): + 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") + + # Config Modal - FIXED: Proper modal implementation + 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 + + # 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: + f.write(workflow.render_svg()) + + 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) \ No newline at end of file