Spaces:
No application file
No application file
Update app.py
Browse files
app.py
CHANGED
|
@@ -520,9 +520,12 @@ async def analyze_file(
|
|
| 520 |
def create_gradio_interface():
|
| 521 |
logger.info("Attempting to create Gradio interface...")
|
| 522 |
try:
|
|
|
|
|
|
|
|
|
|
| 523 |
with gr.Blocks(
|
| 524 |
title="🧬 Gene Analysis Pipeline",
|
| 525 |
-
theme=
|
| 526 |
css="""
|
| 527 |
.gradio-container { max-width: 1200px !important; }
|
| 528 |
.status-box { padding: 10px; border-radius: 5px; margin: 5px 0; }
|
|
@@ -533,26 +536,36 @@ def create_gradio_interface():
|
|
| 533 |
) as iface:
|
| 534 |
logger.info("Gradio Blocks initialized")
|
| 535 |
gr.Markdown("# 🧬 Gene Analysis Pipeline")
|
|
|
|
|
|
|
| 536 |
with gr.Row():
|
| 537 |
with gr.Column():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
status_display = gr.HTML(value=f"""
|
| 539 |
<div class="status-box">
|
| 540 |
<h3>🔧 System Status</h3>
|
| 541 |
<p>🤖 Boundary Model: {'✅ Loaded' if boundary_model else '❌ Missing'}</p>
|
| 542 |
<p>🧠 Keras Model: {'✅ Loaded' if keras_model else '❌ Missing'}</p>
|
| 543 |
<p>🌳 Tree Analyzer: {'✅ Loaded' if analyzer else '❌ Missing'}</p>
|
| 544 |
-
<p>🧬 MAFFT: {'✅ Available' if
|
| 545 |
-
<p>🌲 IQ-TREE: {'✅ Available' if
|
| 546 |
</div>
|
| 547 |
""")
|
|
|
|
|
|
|
| 548 |
with gr.Tabs():
|
| 549 |
with gr.TabItem("📝 Text Input"):
|
| 550 |
with gr.Row():
|
| 551 |
with gr.Column(scale=2):
|
| 552 |
dna_input = gr.Textbox(
|
| 553 |
-
label="🧬 DNA Sequence
|
| 554 |
placeholder="Enter DNA sequence (ATCG format)...",
|
| 555 |
-
lines=5
|
|
|
|
| 556 |
)
|
| 557 |
with gr.Column(scale=1):
|
| 558 |
similarity_score = gr.Slider(
|
|
@@ -560,18 +573,19 @@ def create_gradio_interface():
|
|
| 560 |
maximum=99,
|
| 561 |
value=95.0,
|
| 562 |
step=1.0,
|
| 563 |
-
label="🎯 Similarity Threshold (%)
|
| 564 |
)
|
| 565 |
build_ml_tree = gr.Checkbox(
|
| 566 |
-
label="🌲 Build ML Tree
|
| 567 |
value=False
|
| 568 |
)
|
| 569 |
analyze_btn = gr.Button("🔬 Analyze Sequence", variant="primary")
|
|
|
|
| 570 |
with gr.TabItem("📁 File Upload"):
|
| 571 |
with gr.Row():
|
| 572 |
with gr.Column(scale=2):
|
| 573 |
file_input = gr.File(
|
| 574 |
-
label="📄 Upload FASTA File
|
| 575 |
file_types=[".fasta", ".fa", ".fas", ".txt"]
|
| 576 |
)
|
| 577 |
with gr.Column(scale=1):
|
|
@@ -580,14 +594,18 @@ def create_gradio_interface():
|
|
| 580 |
maximum=99,
|
| 581 |
value=95.0,
|
| 582 |
step=1.0,
|
| 583 |
-
label="🎯 Similarity Threshold (%)
|
| 584 |
)
|
| 585 |
file_build_ml_tree = gr.Checkbox(
|
| 586 |
-
label="🌲 Build ML Tree
|
| 587 |
value=False
|
| 588 |
)
|
| 589 |
analyze_file_btn = gr.Button("🔬 Analyze File", variant="primary")
|
|
|
|
|
|
|
| 590 |
gr.Markdown("## 📊 Analysis Results")
|
|
|
|
|
|
|
| 591 |
with gr.Row():
|
| 592 |
with gr.Column():
|
| 593 |
boundary_output = gr.Textbox(
|
|
@@ -611,47 +629,95 @@ def create_gradio_interface():
|
|
| 611 |
interactive=False,
|
| 612 |
lines=2
|
| 613 |
)
|
|
|
|
|
|
|
| 614 |
summary_output = gr.Textbox(
|
| 615 |
label="📋 Summary",
|
| 616 |
interactive=False,
|
| 617 |
lines=8
|
| 618 |
)
|
|
|
|
|
|
|
| 619 |
with gr.Row():
|
| 620 |
aligned_file = gr.File(label="📄 Alignment File", visible=False)
|
| 621 |
tree_file = gr.File(label="🌲 Tree File", visible=False)
|
|
|
|
|
|
|
| 622 |
with gr.Tabs():
|
| 623 |
with gr.TabItem("🌳 Interactive Tree"):
|
| 624 |
tree_html = gr.HTML(
|
| 625 |
-
label="Phylogenetic Tree",
|
| 626 |
value="<div style='text-align: center; padding: 20px; color: #666;'>No tree generated yet.</div>"
|
| 627 |
)
|
| 628 |
with gr.TabItem("📊 Detailed Report"):
|
| 629 |
report_html = gr.HTML(
|
| 630 |
-
label="Analysis Report",
|
| 631 |
value="<div style='text-align: center; padding: 20px; color: #666;'>No report generated yet.</div>"
|
| 632 |
)
|
| 633 |
|
| 634 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 635 |
analyze_btn.click(
|
| 636 |
-
fn=
|
| 637 |
inputs=[dna_input, similarity_score, build_ml_tree],
|
| 638 |
outputs=[
|
| 639 |
-
boundary_output,
|
| 640 |
-
|
| 641 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 642 |
]
|
| 643 |
)
|
| 644 |
|
| 645 |
analyze_file_btn.click(
|
| 646 |
-
fn=
|
| 647 |
inputs=[file_input, file_similarity_score, file_build_ml_tree],
|
| 648 |
outputs=[
|
| 649 |
-
boundary_output,
|
| 650 |
-
|
| 651 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 652 |
]
|
| 653 |
)
|
| 654 |
|
|
|
|
| 655 |
gr.Markdown("""
|
| 656 |
## 📖 Instructions
|
| 657 |
|
|
@@ -685,26 +751,81 @@ def create_gradio_interface():
|
|
| 685 |
|
| 686 |
except Exception as e:
|
| 687 |
logger.error(f"❌ Failed to create Gradio interface: {e}")
|
| 688 |
-
|
|
|
|
|
|
|
| 689 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
simple_iface = gr.Interface(
|
| 691 |
-
fn=
|
| 692 |
-
inputs=gr.Textbox(
|
| 693 |
-
|
| 694 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
)
|
| 696 |
logger.info("⚠️ Fallback interface created")
|
| 697 |
return simple_iface
|
|
|
|
| 698 |
except Exception as fallback_error:
|
| 699 |
logger.error(f"❌ Even fallback interface failed: {fallback_error}")
|
| 700 |
return None
|
| 701 |
|
| 702 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 703 |
def main():
|
| 704 |
-
"""Main function to launch the application."""
|
| 705 |
logger.info("🚀 Starting Gene Analysis Pipeline...")
|
| 706 |
|
| 707 |
-
# Check if we should run in simple mode
|
| 708 |
simple_mode = os.environ.get("SIMPLE_MODE", "false").lower() == "true"
|
| 709 |
|
| 710 |
if simple_mode:
|
|
@@ -713,48 +834,48 @@ def main():
|
|
| 713 |
return
|
| 714 |
|
| 715 |
try:
|
| 716 |
-
# Create Gradio interface
|
| 717 |
gradio_app = create_gradio_interface()
|
| 718 |
|
| 719 |
if gradio_app is None:
|
| 720 |
-
logger.error("❌ Failed to create Gradio interface")
|
|
|
|
| 721 |
return
|
| 722 |
|
| 723 |
-
#
|
| 724 |
try:
|
| 725 |
-
|
| 726 |
-
gradio_app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
|
| 727 |
logger.info("✅ Gradio mounted to FastAPI at /gradio")
|
| 728 |
-
except Exception as
|
| 729 |
-
logger.error(f"❌ Failed to mount Gradio: {
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
|
|
|
| 736 |
gradio_app.launch(
|
| 737 |
server_name="0.0.0.0",
|
| 738 |
-
server_port=7861,
|
| 739 |
share=False,
|
| 740 |
debug=False,
|
| 741 |
prevent_thread_lock=True
|
| 742 |
)
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
|
| 750 |
# Launch configuration
|
| 751 |
port = int(os.environ.get("PORT", 7860))
|
| 752 |
host = "0.0.0.0"
|
| 753 |
|
| 754 |
logger.info(f"🌐 Launching FastAPI on {host}:{port}")
|
| 755 |
-
logger.info(f"📱
|
| 756 |
-
logger.info(f"🧬 Gradio interface: http://{host}:
|
| 757 |
-
logger.info(f"🧬 Gradio interface: http://{host}:{port}/gradio (if mounted successfully)")
|
| 758 |
|
| 759 |
# Launch the server
|
| 760 |
uvicorn.run(
|
|
@@ -763,34 +884,18 @@ def main():
|
|
| 763 |
port=port,
|
| 764 |
log_level="info",
|
| 765 |
access_log=True,
|
| 766 |
-
reload=False
|
| 767 |
)
|
| 768 |
|
| 769 |
except Exception as e:
|
| 770 |
-
logger.error(f"❌
|
| 771 |
-
#
|
| 772 |
try:
|
| 773 |
-
logger.info("🔄
|
| 774 |
launch_gradio_only()
|
| 775 |
-
except Exception as
|
| 776 |
-
logger.error(f"❌
|
| 777 |
-
|
| 778 |
-
if __name__ == "__main__":
|
| 779 |
-
main()
|
| 780 |
-
|
| 781 |
-
# Alternative entry points for different deployment scenarios
|
| 782 |
-
def launch_gradio_only():
|
| 783 |
-
"""Launch only Gradio interface (for simpler deployments)"""
|
| 784 |
-
logger.info("🚀 Launching Gradio-only mode...")
|
| 785 |
-
gradio_app = create_gradio_interface()
|
| 786 |
-
if gradio_app:
|
| 787 |
-
gradio_app.launch(
|
| 788 |
-
server_name="0.0.0.0",
|
| 789 |
-
server_port=int(os.environ.get("PORT", 7860)),
|
| 790 |
-
share=False,
|
| 791 |
-
debug=False
|
| 792 |
-
)
|
| 793 |
-
|
| 794 |
def launch_fastapi_only():
|
| 795 |
"""Launch only FastAPI (for API-only deployments)"""
|
| 796 |
logger.info("🚀 Launching FastAPI-only mode...")
|
|
|
|
| 520 |
def create_gradio_interface():
|
| 521 |
logger.info("Attempting to create Gradio interface...")
|
| 522 |
try:
|
| 523 |
+
# Use a more basic theme to avoid compatibility issues
|
| 524 |
+
theme = gr.themes.Base()
|
| 525 |
+
|
| 526 |
with gr.Blocks(
|
| 527 |
title="🧬 Gene Analysis Pipeline",
|
| 528 |
+
theme=theme,
|
| 529 |
css="""
|
| 530 |
.gradio-container { max-width: 1200px !important; }
|
| 531 |
.status-box { padding: 10px; border-radius: 5px; margin: 5px 0; }
|
|
|
|
| 536 |
) as iface:
|
| 537 |
logger.info("Gradio Blocks initialized")
|
| 538 |
gr.Markdown("# 🧬 Gene Analysis Pipeline")
|
| 539 |
+
|
| 540 |
+
# System Status
|
| 541 |
with gr.Row():
|
| 542 |
with gr.Column():
|
| 543 |
+
try:
|
| 544 |
+
mafft_avail, iqtree_avail, _, _ = check_tool_availability()
|
| 545 |
+
except:
|
| 546 |
+
mafft_avail, iqtree_avail = False, False
|
| 547 |
+
|
| 548 |
status_display = gr.HTML(value=f"""
|
| 549 |
<div class="status-box">
|
| 550 |
<h3>🔧 System Status</h3>
|
| 551 |
<p>🤖 Boundary Model: {'✅ Loaded' if boundary_model else '❌ Missing'}</p>
|
| 552 |
<p>🧠 Keras Model: {'✅ Loaded' if keras_model else '❌ Missing'}</p>
|
| 553 |
<p>🌳 Tree Analyzer: {'✅ Loaded' if analyzer else '❌ Missing'}</p>
|
| 554 |
+
<p>🧬 MAFFT: {'✅ Available' if mafft_avail else '❌ Missing'}</p>
|
| 555 |
+
<p>🌲 IQ-TREE: {'✅ Available' if iqtree_avail else '❌ Missing'}</p>
|
| 556 |
</div>
|
| 557 |
""")
|
| 558 |
+
|
| 559 |
+
# Input Tabs
|
| 560 |
with gr.Tabs():
|
| 561 |
with gr.TabItem("📝 Text Input"):
|
| 562 |
with gr.Row():
|
| 563 |
with gr.Column(scale=2):
|
| 564 |
dna_input = gr.Textbox(
|
| 565 |
+
label="🧬 DNA Sequence",
|
| 566 |
placeholder="Enter DNA sequence (ATCG format)...",
|
| 567 |
+
lines=5,
|
| 568 |
+
max_lines=10
|
| 569 |
)
|
| 570 |
with gr.Column(scale=1):
|
| 571 |
similarity_score = gr.Slider(
|
|
|
|
| 573 |
maximum=99,
|
| 574 |
value=95.0,
|
| 575 |
step=1.0,
|
| 576 |
+
label="🎯 Similarity Threshold (%)"
|
| 577 |
)
|
| 578 |
build_ml_tree = gr.Checkbox(
|
| 579 |
+
label="🌲 Build ML Tree",
|
| 580 |
value=False
|
| 581 |
)
|
| 582 |
analyze_btn = gr.Button("🔬 Analyze Sequence", variant="primary")
|
| 583 |
+
|
| 584 |
with gr.TabItem("📁 File Upload"):
|
| 585 |
with gr.Row():
|
| 586 |
with gr.Column(scale=2):
|
| 587 |
file_input = gr.File(
|
| 588 |
+
label="📄 Upload FASTA File",
|
| 589 |
file_types=[".fasta", ".fa", ".fas", ".txt"]
|
| 590 |
)
|
| 591 |
with gr.Column(scale=1):
|
|
|
|
| 594 |
maximum=99,
|
| 595 |
value=95.0,
|
| 596 |
step=1.0,
|
| 597 |
+
label="🎯 Similarity Threshold (%)"
|
| 598 |
)
|
| 599 |
file_build_ml_tree = gr.Checkbox(
|
| 600 |
+
label="🌲 Build ML Tree",
|
| 601 |
value=False
|
| 602 |
)
|
| 603 |
analyze_file_btn = gr.Button("🔬 Analyze File", variant="primary")
|
| 604 |
+
|
| 605 |
+
# Results Section
|
| 606 |
gr.Markdown("## 📊 Analysis Results")
|
| 607 |
+
|
| 608 |
+
# Main Results
|
| 609 |
with gr.Row():
|
| 610 |
with gr.Column():
|
| 611 |
boundary_output = gr.Textbox(
|
|
|
|
| 629 |
interactive=False,
|
| 630 |
lines=2
|
| 631 |
)
|
| 632 |
+
|
| 633 |
+
# Summary
|
| 634 |
summary_output = gr.Textbox(
|
| 635 |
label="📋 Summary",
|
| 636 |
interactive=False,
|
| 637 |
lines=8
|
| 638 |
)
|
| 639 |
+
|
| 640 |
+
# File outputs (hidden by default)
|
| 641 |
with gr.Row():
|
| 642 |
aligned_file = gr.File(label="📄 Alignment File", visible=False)
|
| 643 |
tree_file = gr.File(label="🌲 Tree File", visible=False)
|
| 644 |
+
|
| 645 |
+
# Visualization Tabs
|
| 646 |
with gr.Tabs():
|
| 647 |
with gr.TabItem("🌳 Interactive Tree"):
|
| 648 |
tree_html = gr.HTML(
|
|
|
|
| 649 |
value="<div style='text-align: center; padding: 20px; color: #666;'>No tree generated yet.</div>"
|
| 650 |
)
|
| 651 |
with gr.TabItem("📊 Detailed Report"):
|
| 652 |
report_html = gr.HTML(
|
|
|
|
| 653 |
value="<div style='text-align: center; padding: 20px; color: #666;'>No report generated yet.</div>"
|
| 654 |
)
|
| 655 |
|
| 656 |
+
# Create a wrapper function to handle the outputs properly
|
| 657 |
+
def run_pipeline_wrapper(*args):
|
| 658 |
+
try:
|
| 659 |
+
result = run_pipeline(*args)
|
| 660 |
+
# Ensure we return exactly 11 outputs
|
| 661 |
+
if len(result) < 11:
|
| 662 |
+
# Pad with default values
|
| 663 |
+
result = list(result) + [None] * (11 - len(result))
|
| 664 |
+
return result[:11] # Truncate if too many
|
| 665 |
+
except Exception as e:
|
| 666 |
+
logger.error(f"Pipeline wrapper error: {e}")
|
| 667 |
+
error_msg = f"❌ Error: {str(e)}"
|
| 668 |
+
return (error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg)
|
| 669 |
+
|
| 670 |
+
def run_pipeline_from_file_wrapper(*args):
|
| 671 |
+
try:
|
| 672 |
+
result = run_pipeline_from_file(*args)
|
| 673 |
+
# Ensure we return exactly 11 outputs
|
| 674 |
+
if len(result) < 11:
|
| 675 |
+
# Pad with default values
|
| 676 |
+
result = list(result) + [None] * (11 - len(result))
|
| 677 |
+
return result[:11] # Truncate if too many
|
| 678 |
+
except Exception as e:
|
| 679 |
+
logger.error(f"Pipeline file wrapper error: {e}")
|
| 680 |
+
error_msg = f"❌ Error: {str(e)}"
|
| 681 |
+
return (error_msg, "", "", "", "", None, None, None, None, error_msg, error_msg)
|
| 682 |
+
|
| 683 |
+
# Event handlers with explicit output specification
|
| 684 |
analyze_btn.click(
|
| 685 |
+
fn=run_pipeline_wrapper,
|
| 686 |
inputs=[dna_input, similarity_score, build_ml_tree],
|
| 687 |
outputs=[
|
| 688 |
+
boundary_output,
|
| 689 |
+
keras_output,
|
| 690 |
+
ml_tree_output,
|
| 691 |
+
tree_analysis_output,
|
| 692 |
+
summary_output,
|
| 693 |
+
aligned_file,
|
| 694 |
+
tree_file,
|
| 695 |
+
gr.File(visible=False), # dummy output 1
|
| 696 |
+
gr.File(visible=False), # dummy output 2
|
| 697 |
+
tree_html,
|
| 698 |
+
report_html
|
| 699 |
]
|
| 700 |
)
|
| 701 |
|
| 702 |
analyze_file_btn.click(
|
| 703 |
+
fn=run_pipeline_from_file_wrapper,
|
| 704 |
inputs=[file_input, file_similarity_score, file_build_ml_tree],
|
| 705 |
outputs=[
|
| 706 |
+
boundary_output,
|
| 707 |
+
keras_output,
|
| 708 |
+
ml_tree_output,
|
| 709 |
+
tree_analysis_output,
|
| 710 |
+
summary_output,
|
| 711 |
+
aligned_file,
|
| 712 |
+
tree_file,
|
| 713 |
+
gr.File(visible=False), # dummy output 1
|
| 714 |
+
gr.File(visible=False), # dummy output 2
|
| 715 |
+
tree_html,
|
| 716 |
+
report_html
|
| 717 |
]
|
| 718 |
)
|
| 719 |
|
| 720 |
+
# Instructions
|
| 721 |
gr.Markdown("""
|
| 722 |
## 📖 Instructions
|
| 723 |
|
|
|
|
| 751 |
|
| 752 |
except Exception as e:
|
| 753 |
logger.error(f"❌ Failed to create Gradio interface: {e}")
|
| 754 |
+
logger.error(f"Error details: {type(e).__name__}: {str(e)}")
|
| 755 |
+
|
| 756 |
+
# Enhanced fallback interface
|
| 757 |
try:
|
| 758 |
+
def simple_analysis(sequence):
|
| 759 |
+
if not sequence:
|
| 760 |
+
return "Please enter a DNA sequence"
|
| 761 |
+
|
| 762 |
+
try:
|
| 763 |
+
# Basic sequence validation
|
| 764 |
+
sequence = sequence.upper().strip()
|
| 765 |
+
valid_chars = set('ATCGN')
|
| 766 |
+
if not all(c in valid_chars for c in sequence):
|
| 767 |
+
return "Invalid characters in sequence. Use only A, T, C, G, N"
|
| 768 |
+
|
| 769 |
+
# Try basic analysis
|
| 770 |
+
if len(sequence) < 10:
|
| 771 |
+
return "Sequence too short (minimum 10 bp)"
|
| 772 |
+
|
| 773 |
+
# Simple analysis result
|
| 774 |
+
result = f"""
|
| 775 |
+
✅ Sequence Analysis Complete
|
| 776 |
+
Length: {len(sequence)} bp
|
| 777 |
+
GC Content: {((sequence.count('G') + sequence.count('C')) / len(sequence) * 100):.1f}%
|
| 778 |
+
|
| 779 |
+
Note: Full analysis pipeline unavailable due to interface error.
|
| 780 |
+
Error: {str(e)}
|
| 781 |
+
"""
|
| 782 |
+
return result
|
| 783 |
+
|
| 784 |
+
except Exception as analysis_error:
|
| 785 |
+
return f"Analysis error: {str(analysis_error)}"
|
| 786 |
+
|
| 787 |
simple_iface = gr.Interface(
|
| 788 |
+
fn=simple_analysis,
|
| 789 |
+
inputs=gr.Textbox(
|
| 790 |
+
label="🧬 DNA Sequence",
|
| 791 |
+
placeholder="Enter DNA sequence (ATCG format)...",
|
| 792 |
+
lines=5
|
| 793 |
+
),
|
| 794 |
+
outputs=gr.Textbox(
|
| 795 |
+
label="📊 Analysis Result",
|
| 796 |
+
lines=10
|
| 797 |
+
),
|
| 798 |
+
title="🧬 Gene Analysis Pipeline - Fallback Mode",
|
| 799 |
+
description=f"Simplified interface due to error: {str(e)}"
|
| 800 |
)
|
| 801 |
logger.info("⚠️ Fallback interface created")
|
| 802 |
return simple_iface
|
| 803 |
+
|
| 804 |
except Exception as fallback_error:
|
| 805 |
logger.error(f"❌ Even fallback interface failed: {fallback_error}")
|
| 806 |
return None
|
| 807 |
|
| 808 |
+
# Additional helper function to handle Gradio mounting issues
|
| 809 |
+
def safe_gradio_mount(app, gradio_app, path="/gradio"):
|
| 810 |
+
"""Safely mount Gradio app with error handling"""
|
| 811 |
+
try:
|
| 812 |
+
# Try the standard mounting approach
|
| 813 |
+
if hasattr(gr, 'mount_gradio_app'):
|
| 814 |
+
return gr.mount_gradio_app(app, gradio_app, path=path)
|
| 815 |
+
else:
|
| 816 |
+
# Alternative approach for older Gradio versions
|
| 817 |
+
gradio_app.mount_path = path
|
| 818 |
+
return app.mount(path, gradio_app.app)
|
| 819 |
+
except Exception as e:
|
| 820 |
+
logger.error(f"Failed to mount Gradio: {e}")
|
| 821 |
+
raise e
|
| 822 |
+
|
| 823 |
+
# Enhanced main function with better error handling
|
| 824 |
def main():
|
| 825 |
+
"""Main function to launch the application with enhanced error handling."""
|
| 826 |
logger.info("🚀 Starting Gene Analysis Pipeline...")
|
| 827 |
|
| 828 |
+
# Check if we should run in simple mode
|
| 829 |
simple_mode = os.environ.get("SIMPLE_MODE", "false").lower() == "true"
|
| 830 |
|
| 831 |
if simple_mode:
|
|
|
|
| 834 |
return
|
| 835 |
|
| 836 |
try:
|
| 837 |
+
# Create Gradio interface with enhanced error handling
|
| 838 |
gradio_app = create_gradio_interface()
|
| 839 |
|
| 840 |
if gradio_app is None:
|
| 841 |
+
logger.error("❌ Failed to create Gradio interface, launching API-only mode")
|
| 842 |
+
launch_fastapi_only()
|
| 843 |
return
|
| 844 |
|
| 845 |
+
# Try to mount Gradio app to FastAPI
|
| 846 |
try:
|
| 847 |
+
safe_gradio_mount(app, gradio_app, path="/gradio")
|
|
|
|
| 848 |
logger.info("✅ Gradio mounted to FastAPI at /gradio")
|
| 849 |
+
except Exception as mount_error:
|
| 850 |
+
logger.error(f"❌ Failed to mount Gradio: {mount_error}")
|
| 851 |
+
logger.info("🔄 Launching Gradio and FastAPI separately...")
|
| 852 |
+
|
| 853 |
+
# Launch Gradio in a separate thread
|
| 854 |
+
import threading
|
| 855 |
+
|
| 856 |
+
def launch_gradio_separate():
|
| 857 |
+
try:
|
| 858 |
gradio_app.launch(
|
| 859 |
server_name="0.0.0.0",
|
| 860 |
+
server_port=7861,
|
| 861 |
share=False,
|
| 862 |
debug=False,
|
| 863 |
prevent_thread_lock=True
|
| 864 |
)
|
| 865 |
+
except Exception as gradio_error:
|
| 866 |
+
logger.error(f"❌ Separate Gradio launch failed: {gradio_error}")
|
| 867 |
+
|
| 868 |
+
gradio_thread = threading.Thread(target=launch_gradio_separate, daemon=True)
|
| 869 |
+
gradio_thread.start()
|
| 870 |
+
logger.info("✅ Gradio launched on separate port 7861")
|
| 871 |
|
| 872 |
# Launch configuration
|
| 873 |
port = int(os.environ.get("PORT", 7860))
|
| 874 |
host = "0.0.0.0"
|
| 875 |
|
| 876 |
logger.info(f"🌐 Launching FastAPI on {host}:{port}")
|
| 877 |
+
logger.info(f"📱 API docs available at: http://{host}:{port}/docs")
|
| 878 |
+
logger.info(f"🧬 Gradio interface at: http://{host}:{port}/gradio")
|
|
|
|
| 879 |
|
| 880 |
# Launch the server
|
| 881 |
uvicorn.run(
|
|
|
|
| 884 |
port=port,
|
| 885 |
log_level="info",
|
| 886 |
access_log=True,
|
| 887 |
+
reload=False
|
| 888 |
)
|
| 889 |
|
| 890 |
except Exception as e:
|
| 891 |
+
logger.error(f"❌ Main application launch failed: {e}")
|
| 892 |
+
# Final fallback: Launch only Gradio
|
| 893 |
try:
|
| 894 |
+
logger.info("🔄 Final fallback: Gradio-only launch...")
|
| 895 |
launch_gradio_only()
|
| 896 |
+
except Exception as final_error:
|
| 897 |
+
logger.error(f"❌ Final fallback failed: {final_error}")
|
| 898 |
+
logger.error("❌ All launch methods failed. Check your environment and dependencies.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 899 |
def launch_fastapi_only():
|
| 900 |
"""Launch only FastAPI (for API-only deployments)"""
|
| 901 |
logger.info("🚀 Launching FastAPI-only mode...")
|