FlowLab / app.py
jayavibhav's picture
Update app.py
ee8551d verified
import gradio as gr
import json
import sys
import os
import modal
import datetime
import base64
from anthropic import Anthropic
import subprocess
import re
import time
#prompts
modal_params = open("modal_params.txt", 'r').read()
modal_demo = open("modal_demo.txt", 'r').read()
all_nodes = open("all_nodes.txt", 'r').read()
external_modal_config = ""
gradio_design_json = ""
base_prompt = """
You are an expert at building and deploying gradio apps, the current project is a workflow builder which builds ai workflows which have to be deployed to Modal.
You will think and use your powerful reasoning to develop the app. You will be given what all the nodes look like plus their paramters/properties along with suitable python code for each node.
You will also be given all possible modal deployment configurations (most of the times a simple deployment configuration works) and also a modal gradio app demo.
You will strictly need to follow the format and also ensure that there are no errors.
You will only return the python code and nothing else.
Sometimes, the user may give a gradio design image along with a json for the design of the gradio app, incase this is given you will follow that during app creation, if this is not there ignore this sentence.
### Core Nodes:
- **Input**: Handles various data types (string, image, video, audio, file)
- **Output**: Displays final results
- **APIRequest**: Makes HTTP requests with headers and body
- **WebSearch**: Performs web searches using DuckDuckGo
- **ExecutePython**: Executes custom Python code
- **ConditionalLogic**: Implements if/else branching logic
- **Wait**: Adds delays to workflows
- **ChatModel**: Integrates OpenAI/Anthropic LLMs
### AI/ML Nodes:
- **KnowledgeBase**: Creates RAG knowledge bases from URLs/directories
- **RAGQuery**: Performs retrieval-augmented generation
- **HFSpeechToText**: Transcribes audio using Whisper
- **HFTextToSpeech**: Generates speech from text
- **HFTextGeneration**: Generates text using Hugging Face models
- **HFImageGeneration**: Creates images using Stable Diffusion
- **HFVisionModel**: Performs image captioning/analysis
- **NebiusImage**: Generates images using Nebius API
### Advanced Nodes:
- **MCPConnection**: Connects to MCP servers
- **MCPAgent**: Creates AI agents with MCP tools
## Node Implementation Functions:
Each node has a corresponding `process_[node_type]()` function that takes the node's parameters and returns results.
## Workflow Structure:
{
"workflow_id": "example-v1",
"workflow_name": "Example Workflow",
"nodes": [...],
"edges": [
{
"source": "Node-1",
"source_handle": "output_field",
"target": "Node-2",
"target_handle": "input_field"
}
]
}
here are some of the requirements you can refer:
# Core workflow framework
pydantic==2.11.0 # Prevents the schema validation error make sure to add this for all apps
gradio==5.33.0
# AI/LLM Dependencies
llama-index # Latest is compatible with Python <3.12
# LlamaIndex Extensions
llama-index-llms-openai
llama-index-llms-anthropic
llama-index-embeddings-huggingface
llama-index-readers-web
llama-index-tools-requests
sentence-transformers
# Web Search & API Tools
duckduckgo-search
# MCP Integration
fastmcp
if youre not sure use the version you are sure with.
you are free to make assumptions but stick to the workflow. (Also you just have to use the workflow node corresponding code as a reference, while youre making the app make sure there are no errors especially data type errors)
Also if there are any secrets ask the user to use thier own in the gradio app, also use the latest modal code (try to keep it simple and avoid too many params)
also bro first do the module installations then imports otherwise the app will fail and one more thing, the app should be named after what the workflow is named.
no errors please!
Bro this is very important!!, first run the library installations, after that you import it!!!, make sure you dont make a mistake here
bro there will be no api keys in the environment, you have to ask the user to enter them!!!
also for modal deployment use gpu=modal.gpu.T4(count=1), the other function is depricated
you may now create the gradio app,
here are your inputs:
"""
# Initialize Anthropic client
try:
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
ANTHROPIC_AVAILABLE = True
print("βœ… Anthropic client initialized successfully!")
except Exception as e:
ANTHROPIC_AVAILABLE = False
print(f"❌ Anthropic client initialization failed: {e}")
# Get the current directory and potential component paths
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
# Try multiple possible locations for components
possible_paths = [
parent_dir, # Parent directory
current_dir, # Current directory
os.path.join(parent_dir, 'workflowbuilder'),
os.path.join(parent_dir, 'gradiodesigner'),
os.path.join(parent_dir, 'iframecomponent')
]
# Add all possible paths to sys.path
for path in possible_paths:
if os.path.exists(path) and path not in sys.path:
sys.path.insert(0, path)
# Try to import components with correct package names
COMPONENTS_AVAILABLE = {}
try:
from gradio_workflowbuilder import WorkflowBuilder
COMPONENTS_AVAILABLE['workflow'] = True
print("βœ… WorkflowBuilder imported successfully!")
except ImportError as e:
COMPONENTS_AVAILABLE['workflow'] = False
print(f"❌ WorkflowBuilder not available: {e}")
try:
from gradio_gradiodesigner import GradioDesigner
COMPONENTS_AVAILABLE['designer'] = True
print("βœ… GradioDesigner imported successfully!")
except ImportError as e:
COMPONENTS_AVAILABLE['designer'] = False
print(f"❌ GradioDesigner not available: {e}")
try:
from gradio_iframecomponent import IFrame
COMPONENTS_AVAILABLE['iframe'] = True
print("βœ… IFrame imported successfully!")
except ImportError as e:
COMPONENTS_AVAILABLE['iframe'] = False
print(f"❌ IFrame not available: {e}")
print(f"\nπŸ“Š Components Status: {COMPONENTS_AVAILABLE}")
# Global state for app data
app_state = {
"name": "",
"workflow": None,
"design": None,
"config": None
}
# Backend integration - Your provided functions
def create_prompt(base_prompt, input_workflow, modal_params, modal_demo, all_nodes, external_modal_config, gradio_design_json):
base_prompt = base_prompt + "Here are all of the workflow nodes plus corresponding python codes \n" + all_nodes
base_prompt = base_prompt + "Here are the modal parameters you can use \n " + modal_params
base_prompt = base_prompt + "Here is the modal gradio app demo \n" + modal_demo
if len(external_modal_config) > 1:
base_prompt = base_prompt + "Use this modal config given by the user: \n" + external_modal_config
if len(gradio_design_json) > 1:
base_prompt = base_prompt + "Use this gradio app design json: \n" + gradio_design_json
base_prompt = base_prompt + "Here is the input workflow \n" + input_workflow
return base_prompt
def query_anthropic(base_prompt, gradio_image_path=None):
if not ANTHROPIC_AVAILABLE:
raise Exception("Anthropic client not available. Please set ANTHROPIC_API_KEY environment variable.")
content = [{"type": "text", "text": base_prompt}]
if gradio_image_path and os.path.exists(gradio_image_path):
try:
with open(gradio_image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode()
content.append({
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data
}
})
except (IOError, OSError) as e:
print(f"Warning: Could not read image file: {e}")
pass
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=5000,
messages=[{
"role": "user",
"content": content
}]
)
return response.content[0].text
def generate_gradio_app(base_prompt, gradio_image_path=None):
try:
response = query_anthropic(base_prompt, gradio_image_path)
# Extract Python code from response
start_idx = response.find("```")
end_idx = response.rfind("```")
if start_idx == -1 or end_idx == -1:
raise Exception("No Python code found in response")
gradio_code = response[start_idx + 9:end_idx]
# Ensure the directory exists and write to temp_app.py
temp_app_path = os.path.join(current_dir, "temp_app.py")
with open(temp_app_path, 'w', encoding='utf-8') as temp_app:
temp_app.write(gradio_code)
print(f"βœ… Generated app saved to: {temp_app_path}")
return True
except Exception as e:
print(f"❌ Error generating Gradio app: {e}")
return False
def parse_modal_url_specific(deployment_output):
pattern = r'https://jniranja--[a-zA-Z0-9\-]+\.modal\.run'
match = re.search(pattern, deployment_output)
if match:
return match.group(0)
return None
def deploy_gradio_app():
try:
temp_app_path = os.path.join(current_dir, "temp_app.py")
if not os.path.exists(temp_app_path):
return "❌ temp_app.py not found. Generate app first."
print("πŸš€ Starting Modal deployment...")
time.sleep(1)
# Run modal deploy command
result = subprocess.run(
["modal", "deploy", temp_app_path],
capture_output=True,
text=True,
cwd=current_dir,
timeout=360 # 5 minute timeout
)
print("πŸ“‹ Deployment output:")
print(result.stdout)
if result.stderr:
print("⚠️ Deployment stderr:")
print(result.stderr)
# Parse URL from output
app_url = parse_modal_url_specific(result.stdout)
if result.returncode != 0 or not app_url:
return "❌ Deployment failed. Check logs above."
print(f"βœ… Deployment successful: {app_url}")
return app_url
except subprocess.TimeoutExpired:
return "❌ Deployment timed out after 5 minutes"
except Exception as e:
print(f"❌ Deployment error: {e}")
return f"❌ Deployment error: {str(e)}"
def main_function(base_prompt, input_workflow, modal_params, modal_demo, all_nodes, external_modal_config="", gradio_design_json="", gradio_image_path=None):
try:
print("πŸ”§ Setting things up!")
# Create the full prompt
full_prompt = create_prompt(
base_prompt,
input_workflow,
modal_params,
modal_demo,
all_nodes,
external_modal_config,
gradio_design_json
)
print("πŸ€– Generating Gradio app with AI...")
# Generate the app
if generate_gradio_app(full_prompt, gradio_image_path):
print("πŸš€ Deploying Gradio app to Modal...")
app_url = deploy_gradio_app()
# Retry once if deployment failed
if app_url.startswith("❌"):
print("πŸ”„ Deployment failed, trying again...")
if generate_gradio_app(full_prompt, gradio_image_path):
app_url = deploy_gradio_app()
else:
return "❌ Failed to regenerate app"
return app_url
else:
return "❌ Failed to generate Gradio app"
except Exception as e:
print(f"❌ Error in main_function: {e}")
return f"❌ Error: {str(e)}"
# Helper functions for the frontend
def analyze_workflow(workflow_data):
"""Analyze the workflow and provide insights"""
if not workflow_data or not isinstance(workflow_data, dict):
return "No workflow data available"
nodes = workflow_data.get("nodes", [])
edges = workflow_data.get("edges", [])
# Count node types
node_counts = {}
for node in nodes:
node_type = node.get("type", "unknown")
node_counts[node_type] = node_counts.get(node_type, 0) + 1
# Category analysis
input_nodes = [n for n in nodes if n.get("type", "").endswith("Input")]
processing_nodes = [n for n in nodes if n.get("type") in ["ChatModel", "OpenAIModel", "RAGQuery", "ExecutePython", "ConditionalLogic", "APIRequest"]]
output_nodes = [n for n in nodes if n.get("type", "").endswith("Output")]
analysis = f"""## πŸ” Workflow Analysis
### Overview
- **Total Nodes:** {len(nodes)}
- **Total Connections:** {len(edges)}
- **Workflow Complexity:** {'Simple' if len(nodes) <= 5 else 'Medium' if len(nodes) <= 10 else 'Complex'}
### Node Categories
- **Input Nodes:** {len(input_nodes)} πŸ“₯
- **Processing Nodes:** {len(processing_nodes)} βš™οΈ
- **Output Nodes:** {len(output_nodes)} πŸ“€
### Node Details"""
for node_type, count in node_counts.items():
emoji_map = {
"ChatInput": "πŸ’¬", "Input": "πŸ“₯", "ChatOutput": "πŸ’­", "Output": "πŸ“€",
"ChatModel": "🧠", "OpenAIModel": "🎯", "RAGQuery": "πŸ”Ž", "KnowledgeBase": "πŸ“–",
"ExecutePython": "🐍", "ConditionalLogic": "πŸ”€", "APIRequest": "🌐", "WebSearch": "πŸ”"
}
emoji = emoji_map.get(node_type, "⚑")
analysis += f"\n- **{node_type}:** {count} {emoji}"
return analysis
def add_app_name_to_workflow(workflow_data, app_name):
"""Add app name to workflow metadata"""
if not workflow_data:
workflow_data = {"nodes": [], "edges": []}
workflow_data["app_name"] = app_name
workflow_data["created_at"] = datetime.datetime.now().isoformat()
workflow_data["workflow_id"] = f"{app_name.lower().replace(' ', '-')}-{int(datetime.datetime.now().timestamp())}"
return workflow_data
def save_workflow_with_name(workflow_data, app_name):
"""Save workflow with app name added"""
if app_name:
app_state["name"] = app_name
updated_workflow = add_app_name_to_workflow(workflow_data, app_name)
app_state["workflow"] = updated_workflow
return updated_workflow
return workflow_data
def validate_workflow(workflow_data):
"""Validate if workflow is complete and ready for deployment"""
if not workflow_data:
return False, "No workflow data found"
nodes = workflow_data.get("nodes", [])
edges = workflow_data.get("edges", [])
if len(nodes) == 0:
return False, "Workflow must have at least one node"
# Check for input and output nodes
has_input = any(n.get("type", "").endswith("Input") for n in nodes)
has_output = any(n.get("type", "").endswith("Output") for n in nodes)
if not has_input:
return False, "Workflow must have at least one Input node"
if not has_output:
return False, "Workflow must have at least one Output node"
if len(edges) == 0 and len(nodes) > 1:
return False, "Nodes must be connected with edges"
return True, "Workflow is valid and ready for deployment"
def export_workflow(workflow_data):
"""Export workflow as formatted JSON"""
if not workflow_data:
return "No workflow to export"
return json.dumps(workflow_data, indent=2)
def generate_python_code(workflow_data):
"""Generate Python code from workflow"""
if not workflow_data:
return "No workflow data"
app_name = workflow_data.get("app_name", "MyApp")
nodes = workflow_data.get("nodes", [])
code = f'''import gradio as gr
import json
def create_{app_name.lower().replace(" ", "_")}_app():
"""Generated {app_name} from FlowForge visual editor"""
# Initialize components
'''
# Generate code for each node
for node in nodes:
node_type = node.get("type", "")
data = node.get("data", {})
node_id = node.get("id", "").replace("-", "_")
display_name = data.get("display_name", "Component")
if node_type == "ChatInput":
code += f''' {node_id} = gr.Textbox(
label="{display_name}",
placeholder="Enter your message...",
lines=2
)
'''
elif node_type == "Input":
data_type = data.get("template", {}).get("data_type", {}).get("value", "string")
if data_type == "image":
code += f''' {node_id} = gr.Image(label="{display_name}")
'''
elif data_type == "file":
code += f''' {node_id} = gr.File(label="{display_name}")
'''
else:
code += f''' {node_id} = gr.Textbox(label="{display_name}")
'''
elif node_type in ["ChatModel", "OpenAIModel"]:
code += f''' # {display_name} - AI Model Processing
def {node_id}_process(input_text):
# TODO: Implement your AI model logic here
# Model: {data.get("template", {}).get("model", {}).get("value", "gpt-4")}
return f"AI Response: {{input_text}}"
'''
elif node_type in ["ChatOutput", "Output"]:
code += f''' {node_id} = gr.Textbox(
label="{display_name}",
interactive=False
)
'''
code += f'''
# Create the interface
with gr.Blocks(title="{app_name}") as demo:
gr.Markdown("# {app_name}")
gr.Markdown("*Generated with FlowForge*")
# Add your component layout here
# TODO: Connect components based on your workflow
return demo
if __name__ == "__main__":
app = create_{app_name.lower().replace(" ", "_")}_app()
app.launch()
'''
return code
def create_modal_config(cpu, memory, gpu_type, gpu_count, min_containers, max_containers):
"""Create Modal deployment configuration"""
config = {
"image": "modal.Image.debian_slim(python_version='3.12').pip_install('torch', 'transformers', 'accelerate', 'gradio')",
"cpu": float(cpu),
"memory": int(memory),
"gpu": f"modal.gpu.{gpu_type}(count={gpu_count})" if gpu_type != "none" else "none",
"min_containers": int(min_containers),
"max_containers": int(max_containers),
"timeout": 3600,
"max_concurrent_inputs": 100
}
app_state["config"] = config
return json.dumps(config, indent=2)
def build_app_with_backend(workflow_json, design_json, modal_config, app_name):
"""FIXED: Build and deploy the app using the backend integration"""
if not app_name:
return "❌ Please set an app name first"
if not ANTHROPIC_AVAILABLE:
return "❌ Anthropic API not available. Please set ANTHROPIC_API_KEY environment variable."
# Validate workflow
# is_valid, message = validate_workflow(workflow_json)
# if not is_valid:
# return f"❌ Workflow validation failed: {message}"
try:
# Prepare data for backend
input_workflow = json.dumps(workflow_json, indent=2) if workflow_json else ""
print(input_workflow)
external_modal_config = str(modal_config) if modal_config else ""
gradio_design_json = json.dumps(design_json, indent=2) if design_json else ""
# Call the backend main function
result = main_function(
base_prompt=base_prompt,
input_workflow=input_workflow,
modal_params=modal_params,
modal_demo=modal_demo,
all_nodes=all_nodes,
external_modal_config=external_modal_config,
gradio_design_json=gradio_design_json,
gradio_image_path=None
)
return result
except Exception as e:
print(f"❌ Error in build_app_with_backend: {e}")
return f"❌ Error building app: {str(e)}"
def load_url_in_iframe(url):
"""Load URL in iframe with validation"""
if not url or url.startswith("❌"):
return "https://www.gradio.app"
# Add protocol if missing
if not url.startswith(('http://', 'https://')):
url = 'https://' + url
return url
def create_fallback_component(component_name, message):
"""Create a fallback component when the real component isn't available"""
return gr.HTML(f"""
<div style="
border: 2px dashed #e5e7eb;
border-radius: 12px;
padding: 40px;
text-align: center;
background: #f9fafb;
color: #6b7280;
">
<h3>⚠️ {component_name} Not Available</h3>
<p>{message}</p>
<p><strong>To enable this component:</strong></p>
<ol style="text-align: left; display: inline-block;">
<li>Navigate to the {component_name.lower().replace(' ', '')} folder</li>
<li>Run: <code>pip install -e .</code></li>
<li>Restart this demo</li>
</ol>
</div>
""")
def toggle_designer_visibility(show_designer):
"""Toggle designer section visibility"""
return gr.Column(visible=show_designer)
def set_app_name(name):
"""Set the global app name"""
app_state["name"] = name
return f"βœ… App name set to: {name}"
# Create the main interface with improved styling
with gr.Blocks(
title="FlowForge - AI App Builder",
theme=gr.themes.Soft(),
css="""
/* Enhanced styling */
.main-container {
max-width: 1600px;
margin: 0 auto;
padding: 0 1rem;
}
/* Beautiful centered header */
.hero-header {
text-align: center;
padding: 1.5rem 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 12px;
margin: 1rem 0 1.5rem 0;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.hero-title {
font-size: 2.2rem;
font-weight: 700;
margin: 0 0 0.5rem 0;
text-shadow: 1px 1px 2px rgba(0,0,0,0.2);
letter-spacing: -0.5px;
}
.hero-subtitle {
font-size: 1.1rem;
opacity: 0.95;
margin: 0;
font-weight: 400;
}
/* App name section */
.app-name-section {
background: #f0f9ff;
border: 2px solid #0ea5e9;
border-radius: 15px;
padding: 2rem;
margin: 2rem 0;
text-align: center;
}
/* Instructions styling */
.instructions {
background: #fef3c7;
border-left: 4px solid #f59e0b;
padding: 1rem 1.5rem;
margin: 1rem 0;
border-radius: 0 8px 8px 0;
font-size: 0.9rem;
}
.instructions h4 {
margin: 0 0 0.5rem 0;
color: #92400e;
font-size: 1rem;
}
.instructions ul {
margin: 0;
padding-left: 1.5rem;
}
.instructions li {
margin: 0.3rem 0;
color: #78350f;
}
/* Status indicators */
.status-success {
background: #dcfce7;
color: #166534;
padding: 0.5rem 1rem;
border-radius: 25px;
font-weight: 500;
display: inline-block;
margin: 0.5rem 0;
}
.status-error {
background: #fee2e2;
color: #991b1b;
padding: 0.5rem 1rem;
border-radius: 25px;
font-weight: 500;
display: inline-block;
margin: 0.5rem 0;
}
.workflow-section {
margin-bottom: 2rem;
padding: 0rem;
background: white;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
}
.feature-card {
background: white;
border-radius: 15px;
padding: 2rem;
margin: 1rem 0;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #e2e8f0;
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
}
"""
) as demo:
# Beautiful centered header
gr.HTML("""
<div class="hero-header">
<h1 class="hero-title">✨ FlowLab</h1>
<p class="hero-subtitle">πŸš€ Build your own AI app using no-code tools</p>
<p class="hero-subtitle">πŸš€ Build AI Agents, RAG pipelines, MCP agents, simple AI workflows and more!</p>
</div>
""")
# App Name Setup Section
with gr.Column(elem_classes=["clean-section"]):
# gr.HTML('<p class="tagline">Build your own AI app in 3 easy steps</p>')
app_name_input = gr.Textbox(
placeholder="Enter your app name",
show_label=False,
container=False,
elem_classes=["compact-input"]
)
set_name_btn = gr.Button(
"Continue",
variant="primary",
size="sm",
elem_classes=["compact-btn"]
)
app_name_status = gr.HTML("", elem_classes=["status"])
set_name_btn.click(
fn=set_app_name,
inputs=[app_name_input],
outputs=[app_name_status]
)
# Main application tabs
with gr.Tabs(elem_classes=["step-tabs"]) as tabs:
# Step 1: Workflow Builder
with gr.TabItem("🎨 Create Workflow", elem_classes=["workflow-section"]) as tab1:
# Instructions
if COMPONENTS_AVAILABLE['workflow']:
with gr.Column(elem_classes=["main-container"]):
workflow_builder = WorkflowBuilder(
label="🎨 Visual Workflow Designer",
info="Build your AI workflow by connecting nodes"
)
with gr.Row():
with gr.Column(scale=2):
workflow_json = gr.JSON(
label="πŸ“‹ Workflow Configuration",
show_label=True,
visible=False
)
with gr.Column(scale=1):
save_workflow_btn = gr.Button(
"πŸ’Ύ Save Workflow",
variant="primary",
size="lg"
)
# Analysis section
with gr.Column(elem_classes=["feature-card"]):
workflow_analysis = gr.Markdown("### πŸ” Workflow Analysis\n*Save your workflow to see analysis*")
workflow_validation = gr.HTML("")
# Export options
with gr.Accordion("πŸ“₯ Export Options", open=False):
with gr.Row():
export_json_btn = gr.Button("πŸ“„ Export JSON", variant="secondary")
export_code_btn = gr.Button("🐍 Generate Python Code", variant="secondary")
with gr.Column():
exported_json = gr.Code(label="Exported JSON", language="json", visible=False)
exported_code = gr.Code(label="Generated Python Code", language="python", visible=False)
gr.HTML("""
<div class="instructions">
<h4>πŸ“‹ How to Create Your Workflow:</h4>
<ul>
<li><strong>Drag & Drop:</strong> Drag components from the sidebar onto the canvas</li>
<li><strong>Connect Nodes:</strong> Click and drag from output points to input points</li>
<li><strong>Edit Properties:</strong> Select nodes to configure their settings in the right panel</li>
<li><strong>Save Workflow:</strong> Click "Save Workflow" when you're done designing</li>
</ul>
</div>
""")
# Event handlers
save_workflow_btn.click(
fn=save_workflow_with_name,
inputs=[workflow_builder, app_name_input],
outputs=[workflow_json]
).then(
fn=analyze_workflow,
inputs=[workflow_json],
outputs=[workflow_analysis]
).then(
fn=lambda w: (
gr.JSON(visible=True),
gr.HTML(f'<div class="status-success">βœ… Workflow saved successfully!</div>')
if w else gr.HTML('<div class="status-error">❌ Failed to save workflow</div>')
),
inputs=[workflow_json],
outputs=[workflow_json, workflow_validation]
)
export_json_btn.click(
fn=export_workflow,
inputs=[workflow_json],
outputs=[exported_json]
).then(
fn=lambda x: gr.Code(visible=True),
inputs=[exported_json],
outputs=[exported_json]
)
export_code_btn.click(
fn=generate_python_code,
inputs=[workflow_json],
outputs=[exported_code]
).then(
fn=lambda x: gr.Code(visible=True),
inputs=[exported_code],
outputs=[exported_code]
)
else:
create_fallback_component("WorkflowBuilder", "Workflow visual editor not available")
# Step 2: Gradio Designer
with gr.TabItem("🎨 Design Interface", elem_classes=["workflow-section"]) as tab2:
# Instructions
gr.HTML("""
<div class="instructions">
<h4>🎨 How to Design Your Interface:</h4>
<ul>
<li><strong>Enable Custom Design:</strong> Check the box to create a custom interface</li>
<li><strong>Add Components:</strong> Drag UI components to build your interface layout</li>
<li><strong>Configure Layout:</strong> Arrange components in rows, columns, and tabs</li>
<li><strong>Preview & Save:</strong> Test your interface and save the design configuration</li>
</ul>
</div>
""")
if COMPONENTS_AVAILABLE['designer']:
with gr.Column():
with gr.Row():
use_custom_design = gr.Checkbox(
label="✨ Create Custom Interface Design",
value=False,
info="Design a custom interface for your workflow (optional)"
)
with gr.Column(visible=False, elem_classes=["feature-card"]) as designer_section:
gradio_designer = GradioDesigner(
label="🎨 Visual App Designer",
value={"components": [], "layout": "blocks"}
)
with gr.Row():
with gr.Column(scale=2):
design_json = gr.JSON(label="πŸ“‹ Design Configuration", show_label=True)
with gr.Column(scale=1):
save_design_btn = gr.Button(
"πŸ’Ύ Save Design",
variant="primary",
size="lg"
)
save_design_btn.click(
fn=lambda x: x,
inputs=[gradio_designer],
outputs=[design_json]
)
# FIXED: Proper visibility toggle
use_custom_design.change(
fn=toggle_designer_visibility,
inputs=[use_custom_design],
outputs=[designer_section]
)
# Default design fallback
with gr.Column(elem_classes=["feature-card"]):
gr.Markdown("""
### 🎯 Default Interface
*If you don't create a custom design, FlowForge will automatically generate a clean interface based on your workflow.*
**Default Features:**
- Auto-generated forms for your workflow inputs
- Clean, responsive layout
- Built-in error handling and validation
- Mobile-friendly design
""")
else:
create_fallback_component("GradioDesigner", "Interface designer not available")
# Step 3: Modal Deployment
with gr.TabItem("πŸš€ Deploy to Modal", elem_classes=["workflow-section"]) as tab3:
# Instructions
gr.HTML("""
<div class="instructions">
<h4>πŸš€ How to Deploy Your App:</h4>
<ul>
<li><strong>Configure Resources:</strong> Set CPU, memory, and GPU requirements</li>
<li><strong>Set Scaling:</strong> Define minimum and maximum container counts</li>
<li><strong>Update Config:</strong> Generate the Modal deployment configuration</li>
<li><strong>Deploy:</strong> Click "Build & Deploy" to launch your app to production</li>
<li><strong>Deploy:</strong> It can take 1 to 5 mins to deploy the app depending on the complexity</li>
</ul>
</div>
""")
# # Add logging component
# with gr.Column(elem_classes=["feature-card"]):
# logs = gr.Textbox(
# label="πŸ“ Build Logs",
# lines=5,
# interactive=False,
# show_copy_button=True,
# elem_classes=["logs-box"]
# )
with gr.Column():
# ONLY wrap the resource configuration in accordion
with gr.Accordion("βš™οΈ Configure Modal Resources", open=False):
with gr.Column(scale=1, elem_classes=["feature-card"]):
gr.Markdown("#### πŸ’» Resource Configuration")
with gr.Group():
with gr.Row():
cpu = gr.Slider(0.1, 8.0, value=0.5, step=0.1, label="πŸ”§ CPU Cores")
memory = gr.Slider(128, 32768, value=512, step=128, label="🧠 Memory (MB)")
with gr.Row():
gpu_type = gr.Dropdown(
choices=["none", "T4", "A10G", "A100"],
value="none",
label="⚑ GPU Type"
)
gpu_count = gr.Number(1, label="πŸ”’ GPU Count", minimum=1, maximum=8)
gr.Markdown("#### πŸ“ˆ Auto Scaling")
with gr.Row():
min_containers = gr.Number(0, label="πŸ“‰ Min Containers", minimum=0)
max_containers = gr.Number(10, label="πŸ“ˆ Max Containers", minimum=1)
# Keep modal config and update button outside accordion
with gr.Row():
with gr.Column(scale=2):
modal_config = gr.JSON(label="βš™οΈ Modal Configuration", show_label=True)
with gr.Column(scale=1):
update_config_btn = gr.Button(
"πŸ”„ Update Config",
variant="secondary",
size="lg"
)
update_config_btn.click(
fn=create_modal_config,
inputs=[cpu, memory, gpu_type, gpu_count, min_containers, max_containers],
outputs=[modal_config]
)
# Deployment section - keep this exactly as it was
with gr.Column(elem_classes=["feature-card"]):
gr.Markdown("### πŸš€ Deploy Your Application")
with gr.Row():
deploy_btn = gr.Button(
"πŸš€ Build & Deploy App with AI",
# "πŸš€ Build your own AI app in 3 easy steps",
variant="primary",
size="lg",
scale=2
)
deployment_status = gr.HTML("")
app_url = gr.Textbox(
label="🌐 Your Deployed App URL",
interactive=False,
placeholder="Your app will appear here after deployment...",
info="Share this URL with others to access your deployed app"
)
# Keep the deploy button click handler as is
deploy_btn.click(
fn=build_app_with_backend,
inputs=[workflow_json, design_json, modal_config, app_name_input],
outputs=[app_url]
).then(
fn=lambda url: (
gr.HTML('<div class="status-success">βœ… Deployment successful!</div>')
if not url.startswith("❌")
else gr.HTML('<div class="status-error">❌ Deployment failed</div>')
),
inputs=[app_url],
outputs=[deployment_status]
)
# Live preview
if COMPONENTS_AVAILABLE['iframe']:
with gr.Column(elem_classes=["feature-card"]):
gr.Markdown("### πŸ“± Live App Preview")
app_preview = IFrame(
label="πŸ” App Preview",
height=600
)
app_url.change(
fn=load_url_in_iframe,
inputs=[app_url],
outputs=[app_preview]
)
else:
create_fallback_component("IFrame", "App preview not available")
# Add CSS for logs only
gr.HTML("""
<style>
.logs-box {
font-family: monospace;
background-color: #1a1a1a;
color: #ffffff;
border-radius: 8px;
padding: 12px;
margin: 10px 0;
}
</style>
""")
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
show_error=True
)