52 lines
2.2 KiB
Go
52 lines
2.2 KiB
Go
package transcript
|
||
|
||
import (
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||
)
|
||
|
||
// DocumentPathPrefix 是文档读取的版本化路径前缀。转录还原出的文档部件必须与网关读侧
|
||
// (`GET /api/v1/documents/{name}`)指向同一个 URL,否则续接的回合会把模型指到一个取不到的
|
||
// 地址。它是这个契约字符串的**唯一字面量**:网关的 `documentFilePrefix` 直接取自这里。
|
||
const DocumentPathPrefix = "/api/v1/documents/"
|
||
|
||
// documentParts 把已存 document 列里的**全部**引用还原为该回合的文档部件(design/24 §9.5)。
|
||
// 部件只交给 RoleUser 回合(§2.3-③):文档在任何别的角色上都会让编码器 fail-loud,而一行
|
||
// 坏数据不该把整个会话卡死。
|
||
//
|
||
// 列里是引用数组(一个回合最多 5 个附件),因此这里逐条还原、保持部件顺序 —— 少还原一条,
|
||
// 那个附件就会在发起它的回合之后的所有回合里消失,与「清单恒在」的承诺自相矛盾。
|
||
//
|
||
// 列被写坏(或附件落在非用户行上)时**不静默**:打一条带来源的 WARN 并省掉该部件,回合照常
|
||
// 继续 —— 附件凭空消失正是这条清单要修的缺陷,因此必须留下痕迹。日志只带消息 id 与错误,
|
||
// 绝不带正文。
|
||
func documentParts(role model.Role, h history.Message) []model.Content {
|
||
if h.Document == "" {
|
||
return nil
|
||
}
|
||
if role != model.RoleUser {
|
||
transcriptLog().Warn("transcript document on non-user row", "message", h.ID, "role", h.Role)
|
||
return nil
|
||
}
|
||
refs, err := history.DecodeDocuments(h.Document)
|
||
if err != nil {
|
||
transcriptLog().Warn("transcript document decode", "message", h.ID, "err", err)
|
||
return nil
|
||
}
|
||
parts := make([]model.Content, 0, len(refs))
|
||
for _, ref := range refs {
|
||
if ref.Name == "" {
|
||
transcriptLog().Warn("transcript document without name", "message", h.ID)
|
||
continue
|
||
}
|
||
doc := model.Document{
|
||
Name: ref.Name,
|
||
MIME: ref.MIME,
|
||
URL: DocumentPathPrefix + ref.Name,
|
||
Size: ref.Size,
|
||
}
|
||
parts = append(parts, model.DocumentMessage(model.RoleUser, "", doc).Content...)
|
||
}
|
||
return parts
|
||
}
|