re-type commited on
Commit
ab77d45
·
verified ·
1 Parent(s): f5d788f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +294 -71
app.py CHANGED
@@ -4,9 +4,10 @@ import numpy as np
4
  import os
5
  import traceback
6
  import logging
 
7
 
8
  # Configure logging
9
- logging.basicConfig(level=logging.INFO)
10
  logger = logging.getLogger(__name__)
11
 
12
  print("=== Gene Prediction App Starting ===")
@@ -14,6 +15,7 @@ print(f"Working directory: {os.getcwd()}")
14
  print(f"Available files: {os.listdir('.')}")
15
  print(f"PyTorch version: {torch.__version__}")
16
  print(f"Gradio version: {gr.__version__}")
 
17
 
18
  # Global variables
19
  predictor = None
@@ -26,29 +28,55 @@ def initialize_model():
26
 
27
  try:
28
  print("Attempting to import predictor...")
29
- from predictor import GenePredictor
30
- print("✅ Predictor imported successfully")
31
 
32
- model_path = 'best_boundary_aware_model.pth'
33
- print(f"Looking for model file: {model_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- if not os.path.exists(model_path):
36
- error_message = f"❌ Model file '{model_path}' not found in directory"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  print(error_message)
38
- print(f"Available files: {[f for f in os.listdir('.') if f.endswith('.pth')]}")
39
  return False
40
 
41
- print(f"Model file found. File size: {os.path.getsize(model_path)} bytes")
 
42
 
 
43
  predictor = GenePredictor(model_path=model_path)
44
  model_loaded = True
45
  print("✅ Model initialized successfully")
46
  return True
47
 
48
- except ImportError as e:
49
- error_message = f"❌ Failed to import predictor: {str(e)}"
50
- print(error_message)
51
- return False
52
  except Exception as e:
53
  error_message = f"❌ Model initialization failed: {str(e)}"
54
  print(error_message)
@@ -61,13 +89,15 @@ def predict_genes(sequence):
61
  try:
62
  # Check if model is loaded
63
  if not model_loaded or predictor is None:
64
- return f"🚫 **Model Error**\n\n{error_message}\n\nPlease check the logs for more details."
65
 
66
  # Input validation
67
  if not sequence or not sequence.strip():
68
  return "⚠️ **Input Error**\n\nPlease enter a DNA sequence."
69
 
70
- sequence = sequence.strip().upper().replace(' ', '').replace('\n', '').replace('\t', '')
 
 
71
 
72
  # Character validation
73
  valid_chars = set('ATCGN')
@@ -80,7 +110,7 @@ def predict_genes(sequence):
80
  return f"⚠️ **Sequence Too Short**\n\nMinimum length: 3 nucleotides\nYour sequence: {len(sequence)} nucleotides"
81
 
82
  if len(sequence) > 10000:
83
- return f"⚠️ **Sequence Too Long**\n\nMaximum length: 10,000 nucleotides\nYour sequence: {len(sequence)} nucleotides"
84
 
85
  print(f"Processing sequence of length: {len(sequence)}")
86
 
@@ -89,125 +119,318 @@ def predict_genes(sequence):
89
  regions = predictor.extract_gene_regions(predictions, sequence)
90
 
91
  # Format results
92
- if not regions:
93
- return f"🔍 **No Gene Regions Detected**\n\nSequence length: {len(sequence)} bp\nConfidence: {confidence:.3f}\n\nThe model did not detect any gene regions in this sequence."
94
-
95
  result = f"🧬 **Gene Prediction Results**\n\n"
96
- result += f"📊 **Summary:**\n"
97
- result += f"• Found: {len(regions)} gene region(s)\n"
98
- result += f"• Sequence length: {len(sequence)} bp\n"
99
- result += f"• Overall confidence: {confidence:.3f}\n\n"
 
 
 
 
 
 
 
 
 
 
100
 
101
- result += f"📍 **Detected Regions:**\n\n"
102
 
 
103
  for i, region in enumerate(regions, 1):
104
- result += f"**Region {i}:**\n"
105
- result += f" Position: {region['start']:,} - {region['end']:,}\n"
106
- result += f" Length: {region['length']:,} bp\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  # Sequence preview
109
  seq = region.get('sequence', '')
110
  if seq:
111
- if len(seq) <= 100:
112
- result += f" Sequence: `{seq}`\n"
113
  else:
114
- preview = seq[:50] + '...' + seq[-50:]
115
- result += f" Preview: `{preview}`\n"
116
 
 
117
  result += "\n"
118
 
 
 
 
 
 
 
119
  return result
120
 
121
  except Exception as e:
122
- error_msg = f"🚫 **Prediction Error**\n\nAn error occurred during prediction:\n```\n{str(e)}\n```"
 
 
 
 
 
 
 
123
  print(f"Prediction error: {e}")
124
  traceback.print_exc()
125
  return error_msg
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  # Initialize model on startup
128
  print("Initializing model...")
129
  model_status = initialize_model()
130
 
131
- # Create interface
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  print("Creating Gradio interface...")
133
 
134
- # Determine status message and color
135
  if model_loaded:
136
- status_html = '<div style="padding: 10px; background-color: #d4edda; border: 1px solid #c3e6cb; border-radius: 5px; color: #155724;"><strong>✅ Model Status:</strong> Ready for predictions!</div>'
 
 
 
 
 
137
  else:
138
- status_html = f'<div style="padding: 10px; background-color: #f8d7da; border: 1px solid #f5c6cb; border-radius: 5px; color: #721c24;"><strong>❌ Model Status:</strong> {error_message}</div>'
 
 
 
 
 
139
 
140
- # Example sequence
141
- example_sequence = "ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGACGCGTACAGGAAACACAGAAAAAAGCCCGCACCTGACAGTGCGGGCTTTTTTTTTCGACCAAAGGTAACGAGGTAACAACCATGCGAGTGTTGAAGTTCGGCGGTACATCAGTGGCAAATGCAGAACGTTTTCTGCG"
 
 
 
 
 
142
 
143
- with gr.Blocks(title="🧬 Gene Prediction Tool", theme=gr.themes.Soft()) as demo:
144
- gr.HTML("<h1 style='text-align: center; color: #2E8B57;'>🧬 Gene Prediction Tool</h1>")
 
 
 
 
 
145
 
146
- gr.HTML(status_html)
 
147
 
148
- gr.Markdown("""
149
- ### Instructions:
150
- 1. **Enter a DNA sequence** using only A, T, C, G, N characters
151
- 2. **Click Submit** to analyze the sequence
152
- 3. **View results** showing predicted gene regions with positions and confidence scores
153
 
154
- **Sequence Requirements:**
155
- - Only A, T, C, G, N characters allowed
156
- - Minimum length: 3 nucleotides
157
- - Maximum length: 10,000 nucleotides
158
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
  with gr.Row():
161
- with gr.Column(scale=2):
162
  sequence_input = gr.Textbox(
163
- label="DNA Sequence",
164
- placeholder="Enter DNA sequence (A, T, C, G, N)...",
165
- lines=8,
166
- max_lines=15
 
 
167
  )
168
 
 
 
 
 
 
 
 
 
 
169
  with gr.Row():
170
- submit_btn = gr.Button("🔬 Analyze Sequence", variant="primary", size="lg")
171
- clear_btn = gr.Button("🗑️ Clear", variant="secondary")
172
- example_btn = gr.Button("📝 Load Example", variant="secondary")
173
 
174
- with gr.Column(scale=3):
175
  output = gr.Textbox(
176
- label="Prediction Results",
177
- lines=20,
178
- max_lines=30,
179
- show_copy_button=True
 
 
180
  )
181
 
 
 
 
 
 
 
 
182
  # Event handlers
 
 
 
 
 
 
 
 
 
 
 
183
  submit_btn.click(
184
  fn=predict_genes,
185
  inputs=sequence_input,
186
  outputs=output
187
  )
188
 
 
189
  clear_btn.click(
190
- fn=lambda: ("", ""),
191
- outputs=[sequence_input, output]
192
  )
193
 
194
- example_btn.click(
195
- fn=lambda: example_sequence,
 
196
  outputs=sequence_input
197
  )
198
 
199
- # Also allow Enter key to submit
 
 
 
 
 
200
  sequence_input.submit(
201
  fn=predict_genes,
202
  inputs=sequence_input,
203
  outputs=output
204
  )
205
 
 
206
  if __name__ == "__main__":
207
- print("Launching Gradio app...")
 
 
 
208
  demo.launch(
209
  server_name="0.0.0.0",
210
  server_port=7860,
211
  show_error=True,
212
- show_api=False
 
 
 
213
  )
 
4
  import os
5
  import traceback
6
  import logging
7
+ import sys
8
 
9
  # Configure logging
10
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
11
  logger = logging.getLogger(__name__)
12
 
13
  print("=== Gene Prediction App Starting ===")
 
15
  print(f"Available files: {os.listdir('.')}")
16
  print(f"PyTorch version: {torch.__version__}")
17
  print(f"Gradio version: {gr.__version__}")
18
+ print(f"Python path: {sys.path}")
19
 
20
  # Global variables
21
  predictor = None
 
28
 
29
  try:
30
  print("Attempting to import predictor...")
 
 
31
 
32
+ # Try different import approaches
33
+ try:
34
+ from predictor import GenePredictor
35
+ print("✅ Imported from predictor module")
36
+ except ImportError:
37
+ try:
38
+ # If predictor.py is in the same directory
39
+ import importlib.util
40
+ spec = importlib.util.spec_from_file_location("predictor", "predictor.py")
41
+ predictor_module = importlib.util.module_from_spec(spec)
42
+ spec.loader.exec_module(predictor_module)
43
+ GenePredictor = predictor_module.GenePredictor
44
+ print("✅ Imported predictor.py directly")
45
+ except Exception as e:
46
+ print(f"Failed to import predictor: {e}")
47
+ raise ImportError(f"Could not import GenePredictor: {e}")
48
 
49
+ # Look for model file
50
+ possible_model_paths = [
51
+ 'best_boundary_aware_model.pth',
52
+ 'model/best_boundary_aware_model.pth',
53
+ './best_boundary_aware_model.pth'
54
+ ]
55
+
56
+ model_path = None
57
+ for path in possible_model_paths:
58
+ if os.path.exists(path):
59
+ model_path = path
60
+ break
61
+
62
+ if not model_path:
63
+ available_models = [f for f in os.listdir('.') if f.endswith('.pth')]
64
+ if os.path.exists('model'):
65
+ available_models.extend([f"model/{f}" for f in os.listdir('model') if f.endswith('.pth')])
66
+
67
+ error_message = f"❌ Model file not found. Searched: {possible_model_paths}. Available: {available_models}"
68
  print(error_message)
 
69
  return False
70
 
71
+ print(f"Found model file: {model_path}")
72
+ print(f"Model file size: {os.path.getsize(model_path)} bytes")
73
 
74
+ # Initialize predictor
75
  predictor = GenePredictor(model_path=model_path)
76
  model_loaded = True
77
  print("✅ Model initialized successfully")
78
  return True
79
 
 
 
 
 
80
  except Exception as e:
81
  error_message = f"❌ Model initialization failed: {str(e)}"
82
  print(error_message)
 
89
  try:
90
  # Check if model is loaded
91
  if not model_loaded or predictor is None:
92
+ return f"🚫 **Model Error**\n\n{error_message}\n\nPlease check that:\n1. predictor.py is in the same directory\n2. Model file (.pth) exists\n3. All dependencies are installed"
93
 
94
  # Input validation
95
  if not sequence or not sequence.strip():
96
  return "⚠️ **Input Error**\n\nPlease enter a DNA sequence."
97
 
98
+ # Clean sequence
99
+ sequence = sequence.strip().upper()
100
+ sequence = sequence.replace(' ', '').replace('\n', '').replace('\t', '').replace('\r', '')
101
 
102
  # Character validation
103
  valid_chars = set('ATCGN')
 
110
  return f"⚠️ **Sequence Too Short**\n\nMinimum length: 3 nucleotides\nYour sequence: {len(sequence)} nucleotides"
111
 
112
  if len(sequence) > 10000:
113
+ return f"⚠️ **Sequence Too Long**\n\nMaximum length: 10,000 nucleotides\nYour sequence: {len(sequence)} nucleotides\n\nFor longer sequences, consider splitting them into smaller chunks."
114
 
115
  print(f"Processing sequence of length: {len(sequence)}")
116
 
 
119
  regions = predictor.extract_gene_regions(predictions, sequence)
120
 
121
  # Format results
 
 
 
122
  result = f"🧬 **Gene Prediction Results**\n\n"
123
+ result += f"📊 **Analysis Summary:**\n"
124
+ result += f"• Sequence length: {len(sequence):,} bp\n"
125
+ result += f"• Gene regions found: {len(regions)}\n"
126
+ result += f"• Overall confidence: {confidence:.3f}\n"
127
+ result += f"• Analysis completed successfully ✅\n\n"
128
+
129
+ if not regions:
130
+ result += f"🔍 **No Gene Regions Detected**\n\n"
131
+ result += f"The model did not detect any gene regions meeting the minimum criteria in this sequence.\n"
132
+ result += f"This could mean:\n"
133
+ result += f"• The sequence may not contain protein-coding genes\n"
134
+ result += f"• Genes may be partial or fragmented\n"
135
+ result += f"• The sequence may be non-coding DNA\n"
136
+ return result
137
 
138
+ result += f"📍 **Detected Gene Regions:**\n\n"
139
 
140
+ total_gene_length = 0
141
  for i, region in enumerate(regions, 1):
142
+ result += f"**🧬 Gene Region {i}:**\n"
143
+ result += f"├─ Position: {region['start']:,} - {region['end']:,} bp\n"
144
+ result += f"├─ Length: {region['length']:,} bp\n"
145
+ result += f"├─ In-frame: {'Yes' if region.get('in_frame', False) else 'No'}\n"
146
+
147
+ # Start codon info
148
+ start_codon = region.get('start_codon')
149
+ if start_codon:
150
+ result += f"├─ Start codon: {start_codon}\n"
151
+ else:
152
+ result += f"├─ Start codon: Not detected\n"
153
+
154
+ # Stop codon info
155
+ stop_codon = region.get('stop_codon')
156
+ if stop_codon:
157
+ result += f"├─ Stop codon: {stop_codon}\n"
158
+ else:
159
+ result += f"├─ Stop codon: Not detected\n"
160
 
161
  # Sequence preview
162
  seq = region.get('sequence', '')
163
  if seq:
164
+ if len(seq) <= 120:
165
+ result += f"└─ Sequence: `{seq}`\n"
166
  else:
167
+ preview = seq[:60] + '...' + seq[-60:]
168
+ result += f"└─ Sequence: `{preview}`\n"
169
 
170
+ total_gene_length += region['length']
171
  result += "\n"
172
 
173
+ # Summary statistics
174
+ result += f"📈 **Statistics:**\n"
175
+ result += f"• Total gene content: {total_gene_length:,} bp ({total_gene_length/len(sequence)*100:.1f}% of sequence)\n"
176
+ result += f"• Average gene length: {total_gene_length//len(regions):,} bp\n"
177
+ result += f"• Gene density: {len(regions)/(len(sequence)/1000):.2f} genes per kb\n"
178
+
179
  return result
180
 
181
  except Exception as e:
182
+ error_msg = f"🚫 **Prediction Error**\n\n"
183
+ error_msg += f"An error occurred during prediction:\n\n"
184
+ error_msg += f"```\n{str(e)}\n```\n\n"
185
+ error_msg += f"**Troubleshooting:**\n"
186
+ error_msg += f"• Check that predictor.py is in the same directory\n"
187
+ error_msg += f"• Verify model file exists and is not corrupted\n"
188
+ error_msg += f"• Ensure sequence contains only valid DNA characters\n"
189
+
190
  print(f"Prediction error: {e}")
191
  traceback.print_exc()
192
  return error_msg
193
 
194
+ def get_sequence_stats(sequence):
195
+ """Get basic statistics about the input sequence"""
196
+ if not sequence or not sequence.strip():
197
+ return ""
198
+
199
+ sequence = sequence.strip().upper().replace(' ', '').replace('\n', '').replace('\t', '')
200
+
201
+ if not sequence:
202
+ return ""
203
+
204
+ stats = f"**Sequence Info:** {len(sequence)} bp"
205
+
206
+ # Base composition
207
+ a_count = sequence.count('A')
208
+ t_count = sequence.count('T')
209
+ c_count = sequence.count('C')
210
+ g_count = sequence.count('G')
211
+ n_count = sequence.count('N')
212
+
213
+ total_valid = a_count + t_count + c_count + g_count
214
+ if total_valid > 0:
215
+ gc_content = (c_count + g_count) / total_valid * 100
216
+ stats += f" | GC: {gc_content:.1f}%"
217
+
218
+ if n_count > 0:
219
+ stats += f" | N's: {n_count}"
220
+
221
+ return stats
222
+
223
  # Initialize model on startup
224
  print("Initializing model...")
225
  model_status = initialize_model()
226
 
227
+ # Create custom CSS for better styling
228
+ custom_css = """
229
+ .gene-app {
230
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
231
+ }
232
+
233
+ .status-ready {
234
+ background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
235
+ border: 2px solid #28a745;
236
+ border-radius: 10px;
237
+ padding: 15px;
238
+ color: #155724;
239
+ font-weight: bold;
240
+ box-shadow: 0 2px 10px rgba(40, 167, 69, 0.2);
241
+ }
242
+
243
+ .status-error {
244
+ background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
245
+ border: 2px solid #dc3545;
246
+ border-radius: 10px;
247
+ padding: 15px;
248
+ color: #721c24;
249
+ font-weight: bold;
250
+ box-shadow: 0 2px 10px rgba(220, 53, 69, 0.2);
251
+ }
252
+
253
+ .main-title {
254
+ text-align: center;
255
+ background: linear-gradient(135deg, #2E8B57 0%, #20B2AA 100%);
256
+ -webkit-background-clip: text;
257
+ -webkit-text-fill-color: transparent;
258
+ background-clip: text;
259
+ font-size: 2.5rem;
260
+ font-weight: bold;
261
+ margin-bottom: 1rem;
262
+ }
263
+
264
+ .instructions {
265
+ background: #f8f9fa;
266
+ border-radius: 10px;
267
+ padding: 20px;
268
+ border-left: 4px solid #2E8B57;
269
+ margin: 1rem 0;
270
+ }
271
+
272
+ .sequence-stats {
273
+ font-size: 0.9rem;
274
+ color: #6c757d;
275
+ font-style: italic;
276
+ margin-top: 5px;
277
+ }
278
+ """
279
+
280
+ # Create the interface
281
  print("Creating Gradio interface...")
282
 
283
+ # Determine status message and styling
284
  if model_loaded:
285
+ status_html = '''
286
+ <div class="status-ready">
287
+ <strong>✅ Model Status:</strong> Ready for gene prediction!<br>
288
+ <small>🔬 Boundary-aware deep learning model loaded successfully</small>
289
+ </div>
290
+ '''
291
  else:
292
+ status_html = f'''
293
+ <div class="status-error">
294
+ <strong>❌ Model Status:</strong> Model initialization failed<br>
295
+ <small>📋 Details: {error_message}</small>
296
+ </div>
297
+ '''
298
 
299
+ # Example sequences
300
+ examples = [
301
+ # Short example with clear gene
302
+ ["ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGACGCGTACAGGAAACACAGAAAAAAGCCCGCACCTGACAGTGCGGGCTTTTTTTTTCGACCAAAGGTAACGAGGTAACAACCATGCGAGTGTTGAAGTTCGGCGGTACATCAGTGGCAAATGCAGAACGTTTTCTGCGTAA"],
303
+ # Longer example
304
+ ["ATGAAACGCATTAGCACCACCATTACCACCACCATCACCATTACCACAGGTAACGGTGCGGGCTGACGCGTACAGGAAACACAGAAAAAAGCCCGCACCTGACAGTGCGGGCTTTTTTTTTCGACCAAAGGTAACGAGGTAACAACCATGCGAGTGTTGAAGTTCGGCGGTACATCAGTGGCAAATGCAGAACGTTTTCTGCGTGTTGCCGATATTCTGGAAAGCAATGCCAGGCAGGGGCAGGTGGCCACCGTCCTCTCTGCCCCCGCCAAAATCACCAACCACCTGGTGGCGATGATTGAAAAAACCATTAGCGGCCAGGATGCTTTACCCAATATCAGCGATGCCGAACGTATTTTTGCCGAACTTTTGACGGGACTCGCCGCCGCCCAGCCGGGGTTCCCGCTGGCGCAATTGAAAACTTTCGTCGATCAGGAATTTGCCCAATAG"],
305
+ ]
306
 
307
+ # Create the interface with custom theme
308
+ with gr.Blocks(
309
+ title="🧬 Gene Prediction Tool",
310
+ theme=gr.themes.Soft(primary_hue="emerald", secondary_hue="teal"),
311
+ css=custom_css,
312
+ head="<meta name='viewport' content='width=device-width, initial-scale=1.0'>"
313
+ ) as demo:
314
 
315
+ gr.HTML('<h1 class="main-title">🧬 Advanced Gene Prediction Tool</h1>')
316
+ gr.HTML('<p style="text-align: center; font-size: 1.1rem; color: #6c757d; margin-bottom: 2rem;">AI-powered boundary-aware gene detection system</p>')
317
 
318
+ gr.HTML(status_html)
 
 
 
 
319
 
320
+ with gr.Row():
321
+ gr.HTML('''
322
+ <div class="instructions">
323
+ <h3>🔬 How to Use:</h3>
324
+ <ol>
325
+ <li><strong>Enter DNA sequence:</strong> Paste your sequence using A, T, C, G, N characters</li>
326
+ <li><strong>Click Analyze:</strong> The AI model will predict gene regions</li>
327
+ <li><strong>Review results:</strong> View detected genes with positions, codons, and confidence</li>
328
+ </ol>
329
+
330
+ <h4>📏 Requirements:</h4>
331
+ <ul>
332
+ <li>Characters: Only A, T, C, G, N allowed</li>
333
+ <li>Length: 3 - 10,000 nucleotides</li>
334
+ <li>Format: Raw sequence (FASTA headers will be ignored)</li>
335
+ </ul>
336
+ </div>
337
+ ''')
338
 
339
  with gr.Row():
340
+ with gr.Column(scale=1):
341
  sequence_input = gr.Textbox(
342
+ label="🧬 DNA Sequence Input",
343
+ placeholder="Enter or paste your DNA sequence here...\nExample: ATGAAACGCATTAGCACC...",
344
+ lines=10,
345
+ max_lines=20,
346
+ show_copy_button=True,
347
+ container=True
348
  )
349
 
350
+ # Real-time sequence stats
351
+ sequence_stats = gr.HTML(value="", elem_classes=["sequence-stats"])
352
+
353
+ with gr.Row():
354
+ submit_btn = gr.Button("🔬 Analyze Sequence", variant="primary", size="lg", scale=2)
355
+ clear_btn = gr.Button("🗑️ Clear", variant="secondary", size="lg", scale=1)
356
+
357
+ # Example buttons
358
+ gr.Markdown("### 📝 Quick Examples:")
359
  with gr.Row():
360
+ example1_btn = gr.Button("Short Gene", variant="secondary", size="sm")
361
+ example2_btn = gr.Button("Longer Sequence", variant="secondary", size="sm")
 
362
 
363
+ with gr.Column(scale=2):
364
  output = gr.Textbox(
365
+ label="🔬 Analysis Results",
366
+ lines=25,
367
+ max_lines=35,
368
+ show_copy_button=True,
369
+ container=True,
370
+ placeholder="Results will appear here after analysis..."
371
  )
372
 
373
+ # Footer
374
+ gr.HTML('''
375
+ <div style="text-align: center; margin-top: 2rem; padding: 1rem; border-top: 1px solid #dee2e6; color: #6c757d;">
376
+ <small>🧬 Powered by boundary-aware deep learning | Built with PyTorch & Gradio</small>
377
+ </div>
378
+ ''')
379
+
380
  # Event handlers
381
+ def update_stats(sequence):
382
+ return get_sequence_stats(sequence)
383
+
384
+ # Real-time sequence stats update
385
+ sequence_input.change(
386
+ fn=update_stats,
387
+ inputs=sequence_input,
388
+ outputs=sequence_stats
389
+ )
390
+
391
+ # Main prediction
392
  submit_btn.click(
393
  fn=predict_genes,
394
  inputs=sequence_input,
395
  outputs=output
396
  )
397
 
398
+ # Clear functionality
399
  clear_btn.click(
400
+ fn=lambda: ("", "", ""),
401
+ outputs=[sequence_input, output, sequence_stats]
402
  )
403
 
404
+ # Example buttons
405
+ example1_btn.click(
406
+ fn=lambda: examples[0][0],
407
  outputs=sequence_input
408
  )
409
 
410
+ example2_btn.click(
411
+ fn=lambda: examples[1][0],
412
+ outputs=sequence_input
413
+ )
414
+
415
+ # Allow Enter key to submit
416
  sequence_input.submit(
417
  fn=predict_genes,
418
  inputs=sequence_input,
419
  outputs=output
420
  )
421
 
422
+ # Launch configuration
423
  if __name__ == "__main__":
424
+ print("🚀 Launching Gene Prediction App...")
425
+ print(f"Model loaded: {model_loaded}")
426
+ print(f"Open your browser to see the interface")
427
+
428
  demo.launch(
429
  server_name="0.0.0.0",
430
  server_port=7860,
431
  show_error=True,
432
+ show_api=False,
433
+ share=False, # Set to True if you want a public link
434
+ inbrowser=True, # Automatically open browser
435
+ prevent_thread_lock=False
436
  )