hrlima commited on
Commit
3490a66
·
verified ·
1 Parent(s): 68c5b1b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +126 -81
app.py CHANGED
@@ -3,11 +3,16 @@ import json
3
  import firebase_admin
4
  from firebase_admin import credentials, firestore
5
  from flask import Flask, request, jsonify
6
- from transformers import pipeline
 
 
 
7
 
8
  app = Flask(__name__)
9
 
10
- # ====== CONFIGURAÇÃO FIREBASE ======
 
 
11
  try:
12
  firebase_key = os.getenv("FIREBASE_KEY")
13
  if firebase_key:
@@ -17,19 +22,35 @@ try:
17
  db = firestore.client()
18
  print("✅ Firebase conectado com sucesso.")
19
  else:
20
- print("⚠️ FIREBASE_KEY não encontrada nos secrets do Space.")
21
  except Exception as e:
22
  print(f"❌ Erro ao inicializar Firebase: {e}")
23
 
24
- # ====== MODELO ======
 
 
25
  try:
26
  model_pipeline = pipeline("text-classification", model="pysentimiento/robertuito-emotion-analysis")
27
- print("✅ Modelo carregado com sucesso!")
28
  except Exception as e:
29
- print(f"❌ Erro ao carregar modelo: {e}")
30
  model_pipeline = None
31
 
