agusansola commited on
Commit
6d56bb7
·
1 Parent(s): ee282b8

Add simple audio analysis API for Flutter integration

Browse files
Files changed (3) hide show
  1. .gitignore +8 -0
  2. app.py +137 -0
  3. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ .cache/
6
+ .local/
7
+ .env
8
+ .DS_Store
app.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re #serve per sostituire le parole nel testo
2
+ import gradio as gr #serve per esporre la funzione come app/API
3
+ from transformers import pipeline #serve per usare i modelli in modo semplice
4
+
5
+ # LOAD MODELS
6
+
7
+ transcriber = pipeline(
8
+ "automatic-speech-recognition",
9
+ model="openai/whisper-base"
10
+ )
11
+
12
+ ner_model = pipeline(
13
+ "ner",
14
+ model="Davlan/xlm-roberta-base-ner-hrl",
15
+ aggregation_strategy="simple"
16
+ )
17
+
18
+ sentiment_model = pipeline(
19
+ "sentiment-analysis",
20
+ model="cardiffnlp/twitter-xlm-roberta-base-sentiment"
21
+ )
22
+
23
+
24
+ # HELPERS - alcuni modelli non restituiscono direttamente positive, negative o neutral, ma codici. Con questa funzione si transformano in parole leggibili.
25
+
26
+ def map_sentiment_label(label):
27
+ label_map = {
28
+ "LABEL_0": "negative",
29
+ "LABEL_1": "neutral",
30
+ "LABEL_2": "positive"
31
+ }
32
+ return label_map.get(label, label)
33
+
34
+ #funzione di anonimizzazione prende il testo originale, le entità trovate dal NER e sostuisce le entità con placeholder tipo [PER], [LOC], [ORG]
35
+ def anonymize_text(text, entities):
36
+ anonymized_text = text
37
+
38
+ # Si ordina le entità dalla più lunga alla più corta per evitare sostituzioni parziali sbagliate.
39
+ sorted_entities = sorted(
40
+ entities,
41
+ key=lambda item: len(item["word"]),
42
+ reverse=True
43
+ )
44
+
45
+ for entity in sorted_entities:
46
+ word = entity["word"]
47
+ entity_group = entity["entity_group"]
48
+
49
+ if word:
50
+ anonymized_text = re.sub(
51
+ re.escape(word),
52
+ f"[{entity_group}]",
53
+ anonymized_text,
54
+ flags=re.IGNORECASE
55
+ )
56
+
57
+ return anonymized_text
58
+
59
+ # MAIN FUNCTION - la funzione principale che chiamerà flutter indirettamente tramite lo Space. Riceve il file audio caricato. Se non arriva niente, restituiamo un JSON valido con errore.
60
+
61
+ def process_audio(audio_path):
62
+ if audio_path is None:
63
+ return {
64
+ "error": "No audio file provided",
65
+ "transcription": "",
66
+ "anonymized_text": "",
67
+ "entities": [],
68
+ "sentiment": {
69
+ "label": "",
70
+ "score": 0.0
71
+ }
72
+ }
73
+
74
+ # Transcription - whisper ascolta l'audio e produce il testo
75
+ transcription_result = transcriber(audio_path)
76
+ transcription = transcription_result.get("text", "").strip()
77
+
78
+ #se la trascrizione è vuota, restituisce errore
79
+ if not transcription:
80
+ return {
81
+ "error": "Empty transcription",
82
+ "transcription": "",
83
+ "anonymized_text": "",
84
+ "entities": [],
85
+ "sentiment": {
86
+ "label": "",
87
+ "score": 0.0
88
+ }
89
+ }
90
+
91
+ # NER - si passa il testo trascritto al modello NER
92
+ ner_result = ner_model(transcription)
93
+
94
+ # di trasforma l'output in una lista più pulita
95
+ entities = []
96
+ for item in ner_result:
97
+ entities.append({
98
+ "entity_group": item.get("entity_group", ""),
99
+ "word": item.get("word", ""),
100
+ "score": round(float(item.get("score", 0.0)), 4)
101
+ })
102
+
103
+ # qui si applica l'anonimizzazione, ovvero le sostituzioni.
104
+ anonymized_text = anonymize_text(transcription, entities)
105
+
106
+ # Sentiment analysis sul testo anonimizzato
107
+ sentiment_result = sentiment_model(anonymized_text)[0]
108
+
109
+ sentiment = {
110
+ "label": map_sentiment_label(sentiment_result.get("label", "")),
111
+ "score": round(float(sentiment_result.get("score", 0.0)), 4)
112
+ }
113
+
114
+ # Risposta finale
115
+ return {
116
+ "error": "",
117
+ "transcription": transcription,
118
+ "anonymized_text": anonymized_text,
119
+ "entities": entities,
120
+ "sentiment": sentiment
121
+ }
122
+
123
+ # GRADIO APP
124
+
125
+ app = gr.Interface(
126
+ #usa la funzione process_audio
127
+ fn=process_audio,
128
+ #input è un audio
129
+ inputs=gr.Audio(type="filepath", sources=["upload", "microphone"]),
130
+ #output è un JSON
131
+ outputs=gr.JSON(),
132
+ title="Audio Analysis API",
133
+ description="Upload an audio file to get transcription, anonymization and sentiment."
134
+ )
135
+
136
+ #avvia l'app
137
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ transformers
2
+ torch
3
+ gradio
4
+ accelerate
5
+ sentencepiece