fix(talkbot): convert incoming audio to WAV before transcription
这个提交包含在:
+54
-1
@@ -3,6 +3,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -489,13 +490,59 @@ def is_blank_transcription(text: str) -> bool:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def convert_audio_to_wav(audio_bytes: bytes) -> bytes:
|
||||||
|
"""Convert any FFmpeg-supported audio format to Whisper's WAV format."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"ffmpeg",
|
||||||
|
"-hide_banner",
|
||||||
|
"-loglevel",
|
||||||
|
"error",
|
||||||
|
"-i",
|
||||||
|
"pipe:0",
|
||||||
|
"-vn",
|
||||||
|
"-ac",
|
||||||
|
"1",
|
||||||
|
"-ar",
|
||||||
|
"16000",
|
||||||
|
"-c:a",
|
||||||
|
"pcm_s16le",
|
||||||
|
"-f",
|
||||||
|
"wav",
|
||||||
|
"pipe:1",
|
||||||
|
],
|
||||||
|
input=audio_bytes,
|
||||||
|
capture_output=True,
|
||||||
|
check=False,
|
||||||
|
timeout=120,
|
||||||
|
)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
raise RuntimeError("ffmpeg is not installed in the talkbot container") from exc
|
||||||
|
except subprocess.TimeoutExpired as exc:
|
||||||
|
raise RuntimeError("Audio conversion with ffmpeg timed out") from exc
|
||||||
|
|
||||||
|
if result.returncode != 0 or not result.stdout:
|
||||||
|
error = result.stderr.decode("utf-8", errors="replace").strip()
|
||||||
|
raise RuntimeError(f"Unable to convert audio to WAV: {error[:500]}")
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"Audio converted to WAV: %s -> %s bytes",
|
||||||
|
len(audio_bytes),
|
||||||
|
len(result.stdout),
|
||||||
|
)
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
|
||||||
def transcribe(audio_bytes: bytes) -> tuple[str, str]:
|
def transcribe(audio_bytes: bytes) -> tuple[str, str]:
|
||||||
|
wav_bytes = convert_audio_to_wav(audio_bytes)
|
||||||
|
|
||||||
response = httpx.post(
|
response = httpx.post(
|
||||||
f"{WHISPER_URL}/inference",
|
f"{WHISPER_URL}/inference",
|
||||||
files={
|
files={
|
||||||
"file": (
|
"file": (
|
||||||
"audio.wav",
|
"audio.wav",
|
||||||
audio_bytes,
|
wav_bytes,
|
||||||
"audio/wav",
|
"audio/wav",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -505,6 +552,12 @@ def transcribe(audio_bytes: bytes) -> tuple[str, str]:
|
|||||||
timeout=300,
|
timeout=300,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if response.is_error:
|
||||||
|
log.error(
|
||||||
|
"Whisper returned HTTP %s: %s",
|
||||||
|
response.status_code,
|
||||||
|
response.text[:500],
|
||||||
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ USER root
|
|||||||
EXPOSE 8100
|
EXPOSE 8100
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends curl \
|
&& apt-get install -y --no-install-recommends curl ffmpeg \
|
||||||
&& apt-get autoremove -y \
|
&& apt-get autoremove -y \
|
||||||
&& apt-get clean -y \
|
&& apt-get clean -y \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户