anfastech commited on
Commit
0c39d91
Β·
1 Parent(s): fb4e8a7

Fix: numpy dependency order, add proper error handling

Browse files
Files changed (3) hide show
  1. Dockerfile +11 -6
  2. app.py +89 -35
  3. requirements.txt +13 -2
Dockerfile CHANGED
@@ -1,23 +1,28 @@
 
1
  FROM python:3.10-slim
2
 
3
  WORKDIR /app
4
 
5
- # Install system dependencies
6
  RUN apt-get update && apt-get install -y \
7
  ffmpeg \
 
8
  && rm -rf /var/lib/apt/lists/*
9
 
10
  # Copy requirements
11
  COPY requirements.txt .
12
 
13
- # Install Python dependencies
14
- RUN pip install --no-cache-dir -r requirements.txt
 
 
15
 
16
- # Copy app code
17
  COPY . .
18
 
19
  # Expose port
20
  EXPOSE 7860
21
 
22
- # Run the API
23
- CMD ["python", "app.py"]
 
 
1
+ # Dockerfile
2
  FROM python:3.10-slim
3
 
4
  WORKDIR /app
5
 
6
+ # Install system dependencies FIRST
7
  RUN apt-get update && apt-get install -y \
8
  ffmpeg \
9
+ libsndfile1 \
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
  # Copy requirements
13
  COPY requirements.txt .
14
 
15
+ # Install Python dependencies in CORRECT ORDER
16
+ # numpy MUST be first, before torch/torchaudio
17
+ RUN pip install --no-cache-dir numpy>=1.24.0,<2.0.0 && \
18
+ pip install --no-cache-dir -r requirements.txt
19
 
20
+ # Copy application code
21
  COPY . .
22
 
23
  # Expose port
24
  EXPOSE 7860
25
 
26
+ # Run with proper Python settings
27
+ ENV PYTHONUNBUFFERED=1
28
+ CMD ["python", "app.py"]
app.py CHANGED
@@ -1,23 +1,39 @@
1
  # app.py
 
 
 
 
2
  from fastapi import FastAPI, UploadFile, File, HTTPException
3
  from fastapi.responses import JSONResponse
4
  from fastapi.middleware.cors import CORSMiddleware
5
- import logging
6
- import os
7
- from diagnosis.ai_engine.detect_stuttering import get_stutter_detector
8
 
9
- # Configure logging
10
- logging.basicConfig(level=logging.INFO)
 
 
 
 
11
  logger = logging.getLogger(__name__)
12
 
 
 
 
 
 
 
 
 
 
 
 
13
  # Initialize FastAPI
14
  app = FastAPI(
15
  title="Stutter Detector API",
16
- description="Speech analysis using Wav2Vec2 models",
17
  version="1.0.0"
18
  )
19
 
20
- # Add CORS
21
  app.add_middleware(
22
  CORSMiddleware,
23
  allow_origins=["*"],
@@ -26,73 +42,111 @@ app.add_middleware(
26
  allow_headers=["*"],
27
  )
28
 
29
- # Initialize detector (loads models once)
30
  detector = None
31
 
32
  @app.on_event("startup")
33
  async def startup_event():
34
- """Load models when app starts"""
35
  global detector
36
- logger.info("πŸš€ Starting up... Loading models...")
37
- detector = get_stutter_detector()
38
- logger.info("βœ… Models loaded successfully!")
 
 
 
 
39
 
40
  @app.get("/health")
41
  async def health_check():
42
  """Health check endpoint"""
43
  return {
44
  "status": "healthy",
45
- "models_loaded": detector is not None
 
46
  }
47
 
48
  @app.post("/analyze")
49
  async def analyze_audio(
50
  audio: UploadFile = File(...),
51
- transcript: str = None
52
  ):
53
  """
54
- Analyze audio for stuttering
55
 
56
  Parameters:
57
- - audio: MP3/WAV file upload
58
  - transcript: Optional expected transcript
59
 
60
- Returns: Stuttering analysis results
61
  """
 
62
  try:
63
- # Save uploaded file temporarily
64
- file_path = f"/tmp/{audio.filename}"
65
- with open(file_path, "wb") as f:
66
- contents = await audio.read()
67
- f.write(contents)
68
 
69
- logger.info(f"πŸ“₯ Analyzing: {audio.filename}")
70
 
71
- # Run analysis
72
- result = detector.analyze_audio(file_path, transcript or "")
 
73
 
74
- # Cleanup
75
- os.remove(file_path)
 
 
 
 
 
 
76
 
77
- logger.info(f"βœ… Analysis complete for {audio.filename}")
 
 
 
 
78
  return result
79
 
 
 
80
  except Exception as e:
81
- logger.error(f"❌ Error: {str(e)}")
82
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
83
 
84
  @app.get("/")
85
  async def root():
86
  """API documentation"""
87
  return {
88
- "message": "Stutter Detector API",
 
 
89
  "endpoints": {
90
- "health": "/health",
91
- "analyze": "/analyze (POST with audio file + optional transcript)",
92
- "docs": "/docs"
 
 
 
 
 
93
  }
94
  }
95
 
96
  if __name__ == "__main__":
97
  import uvicorn
98
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
1
  # app.py
2
+ import logging
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
  from fastapi import FastAPI, UploadFile, File, HTTPException
7
  from fastapi.responses import JSONResponse
8
  from fastapi.middleware.cors import CORSMiddleware
 
 
 
9
 
10
+ # Configure logging FIRST
11
+ logging.basicConfig(
12
+ level=logging.INFO,
13
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
14
+ stream=sys.stdout
15
+ )
16
  logger = logging.getLogger(__name__)
17
 
18
+ # Add project root to path
19
+ sys.path.insert(0, str(Path(__file__).parent))
20
+
21
+ # Import detector
22
+ try:
23
+ from diagnosis.ai_engine.detect_stuttering import get_stutter_detector
24
+ logger.info("βœ… Successfully imported StutterDetector")
25
+ except ImportError as e:
26
+ logger.error(f"❌ Failed to import StutterDetector: {e}")
27
+ raise
28
+
29
  # Initialize FastAPI
30
  app = FastAPI(
31
  title="Stutter Detector API",
32
+ description="Speech analysis using Wav2Vec2 models for stutter detection",
33
  version="1.0.0"
34
  )
35
 
36
+ # Add CORS middleware
37
  app.add_middleware(
38
  CORSMiddleware,
39
  allow_origins=["*"],
 
42
  allow_headers=["*"],
43
  )
44
 
45
+ # Global detector instance
46
  detector = None
47
 
48
  @app.on_event("startup")
49
  async def startup_event():
50
+ """Load models on startup"""
51
  global detector
52
+ try:
53
+ logger.info("πŸš€ Startup event: Loading AI models...")
54
+ detector = get_stutter_detector()
55
+ logger.info("βœ… Models loaded successfully!")
56
+ except Exception as e:
57
+ logger.error(f"❌ Failed to load models: {e}", exc_info=True)
58
+ raise
59
 
60
  @app.get("/health")
61
  async def health_check():
62
  """Health check endpoint"""
63
  return {
64
  "status": "healthy",
65
+ "models_loaded": detector is not None,
66
+ "timestamp": str(os.popen("date").read()).strip()
67
  }
68
 
69
  @app.post("/analyze")
70
  async def analyze_audio(
71
  audio: UploadFile = File(...),
72
+ transcript: str = ""
73
  ):
74
  """
75
+ Analyze audio file for stuttering
76
 
77
  Parameters:
78
+ - audio: WAV or MP3 audio file
79
  - transcript: Optional expected transcript
80
 
81
+ Returns: Complete stutter analysis results
82
  """
83
+ temp_file = None
84
  try:
85
+ if not detector:
86
+ raise HTTPException(status_code=503, detail="Models not loaded yet. Try again in a moment.")
 
 
 
87
 
88
+ logger.info(f"πŸ“₯ Processing: {audio.filename}")
89
 
90
+ # Create temp directory if needed
91
+ temp_dir = "/tmp/stutter_analysis"
92
+ os.makedirs(temp_dir, exist_ok=True)
93
 
94
+ # Save uploaded file
95
+ temp_file = os.path.join(temp_dir, audio.filename)
96
+ content = await audio.read()
97
+
98
+ with open(temp_file, "wb") as f:
99
+ f.write(content)
100
+
101
+ logger.info(f"πŸ“‚ Saved to: {temp_file} ({len(content) / 1024 / 1024:.2f} MB)")
102
 
103
+ # Analyze
104
+ logger.info(f"πŸ”„ Analyzing audio with transcript: '{transcript[:50]}...'")
105
+ result = detector.analyze_audio(temp_file, transcript)
106
+
107
+ logger.info(f"βœ… Analysis complete: severity={result['severity']}, mismatch={result['mismatch_percentage']}%")
108
  return result
109
 
110
+ except HTTPException:
111
+ raise
112
  except Exception as e:
113
+ logger.error(f"❌ Error during analysis: {str(e)}", exc_info=True)
114
+ raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")
115
+
116
+ finally:
117
+ # Cleanup
118
+ if temp_file and os.path.exists(temp_file):
119
+ try:
120
+ os.remove(temp_file)
121
+ logger.info(f"🧹 Cleaned up: {temp_file}")
122
+ except Exception as e:
123
+ logger.warning(f"Could not clean up {temp_file}: {e}")
124
 
125
  @app.get("/")
126
  async def root():
127
  """API documentation"""
128
  return {
129
+ "name": "SLAQ Stutter Detector API",
130
+ "version": "1.0.0",
131
+ "status": "running",
132
  "endpoints": {
133
+ "health": "GET /health",
134
+ "analyze": "POST /analyze (multipart: audio file + optional transcript field)",
135
+ "docs": "GET /docs (interactive API docs)"
136
+ },
137
+ "models": {
138
+ "base": "facebook/wav2vec2-base-960h",
139
+ "large": "facebook/wav2vec2-large-960h-lv60-self",
140
+ "xlsr": "jonatasgrosman/wav2vec2-large-xlsr-53-english"
141
  }
142
  }
143
 
144
  if __name__ == "__main__":
145
  import uvicorn
146
+ logger.info("πŸš€ Starting SLAQ Stutter Detector API...")
147
+ uvicorn.run(
148
+ app,
149
+ host="0.0.0.0",
150
+ port=7860,
151
+ log_level="info"
152
+ )
requirements.txt CHANGED
@@ -1,7 +1,18 @@
1
- librosa==0.10.0
 
2
  torch==2.0.1
3
  torchaudio==2.0.2
 
4
  transformers==4.35.0
 
 
 
 
 
 
5
  fastapi==0.104.1
6
  uvicorn==0.24.0
7
- python-multipart==0.0.6
 
 
 
 
1
+ # Core ML Dependencies - ORDER MATTERS!
2
+ numpy>=1.24.0,<2.0.0
3
  torch==2.0.1
4
  torchaudio==2.0.2
5
+ librosa>=0.10.0
6
  transformers==4.35.0
7
+
8
+ # Audio Processing
9
+ soundfile>=0.12.1
10
+ scipy>=1.11.0
11
+
12
+ # API Framework
13
  fastapi==0.104.1
14
  uvicorn==0.24.0
15
+ python-multipart==0.0.6
16
+
17
+ # Logging
18
+ python-json-logger>=2.0.0