Skip to content
This repository was archived by the owner on Apr 24, 2026. It is now read-only.

feat: enable Whisper STT locally (medium.en)#5

Merged
jaewilson07 merged 7 commits into
mainfrom
feat/config-yaml-settings
Apr 21, 2026
Merged

feat: enable Whisper STT locally (medium.en)#5
jaewilson07 merged 7 commits into
mainfrom
feat/config-yaml-settings

Conversation

@jaewilson07

Copy link
Copy Markdown
Owner

Summary

  • Set whisper.enabled: true in config.yaml — Whisper was installed but disabled
  • medium.en model, auto device (uses GPU if available, falls back to CPU)
  • config.yaml is volume-mounted so no image rebuild needed; just docker compose restart api

Test plan

  • curl http://localhost:7625/v1/configstt_enabled: true
  • Click "start dictating" on the website — voice button enabled, recording indicator appears
  • Speak — transcript appears in editor, diagram auto-generates

🤖 Generated with Claude Code

jaewilson07 and others added 4 commits April 19, 2026 23:04
Ensures containers recover automatically after crashes or Docker
restarts, supporting Docker Desktop auto-start on Windows login.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
whisper.enabled/model/language/device, ollama.default_model, openai.model,
and paths now live in config.yaml (volume-mounted, editable without rebuild).
.env reduced to secrets only (OLLAMA_URL). Env var overrides still work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck, WHISPER_DEVICE wired, default enabled=false
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 21, 2026 15:22
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables local Whisper STT configuration via config.yaml (including medium.en + auto device selection) and improves container resiliency with restart policies.

Changes:

  • Move non-secret runtime settings (models, Whisper settings, paths, model filter) from environment variables to backend/config.yaml.
  • Add Whisper device selection and handle missing STT dependency imports during app startup.
  • Add restart: unless-stopped to ollama and api services in Docker Compose.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
voice-to-mermaid/backend/main.py Loads settings from config.yaml, adds Whisper device config, and attempts graceful handling when STT deps aren’t installed.
voice-to-mermaid/backend/docker-compose.yml Adds restart policies for ollama and api.
voice-to-mermaid/backend/config.yaml Defines defaults for Ollama/OpenAI/Whisper and paths; enables Whisper by default.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +262 to +265
except ImportError:
log.warning("whisper.enabled=true but whisperlivekit not installed — STT disabled. Install requirements.stt.txt to enable.")
yield
return

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If whisper.enabled is true but whisperlivekit isn’t installed, this branch yields/returns but leaves WHISPER_ENABLED as True. Other code paths (e.g. the WebSocket handler’s conditional Whisper imports) can still crash at runtime, and /v1/config will still report stt_enabled: true. Consider introducing a runtime flag like _stt_available (set false on ImportError) or flipping WHISPER_ENABLED to false here and guarding all Whisper imports/usage on that flag.

Copilot uses AI. Check for mistakes.
Comment thread voice-to-mermaid/backend/main.py Outdated
Comment on lines +74 to +88
# Secrets — env only
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434").rstrip("/")
OPENAI_URL = os.environ.get("OPENAI_BASE_URL", "").rstrip("/")
OPENAI_KEY = os.environ.get("OPENAI_API_KEY", "")

# Settings — config.yaml only; edit config.yaml to change these
OLLAMA_MODEL = _cfg.get("ollama", {}).get("default_model", "qwen3:8b")
OPENAI_MODEL = _cfg.get("openai", {}).get("model", "gpt-4o-mini")
WHISPER_ENABLED = _cfg.get("whisper", {}).get("enabled", False)
WHISPER_MODEL = _cfg.get("whisper", {}).get("model", "medium.en")
WHISPER_LANG = _cfg.get("whisper", {}).get("language", "en")
WHISPER_DEVICE = _cfg.get("whisper", {}).get("device", "auto")
PROMPT_PATH = Path(_cfg.get("paths", {}).get("prompt", "prompts/mermaid.txt"))
LOG_DIR = Path(_cfg.get("paths", {}).get("log_dir", "data/logs"))
_MODEL_FILTER: list[str] = [str(p) for p in (_cfg.get("ollama", {}).get("model_filter") or [])]

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These reads assume nested config sections are mappings (e.g. _cfg.get("ollama", {}).get(...)). Since config.yaml is intended to be user-edited, a malformed section like ollama: "..." will raise at import time (AttributeError: 'str' object has no attribute 'get') and prevent startup. Consider validating/coercing each subsection (ollama/openai/whisper/paths) to a dict (or using a small schema/dataclass) before reading nested keys.

