docs(gateway): WS 帧门相关 11 个文件的注释改中文(只改注释,代码零字节改动)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人 2026-10-03 拍板「注释一律中文」。本次先把本会话动过的文件就地翻;
不做全仓一次性翻译,存量按包分批排期(见 工作小结.md 待办)。

范围:ws_frame_scope.go / ws_frame_scope_test.go / broadcast.go / ws_hub_turn.go /
ws_hub_message_event.go / session_read.go / server.go / ws_hub_stream_scope_test.go /
history_write_broadcast_test.go / mood_test.go / extra_test.go
含中文的整行注释 18 → 382。

校验(判据 = "只改注释、零代码字节改动"):
- `git diff -U0 | 排除 +++/--- | 排除注释行/空行/行尾注释` → 为空
- 独立实现:两侧剥掉注释后逐行比对代码文本 → 11/11 文件全等,不一致数 0
- 行尾注释 7 行逐条列出,代码列与原文逐字节相同(gofmt 未要求重排)
- gofmt -l 干净;全树 go build/vet 绿;go test ./... = 58 包 ok / 0 FAIL;
  linux/amd64 + darwin/arm64 交叉编译绿
语义未删减:部分折行英文长句合并为更少行,故整行注释数 438 → 399。
This commit is contained in:
AskaEth committed 2026-10-03 22:07:56 +08:00
1 parent 770a83c3fb
commit fe1ff33115
11 files changed
+392 -431

No files matched your search

