Spaces:
Sleeping
Sleeping
| """ | |
| 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 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 | |
| ) |