Files
Cyrene_v2/internal/services/gateway/session_tail.go
T
AskaEth a4789ec13e
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(gateway): chat.documents 解析 + 修两处静默吞掉(design/24 S3-B)
- REST(同步 + stream:true)与 WS 两条入口同形解析 documents:只收服务端存储名(形态 + 真实存在 + 嗅探 MIME + 本地 URL + 大小),客户端谎报 mime/url/size 一律被服务端覆盖 ⇒ 不重开 SSRF 面(有「真实可命中 URL,命中次数 0」的用例钉住)。

- 拒绝口径对齐 S1:超过 5 条 → 400 too_many_documents(且先于任何 FS 访问);形态非法/穿越/不存在 → 404 document_not_found。

- 挂载:一回合一条 RoleUser 消息,文档部件追加其后(纯附件回合不塞空文本块,空文本会被 provider 拒)。

- 修 S2 移交的两处静默吞掉:session_transcript 用 history.DecodeDocument 把 h.Document 还原为部件(坏列带来源 WARN 不静默、也不卡会话);session_tail 的 messagesEmpty 计入 ContentTypeDocument(纯附件不再被判空回合)。

- 拆文件/拆函数(AGENTS:文件≤500 行、函数≤80 行):new chat_documents.go(rest.go 原 499/500 行塞不下)、handleChat 尾段 54 行原样搬成 submitChatTurn(handleChat 90→40 行),ws_hub 增 documents 依赖(与既有 envResolver 同接法)。

- 主会话独立验收(隔离 worktree = 当前 HEAD + 仅 B 的文件 ⇒ 正是 A+B 合流态):build/vet 绿、全树 59 包 0 FAIL、13 条新用例 PASS、linux/darwin 交叉编译绿;变异独立复现(摘掉条数上限判定 ⇒ 红)。

- 主会话补的测试钉子:TestMaxChatDocumentsMatchesDesign —— 原条数用例是用常量自身算输入条数的,改常量它照样绿(实测 5→1000 全包仍绿);补钉后改 10 时新钉子红、旧用例仍绿。
2026-10-04 17:25:35 +08:00

51 lines
1.8 KiB
Go
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.
package gateway
import "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
// 本文件拥有会话编排器的对话尾部辅助件
// (design/15 §2.2/§4):续跑所依据的有界窗口,以及
// 防止空洞回合把会话标记为忙碌的空判定。它们是
// 作用于 []model.Message 的纯函数,与状态机分开
// 放置,使编排器文件保持小。
// boundedTail 返回最后 maxTail 条消息的全新副本,使续跑
// 保持增量且有界(design/15 §2.2、§4),且绝不与输入
// 切片或另一个状态的尾部共享底层数组。
func (o *Orchestrator) boundedTail(msgs []model.Message) []model.Message {
if len(msgs) <= o.maxTail {
return append([]model.Message(nil), msgs...)
}
out := make([]model.Message, o.maxTail)
copy(out, msgs[len(msgs)-o.maxTail:])
return out
}
// messagesEmpty 报告回合是否不携带可用文本、图片或文档,使
// 编排器不会因空洞消息把会话标记为忙碌。文档部件同样算内容:
// 「只带附件」的回合是真实回合,不该被判空(design/24 §9.5)。
func (o *Orchestrator) messagesEmpty(msgs []model.Message) bool {
for _, m := range msgs {
for _, c := range m.Content {
if c.Type == model.ContentTypeText && c.Text != "" {
return false
}
if c.Type == model.ContentTypeImage && c.ImageURL != "" {
return false
}
if c.Type == model.ContentTypeDocument && c.DocumentName != "" {
return false
}
}
}
return true
}
// concatMessages 返回合并 a 与 b 的新切片(绝不复用 a 或 b
// 的底层数组),因此续跑尾部可以安全地交给并发运行。
func concatMessages(a []model.Message, b ...model.Message) []model.Message {
out := make([]model.Message, 0, len(a)+len(b))
out = append(out, a...)
out = append(out, b...)
return out
}