32
- # ====== MAPEAMENTO DE EMOÇÕES ======
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  emotion_labels = {
34
  "sadness": "tristeza",
35
  "joy": "alegria",
@@ -40,77 +61,105 @@ emotion_labels = {
40
  "others": "neutro"
41
  }
42
 
43
- # ====== SUGESTÕES ======
 
 
44
  def gerar_sugestao(emotion_pt):
45
- sugestoes = {
46
- "tristeza": "Procure fazer algo que te acalme e traga conforto emocional.",
47
- "depressão": "Você não está sozinho. Considere conversar com alguém de confiança ou buscar apoio profissional.",
48
- "alegria": "Continue aproveitando esse momento positivo e compartilhe boas energias!",
49
- "raiva": "Afaste-se da situação e respire antes de reagir. Canalize essa energia em algo produtivo.",
50
- "ansiedade": "Tente identificar o que está te causando ansiedade e pequenos passos para enfrentá-la.",
51
- "insegurança": "Analise os pontos que causam insegurança e busque soluções práticas.",
52
- "neutro": "Mantenha o equilíbrio emocional e cuide de si mesmo.",
53
- "desconhecido": "Emoção não identificada com precisão."
54
  }
55
- return sugestoes.get(emotion_pt, "Mantenha o equilíbrio emocional e cuide de você mesmo.")
56
 
57
- # ====== FALLBACK APRIMORADO COM PALAVRAS-CHAVE ======
 
 
58
  EMOTION_KEYWORDS = {
59
- "tristeza": ["triste","desanimado","melancólico","chateado","solitário","deprimido","abatido","infeliz","desmotivado"],
60
- "ansiedade": ["ansioso","preocupado","nervoso","tenso","inquieto","aflito","alarmado","sobrecarregado","inseguro","apreensivo"],
61
- "insegurança": ["inseguro","incerto","receoso","hesitante","duvidoso","apreensivo","desconfiado"],
62
- "raiva": ["irritado","zangado","raiva","furioso","ódio","revoltado","frustrado","indignado","hostil","bravo","enfurecido","irado"],
63
- "alegria": ["feliz","animado","contente","alegre","satisfeito","entusiasmado","radiante","orgulhoso","euforia"],
64
- "depressão": ["sem esperança","vazio","desesperado","sem vontade","cansado da vida","desamparado"],
65
- "neutro": ["ok","normal","tranquilo","indiferente","equilibrado","estável"]
66
  }
67
 
68
- def fallback_emotion(text):
69
- text_lower = text.lower()
70
- match_counts = {k: sum(1 for w in v if w in text_lower) for k, v in EMOTION_KEYWORDS.items()}
71
- emotion = max(match_counts, key=match_counts.get)
72
- if match_counts[emotion] == 0:
73
- emotion = "neutro"
74
  return {
75
  "status": "fallback",
76
- "emotion": emotion,
77
- "emode": [emotion],
78
- "confidence": 0.6 if match_counts[emotion] > 0 else 0.0,
79
- "suggestion": gerar_sugestao(emotion),
80
- "debug": "Fallback ativado"
81
  }
82
 
83
- # ====== AJUSTE HÍBRIDO ======
84
- def hybrid_emotion(text, result):
85
- text_lower = text.lower()
86
- detected = result.get("emotion", "neutro")
87
- max_matches = 0
 
 
 
88
 
89
- for emo, keywords in EMOTION_KEYWORDS.items():
90
- matches = sum(2 for w in keywords if w in text_lower)
91
- if matches > max_matches:
92
- max_matches = matches
93
- if emo != detected:
94
- detected = emo
95
 
96
- confidence = result.get("confidence", 0.0)
97
- if detected != result.get("emotion"):
98
- confidence = 0.7 + max_matches * 0.05
99
- confidence = min(confidence, 1.0)
100
 
101
- return {
102
- "status": "ok",
103
- "emotion": detected,
104
- "emode": [detected],
105
- "confidence": round(confidence, 2),
106
- "probabilities": result.get("probabilities", {detected: 1.0}),
107
- "suggestion": result.get("suggestion", gerar_sugestao(detected)),
108
- "debug": result.get("debug", "Híbrido aplicado")
109
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- # ====== ROTA DE ANÁLISE ======
 
 
 
 
 
 
 
112
  @app.route("/analyze", methods=["POST"])
113
- def analyze():
114
  try:
115
  data = request.get_json()
116
  if not data or "text" not in data:
@@ -119,36 +168,32 @@ def analyze():
119
  text = data["text"]
120
 
121
  if not model_pipeline:
122
- return jsonify(fallback_emotion(text))
123
 
124
- result = model_pipeline(text, return_all_scores=True)
125
- if not result or len(result) == 0:
126
- return jsonify(fallback_emotion(text))
127
 
128
- scores = {r["label"]: r["score"] for r in result[0]}
129
  top_label = max(scores, key=scores.get)
130
- confidence = round(scores[top_label], 2)
131
- emotion_pt = emotion_labels.get(top_label, "desconhecido")
132
 
133
- # Ajuste especial para "tristeza" muito forte
134
- if emotion_pt == "tristeza" and confidence >= 0.9:
135
- emotion_pt = "depressão"
136
 
137
- base_result = {
138
  "status": "ok",
139
  "emotion": emotion_pt,
140
- "emode": [emotion_pt],
141
  "confidence": confidence,
142
- "probabilities": {emotion_labels.get(k, k): round(v,3) for k,v in scores.items()},
143
  "suggestion": gerar_sugestao(emotion_pt)
144
- }
145
-
146
- # Aplica lógica híbrida com fallback de palavras-chave
147
- final_result = hybrid_emotion(text, base_result)
148
- return jsonify(final_result)
149
 
150
  except Exception as e:
151
  return jsonify({"error": str(e)}), 500
152
 
 
 
 
 
153
  if __name__ == "__main__":
154
  app.run(host="0.0.0.0", port=7860)
 
3
  import firebase_admin
4
  from firebase_admin import credentials, firestore
5
  from flask import Flask, request, jsonify
6
+ from transformers import pipeline, AutoProcessor, AutoModelForAudioClassification
7
+ import torch
8
+ import librosa
9
+ import numpy as np
10
 
11
  app = Flask(__name__)
12
 
13
+ # ============================================================
14
+ # 1) CONFIGURAÇÃO FIREBASE
15
+ # ============================================================
16
  try:
17
  firebase_key = os.getenv("FIREBASE_KEY")
18
  if firebase_key:
 
22
  db = firestore.client()
23
  print("✅ Firebase conectado com sucesso.")
24
  else:
25
+ print("⚠️ FIREBASE_KEY não encontrada.")
26
  except Exception as e:
27
  print(f"❌ Erro ao inicializar Firebase: {e}")
28
 
29
+ # ============================================================
30
+ # 2) CARREGAMENTO DO MODELO DE TEXTO (JÁ EXISTENTE)
31
+ # ============================================================
32
  try:
33
  model_pipeline = pipeline("text-classification", model="pysentimiento/robertuito-emotion-analysis")
34
+ print("✅ Modelo de texto carregado com sucesso!")
35
  except Exception as e:
36
+ print(f"❌ Erro ao carregar modelo de texto: {e}")
37
  model_pipeline = None
38
 
39
+ # ============================================================
40
+ # 3) CARREGAMENTO DO MODELO DE ÁUDIO
41
+ # ============================================================
42
+ try:
43
+ audio_processor = AutoProcessor.from_pretrained("superb/wav2vec2-base-superb-ks")
44
+ audio_model = AutoModelForAudioClassification.from_pretrained("superb/wav2vec2-base-superb-ks")
45
+ print("🎤 Modelo de áudio carregado com sucesso!")
46
+ except Exception as e:
47
+ print(f"❌ Erro ao carregar modelo de áudio: {e}")
48
+ audio_processor = None
49
+ audio_model = None
50
+
51
+ # ============================================================
52
+ # 4) MAPEAMENTO DE EMOÇÕES (TEXTO + ÁUDIO)
53
+ # ============================================================
54
  emotion_labels = {
55
  "sadness": "tristeza",
56
  "joy": "alegria",
 
61
  "others": "neutro"
62
  }
63
 
64
+ # ============================================================
65
+ # 5) SUGESTÕES
66
+ # ============================================================
67
  def gerar_sugestao(emotion_pt):
68
+ mensagens = {
69
+ "tristeza": "Procure um momento de autocuidado, respire fundo e busque apoio.",
70
+ "depressão": "Considere conversar com alguém de confiança ou buscar apoio profissional.",
71
+ "alegria": "Que ótimo! Mantenha-se aproveitando esse momento positivo.",
72
+ "raiva": "Afaste-se da situação e procure se acalmar antes de agir.",
73
+ "ansiedade": "Tente identificar o motivo da ansiedade e respire profundamente.",
74
+ "insegurança": "Ganhe confiança focando no que você pode controlar.",
75
+ "neutro": "Mantenha o equilíbrio emocional e cuide de si mesmo."
 
76
  }
77
+ return mensagens.get(emotion_pt, "Cuide de si mesmo e busque equilíbrio emocional.")
78
 
79
+ # ============================================================
80
+ # 6) FALLBACK DE PALAVRAS-CHAVE (TEXTO)
81
+ # ============================================================
82
  EMOTION_KEYWORDS = {
83
+ "tristeza": ["triste","desanimado","melancólico","chateado","solitário","abatido","infeliz"],
84
+ "ansiedade": ["ansioso","preocupado","nervoso","tenso","inquieto","aflito"],
85
+ "insegurança": ["inseguro","receoso","hesitante","duvidoso"],
86
+ "raiva": ["irritado","zangado","raiva","furioso","ódio","revoltado","bravo"],
87
+ "alegria": ["feliz","contente","animado","alegre","satisfeito","entusiasmado"],
88
+ "depressão": ["vazio","sem esperança","desesperado","desamparado"],
89
+ "neutro": ["ok","normal","tranquilo","indiferente"]
90
  }
91
 
92
+ def fallback_emotion():
 
 
 
 
 
93
  return {
94
  "status": "fallback",
95
+ "emotion": "neutro",
96
+ "confidence": 0.3,
97
+ "suggestion": gerar_sugestao("neutro"),
98
+ "debug": "Fallback ativado - Sem modelo"
 
99
  }
100
 
101
+ # ============================================================
102
+ # 7) PROCESSAMENTO DE ÁUDIO
103
+ # ============================================================
104
+ def analyze_audio_file(filepath):
105
+ try:
106
+ # Carrega áudio com 16kHz (esperado pelo modelo)
107
+ audio, sr = librosa.load(filepath, sr=16000)
108
+ inputs = audio_processor(audio, sampling_rate=16000, return_tensors="pt")
109
 
110
+ with torch.no_grad():
111
+ outputs = audio_model(**inputs)
 
 
 
 
112
 
113
+ logits = outputs.logits
114
+ predicted_class_id = logits.argmax().item()
 
 
115
 
116
+ label = audio_model.config.id2label[predicted_class_id]
117
+
118
+ # Simplificação: considera "surprise" como "alegria"
119
+ emotion = emotion_labels.get(label, "neutro")
120
+
121
+ confidence = float(torch.softmax(logits, dim=1)[0][predicted_class_id].item())
122
+
123
+ return {
124
+ "status": "ok",
125
+ "emotion": emotion,
126
+ "confidence": round(confidence, 2),
127
+ "probabilities": {emotion: confidence},
128
+ "suggestion": gerar_sugestao(emotion)
129
+ }
130
+
131
+ except Exception as e:
132
+ print("Erro ao processar áudio:", e)
133
+ return fallback_emotion()
134
+
135
+ # ============================================================
136
+ # 8) ENDPOINT DE ÁUDIO – NOVO
137
+ # ============================================================
138
+ @app.route("/analyze-audio", methods=["POST"])
139
+ def analyze_audio():
140
+ try:
141
+ if 'audio' not in request.files:
142
+ return jsonify({"error": "Arquivo de áudio é obrigatório."}), 400
143
+
144
+ file = request.files['audio']
145
+ filepath = "temp_audio.wav"
146
+ file.save(filepath)
147
+
148
+ result = analyze_audio_file(filepath)
149
+
150
+ # Remove arquivo temporário
151
+ os.remove(filepath)
152
 
153
+ return jsonify(result)
154
+
155
+ except Exception as e:
156
+ return jsonify({"error": str(e)}), 500
157
+
158
+ # ============================================================
159
+ # 9) ENDPOINT ORIGINAL DE TEXTO
160
+ # ============================================================
161
  @app.route("/analyze", methods=["POST"])
162
+ def analyze_text():
163
  try:
164
  data = request.get_json()
165
  if not data or "text" not in data:
 
168
  text = data["text"]
169
 
170
  if not model_pipeline:
171
+ return jsonify(fallback_emotion())
172
 
173
+ model_result = model_pipeline(text, return_all_scores=True)
174
+ if not model_result:
175
+ return jsonify(fallback_emotion())
176
 
177
+ scores = {r["label"]: r["score"] for r in model_result[0]}
178
  top_label = max(scores, key=scores.get)
179
+ emotion_pt = emotion_labels.get(top_label, "neutro")
 
180
 
181
+ confidence = round(scores[top_label], 2)
 
 
182
 
183
+ return jsonify({
184
  "status": "ok",
185
  "emotion": emotion_pt,
 
186
  "confidence": confidence,
187
+ "probabilities": {emotion_pt: confidence},
188
  "suggestion": gerar_sugestao(emotion_pt)
189
+ })
 
 
 
 
190
 
191
  except Exception as e:
192
  return jsonify({"error": str(e)}), 500
193
 
194
+
195
+ # ============================================================
196
+ # 10) INICIAR SERVIDOR
197
+ # ============================================================
198
  if __name__ == "__main__":
199
  app.run(host="0.0.0.0", port=7860)