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:
Eric Wang
2026-10-22 20:50:10 +08:00
parent 6c1fbd0295
commit 8717d9066c
12 changed files with 882 additions and 16 deletions
+4
View File
@@ -13,6 +13,10 @@ class ChatInput:
text: str
need_form_update: bool = False
client_mode: str = ""
# 作业 6:应用服务注入的最新业务字段(acd 进 accident_fields,
# 人员进 person_fields)。LangGraph 后端合并进图输入;
# FastGPT 后端忽略该字段。
injected_fields: dict | None = None
@dataclass(frozen=True)
+10 -5
View File
@@ -49,12 +49,17 @@ class LangGraphBackend(ChatBackend):
- 本阶段不处理 ``need_form_update`` 的真实表单差量
(作业 6 起实现)。
"""
graph_input: dict = {
"session_id": chat_input.session_id,
"input_text": chat_input.text,
"client_mode": chat_input.client_mode,
}
# 作业 6:应用服务注入的最新业务字段覆盖本轮输入,
# 使 set_info 写入后本轮 chat 立即可见(输入覆盖 checkpoint 同键)。
if chat_input.injected_fields:
graph_input.update(chat_input.injected_fields)
result = await self._graph.ainvoke(
{
"session_id": chat_input.session_id,
"input_text": chat_input.text,
"client_mode": chat_input.client_mode,
},
graph_input,
config={"configurable": {"thread_id": chat_input.session_id}},
context=GraphDeps(llm=self._llm),
)