PsalmsJava commited on
Commit
12f6795
·
1 Parent(s): 9434231

Made Some Changes

Browse files
Files changed (1) hide show
  1. app.py +149 -0
app.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ import subprocess
4
+ from fastapi import FastAPI, File, UploadFile, HTTPException
5
+ from fastapi.responses import JSONResponse
6
+ import aiohttp
7
+ import numpy as np
8
+ from datetime import datetime
9
+ import logging
10
+
11
+ # Setup
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger(__name__)
14
+ app = FastAPI(title="Emotion Detection API", docs_url="/docs")
15
+
16
+ # Config - get from environment
17
+ HF_TOKEN = os.getenv("HF_TOKEN", "")
18
+ API_TOKEN = os.getenv("API_TOKEN", "test123")
19
+
20
+ # Models - using only 2 for reliability
21
+ MODELS = {
22
+ "wav2vec2_english": {
23
+ "url": "https://api-inference.huggingface.co/models/ehcalabres/wav2vec2-lg-xlsr-en-speech-emotion-recognition",
24
+ "weight": 0.7,
25
+ },
26
+ "gigam_emo": {
27
+ "url": "https://api-inference.huggingface.co/models/salute-developers/GigaAM-emo",
28
+ "weight": 0.3,
29
+ }
30
+ }
31
+
32
+ # Emotion mapping
33
+ EMOTION_MAPPING = {
34
+ "angry": ["angry", "ang"],
35
+ "happy": ["happy", "hap"],
36
+ "sad": ["sad"],
37
+ "fear": ["fear"],
38
+ "surprise": ["surprise"],
39
+ "disgust": ["disgust"],
40
+ "neutral": ["neutral", "neu"]
41
+ }
42
+
43
+ @app.get("/health")
44
+ async def health():
45
+ return {"status": "ok", "hf_token": bool(HF_TOKEN)}
46
+
47
+ @app.get("/")
48
+ async def root():
49
+ return {
50
+ "message": "Emotion Detection API",
51
+ "docs": "/docs",
52
+ "endpoints": ["POST /analyze"]
53
+ }
54
+
55
+ @app.post("/analyze")
56
+ async def analyze(file: UploadFile = File(...)):
57
+ """Analyze emotion from audio file"""
58
+
59
+ # Check auth header
60
+ auth = file.headers.get("authorization", "")
61
+ if not auth or auth.replace("Bearer ", "") != API_TOKEN:
62
+ return JSONResponse(
63
+ status_code=401,
64
+ content={"error": "Invalid or missing Authorization header"}
65
+ )
66
+
67
+ # Save uploaded file
68
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
69
+ content = await file.read()
70
+ tmp.write(content)
71
+ input_path = tmp.name
72
+
73
+ try:
74
+ # Convert to proper format
75
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as out:
76
+ output_path = out.name
77
+
78
+ subprocess.run([
79
+ "ffmpeg", "-i", input_path,
80
+ "-ar", "16000", "-ac", "1",
81
+ "-y", output_path
82
+ ], check=True, capture_output=True)
83
+
84
+ # Read converted file
85
+ with open(output_path, "rb") as f:
86
+ audio_bytes = f.read()
87
+
88
+ # Query models
89
+ headers = {"Authorization": f"Bearer {HF_TOKEN}"}
90
+ results = {}
91
+
92
+ async with aiohttp.ClientSession() as session:
93
+ for name, config in MODELS.items():
94
+ try:
95
+ async with session.post(
96
+ config["url"],
97
+ headers=headers,
98
+ data=audio_bytes,
99
+ timeout=10
100
+ ) as resp:
101
+ if resp.status == 200:
102
+ results[name] = await resp.json()
103
+ except Exception as e:
104
+ logger.warning(f"{name} failed: {e}")
105
+
106
+ # Simple ensemble
107
+ emotion_scores = {}
108
+ total_weight = 0
109
+
110
+ for name, predictions in results.items():
111
+ weight = MODELS[name]["weight"]
112
+ total_weight += weight
113
+
114
+ for pred in predictions:
115
+ label = pred.get("label", "").lower()
116
+ score = pred.get("score", 0)
117
+
118
+ # Map to standard emotions
119
+ for std_emo, variations in EMOTION_MAPPING.items():
120
+ if any(v in label for v in variations):
121
+ emotion_scores[std_emo] = emotion_scores.get(std_emo, 0) + score * weight
122
+ break
123
+
124
+ # Normalize
125
+ if total_weight > 0:
126
+ emotion_scores = {k: v/total_weight for k, v in emotion_scores.items()}
127
+
128
+ # Get primary emotion
129
+ primary = max(emotion_scores.items(), key=lambda x: x[1]) if emotion_scores else ("unknown", 0)
130
+
131
+ return {
132
+ "primary_emotion": primary[0],
133
+ "confidence": round(primary[1], 3),
134
+ "all_emotions": {k: round(v, 3) for k, v in emotion_scores.items()},
135
+ "models_used": list(results.keys())
136
+ }
137
+
138
+ except Exception as e:
139
+ logger.error(f"Error: {e}")
140
+ return JSONResponse(status_code=500, content={"error": str(e)})
141
+ finally:
142
+ # Cleanup
143
+ for path in [input_path, output_path]:
144
+ if os.path.exists(path):
145
+ os.unlink(path)
146
+
147
+ # For Hugging Face
148
+ from fastapi.middleware.cors import CORSMiddleware
149
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])