Files
AskaEth 09fba77f87
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
docs(comments): 注释中文化 · internal/runtime/history(15 文件,纯替换)
- 只翻注释/文档字符串:剥离注释后代码行逐字节不变(机械校验只剩 4 条行尾注释的翻译,代码前缀全等)
- 交叉引用(design/16 §4.6、D4/B3、0010 迁移文件名)逐字保留
- 保留未译技术词:DDL/rowid/LIKE/LEFT JOIN/DTO/rune/WS/actor 及全部标识符与表名
- 顺带副作用:store.go、session_persona_test.go 由注释文字改变而变得 gofmt 干净(未运行 gofmt -w)
2026-10-04 12:28:48 +08:00

77 lines
3.5 KiB
Go
Raw Permalink 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 history
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
)
// 本文件负责每会话的角色绑定(DevDocs/design/22 多角色并存 +
// 按会话选角 §2/§4.1)。一个会话的角色是「这个对话里由哪个角色应答」的唯一
// 事实来源:循环据此解析系统提示词、记忆作用域、情绪机与思考预算。该值只是
// 展示/作用域元数据 —— 身份与权限仍由渠道 actor 元数据决定(项目红线)。
//
// 空意味着「继承当前全局默认角色」,因此它既是列默认值,也是一个合法的显式
// 取值:把它设为空即清除显式绑定,让会话重新跟随默认角色变更
// (design/22 §7-6)。
// NewSubSessionFor 创建绑定到 personaID 的子会话并返回其 id
// (MainSessionID:uuid)。personaID 为空则不绑定任何显式角色,会话因此继承
// 全局默认 —— 与 NewSubSession 行为相同。ownerID 为与 NewSubSession 对称而
// 接受(Web 主人由 JWT 识别,不按会话存储);它只以 debug 级别记日志,不被
// 持久化。
func (s *Store) NewSubSessionFor(ctx context.Context, ownerID, personaID string) (string, error) {
id, err := newID()
if err != nil {
return "", fmt.Errorf("history: new sub id: %w", err)
}
sessionID := MainSessionID + ":" + id
now := time.Now().UnixMilli()
if _, err := s.db.ExecContext(ctx, `INSERT INTO chat_sessions (id, type, title, persona_id, created_at)
VALUES (?, ?, ?, ?, ?)`, sessionID, SessionSub, "", strings.TrimSpace(personaID), now); err != nil {
return "", fmt.Errorf("history: insert sub session: %w", err)
}
if s.log != nil {
s.log.Debug("sub session created", "session_id", sessionID, "owner_id", ownerID, "persona_id", strings.TrimSpace(personaID))
}
return sessionID, nil
}
// SetSessionPersona 绑定(或在 personaID 为空时清除)一个会话的角色。固定主
// 会话与子会话都可被改绑:该绑定是作用域元数据,不是身份。未知 id 返回
// ErrSessionNotFound。它绝不触碰对话:已存消息保留说出它们的那个角色
// (design/22 §7-5),下一个回合只是解析新角色。
func (s *Store) SetSessionPersona(ctx context.Context, sessionID, personaID string) error {
res, err := s.db.ExecContext(ctx, `UPDATE chat_sessions SET persona_id = ? WHERE id = ?`,
strings.TrimSpace(personaID), sessionID)
if err != nil {
return fmt.Errorf("history: set session persona %s: %w", sessionID, err)
}
n, err := res.RowsAffected()
if err != nil {
return fmt.Errorf("history: set session persona %s: %w", sessionID, err)
}
if n == 0 {
return fmt.Errorf("history: set session persona %s: %w", sessionID, ErrSessionNotFound)
}
return nil
}
// SessionPersona 返回绑定到 sessionID 的角色,会话继承全局默认时返回空。未知
// id 返回 ErrSessionNotFound,使调用方能区分「没有显式角色」(真实会话、绑定
// 为空)与「没有这个会话」;只需要有效角色的调用方把两者都当作「用默认」。
func (s *Store) SessionPersona(ctx context.Context, sessionID string) (string, error) {
var personaID string
err := s.db.QueryRowContext(ctx, `SELECT persona_id FROM chat_sessions WHERE id = ?`, sessionID).Scan(&personaID)
switch {
case errors.Is(err, sql.ErrNoRows):
return "", fmt.Errorf("history: session persona %s: %w", sessionID, ErrSessionNotFound)
case err != nil:
return "", fmt.Errorf("history: session persona %s: %w", sessionID, err)
}
return personaID, nil
}