refactor(gateway): restructure gateway with new dependency injection and services

Reorganize app/gateway/ with:
- common/ - lifespan management
- dependencies/ - FastAPI dependency injection (db, checkpointer, repositories, stream_bridge)
- services/runs/ - run execution services (facade_factory, input adapters, store operations)
- registrar.py - router registration
- router.py - main router setup

Simplify app.py to use the new modular structure.
Remove deprecated utils.py.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rayhpeng
2026-04-22 11:27:08 +08:00
parent 274255b1a5
commit 39a575617b
24 changed files with 1120 additions and 371 deletions
@@ -0,0 +1,59 @@
from app.gateway.dependencies.checkpointer import (
CurrentCheckpointer,
get_checkpointer,
)
from app.plugins.auth.security.dependencies import (
CurrentAuthService,
CurrentUserRepository,
get_auth_service,
get_current_user_from_request,
get_current_user_id,
get_optional_user_from_request,
get_user_repository,
)
from app.gateway.dependencies.db import (
CurrentSession,
CurrentSessionTransaction,
get_db_session,
get_db_session_transaction,
)
from app.gateway.dependencies.repositories import (
CurrentFeedbackRepository,
CurrentRunRepository,
CurrentThreadMetaRepository,
CurrentThreadMetaStorage,
get_feedback_repository,
get_run_repository,
get_thread_meta_repository,
get_thread_meta_storage,
)
from app.gateway.dependencies.stream_bridge import (
CurrentStreamBridge,
get_stream_bridge,
)
__all__ = [
"CurrentCheckpointer",
"CurrentAuthService",
"CurrentFeedbackRepository",
"CurrentRunRepository",
"CurrentSession",
"CurrentSessionTransaction",
"CurrentStreamBridge",
"CurrentThreadMetaRepository",
"CurrentThreadMetaStorage",
"CurrentUserRepository",
"get_auth_service",
"get_checkpointer",
"get_current_user_from_request",
"get_current_user_id",
"get_db_session",
"get_db_session_transaction",
"get_feedback_repository",
"get_optional_user_from_request",
"get_run_repository",
"get_stream_bridge",
"get_thread_meta_repository",
"get_thread_meta_storage",
"get_user_repository",
]
@@ -0,0 +1,20 @@
from __future__ import annotations
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from langgraph.types import Checkpointer
def get_checkpointer(request: Request) -> Checkpointer:
"""Get checkpointer from app.state.persistence."""
persistence = getattr(request.app.state, "persistence", None)
if persistence is None:
raise HTTPException(status_code=503, detail="Persistence not available")
checkpointer = getattr(persistence, "checkpointer", None)
if checkpointer is None:
raise HTTPException(status_code=503, detail="Checkpointer not available")
return checkpointer
CurrentCheckpointer = Annotated[Checkpointer, Depends(get_checkpointer)]
+37
View File
@@ -0,0 +1,37 @@
from __future__ import annotations
from collections.abc import AsyncIterator
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
def _get_session_factory(request: Request) -> async_sessionmaker[AsyncSession]:
factory = getattr(request.app.state.persistence, "session_factory", None)
if factory is None:
raise HTTPException(status_code=503, detail="Database session factory not available")
return factory
async def get_db_session(request: Request) -> AsyncIterator[AsyncSession]:
"""Open a session without auto-commit. Use for read-only endpoints."""
session_factory = _get_session_factory(request)
async with session_factory() as session:
yield session
async def get_db_session_transaction(request: Request) -> AsyncIterator[AsyncSession]:
"""Open a session and commit on success, rollback on error."""
session_factory = _get_session_factory(request)
async with session_factory() as session:
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
CurrentSession = Annotated[AsyncSession, Depends(get_db_session)]
CurrentSessionTransaction = Annotated[AsyncSession, Depends(get_db_session_transaction)]
@@ -0,0 +1,41 @@
from __future__ import annotations
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from app.infra.storage import ThreadMetaStorage
from store.repositories.contracts import (
FeedbackRepositoryProtocol,
RunRepositoryProtocol,
ThreadMetaRepositoryProtocol,
)
def _require_state(request: Request, attr: str, label: str):
value = getattr(request.app.state, attr, None)
if value is None:
raise HTTPException(status_code=503, detail=f"{label} not available")
return value
def get_run_repository(request: Request) -> RunRepositoryProtocol:
return _require_state(request, "run_store", "Run store")
def get_thread_meta_repository(request: Request) -> ThreadMetaRepositoryProtocol:
return _require_state(request, "thread_meta_repo", "Thread metadata store")
def get_thread_meta_storage(request: Request) -> ThreadMetaStorage:
return _require_state(request, "thread_meta_storage", "Thread metadata storage")
def get_feedback_repository(request: Request) -> FeedbackRepositoryProtocol:
return _require_state(request, "feedback_repo", "Feedback")
CurrentRunRepository = Annotated[RunRepositoryProtocol, Depends(get_run_repository)]
CurrentThreadMetaRepository = Annotated[ThreadMetaRepositoryProtocol, Depends(get_thread_meta_repository)]
CurrentThreadMetaStorage = Annotated[ThreadMetaStorage, Depends(get_thread_meta_storage)]
CurrentFeedbackRepository = Annotated[FeedbackRepositoryProtocol, Depends(get_feedback_repository)]
@@ -0,0 +1,18 @@
from __future__ import annotations
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from deerflow.runtime import StreamBridge
def get_stream_bridge(request: Request) -> StreamBridge:
"""Get stream bridge from app.state."""
bridge = getattr(request.app.state, "stream_bridge", None)
if bridge is None:
raise HTTPException(status_code=503, detail="Stream bridge not available")
return bridge
CurrentStreamBridge = Annotated[StreamBridge, Depends(get_stream_bridge)]