Spaces:
Running
Running
| import gradio as gr | |
| import cv2 | |
| import os | |
| import modules.utilities.utils as utils | |
| from modules.binary_classification import binary_classification as binary | |
| from modules.image_classification import image_classification as image | |
| from modules.multilabel_classification import multi_classification as multi | |
| from modules.retina import predict_diabetic_retinopathy as retina_detector | |
| from modules.bpo_dispatcher import predict_bpo_ticket | |
| import modules.forecasting as forecast | |
| # --- CONFIGURAZIONE TEMA --- | |
| theme = gr.themes.Soft( | |
| primary_hue="purple", | |
| neutral_hue="slate", | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], | |
| ).set( | |
| block_title_text_weight="700", | |
| block_shadow="*shadow_drop_lg", | |
| block_label_background_fill="*primary_100", | |
| ) | |
| PATH_ROOT = "./data/gallery" | |
| PATH_RETINA = "./data/gallery/retinopaty" | |
| PATH_XRAY = "./data/gallery/xray" | |
| PATH_FORECAST = "./data/export" | |
| # Crea cartella e file demo se non esistono | |
| if not os.path.exists(PATH_FORECAST): | |
| os.makedirs(PATH_FORECAST) | |
| demo_csv_path = os.path.join(PATH_FORECAST, "plan_sample.csv") | |
| # Se il file non esiste, ORA generiamo 4 settimane di dati procedurali | |
| if not os.path.exists(demo_csv_path): | |
| csv_content = forecast.generate_mock_export() | |
| with open(demo_csv_path, "w") as f: | |
| f.write(csv_content) | |
| def forecast_logic(file): | |
| if file is None: raise gr.Error("Seleziona un file CSV") | |
| if isinstance(file, list): file = file[0] | |
| img, text = forecast.predict_workload(file) | |
| if img is None: raise gr.Error(text) | |
| return img, text | |
| def binary_classification(text): | |
| if text.strip(): return binary(text) | |
| raise gr.Error('Il testo è obbligatorio!') | |
| def multi_classification(text): | |
| if text.strip(): | |
| try: return multi(text) | |
| except Exception as e: raise gr.Error(f'Errore nel modello: {str(e)}') | |
| raise gr.Error('Il testo è obbligatorio!') | |
| def file_change(file): | |
| if isinstance(file, list): file = file[0] | |
| if file: return cv2.imread(file) | |
| return None | |
| def image_classification(img): | |
| if img is not None: return image(img) | |
| raise gr.Error('L\'immagine è obbligatoria!') | |
| def retina_classification(retina): | |
| if retina is not None: return retina_detector(retina) | |
| raise gr.Error('L\'immagine è obbligatoria!') | |
| def bpo_dispatch_logic(text): | |
| """ | |
| Funzione Ponte: Chiama il modulo AI e decide l'azione di business. | |
| Restituisce un aggiornamento COMPLETO del componente NER per pulire la grafica. | |
| """ | |
| try: | |
| intent, urgency, entities = predict_bpo_ticket(text) | |
| if intent is None: | |
| raise gr.Error("Errore nel modello BPO. Verifica i log.") | |
| top_intent = max(intent, key=intent.get) | |
| action = "Inoltro generico" | |
| if top_intent == "Retention / Churn Risk": | |
| action = "🚨 ALERT: Assegnazione coda 'Retention' + Chiamata Outbound" | |
| elif top_intent == "Supporto Tecnico": | |
| action = "🛠️ Apertura Ticket JIRA (Livello 1) - Priorità Tecnica" | |
| elif top_intent == "Amministrazione / Billing": | |
| action = "💰 Verifica insoluti su SAP + Inoltro Backoffice Amm.vo" | |
| html_output = utils.render_ner_html(entities) | |
| return intent, urgency, action, html_output | |
| except Exception as e: | |
| raise gr.Error(f"Errore nell'analisi: {str(e)}") | |
| with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as demo: | |
| # --- SIDEBAR (FILE EXPLORER) --- | |
| with gr.Sidebar(position="left", width=300, visible=False) as main_sidebar: | |
| gr.Markdown("### 📂 Risorse Demo") | |
| gr.Markdown("Seleziona i file di esempio:") | |
| sidebar_explorer = gr.FileExplorer( | |
| root_dir=PATH_ROOT, | |
| glob="**/*", | |
| ignore_glob="*.h5,*.json,*.py,*.pyc", | |
| file_count="single", | |
| label="Archivio File", | |
| height=400, | |
| interactive=True | |
| ) | |
| gr.Markdown("---") | |
| gr.Markdown("**Info Sistema:**") | |
| gr.Markdown("🟢 Server: Online") | |
| gr.Markdown("🟣 GPU: N/A (CPU Mode)") | |
| # --- MAIN CONTENT --- | |
| with gr.Column(): | |
| # --- HEADER --- | |
| with gr.Row(elem_classes="header-row"): | |
| with gr.Column(scale=0, min_width=80, elem_classes="logo-container"): | |
| gr.Image(value="data/icon.png", show_label=False, show_download_button=False, show_share_button=False, container=False, show_fullscreen_button=False, interactive=False, height=80, width=80) | |
| with gr.Column(scale=1, elem_classes="header-text-col"): | |
| gr.Markdown("""<h1>AI Platform</h1><div class='subheader'>Advanced Machine Learning Solutions</div>""") | |
| # --- BPO INTELLIGENT DISPATCHER --- | |
| with gr.Tab("🧩 BPO Dispatcher") as tab_bpo: | |
| gr.Markdown(""" | |
| # 🧩 Intelligent Ticket Routing & NER | |
| Sistema proprietario per l'analisi automatica dei ticket di assistenza. Il modello identifica l'intento, l'urgenza e i dati sensibili del cliente. | |
| """) | |
| with gr.Row(elem_classes="responsive-row"): | |
| # INPUT | |
| with gr.Column(scale=1): | |
| bpo_input = gr.Textbox(lines=8, placeholder="Incolla qui il contenuto della mail o del ticket...", label="Contenuto Ticket / Email") | |
| analyze_btn_bpo = gr.Button("⚡ Analizza Richiesta", variant="primary") | |
| gr.HTML(""" | |
| <div class='model-card'> | |
| <strong>🛠️ Model Architecture:</strong> NGT-BERT-Custom (DistilBERT)<br> | |
| <strong>📚 Training Data:</strong> Synthetic BPO Dataset (2025)<br> | |
| <strong>🎯 Tasks:</strong> Intent Classification (Multi-class), Entity Extraction (NER) | |
| </div> | |
| """) | |
| # OUTPUT | |
| with gr.Column(scale=1): | |
| with gr.Group(): | |
| gr.Markdown("#### 📋 Analisi Processata", elem_classes="h4-margin") | |
| bpo_intent_output = gr.Label(num_top_classes=3, label="Intento Rilevato") | |
| with gr.Row(): | |
| bpo_urgency_output = gr.Textbox(label="Livello Urgenza", scale=1) | |
| bpo_action_output = gr.Textbox(label="Azione Consigliata (Auto)", scale=1) | |
| gr.Markdown("#### 🔍 Dati Estratti (NER)", elem_classes="h4-margin") | |
| bpo_ner_output = gr.HTML(label="Visualizzazione Entità") | |
| gr.Examples( | |
| examples=[ | |
| ["Buongiorno, vi scrivo perché la fattura n. 99283 del mese scorso è sbagliata. Non ho consumato così tanto. Il mio codice cliente è 4599201. Attendo rettifica urgente."], | |
| ["Salve, il servizio non funziona da ieri. Mi dà errore 504 sul router. Risolvete subito per favore!"], | |
| ["Vorrei disdire il contratto con decorrenza immediata se non mi risolvete il problema."] | |
| ], | |
| inputs=bpo_input | |
| ) | |
| analyze_btn_bpo.click( | |
| bpo_dispatch_logic, | |
| inputs=bpo_input, | |
| outputs=[bpo_intent_output, bpo_urgency_output, bpo_action_output, bpo_ner_output] | |
| ) | |
| # --- AI FORECASTER --- | |
| with gr.Tab("🔮 AI Forecaster") as tab_forecast: | |
| gr.Markdown(""" | |
| # 🔮 AI Brain: Predictive Planning | |
| Modulo interattivo per la pianificazione dei turni. Confronta in tempo reale il forecast AI con i dati storici effettivi. | |
| """) | |
| with gr.Row(elem_classes="responsive-row"): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 1. Configurazione") | |
| with gr.Row(): | |
| gr.Dropdown(["Customer Care"], label="Business Unit", value="Customer Care") | |
| gr.Dropdown(["Ass. Tecnica"], label="Reparto", value="Ass. Tecnica") | |
| gr.Dropdown(["Inbound Calls"], label="Attività", value="Inbound Calls") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 2. Dati Storici") | |
| with gr.Row(): | |
| forecast_file = gr.File( | |
| label="Seleziona Export (.csv)", | |
| file_types=[".csv"], | |
| height=100, | |
| interactive=False | |
| ) | |
| with gr.Row(elem_classes="responsive-row"): | |
| forecast_btn = gr.Button("🔮 Genera\nGrafico", variant="primary", scale=1) | |
| with gr.Row(elem_classes="responsive-row"): | |
| with gr.Column(): | |
| gr.Markdown("### 📊 Dashboard Interattiva") | |
| # show_label=False rende il grafico più pulito senza il titolino grigio sopra | |
| forecast_plot = gr.Plot(show_label=False) | |
| with gr.Row(elem_classes="responsive-row"): | |
| forecast_stats = gr.Textbox(label="KPI Backtesting & Performance", lines=3) | |
| forecast_btn.click( | |
| forecast_logic, | |
| inputs=forecast_file, | |
| outputs=[forecast_plot, forecast_stats] | |
| ) | |
| # --- Sentiment Analysis (BPO) --- | |
| with gr.Tab("📢 Sentiment Analysis (BPO)") as tab_sentiment: | |
| # 1. HEADER E GUIDA UTENTE | |
| gr.Markdown(""" | |
| # 😠/😍 Analizzatore di Sentiment (Dominio Helpdesk) | |
| **ATTENZIONE:** Questo modello è **altamente specializzato** nel dominio dell'Assistenza Clienti (Telco/Energy). | |
| * ✅ **Usa questo modulo per:** Ticket di guasti, lamentele amministrative, feedback su operatori, richieste di disdetta. | |
| * ❌ **NON usare questo modulo per:** Frasi generiche ("Il cielo è blu"), recensioni di film, o linguaggio comune non tecnico. | |
| _Il modello potrebbe interpretare frasi generiche positive come negative se non contengono parole chiave del suo vocabolario specifico._ | |
| """) | |
| # 2. DETTAGLI TECNICI (Nascosti in un Accordion per pulizia) | |
| with gr.Accordion("ℹ️ Come funziona questo modello?", open=False): | |
| gr.Markdown(""" | |
| Questo sistema utilizza una rete neurale leggera addestrata su un dataset proprietario di **1.200 interazioni reali** cliente-operatore. | |
| * **Preprocessing:** Lemmatizzazione Spacy + Rimozione Stopwords (con Whitelist per le negazioni). | |
| * **Architettura:** Dense Neural Network con Dropout e L2 Regularization. | |
| * **Focus:** È calibrato per rilevare l'urgenza nascosta anche in frasi apparentemente calme. | |
| """) | |
| # 3. INTERFACCIA | |
| with gr.Row(elem_classes="responsive-row"): | |
| with gr.Column(): | |
| # Input Text | |
| sentiment_input = gr.Textbox( | |
| label="Inserisci il testo del ticket o della mail", | |
| placeholder="Es: Non funziona internet e nessuno mi risponde...", | |
| lines=3 | |
| ) | |
| # ESEMPI CLICCABILI (Fondamentali per guidare l'utente!) | |
| gr.Examples( | |
| examples=[ | |
| ["L'assistenza ricevuta è stata pessima, sono deluso."], | |
| ["Il router funziona benissimo, grazie per la velocità."], | |
| ["Non ho ancora ricevuto la fattura di gennaio."], | |
| ["Sono due giorni che ho la linea ferma, è inaccettabile!"], | |
| ["L'operatore Marco è stato gentilissimo e ha risolto tutto."] | |
| ], | |
| inputs=sentiment_input, | |
| label="Prova questi esempi BPO:" | |
| ) | |
| sentiment_btn = gr.Button("Analizza Sentiment", variant="primary") | |
| with gr.Column(): | |
| # Output Label (Percentuali) | |
| sentiment_output = gr.Label(num_top_classes=2, label="Risultato Analisi") | |
| # 4. COLLEGAMENTO FUNZIONE | |
| sentiment_btn.click( | |
| fn=binary, | |
| inputs=sentiment_input, | |
| outputs=sentiment_output | |
| ) | |
| # --- News Classification (AI Editor) --- | |
| with gr.Tab("📰 Smart Content Tagger") as tab_news: | |
| # 1. HEADER & CONTESTO | |
| gr.Markdown(""" | |
| # 🏷️ Classificazione Editoriale Automatica | |
| Questo modulo simula un **assistente editoriale AI**. Analizza il testo di un articolo o di un lancio di agenzia e suggerisce la categoria tematica corretta per l'archiviazione. | |
| * **Categorie Supportate:** `Economia`, `Politica`, `Scienza & Tecnica`, `Sport`, `Storia`. | |
| * **Tecnologia:** Deep Learning su sequenze di testo (Embedding layer). | |
| """) | |
| with gr.Row(elem_classes="responsive-row"): | |
| # COLONNA INPUT (L'Articolo) | |
| with gr.Column(scale=3): | |
| multi_input = gr.Textbox( | |
| label="Testo dell'articolo", | |
| placeholder="Incolla qui il titolo o il corpo del testo (es. news ANSA, Reuters...)", | |
| lines=6 | |
| ) | |
| # Esempi calibrati sulle tue 5 classi | |
| gr.Examples( | |
| examples=[ | |
| ["L'inflazione nell'area euro scende al 2.4%, la BCE valuta il taglio dei tassi di interesse."], # Economia | |
| ["Il Parlamento ha approvato il nuovo decreto legge con 200 voti favorevoli. Il Premier esprime soddisfazione."], # Politica | |
| ["La sonda spaziale ha inviato nuove immagini ad alta risoluzione della superficie di Marte, rivelando tracce di antichi fiumi."], # Scienza | |
| ["Finale incredibile allo stadio: la squadra di casa ribalta il risultato al 90° minuto e vola in testa alla classifica."], # Sport | |
| ["Durante gli scavi a Pompei sono emersi nuovi affreschi risalenti al I secolo d.C. perfettamente conservati."] # Storia | |
| ], | |
| inputs=multi_input, | |
| label="Prova questi lanci d'agenzia:" | |
| ) | |
| analyze_btn_multi = gr.Button("🏷️ Classifica Articolo", variant="primary") | |
| # COLONNA OUTPUT (I Tag) | |
| with gr.Column(scale=2): | |
| with gr.Group(): | |
| gr.Markdown("### 📊 Categorie Rilevate", elem_classes="h4-margin") | |
| # Usiamo un Label con top_classes=5 per vedere la distribuzione completa | |
| multi_output = gr.Label(num_top_classes=5, label="Confidenza del Modello") | |
| analyze_btn_multi.click(multi, inputs=multi_input, outputs=multi_output) | |
| # --- Chest X-Ray Diagnostics --- | |
| with gr.Tab("🩻 Chest X-Ray Diagnostics") as tab_xray: | |
| # 1. DISCLAIMER MEDICO (Fondamentale) | |
| gr.Markdown(""" | |
| # 🩻 Analisi Radiografica Toracica (Supporto Decisionale) | |
| **DISCLAIMER:** Questo modulo è un prototipo di ricerca AI. **NON sostituisce il parere di un medico.** | |
| Il sistema è addestrato per identificare pattern visivi associati a: | |
| * **Polmonite** (Pneumonia) | |
| * **Tubercolosi** (Tuberculosis) | |
| *Utilizzare gli esempi di radiografiche frontali (Chest X-Ray) recuperabili nella sidebar laterale.* | |
| """) | |
| with gr.Row(elem_classes="responsive-row"): | |
| # COLONNA INPUT | |
| with gr.Column(scale=1): | |
| image_input = gr.Image(type="numpy", label="Seleziona Radiografia", height=400, interactive=False) | |
| analyze_btn_img = gr.Button("🏥 Avvia Diagnosi AI", variant="primary") | |
| # COLONNA OUTPUT | |
| with gr.Column(scale=1): | |
| with gr.Group(): | |
| gr.Markdown("#### 📋 Referto AI", elem_classes="h4-margin") | |
| output_label = gr.Label(num_top_classes=4, label="Probabilità Patologia") | |
| analyze_btn_img.click(image, inputs=image_input, outputs=output_label) | |
| # --- Diabetic Retinopathy --- | |
| with gr.Tab("👁️ Diabetic Retinopathy") as tab_retina: | |
| gr.Markdown(""" | |
| # 👁️ Screening Retinopatia Diabetica | |
| **DISCLAIMER:** Questo modulo è un prototipo di ricerca AI. **NON sostituisce il parere di un medico.** Sistema di supporto decisionale. Analizza scansioni del fondo oculare. | |
| *Utilizzare gli esempi di retinografie digitali recuperabili nella sidebar laterale.* | |
| """) | |
| with gr.Row(elem_classes="responsive-row"): | |
| # COLONNA INPUT | |
| with gr.Column(scale=1): | |
| image_input_dr = gr.Image( | |
| type="numpy", | |
| label="Seleziona Scansione Retinica", | |
| height=400, | |
| sources=["upload", "clipboard"], | |
| interactive=False | |
| ) | |
| analyze_btn_dr = gr.Button("🏥 Analisi Fondo Oculare", variant="primary") | |
| # COLONNA OUTPUT | |
| with gr.Column(scale=1): | |
| with gr.Group(): | |
| gr.Markdown("### 📋 Esito Screening", elem_classes="h4-margin") | |
| # Output 1: La Diagnosi (Testo) | |
| output_dr_diagnosis = gr.Label(label="Diagnosi AI") | |
| # Output 2: La Percentuale (Testo/Numero) | |
| output_dr_prob = gr.Label(label="Livello di Confidenza (Rischio)") | |
| analyze_btn_dr.click( | |
| retina_detector, | |
| inputs=image_input_dr, | |
| outputs=[output_dr_diagnosis, output_dr_prob] | |
| ) | |
| ui_outputs = [main_sidebar, sidebar_explorer, image_input, image_input_dr] | |
| # 1. Click-to-Load (Rimane invariato) | |
| sidebar_explorer.change( | |
| fn=utils.global_file_loader, | |
| inputs=sidebar_explorer, | |
| outputs=[bpo_input, forecast_file, image_input, image_input_dr, multi_input, sentiment_input] | |
| ) | |
| # 2. ESET TOTALE | |
| # Questa funzione restituisce valori "vuoti" per tutti i campi sensibili | |
| def reset_all_fields(): | |
| return ( | |
| None, # 1. bpo_input | |
| None, # 2. bpo_intent | |
| None, # 3. bpo_urgency | |
| None, # 4. bpo_action | |
| None, # 5. bpo_ner | |
| None, # 6. forecast_file | |
| None, # 7. forecast_plot | |
| None, # 8. forecast_stats | |
| None, # 9. image_input | |
| None, # 10. output_label | |
| None, # 11. image_input_dr | |
| None, # 12. output_dr_diag | |
| None, # 13. output_dr_prob | |
| None, # 14. multi_input | |
| None, # 15. multi_output | |
| None, # 16. sentiment_input | |
| None # 17. sentiment_output | |
| ) | |
| reset_outputs = [ | |
| bpo_input, bpo_intent_output, bpo_urgency_output, bpo_action_output, bpo_ner_output, | |
| forecast_file, forecast_plot, forecast_stats, | |
| image_input, output_label, | |
| image_input_dr, output_dr_diagnosis, output_dr_prob, | |
| multi_input, multi_output, | |
| sentiment_input, sentiment_output | |
| ] | |
| # 3. GESTIONE CAMBIO TAB (RESET + SIDEBAR) | |
| # Per i Tab NLP: Disabilita Sidebar + Resetta TUTTO | |
| tab_bpo.select( | |
| fn=utils.disable_sidebar, | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, outputs=reset_outputs | |
| ) | |
| tab_forecast.select( | |
| fn=lambda: utils.enable_sidebar(PATH_FORECAST), | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, | |
| outputs=reset_outputs | |
| ) | |
| tab_news.select( | |
| fn=utils.disable_sidebar, | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, outputs=reset_outputs | |
| ) | |
| tab_sentiment.select( | |
| fn=utils.disable_sidebar, | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, outputs=reset_outputs | |
| ) | |
| # Per i Tab VISION: Abilita Sidebar specifica + Resetta TUTTO | |
| tab_xray.select( | |
| fn=lambda: utils.enable_sidebar(PATH_XRAY), | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, outputs=reset_outputs | |
| ) | |
| tab_retina.select( | |
| fn=lambda: utils.enable_sidebar(PATH_RETINA), | |
| outputs=ui_outputs | |
| ).then( | |
| fn=reset_all_fields, outputs=reset_outputs | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| allowed_paths=["data"] | |
| ) |