openscribe/server/app/config.py
Laurence 19c3e156a0
Some checks failed
ci / firmware (pull_request) Failing after 2s
ci / openapi (pull_request) Failing after 32s
ci / emulator (pull_request) Failing after 35s
ci / server (pull_request) Failing after 29s
feat(web): Nightjar web app - Plaud-style record/transcribe/summarise UI
A runnable, self-hosted web interface so the whole workflow can be tested end to
end: record or upload audio in the browser, watch it transcribe and summarise,
browse a library, play back, and export.

What changed:
- server/app/store.py: SQLite + on-disk audio storage for recordings (stdlib only).
- server/app/pipeline.py: background task audio -> transcribe -> summarise via the
  existing provider layer, updating status (queued/transcribing/summarising/done/error).
- server/app/main.py: web API - POST upload, list, detail, audio (Range), delete, and
  export (txt/md/srt/vtt/json) - and serves the SPA at /.
- server/app/web/: Plaud-style single-page UI (index.html, styles.css, app.js). Sidebar
  library, in-browser recording (MediaRecorder) + file upload, live status polling,
  audio player, summary (overview/key points/actions), timestamped transcript, exports.
- server/Dockerfile + README: two-minute run instructions (default provider: Groq free
  tier for both Whisper + LLM), and a Docker option.
- config: env prefix switched OPENSCRIBE_ -> NIGHTJAR_ to match the brand and the site
  tutorials; .env.example rewritten with a ready Groq quick-start.
- state/TODO: web app recorded as done.

Why:
- User asked for a Plaud-like web interface to test how it all works. Nothing testable
  existed before (marketing site is a brochure; pipeline was unwired). This delivers a
  real, runnable product demo and effectively lands M5/M6 for the HTTP providers.

Notes:
- Slim by design: AI is offloaded to the configured provider, so no local ML deps needed
  for the demo. Byte-compiles clean; JS passes node --check. Local faster-whisper still
  needs its model (M5) for the fully-offline path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:57:42 +01:00

55 lines
No EOL
2.1 KiB
Python

# SPDX-License-Identifier: GPL-3.0-only
"""Server configuration, loaded from environment / .env (see .env.example).
Everything points at self-hosted services by default: local object storage, a local
Ollama, and a local faster-whisper model. Nothing here requires a proprietary cloud.
"""
from __future__ import annotations
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="NIGHTJAR_", env_file=".env")
# API
api_token: str = "change-me" # bearer token for write endpoints
# Storage: "local" | "s3" | "webdav"
storage_backend: str = "local"
local_media_dir: str = "./media"
# S3-compatible (MinIO) - used when storage_backend == "s3"
s3_endpoint: str = "http://localhost:9000"
s3_bucket: str = "openscribe"
s3_access_key: str = ""
s3_secret_key: str = ""
# Metadata DB (SQLite to start; Postgres URL later)
database_url: str = "sqlite:///./openscribe.db"
# --- AI providers (see docs/ai-providers.md) --------------------------------------
# Transcription provider: "local_whisper" (self-hosted) | "openai_compatible"
transcription_provider: str = "local_whisper"
transcription_base_url: str = "" # e.g. https://api.openai.com/v1 or a Groq/local URL
transcription_api_key: str = ""
transcription_model: str = "" # e.g. whisper-1, whisper-large-v3
# LLM provider for summaries + Ask-AI:
# "ollama" (default, self-hosted) | "openai_compatible" | "anthropic"
llm_provider: str = "ollama"
llm_base_url: str = "https://api.openai.com/v1" # used by openai_compatible
llm_api_key: str = ""
llm_model: str = "" # per-provider default applied if empty
# Local faster-whisper (used when transcription_provider == local_whisper)
whisper_model: str = "base" # tiny|base|small|medium|large-v3
whisper_device: str = "cpu" # cpu|cuda
whisper_compute_type: str = "int8"
# Local Ollama (used when llm_provider == ollama)
ollama_url: str = "http://localhost:11434"
ollama_model: str = "llama3.1"
settings = Settings()