Spaces:
Sleeping
Sleeping
Commit ·
96bc481
1
Parent(s): a4a884a
migliorata visualizzazione BPO Dispatcher
Browse files- .gitignore +1 -0
- app.py +40 -28
- data/logs/access_logs.csv +2 -0
- modules/bpo_dispatcher.py +21 -17
- modules/image_classification.py +3 -3
- modules/multilabel_classification.py +6 -5
- modules/retina.py +3 -3
- modules/utilities/logger.py +21 -8
- modules/utilities/utils.py +2 -2
- style.css +18 -1
.gitignore
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
__pycache__/
|
|
|
|
|
|
| 1 |
__pycache__/
|
| 2 |
+
.venv
|
app.py
CHANGED
|
@@ -128,7 +128,7 @@ def bpo_dispatch_logic(text, request: gr.Request):
|
|
| 128 |
start_time = time.time()
|
| 129 |
|
| 130 |
try:
|
| 131 |
-
intent, urgency, entities = predict_bpo_ticket(text)
|
| 132 |
|
| 133 |
if intent is None:
|
| 134 |
raise gr.Error("Errore nel modello BPO. Verifica i log.")
|
|
@@ -145,6 +145,11 @@ def bpo_dispatch_logic(text, request: gr.Request):
|
|
| 145 |
|
| 146 |
html_output = utils.render_ner_html(entities)
|
| 147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
elapsed_time = time.time() - start_time
|
| 149 |
logger.log_interaction(
|
| 150 |
request=request,
|
|
@@ -154,7 +159,7 @@ def bpo_dispatch_logic(text, request: gr.Request):
|
|
| 154 |
execution_time=elapsed_time
|
| 155 |
)
|
| 156 |
|
| 157 |
-
return intent, urgency, action, html_output
|
| 158 |
|
| 159 |
except Exception as e:
|
| 160 |
raise gr.Error(f"Errore nell'analisi: {str(e)}")
|
|
@@ -190,8 +195,6 @@ with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as d
|
|
| 190 |
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)
|
| 191 |
with gr.Column(scale=1, elem_classes="header-text-col"):
|
| 192 |
gr.Markdown("""<h1>AI Platform</h1><div class='subheader'>Advanced Machine Learning Solutions</div>""")
|
| 193 |
-
|
| 194 |
-
# --- BPO INTELLIGENT DISPATCHER ---
|
| 195 |
with gr.Tab("🧩 BPO Dispatcher") as tab_bpo:
|
| 196 |
gr.Markdown("""
|
| 197 |
# 🧩 Intelligent Ticket Routing & NER
|
|
@@ -205,24 +208,32 @@ with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as d
|
|
| 205 |
analyze_btn_bpo = gr.Button("⚡ Analizza Richiesta", variant="primary")
|
| 206 |
gr.HTML("""
|
| 207 |
<div class='model-card'>
|
| 208 |
-
<strong>🛠️ Model Architecture:</strong> NGT-BERT-Custom (DistilBERT)<br>
|
| 209 |
-
<strong>📚 Training Data:</strong> Synthetic BPO Dataset
|
| 210 |
-
<strong>🎯 Tasks:</strong> Intent Classification
|
| 211 |
</div>
|
| 212 |
""")
|
| 213 |
-
|
| 214 |
# OUTPUT
|
| 215 |
with gr.Column(scale=1):
|
| 216 |
with gr.Group():
|
| 217 |
gr.Markdown("#### 📋 Analisi Processata", elem_classes="h4-margin")
|
| 218 |
-
|
|
|
|
|
|
|
| 219 |
with gr.Row():
|
| 220 |
bpo_urgency_output = gr.Textbox(label="Livello Urgenza", scale=1)
|
| 221 |
bpo_action_output = gr.Textbox(label="Azione Consigliata (Auto)", scale=1)
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
gr.Markdown("#### 🔍 Dati Estratti (NER)", elem_classes="h4-margin")
|
| 224 |
bpo_ner_output = gr.HTML(label="Visualizzazione Entità")
|
| 225 |
-
|
| 226 |
gr.Examples(
|
| 227 |
examples=[
|
| 228 |
["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."],
|
|
@@ -235,7 +246,7 @@ with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as d
|
|
| 235 |
analyze_btn_bpo.click(
|
| 236 |
bpo_dispatch_logic,
|
| 237 |
inputs=bpo_input,
|
| 238 |
-
outputs=[bpo_intent_output, bpo_urgency_output, bpo_action_output, bpo_ner_output]
|
| 239 |
)
|
| 240 |
|
| 241 |
# --- AI FORECASTER ---
|
|
@@ -468,25 +479,26 @@ with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as d
|
|
| 468 |
return (
|
| 469 |
None, # 1. bpo_input
|
| 470 |
None, # 2. bpo_intent
|
| 471 |
-
None, # 3.
|
| 472 |
-
None, # 4.
|
| 473 |
-
None, # 5.
|
| 474 |
-
None, # 6.
|
| 475 |
-
None, # 7.
|
| 476 |
-
None, # 8.
|
| 477 |
-
None, # 9.
|
| 478 |
-
None, # 10.
|
| 479 |
-
None, # 11.
|
| 480 |
-
None, # 12.
|
| 481 |
-
None, # 13.
|
| 482 |
-
None, # 14.
|
| 483 |
-
None, # 15.
|
| 484 |
-
None, # 16.
|
| 485 |
-
None
|
|
|
|
| 486 |
)
|
| 487 |
|
| 488 |
reset_outputs = [
|
| 489 |
-
bpo_input, bpo_intent_output, bpo_urgency_output, bpo_action_output, bpo_ner_output,
|
| 490 |
forecast_file, forecast_plot, forecast_stats,
|
| 491 |
image_input, output_label,
|
| 492 |
image_input_dr, output_dr_diagnosis, output_dr_prob,
|
|
@@ -545,6 +557,6 @@ with gr.Blocks(title="NGT AI Platform", theme=theme, css_paths="style.css") as d
|
|
| 545 |
if __name__ == "__main__":
|
| 546 |
demo.launch(
|
| 547 |
server_name="0.0.0.0",
|
| 548 |
-
server_port=
|
| 549 |
allowed_paths=["data"]
|
| 550 |
)
|
|
|
|
| 128 |
start_time = time.time()
|
| 129 |
|
| 130 |
try:
|
| 131 |
+
intent, urgency, entities, sentiment = predict_bpo_ticket(text)
|
| 132 |
|
| 133 |
if intent is None:
|
| 134 |
raise gr.Error("Errore nel modello BPO. Verifica i log.")
|
|
|
|
| 145 |
|
| 146 |
html_output = utils.render_ner_html(entities)
|
| 147 |
|
| 148 |
+
sentiment_formatted = {
|
| 149 |
+
"Positivo": sentiment.get("positive", 0.0),
|
| 150 |
+
"Negativo": sentiment.get("negative", 0.0)
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
elapsed_time = time.time() - start_time
|
| 154 |
logger.log_interaction(
|
| 155 |
request=request,
|
|
|
|
| 159 |
execution_time=elapsed_time
|
| 160 |
)
|
| 161 |
|
| 162 |
+
return intent, sentiment_formatted, urgency, action, html_output
|
| 163 |
|
| 164 |
except Exception as e:
|
| 165 |
raise gr.Error(f"Errore nell'analisi: {str(e)}")
|
|
|
|
| 195 |
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)
|
| 196 |
with gr.Column(scale=1, elem_classes="header-text-col"):
|
| 197 |
gr.Markdown("""<h1>AI Platform</h1><div class='subheader'>Advanced Machine Learning Solutions</div>""")
|
|
|
|
|
|
|
| 198 |
with gr.Tab("🧩 BPO Dispatcher") as tab_bpo:
|
| 199 |
gr.Markdown("""
|
| 200 |
# 🧩 Intelligent Ticket Routing & NER
|
|
|
|
| 208 |
analyze_btn_bpo = gr.Button("⚡ Analizza Richiesta", variant="primary")
|
| 209 |
gr.HTML("""
|
| 210 |
<div class='model-card'>
|
| 211 |
+
<strong>🛠️ Model Architecture:</strong> NGT-BERT-Custom (DistilBERT) + NGT Sentiment Classifier<br>
|
| 212 |
+
<strong>📚 Training Data:</strong> Synthetic BPO Dataset & Helpdesk Sentiment Dataset<br>
|
| 213 |
+
<strong>🎯 Tasks:</strong> Intent Classification, Entity Extraction, & Sentiment-Driven Urgency Routing
|
| 214 |
</div>
|
| 215 |
""")
|
| 216 |
+
|
| 217 |
# OUTPUT
|
| 218 |
with gr.Column(scale=1):
|
| 219 |
with gr.Group():
|
| 220 |
gr.Markdown("#### 📋 Analisi Processata", elem_classes="h4-margin")
|
| 221 |
+
with gr.Row():
|
| 222 |
+
bpo_intent_output = gr.Label(num_top_classes=3, label="Intento Rilevato", scale=1)
|
| 223 |
+
bpo_sentiment_output = gr.Label(num_top_classes=2, label="Sentiment Rilevato (Sentiment Analysis)", scale=1)
|
| 224 |
with gr.Row():
|
| 225 |
bpo_urgency_output = gr.Textbox(label="Livello Urgenza", scale=1)
|
| 226 |
bpo_action_output = gr.Textbox(label="Azione Consigliata (Auto)", scale=1)
|
| 227 |
|
| 228 |
+
gr.HTML("""
|
| 229 |
+
<div class='sentiment-info-badge'>
|
| 230 |
+
💡 <strong>Integrazione Sentiment Analysis:</strong> L'urgenza e l'assegnazione automatica del ticket sono calibrate in tempo reale integrando il modello di <em>Sentiment Analysis</em> per rilevare lo stato emotivo del cliente (es. minacce legali o toni irritati).
|
| 231 |
+
</div>
|
| 232 |
+
""")
|
| 233 |
+
|
| 234 |
gr.Markdown("#### 🔍 Dati Estratti (NER)", elem_classes="h4-margin")
|
| 235 |
bpo_ner_output = gr.HTML(label="Visualizzazione Entità")
|
| 236 |
+
|
| 237 |
gr.Examples(
|
| 238 |
examples=[
|
| 239 |
["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."],
|
|
|
|
| 246 |
analyze_btn_bpo.click(
|
| 247 |
bpo_dispatch_logic,
|
| 248 |
inputs=bpo_input,
|
| 249 |
+
outputs=[bpo_intent_output, bpo_sentiment_output, bpo_urgency_output, bpo_action_output, bpo_ner_output]
|
| 250 |
)
|
| 251 |
|
| 252 |
# --- AI FORECASTER ---
|
|
|
|
| 479 |
return (
|
| 480 |
None, # 1. bpo_input
|
| 481 |
None, # 2. bpo_intent
|
| 482 |
+
None, # 3. bpo_sentiment
|
| 483 |
+
None, # 4. bpo_urgency
|
| 484 |
+
None, # 5. bpo_action
|
| 485 |
+
None, # 6. bpo_ner
|
| 486 |
+
None, # 7. forecast_file
|
| 487 |
+
None, # 8. forecast_plot
|
| 488 |
+
None, # 9. forecast_stats
|
| 489 |
+
None, # 10. image_input
|
| 490 |
+
None, # 11. output_label
|
| 491 |
+
None, # 12. image_input_dr
|
| 492 |
+
None, # 13. output_dr_diag
|
| 493 |
+
None, # 14. output_dr_prob
|
| 494 |
+
None, # 15. multi_input
|
| 495 |
+
None, # 16. multi_output
|
| 496 |
+
None, # 17. sentiment_input
|
| 497 |
+
None # 18. sentiment_output
|
| 498 |
)
|
| 499 |
|
| 500 |
reset_outputs = [
|
| 501 |
+
bpo_input, bpo_intent_output, bpo_sentiment_output, bpo_urgency_output, bpo_action_output, bpo_ner_output,
|
| 502 |
forecast_file, forecast_plot, forecast_stats,
|
| 503 |
image_input, output_label,
|
| 504 |
image_input_dr, output_dr_diagnosis, output_dr_prob,
|
|
|
|
| 557 |
if __name__ == "__main__":
|
| 558 |
demo.launch(
|
| 559 |
server_name="0.0.0.0",
|
| 560 |
+
server_port=7862,
|
| 561 |
allowed_paths=["data"]
|
| 562 |
)
|
data/logs/access_logs.csv
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
timestamp,session_id,module,action,ip_address,user_agent,language,input_size,input_text,processing_time
|
| 2 |
+
2026-06-05T12:50:51.155066+02:00,59c1bbc1,BPO Dispatcher,Prediction,127.0.0.1,"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",it-IT,166 chars,"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.",2.6220s
|
modules/bpo_dispatcher.py
CHANGED
|
@@ -121,7 +121,7 @@ class BPODispatcher:
|
|
| 121 |
add_entity(ent.text, "AZIENDA", ent.start_char, ent.end_char)
|
| 122 |
return entities
|
| 123 |
|
| 124 |
-
def _calculate_smart_urgency(self, text, intent_label):
|
| 125 |
"""
|
| 126 |
MATRICE DI URGENZA (Intent + Sentiment)
|
| 127 |
Combina la gravità del problema con lo stato d'animo del cliente.
|
|
@@ -129,18 +129,6 @@ class BPODispatcher:
|
|
| 129 |
urgency = "Bassa"
|
| 130 |
text_lower = text.lower()
|
| 131 |
|
| 132 |
-
# Analisi Sentiment
|
| 133 |
-
sentiment_score_neg = 0.0
|
| 134 |
-
sentiment_score_pos = 0.0
|
| 135 |
-
|
| 136 |
-
if binary_classification:
|
| 137 |
-
try:
|
| 138 |
-
sent_result = binary_classification(text)
|
| 139 |
-
sentiment_score_neg = float(sent_result.get('negative', 0.0))
|
| 140 |
-
sentiment_score_pos = float(sent_result.get('positive', 0.0))
|
| 141 |
-
except Exception:
|
| 142 |
-
sentiment_score_neg = 0.5 # Fallback neutro
|
| 143 |
-
|
| 144 |
# CASO CHURN (Disdetta) -> Sempre Critico
|
| 145 |
# Indipendentemente dal tono, se uno vuole andare via è priorità assoluta.
|
| 146 |
if intent_label == "Retention / Churn Risk":
|
|
@@ -187,8 +175,8 @@ class BPODispatcher:
|
|
| 187 |
return urgency
|
| 188 |
|
| 189 |
def predict(self, text):
|
| 190 |
-
if self.model is None: return None, "Errore", []
|
| 191 |
-
if not text.strip(): return None, "Vuoto", []
|
| 192 |
|
| 193 |
# Intent Classification (BERT)
|
| 194 |
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=128, padding=True)
|
|
@@ -202,13 +190,29 @@ class BPODispatcher:
|
|
| 202 |
top_idx = torch.max(probs, dim=-1)[1].item()
|
| 203 |
predicted_label = LABELS_MAP[top_idx]
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
# Urgenza (AI + Sentiment + Rules)
|
| 206 |
-
urgency = self._calculate_smart_urgency(text, predicted_label)
|
| 207 |
|
| 208 |
# NER Extraction
|
| 209 |
entities = self._extract_smart_entities(text)
|
| 210 |
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
|
| 213 |
dispatcher = BPODispatcher()
|
| 214 |
def predict_bpo_ticket(text): return dispatcher.predict(text)
|
|
|
|
| 121 |
add_entity(ent.text, "AZIENDA", ent.start_char, ent.end_char)
|
| 122 |
return entities
|
| 123 |
|
| 124 |
+
def _calculate_smart_urgency(self, text, intent_label, sentiment_score_neg, sentiment_score_pos):
|
| 125 |
"""
|
| 126 |
MATRICE DI URGENZA (Intent + Sentiment)
|
| 127 |
Combina la gravità del problema con lo stato d'animo del cliente.
|
|
|
|
| 129 |
urgency = "Bassa"
|
| 130 |
text_lower = text.lower()
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
# CASO CHURN (Disdetta) -> Sempre Critico
|
| 133 |
# Indipendentemente dal tono, se uno vuole andare via è priorità assoluta.
|
| 134 |
if intent_label == "Retention / Churn Risk":
|
|
|
|
| 175 |
return urgency
|
| 176 |
|
| 177 |
def predict(self, text):
|
| 178 |
+
if self.model is None: return None, "Errore", [], {}
|
| 179 |
+
if not text.strip(): return None, "Vuoto", [], {}
|
| 180 |
|
| 181 |
# Intent Classification (BERT)
|
| 182 |
inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=128, padding=True)
|
|
|
|
| 190 |
top_idx = torch.max(probs, dim=-1)[1].item()
|
| 191 |
predicted_label = LABELS_MAP[top_idx]
|
| 192 |
|
| 193 |
+
# Analisi Sentiment
|
| 194 |
+
sentiment_score_neg = 0.5
|
| 195 |
+
sentiment_score_pos = 0.5
|
| 196 |
+
if binary_classification:
|
| 197 |
+
try:
|
| 198 |
+
sent_result = binary_classification(text)
|
| 199 |
+
sentiment_score_neg = float(sent_result.get('negative', 0.5))
|
| 200 |
+
sentiment_score_pos = float(sent_result.get('positive', 0.5))
|
| 201 |
+
except Exception:
|
| 202 |
+
pass
|
| 203 |
+
|
| 204 |
# Urgenza (AI + Sentiment + Rules)
|
| 205 |
+
urgency = self._calculate_smart_urgency(text, predicted_label, sentiment_score_neg, sentiment_score_pos)
|
| 206 |
|
| 207 |
# NER Extraction
|
| 208 |
entities = self._extract_smart_entities(text)
|
| 209 |
|
| 210 |
+
sentiment_output = {
|
| 211 |
+
"negative": sentiment_score_neg,
|
| 212 |
+
"positive": sentiment_score_pos
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
return label_output, urgency, entities, sentiment_output
|
| 216 |
|
| 217 |
dispatcher = BPODispatcher()
|
| 218 |
def predict_bpo_ticket(text): return dispatcher.predict(text)
|
modules/image_classification.py
CHANGED
|
@@ -30,13 +30,13 @@ def load_resources():
|
|
| 30 |
except Exception as e:
|
| 31 |
print(f"❌ Errore caricamento modello X-Ray: {e}")
|
| 32 |
|
| 33 |
-
# Inizializziamo subito
|
| 34 |
-
load_resources()
|
| 35 |
-
|
| 36 |
def image_classification(image_array):
|
| 37 |
"""
|
| 38 |
Analizza un'immagine radiografica (numpy array) e restituisce le probabilità.
|
| 39 |
"""
|
|
|
|
|
|
|
|
|
|
| 40 |
if model is None:
|
| 41 |
return {"Errore": "Modello non disponibile (Verifica il percorso file)"}
|
| 42 |
if image_array is None:
|
|
|
|
| 30 |
except Exception as e:
|
| 31 |
print(f"❌ Errore caricamento modello X-Ray: {e}")
|
| 32 |
|
|
|
|
|
|
|
|
|
|
| 33 |
def image_classification(image_array):
|
| 34 |
"""
|
| 35 |
Analizza un'immagine radiografica (numpy array) e restituisce le probabilità.
|
| 36 |
"""
|
| 37 |
+
global model
|
| 38 |
+
if model is None:
|
| 39 |
+
load_resources()
|
| 40 |
if model is None:
|
| 41 |
return {"Errore": "Modello non disponibile (Verifica il percorso file)"}
|
| 42 |
if image_array is None:
|
modules/multilabel_classification.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
| 2 |
import json
|
| 3 |
from keras.models import load_model
|
| 4 |
from keras_preprocessing.sequence import pad_sequences
|
| 5 |
-
from
|
| 6 |
|
| 7 |
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Risale alla root
|
| 8 |
MODEL_PATH = os.path.join(BASE_DIR, 'data', 'model', 'multi-classification.h5')
|
|
@@ -12,9 +12,10 @@ CLASS_NAMES = ['Economia', 'Politica', 'Scienza_e_tecnica', 'Sport', 'Storia']
|
|
| 12 |
|
| 13 |
# Caricamento Singleton (lo carichiamo una volta sola)
|
| 14 |
model = None
|
|
|
|
| 15 |
|
| 16 |
def load_resources():
|
| 17 |
-
global model
|
| 18 |
if model is None and os.path.exists(MODEL_PATH):
|
| 19 |
try:
|
| 20 |
# Carica Tokenizer
|
|
@@ -29,10 +30,10 @@ def load_resources():
|
|
| 29 |
print(f"Errore caricamento risorse MultiLabel: {e}")
|
| 30 |
return None, None
|
| 31 |
|
| 32 |
-
# Carichiamo una volta sola all'avvio (Singleton) per velocità
|
| 33 |
-
model, tokenizer = load_resources()
|
| 34 |
-
|
| 35 |
def multi_classification(text):
|
|
|
|
|
|
|
|
|
|
| 36 |
if model is None or tokenizer is None:
|
| 37 |
return {"Errore": "Modello non caricato"}
|
| 38 |
try:
|
|
|
|
| 2 |
import json
|
| 3 |
from keras.models import load_model
|
| 4 |
from keras_preprocessing.sequence import pad_sequences
|
| 5 |
+
from keras_preprocessing.text import tokenizer_from_json
|
| 6 |
|
| 7 |
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Risale alla root
|
| 8 |
MODEL_PATH = os.path.join(BASE_DIR, 'data', 'model', 'multi-classification.h5')
|
|
|
|
| 12 |
|
| 13 |
# Caricamento Singleton (lo carichiamo una volta sola)
|
| 14 |
model = None
|
| 15 |
+
tokenizer = None
|
| 16 |
|
| 17 |
def load_resources():
|
| 18 |
+
global model, tokenizer
|
| 19 |
if model is None and os.path.exists(MODEL_PATH):
|
| 20 |
try:
|
| 21 |
# Carica Tokenizer
|
|
|
|
| 30 |
print(f"Errore caricamento risorse MultiLabel: {e}")
|
| 31 |
return None, None
|
| 32 |
|
|
|
|
|
|
|
|
|
|
| 33 |
def multi_classification(text):
|
| 34 |
+
global model, tokenizer
|
| 35 |
+
if model is None or tokenizer is None:
|
| 36 |
+
load_resources()
|
| 37 |
if model is None or tokenizer is None:
|
| 38 |
return {"Errore": "Modello non caricato"}
|
| 39 |
try:
|
modules/retina.py
CHANGED
|
@@ -19,11 +19,11 @@ def load_resources():
|
|
| 19 |
except Exception as e:
|
| 20 |
print(f"❌ Errore caricamento modello Retina: {e}")
|
| 21 |
|
| 22 |
-
# Caricamento all'avvio
|
| 23 |
-
load_resources()
|
| 24 |
-
|
| 25 |
def predict_diabetic_retinopathy(image_array):
|
| 26 |
# Restituiamo sempre DUE valori (Diagnosi, Percentuale) anche in caso di errore
|
|
|
|
|
|
|
|
|
|
| 27 |
if model is None:
|
| 28 |
return "❌ Errore: Modello non trovato", "0%"
|
| 29 |
|
|
|
|
| 19 |
except Exception as e:
|
| 20 |
print(f"❌ Errore caricamento modello Retina: {e}")
|
| 21 |
|
|
|
|
|
|
|
|
|
|
| 22 |
def predict_diabetic_retinopathy(image_array):
|
| 23 |
# Restituiamo sempre DUE valori (Diagnosi, Percentuale) anche in caso di errore
|
| 24 |
+
global model
|
| 25 |
+
if model is None:
|
| 26 |
+
load_resources()
|
| 27 |
if model is None:
|
| 28 |
return "❌ Errore: Modello non trovato", "0%"
|
| 29 |
|
modules/utilities/logger.py
CHANGED
|
@@ -42,14 +42,27 @@ if not LOG_FILE.exists() or LOG_FILE.stat().st_size == 0:
|
|
| 42 |
"ip_address", "user_agent", "language", "input_size", "input_text" ,"processing_time"
|
| 43 |
])
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
def log_interaction(request, module_name, action, input_data=None, execution_time=0.0):
|
| 55 |
try:
|
|
|
|
| 42 |
"ip_address", "user_agent", "language", "input_size", "input_text" ,"processing_time"
|
| 43 |
])
|
| 44 |
|
| 45 |
+
class DummyScheduler:
|
| 46 |
+
def __init__(self):
|
| 47 |
+
class Lock:
|
| 48 |
+
def __enter__(self): pass
|
| 49 |
+
def __exit__(self, exc_type, exc_val, exc_tb): pass
|
| 50 |
+
self.lock = Lock()
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
scheduler = CommitScheduler(
|
| 54 |
+
repo_id=DATASET_REPO_ID,
|
| 55 |
+
repo_type="dataset",
|
| 56 |
+
folder_path=LOG_DIR,
|
| 57 |
+
path_in_repo="logs",
|
| 58 |
+
every=TIME,
|
| 59 |
+
token=HF_TOKEN
|
| 60 |
+
)
|
| 61 |
+
print("✅ CommitScheduler caricato con successo.")
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(f"⚠️ Impossibile inizializzare CommitScheduler: {e}")
|
| 64 |
+
print("ℹ️ I log verranno salvati solo in locale.")
|
| 65 |
+
scheduler = DummyScheduler()
|
| 66 |
|
| 67 |
def log_interaction(request, module_name, action, input_data=None, execution_time=0.0):
|
| 68 |
try:
|
modules/utilities/utils.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import json
|
| 2 |
import gradio as gr
|
| 3 |
import os
|
| 4 |
-
from
|
| 5 |
-
from
|
| 6 |
|
| 7 |
def load_doc(filename):
|
| 8 |
# open the file as read only
|
|
|
|
| 1 |
import json
|
| 2 |
import gradio as gr
|
| 3 |
import os
|
| 4 |
+
from keras_preprocessing.text import Tokenizer
|
| 5 |
+
from keras_preprocessing.text import tokenizer_from_json
|
| 6 |
|
| 7 |
def load_doc(filename):
|
| 8 |
# open the file as read only
|
style.css
CHANGED
|
@@ -180,4 +180,21 @@ button.primary:hover { filter: brightness(1.1); box-shadow: 0 4px 15px rgba(139,
|
|
| 180 |
}
|
| 181 |
|
| 182 |
}
|
| 183 |
-
footer {visibility: hidden}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
}
|
| 181 |
|
| 182 |
}
|
| 183 |
+
footer {visibility: hidden}
|
| 184 |
+
|
| 185 |
+
/* Sentiment info badge for BPO Dispatcher */
|
| 186 |
+
.sentiment-info-badge {
|
| 187 |
+
background: #f0fdf4;
|
| 188 |
+
border: 1px solid #bbf7d0;
|
| 189 |
+
border-radius: 8px;
|
| 190 |
+
padding: 12px;
|
| 191 |
+
font-size: 0.9em;
|
| 192 |
+
color: #166534;
|
| 193 |
+
margin-top: 15px;
|
| 194 |
+
margin-bottom: 15px;
|
| 195 |
+
line-height: 1.4;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
.sentiment-info-badge strong {
|
| 199 |
+
color: #14532d !important;
|
| 200 |
+
}
|