Suggested change
# Secrets — env only
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434").rstrip("/")
OPENAI_URL = os.environ.get("OPENAI_BASE_URL", "").rstrip("/")
OPENAI_KEY = os.environ.get("OPENAI_API_KEY", "")
# Settings — config.yaml only; edit config.yaml to change these
OLLAMA_MODEL = _cfg.get("ollama", {}).get("default_model", "qwen3:8b")
OPENAI_MODEL = _cfg.get("openai", {}).get("model", "gpt-4o-mini")
WHISPER_ENABLED = _cfg.get("whisper", {}).get("enabled", False)
WHISPER_MODEL = _cfg.get("whisper", {}).get("model", "medium.en")
WHISPER_LANG = _cfg.get("whisper", {}).get("language", "en")
WHISPER_DEVICE = _cfg.get("whisper", {}).get("device", "auto")
PROMPT_PATH = Path(_cfg.get("paths", {}).get("prompt", "prompts/mermaid.txt"))
LOG_DIR = Path(_cfg.get("paths", {}).get("log_dir", "data/logs"))
_MODEL_FILTER: list[str] = [str(p) for p in (_cfg.get("ollama", {}).get("model_filter") or [])]
def _config_section(name: str) -> dict:
section = _cfg.get(name, {})
if isinstance(section, dict):
return section
log.warning("config.%s is not a mapping — using defaults for that section", name)
return {}
_ollama_cfg = _config_section("ollama")
_openai_cfg = _config_section("openai")
_whisper_cfg = _config_section("whisper")
_paths_cfg = _config_section("paths")
# Secrets — env only
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434").rstrip("/")
OPENAI_URL = os.environ.get("OPENAI_BASE_URL", "").rstrip("/")
OPENAI_KEY = os.environ.get("OPENAI_API_KEY", "")
# Settings — config.yaml only; edit config.yaml to change these
OLLAMA_MODEL = _ollama_cfg.get("default_model", "qwen3:8b")
OPENAI_MODEL = _openai_cfg.get("model", "gpt-4o-mini")
WHISPER_ENABLED = _whisper_cfg.get("enabled", False)
WHISPER_MODEL = _whisper_cfg.get("model", "medium.en")
WHISPER_LANG = _whisper_cfg.get("language", "en")
WHISPER_DEVICE = _whisper_cfg.get("device", "auto")
PROMPT_PATH = Path(_paths_cfg.get("prompt", "prompts/mermaid.txt"))
LOG_DIR = Path(_paths_cfg.get("log_dir", "data/logs"))
_MODEL_FILTER: list[str] = [str(p) for p in (_ollama_cfg.get("model_filter") or [])]

Copilot uses AI. Check for mistakes.
model: "gpt-4o-mini"

whisper:
enabled: true

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whisper.enabled is set to true by default, but the Docker image (Dockerfile installs only requirements.txt) doesn’t include whisperlivekit/faster-whisper. In the default docker compose up flow this will result in STT being effectively unavailable (and currently can still cause runtime import failures unless additionally guarded). Either ship STT deps in the image (e.g., install requirements.stt.txt behind a build arg/profile) or default whisper.enabled to false and document the steps needed to enable STT locally.

Suggested change
enabled: true
enabled: false # Enable only when whisperlivekit/faster-whisper dependencies are installed in the environment/image.

Copilot uses AI. Check for mistakes.
Comment thread voice-to-mermaid/backend/main.py Outdated
Comment on lines +79 to +82
# Settings — config.yaml only; edit config.yaml to change these
OLLAMA_MODEL = _cfg.get("ollama", {}).get("default_model", "qwen3:8b")
OPENAI_MODEL = _cfg.get("openai", {}).get("model", "gpt-4o-mini")
WHISPER_ENABLED = _cfg.get("whisper", {}).get("enabled", False)

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By switching these settings to config.yaml only, existing setup docs/samples that instruct users to set .env values like OLLAMA_MODEL, OPENAI_MODEL, WHISPER_MODEL/DEVICE, MERMAID_PROMPT_PATH, and model filtering will now be incorrect (those env vars are ignored). If this is intentional, please update README.md and backend/.env.example accordingly (or reintroduce env overrides) to avoid confusing deployments.

Copilot uses AI. Check for mistakes.
jaewilson07 and others added 3 commits April 21, 2026 09:38
…nabled in /v1/config

Address Copilot review on PR #5:
- Add _config_section() helper to guard against non-dict config sections
- Replace bare _cfg.get("x", {}).get() chains with per-section dicts
- Introduce _stt_available flag (False until WhisperLiveKit loads); stt_enabled in
  /v1/config now reflects actual runtime state instead of config intent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d _stt_available flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…an up .env.example and README

