ReplyFlow / app /main.py
nvikou's picture
Fix HF startup: JSON store, local seed, non-blocking lifespan
a6e51ba verified
Raw History Blame Contribute Delete
3.61 kB
"""ReplyFlow FastAPI application entrypoint."""
from __future__ import annotations
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from app.api import (
appointments,
chat,
conversations,
dashboard,
knowledge,
whatsapp,
)
from app.config import get_settings
from app.db import init_db
from app.schemas import HealthResponse
from app.services.rag import get_knowledge_store, ingest_directory
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("replyflow")
@asynccontextmanager
async def lifespan(_: FastAPI):
"""Initialize DB and knowledge base on startup."""
settings = get_settings()
try:
Path("data/db").mkdir(parents=True, exist_ok=True)
Path(settings.chroma_path).mkdir(parents=True, exist_ok=True)
Path(settings.knowledge_path).mkdir(
parents=True, exist_ok=True
)
await init_db()
store = get_knowledge_store()
if store.chunk_count == 0:
kb_path = Path(settings.knowledge_path)
count = await ingest_directory(kb_path)
logger.info(
"Seeded knowledge base with %s chunks", count
)
else:
logger.info(
"Knowledge base already loaded (%s chunks)",
store.chunk_count,
)
except Exception as exc: # noqa: BLE001
# Never block Hugging Face from binding port 7860.
logger.exception("Startup seed failed: %s", exc)
yield
settings = get_settings()
app = FastAPI(
title=settings.app_name,
version="1.0.1",
description=(
"Multilingual customer chatbot for web and WhatsApp "
"with RAG, lead qualification, and appointments."
),
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(chat.router)
app.include_router(appointments.router)
app.include_router(conversations.router)
app.include_router(dashboard.router)
app.include_router(knowledge.router)
app.include_router(whatsapp.router)
static_dir = Path(__file__).parent / "static"
app.mount(
"/static",
StaticFiles(directory=str(static_dir)),
name="static",
)
@app.get("/api/health", response_model=HealthResponse)
async def health() -> HealthResponse:
"""Health check and integration flags."""
store = get_knowledge_store()
return HealthResponse(
status="ok",
app=settings.app_name,
openai=settings.has_openai,
calcom=settings.has_calcom,
whatsapp=settings.has_whatsapp,
knowledge_chunks=store.chunk_count,
)
@app.get("/")
async def home() -> FileResponse:
"""Landing page."""
return FileResponse(static_dir / "index.html")
@app.get("/chat")
async def chat_page() -> FileResponse:
"""Full-page chat demo."""
return FileResponse(static_dir / "chat.html")
@app.get("/dashboard")
async def dashboard_page() -> FileResponse:
"""Operator dashboard."""
return FileResponse(static_dir / "dashboard.html")
@app.get("/widget.js")
async def widget_script() -> FileResponse:
"""Embeddable chat widget script."""
return FileResponse(
static_dir / "widget" / "replyflow-widget.js",
media_type="application/javascript",
)