feat(dependencies): update database engine dependencies to support async operations
This commit is contained in:
@@ -6,7 +6,6 @@ from typing import Annotated
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
||||||
|
|
||||||
from python.ebook_search.config import EbookSearchConfig
|
from python.ebook_search.config import EbookSearchConfig
|
||||||
|
|
||||||
@@ -16,16 +15,10 @@ def get_config(request: Request) -> EbookSearchConfig:
|
|||||||
return request.app.state.config
|
return request.app.state.config
|
||||||
|
|
||||||
|
|
||||||
def get_engine(request: Request) -> AsyncEngine:
|
|
||||||
"""Get the database engine from app state."""
|
|
||||||
return request.app.state.engine
|
|
||||||
|
|
||||||
|
|
||||||
def get_http_client(request: Request) -> httpx.AsyncClient:
|
def get_http_client(request: Request) -> httpx.AsyncClient:
|
||||||
"""Get the shared LLM HTTP client from app state."""
|
"""Get the shared LLM HTTP client from app state."""
|
||||||
return request.app.state.http_client
|
return request.app.state.http_client
|
||||||
|
|
||||||
|
|
||||||
AppConfig = Annotated[EbookSearchConfig, Depends(get_config)]
|
AppConfig = Annotated[EbookSearchConfig, Depends(get_config)]
|
||||||
AppEngine = Annotated[AsyncEngine, Depends(get_engine)]
|
|
||||||
AppHttpClient = Annotated[httpx.AsyncClient, Depends(get_http_client)]
|
AppHttpClient = Annotated[httpx.AsyncClient, Depends(get_http_client)]
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from fastapi.responses import HTMLResponse
|
|||||||
from python.ebook_search.api.bm25_tasks import schedule_bm25_refresh
|
from python.ebook_search.api.bm25_tasks import schedule_bm25_refresh
|
||||||
from python.ebook_search.api.dependencies import ( # noqa: TC001 FastAPI resolves these annotated dependencies at runtime
|
from python.ebook_search.api.dependencies import ( # noqa: TC001 FastAPI resolves these annotated dependencies at runtime
|
||||||
AppConfig,
|
AppConfig,
|
||||||
AppEngine,
|
|
||||||
AppHttpClient,
|
AppHttpClient,
|
||||||
)
|
)
|
||||||
from python.ebook_search.api.web import error_response, templates
|
from python.ebook_search.api.web import error_response, templates
|
||||||
@@ -19,7 +18,10 @@ from python.ebook_search.ingest import ingest_configured_paths
|
|||||||
from python.ebook_search.protected_phrases.generate_ngrams import generate_candidate_phrases_for_books
|
from python.ebook_search.protected_phrases.generate_ngrams import generate_candidate_phrases_for_books
|
||||||
from python.ebook_search.protected_phrases.judge_ngrams import judge_candidate_phrases_for_books
|
from python.ebook_search.protected_phrases.judge_ngrams import judge_candidate_phrases_for_books
|
||||||
from python.ebook_search.protected_phrases.store import book_ids_pending_first_judgment, corpus_phrase_stats
|
from python.ebook_search.protected_phrases.store import book_ids_pending_first_judgment, corpus_phrase_stats
|
||||||
from python.fastapi_tools import AsyncDbSession # noqa: TC001 FastAPI resolves this annotated dependency at runtime
|
from python.fastapi_tools import ( # noqa: TC001 FastAPI resolves these annotated dependencies at runtime
|
||||||
|
AppAsyncEngine,
|
||||||
|
AsyncDbSession,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -59,7 +61,7 @@ async def scan_library(request: Request, config: AppConfig, session: AsyncDbSess
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/phrases/generate-all", response_class=HTMLResponse)
|
@router.post("/phrases/generate-all", response_class=HTMLResponse)
|
||||||
async def generate_all_phrases(request: Request, config: AppConfig, engine: AppEngine) -> HTMLResponse:
|
async def generate_all_phrases(request: Request, config: AppConfig, engine: AppAsyncEngine) -> HTMLResponse:
|
||||||
"""Regenerate candidate phrases for every indexed book without LLM judging."""
|
"""Regenerate candidate phrases for every indexed book without LLM judging."""
|
||||||
try:
|
try:
|
||||||
result = await generate_candidate_phrases_for_books(engine, config)
|
result = await generate_candidate_phrases_for_books(engine, config)
|
||||||
@@ -83,7 +85,7 @@ async def generate_all_phrases(request: Request, config: AppConfig, engine: AppE
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/phrases/judge-all", response_class=HTMLResponse)
|
@router.post("/phrases/judge-all", response_class=HTMLResponse)
|
||||||
async def judge_all_phrases(request: Request, engine: AppEngine, config: AppConfig) -> HTMLResponse:
|
async def judge_all_phrases(request: Request, engine: AppAsyncEngine, config: AppConfig) -> HTMLResponse:
|
||||||
"""Judge unjudged candidate phrases across every indexed book."""
|
"""Judge unjudged candidate phrases across every indexed book."""
|
||||||
return await run_phrase_judgment(request, engine, config, source_ids=None)
|
return await run_phrase_judgment(request, engine, config, source_ids=None)
|
||||||
|
|
||||||
@@ -91,7 +93,7 @@ async def judge_all_phrases(request: Request, engine: AppEngine, config: AppConf
|
|||||||
@router.post("/phrases/judge-missing", response_class=HTMLResponse)
|
@router.post("/phrases/judge-missing", response_class=HTMLResponse)
|
||||||
async def judge_missing_phrases(
|
async def judge_missing_phrases(
|
||||||
request: Request,
|
request: Request,
|
||||||
engine: AppEngine,
|
engine: AppAsyncEngine,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
session: AsyncDbSession,
|
session: AsyncDbSession,
|
||||||
) -> HTMLResponse:
|
) -> HTMLResponse:
|
||||||
@@ -108,7 +110,7 @@ async def judge_missing_phrases(
|
|||||||
|
|
||||||
async def run_phrase_judgment(
|
async def run_phrase_judgment(
|
||||||
request: Request,
|
request: Request,
|
||||||
engine: AppEngine,
|
engine: AppAsyncEngine,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
*,
|
*,
|
||||||
source_ids: list[int] | None,
|
source_ids: list[int] | None,
|
||||||
@@ -117,7 +119,7 @@ async def run_phrase_judgment(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (Request): Current request, for template rendering.
|
request (Request): Current request, for template rendering.
|
||||||
engine (AppEngine): Engine used to open per-book judging sessions.
|
engine (AppAsyncEngine): Engine used to open per-book judging sessions.
|
||||||
config (AppConfig): Runtime phrase-tuning settings.
|
config (AppConfig): Runtime phrase-tuning settings.
|
||||||
source_ids (list[int] | None): Books to judge; ``None`` judges every indexed book.
|
source_ids (list[int] | None): Books to judge; ``None`` judges every indexed book.
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from fastapi.responses import HTMLResponse
|
|||||||
from python.ebook_search.answer import answer_query
|
from python.ebook_search.answer import answer_query
|
||||||
from python.ebook_search.api.dependencies import ( # noqa: TC001 FastAPI resolves these annotated dependencies at runtime
|
from python.ebook_search.api.dependencies import ( # noqa: TC001 FastAPI resolves these annotated dependencies at runtime
|
||||||
AppConfig,
|
AppConfig,
|
||||||
AppEngine,
|
|
||||||
AppHttpClient,
|
AppHttpClient,
|
||||||
)
|
)
|
||||||
from python.ebook_search.api.web import error_response, templates
|
from python.ebook_search.api.web import error_response, templates
|
||||||
@@ -25,6 +24,7 @@ from python.ebook_search.guardrails import (
|
|||||||
)
|
)
|
||||||
from python.ebook_search.search import SearchResponse, search_ebooks
|
from python.ebook_search.search import SearchResponse, search_ebooks
|
||||||
from python.ebook_search.timing import runtime_step_from_start
|
from python.ebook_search.timing import runtime_step_from_start
|
||||||
|
from python.fastapi_tools import AppAsyncEngine # noqa: TC001 FastAPI resolves this annotated dependency at runtime
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import httpx
|
import httpx
|
||||||
@@ -76,7 +76,7 @@ async def build_answer(
|
|||||||
async def search(
|
async def search(
|
||||||
request: Request,
|
request: Request,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
engine: AppEngine,
|
engine: AppAsyncEngine,
|
||||||
client: AppHttpClient,
|
client: AppHttpClient,
|
||||||
query: Annotated[str, Form()],
|
query: Annotated[str, Form()],
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -1,6 +1,25 @@
|
|||||||
"""Reusable FastAPI tools."""
|
"""Reusable FastAPI tools."""
|
||||||
|
|
||||||
from python.fastapi_tools.db import AsyncDbSession, DbSession, get_async_db, get_db
|
from python.fastapi_tools.db import (
|
||||||
|
AppAsyncEngine,
|
||||||
|
AppEngine,
|
||||||
|
AsyncDbSession,
|
||||||
|
DbSession,
|
||||||
|
get_async_db,
|
||||||
|
get_async_engine,
|
||||||
|
get_db,
|
||||||
|
get_engine,
|
||||||
|
)
|
||||||
from python.fastapi_tools.zstd_middleware import ZstdMiddleware
|
from python.fastapi_tools.zstd_middleware import ZstdMiddleware
|
||||||
|
|
||||||
__all__ = ["AsyncDbSession", "DbSession", "ZstdMiddleware", "get_async_db", "get_db"]
|
__all__ = [
|
||||||
|
"AppAsyncEngine",
|
||||||
|
"AppEngine",
|
||||||
|
"AsyncDbSession",
|
||||||
|
"DbSession",
|
||||||
|
"ZstdMiddleware",
|
||||||
|
"get_async_db",
|
||||||
|
"get_async_engine",
|
||||||
|
"get_db",
|
||||||
|
"get_engine",
|
||||||
|
]
|
||||||
|
|||||||
@@ -5,13 +5,24 @@ from __future__ import annotations
|
|||||||
from typing import TYPE_CHECKING, Annotated
|
from typing import TYPE_CHECKING, Annotated
|
||||||
|
|
||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.engine import Engine
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import AsyncIterator, Iterator
|
from collections.abc import AsyncIterator, Iterator
|
||||||
|
|
||||||
|
|
||||||
|
def get_engine(request: Request) -> Engine:
|
||||||
|
"""Get a synchronous database engine from app state."""
|
||||||
|
return request.app.state.engine
|
||||||
|
|
||||||
|
|
||||||
|
def get_async_engine(request: Request) -> AsyncEngine:
|
||||||
|
"""Get an asynchronous database engine from app state."""
|
||||||
|
return request.app.state.engine
|
||||||
|
|
||||||
|
|
||||||
def get_db(request: Request) -> Iterator[Session]:
|
def get_db(request: Request) -> Iterator[Session]:
|
||||||
"""Get database session from app state."""
|
"""Get database session from app state."""
|
||||||
with Session(request.app.state.engine) as session:
|
with Session(request.app.state.engine) as session:
|
||||||
@@ -28,5 +39,7 @@ async def get_async_db(request: Request) -> AsyncIterator[AsyncSession]:
|
|||||||
yield session
|
yield session
|
||||||
|
|
||||||
|
|
||||||
|
AppEngine = Annotated[Engine, Depends(get_engine)]
|
||||||
|
AppAsyncEngine = Annotated[AsyncEngine, Depends(get_async_engine)]
|
||||||
DbSession = Annotated[Session, Depends(get_db)]
|
DbSession = Annotated[Session, Depends(get_db)]
|
||||||
AsyncDbSession = Annotated[AsyncSession, Depends(get_async_db)]
|
AsyncDbSession = Annotated[AsyncSession, Depends(get_async_db)]
|
||||||
|
|||||||
Reference in New Issue
Block a user