- config.yaml: add comment explaining whisper.enabled=true is safe (graceful failure via _stt_available)
- .env.example: remove OLLAMA_MODEL, WHISPER_MODEL/DEVICE, MERMAID_PROMPT_PATH (moved to config.yaml)
- README: split env table into config.yaml section and .env secrets-only section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jaewilson07
jaewilson07 merged commit af20f6d into main Apr 21, 2026
2 checks passed
@jaewilson07
jaewilson07 deleted the feat/config-yaml-settings branch April 21, 2026 16:07
jaewilson07 added a commit that referenced this pull request Apr 21, 2026
…eway (#6)

* fix: add restart: unless-stopped to vtm-api and ollama services

Ensures containers recover automatically after crashes or Docker
restarts, supporting Docker Desktop auto-start on Windows login.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: move non-secret settings from .env to config.yaml

whisper.enabled/model/language/device, ollama.default_model, openai.model,
and paths now live in config.yaml (volume-mounted, editable without rebuild).
.env reduced to secrets only (OLLAMA_URL). Env var overrides still work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address PR review comments — dict validation, ImportError fallback, WHISPER_DEVICE wired, default enabled=false

* feat: enable whisper STT locally (medium.en, auto device)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: _stt_available flag, _config_section() safe reads, correct stt_enabled in /v1/config

Address Copilot review on PR #5:
- Add _config_section() helper to guard against non-dict config sections
- Replace bare _cfg.get("x", {}).get() chains with per-section dicts
- Introduce _stt_available flag (False until WhisperLiveKit loads); stt_enabled in
  /v1/config now reflects actual runtime state instead of config intent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: address PR review comments — clarify config.yaml ownership, clean up .env.example and README

- config.yaml: add comment explaining whisper.enabled=true is safe (graceful failure via _stt_available)
- .env.example: remove OLLAMA_MODEL, WHISPER_MODEL/DEVICE, MERMAID_PROMPT_PATH (moved to config.yaml)
- README: split env table into config.yaml section and .env secrets-only section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use _stt_available (not WHISPER_ENABLED) in ws_mermaid, host Ollama via host-gateway

- ws_mermaid: replace WHISPER_ENABLED guards with _stt_available so WS works
  when whisper config is enabled but import failed (Docker without STT deps)
- docker-compose: remove hard-coded OLLAMA_URL override; add extra_hosts for
  host.docker.internal so .env can point at host Ollama

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: restore OLLAMA_URL default for bundled ollama, align whisper.enabled default

- docker-compose: restore OLLAMA_URL default (http://ollama:11434) via
  ${OLLAMA_URL:-http://ollama:11434} so docker-compose up works out of the box;
  .env still overrides it (host.docker.internal:11434 for host Ollama)
- .env.example: use http://ollama:11434 as default with host Ollama commented variant
- config.yaml: revert whisper.enabled to false (safe default; README already said false)
- README: align whisper.enabled description and OLLAMA_URL default with actual shipped values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
jaewilson07 added a commit that referenced this pull request Apr 21, 2026
* fix: add restart: unless-stopped to vtm-api and ollama services

Ensures containers recover automatically after crashes or Docker
restarts, supporting Docker Desktop auto-start on Windows login.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: move non-secret settings from .env to config.yaml

whisper.enabled/model/language/device, ollama.default_model, openai.model,
and paths now live in config.yaml (volume-mounted, editable without rebuild).
.env reduced to secrets only (OLLAMA_URL). Env var overrides still work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address PR review comments — dict validation, ImportError fallback, WHISPER_DEVICE wired, default enabled=false

* feat: enable whisper STT locally (medium.en, auto device)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: _stt_available flag, _config_section() safe reads, correct stt_enabled in /v1/config

Address Copilot review on PR #5:
- Add _config_section() helper to guard against non-dict config sections
- Replace bare _cfg.get("x", {}).get() chains with per-section dicts
- Introduce _stt_available flag (False until WhisperLiveKit loads); stt_enabled in
  /v1/config now reflects actual runtime state instead of config intent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: address PR review comments — clarify config.yaml ownership, clean up .env.example and README

- config.yaml: add comment explaining whisper.enabled=true is safe (graceful failure via _stt_available)
- .env.example: remove OLLAMA_MODEL, WHISPER_MODEL/DEVICE, MERMAID_PROMPT_PATH (moved to config.yaml)
- README: split env table into config.yaml section and .env secrets-only section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use _stt_available (not WHISPER_ENABLED) in ws_mermaid, host Ollama via host-gateway

- ws_mermaid: replace WHISPER_ENABLED guards with _stt_available so WS works
  when whisper config is enabled but import failed (Docker without STT deps)
- docker-compose: remove hard-coded OLLAMA_URL override; add extra_hosts for
  host.docker.internal so .env can point at host Ollama

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: restore OLLAMA_URL default for bundled ollama, align whisper.enabled default

- docker-compose: restore OLLAMA_URL default (http://ollama:11434) via
  ${OLLAMA_URL:-http://ollama:11434} so docker-compose up works out of the box;
  .env still overrides it (host.docker.internal:11434 for host Ollama)
- .env.example: use http://ollama:11434 as default with host Ollama commented variant
- config.yaml: revert whisper.enabled to false (safe default; README already said false)
- README: align whisper.enabled description and OLLAMA_URL default with actual shipped values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(vtm): install STT deps in Docker image, enable whisper in config

Dockerfile: install requirements.stt.txt (whisperlivekit + faster-whisper)
  so _stt_available=True at runtime and /v1/config returns stt_enabled: true
config.yaml: whisper.enabled=true — deps now in image; graceful no-op if import fails

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address Copilot review comments on STT PR

- Move AudioProcessor import next to instantiation site (not function top)
- Don't cache empty Ollama model list — retry on next request if Ollama unreachable at startup
- Update README whisper.enabled default to true (matches config.yaml and Dockerfile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants