Spaces:
Running
Running
| """ | |
| VoiceVault β FastAPI Server | |
| ============================ | |
| Custom web frontend entry point. Serves the HTML/CSS/JS SPA and REST API. | |
| Run: python server.py | |
| URL: http://localhost:7860 | |
| """ | |
| from __future__ import annotations | |
| # ββ Environment overrides (must be first, before any ML imports) ββββββββ | |
| import os | |
| # RTX 5070 (sm_120) incompatible with packaged PyTorch β force CPU | |
| os.environ["CUDA_VISIBLE_DEVICES"] = "-1" | |
| # Suppress Windows symlink warning from huggingface_hub cache | |
| os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1" | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| import logging | |
| import sys | |
| from contextlib import asynccontextmanager | |
| from pathlib import Path | |
| import uvicorn | |
| from fastapi import FastAPI | |
| from fastapi.responses import FileResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from config import cfg | |
| from voicevault import __version__ | |
| # ------------------------------------------------------------------ # | |
| # Logging # | |
| # ------------------------------------------------------------------ # | |
| logging.basicConfig( | |
| level=logging.DEBUG if cfg.debug else logging.INFO, | |
| format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", | |
| datefmt="%Y-%m-%d %H:%M:%S", | |
| handlers=[logging.StreamHandler(sys.stdout)], | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Suppress noisy third-party loggers that spam the console | |
| for _noisy in ("httpx", "httpcore", "huggingface_hub", "sentence_transformers", | |
| "transformers", "filelock", "urllib3"): | |
| logging.getLogger(_noisy).setLevel(logging.WARNING) | |
| _CENTRAL_DB_PATH = cfg.data_dir / "voicevault.db" | |
| _STATIC_DIR = Path(__file__).parent / "static" | |
| # ------------------------------------------------------------------ # | |
| # Startup # | |
| # ------------------------------------------------------------------ # | |
| def _startup(): | |
| """Initialize all pipeline components.""" | |
| cfg.ensure_directories() | |
| logger.info("=" * 60) | |
| logger.info("VoiceVault v%s β FastAPI Server Starting", __version__) | |
| logger.info("Data directory : %s", cfg.data_dir.resolve()) | |
| logger.info("Central DB : %s", _CENTRAL_DB_PATH) | |
| logger.info("Groq key : %s", "β configured" if cfg.has_groq_key() else "β not set") | |
| logger.info("Gemini key : %s", "β configured" if cfg.has_gemini_key() else "β not set") | |
| logger.info("Static dir : %s", _STATIC_DIR.resolve()) | |
| logger.info("=" * 60) | |
| if not cfg.has_any_llm_key(): | |
| logger.warning("No LLM API key found β set GROQ_API_KEY or GEMINI_API_KEY in .env") | |
| from voicevault.kb.kb_manager import KBManager | |
| kb_manager = KBManager(db_path=_CENTRAL_DB_PATH) | |
| logger.info("KBManager ready β %d KB(s) found", len(kb_manager.list_kbs())) | |
| # Prefer Groq Whisper API (instant, ~200ms) over local model (slow CPU) | |
| if cfg.has_groq_key(): | |
| from voicevault.asr.groq_transcriber import GroqTranscriber | |
| transcriber = GroqTranscriber() | |
| logger.info("Transcriber: Groq Whisper API (whisper-large-v3-turbo) β fast cloud mode") | |
| else: | |
| from voicevault.asr.whisper_transcriber import WhisperTranscriber | |
| transcriber = WhisperTranscriber() | |
| logger.info("Transcriber: local Whisper (CPU) β set GROQ_API_KEY for instant transcription") | |
| from voicevault.generation.answer_chain import AnswerChain | |
| answer_chain = AnswerChain() | |
| logger.info("AnswerChain ready (Groq β Gemini fallback)") | |
| return kb_manager, transcriber, answer_chain | |
| # ------------------------------------------------------------------ # | |
| # App Factory # | |
| # ------------------------------------------------------------------ # | |
| async def _lifespan(app: FastAPI): | |
| """Initialize pipeline singletons before the server starts accepting requests.""" | |
| kb_manager, transcriber, answer_chain = _startup() | |
| from api.routes import init_routes | |
| init_routes(kb_manager, transcriber, answer_chain, _CENTRAL_DB_PATH) | |
| logger.info("All routes initialized. App is ready.") | |
| yield | |
| # (shutdown hooks go here if needed) | |
| def create_app() -> FastAPI: | |
| app = FastAPI( | |
| title="VoiceVault", | |
| version=__version__, | |
| docs_url=None, | |
| redoc_url=None, | |
| lifespan=_lifespan, | |
| ) | |
| # Serve static assets | |
| app.mount("/static", StaticFiles(directory=str(_STATIC_DIR)), name="static") | |
| from api.routes import router | |
| app.include_router(router) | |
| async def index(): | |
| return FileResponse(_STATIC_DIR / "index.html") | |
| return app | |
| # ------------------------------------------------------------------ # | |
| # Entry Point # | |
| # ------------------------------------------------------------------ # | |
| app = create_app() | |
| if __name__ == "__main__": | |
| uvicorn.run( | |
| "server:app", | |
| host=cfg.host, | |
| port=cfg.port, | |
| reload=False, | |
| log_level="info", | |
| ) | |