File size: 5,393 Bytes
3d9ba5b
ca8888d
b4255e9
3d9ba5b
 
 
 
 
ca8888d
3d9ba5b
 
 
 
 
b4255e9
 
 
 
 
 
 
 
 
 
 
ca8888d
b4255e9
3d9ba5b
ca8888d
3d9ba5b
 
 
 
 
 
 
ca8888d
3d9ba5b
 
 
 
 
 
 
 
 
b4255e9
 
 
 
 
 
 
3d9ba5b
b4255e9
 
 
 
 
 
 
 
 
 
 
 
3d9ba5b
 
ca8888d
3d9ba5b
b4255e9
3d9ba5b
b4255e9
 
 
 
 
 
 
ca8888d
b4255e9
ca8888d
79d7dca
b4255e9
 
 
 
 
 
ca8888d
b4255e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79d7dca
b4255e9
 
 
 
 
 
79d7dca
b4255e9
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
Bilingual Arabic-English Text Summarization Suite
Production Hugging Face Space (Gradio 5.x + API Endpoint).
"""

import os
import sys
import time
from typing import Optional

PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
if PROJECT_ROOT not in sys.path:
    sys.path.insert(0, PROJECT_ROOT)

# ZeroGPU decorator support
try:
    import spaces
    has_spaces = True
except ImportError:
    has_spaces = False

def gpu_decorator(func):
    if has_spaces and hasattr(spaces, "GPU"):
        return spaces.GPU(func)
    return func

import gradio as gr
from nlp_core.language_detector import LanguageDetector
from nlp_core.tokenizer import BilingualTokenizer
from models.extractive.textrank import TextRankSummarizer
from models.extractive.lsa import LSASummarizer
from models.extractive.hybrid_scorer import HybridSummarizer
from models.abstractive.seq2seq_model import Seq2SeqSummarizer
from evaluation.metrics_manager import MetricsManager

lang_detector = LanguageDetector()
tokenizer = BilingualTokenizer()
metrics_mgr = MetricsManager()

CKPT_AR = os.path.join(PROJECT_ROOT, "checkpoints", "seq2seq_arabic.pt")
CKPT_EN = os.path.join(PROJECT_ROOT, "checkpoints", "seq2seq_english.pt")

seq_ar = Seq2SeqSummarizer.load_checkpoint(CKPT_AR, device="cpu") if os.path.exists(CKPT_AR) else None
seq_en = Seq2SeqSummarizer.load_checkpoint(CKPT_EN, device="cpu") if os.path.exists(CKPT_EN) else None


@gpu_decorator
def summarize_text(text, algorithm="Seq2Seq with Bahdanau Attention (Abstractive 20-Ep)", sentences=3, beam_width=3):
    """Core summarization engine exposed to both Gradio UI and REST API."""
    if not text or not str(text).strip():
        return "Please enter text to summarize.", "N/A", "0%", "0 ms"
    
    text = str(text).strip()
    t0 = time.time()
    detected_lang = lang_detector.detect_language(text)
    
    if "lsa" in str(algorithm).lower():
        res = LSASummarizer().summarize(text, num_sentences=int(sentences), lang=detected_lang)
        summary = res["summary"]
    elif "hybrid" in str(algorithm).lower():
        res = HybridSummarizer().summarize(text, num_sentences=int(sentences), lang=detected_lang)
        summary = res["summary"]
    elif "textrank" in str(algorithm).lower():
        res = TextRankSummarizer().summarize(text, num_sentences=int(sentences), lang=detected_lang)
        summary = res["summary"]
    else:  # Seq2Seq Abstractive
        model = seq_ar if detected_lang == "ar" else seq_en
        if model:
            toks = tokenizer.tokenize_words(text, lang=detected_lang)
            gen_toks = model.summarize_beam(toks, beam_width=int(beam_width), max_len=60)
            summary = " ".join(gen_toks)
        else:
            res = HybridSummarizer().summarize(text, num_sentences=int(sentences), lang=detected_lang)
            summary = res["summary"] + " (Used Hybrid fallback)"
            
    latency = f"{(time.time() - t0)*1000.0:.1f} ms"
    orig_w = len(text.split())
    sum_w = len(summary.split())
    reduction = f"{(1 - (sum_w/orig_w))*100:.1f}%" if orig_w > 0 else "0%"
    
    return summary, detected_lang.upper(), reduction, latency


# Build Gradio UI with named API endpoints
with gr.Blocks(title="Bilingual Arabic-English Text Summarization AI", theme=gr.themes.Soft()) as demo:
    gr.Markdown("# 📖 Bilingual Arabic-English Text Summarization AI System")
    gr.Markdown("""
    **Production NLP Platform** supporting Extractive (TextRank, LSA, Hybrid) and Abstractive (Seq2Seq + Bahdanau Attention) summarization.
    """)
    
    with gr.Row():
        with gr.Column():
            input_text = gr.Textbox(
                label="Source Document Text | النص الأصلي",
                placeholder="Paste Arabic or English article text here...",
                lines=10
            )
            algorithm = gr.Dropdown(
                label="Summarization Algorithm | خوارزمية التلخيص",
                choices=[
                    "Seq2Seq with Bahdanau Attention (Abstractive 20-Ep)",
                    "Hybrid Scorer (Extractive)",
                    "TextRank (Extractive)",
                    "LSA (Extractive)"
                ],
                value="Seq2Seq with Bahdanau Attention (Abstractive 20-Ep)"
            )
            with gr.Row():
                sentences = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Sentences (Extractive)")
                beam_width = gr.Slider(minimum=1, maximum=6, value=3, step=1, label="Beam Width (Seq2Seq)")
            
            btn = gr.Button("✨ Generate Summary | توليد الملخص", variant="primary")
            
        with gr.Column():
            output_summary = gr.Textbox(label="Generated Summary | الملخص الناتج", lines=8)
            with gr.Row():
                out_lang = gr.Textbox(label="Detected Language", lines=1)
                out_reduc = gr.Textbox(label="Word Reduction", lines=1)
                out_lat = gr.Textbox(label="Latency (ms)", lines=1)
                
    btn.click(
        fn=summarize_text,
        inputs=[input_text, algorithm, sentences, beam_width],
        outputs=[output_summary, out_lang, out_reduc, out_lat],
        api_name="summarize"
    )

# Launch Gradio app directly (required for Hugging Face Space supervisor)
demo.queue()
demo.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False)