fariedalfarizi commited on
Commit
2babf34
·
1 Parent(s): cb2f8ff

Fix: Disable OpenAPI docs and simplify endpoints to fix Content-Length error

Browse files
Files changed (1) hide show
  1. api/routes.py +30 -189
api/routes.py CHANGED
@@ -52,41 +52,12 @@ class CustomJSONResponse(Response):
52
  # =======================================
53
 
54
  app = FastAPI(
55
- title="Vocal Articulation Assessment API v2",
56
- description="""
57
- ## API untuk Penilaian Artikulasi Vokal Indonesia
58
-
59
- Sistem penilaian berbasis **Whisper ASR** dengan analisis audio komprehensif untuk 5 level artikulasi.
60
-
61
- ### Features
62
- - **ASR-based Clarity Scoring** menggunakan Whisper model
63
- - **6 Metrik Komprehensif**: Clarity, Energy, Speech Rate, Pitch Consistency, SNR, Articulation
64
- - **Multi-level Support**: Level 1-5 (Vokal → Kalimat)
65
- - **Grading System**: A-E berdasarkan overall score
66
-
67
- ### Documentation
68
- - **Swagger UI**: `/docs` (interactive API testing)
69
- - **ReDoc**: `/redoc` (alternative documentation)
70
- - **OpenAPI JSON**: `/openapi.json`
71
-
72
- ### Endpoints
73
- - `GET /` - API information
74
- - `GET /health` - Health check & model status
75
- - `GET /levels` - List all articulation levels
76
- - `POST /score` - Score single audio file
77
- - `POST /batch_score` - Score multiple audio files
78
- """,
79
  version="2.0.0",
80
- docs_url="/docs",
81
- redoc_url="/redoc",
82
- openapi_url="/openapi.json",
83
- contact={
84
- "name": "Vocal Articulation Assessment Team",
85
- "url": "https://huggingface.co/spaces/Cyberlace/latihan-artikulasi",
86
- },
87
- license_info={
88
- "name": "MIT License",
89
- }
90
  )
91
 
92
  # CORS middleware
@@ -99,48 +70,11 @@ app.add_middleware(
99
  )
100
 
101
  # =======================================
102
- # PYDANTIC MODELS
103
  # =======================================
104
 
105
- class ScoreResponse(BaseModel):
106
- """Response model untuk scoring"""
107
- success: bool
108
- overall_score: float
109
- grade: str
110
-
111
- # Component scores
112
- clarity_score: float
113
- energy_score: float
114
- speech_rate_score: float
115
- pitch_consistency_score: float
116
- snr_score: float
117
- articulation_score: float
118
-
119
- # ASR results
120
- transcription: str
121
- target: str
122
- similarity: float
123
- wer: float
124
-
125
- # Feedback
126
- feedback: str
127
- suggestions: List[str]
128
-
129
- # Audio features
130
- audio_features: dict
131
- level: int
132
-
133
- class HealthResponse(BaseModel):
134
- """Response untuk health check"""
135
- status: str
136
- model_loaded: bool
137
- device: str
138
- whisper_model: str
139
-
140
- class LevelsResponse(BaseModel):
141
- """Response untuk supported levels"""
142
- levels: dict
143
- total_levels: int
144
 
145
  # =======================================
146
  # GLOBAL VARIABLES
@@ -201,93 +135,31 @@ async def root():
201
  }
202
  )
203
 
204
- @app.get("/health", response_model=HealthResponse, tags=["System"])
205
  async def health_check():
206
- """
207
- ## Health Check
208
-
209
- Check API health status and model loading status.
210
-
211
- **Returns:**
212
- - `status`: "healthy" or "unhealthy"
213
- - `model_loaded`: Whether Whisper model is loaded
214
- - `device`: CPU or CUDA
215
- - `whisper_model`: Model name
216
- """
217
- return HealthResponse(
218
- status="healthy" if scorer is not None else "unhealthy",
219
- model_loaded=scorer is not None,
220
- device=scorer.device if scorer else "unknown",
221
- whisper_model="openai/whisper-small" if scorer else "not loaded"
222
- )
223
 
224
- @app.get("/levels", response_model=LevelsResponse, tags=["Articulation"])
225
  async def get_levels():
226
- """
227
- ## Get Articulation Levels
228
-
229
- Retrieve all available articulation levels with their targets.
230
-
231
- **Levels:**
232
- - **Level 1**: Vokal Tunggal (A, I, U, E, O)
233
- - **Level 2**: Konsonan + Vokal (BA, DA, KA, etc.)
234
- - **Level 3**: Suku Kata Kompleks (BRA, TRI, etc.)
235
- - **Level 4**: Kata Penuh (RUMAH, STRATEGI, etc.)
236
- - **Level 5**: Kalimat Lengkap
237
-
238
- **Returns:**
239
- - `levels`: Dictionary of all levels with targets
240
- - `total_levels`: Total number of levels (5)
241
- """
242
- return LevelsResponse(
243
- levels=ARTICULATION_LEVELS,
244
- total_levels=len(ARTICULATION_LEVELS)
245
- )
246
 
247
  @app.post("/score", response_class=CustomJSONResponse, tags=["Scoring"])
248
  async def score_audio(
249
- audio: UploadFile = File(..., description="Audio file (WAV, MP3, M4A, FLAC, OGG)"),
250
- target_text: str = Form(..., description="Target text yang seharusnya diucapkan"),
251
- level: int = Form(1, description="Level artikulasi (1-5)")
252
  ):
