Files
ZNJJ-api-server/src/core/fastgpt_client.py
T

99 lines
3.6 KiB
Python
Raw Normal View History

"""FastGPT client and chat backend dependency injection."""
2026-01-08 16:22:25 +08:00
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastgpt_client import AsyncChatClient
from ..agent.graph import build_graph
from ..backends.chat import ChatBackend
from ..backends.fastgpt import FastGPTBackend
from ..backends.langgraph import LangGraphBackend
from ..services.app_service import AccidentAppService
from ..services.repository import InMemorySessionRepository
2026-01-08 16:22:25 +08:00
from .config import Config
2026-07-28 08:41:34 +08:00
2026-01-08 16:22:25 +08:00
_fastgpt_client: AsyncChatClient | None = None
_chat_backend: ChatBackend | None = None
_langgraph_graph = None
_app_service: AccidentAppService | None = None
2026-01-08 16:22:25 +08:00
# 后端超时/重试 SLA(两后端对齐;契约测试防止再漂移)。
# FastGPT 侧:客户端每次请求总超时 + 重试次数;
# LangGraph 侧:LLM 每次调用总超时 + 重试次数(退避为指数式,
# 与 FastGPT 固定 1s 不同——总尝试次数与单次超时可比,见
# test/backends/test_sla_alignment.py)。
FASTGPT_TIMEOUT_SECONDS = 60.0
FASTGPT_MAX_RETRIES = 3
2026-01-08 16:22:25 +08:00
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Manage chat backend resource lifecycle."""
global _chat_backend, _fastgpt_client
2026-01-08 16:22:25 +08:00
Config.validate()
2026-07-28 08:41:34 +08:00
if Config.AGENT_BACKEND == "langgraph":
# 作业 2:LangGraph fake 模式不要求 FastGPT 密钥。
# TODO(作业 2): 若语音仍启用,需在此单独初始化并校验 FastGPT 配置。
from ..agent.llm import build_llm_service
global _langgraph_graph, _app_service
_langgraph_graph = build_graph()
_chat_backend = LangGraphBackend(_langgraph_graph, llm=build_llm_service())
# 作业 6:应用服务 + 内存 repository(作业 7 换 PostgreSQL)。
_app_service = AccidentAppService(
_chat_backend, InMemorySessionRepository()
)
else:
_fastgpt_client = AsyncChatClient(
api_key=Config.FASTGPT_API_KEY,
base_url=Config.FASTGPT_BASE_URL,
timeout=FASTGPT_TIMEOUT_SECONDS,
max_retries=FASTGPT_MAX_RETRIES,
retry_delay=1.0,
enable_logging=Config.DEBUG,
)
await _fastgpt_client.__aenter__()
_chat_backend = FastGPTBackend(_fastgpt_client)
try:
yield
finally:
_chat_backend = None
_langgraph_graph = None
_app_service = None
if _fastgpt_client:
await _fastgpt_client.__aexit__(None, None, None)
_fastgpt_client = None
2026-01-08 16:22:25 +08:00
async def delete_chat_session(thread_id: str) -> None:
"""langgraph 模式下删除会话:清理图 checkpoint(对照计划作业 6 的
完整语义——业务字段、事件、路由持久化清理在接入 repository 后补齐)。
"""
if _langgraph_graph is None:
raise RuntimeError("LangGraph backend not initialized")
checkpointer = getattr(_langgraph_graph, "checkpointer", None)
if checkpointer is not None and hasattr(checkpointer, "delete_thread"):
checkpointer.delete_thread(thread_id)
2026-01-08 16:22:25 +08:00
def get_fastgpt_client() -> AsyncChatClient:
2026-07-28 10:38:01 +08:00
"""Get the initialized FastGPT client."""
2026-01-08 16:22:25 +08:00
if _fastgpt_client is None:
raise RuntimeError("FastGPT client not initialized")
return _fastgpt_client
def get_app_service() -> AccidentAppService:
"""Get the initialized application service(langgraph 模式)。"""
if _app_service is None:
raise RuntimeError("Application service not initialized")
return _app_service
def get_chat_backend() -> ChatBackend:
"""Get the backend-neutral chat service."""
if _chat_backend is None:
raise RuntimeError("Chat backend not initialized")
return _chat_backend