fix: Wave 0 独立 AI 评审闭环(有条件通过→闭环, 273 绿)——P2-1 python-jose>=3.5.0(防 CVE-2024-33663/33664) P2-2 audit 降级+OPTIONS 测试 P2-3 JWT 通道 chat 3 例 P2-4 debug 兜底双闸门(dev+无 RS256 公钥) P2-5 留痕失败降级显式化(401/403 拒绝语义不漂移, _safe_unauth_audit 保护 repo 构造); P3 顺手: 401 归 platform/值域外 400 补留痕/Bearer 大小写不敏感/jti 加 uuid/CLI permissions+exp 上限/dev 默认密钥 warning/LLM 单例加锁/chat 主角色优先序+message strip+guard 日志脱敏/必填 claims 参数化+none 算法固化; P3 登记项(会话枚举/seq_no 锁/trace_id 口径/文档同步)归 M4 文档
This commit is contained in:
+15
-5
@@ -62,6 +62,11 @@ class ChatRequest(BaseModel):
|
||||
|
||||
|
||||
def _primary_role(auth: AuthContext) -> str:
|
||||
"""会话主角色(agent_session.actor_role):按 Agent 边界优先序取(评审 P3-11,
|
||||
多角色 token 落库稳定)。"""
|
||||
for role in ("customer", "advisor", "analyst", "risk_officer", "compliance", "ops"):
|
||||
if role in auth.roles:
|
||||
return role
|
||||
return auth.roles[0] if auth.roles else "unknown"
|
||||
|
||||
|
||||
@@ -71,7 +76,7 @@ def _resolve_customer_id(
|
||||
"""会话关联客户:customer 强制本人;其余角色指定时过 G-01 归属校验。"""
|
||||
if agent_type == "customer":
|
||||
if requested and requested != auth.customer_id:
|
||||
deny(auth, "AUTH_403_NOT_OWNER", _repo(), customer_id=requested)
|
||||
deny(auth, "AUTH_403_NOT_OWNER", _repo(), customer_id=requested, agent_type=agent_type)
|
||||
return auth.customer_id
|
||||
if requested:
|
||||
assert_customer_access(auth, requested, core_ro=_core_ro(), risk_repo=_repo())
|
||||
@@ -83,11 +88,15 @@ def _resolve_customer_id(
|
||||
def chat_api(req: ChatRequest, request: Request, auth: AuthContext = Depends(get_auth_context)) -> dict:
|
||||
agent_type = request.headers.get("X-Agent-Type", "").strip()
|
||||
if not agent_type:
|
||||
raise ApiError(401, "AUTH_401_MISSING_AGENT_TYPE", f"missing X-Agent-Type header")
|
||||
raise ApiError(401, "AUTH_401_MISSING_AGENT_TYPE", "missing X-Agent-Type header")
|
||||
if agent_type not in AGENT_TYPES:
|
||||
raise ApiError(400, "BAD_REQUEST", f"invalid X-Agent-Type: {agent_type}")
|
||||
assert_agent_access(auth, agent_type, risk_repo=_repo())
|
||||
|
||||
message = req.message.strip()
|
||||
if not message:
|
||||
raise ApiError(400, "BAD_REQUEST", "message is blank")
|
||||
|
||||
customer_id = _resolve_customer_id(auth, agent_type, req.customer_id)
|
||||
session_repo = _session_repo()
|
||||
|
||||
@@ -103,6 +112,7 @@ def chat_api(req: ChatRequest, request: Request, auth: AuthContext = Depends(get
|
||||
_repo(),
|
||||
customer_id=session.get("customer_id"),
|
||||
message="session belongs to another actor or agent",
|
||||
agent_type=agent_type,
|
||||
)
|
||||
if session["status"] != "active":
|
||||
raise ApiError(409, "STATE_CONFLICT", f"session is {session['status']}")
|
||||
@@ -121,13 +131,13 @@ def chat_api(req: ChatRequest, request: Request, auth: AuthContext = Depends(get
|
||||
)
|
||||
|
||||
history = memory_service.get_recent(agent_type, sid)
|
||||
result = agent_service.chat(agent_type, history, req.message)
|
||||
result = agent_service.chat(agent_type, history, message)
|
||||
|
||||
# 落盘:user + assistant 同步写(异步化归后续);同 trace_id 贯通
|
||||
trace_id = current_trace()
|
||||
seq = session_repo.next_seq_no(sid)
|
||||
session_repo.insert_message(
|
||||
session_id=sid, trace_id=trace_id, seq_no=seq, role="user", content=req.message
|
||||
session_id=sid, trace_id=trace_id, seq_no=seq, role="user", content=message
|
||||
)
|
||||
session_repo.insert_message(
|
||||
session_id=sid,
|
||||
@@ -141,7 +151,7 @@ def chat_api(req: ChatRequest, request: Request, auth: AuthContext = Depends(get
|
||||
agent_type,
|
||||
sid,
|
||||
[
|
||||
{"role": "user", "content": req.message},
|
||||
{"role": "user", "content": message},
|
||||
{"role": "assistant", "content": result["reply"]},
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user