Team Ai
Apppublic

BluescarfAI/Design-To-code-Agent

sourceHugging Facecc-by-nc-nd-4.0updated 1y agoView on Hugging Face
0likes
app.py785 linesDownload Raw Back to root
1 2 3# #main.py4# import sys5# import os6# import warnings7# from crew import DocProcessing8# import re9 10# # Fix Unicode encoding issues on Windows11# if sys.platform.startswith('win'):12#     import codecs13#     sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach())14#     sys.stderr = codecs.getwriter('utf-8')(sys.stderr.detach())15 16# warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")17 18# def determine_file_type(file_path):19#     """20#     Determine the file type based on the file extension.21    22#     Args:23#         file_path (str): Path to the file24        25#     Returns:26#         str: 'pdf' if the file is a PDF, 'image' otherwise27#     """28#     _, ext = os.path.splitext(file_path)29#     if ext.lower() == '.pdf':30#         return 'pdf'31#     return 'image'32 33# def run():34#     """35#     Run the crew with file paths received from command line arguments.36#     """37#     # Get file paths from command line arguments38#     file_paths = sys.argv[1:] if len(sys.argv) > 1 else []39    40#     if not file_paths:41#         print("No file paths provided. Usage: python main.py <file_path1> <file_path2> ...")42#         return43    44#     # Process the first file (you can modify this to handle multiple files if needed)45#     file_path = file_paths[0]46#     file_type = determine_file_type(file_path)47    48    49#     # Prepare inputs for the CrewAI50#     inputs = {51#         "file_path": file_path,52#         "file_type": file_type,53#     }54    55#     try: 56#         # Pass the inputs to the crew kickoff method57#         result = DocProcessing().crew().kickoff(inputs=inputs)58    59#         # Try to get the actual output content from CrewOutput60      61#         return result62#     except Exception as e:63#         error_msg = f"An error occurred while running the crew: {e}"64#         print(error_msg)65#         raise Exception(error_msg)66 67# if __name__ == "__main__":68#     run()69 70#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&71# import gradio as gr72# import os73# import tempfile74# import shutil75# from pathlib import Path76# import warnings77# from typing import Tuple, Optional78# import sys79 80# # Suppress warnings81# warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")82 83# # Import your existing modules84# from crew import DocProcessing85# from tools.custom_tool import landing_ai_document_analysis86 87# def get_landing_ai_key():88#     """Get Landing AI API key from environment (HF secrets or local .env)"""89#     # Try multiple environment variable names for flexibility90#     return (91#         os.getenv("LANDING_AI_API_KEY") or           # HuggingFace secrets or production92#         os.getenv("LANDING_AI_API_KEY_LOCAL") or     # Local development alternative93#         os.getenv("LANDINGAI_API_KEY") or            # Alternative naming94#         os.getenv("LANDING_API_KEY")                 # Another alternative95#     )96 97# def validate_api_key(api_key: str) -> bool:98#     """Validate Anthropic API key format"""99#     if not api_key:100#         return False101#     return api_key.startswith("sk-ant-") and len(api_key) > 20102 103# def determine_file_type(file_path: str) -> str:104#     """Determine file type based on extension"""105#     _, ext = os.path.splitext(file_path)106#     return 'pdf' if ext.lower() == '.pdf' else 'image'107 108# def process_document(file, anthropic_api_key: str) -> Tuple[str, str]:109#     """110#     Process the uploaded document and return analysis + generated code111    112#     Args:113#         file: Uploaded file object from Gradio114#         anthropic_api_key: User's Anthropic API key115        116#     Returns:117#         Tuple of (analysis_result, generated_code)118#     """119#     try:120#         # Validate inputs121#         if not file:122#             return "❌ No file uploaded", ""123            124#         if not validate_api_key(anthropic_api_key):125#             return "❌ Invalid Anthropic API key. Please ensure it starts with 'sk-ant-' and is complete.", ""126            127#         # Check if Landing AI key is available128#         landing_ai_key = get_landing_ai_key()129#         if not landing_ai_key:130#             return "❌ Landing AI API key not configured. Please ensure LANDING_AI_API_KEY is set in environment variables or HuggingFace secrets.", ""131        132#         # Set environment variables securely133#         os.environ["ANTHROPIC_API_KEY"] = anthropic_api_key134#         os.environ["LANDING_AI_API_KEY"] = landing_ai_key135        136#         # Create temporary directory for processing137#         with tempfile.TemporaryDirectory() as temp_dir:138#             # Save uploaded file139#             file_extension = Path(file.name).suffix140#             temp_file_path = os.path.join(temp_dir, f"uploaded_file{file_extension}")141            142#             # Copy uploaded file to temp location143#             shutil.copy2(file.name, temp_file_path)144            145#             # Determine file type146#             file_type = determine_file_type(temp_file_path)147            148#             # Prepare inputs for CrewAI149#             inputs = {150#                 "file_path": temp_file_path,151#                 "file_type": file_type,152#             }153            154#             # Initialize and run the crew with the API key155#             doc_processing = DocProcessing(anthropic_api_key=anthropic_api_key)156#             result = doc_processing.crew().kickoff(inputs=inputs)157            158#             # Extract results from CrewOutput159#             if hasattr(result, 'tasks_output') and result.tasks_output:160#                 analysis_result = ""161#                 generated_code = ""162                163#                 for i, task_output in enumerate(result.tasks_output):164#                     if i == 0:  # First task is document analysis165#                         analysis_result = str(task_output.raw) if hasattr(task_output, 'raw') else str(task_output)166#                     elif i == 1:  # Second task is code implementation167#                         generated_code = str(task_output.raw) if hasattr(task_output, 'raw') else str(task_output)168                169#                 return analysis_result, generated_code170#             else:171#                 # Fallback if structure is different172#                 result_str = str(result)173#                 return result_str, "Code generation completed. Check the analysis section for details."174                175#     except Exception as e:176#         error_msg = f"❌ Error processing document: {str(e)}"177#         return error_msg, ""178    179#     finally:180#         # Clean up environment variables for security181#         if "ANTHROPIC_API_KEY" in os.environ:182#             del os.environ["ANTHROPIC_API_KEY"]183 184# def create_demo_interface():185#     """Create the Gradio interface"""186    187#     # Custom CSS for better styling188#     custom_css = """189#     .gradio-container {190#         max-width: 1200px !important;191#         margin: auto !important;192#     }193#     .header {194#         text-align: center;195#         margin-bottom: 2rem;196#     }197#     .api-key-input {198#         margin-bottom: 1rem;199#     }200#     .output-section {201#         margin-top: 2rem;202#     }203#     """204    205#     with gr.Blocks(css=custom_css, title="Document to Code Converter") as demo:206#         gr.HTML("""207#         <div class="header">208#             <h1>🚀 Document to Code Converter</h1>209#             <p>Upload your design documents (images/PDFs) and convert them to working HTML/CSS/JS code!</p>210#         </div>211#         """)212        213#         with gr.Row():214#             with gr.Column(scale=1):215#                 gr.HTML("<h3>📤 Upload & Configuration</h3>")216                217#                 # API Key input218#                 api_key_input = gr.Textbox(219#                     label="🔑 Anthropic API Key",220#                     placeholder="sk-ant-...",221#                     type="password",222#                     info="Enter your Anthropic API key. It will be used securely and not stored.",223#                     elem_classes=["api-key-input"]224#                 )225                226#                 # File upload227#                 file_input = gr.File(228#                     label="📁 Upload Document",229#                     file_types=[".pdf", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff"],230                   231#                 )232                233#                 # Process button234#                 process_btn = gr.Button(235#                     "🔄 Process Document",236#                     variant="primary",237#                     size="lg"238#                 )239                240#                 # Status indicator241#                 gr.HTML("""242#                 <div style="margin-top: 1rem; padding: 1rem; background: #f0f9ff; border-radius: 8px; border-left: 4px solid #0ea5e9;">243#                     <h4 style="margin: 0; color: #0c4a6e;">ℹ️ How it works:</h4>244#                     <ol style="margin: 0.5rem 0; color: #164e63;">245#                         <li>Upload your design document (image or PDF)</li>246#                         <li>Enter your Anthropic API key</li>247#                         <li>Click "Process Document" to analyze and generate code</li>248#                         <li>Get detailed analysis and working HTML/CSS/JS code</li>249#                     </ol>250#                 </div>251#                 """)252        253#         with gr.Row():254#             with gr.Column(scale=1):255#                 gr.HTML("<h3 class='output-section'>📊 Document Analysis</h3>")256#                 analysis_output = gr.Textbox(257#                     label="Analysis Results",258#                     lines=15,259#                     placeholder="Document analysis will appear here...",260#                     show_copy_button=True261#                 )262            263#             with gr.Column(scale=1):264#                 gr.HTML("<h3 class='output-section'>💻 Generated Code</h3>")265#                 code_output = gr.Textbox(266#                     label="HTML/CSS/JS Code",267#                     lines=15,268#                     placeholder="Generated code will appear here...",269#                     show_copy_button=True270#                 )271        272#         # Add examples section273#         gr.HTML("""274#         <div style="margin-top: 2rem; padding: 1rem; background: #f8fafc; border-radius: 8px;">275#             <h4>🎯 Best Results Tips:</h4>276#             <ul>277#                 <li><strong>High Quality Images:</strong> Use clear, high-resolution images for better analysis</li>278#                 <li><strong>Design Mockups:</strong> Website mockups, app designs, and UI sketches work great</li>279#                 <li><strong>PDF Documents:</strong> Multi-page design documents are supported</li>280#                 <li><strong>Clear Layouts:</strong> Well-organized designs produce better code structure</li>281#             </ul>282#         </div>283#         """)284        285#         # Set up the processing event286#         process_btn.click(287#             fn=process_document,288#             inputs=[file_input, api_key_input],289#             outputs=[analysis_output, code_output],290#             show_progress=True291#         )292        293#         # Add footer294#         gr.HTML("""295#         <div style="text-align: center; margin-top: 2rem; padding: 1rem; border-top: 1px solid #e2e8f0; color: #64748b;">296#             <p>🔒 Your API keys are handled securely and never stored. 297#             <br>Built with CrewAI, LandingAI, and Anthropic Claude.</p>298#         </div>299#         """)300    301#     return demo302 303# if __name__ == "__main__":304#     # Test Landing AI connection on startup305#     from tools.custom_tool import test_landing_ai_connection306    307#     print("🔍 Testing API connections...")308#     landing_ai_available = test_landing_ai_connection()309    310#     if not landing_ai_available:311#         print("\n⚠️ Warning: Landing AI API key not properly configured!")312#         print("For local development: Set LANDING_AI_API_KEY in your .env file")313#         print("For HuggingFace deployment: Add LANDING_AI_API_KEY to your Space secrets")314#         print("The app will still start, but document analysis will fail without the API key.\n")315#     else:316#         print("✅ Landing AI API key configured correctly!\n")317    318#     # Create and launch the demo319#     demo = create_demo_interface()320    321#     # Launch configuration322#     demo.launch(323#         server_name="0.0.0.0",  # Required for HuggingFace deployment324#         server_port=7860,       # Standard port for HuggingFace325#         share=False,            # Set to True for temporary public link during development326#         debug=False,            # Set to True for development327#         show_error=True,        # Show errors in the interface328#         quiet=False             # Set to True to reduce console output329#     )330 331import gradio as gr332import os333import tempfile334import shutil335from pathlib import Path336import warnings337from typing import Tuple, Optional338import sys339 340# Suppress warnings341warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd")342 343# Import your existing modules344from crew import DocProcessing345from tools.custom_tool import landing_ai_document_analysis346 347def get_landing_ai_key():348    """Get Landing AI API key from environment (HF secrets or local .env)"""349    # Try multiple environment variable names for flexibility350    return (351        os.getenv("LANDING_AI_API_KEY") or           # HuggingFace secrets or production352        os.getenv("LANDING_AI_API_KEY_LOCAL") or     # Local development alternative353        os.getenv("LANDINGAI_API_KEY") or            # Alternative naming354        os.getenv("LANDING_API_KEY")                 # Another alternative355    )356 357def validate_api_key(api_key: str) -> bool:358    """Validate Anthropic API key format"""359    if not api_key:360        return False361    return api_key.startswith("sk-ant-") and len(api_key) > 20362 363def create_html_file(code_content: str, filename: str = "generated_page.html") -> str:364    """365    Create an HTML file from the generated code366    367    Args:368        code_content: The HTML/CSS/JS code369        filename: Name for the HTML file370        371    Returns:372        str: Path to the created file373    """374    try:375        # Create temporary file376        temp_dir = tempfile.gettempdir()377        file_path = os.path.join(temp_dir, filename)378        379        # Clean the code content - remove any markdown formatting if present380        cleaned_code = code_content.strip()381        if cleaned_code.startswith("```html"):382            cleaned_code = cleaned_code[7:]383        if cleaned_code.endswith("```"):384            cleaned_code = cleaned_code[:-3]385        cleaned_code = cleaned_code.strip()386        387        # Ensure it's a complete HTML document388        if not cleaned_code.lower().startswith('<!doctype') and not cleaned_code.lower().startswith('<html'):389            cleaned_code = f"""<!DOCTYPE html>390<html lang="en">391<head>392    <meta charset="UTF-8">393    <meta name="viewport" content="width=device-width, initial-scale=1.0">394    <title>Generated Page</title>395</head>396<body>397{cleaned_code}398</body>399</html>"""400        401        # Write to file402        with open(file_path, 'w', encoding='utf-8') as f:403            f.write(cleaned_code)404        405        return file_path406        407    except Exception as e:408        print(f"Error creating HTML file: {e}")409        return None410 411def determine_file_type(file_path: str) -> str:412    """Determine file type based on extension"""413    _, ext = os.path.splitext(file_path)414    return 'pdf' if ext.lower() == '.pdf' else 'image'415def prepare_preview_content(code_content: str) -> str:416    """417    Prepare code content for safe preview in iframe418    419    Args:420        code_content: The HTML/CSS/JS code421        422    Returns:423        str: Cleaned and safe HTML content for preview424    """425    try:426        if not code_content or code_content.strip() == "":427            return "<div style='padding: 20px; text-align: center; color: #666;'>No code generated yet...</div>"428        429        # Clean the code content430        cleaned_code = code_content.strip()431        432        # Remove markdown formatting if present433        if cleaned_code.startswith("```html"):434            cleaned_code = cleaned_code[7:]435        if cleaned_code.startswith("```"):436            cleaned_code = cleaned_code[3:]437        if cleaned_code.endswith("```"):438            cleaned_code = cleaned_code[:-3]439        cleaned_code = cleaned_code.strip()440        441        # If it's not a complete HTML document, wrap it442        if not cleaned_code.lower().startswith('<!doctype') and not cleaned_code.lower().startswith('<html'):443            cleaned_code = f"""<!DOCTYPE html>444<html lang="en">445<head>446    <meta charset="UTF-8">447    <meta name="viewport" content="width=device-width, initial-scale=1.0">448    <title>Preview</title>449    <style>450        body {{ margin: 0; padding: 10px; font-family: Arial, sans-serif; }}451        * {{ box-sizing: border-box; }}452    </style>453</head>454<body>455{cleaned_code}456</body>457</html>"""458        459        # Wrap in iframe for safe rendering460        iframe_content = f"""461        <iframe 462            srcdoc="{cleaned_code.replace('"', '&quot;')}" 463            style="width: 100%; height: 400px; border: 1px solid #ddd; border-radius: 8px;"464            sandbox="allow-scripts allow-same-origin">465        </iframe>466        """467        468        return iframe_content469        470    except Exception as e:471        return f"<div style='padding: 20px; color: red;'>Error preparing preview: {str(e)}</div>"472 473def toggle_to_code():474    """Show code view and hide preview"""475    return (476        gr.update(visible=True),   # code_output477        gr.update(visible=False),  # preview_output478        gr.update(variant="primary"),   # code_view_btn479        gr.update(variant="secondary")  # preview_btn480    )481 482def toggle_to_preview(code_content):483    """Show preview and hide code view"""484    preview_html = prepare_preview_content(code_content)485    return (486        gr.update(visible=False),  # code_output487        gr.update(visible=True, value=preview_html),  # preview_output488        gr.update(variant="secondary"),  # code_view_btn489        gr.update(variant="primary")     # preview_btn490    )491 492# def download_html_file(code_content):493#     """Create downloadable HTML file"""494#     if not code_content or code_content.strip() == "":495#         return gr.update(visible=False)496    497#     file_path = create_html_file(code_content)498#     if file_path:499#         return gr.update(visible=True, value=file_path)500#     else:501#         return gr.update(visible=False)502 503def process_document(file, anthropic_api_key: str) -> Tuple[str, str]:504    """505    Process the uploaded document and return analysis + generated code506    507    Args:508        file: Uploaded file object from Gradio509        anthropic_api_key: User's Anthropic API key510        511    Returns:512        Tuple of (analysis_result, generated_code)513    """514    try:515        # Validate inputs516        if not file:517            return "❌ No file uploaded", ""518            519        if not validate_api_key(anthropic_api_key):520            return "❌ Invalid Anthropic API key. Please ensure it starts with 'sk-ant-' and is complete.", ""521            522        # Check if Landing AI key is available523        landing_ai_key = get_landing_ai_key()524        if not landing_ai_key:525            return "❌ Landing AI API key not configured. Please ensure LANDING_AI_API_KEY is set in environment variables or HuggingFace secrets.", ""526        527        # Set environment variables securely528        os.environ["ANTHROPIC_API_KEY"] = anthropic_api_key529        os.environ["LANDING_AI_API_KEY"] = landing_ai_key530        531        # Create temporary directory for processing532        with tempfile.TemporaryDirectory() as temp_dir:533            # Save uploaded file534            file_extension = Path(file.name).suffix535            temp_file_path = os.path.join(temp_dir, f"uploaded_file{file_extension}")536            537            # Copy uploaded file to temp location538            shutil.copy2(file.name, temp_file_path)539            540            # Determine file type541            file_type = determine_file_type(temp_file_path)542            543            # Prepare inputs for CrewAI544            inputs = {545                "file_path": temp_file_path,546                "file_type": file_type,547            }548            549            # Initialize and run the crew with the API key550            doc_processing = DocProcessing(anthropic_api_key=anthropic_api_key)551            result = doc_processing.crew().kickoff(inputs=inputs)552            553            # Extract results from CrewOutput554            if hasattr(result, 'tasks_output') and result.tasks_output:555                analysis_result = ""556                generated_code = ""557                558                for i, task_output in enumerate(result.tasks_output):559                    if i == 0:  # First task is document analysis560                        analysis_result = str(task_output.raw) if hasattr(task_output, 'raw') else str(task_output)561                    elif i == 1:  # Second task is code implementation562                        generated_code = str(task_output.raw) if hasattr(task_output, 'raw') else str(task_output)563                564                return generated_code565            # analysis_result,566            else:567                # Fallback if structure is different568                result_str = str(result)569                return result_str, "Code generation completed. Check the analysis section for details."570                571    except Exception as e:572        error_msg = f"❌ Error processing document: {str(e)}"573        return error_msg, ""574    575    finally:576        # Clean up environment variables for security577        if "ANTHROPIC_API_KEY" in os.environ:578            del os.environ["ANTHROPIC_API_KEY"]579 580def create_demo_interface():581    """Create the Gradio interface"""582    583    # Custom CSS for better styling584    custom_css = """585    .gradio-container {586        max-width: 1200px !important;587        margin: auto !important;588    }589    .header {590        text-align: center;591        margin-bottom: 2rem;592    }593    .api-key-input {594        margin-bottom: 1rem;595    }596    .output-section {597        margin-top: 2rem;598    }599    .toggle-buttons {600        margin-bottom: 1rem;601    }602    .preview-container {603        border: 1px solid #e2e8f0;604        border-radius: 8px;605        background: #f8fafc;606        min-height: 400px;607    }608    """609    610    with gr.Blocks(css=custom_css, title="Document to Code Converter") as demo:611        gr.HTML("""612        <div class="header">613            <h1>🚀 Document to Code Converter</h1>614            <p>Upload your design documents (images/PDFs) and convert them to working HTML/CSS/JS code!</p>615        </div>616        """)617        618        with gr.Row():619            with gr.Column(scale=1):620                gr.HTML("<h3>📤 Upload & Configuration</h3>")621                622                # API Key input623                api_key_input = gr.Textbox(624                    label="🔑 Anthropic API Key",625                    placeholder="sk-ant-...",626                    type="password",627                    info="Enter your Anthropic API key. It will be used securely and not stored.",628                    elem_classes=["api-key-input"]629                )630                631                # File upload632                file_input = gr.File(633                    label="📁 Upload Document",634                    file_types=[".pdf", ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff"],635                   636                )637                638                # Process button639                process_btn = gr.Button(640                    "🔄 Process Document",641                    variant="primary",642                    size="lg"643                )644                645                # Status indicator646                gr.HTML("""647                <div style="margin-top: 1rem; padding: 1rem; background: #f0f9ff; border-radius: 8px; border-left: 4px solid #0ea5e9;">648                    <h4 style="margin: 0; color: #0c4a6e;">ℹ️ How it works:</h4>649                    <ol style="margin: 0.5rem 0; color: #164e63;">650                        <li>Upload your design document (image or PDF)</li>651                        <li>Enter your Anthropic API key</li>652                        <li>Click "Process Document" to analyze and generate code</li>653                        <li>Use toggle buttons to switch between code view and live preview</li>654                        655                    </ol>656                </div>657                """)658                gr.HTML("<h3 class='output-section'>💻 Generated Code & Preview</h3>")659        660                # Toggle buttons for code/preview661                with gr.Row():662                    code_view_btn = gr.Button("📝 View Code", variant="primary", size="sm")663                    preview_btn = gr.Button("👁️ Preview Result", variant="secondary", size="sm")664                    665                666                # Code output (visible by default)667                code_output = gr.Textbox(668                    label="HTML/CSS/JS Code",669                    lines=15,670                    placeholder="Generated code will appear here...",671                    show_copy_button=True,672                    visible=True673                )674                675                # Preview output (hidden by default)676                preview_output = gr.HTML(677                    label="Live Preview",678                    value="<div style='padding: 20px; text-align: center; color: #666;'>Preview will appear here after code generation...</div>",679                    visible=False680                )681        682        # with gr.Row():683        #     with gr.Column(scale=1):684        #         gr.HTML("<h3 class='output-section'>📊 Document Analysis</h3>")685                # analysis_output = gr.Textbox(686                #     label="Analysis Results",687                #     lines=15,688                #     placeholder="Document analysis will appear here...",689                #     show_copy_button=True690                # )691            692            # with gr.Column(scale=1):693        694                695                # Download file component (hidden)696                # download_file = gr.File(697                #     label="Download",698                #     visible=False699                # )700        701        # Add examples section702        gr.HTML("""703        <div style="margin-top: 2rem; padding: 1rem; background: #f8fafc; border-radius: 8px;">704            <h4>🎯 New Features:</h4>705            <ul>706                <li><strong>📝 Code View:</strong> See the raw HTML/CSS/JS code generated from your design</li>707                <li><strong>👁️ Live Preview:</strong> Toggle to see how your code will actually look when rendered</li>708                <li><strong>🔄 Real-time Toggle:</strong> Switch between code and preview instantly</li>709            </ul>710            <h4>🎯 Best Results Tips:</h4>711            <ul>712                <li><strong>High Quality Images:</strong> Use clear, high-resolution images for better analysis</li>713                <li><strong>Design Mockups:</strong> Website mockups, app designs, and UI sketches work great</li>714                <li><strong>PDF Documents:</strong> Multi-page design documents are supported</li>715                <li><strong>Clear Layouts:</strong> Well-organized designs produce better code structure</li>716            </ul>717        </div>718        """)719        720        # Set up the processing event721        process_btn.click(722            fn=process_document,723            inputs=[file_input, api_key_input],724            outputs=[code_output],725            # analysis_output,726            show_progress=True727        )728        729        # Set up toggle events730        code_view_btn.click(731            fn=toggle_to_code,732            inputs=[],733            outputs=[code_output, preview_output, code_view_btn, preview_btn]734        )735        736        preview_btn.click(737            fn=toggle_to_preview,738            inputs=[code_output],739            outputs=[code_output, preview_output, code_view_btn, preview_btn]740        )741        742        # Set up download event743        # download_btn.click(744        #     fn=download_html_file,745        #     inputs=[code_output],746        #     outputs=[download_file]747        # )748        749        # Add footer750        gr.HTML("""751        <div style="text-align: center; margin-top: 2rem; padding: 1rem; border-top: 1px solid #e2e8f0; color: #64748b;">752            <p>🔒 Your API keys are handled securely and never stored. 753            <br>Built with CrewAI, LandingAI, and Anthropic Claude.</p>754        </div>755        """)756    757    return demo758 759if __name__ == "__main__":760    # Test Landing AI connection on startup761    from tools.custom_tool import test_landing_ai_connection762    763    print("🔍 Testing API connections...")764    landing_ai_available = test_landing_ai_connection()765    766    if not landing_ai_available:767        print("\n⚠️ Warning: Landing AI API key not properly configured!")768        print("For local development: Set LANDING_AI_API_KEY in your .env file")769        print("For HuggingFace deployment: Add LANDING_AI_API_KEY to your Space secrets")770        print("The app will still start, but document analysis will fail without the API key.\n")771    else:772        print("✅ Landing AI API key configured correctly!\n")773    774    # Create and launch the demo775    demo = create_demo_interface()776    777    # Launch configuration778    demo.launch(779        server_name="0.0.0.0",  # Required for HuggingFace deployment780        server_port=7860,       # Standard port for HuggingFace781        share=False,            # Set to True for temporary public link during development782        debug=False,            # Set to True for development783        show_error=True,        # Show errors in the interface784        quiet=False             # Set to True to reduce console output785    )