docs(gateway): gateway 剩余文件注释改中文(逐行 1:1,代码零字节改动)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

主人 2026-10-03 拍板「注释一律中文」;gateway 至此整包注释中文化完成(本会话共 3 批:
已推库 16 个文件 + 本批剩余文件)。
校验:机械校验(非注释改动行)= 空;剥注释后代码文本逐行比对全等(不一致文件数 0);
全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
This commit is contained in:
AskaEth committed 2026-10-03 22:28:25 +08:00
1 parent 91e8d7a029
commit 074ffc4ebf
73 files changed
+2529 -2529

No files matched your search

+85 -85
View File
@@ -14,10 +14,10 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
)
// tokenTTL is how long an issued JWT remains valid.
// tokenTTL 是已签发 JWT 的有效时长。
const tokenTTL = 24 * time.Hour
// PersonaSummary is the wire shape of a persona in REST/admin list responses.
// PersonaSummary 是 REST/管理列表响应中一个人格的线上结构。
type PersonaSummary struct {
ID string `json:"id"`
Name string `json:"name"`
@@ -26,53 +26,53 @@ type PersonaSummary struct {
Mode string `json:"mode"`
MemoryNS string `json:"memoryNamespace"`
Avatar string `json:"avatar,omitempty"`
// ChatBackground holds per-orientation wallpaper; a role's own, never another's.
// ChatBackground 持有分方向的壁纸;是本角色自己的,绝不是别人的。
ChatBackground ChatBackgroundSummary `json:"chatBackground,omitempty"`
// Mood is the role's current emotion state (design/01 §2: emotion follows the
// role, never a session). "" means the role has no tracked emotion — an honest
// empty, never a fabricated default. Clients seed their mood badge from it so a
// refresh no longer flashes an empty mood.
// Mood 是该角色的当前情绪状态(design/01 §2:情绪跟随角色,
// 绝不是会话)。"" 意味着该角色没有受跟踪的情绪 —— 诚实的
// 空值,绝不是编造的默认。客户端从它给 mood 角标播种,因此
// 刷新不再闪出空 mood。
Mood string `json:"mood"`
// Thinking carries the persona's optional reasoning/presentation block
// (design/01 §2.1). It is nil — and so absent from the JSON — when the persona
// configures none, mirroring the write side's "an omitted block keeps the
// stored one". Admin list only: these are model-call knobs, not user-UI state
// (the user list takes Mood and leaves the knobs to the panel).
// Thinking 携带人格可选的推理/呈现块
// (design/01 §2.1)。人格未配置时它是 nil —— 因而从 JSON 中缺席 ——
// 与写侧「省略的块保留
// 已存块」。仅管理列表:这些是模型调用开关,不是用户 UI 状态
// (用户列表取 Mood,把开关留给面板)。
Thinking *PersonaThinkingSummary `json:"thinking,omitempty"`
}
// PersonaThinkingSummary mirrors a persona's `thinking` block on the wire
// (design/01 §2.1; provider mapping in design/04 §3.2).
// PersonaThinkingSummary 在线上镜像人格的 `thinking` 块
// (design/01 §2.1;provider 映射见 design/04 §3.2)。
type PersonaThinkingSummary struct {
// BudgetTokens is the reasoning budget (Anthropic thinking.budget_tokens).
// BudgetTokens 是推理预算(Anthropic thinking.budget_tokens)。
BudgetTokens int `json:"budgetTokens"`
// Effort is the provider-specific reasoning strength (reasoning_effort). The
// model layer validates it against the active provider's whitelist and WARNs
// when it is dropped — this side just reports what is stored.
// Effort 是 provider 特有的推理强度(reasoning_effort)。
// 模型层会按活动 provider 的白名单校验它,并在它被丢弃时
// 打 WARN —— 这一侧只报告已存的内容。
Effort string `json:"effort"`
// ShowToUser is TRI-STATE and stays that way on the wire: null = not
// configured (thinking frames keep flowing — the pre-existing behavior),
// true/false = an explicit choice. A plain bool would flatten "unset" onto
// "off" and lose the distinction the write side keeps with *bool.
// ShowToUser 是三态的,且在线上保持如此:null = 未
// 配置(thinking 帧继续流出 —— 既有行为),
// true/false = 显式选择。普通 bool 会把「未设置」压平到
// 「关闭」上,丢掉写侧用 *bool 保持的区分。
ShowToUser *bool `json:"showToUser"`
// Mode is the explicit hard switch: default | on | off ("" = default).
// Mode 是显式的硬开关:default | on | off("" = default)。
Mode string `json:"mode"`
}
// ChatBackgroundSummary mirrors the persona chatBackground on the wire.
// ChatBackgroundSummary 在线上镜像人格的 chatBackground。
type ChatBackgroundSummary struct {
Landscape string `json:"landscape,omitempty"`
Portrait string `json:"portrait,omitempty"`
}
// MemorySummary is the wire shape of a memory entry in the admin list response.
// MemorySummary 是管理列表响应中一条记忆条目的线上结构。
type MemorySummary struct {
ID string `json:"id"`
Scope string `json:"scope"`
Text string `json:"text"`
}
// SessionSummary is the wire shape of a channel session in list responses.
// SessionSummary 是列表响应中一个渠道会话的线上结构。
type SessionSummary struct {
Platform string `json:"platform"`
ChannelID string `json:"channelId"`
@@ -83,15 +83,15 @@ type SessionSummary struct {
MemWrite bool `json:"memWritable"`
}
// loginRequest is the POST /api/v1/auth/login body.
// loginRequest 是 POST /api/v1/auth/login 的请求体。
type loginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
// loginResponse is the login success payload. RefreshToken is the opaque
// long-lived credential (POST /auth/refresh) added by B2; it is omitted when no
// refresh store is wired so the pre-B2 contract is unchanged.
// loginResponse 是登录成功的载荷。RefreshToken 是 B2 新增的非透明
// 长期凭据(POST /auth/refresh);未接线 refresh 存储时被省略,
// 因此 B2 之前的契约不变。
type loginResponse struct {
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
@@ -99,15 +99,15 @@ type loginResponse struct {
RefreshToken string `json:"refreshToken,omitempty"`
}
// chatRequest is the POST /api/v1/chat body. When Stream is true the gateway
// triggers streaming over the WebSocket channel and returns immediately. Image is
// an optional base64 data: URI or http(s) URL attached for the vision path; the
// loop routes it through the vision/OCR tool or the main model (design/06 §0).
// DeviceID is the client-produced identifier of the sending browser/device
// (design/16 §4.5), e.g. "web:chrome:a1b2c3"; it is optional and persisted as-is.
// Mode is the behavior mode the client sent the turn in (design/01 §3, design/16
// §5.1); it is optional and persisted per message for bubble rendering, falling
// back to "chat" when omitted.
// chatRequest 是 POST /api/v1/chat 的请求体。Stream 为 true 时网关
// 经 WebSocket 通道触发流式并立即返回。Image 是
// 为视觉路径附带的可选 base64 data: URI 或 http(s) URL;
// loop 把它经视觉/OCR 工具或主模型路由(design/06 §0)。
// DeviceID 是客户端产出的发送浏览器/设备标识
// (design/16 §4.5),例如 "web:chrome:a1b2c3";它可选且按原样持久化。
// Mode 是客户端发送该回合时的行为模式(design/01 §3、design/16
// §5.1);它可选,并按消息持久化以供气泡渲染,省略时
// 回退到 "chat"。
type chatRequest struct {
SessionID string `json:"sessionId"`
Text string `json:"text"`
@@ -117,19 +117,19 @@ type chatRequest struct {
Stream bool `json:"stream"`
}
// chatResponse is the synchronous chat reply payload.
// chatResponse 是同步聊天回复的载荷。
type chatResponse struct {
Reply string `json:"reply"`
Steps int `json:"steps"`
StopReason string `json:"stopReason"`
}
// handleHealth reports liveness. It is unauthenticated because it is a probe.
// handleHealth 报告存活。它不认证,因为它是探针。
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]any{"status": "ok"})
}
// handleLogin exchanges the admin username/password for a signed JWT.
// handleLogin 用管理用户名/密码换取已签名的 JWT。
func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
var req loginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -152,9 +152,9 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
ExpiresAt: time.Now().Add(tokenTTL).Unix(),
Role: string(auth.RoleOwner),
}
// Issue the long-lived refresh credential alongside the access token. A store
// failure fails the login rather than silently handing back a login without
// the advertised refresh token; the plaintext is never logged.
// 在签发访问令牌的同时签发长期 refresh 凭据。存储失败会让
// 登录失败,而不是静默交回一个没有所声称 refresh token 的
// 登录;明文绝不记日志。
if s.refresh != nil {
refreshToken, _, rerr := s.refresh.Issue(r.Context(), req.Username, auth.RoleOwner, s.refreshTTL())
if rerr != nil {
@@ -167,7 +167,7 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, resp)
}
// handleChat runs one synchronous agent turn (or triggers streaming).
// handleChat 运行一次同步 agent 回合(或触发流式)。
func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
var req chatRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -190,9 +190,9 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
if req.SessionID == "" {
req.SessionID = "web"
}
// The Web main session is the owner's private conversation (JWT = owner), so
// it binds the full-access env: memory read+write, unchanged behavior. The role
// comes from the session's binding (design/22 §4.5).
// Web 主会话是主人的私密对话(JWT = owner),因此它绑定
// 全访问 env:记忆读+写,行为不变。角色
// 来自会话的绑定(design/22 §4.5)。
msgs := userChatMessages(req.Text, req.Image)
env := s.envResolver.owner(r.Context(), req.SessionID)
if s.hub.orchestrator != nil {
@@ -205,8 +205,8 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
}
return
}
// A busy session returns a non-blocking ack (design/15 §2.1) instead of
// blocking the request on a turn that may run for a while.
// 忙碌会话返回非阻塞 ack(design/15 §2.1),而不是
// 让请求阻塞在一个可能跑一阵子的回合上。
if sr.Pending {
writeJSON(w, http.StatusAccepted, map[string]any{"queued": true, "queueDepth": sr.QueueDepth})
return
@@ -219,8 +219,8 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, chatResponse{Reply: res.Text, Steps: res.Steps, StopReason: res.StopReason})
return
}
// Legacy synchronous path when no orchestrator is wired (a hub built without
// one keeps the exact pre-design/15 reply contract).
// 未接线编排器时的旧同步路径(未接线的 hub
// 保持完全与 design/15 之前相同的回复契约)。
res, err := s.runner.Run(loop.WithSessionEnv(r.Context(), env), req.SessionID, msgs)
if err != nil {
s.log.Error("chat run", "err", err, "session", req.SessionID)
@@ -230,10 +230,10 @@ func (s *Server) handleChat(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, chatResponse{Reply: res.Text, Steps: res.Steps, StopReason: res.StopReason})
}
// handleListSessions returns the persistent chat-history sessions (main + sub) when a
// history store is wired, otherwise falls back to the channel-session view. The fallback
// keeps the pre-design/16 contract: a server built without history still lists the known
// channel sessions (or [] when no store is wired).
// handleListSessions 在接线了 history 存储时返回持久化聊天历史会话
// (main + sub),否则回退到渠道会话视图。回退保持
// design/16 之前的契约:未接 history 的服务器仍列出已知的
// 渠道会话(未接线任何存储时为 [])。
func (s *Server) handleListSessions(w http.ResponseWriter, r *http.Request) {
if s.history != nil {
s.handleHistorySessions(w, r)
@@ -254,16 +254,16 @@ func (s *Server) handleListSessions(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, out)
}
// handleListPersonas returns the personas (or [] when no store is wired). It is
// the user-side shape: the role-level mood rides along, the admin-only thinking
// knobs do not.
// handleListPersonas 返回人格(未接线存储时为 [])。它是
// 用户侧形状:角色级 mood 一并带上,仅管理的 thinking
// 开关则不然。
func (s *Server) handleListPersonas(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, s.collectPersonas(false))
}
// collectPersonas returns a non-nil persona slice from the configured store.
// withThinking adds the admin-only `thinking` block; mood is always reported
// because the user UI renders the badge.
// collectPersonas 从已配置的存储返回非 nil 的人格切片。
// withThinking 加上仅管理的 `thinking` 块;mood 总是报告,
// 因为用户 UI 要渲染角标。
func (s *Server) collectPersonas(withThinking bool) []PersonaSummary {
out := make([]PersonaSummary, 0)
if s.personas == nil {
@@ -282,9 +282,9 @@ func (s *Server) collectPersonas(withThinking bool) []PersonaSummary {
Mode: firstMode(c.Modes),
Mood: s.moodOf(id),
}
// Read this persona's own visual assets (avatar / chat background). Each
// role carries its own; a missing persona or unset visual yields empty,
// never another role's — the frontend falls back to a neutral default.
// 读取该人格自己的视觉资产(头像 / 聊天背景)。每个
// 角色携带自己的;人格缺失或视觉未设置得到空,
// 绝不是别的角色的 —— 前端回退到中性默认。
if p, ok := s.personas.Persona(id); ok && p != nil {
if p.Visual.Avatar != "" {
sum.Avatar = p.Visual.Avatar
@@ -302,10 +302,10 @@ func (s *Server) collectPersonas(withThinking bool) []PersonaSummary {
return out
}
// moodOf reports the role's current emotion state. Emotion is owned by the
// persona store (design/01 §2), read through the optional PersonaMoodStore
// capability; a store that does not track emotion, or a role with no machine,
// reports "" instead of a made-up mood.
// moodOf 报告该角色的当前情绪状态。情绪归
// 人格存储所有(design/01 §2),经可选的 PersonaMoodStore
// 能力读取;不跟踪情绪的存储,或没有情绪机的角色,
// 报告 "" 而不是编造一个 mood。
func (s *Server) moodOf(id string) string {
store, ok := s.personas.(PersonaMoodStore)
if !ok {
@@ -322,11 +322,11 @@ func (s *Server) moodOf(id string) string {
return string(machine.Current())
}
// thinkingSummaryOf maps a stored thinking block onto the wire. It returns nil
// when the persona configures none — the key is then absent, so a client can tell
// "not configured" from an explicit value (the read-side mirror of the write-side
// rule that an omitted block keeps the stored one). include is false for the
// user-side list, which does not carry these knobs.
// thinkingSummaryOf 把已存 thinking 块映射到线上。人格未配置时
// 它返回 nil —— 该键因而缺席,使客户端能把「未配置」
// 与显式取值区分开(写侧「省略的块保留已存块」规则的
// 读侧镜像)。用户侧列表的 include 为 false,
// 因为用户侧不携带这些开关。
func thinkingSummaryOf(t loader.ThinkingParams, include bool) *PersonaThinkingSummary {
if !include || !thinkingConfigured(t) {
return nil
@@ -339,13 +339,13 @@ func thinkingSummaryOf(t loader.ThinkingParams, include bool) *PersonaThinkingSu
}
}
// thinkingConfigured reports whether any thinking knob is set. An empty block
// must read as "not configured" rather than as an all-zero object.
// thinkingConfigured 报告是否有任何 thinking 开关被设置。空块
// 必须读作「未配置」,而不是全零对象。
func thinkingConfigured(t loader.ThinkingParams) bool {
return t.BudgetTokens != 0 || t.Effort != "" || t.ShowToUser != nil || t.Mode != ""
}
// firstMode returns the first declared behavior mode, or "" when none are set.
// firstMode 返回首个声明的行为模式,未设置时返回 ""。
func firstMode(modes []string) string {
if len(modes) == 0 {
return ""
@@ -353,9 +353,9 @@ func firstMode(modes []string) string {
return modes[0]
}
// userChatMessages builds the user turn for a chat / stream request. When image is
// present the turn becomes an image message (with optional text); otherwise a
// plain text message. An empty text with an image yields an image-only turn.
// userChatMessages 为 chat / stream 请求构建用户回合。image
// 存在时该回合成为图片消息(可带文本);否则是
// 纯文本消息。空文本加图片得到仅图片的回合。
func userChatMessages(text, image string) []model.Message {
if strings.TrimSpace(image) != "" {
return []model.Message{model.ImageMessage(model.RoleUser, text, image)}
@@ -363,7 +363,7 @@ func userChatMessages(text, image string) []model.Message {
return []model.Message{model.TextMessage(model.RoleUser, text)}
}
// summaryOfSession maps a session.Session to its wire shape.
// summaryOfSession 把 session.Session 映射为其线上结构。
func summaryOfSession(sess session.Session) SessionSummary {
return SessionSummary{
Platform: sess.Platform,
@@ -376,15 +376,15 @@ func summaryOfSession(sess session.Session) SessionSummary {
}
}
// writeJSON encodes v as the {data: v} envelope with the given status.
// writeJSON 把 v 编码为 {data: v} 信封并带上给定状态。
func writeJSON(w http.ResponseWriter, status int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(map[string]any{"data": v})
}
// writeError encodes err as the {error:{code,message}} envelope with the status
// resolved by apperr.HTTPStatus.
// writeError 把 err 编码为 {error:{code,message}} 信封,状态由
// apperr.HTTPStatus 解析。
func writeError(w http.ResponseWriter, err error) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(apperr.HTTPStatus(err))