Mgolo commited on
Commit
bbd3488
·
verified ·
1 Parent(s): 738edc4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +336 -1
app.py CHANGED
@@ -307,4 +307,339 @@ class TranslationService:
307
  pipeline_obj, lang_tag = self.model_manager.get_translation_pipeline(
308
  source_lang, target_lang
309
  )
310
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  pipeline_obj, lang_tag = self.model_manager.get_translation_pipeline(
308
  source_lang, target_lang
309
  )
310
+
311
+ return self._process_text_with_pipeline(text, pipeline_obj, lang_tag)
312
+
313
+ def _chained_translate(
314
+ self,
315
+ text: str,
316
+ source_lang: Language,
317
+ target_lang: Language
318
+ ) -> str:
319
+ """
320
+ Perform chained translation through English as intermediate language.
321
+
322
+ Args:
323
+ text: Input text to translate
324
+ source_lang: Source language
325
+ target_lang: Target language
326
+
327
+ Returns:
328
+ Translated text through chaining
329
+ """
330
+ # First: source_lang -> English
331
+ intermediate_text = self._direct_translate(
332
+ text, source_lang, Language.ENGLISH
333
+ )
334
+
335
+ # Second: English -> target_lang
336
+ final_text = self._direct_translate(
337
+ intermediate_text, Language.ENGLISH, target_lang
338
+ )
339
+
340
+ return final_text
341
+
342
+ def _process_text_with_pipeline(
343
+ self,
344
+ text: str,
345
+ pipeline_obj: Any,
346
+ lang_tag: str
347
+ ) -> str:
348
+ """Process text using translation pipeline."""
349
+ # Process text in paragraphs
350
+ paragraphs = text.splitlines()
351
+ translated_paragraphs = []
352
+
353
+ with torch.no_grad():
354
+ for paragraph in paragraphs:
355
+ if not paragraph.strip():
356
+ translated_paragraphs.append("")
357
+ continue
358
+
359
+ # Split into sentences and translate
360
+ sentences = [
361
+ s.strip() for s in paragraph.split(". ")
362
+ if s.strip()
363
+ ]
364
+
365
+ # Add language tag to each sentence
366
+ formatted_sentences = [
367
+ f"{lang_tag} {sentence}"
368
+ for sentence in sentences
369
+ ]
370
+
371
+ # Perform translation
372
+ results = pipeline_obj(
373
+ formatted_sentences,
374
+ max_length=5000,
375
+ num_beams=5,
376
+ early_stopping=True,
377
+ no_repeat_ngram_size=3,
378
+ repetition_penalty=1.5,
379
+ length_penalty=1.2
380
+ )
381
+
382
+ # Process results
383
+ translated_sentences = [
384
+ result["translation_text"].capitalize()
385
+ for result in results
386
+ ]
387
+
388
+ translated_paragraphs.append(". ".join(translated_sentences))
389
+
390
+ return "\n".join(translated_paragraphs)
391
+
392
+ # ================================
393
+ # Audio Processing
394
+ # ================================
395
+
396
+ class AudioProcessor:
397
+ """Handles audio file transcription using Whisper."""
398
+
399
+ def __init__(self, model_manager: ModelManager):
400
+ self.model_manager = model_manager
401
+
402
+ def transcribe(self, audio_file_path: str) -> str:
403
+ """
404
+ Transcribe audio file to text.
405
+
406
+ Args:
407
+ audio_file_path: Path to audio file
408
+
409
+ Returns:
410
+ Transcribed text
411
+ """
412
+ model = self.model_manager.get_whisper_model()
413
+ result = model.transcribe(audio_file_path)
414
+ return result["text"]
415
+
416
+ # ================================
417
+ # Main Application
418
+ # ================================
419
+
420
+ class TranslationApp:
421
+ """Main application orchestrating all components."""
422
+
423
+ def __init__(self):
424
+ self.model_manager = ModelManager()
425
+ self.content_processor = ContentProcessor()
426
+ self.translation_service = TranslationService(self.model_manager)
427
+ self.audio_processor = AudioProcessor(self.model_manager)
428
+
429
+ def process_input(
430
+ self,
431
+ mode: InputMode,
432
+ source_lang: Language,
433
+ text_input: str,
434
+ audio_file: Optional[str],
435
+ file_obj: Optional[gr.FileData]
436
+ ) -> str:
437
+ """
438
+ Process input based on selected mode.
439
+
440
+ Args:
441
+ mode: Input mode
442
+ source_lang: Source language
443
+ text_input: Text input
444
+ audio_file: Audio file path
445
+ file_obj: Uploaded file object
446
+
447
+ Returns:
448
+ Processed text content
449
+ """
450
+ if mode == InputMode.TEXT:
451
+ return text_input
452
+
453
+ elif mode == InputMode.AUDIO:
454
+ if source_lang != Language.ENGLISH:
455
+ raise ValueError("Audio input must be in English.")
456
+ if not audio_file:
457
+ raise ValueError("No audio file provided.")
458
+ return self.audio_processor.transcribe(audio_file)
459
+
460
+ elif mode == InputMode.FILE:
461
+ if not file_obj:
462
+ raise ValueError("No file uploaded.")
463
+ return self.content_processor.extract_text_from_file(file_obj.name)
464
+
465
+ return ""
466
+
467
+ def create_interface(self) -> gr.Blocks:
468
+ """Create and return the Gradio interface."""
469
+
470
+ with gr.Blocks(
471
+ title="LocaleNLP Translation Service",
472
+ theme=gr.themes.Monochrome()
473
+ ) as interface:
474
+ # Header
475
+ gr.Markdown("""
476
+ # 🌍 LocaleNLP Translation Service
477
+ Translate between English, Wolof, Hausa, and Darija with support for text, audio, and documents.
478
+ """)
479
+
480
+ # Input controls
481
+ with gr.Row():
482
+ input_mode = gr.Radio(
483
+ choices=[mode.value for mode in InputMode],
484
+ label="Input Type",
485
+ value=InputMode.TEXT.value
486
+ )
487
+
488
+ input_lang = gr.Dropdown(
489
+ choices=[lang.value for lang in Language],
490
+ label="Input Language",
491
+ value=Language.ENGLISH.value
492
+ )
493
+
494
+ output_lang = gr.Dropdown(
495
+ choices=[lang.value for lang in Language],
496
+ label="Output Language",
497
+ value=Language.WOLOF.value
498
+ )
499
+
500
+ # Input components
501
+ input_text = gr.Textbox(
502
+ label="Enter Text",
503
+ lines=8,
504
+ visible=True,
505
+ placeholder="Type or paste your text here..."
506
+ )
507
+
508
+ audio_input = gr.Audio(
509
+ label="Upload Audio",
510
+ type="filepath",
511
+ visible=False
512
+ )
513
+
514
+ file_input = gr.File(
515
+ file_types=SUPPORTED_FILE_TYPES,
516
+ label="Upload Document",
517
+ visible=False
518
+ )
519
+
520
+ # Processing area
521
+ extracted_text = gr.Textbox(
522
+ label="Extracted / Transcribed Text",
523
+ lines=8,
524
+ interactive=False
525
+ )
526
+
527
+ translate_btn = gr.Button(
528
+ "🔄 Process & Translate",
529
+ variant="secondary"
530
+ )
531
+
532
+ output_text = gr.Textbox(
533
+ label="Translated Text",
534
+ lines=10,
535
+ interactive=False
536
+ )
537
+
538
+ # Event handlers
539
+ def update_visibility(mode: str) -> Dict[str, Any]:
540
+ """Update component visibility based on input mode."""
541
+ return {
542
+ input_text: gr.update(visible=(mode == InputMode.TEXT.value)),
543
+ audio_input: gr.update(visible=(mode == InputMode.AUDIO.value)),
544
+ file_input: gr.update(visible=(mode == InputMode.FILE.value)),
545
+ extracted_text: gr.update(value="", visible=True),
546
+ output_text: gr.update(value="")
547
+ }
548
+
549
+ def handle_process(
550
+ mode: str,
551
+ source_lang: str,
552
+ text_input: str,
553
+ audio_file: Optional[str],
554
+ file_obj: Optional[gr.FileData]
555
+ ) -> Tuple[str, str]:
556
+ """Handle initial input processing."""
557
+ try:
558
+ processed_text = self.process_input(
559
+ InputMode(mode),
560
+ Language(source_lang),
561
+ text_input,
562
+ audio_file,
563
+ file_obj
564
+ )
565
+ return processed_text, ""
566
+ except Exception as e:
567
+ logger.error(f"Processing error: {e}")
568
+ return "", f"❌ Error: {str(e)}"
569
+
570
+ def handle_translate(
571
+ extracted_text: str,
572
+ source_lang: str,
573
+ target_lang: str
574
+ ) -> str:
575
+ """Handle translation of processed text."""
576
+ if not extracted_text.strip():
577
+ return "📝 No text to translate."
578
+ try:
579
+ return self.translation_service.translate(
580
+ extracted_text,
581
+ Language(source_lang),
582
+ Language(target_lang)
583
+ )
584
+ except Exception as e:
585
+ logger.error(f"Translation error: {e}")
586
+ return f"❌ Translation error: {str(e)}"
587
+
588
+ # Connect events
589
+ input_mode.change(
590
+ fn=update_visibility,
591
+ inputs=input_mode,
592
+ outputs=[input_text, audio_input, file_input, extracted_text, output_text]
593
+ )
594
+
595
+ translate_btn.click(
596
+ fn=handle_process,
597
+ inputs=[input_mode, input_lang, input_text, audio_input, file_input],
598
+ outputs=[extracted_text, output_text]
599
+ ).then(
600
+ fn=handle_translate,
601
+ inputs=[extracted_text, input_lang, output_lang],
602
+ outputs=output_text
603
+ )
604
+
605
+ # Custom CSS for black button (applied after interface creation)
606
+ interface.load(lambda: None, None, None, _js="""
607
+ () => {
608
+ const style = document.createElement('style');
609
+ style.textContent = `
610
+ .gr-button-secondary {
611
+ background-color: #000000 !important;
612
+ border-color: #000000 !important;
613
+ color: white !important;
614
+ }
615
+ .gr-button-secondary:hover {
616
+ background-color: #333333 !important;
617
+ border-color: #333333 !important;
618
+ }
619
+ `;
620
+ document.head.appendChild(style);
621
+ }
622
+ """)
623
+
624
+ return interface
625
+
626
+ # ================================
627
+ # Application Entry Point
628
+ # ================================
629
+
630
+ def main():
631
+ """Main application entry point."""
632
+ try:
633
+ app = TranslationApp()
634
+ interface = app.create_interface()
635
+ interface.launch(
636
+ server_name="0.0.0.0",
637
+ server_port=int(os.getenv("PORT", 7860)),
638
+ share=False
639
+ )
640
+ except Exception as e:
641
+ logger.critical(f"Failed to start application: {e}")
642
+ raise
643
+
644
+ if __name__ == "__main__":
645
+ main()