+47 -54
View File
@@ -1,24 +1,21 @@
// Package gateway implements the Cyrene v2 HTTP gateway: the only socket the
// single binary opens. It serves the versioned REST API (/api/v1), the realtime
// WebSocket hub (/ws/chat) and the RBAC-protected admin API (/api/v1/admin/*)
// all on one port (:8080).
// Package gateway 实现 Cyrene v2 的 HTTP 网关:单二进制唯一打开的套接字。
// 它在同一个端口(:8080)上提供版本化 REST API(/api/v1)、实时
// WebSocket hub(/ws/chat)以及受 RBAC 保护的管理 API(/api/v1/admin/*)。
//
// It is deliberately lean. All backend capability flows through injected
// dependencies (construction-time dependency injection) and the in-process bus,
// never through cross-process IPC, so the package never opens a listener of its
// own beyond the gateway port.
// 它是刻意精简的。所有后端能力都经由注入的依赖(构造期依赖注入)和
// 进程内 bus 流动,绝不跨进程 IPC,因此本包除网关端口外永不自行
// 打开监听器。
//
// Client contract (DevDocs/02-development-constraints.md §九, §十一):
// - REST responses are {data} on success and {error:{code,message}} on error.
// - Authentication is JWT bearer (internal/auth); authorization is the
// owner>admin>viewer RBAC hierarchy (internal/auth).
// - Admin writes are audited (who/what/when/result).
// - Realtime events follow DevDocs/design/07 §3 (WS protocol).
// 客户端契约(DevDocs/02-development-constraints.md §九、§十一):
// - REST 成功响应为 {data},错误响应为 {error:{code,message}}。
// - 认证是 JWT bearer(internal/auth);授权是 owner>admin>viewer 的
// RBAC 层级(internal/auth)。
// - 管理写操作有审计(谁/做了什么/何时/结果)。
// - 实时事件遵循 DevDocs/design/07 §3(WS 协议)。
//
// Layout: contracts.go declares the injected dependency interfaces, deps.go the
// Deps bundle, server.go the Server lifecycle (construct / serve / close),
// routes.go the route table + outer middleware, broadcast.go the outbound
// realtime pushes.
// 文件布局:contracts.go 声明注入的依赖接口,deps.go 声明 Deps 组合,
// server.go 是 Server 生命周期(构造 / 服务 / 关闭),routes.go 是路由表
// 加外层中间件,broadcast.go 是下行实时推送。
package gateway
import (
@@ -40,11 +37,11 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// source is the log provenance tag for every record this package emits.
// source 是本包每条日志记录的来源标签。
const source = "gateway"
// Server is the gateway. It owns a ServeMux aggregating REST + WS + admin and a
// WebSocket hub. It is safe for concurrent use once constructed.
// Server 是网关本体。它持有聚合 REST + WS + admin 的 ServeMux 与一个
// WebSocket hub。构造完成后可安全并发使用。
type Server struct {
cfg *config.Config
auth Authenticator
@@ -68,32 +65,30 @@ type Server struct {
streaming bool
frontend fs.FS
dbPath string
// dataDir is the resolved data directory backing the upload store
// (<dataDir>/uploads). Injected by the composition root; cfg.DataDir is the
// fallback. See Deps.DataDir.
// dataDir 是支撑上传存储的已解析数据目录(<dataDir>/uploads)。
// 由组合根注入;cfg.DataDir 是回退值。见 Deps.DataDir。
dataDir string
// llm is the hot-swappable provider stack (models.json). When nil the
// admin /llm endpoints fall back to the env config. It is optional so tests
// that do not wire it keep working.
// llm 是可热切换的提供方栈(models.json)。为 nil 时 admin 的 /llm
// 端点回退到环境变量配置。它是可选的,以便未接它的测试仍能工作。
llm *model.RuntimeLLM
channels ChannelConfigStore
// plugins is the runtime-installable external tool provider manager. See
// Deps.Plugins.
// plugins 是可在运行时安装的外部工具提供方管理器。见
// Deps.Plugins。
plugins *pluginmgr.Manager
// ownerRegistrar stamps a scan-login bound owner id into the owner whitelist
// (best-effort). See Deps.OwnerRegistrar.
// ownerRegistrar 把扫码登录绑定的 owner id 写入 owner 白名单
// (尽力而为)。见 Deps.OwnerRegistrar。
ownerRegistrar OwnerRegistrar
webhooks map[string]http.Handler
// envResolver resolves the per-turn loop environment (its role) for a session.
// See persona_env.go / design/22 §4.5.
// envResolver 解析会话每轮的 loop 环境(其角色)。见
// persona_env.go / design/22 §4.5。
envResolver turnEnvResolver
log *slog.Logger
hub *hub
}
// New validates cfg and deps and returns a ready Server. Auth is required; the
// WebSocket hub subscribes to deps.Bus immediately so events published before
// ListenAndServe are still relayed to clients.
// New 校验 cfg 与 deps 并返回就绪的 Server。Auth 必填;WebSocket hub
// 会立即订阅 deps.Bus,因此 ListenAndServe 之前发布的事件仍会转发给
// 客户端。
func New(cfg *config.Config, deps Deps) (*Server, error) {
if cfg == nil {
return nil, fmt.Errorf("gateway: nil config")
@@ -131,9 +126,9 @@ func New(cfg *config.Config, deps Deps) (*Server, error) {
dataDir: deps.DataDir,
log: sourcedLog(deps.Log),
}
// One role resolver shared by the Server handlers and the hub, so every
// owner-facing entry point (REST, WS, voice) resolves the session's role the
// same way (design/22 §4.5) instead of each hardcoding loop.OwnerEnv().
// 一个角色解析器由 Server 各处理器与 hub 共用,使每个面向 owner
// 的入口(REST、WS、语音)以同样方式解析会话角色(design/22 §4.5),
// 而不是各自硬编码 loop.OwnerEnv()。
s.envResolver = turnEnvResolver{history: deps.History, personas: deps.Personas, log: s.log}
s.hub = newHub(s)
s.hub.orchestrator = newOrchestrator(s)
@@ -141,16 +136,16 @@ func New(cfg *config.Config, deps Deps) (*Server, error) {
return s, nil
}
// Start launches the gateway in a goroutine, returning a channel that carries the
// ListenAndServe result. The server runs until ctx is canceled.
// Start 在 goroutine 中启动网关,返回携带 ListenAndServe 结果的
// channel。服务器运行至 ctx 被取消。
func (s *Server) Start(ctx context.Context) (<-chan error, error) {
errCh := make(chan error, 1)
go func() { errCh <- s.ListenAndServe(ctx) }()
return errCh, nil
}
// ListenAndServe binds the configured host:port (default :8080) and blocks until
// ctx is canceled, then gracefully drains in-flight requests.
// ListenAndServe 绑定配置的 host:port(默认 :8080)并阻塞至 ctx 被
// 取消,随后优雅地排空在途请求。
func (s *Server) ListenAndServe(ctx context.Context) error {
if ctx == nil {
ctx = context.Background()
@@ -178,17 +173,15 @@ func (s *Server) ListenAndServe(ctx context.Context) error {
return nil
}
// Close frees the hub's bus subscription and closes live connections.
// Close 释放 hub 的 bus 订阅并关闭存活连接。
func (s *Server) Close() { s.hub.close() }
// newOrchestrator builds the session-message orchestrator (design/15) bound to
// this server's hub. The TurnRunner shares the hub's runTurn (so a turn always
// emits the same stream contract), and the StateSink fans per-session state
// transitions out through the hub broadcast for the frontend — those transitions
// are the design/11 §7.1 「状态机信号」, so every authenticated connection may follow
// them. It is wrapped in a
// function so the composition root can later override the injection (an explicit
// wiring point rather than a hard-coded construct).
// newOrchestrator 构建绑定到本 server 的 hub 的会话消息 orchestrator
// (design/15)。TurnRunner 复用 hub 的 runTurn(因此一轮总是发出相同的
// 流契约),StateSink 则把每会话状态迁移经由 hub 广播扇出给前端 ——
// 这些迁移属于 design/11 §7.1 的「状态机信号」,因此每个已认证连接都
// 可以跟随它们。它被包在函数里,以便组合根之后能覆盖注入(一个显式
// 的接线点,而不是硬编码的构造)。
func newOrchestrator(s *Server) *Orchestrator {
return NewOrchestrator(OrchestratorOptions{
Run: func(ctx context.Context, sessionID string, messages []model.Message, env loop.SessionEnv) (*loop.Result, error) {
@@ -203,8 +196,8 @@ func newOrchestrator(s *Server) *Orchestrator {
})
}
// sourcedLog returns a logger tagged with source "gateway", substituting a
// discard logger for nil so Server can always log safely.
// sourcedLog 返回带 source "gateway" 标签的 logger,nil 时替换为丢弃型
// logger,使 Server 总能安全地打日志。
func sourcedLog(l *slog.Logger) *slog.Logger {
if l == nil {
l = slog.New(slog.NewTextHandler(io.Discard, nil))