71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
"""Centralized accessors for singleton objects stored on ``app.state``.
|
||
|
||
**Getters** (used by routers): raise 503 when a required dependency is
|
||
missing, except ``get_store`` which returns ``None``.
|
||
|
||
Initialization is handled directly in ``app.py`` via :class:`AsyncExitStack`.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from collections.abc import AsyncGenerator
|
||
from contextlib import AsyncExitStack, asynccontextmanager
|
||
|
||
from fastapi import FastAPI, HTTPException, Request
|
||
|
||
from deerflow.runtime import RunManager, StreamBridge
|
||
|
||
|
||
@asynccontextmanager
|
||
async def langgraph_runtime(app: FastAPI) -> AsyncGenerator[None, None]:
|
||
"""Bootstrap and tear down all LangGraph runtime singletons.
|
||
|
||
Usage in ``app.py``::
|
||
|
||
async with langgraph_runtime(app):
|
||
yield
|
||
"""
|
||
from deerflow.agents.checkpointer.async_provider import make_checkpointer
|
||
from deerflow.runtime import make_store, make_stream_bridge
|
||
|
||
async with AsyncExitStack() as stack:
|
||
app.state.stream_bridge = await stack.enter_async_context(make_stream_bridge())
|
||
app.state.checkpointer = await stack.enter_async_context(make_checkpointer())
|
||
app.state.store = await stack.enter_async_context(make_store())
|
||
app.state.run_manager = RunManager()
|
||
yield
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Getters – called by routers per-request
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def get_stream_bridge(request: Request) -> StreamBridge:
|
||
"""Return the global :class:`StreamBridge`, or 503."""
|
||
bridge = getattr(request.app.state, "stream_bridge", None)
|
||
if bridge is None:
|
||
raise HTTPException(status_code=503, detail="Stream bridge not available")
|
||
return bridge
|
||
|
||
|
||
def get_run_manager(request: Request) -> RunManager:
|
||
"""Return the global :class:`RunManager`, or 503."""
|
||
mgr = getattr(request.app.state, "run_manager", None)
|
||
if mgr is None:
|
||
raise HTTPException(status_code=503, detail="Run manager not available")
|
||
return mgr
|
||
|
||
|
||
def get_checkpointer(request: Request):
|
||
"""Return the global checkpointer, or 503."""
|
||
cp = getattr(request.app.state, "checkpointer", None)
|
||
if cp is None:
|
||
raise HTTPException(status_code=503, detail="Checkpointer not available")
|
||
return cp
|
||
|
||
|
||
def get_store(request: Request):
|
||
"""Return the global store (may be ``None`` if not configured)."""
|
||
return getattr(request.app.state, "store", None)
|