MyPal / app /api /deps.py
KhaledSalehKL1's picture
Deploy 9XAIPal: Gradio+FastAPI app, backend, React build
1086e43 verified
Raw
History Blame Contribute Delete
2.16 kB
"""FastAPI dependencies."""
import os
from typing import AsyncIterator
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.database.connection import async_session_factory
from app.core.config import Settings, settings
async def get_db() -> AsyncIterator[AsyncSession]:
"""Yield a database session per request."""
async with async_session_factory() as session:
yield session
def get_settings() -> Settings:
"""Return app settings."""
return settings
# ------------------------------------------------------------------
# Lightweight concurrency limiter for the expensive /ask path.
# On "your computer = server" with multiple users (or even one user with
# many tabs/sub-threads + research), we do not want 10+ simultaneous
# LLM calls (router + research rounds + synthesis) all hitting the same
# Ollama instance at once. This causes OOM, extreme latency, or GPU
# thrashing.
#
# Each uvicorn worker gets its own semaphore (with --workers 2 this gives
# reasonable headroom). Excess requests are queued by the semaphore.
# Tune via MAX_CONCURRENT_ASKS in the environment / .env.
# ------------------------------------------------------------------
import asyncio
from typing import AsyncIterator
_max_concurrent_asks = int(os.getenv("MAX_CONCURRENT_ASKS", "3"))
_ask_semaphore = asyncio.Semaphore(_max_concurrent_asks)
async def get_ask_limiter() -> AsyncIterator[None]:
"""FastAPI dependency that limits concurrent /ask executions.
Must be a plain async generator (NOT @asynccontextmanager): FastAPI wraps
the generator into a context manager itself. Decorating it again returns
an _AsyncGeneratorContextManager that FastAPI then tries to use as an
async iterator, which raises TypeError at dependency resolution time.
"""
async with _ask_semaphore:
yield
def get_ask_semaphore() -> asyncio.Semaphore:
"""The shared /ask concurrency limiter, for callers that must hold it
beyond the Depends lifecycle (the SSE streaming endpoint acquires it
inside its response generator, which runs after dependencies close)."""
return _ask_semaphore