feat(runtime): document 内容类型与持久化(design/24 S2)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- model 新增 ContentTypeDocument + Content 的四字段 + DocumentMessage 构造器 +
  ValidateDocumentParts(文档部件只允许 RoleUser,非 user ⇒ ErrDocumentOnNonUser,fail-loud)。
- 两个编码器入口(buildMessages / buildAnthropicMessages)对文档部件报 ErrBadRequest:
  本期 document 永不抵达编码器,且绝不静默丢块。
- history:拆出 types.go(Message 增 Document 列),新增 document.go(DocumentRef 同时是
  落库 JSON 与 DTO 形状,6 字段含 status ⇒ §4.4 的「读不到要可见」;坏列回 unreadable 不静默)。
- chat_messages 加 document 列(迁移 0015 + NewStore 幂等前滚);historyMessage.Document DTO;
  session_persist 的 messageTextImage → messageTextImageDocument(保留窄视图以免牵连 steer 配对)。
- 只落「引用 + 统计」,绝不落抽取正文(TestPersistedDocumentHoldsNoBody 把全部列拼起来扫正文)。

验收(主会话独立复跑,非自报):隔离 worktree(HEAD + 本切片)go build/vet 绿、
全树 58 包 0 FAIL、linux/amd64 + darwin/arm64 交叉编译绿、18 条 Document* 测试 PASS。
变异独立复现:ValidateDocumentParts 开头 return nil ⇒ 非 user 负例红。
This commit is contained in:
AskaEth committed 2026-10-04 17:07:24 +08:00
1 parent 407c934a05
commit 6b2b07cbef
15 files changed
+895 -101

No files matched your search

+17
View File
@@ -42,6 +42,10 @@ const (
// ContentTypeToolResult 是工具执行结果片段,挂在 RoleTool
// 回合上并按原生方式编码(Anthropic tool_result / OpenAI tool message)。
ContentTypeToolResult ContentType = "tool_result"
// ContentTypeDocument 是文档内容片段(design/24 §2.2)。文档部件只允许
// 挂在 RoleUser 回合上,且**本期永不抵达编码器**:装配期会把它确定性
// 替换为抽取所得的文本部件或仅清单条目(design/24 §2.3)。
ContentTypeDocument ContentType = "document"
)
// Content 是消息的单个片段,可能是多模态的一部分。
@@ -66,6 +70,19 @@ type Content struct {
// IsError 把 ContentTypeToolResult 片段标记为执行失败,
// 以便原生设置提供商的 tool_result.is_error / error 标志。
IsError bool
// DocumentName 是 Type == ContentTypeDocument 时该文档的服务端存储名
// (design/24 §2.2、§3.2)。它由服务端签发,绝不是客户端传来的原始
// 文件名——原始文件名永不参与落盘路径。
DocumentName string
// DocumentMIME 是服务端**字节嗅探**所得的媒体类型(design/24 §3.2:
// 嗅探是唯一裁决),绝不是客户端的声明。
DocumentMIME string
// DocumentURL 是服务端签发的本地引用(design/24 §6.1 的文档读侧
// URL)。任意 http(s) URL 一律不接受:那会重开 F3 那种「服务端代取
// URL」的 SSRF 面。
DocumentURL string
// DocumentSize 是该文档的字节数。
DocumentSize int64
}
// Message 是单个会话回合,已跨提供商归一化。