pydeploy-studio / app.py
codeboosterstech's picture
Update app.py
4c89ecb verified
raw
history blame
38.8 kB
"""
Main application file for PyDeploy Studio
Professional Gradio application for converting Python code to deployable Gradio apps
Created by Veerakumar C B
"""
import gradio as gr
import tempfile
import os
import sys
from pathlib import Path
# Add utils and components to path
sys.path.append(str(Path(__file__).parent))
from utils.converter import convert_code
from utils.deployer import deploy_to_space
from utils.file_handler import (
create_zip_archive,
read_file_content,
parse_notebook,
save_individual_file
)
from utils.validator import validate_inputs
from components.header import create_header
from components.sidebar import create_sidebar
from components.api_guide import create_api_guide
from components.file_downloader import create_file_download_section
# Constants
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
SUPPORTED_EXTENSIONS = ['.py', '.ipynb', '.txt']
class SpaceCreatorApp:
"""Main application class for PyDeploy Studio"""
def __init__(self):
self.temp_files = []
self.current_step = 1
self.generated_files = {}
def cleanup(self):
"""Clean up temporary files"""
for file_path in self.temp_files:
try:
if os.path.exists(file_path):
os.remove(file_path)
except:
pass
self.temp_files = []
def process_step1(self, input_text, input_file, groq_api_key):
"""Step 1: Convert code to Gradio app"""
try:
# Validate inputs
validation_errors = validate_inputs(groq_api_key, None, None, "Convert Only")
if validation_errors:
return None, None, None, None, self._format_errors(validation_errors), 1
# Get source code
code_content = self._extract_code(input_text, input_file)
if not code_content.strip():
return None, None, None, None, "Please provide Python code to convert.", 1
# Convert code
conversion_result = convert_code(code_content, groq_api_key)
# Store generated files
self.generated_files = {
"app.py": conversion_result["app_py"],
"requirements.txt": conversion_result["requirements_txt"],
"README.md": conversion_result["readme_md"]
}
# Create individual files for download
file_paths = {}
for filename, content in self.generated_files.items():
file_path = save_individual_file(filename, content)
self.temp_files.append(file_path)
file_paths[filename] = file_path
status = self._create_status_message("success", "Code conversion successful!")
return (file_paths["app.py"], file_paths["requirements.txt"],
file_paths["README.md"], None, status, 2)
except Exception as e:
error_msg = self._create_status_message("error", f"Conversion failed: {str(e)}")
return None, None, None, None, error_msg, 1
def process_step2(self, hf_token, space_name, deploy_mode):
"""Step 2: Deploy to Hugging Face"""
try:
if deploy_mode != "Deploy to Hugging Face Space":
status = self._create_status_message("info", "Skipping deployment as requested.")
return None, status, 2
# Validate deployment inputs
validation_errors = validate_inputs(None, hf_token, space_name, "Deploy")
if validation_errors:
return None, self._format_errors(validation_errors), 2
# Deploy to Hugging Face
deploy_url = deploy_to_space(hf_token, space_name, self.generated_files)
# Create zip archive of all files
zip_bytes = create_zip_archive(self.generated_files)
zip_path = save_individual_file("gradio_app_full_package.zip", zip_bytes, binary=True)
self.temp_files.append(zip_path)
status = self._create_status_message("success",
f"Deployment successful! Space URL: {deploy_url}")
return zip_path, status, 3
except Exception as e:
error_msg = self._create_status_message("error", f"Deployment failed: {str(e)}")
return None, error_msg, 2
def _extract_code(self, input_text, input_file):
"""Extract code from text or file"""
code_content = ""
if input_file is not None:
file_content = read_file_content(input_file)
if input_file.name.endswith('.ipynb') if hasattr(input_file, 'name') else False:
with tempfile.NamedTemporaryFile(mode='w', suffix='.ipynb', delete=False, encoding='utf-8') as tmp:
tmp.write(file_content)
tmp_path = tmp.name
try:
code_content = parse_notebook(tmp_path)
finally:
os.unlink(tmp_path)
else:
code_content = file_content
elif input_text.strip():
code_content = input_text
return code_content
def _format_errors(self, errors):
"""Format validation errors"""
error_html = '<div class="error-message"><h4>Validation Errors:</h4><ul>'
for error in errors:
error_html += f'<li>{error}</li>'
error_html += '</ul></div>'
return error_html
def _create_status_message(self, msg_type, message):
"""Create formatted status message"""
icons = {
"success": "✓",
"error": "✗",
"warning": "⚠",
"info": "ℹ"
}
colors = {
"success": "#16a34a",
"error": "#dc2626",
"warning": "#d97706",
"info": "#2563eb"
}
return f'''
<div class="status-message {msg_type}">
<span style="font-weight: bold; color: {colors[msg_type]}; margin-right: 8px;">
{icons[msg_type]}
</span>
<span>{message}</span>
</div>
'''
def create_app():
"""Create the Gradio application interface"""
app = SpaceCreatorApp()
# Custom theme with yellow and elephant grey
theme = gr.themes.Base(
primary_hue="yellow",
secondary_hue="gray",
neutral_hue="gray",
font=("Inter", "Segoe UI", "Roboto", "Helvetica", "Arial", "sans-serif")
)
with gr.Blocks(
title="PyDeploy Studio",
theme=theme,
css="""
/* PyDeploy Studio CSS - Yellow & Elephant Grey Theme */
.gradio-container {
max-width: 1300px !important;
margin: 20px auto !important;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f8f9fa !important;
}
body {
background: #f1f3f4 !important;
}
/* Header with gradient */
.header-main {
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
color: white;
padding: 2.5rem 2rem;
border-radius: 12px;
margin-bottom: 24px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
position: relative;
overflow: hidden;
}
.header-main::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 200px;
height: 200px;
background: linear-gradient(45deg, #fbbf24 0%, transparent 70%);
opacity: 0.1;
border-radius: 50%;
}
.header-content {
position: relative;
z-index: 2;
}
.header-content h1 {
margin: 0 0 0.5rem 0;
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.header-content .subtitle {
font-size: 1.1rem;
color: #cbd5e1;
margin-bottom: 1rem;
}
.creator-info {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.9rem;
color: #fbbf24; /* Changed to yellow */
margin-top: 0.5rem;
}
.creator-info a {
color: #fbbf24;
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.creator-info a:hover {
color: #f59e0b;
text-decoration: underline;
}
.creator-info .linkedin-icon {
color: #0077b5;
}
/* Main Layout */
.main-wrapper {
display: flex;
gap: 24px;
margin-top: 20px;
}
/* Sidebar */
.sidebar-container {
flex: 0 0 300px;
background: white;
padding: 1.75rem;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
height: fit-content;
}
.sidebar-title {
font-size: 1.1rem;
font-weight: 600;
color: #374151;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 2px solid #fbbf24;
}
/* Steps */
.step-container {
margin-bottom: 2rem;
}
.step-item {
display: flex;
align-items: flex-start;
gap: 1rem;
padding: 1.25rem;
margin-bottom: 0.75rem;
background: #f9fafb;
border-radius: 10px;
border: 1px solid #e5e7eb;
transition: all 0.3s ease;
cursor: pointer;
}
.step-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
}
.step-item.active {
background: linear-gradient(135deg, #fef3c7 0%, #fffbeb 100%);
border-color: #fbbf24;
border-left: 4px solid #fbbf24;
}
.step-number {
width: 32px;
height: 32px;
background: #e5e7eb;
color: #6b7280;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 0.9rem;
flex-shrink: 0;
transition: all 0.3s ease;
}
.step-item.active .step-number {
background: #fbbf24;
color: #1f2937;
}
.step-content {
flex: 1;
}
.step-title {
font-weight: 600;
color: #374151;
margin-bottom: 0.25rem;
}
.step-item.active .step-title {
color: #1f2937;
}
.step-description {
font-size: 0.875rem;
color: #6b7280;
line-height: 1.5;
}
/* Tips Box */
.tips-container {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 1.5rem;
}
.tips-title {
font-size: 1rem;
font-weight: 600;
color: #374151;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.tips-title::before {
content: "💡";
}
.tip-item {
display: flex;
align-items: flex-start;
gap: 0.75rem;
margin-bottom: 0.875rem;
}
.tip-item:last-child {
margin-bottom: 0;
}
.tip-bullet {
color: #fbbf24;
font-weight: bold;
flex-shrink: 0;
margin-top: 0.125rem;
}
.tip-text {
font-size: 0.875rem;
color: #4b5563;
line-height: 1.5;
}
/* Main Content */
.content-container {
flex: 1;
background: white;
padding: 2rem;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
/* Step Groups */
.step-group {
margin-bottom: 2rem;
}
.step-header {
font-size: 1.5rem;
font-weight: 600;
color: #1f2937;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 2px solid #fbbf24;
}
/* Form Elements */
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #374151;
font-size: 0.95rem;
}
.form-info {
font-size: 0.85rem;
color: #6b7280;
margin-top: 0.25rem;
}
input, textarea, select {
width: 100%;
padding: 0.875rem 1rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 0.95rem;
font-family: 'Inter', sans-serif;
transition: all 0.2s ease;
color: #374151;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: #fbbf24;
box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.1);
}
input::placeholder, textarea::placeholder {
color: #9ca3af;
}
/* Buttons */
button {
font-family: 'Inter', sans-serif !important;
font-weight: 500 !important;
font-size: 0.95rem !important;
padding: 0.875rem 1.75rem !important;
border-radius: 8px !important;
transition: all 0.3s ease !important;
border: none !important;
}
button.primary {
background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%) !important;
color: #1f2937 !important;
font-weight: 600 !important;
}
button.primary:hover {
transform: translateY(-2px) !important;
box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3) !important;
}
button.secondary {
background: white !important;
color: #4b5563 !important;
border: 1px solid #d1d5db !important;
}
button.secondary:hover {
background: #f9fafb !important;
border-color: #9ca3af !important;
}
/* Tabs */
.tab-nav {
border-bottom: 1px solid #e5e7eb !important;
margin-bottom: 1.5rem !important;
}
.tab-nav button {
padding: 0.75rem 1.5rem !important;
background: none !important;
border: none !important;
border-bottom: 3px solid transparent !important;
color: #6b7280 !important;
border-radius: 0 !important;
}
.tab-nav button.selected {
color: #1f2937 !important;
border-bottom-color: #fbbf24 !important;
font-weight: 600 !important;
}
/* Code Editor */
#code_editor {
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace !important;
font-size: 0.9rem !important;
line-height: 1.6 !important;
background: #f8fafc !important;
border: 1px solid #e2e8f0 !important;
border-radius: 8px !important;
padding: 1.25rem !important;
}
/* Status Messages */
.status-message {
padding: 1rem 1.25rem;
border-radius: 8px;
margin: 1.25rem 0;
border-left: 4px solid;
background: #f8fafc;
font-size: 0.95rem;
display: flex;
align-items: flex-start;
gap: 0.75rem;
}
.status-message.success {
border-left-color: #16a34a;
background: #f0fdf4;
}
.status-message.error {
border-left-color: #dc2626;
background: #fef2f2;
}
.status-message.warning {
border-left-color: #d97706;
background: #fffbeb;
}
.status-message.info {
border-left-color: #2563eb;
background: #eff6ff;
}
/* File Cards */
.file-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.file-card {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 1.5rem;
transition: all 0.3s ease;
}
.file-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-color: #fbbf24;
}
.file-card-title {
font-weight: 600;
color: #1f2937;
margin-bottom: 0.75rem;
font-size: 1.1rem;
}
.file-card-description {
font-size: 0.875rem;
color: #6b7280;
margin-bottom: 1rem;
}
/* Error Message */
.error-message {
background: linear-gradient(135deg, #fef2f2 0%, #fff5f5 100%);
border: 1px solid #fecaca;
border-radius: 10px;
padding: 1.5rem;
margin: 1.5rem 0;
}
.error-message h4 {
margin: 0 0 0.75rem 0;
color: #dc2626;
font-size: 1.1rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.error-message h4::before {
content: "⚠";
}
.error-message ul {
margin: 0;
padding-left: 1.5rem;
}
.error-message li {
margin-bottom: 0.5rem;
color: #7f1d1d;
}
/* Radio Group */
.radio-group {
display: flex;
gap: 2rem;
margin: 1.5rem 0;
}
.radio-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
/* Footer */
.footer {
text-align: center;
padding: 1.5rem;
margin-top: 2rem;
color: #6b7280;
font-size: 0.875rem;
border-top: 1px solid #e5e7eb;
}
.footer a {
color: #f59e0b;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
/* Responsive Design */
@media (max-width: 1024px) {
.main-wrapper {
flex-direction: column;
}
.sidebar-container {
width: 100%;
}
.gradio-container {
padding: 10px !important;
}
}
@media (max-width: 768px) {
.header-content h1 {
font-size: 2rem;
}
.content-container {
padding: 1.5rem;
}
.file-grid {
grid-template-columns: 1fr;
}
.radio-group {
flex-direction: column;
gap: 1rem;
}
}
/* Animation for active step */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.step-item.active {
animation: pulse 2s infinite;
}
"""
) as demo:
# Add fonts
gr.HTML('''
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
''')
# Header with branding
header_html = '''
<div class="header-main">
<div class="header-content">
<h1>PyDeploy Studio</h1>
<div class="subtitle">Professional Python to Gradio Application Converter</div>
<div class="subtitle" style="color: #fbbf24; margin-top: 0.5rem;">
<span>Created by</span>
<a href="https://linkedin.com/in/cbvk28" target="_blank" style="color: #fbbf24; font-weight: 600; text-decoration: none;">
Veerakumar C B
</a>
<span>Streamline your deployment workflow</span>
</div>
</div>
</div>
'''
gr.HTML(header_html)
with gr.Row(elem_classes="main-wrapper"):
# Sidebar
with gr.Column(scale=1, elem_classes="sidebar-container"):
current_step = gr.State(1)
# Steps
steps_html = '''
<div class="sidebar-title">Workflow Steps</div>
<div class="step-container">
<div class="step-item active">
<div class="step-number">1</div>
<div class="step-content">
<div class="step-title">Input Code</div>
<div class="step-description">Upload or paste your Python code</div>
</div>
</div>
<div class="step-item">
<div class="step-number">2</div>
<div class="step-content">
<div class="step-title">Generate Files</div>
<div class="step-description">Download app.py, requirements.txt & README</div>
</div>
</div>
<div class="step-item">
<div class="step-number">3</div>
<div class="step-content">
<div class="step-title">Deploy</div>
<div class="step-description">Deploy to Hugging Face Spaces</div>
</div>
</div>
</div>
<div class="tips-container">
<div class="tips-title">Quick Tips</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">Ensure your code has clear functions for best conversion</span>
</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">API keys are used securely and never stored</span>
</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">Test locally before deploying to production</span>
</div>
</div>
'''
gr.HTML(steps_html)
# Main Content
with gr.Column(scale=3, elem_classes="content-container"):
# Step 1: Code Input
with gr.Group(visible=True) as step1_group:
gr.Markdown('<div class="step-header">Step 1: Provide Your Code</div>')
with gr.Tabs():
with gr.Tab("📁 Upload File"):
file_input = gr.File(
label="Upload Python or Jupyter Notebook",
file_types=SUPPORTED_EXTENSIONS,
type="filepath",
elem_classes="form-group"
)
with gr.Tab("📝 Paste Code"):
text_input = gr.Textbox(
label="Python Code",
lines=15,
placeholder="# Paste your Python code here...\n# Example: Machine Learning model, Data processing function, etc.\n\nimport numpy as np\n\ndef process_data(input_data):\n # Your code here\n return processed_result",
elem_id="code_editor"
)
gr.Markdown('<div class="step-header" style="margin-top: 2rem;">Step 2: API Configuration</div>')
with gr.Row():
with gr.Column():
groq_api_key = gr.Textbox(
label="Groq API Key",
type="password",
placeholder="gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
info="Required for AI-powered conversion. Get yours from console.groq.com",
elem_classes="form-group"
)
# API Guide
with gr.Accordion("🔑 Need help creating API keys?", open=False):
with gr.Tabs():
with gr.Tab("Groq API Key"):
gr.Markdown("""
### Creating a Groq API Key
1. Visit [console.groq.com](https://console.groq.com)
2. Sign up or log in to your account
3. Navigate to **API Keys** section
4. Click **Create API Key**
5. Give your key a descriptive name
6. Copy the generated key (starts with `gsk_`)
7. Paste it in the field above
*Note: Keep your API key secure and never share it publicly.*
""")
with gr.Tab("Hugging Face Token"):
gr.Markdown("""
### Creating a Hugging Face Token
1. Go to [huggingface.co](https://huggingface.co)
2. Click on your profile picture → **Settings**
3. Navigate to **Access Tokens**
4. Click **New Token**
5. Select token type: **Write**
6. Copy the generated token (starts with `hf_`)
7. Paste it in Step 2
*Write token is required for creating and updating Spaces.*
""")
convert_btn = gr.Button(
"🚀 Convert Code",
variant="primary",
size="lg",
elem_classes="mt-3"
)
# Step 2: Generated Files
with gr.Group(visible=False) as step2_group:
gr.Markdown('<div class="step-header">Step 2: Generated Files</div>')
gr.Markdown("### Your Gradio Application is Ready!")
# File Cards Grid
file_cards_html = '''
<div class="file-grid">
<div class="file-card">
<div class="file-card-title">app.py</div>
<div class="file-card-description">Main application file with Gradio interface</div>
</div>
<div class="file-card">
<div class="file-card-title">requirements.txt</div>
<div class="file-card-description">Python dependencies for your application</div>
</div>
<div class="file-card">
<div class="file-card-title">README.md</div>
<div class="file-card-description">Documentation with YAML frontmatter</div>
</div>
</div>
'''
gr.HTML(file_cards_html)
with gr.Row():
with gr.Column():
app_py_download = gr.File(
label="Download app.py",
file_types=[".py"],
interactive=False
)
with gr.Column():
requirements_download = gr.File(
label="Download requirements.txt",
file_types=[".txt"],
interactive=False
)
with gr.Column():
readme_download = gr.File(
label="Download README.md",
file_types=[".md"],
interactive=False
)
gr.Markdown('<div class="step-header" style="margin-top: 2rem;">Step 3: Deployment Options</div>')
with gr.Row():
with gr.Column():
hf_token = gr.Textbox(
label="Hugging Face Token",
type="password",
placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
info="Write token required for deployment",
elem_classes="form-group"
)
with gr.Column():
space_name = gr.Textbox(
label="Space Name",
placeholder="my-awesome-gradio-app",
info="3-30 chars, lowercase letters, numbers, and hyphens only",
elem_classes="form-group"
)
gr.Markdown("### Select Deployment Action")
deploy_mode = gr.Radio(
choices=["📥 Download Only", "🚀 Deploy to Hugging Face Space"],
label="",
value="📥 Download Only",
elem_classes="radio-group"
)
with gr.Row():
back_btn_step2 = gr.Button("← Back", variant="secondary")
deploy_btn = gr.Button("Proceed to Deployment →", variant="primary")
# Step 3: Deployment Results
with gr.Group(visible=False) as step3_group:
gr.Markdown('<div class="step-header">Step 3: Deployment Complete 🎉</div>')
success_html = '''
<div style="text-align: center; padding: 2rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">✅</div>
<h2 style="color: #16a34a; margin-bottom: 1rem;">Deployment Successful!</h2>
<p style="color: #6b7280; margin-bottom: 2rem;">Your application is now live on Hugging Face Spaces</p>
</div>
'''
gr.HTML(success_html)
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### Download Complete Package")
full_package_download = gr.File(
label="gradio_app_full_package.zip",
file_types=[".zip"],
interactive=False
)
gr.Markdown("*Contains all files ready for deployment*")
with gr.Column(scale=1):
gr.Markdown("### Next Steps")
gr.Markdown("""
1. **Visit** your Space on Hugging Face
2. **Monitor** deployment logs
3. **Share** your app with others
4. **Customize** further if needed
""")
deployment_status = gr.Markdown()
with gr.Row():
back_btn_step3 = gr.Button("🔄 Start New Project", variant="secondary")
finish_btn = gr.Button("🎯 Finish", variant="primary")
# Status output
status_output = gr.HTML(
value='',
elem_id="status_output"
)
# Footer
footer_html = '''
<div class="footer">
<p>PyDeploy Studio • Created by <a href="https://linkedin.com/in/cbvk28" target="_blank">Veerakumar C B</a></p>
<p style="font-size: 0.8rem; color: #9ca3af; margin-top: 0.5rem;">Transform your Python code into production-ready applications</p>
</div>
'''
gr.HTML(footer_html)
# JavaScript for updating steps
js_code = """
<script>
function updateSteps(step) {
const steps = document.querySelectorAll('.step-item');
steps.forEach((stepEl, index) => {
stepEl.classList.remove('active');
if (index === step - 1) {
stepEl.classList.add('active');
}
});
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateSteps(1);
});
</script>
"""
gr.HTML(js_code)
# Event handlers
def update_step_display(step):
return f"""
<script>
updateSteps({step});
</script>
"""
convert_btn.click(
fn=app.process_step1,
inputs=[text_input, file_input, groq_api_key],
outputs=[
app_py_download,
requirements_download,
readme_download,
full_package_download,
status_output,
current_step
]
).then(
fn=lambda step: (gr.update(visible=step==1), gr.update(visible=step==2), gr.update(visible=step==3)),
inputs=[current_step],
outputs=[step1_group, step2_group, step3_group]
).then(
fn=update_step_display,
inputs=[current_step],
outputs=[status_output]
)
deploy_btn.click(
fn=app.process_step2,
inputs=[hf_token, space_name, deploy_mode],
outputs=[full_package_download, status_output, current_step]
).then(
fn=lambda step: (gr.update(visible=step==1), gr.update(visible=step==2), gr.update(visible=step==3)),
inputs=[current_step],
outputs=[step1_group, step2_group, step3_group]
).then(
fn=update_step_display,
inputs=[current_step],
outputs=[status_output]
)
back_btn_step2.click(
fn=lambda: (1, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)),
outputs=[current_step, step1_group, step2_group, step3_group]
).then(
fn=update_step_display,
inputs=[current_step],
outputs=[status_output]
)
back_btn_step3.click(
fn=lambda: (1, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)),
outputs=[current_step, step1_group, step2_group, step3_group]
).then(
fn=app.cleanup,
inputs=None,
outputs=None
).then(
fn=update_step_display,
inputs=[current_step],
outputs=[status_output]
)
finish_btn.click(
fn=app.cleanup,
inputs=None,
outputs=None
)
return demo
if __name__ == "__main__":
print("=" * 60)
print("PyDeploy Studio - Professional Deployment Platform")
print("Created by Veerakumar C B")
print("=" * 60)
print("Starting server...")
print(f"Local URL: http://localhost:7860")
print("=" * 60)
app = create_app()
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=False,
favicon_path=None,
show_error=True
)