# app/app.py
from fastapi import FastAPI
from contextlib import asynccontextmanager
from typing import List
# Import centralized settings and other components
from app.config import settings
from app.core.vector_store.faiss_store import FaissVectorStore
from app.core.vector_store.embedder.factory import get_embedder_from_config
from app.core.providers.factory import get_tts_provider, get_stt_provider
from app.core.retrievers.faiss_db_retriever import FaissDBRetriever
from app.core.retrievers.base_retriever import Retriever
from app.db.session import create_db_and_tables
from app.api.routes.api import create_api_router
from app.utils import print_config
from app.api.dependencies import ServiceContainer, get_db
from app.core.services.session import SessionService
from app.core.services.tts import TTSService
from app.core.services.stt import STTService # NEW: Added the missing import for STTService
from app.core.services.workspace import WorkspaceService # NEW: Added the missing import for STTService
# Note: The llm_clients import and initialization are removed as they
# are not used in RAGService's constructor based on your services.py
# from app.core.llm_clients import DeepSeekClient, GeminiClient
from fastapi.middleware.cors import CORSMiddleware
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Manages application startup and shutdown events.
- On startup, it creates database tables.
- On shutdown, it saves the FAISS index to disk.
"""
print("Application startup...")
print_config(settings)
create_db_and_tables()
yield
print("Application shutdown...")
# Access the vector_store from the application state to save it
if hasattr(app.state, 'vector_store'):
app.state.vector_store.save_index()
def create_app() -> FastAPI:
"""
Factory function to create and configure the FastAPI application.
This encapsulates all setup logic, making the main entry point clean.
"""
app = FastAPI(
# Use metadata from the central settings
title=settings.PROJECT_NAME,
version=settings.VERSION,
description="A modular API to route requests to various LLMs with RAG capabilities.",
lifespan=lifespan
)
# --- Initialize Core Services using settings ---
# 1. Use the new, more flexible factory function to create the embedder instance
# This decouples the application from a specific embedding provider.
embedder = get_embedder_from_config(
provider=settings.EMBEDDING_PROVIDER,
dimension=settings.EMBEDDING_DIMENSION,
model_name=settings.EMBEDDING_MODEL_NAME,
api_key=settings.EMBEDDING_API_KEY
)
# 2. Initialize the FaissVectorStore with the chosen embedder
vector_store = FaissVectorStore(
index_file_path=settings.FAISS_INDEX_PATH,
dimension=settings.EMBEDDING_DIMENSION,
embedder=embedder
)
# CRITICAL FIX: Assign the vector_store to the app state so it can be saved on shutdown.
app.state.vector_store = vector_store
# 3. Create the FaissDBRetriever, regardless of the embedder type
retrievers: List[Retriever] = [
FaissDBRetriever(vector_store=vector_store),
]
# --- New TTS Initialization ---
# 4. Get the concrete TTS provider from the factory
tts_provider = get_tts_provider(
provider_name=settings.TTS_PROVIDER,
api_key=settings.TTS_API_KEY,
model_name = settings.TTS_MODEL_NAME,
voice_name=settings.TTS_VOICE_NAME
)
# 6. Get the concrete STT provider from the factory
stt_provider = get_stt_provider(
provider_name=settings.STT_PROVIDER,
api_key=settings.STT_API_KEY,
model_name=settings.STT_MODEL_NAME
)
# 9. Initialize the Service Container with all services
# This replaces the previous, redundant initialization
services = ServiceContainer()
services.with_rag_service(retrievers=retrievers)
services.with_document_service(vector_store=vector_store)
services.with_service("stt_service",service=STTService(stt_provider=stt_provider))
services.with_service("tts_service",service=TTSService(tts_provider=tts_provider))
services.with_service("workspace_service", service=WorkspaceService())
services.with_service("session_service", service=SessionService())
# Create and include the API router, injecting the service
api_router = create_api_router(services=services)
app.include_router(api_router)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # <-- This is a compromised solution should not be used in production.
# In real production, the allow origins should specified with frontend address to only allow the frontend can send request to it.
allow_credentials=True,
allow_methods=["*"], # Allows all HTTP methods (GET, POST, PUT, DELETE, etc.)
allow_headers=["*"], # Allows all headers
)
return app