From 75a73559b57f06256d71235b52eb781c127f69ac Mon Sep 17 00:00:00 2001 From: Mahdi Date: Tue, 22 Sep 2026 12:04:35 +0330 Subject: [PATCH] Modification of the structure --- apps/stt/.env.example | 11 +++-- apps/stt/main.py | 44 ++++++++++++++----- apps/web/src/components/VoiceToTextButton.vue | 6 ++- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/apps/stt/.env.example b/apps/stt/.env.example index 5a738af..7e1b3f7 100644 --- a/apps/stt/.env.example +++ b/apps/stt/.env.example @@ -1,11 +1,10 @@ -# Local Faster-Whisper (same engine as Pipecat local STT) — no GapGPT. -# Sizes: tiny | base | small | medium | large-v3 -# Do NOT use cloud names like whisper-1 -# On low-RAM servers prefer: base or tiny -STT_MODEL=base +# Local Faster-Whisper — no GapGPT. +# Persian dictation needs at least small (base/tiny are too inaccurate). +# medium is better if the server has RAM. +STT_MODEL=small STT_DEVICE=cpu STT_COMPUTE_TYPE=int8 -STT_PROMPT=یادداشت و مکالمهٔ فروش به زبان فارسی. اسامی مشتریان و اصطلاحات کسب‌وکار را دقیق بنویس. +STT_PROMPT=این یک یادداشت فارسی است. لطفاً این مورد را بررسی کنید. STT_HOST=127.0.0.1 STT_PORT=3040 diff --git a/apps/stt/main.py b/apps/stt/main.py index 451ea8a..d4eea58 100644 --- a/apps/stt/main.py +++ b/apps/stt/main.py @@ -46,20 +46,21 @@ def _require_token(header_token: str | None) -> None: def _model_name() -> str: """Map env / legacy cloud names to Faster-Whisper local sizes.""" - raw = (_env("STT_MODEL", "base") or "base").lower() + raw = (_env("STT_MODEL", "small") or "small").lower() legacy = { - "whisper-1": "base", - "whisper": "base", + "whisper-1": "small", + "whisper": "small", "gpt-4o-transcribe": "small", - "gpt-4o-mini-transcribe": "base", + "gpt-4o-mini-transcribe": "small", + # tiny/base are too weak for Persian dictation + "tiny": "small", + "tiny.en": "small", + "base": "small", + "base.en": "small", } if raw in legacy: return legacy[raw] allowed = { - "tiny", - "tiny.en", - "base", - "base.en", "small", "small.en", "medium", @@ -73,7 +74,7 @@ def _model_name() -> str: "distil-large-v3", "large-v3-turbo", } - return raw if raw in allowed else "base" + return raw if raw in allowed else "small" def _get_model() -> WhisperModel: @@ -211,19 +212,38 @@ def _ensure_16k(wav_bytes: bytes) -> np.ndarray: return _resample_audio(audio, rate, 16000) +def _normalize(audio: np.ndarray) -> np.ndarray: + if audio.size == 0: + return audio + peak = float(np.max(np.abs(audio))) + if peak < 1e-4: + return audio + # Bring quiet mic levels up without clipping + gain = min(8.0, 0.9 / peak) + if gain <= 1.05: + return audio + return np.clip(audio * gain, -1.0, 1.0).astype(np.float32) + + def _transcribe_local(wav_bytes: bytes) -> str: model = _get_model() - audio = _ensure_16k(wav_bytes) + audio = _normalize(_ensure_16k(wav_bytes)) initial_prompt = _env( "STT_PROMPT", - "یادداشت و مکالمهٔ فروش به زبان فارسی. اسامی مشتریان و اصطلاحات کسب‌وکار را دقیق بنویس.", + "این یک یادداشت فارسی است. لطفاً این مورد را بررسی کنید.", ) or None segments, _info = model.transcribe( audio, language="fa", + task="transcribe", initial_prompt=initial_prompt, - vad_filter=True, + beam_size=5, + best_of=5, + temperature=0.0, + condition_on_previous_text=False, + vad_filter=False, + no_speech_threshold=0.45, ) parts = [seg.text.strip() for seg in segments if getattr(seg, "text", None)] return " ".join(p for p in parts if p).strip() diff --git a/apps/web/src/components/VoiceToTextButton.vue b/apps/web/src/components/VoiceToTextButton.vue index 3342e06..72f9611 100644 --- a/apps/web/src/components/VoiceToTextButton.vue +++ b/apps/web/src/components/VoiceToTextButton.vue @@ -244,7 +244,11 @@ async function start() { stream = null; return; } - ctx = new Ctor(); + try { + ctx = new Ctor({ sampleRate: 16000 }); + } catch { + ctx = new Ctor(); + } await ctx.resume(); sampleRate = ctx.sampleRate || 16000; source = ctx.createMediaStreamSource(stream);