feat(services): assignment 6 skeleton - app service, field registry, in-memory repository
- fields_registry: 24 business keys in acd/human1/human2 groups with
value_type, writable and SensitiveLevel metadata; strict bool/int
parsing (no bool('0') trap); INTERNAL_FIELD_KEYS protection list
- repository: SessionRepository protocol + InMemorySessionRepository
(idempotent delete; PostgreSQL swap-in is assignment 7)
- AccidentAppService: chat/set_info/get_info/delete_session share one
business state - chat injects latest repository fields into the graph
and writes back validated field diffs; set/get/delete make zero LLM
calls and zero helper conversations (includeInputInfo accepted but
unused); unknown get keys keep the '' JSON-empty-string contract,
unknown set keys are rejected by whitelist
- ChatInput.injected_fields neutral contract extension; LangGraphBackend
merges injected fields into graph input (set_info visible next chat)
- endpoints: thin layer - langgraph mode routes business state through
the app service (fastgpt mode keeps the legacy path); delete cleans
repository + graph checkpoint, idempotent
- tests: registry completeness (sfzmwh/sjwh/csbw included), app service
combos with LLM-call spy, HTTP acceptance flow (set -> get -> chat ->
groups -> delete -> no leak, idempotent re-delete)
- docs/assignment6-compat.md: behavior diff list vs FastGPT status quo
and the audit backlog (arbitrary set_info key usage, stream formUpdate
write-back, sensitive-field display boundary)
Verified: 200 unit tests (172 existing + 28 new)
This commit is contained in:
@@ -6,12 +6,15 @@ 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
|
||||
from .config import Config
|
||||
|
||||
|
||||
_fastgpt_client: AsyncChatClient | None = None
|
||||
_chat_backend: ChatBackend | None = None
|
||||
_langgraph_graph = None
|
||||
_app_service: AccidentAppService | None = None
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -25,9 +28,13 @@ async def lifespan(app: FastAPI):
|
||||
# TODO(作业 2): 若语音仍启用,需在此单独初始化并校验 FastGPT 配置。
|
||||
from ..agent.llm import build_llm_service
|
||||
|
||||
global _langgraph_graph
|
||||
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,
|
||||
@@ -45,6 +52,7 @@ async def lifespan(app: FastAPI):
|
||||
finally:
|
||||
_chat_backend = None
|
||||
_langgraph_graph = None
|
||||
_app_service = None
|
||||
if _fastgpt_client:
|
||||
await _fastgpt_client.__aexit__(None, None, None)
|
||||
_fastgpt_client = None
|
||||
@@ -68,6 +76,13 @@ def get_fastgpt_client() -> AsyncChatClient:
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user