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

@@ -21,27 +21,27 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/plugins/channels/weixin_ilink"
)
// LLM-provider and channel-lifecycle admin writes (owner-only, audited). The LLM
// test performs a real model call; provider "add" records a process-lifetime
// entry. Channel open/close drive the real adapter lifecycle; the identity test
// is the structural actor × whitelist rule, never prompt text.
// LLM provider 与渠道生命周期的管理写操作(仅 owner,带审计)。LLM
// 测试会发起真实模型调用;provider「add」记录一条
// 进程生命周期内的条目。渠道开/关驱动真实适配器生命周期;身份
// 测试走结构化的 actor × 白名单规则,绝不是提示词文本。
const (
// ownerProfilePath is the owner whitelist loaded for the structural identity
// test. It mirrors the path wire.go uses for owner recognition.
// ownerProfilePath 是为结构化身份测试加载的主人白名单。
// 它与 wire.go 用于主人识别的路径一致。
ownerProfilePath = "configs/users/owner.yaml"
// llmTestTimeout bounds a single LLM probe request.
// llmTestTimeout 限定单次 LLM 探测请求。
llmTestTimeout = 30 * time.Second
// llmProbePrompt is the minimal prompt used to verify a model responds.
// llmProbePrompt 是用于验证模型能应答的最小提示词。
llmProbePrompt = "Reply with the single word: OK"
)
// llmProviderRequest is the POST /api/v1/admin/llm body for adding/updating a
// provider group. Every field is a pointer so the handler can tell "absent"
// (preserve the stored value) from "explicitly zero" (overwrite): a partial edit
// from the panel must never wipe a field it did not touch. Name is a client-side
// display label (not persisted — the models.json schema carries id/provider/
// baseUrl/apiKey/model/fallback/multimodal/maxTokens).
// llmProviderRequest 是 POST /api/v1/admin/llm 的请求体,用于添加/更新
// provider 分组。每个字段都是指针,使 handler 能区分「缺失」
// (保留已存值)与「显式零值」(覆盖):来自面板的局部
// 编辑绝不清除它未触碰的字段。Name 是客户端侧
// 显示标签(不持久化 —— models.json 结构含 id/provider/
// baseUrl/apiKey/model/fallback/multimodal/maxTokens)。
type llmProviderRequest struct {
ID *string `json:"id"`
Name *string `json:"name"`
@@ -51,16 +51,16 @@ type llmProviderRequest struct {
Model *string `json:"model"`
Multimodal *bool `json:"multimodal"`
Fallback *string `json:"fallback"`
// MaxTokens is the group's own token budget. Absent preserves the stored
// budget (0 = unset for a new group). Negative is rejected.
// MaxTokens 是该分组自身的 token 预算。缺失则保留已存
// 预算(新分组 0 = 未设置)。负值被拒绝。
MaxTokens *int `json:"maxTokens"`
}
// adminAddLlmProvider saves a provider group to models.json (read-modify-write,
// promoting it to the active group) and hot-reloads the running LLM stack so the
// new provider/model takes effect without a restart (design/04 §3.1). A field the
// client omits keeps its stored value, so a partial edit never resets a budget or
// key. The API key is persisted to the gitignored file but never returned/logged.
// adminAddLlmProvider 把 provider 分组保存到 models.json(read-modify-write,
// 并将其提升为活动分组),再热重载运行中的 LLM 栈,使
// 新的 provider/model 无需重启即生效(design/04 §3.1)。客户端省略的
// 字段保持其已存值,因此局部编辑绝不复位预算或
// key。API key 被持久化进被 gitignore 的文件,但绝不返回/记日志。
func (s *Server) adminAddLlmProvider(w http.ResponseWriter, r *http.Request) {
var req llmProviderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -83,8 +83,8 @@ func (s *Server) adminAddLlmProvider(w http.ResponseWriter, r *http.Request) {
writeError(w, apperr.Wrap(apperr.ErrBadRequest, "bad_request", "%v", err))
return
}
// Promote the saved group to the active (index 0) slot, then persist and
// hot-reload so Current() reflects the new provider/model immediately.
// 把已保存的分组提升到活动(下标 0)槽位,然后持久化并
// 热重载,使 Current() 立即反映新的 provider/model。
groups := runtimeconfig.Activate(s.llm.Groups(), group)
if err := runtimeconfig.SaveModels(s.llm.Path(), groups); err != nil {
s.auditClaims(r, "llm.addProvider", "error")
@@ -117,9 +117,9 @@ func (s *Server) adminAddLlmProvider(w http.ResponseWriter, r *http.Request) {
})
}
// mergeLlmProvider applies a partial provider patch over the stored groups: a
// field present in the request wins, an absent field keeps the stored value (or
// its default for a new group). It returns a validated group.
// mergeLlmProvider 把局部 provider 补丁施加到已存分组之上:
// 请求中出现的字段胜出,缺失字段保留已存值(新分组
// 则用其默认值)。它返回经过校验的分组。
func mergeLlmProvider(groups []runtimeconfig.Group, req llmProviderRequest) (runtimeconfig.Group, error) {
id := ""
if req.ID != nil {
@@ -166,7 +166,7 @@ func mergeLlmProvider(groups []runtimeconfig.Group, req llmProviderRequest) (run
return g, nil
}
// llmTestRequest is the POST /api/v1/admin/llm/test body.
// llmTestRequest 是 POST /api/v1/admin/llm/test 的请求体。
type llmTestRequest struct {
Provider string `json:"provider"`
BaseURL string `json:"baseUrl"`
@@ -175,8 +175,8 @@ type llmTestRequest struct {
Prompt string `json:"prompt"`
}
// adminTestLlm performs a real completion against the supplied provider/model
// endpoint and reports the result — a genuine round-trip, not a placeholder.
// adminTestLlm 针对提供的 provider/model 端点执行一次真实补全
// 并报告结果 —— 真实的往返,而不是占位。
func (s *Server) adminTestLlm(w http.ResponseWriter, r *http.Request) {
var req llmTestRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -217,12 +217,12 @@ func (s *Server) adminTestLlm(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "provider": provider, "model": resp.Model, "reply": resp.Content, "tokens": resp.TotalTokens})
}
// channelActionRequest is the POST /api/v1/admin/channels/open|close body.
// channelActionRequest 是 POST /api/v1/admin/channels/open|close 的请求体。
type channelActionRequest struct {
Name string `json:"name"`
}
// adminChannelOpen connects a registered channel adapter (real lifecycle call).
// adminChannelOpen 连接一个已注册渠道适配器(真实的生命周期调用)。
func (s *Server) adminChannelOpen(w http.ResponseWriter, r *http.Request) {
name, ok := s.channelName(r, w, "channel.open")
if !ok {
@@ -236,17 +236,17 @@ func (s *Server) adminChannelOpen(w http.ResponseWriter, r *http.Request) {
}
if err := ch.Connect(r.Context()); err != nil {
s.auditClaims(r, "channel.open", "error")
// A state a plain reconnect cannot fix must not read as a generic failure:
// the weixin-iLink bot token was rejected by iLink (errcode=-14), so the
// owner needs a fresh scan-code login. Answer 409 with that instruction
// instead of pretending the channel opened (the panel marks it connected).
// 单纯重连修不好的状态不得读作通用失败:
// weixin-iLink 机器人 token 被 iLink 拒绝(errcode=-14),因此
// 主人需要一次新的扫码登录。用该指引返回 409,
// 而不是假装渠道已打开(面板会把它标记为已连接)。
if errors.Is(err, weixin_ilink.ErrSessionRejected) {
writeError(w, apperr.Wrap(apperr.ErrConflict, "session_rejected",
"该渠道会话已被 iLink 拒绝(errcode=-14):请用「扫码登录」重新绑定后再连接"))
return
}
// weixin-iLink with no token yet must not hang nor read as a credential
// error: the panel routes the owner to the out-of-band 扫码登录 flow.
// 尚无 token 的 weixin-iLink 既不能挂起,也不能读作凭据
// 错误:面板会把主人导向带外的扫码登录流程。
if errors.Is(err, weixin_ilink.ErrNeedsLogin) {
writeError(w, apperr.Wrap(apperr.ErrConflict, "needs_login", "该渠道尚未登录:请先用「扫码登录」绑定,再连接"))
return
@@ -259,7 +259,7 @@ func (s *Server) adminChannelOpen(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]any{"name": name, "connected": true})
}
// adminChannelClose disconnects a registered channel adapter (real lifecycle call).
// adminChannelClose 断开一个已注册渠道适配器(真实的生命周期调用)。
func (s *Server) adminChannelClose(w http.ResponseWriter, r *http.Request) {
name, ok := s.channelName(r, w, "channel.close")
if !ok {
@@ -281,17 +281,17 @@ func (s *Server) adminChannelClose(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]any{"name": name, "connected": false})
}
// channelTestRequest is the POST /api/v1/admin/channels/test body: a channel
// account id plus the conversation shape, resolved by the structural actor rule.
// channelTestRequest 是 POST /api/v1/admin/channels/test 的请求体:一个渠道
// 账号 id 加上对话形态,由结构化 actor 规则解析。
type channelTestRequest struct {
Platform string `json:"platform"`
AccountID string `json:"accountId"`
IsGroup bool `json:"isGroup"`
}
// adminChannelTest runs the structural identity rule (actor metadata × owner
// whitelist), never prompt text. It loads the owner whitelist from disk so a
// mismatch degrades to the conservative default (member/stranger).
// adminChannelTest 运行结构化身份规则(actor 元数据 × 主人
// 白名单),绝不是提示词文本。它从磁盘加载主人白名单,
// 因此不匹配时退化为保守默认(member/stranger)。
func (s *Server) adminChannelTest(w http.ResponseWriter, r *http.Request) {
var req channelTestRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -327,8 +327,8 @@ func (s *Server) adminChannelTest(w http.ResponseWriter, r *http.Request) {
})
}
// channelName decodes the {name} body for open/close routes, writing a 400 on
// failure. It reports uniqueness of the decode via ok.
// channelName 为 open/close 路由解码 {name} 请求体,失败时写出 400。
// 它通过 ok 报告解码的唯一性。
func (s *Server) channelName(r *http.Request, w http.ResponseWriter, op string) (string, bool) {
var req channelActionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -343,11 +343,11 @@ func (s *Server) channelName(r *http.Request, w http.ResponseWriter, op string)
return req.Name, true
}
// findChannel returns the registered channel adapter named name, if any. An exact
// adapter name ("kook:conf0") wins; a bare platform name ("kook") that is not an
// exact name still resolves to that platform's first registered account, so the
// panel/CLI "connect kook" reaches the token-bearing account even when it is named
// "kook:conf0" (the empty-token bare placeholder is never registered).
// findChannel 返回名为 name 的已注册渠道适配器(若有)。精确的
// 适配器名("kook:conf0")胜出;非精确名的裸平台名("kook")
// 仍会解析到该平台首个已注册账号,因此面板/CLI 的
// "connect kook" 能抵达携带 token 的账号,即使它名为
// "kook:conf0"(空 token 的裸占位符从不注册)。
func findChannel(caps CapabilityStore, name string) (sdk.Channel, bool) {
if caps == nil {
return nil, false
@@ -371,8 +371,8 @@ func findChannel(caps CapabilityStore, name string) (sdk.Channel, bool) {
return nil, false
}
// loadOwnerProfile loads the owner whitelist from disk, returning nil on any
// failure so the identity test degrades to the conservative deny.
// loadOwnerProfile 从磁盘加载主人白名单,任何失败都返回
// nil,因此身份测试退化为保守的拒绝。
func loadOwnerProfile() *user.OwnerProfile {
p, err := user.LoadOwnerProfile(ownerProfilePath)
if err != nil {
@@ -381,7 +381,7 @@ func loadOwnerProfile() *user.OwnerProfile {
return p
}
// describeIdentity renders the structural verdict for the identity test.
// describeIdentity 为身份测试渲染结构化判定结果。
func describeIdentity(a adapter.Actor, _ *user.OwnerProfile) string {
switch {
case a.IsGroupChat():