add debug info
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import io
|
||||
import logging
|
||||
import mimetypes
|
||||
import re
|
||||
import wave
|
||||
@@ -12,6 +13,9 @@ import numpy as np
|
||||
from .audio_metrics import AudioAnalysisError, decode_audio_mono, speech_frame_mask
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Whisper was trained on 30-second clips. Sending longer audio in a single request
|
||||
# makes the model enter a repetition loop (every later segment becomes the same
|
||||
# sentence while timestamps keep advancing), so long audio is split into short
|
||||
@@ -93,12 +97,44 @@ class MossTranscriber:
|
||||
self, audio_path: Path, language: Optional[str]
|
||||
) -> Transcript:
|
||||
content_type = mimetypes.guess_type(audio_path.name)[0] or "application/octet-stream"
|
||||
logger.info(
|
||||
"Transcription request: endpoint=%s model=%s audio=%s language=%s",
|
||||
self.endpoint,
|
||||
self.model,
|
||||
audio_path.name,
|
||||
language or "auto",
|
||||
)
|
||||
with audio_path.open("rb") as audio_file:
|
||||
payload = self._post_audio(audio_file, audio_path.name, content_type, language)
|
||||
raw_text = str(payload.get("text") or "").strip()
|
||||
raw_segments = payload.get("segments")
|
||||
segments = _parse_json_segments(payload.get("segments"))
|
||||
logger.info(
|
||||
"Transcription response: audio=%s raw_text_chars=%d raw_segments=%s "
|
||||
"parsed_segments=%d text=%r",
|
||||
audio_path.name,
|
||||
len(raw_text),
|
||||
len(raw_segments) if isinstance(raw_segments, list) else "missing",
|
||||
len(segments),
|
||||
_log_text_preview(raw_text),
|
||||
)
|
||||
if not segments:
|
||||
segments = _parse_compact_segments(raw_text)
|
||||
if segments:
|
||||
logger.info(
|
||||
"Recovered %d compact-format segments from response text: audio=%s",
|
||||
len(segments),
|
||||
audio_path.name,
|
||||
)
|
||||
if not segments:
|
||||
logger.warning(
|
||||
"Transcription returned no usable timestamped segments: audio=%s "
|
||||
"payload_keys=%s raw_segments=%s text=%r",
|
||||
audio_path.name,
|
||||
sorted(payload.keys()),
|
||||
_log_segments_preview(raw_segments),
|
||||
_log_text_preview(raw_text, 500),
|
||||
)
|
||||
plain_text = " ".join(segment.text for segment in segments).strip()
|
||||
plain_text = _strip_speaker_tags(plain_text) or _strip_speaker_tags(raw_text)
|
||||
return Transcript(text=plain_text, segments=segments)
|
||||
@@ -117,11 +153,38 @@ class MossTranscriber:
|
||||
"audio/wav",
|
||||
language,
|
||||
)
|
||||
raw_chunk_segments = payload.get("segments")
|
||||
chunk_segments = _parse_json_segments(
|
||||
payload.get("segments"), compression_limit=COMPRESSION_RATIO_LIMIT
|
||||
)
|
||||
logger.info(
|
||||
"Chunk transcription response: range=%.3f-%.3fs raw_segments=%s "
|
||||
"parsed_segments=%d text=%r",
|
||||
start_seconds,
|
||||
end_seconds,
|
||||
len(raw_chunk_segments) if isinstance(raw_chunk_segments, list) else "missing",
|
||||
len(chunk_segments),
|
||||
_log_text_preview(str(payload.get("text") or "")),
|
||||
)
|
||||
if not chunk_segments:
|
||||
chunk_segments = _parse_compact_segments(str(payload.get("text") or "").strip())
|
||||
if chunk_segments:
|
||||
logger.info(
|
||||
"Recovered %d compact-format segments from chunk text: range=%.3f-%.3fs",
|
||||
len(chunk_segments),
|
||||
start_seconds,
|
||||
end_seconds,
|
||||
)
|
||||
if not chunk_segments:
|
||||
logger.warning(
|
||||
"Chunk returned no usable timestamped segments: range=%.3f-%.3fs "
|
||||
"payload_keys=%s raw_segments=%s text=%r",
|
||||
start_seconds,
|
||||
end_seconds,
|
||||
sorted(payload.keys()),
|
||||
_log_segments_preview(raw_chunk_segments),
|
||||
_log_text_preview(str(payload.get("text") or ""), 500),
|
||||
)
|
||||
for segment in chunk_segments:
|
||||
words = None
|
||||
if segment.words:
|
||||
@@ -172,8 +235,20 @@ class MossTranscriber:
|
||||
word_payload, audio_file, filename, content_type
|
||||
)
|
||||
if response.status_code in (400, 422):
|
||||
logger.warning(
|
||||
"Transcription endpoint rejected word timestamps; retrying without them: "
|
||||
"status=%d body=%s",
|
||||
response.status_code,
|
||||
_log_text_preview(getattr(response, "text", ""), 1000),
|
||||
)
|
||||
audio_file.seek(0)
|
||||
response = self._post_once(data, audio_file, filename, content_type)
|
||||
if response.status_code >= 400:
|
||||
logger.warning(
|
||||
"Transcription endpoint returned HTTP %d: body=%s",
|
||||
response.status_code,
|
||||
_log_text_preview(getattr(response, "text", ""), 2000),
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except (httpx.HTTPError, ValueError) as exc:
|
||||
@@ -556,6 +631,30 @@ def _wav_duration_seconds(path: Path) -> Optional[float]:
|
||||
return None
|
||||
|
||||
|
||||
def _log_text_preview(text: str, limit: int = 240) -> str:
|
||||
text = " ".join(text.split())
|
||||
return text if len(text) <= limit else f"{text[:limit]}... [{len(text)} chars]"
|
||||
|
||||
|
||||
def _log_segments_preview(raw_segments, limit: int = 5):
|
||||
if not isinstance(raw_segments, list):
|
||||
return raw_segments
|
||||
preview = []
|
||||
for item in raw_segments[:limit]:
|
||||
if not isinstance(item, dict):
|
||||
preview.append(item)
|
||||
continue
|
||||
preview.append(
|
||||
{
|
||||
"start": item.get("start"),
|
||||
"end": item.get("end"),
|
||||
"compression_ratio": item.get("compression_ratio"),
|
||||
"text": _log_text_preview(str(item.get("text") or "")),
|
||||
}
|
||||
)
|
||||
return {"count": len(raw_segments), "items": preview}
|
||||
|
||||
|
||||
def _wav_bytes(samples: np.ndarray, sample_rate: int) -> bytes:
|
||||
pcm = np.clip(np.round(samples * 32767), -32768, 32767).astype("<i2")
|
||||
buffer = io.BytesIO()
|
||||
|
||||
Reference in New Issue
Block a user