253
- """
254
- ## Score Audio File
255
-
256
- Upload audio dan dapatkan penilaian artikulasi vokal komprehensif.
257
-
258
- **Request:**
259
- - `audio`: Audio file (format: WAV, MP3, M4A, FLAC, OGG)
260
- - `target_text`: Text yang seharusnya diucapkan (contoh: "A", "BA", "STRATEGI")
261
- - `level`: Level artikulasi (1-5)
262
-
263
- **Response:**
264
- - `success`: Boolean status
265
- - `overall_score`: Skor keseluruhan (0-100)
266
- - `grade`: Grade (A-E)
267
- - 6 component scores (clarity, energy, speech_rate, pitch_consistency, snr, articulation)
268
- - `transcription`: Hasil ASR dari audio
269
- - `target`: Target text (uppercase)
270
- - `similarity`: Similarity score (0-1)
271
- - `wer`: Word Error Rate (0-1)
272
- - `feedback`: Feedback teks
273
- - `suggestions`: List saran perbaikan
274
- - `audio_features`: Dictionary fitur audio
275
- - `level`: Level yang digunakan
276
-
277
- **Example:**
278
- ```python
279
- import requests
280
-
281
- files = {'audio': open('recording.wav', 'rb')}
282
- data = {'target_text': 'STRATEGI', 'level': 4}
283
- response = requests.post('http://localhost:8000/score', files=files, data=data)
284
- result = response.json()
285
- print(f"Score: {result['overall_score']}, Grade: {result['grade']}")
286
- ```
287
-
288
- Returns:
289
- ScoreResponse dengan hasil penilaian lengkap
290
- """
291
  if scorer is None:
292
  raise HTTPException(status_code=503, detail="Model not loaded")
293
 
@@ -340,42 +212,11 @@ async def score_audio(
340
 
341
  @app.post("/batch_score", tags=["Scoring"])
342
  async def batch_score_audio(
343
- audios: List[UploadFile] = File(..., description="Multiple audio files"),
344
- target_texts: str = Form(..., description="Comma-separated target texts"),
345
- levels: str = Form("1", description="Comma-separated levels (default: 1 for all)")
346
  ):
347
- """
348
- ## Batch Score Multiple Audio Files
349
-
350
- Upload beberapa audio files sekaligus dan dapatkan penilaian untuk masing-masing.
351
-
352
- **Request:**
353
- - `audios`: List of audio files
354
- - `target_texts`: Comma-separated target texts (contoh: "A,I,U,E,O")
355
- - `levels`: Comma-separated levels (contoh: "1,1,1,2,2") atau single value untuk semua
356
-
357
- **Response:**
358
- - `results`: Array of score results (sama seperti /score endpoint)
359
- - `total`: Total number of processed files
360
-
361
- **Example:**
362
- ```python
363
- import requests
364
-
365
- files = [
366
- ('audios', open('audio1.wav', 'rb')),
367
- ('audios', open('audio2.wav', 'rb')),
368
- ]
369
- data = {
370
- 'target_texts': 'A,I',
371
- 'levels': '1,1'
372
- }
373
- response = requests.post('http://localhost:8000/batch_score', files=files, data=data)
374
- results = response.json()['results']
375
- for r in results:
376
- print(f"{r['filename']}: {r['overall_score']}")
377
- ```
378
- """
379
  if scorer is None:
380
  raise HTTPException(status_code=503, detail="Model not loaded")
381
 
 
52
  # =======================================
53
 
54
  app = FastAPI(
55
+ title="Vocal Articulation API",
56
+ description="API for Indonesian vocal articulation assessment",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  version="2.0.0",
58
+ docs_url=None, # Disable Swagger UI temporarily
59
+ redoc_url=None, # Disable ReDoc temporarily
60
+ openapi_url=None, # Disable OpenAPI JSON
 
 
 
 
 
 
 
61
  )
62
 
63
  # CORS middleware
 
70
  )
71
 
72
  # =======================================
73
+ # PYDANTIC MODELS (Minimal)
74
  # =======================================
75
 
76
+ # Removed to reduce OpenAPI schema size
77
+ # Models are now returned as plain dicts
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  # =======================================
80
  # GLOBAL VARIABLES
 
135
  }
136
  )
137
 
138
+ @app.get("/health", tags=["System"])
139
  async def health_check():
140
+ """Health check endpoint"""
141
+ return {
142
+ "status": "healthy" if scorer is not None else "unhealthy",
143
+ "model_loaded": scorer is not None,
144
+ "device": scorer.device if scorer else "unknown",
145
+ "whisper_model": "openai/whisper-small" if scorer else "not loaded"
146
+ }
 
 
 
 
 
 
 
 
 
 
147
 
148
+ @app.get("/levels", tags=["Articulation"])
149
  async def get_levels():
150
+ """Get all articulation levels"""
151
+ return {
152
+ "levels": ARTICULATION_LEVELS,
153
+ "total_levels": len(ARTICULATION_LEVELS)
154
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
  @app.post("/score", response_class=CustomJSONResponse, tags=["Scoring"])
157
  async def score_audio(
158
+ audio: UploadFile = File(...),
159
+ target_text: str = Form(...),
160
+ level: int = Form(1)
161
  ):
162
+ """Score audio file"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  if scorer is None:
164
  raise HTTPException(status_code=503, detail="Model not loaded")
165
 
 
212
 
213
  @app.post("/batch_score", tags=["Scoring"])
214
  async def batch_score_audio(
215
+ audios: List[UploadFile] = File(...),
216
+ target_texts: str = Form(...),
217
+ levels: str = Form("1")
218
  ):
219
+ """Batch score multiple audio files"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  if scorer is None:
221
  raise HTTPException(status_code=503, detail="Model not loaded")
222