OpenAI-compatible audio transcription endpoint. Accepts the same parameters as OpenAI's /v1/audio/transcriptions: - file: Audio file (wav, mp3, flac, m4a, ogg, webm) - model: Model to use (sensevoice, paraformer, fun-asr-nano) - language: Optional language hint - respon
(
file: UploadFile = File(...),
model: str = Form(default="sensevoice"),
language: Optional[str] = Form(default=None),
response_format: Optional[str] = Form(default="json"),
)
| 89 | |
| 90 | @app.post("/v1/audio/transcriptions") |
| 91 | async def transcribe( |
| 92 | file: UploadFile = File(...), |
| 93 | model: str = Form(default="sensevoice"), |
| 94 | language: Optional[str] = Form(default=None), |
| 95 | response_format: Optional[str] = Form(default="json"), |
| 96 | ): |
| 97 | """ |
| 98 | OpenAI-compatible audio transcription endpoint. |
| 99 | |
| 100 | Accepts the same parameters as OpenAI's /v1/audio/transcriptions: |
| 101 | - file: Audio file (wav, mp3, flac, m4a, ogg, webm) |
| 102 | - model: Model to use (sensevoice, paraformer, fun-asr-nano) |
| 103 | - language: Optional language hint |
| 104 | - response_format: json or verbose_json |
| 105 | """ |
| 106 | # Validate model |
| 107 | if model not in MODEL_CONFIGS: |
| 108 | raise HTTPException( |
| 109 | status_code=400, |
| 110 | detail=f"Model '{model}' not found. Available: {list(MODEL_CONFIGS.keys())}" |
| 111 | ) |
| 112 | |
| 113 | # Save uploaded file |
| 114 | suffix = os.path.splitext(file.filename)[1] if file.filename else ".wav" |
| 115 | with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp: |
| 116 | content = await file.read() |
| 117 | tmp.write(content) |
| 118 | tmp_path = tmp.name |
| 119 | |
| 120 | try: |
| 121 | asr_model = load_model(model) |
| 122 | t0 = time.time() |
| 123 | |
| 124 | generate_kwargs = {"input": tmp_path, "batch_size": 1} |
| 125 | if language: |
| 126 | generate_kwargs["language"] = language |
| 127 | |
| 128 | result = asr_model.generate(**generate_kwargs) |
| 129 | elapsed = time.time() - t0 |
| 130 | |
| 131 | text = clean_text(result[0]["text"]) |
| 132 | |
| 133 | if response_format == "verbose_json": |
| 134 | segments = [] |
| 135 | if "sentence_info" in result[0]: |
| 136 | for seg in result[0]["sentence_info"]: |
| 137 | segments.append({ |
| 138 | "start": seg.get("start", 0) / 1000.0, |
| 139 | "end": seg.get("end", 0) / 1000.0, |
| 140 | "text": clean_text(seg.get("text", "")), |
| 141 | "speaker": seg.get("spk", None), |
| 142 | }) |
| 143 | return JSONResponse({ |
| 144 | "text": text, |
| 145 | "segments": segments, |
| 146 | "language": language or "auto", |
| 147 | "duration": round(elapsed, 3), |
| 148 | "model": model, |
nothing calls this directly
no test coverage detected
searching dependent graphs…