Files
group_fqcd_jr/app/api/schemas/agent_runs.py
T
张胜宇 eb1822d802 docs(W18): D2.4 索引口径更正为 AUTOINDEX(v1.6)+ D2.9 手动对话测试用例成文 + 空白消息 500 修复
- D2.4 v1.3 -> v1.6:索引统一 AUTOINDEX(设计初稿 HNSW/IVF_FLAT 未落地,实库直查为据)
  更正 9 处 + 新增 §4.1 索引口径落地注;语料 628 -> 675 块并补 fin_basic_collection 说明
  (三集合仍是唯一默认检索面,basic 并入会使 M-1 100% -> 91.3%)
- 新增 客服agent/D2.9-客服Agent手动对话测试用例-2026-09-20.md:
  46 条金标逐条可问 + 11 条边界 Z 组 + 判分四问 + M-1~M-10 手动汇总 + 真 HTTP 核验配方
  实测登记 3 项缺口:空白消息 500(已修)/ 语料档位口径矛盾(待裁定)/ 重复问句漂移(待裁定)
- 修修复:message 纯空白 500 -> 422 AGENT_INPUT_INVALID(入口补判据 + 回归测试)
- tools/chat_console.py 提示语去掉已下线产品名(季季盈)
- 落档:D1.1 §27(60 -> 61 份 / 客服agent 8 -> 9 份 / 注入校验 56 -> 57)、D2.1 v6.33、D1.6 §4.46
- 门禁:check_authoritative_docs 通过、_consistency.py GATE PASS、pytest 1915 passed / 0 failed
2026-09-20 17:31:40 +08:00

81 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from typing import Any
from pydantic import BaseModel, ConfigDict, Field, field_validator
class AgentRunCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
agent_type: str
# 上限与前端输入框 `maxlength` = 8000 一致(`widget.js`):前端截断只是体验,
# 真正的边界必须由服务端把关,否则绕过前端直发可把任意长度文本落库/送模型。
message: str = Field(min_length=1, max_length=8000)
# `ConversationSession.session_id` 是 `String(64)`:超长会话号在 MySQL 严格模式下
# 会在落库时才炸成 500,必须在入口用同宽约束拦成 422。
session_id: str = Field(min_length=1, max_length=64)
# 上限 64 不是随手取的:`RequestIdempotency.idempotency_key` 是 `String(64)`,
# 接口此前允许 128,65—128 字符的键会穿过校验、在插入时才炸成 500。
idempotency_key: str = Field(min_length=16, max_length=64)
@field_validator("message")
@classmethod
def message_must_not_be_blank(cls, value: str) -> str:
"""纯空白消息必须在**入口**拦掉,不能穿透到领域层。
`min_length=1` 只数字符:`" "` 长度是 3,能过入口校验;随后 `AgentRequest` 的
`message must not be blank` 会拒掉它 —— 但那抛的是**领域层**的
`pydantic.ValidationError`,不属于 FastAPI 的请求校验异常,会被兜底处理器变成
**500 Internal Server Error**(实测:`POST /api/v1/agent-runs` + `message=" "`
→ 500,而 `message` 超长 → 正常的 422 信封)。判据在入口补一份,错误形状与其余
参数错误一致(422 `AGENT_INPUT_INVALID`)。
"""
if not value.strip():
raise ValueError("message must not be blank")
return value
class AgentRunAcceptedResponse(BaseModel):
run_id: str
trace_id: str
status: str
status_url: str
events_url: str
class AgentRunAcceptedEnvelope(BaseModel):
"""`POST /api/v1/agent-runs` 的 202 响应(文档 §3.3 + §6.2)。
文档 §6.2 明确给出的是 `{data:{run_id,trace_id,status,status_url,events_url}, meta:{trace_id}}`
信封,§3.3 又规定"业务接口不得增加其他顶层字段"。此前这里返回的是平铺对象,接入方按
文档取 `data.run_id` 会拿到空值——这是接入者第一步就会踩到的契约偏差,因此补齐信封。
`data` 内的字段名与语义与改动前完全一致,客户端只是多读一层 `data`;
`meta.trace_id` 是**本次请求**的 trace,`data.trace_id` 是运行自身的 trace(两者通常相同)。
"""
data: AgentRunAcceptedResponse
meta: dict[str, str]
class AgentRunStatusResponse(BaseModel):
run_id: str
trace_id: str
status: str
agent_type: str
session_id: str
result: dict[str, Any] | None = None
error_code: str | None = None
created_at: str
completed_at: str | None = None
class AgentRunStatusEnvelope(BaseModel):
"""`GET /api/v1/agent-runs/{run_id}` 的成功响应(文档 §3.3 + §6.3)。
单资源信封只多一层 `{data, meta}`:`data` 里的字段名与语义**与改动前完全一致**,
客户端除多读一层 `data` 外不需要任何适配。
"""
data: AgentRunStatusResponse
meta: dict[str, str]