- GET /api/v1/admin/backthink 加可选 ?trigger=(闭集=内建触发标签 + 本服务订阅的事件主题;
未知/空 ⇒ 400 invalid_trigger,不静默忽略;合法值下推到 SQL ⇒ limit 是匹配条数)
- 新端点 GET /api/v1/admin/backthink/self-wake(writeAuth):闸的只读镜像
{available,running,reason?,configFile?,gate{pending,used,limit,windowSec,default*,*IsDefault}}
三态分开(未接线 / 有配置未运行 / 运行中),读它**不消耗额度**
- 零新 env 键;旋钮源 = $DATA_DIR/config/backthink.json(**写面未做**,前置=组合根接线,已登记)
- 键名沿用设计与现有代码的 selfWakePerWindow(派工书写错成 selfWakeLimit,未改键名以免废掉主人已写的配置)
- 5 个变异(未知值静默忽略/关掉下推/恒报运行中/白名单摘键/前端把看不到画成 0/0)
⚠️ 本片改了 4 个文件面外文件(admin_query.go/routes.go/api.ts/adminSystem.ts)——已逐条说明理由
119 lines
5.5 KiB
Go
119 lines
5.5 KiB
Go
package gateway
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/config/runtimeconfig"
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/services/backthink"
|
||
)
|
||
|
||
// 本文件是 `GET /api/v1/admin/backthink/self-wake` 的**只读用量可见面**
|
||
// (design/12 §N C.5 的读侧,切片 C2)。
|
||
//
|
||
// 为什么要有它:C.5 的「本窗口已用 N / 上限 M」此前**只**出现在被拒那一轮的审计文案里
|
||
// (`backthink.self_wake.capped` 的 result)—— 也就是「已经被拒过一次」才第一次看得见。
|
||
// 面板要能提前告诉主人「还能自排几次」,就得有一个只读快照出口。
|
||
//
|
||
// 它是**纯读**:不写库、不落审计、不碰闸(读快照永远不改变闸的状态),与列表端点同门
|
||
// 同档(writeAuth = owner|admin;含她自排的成本口径与运行状态,不是给 viewer 的监控投影)。
|
||
//
|
||
// ⚠️ 它**不是**写口:调这两个旋钮要改 `$DATA_DIR/config/backthink.json` + 重启
|
||
// (启动期读一次,与 `QUIET_*` 同口径)。面板写路径未做,原因见 runtimeconfig
|
||
// `BackgroundTuning` 的注释(要经 design/09 §5 的面板声明机制,需组合根接线)。
|
||
|
||
// selfWakeStatusReader 是自排闸只读快照的**可选**读口:生产实现
|
||
// `*backthink.Backthink` 满足它。用可选接口的理由同 backthinkTriggerLister
|
||
// (不改共享契约、不弄坏既有测试替身);不满足时如实回「本实现不提供读口」。
|
||
type selfWakeStatusReader interface {
|
||
SelfWakeStatus() (backthink.SelfWakeStatus, bool)
|
||
}
|
||
|
||
// 编译期钉住生产实现满足本可选口(形状漂移在编译期立刻显形,而不是等到运行期
|
||
// 悄悄变成「本实现不提供自排状态读口」)。
|
||
var _ selfWakeStatusReader = (*backthink.Backthink)(nil)
|
||
|
||
// AdminSelfWakeGate 是自排闸的线上投影:**进程生效**的上限 / 窗口 + 本窗口用量 +
|
||
// 待跑槽占用,外带代码默认值(供面板区分「默认 / 已改」)。
|
||
type AdminSelfWakeGate struct {
|
||
// Pending 报告容量 1 的待跑槽是否占用(有一轮自排排着队还没跑)。
|
||
Pending bool `json:"pending"`
|
||
// Used 是本窗口已用额度(与工具收据 `SelfWakeReceipt.Used` 同一口径)。
|
||
Used int `json:"used"`
|
||
// Limit / WindowSec 是**进程生效**的上限与窗口(已按「≤ 0 回落默认」normalize)。
|
||
Limit int `json:"limit"`
|
||
WindowSec int `json:"windowSec"`
|
||
// DefaultLimit / DefaultWindowSec 是代码默认值;LimitIsDefault / WindowIsDefault
|
||
// 报告生效值**恰好等于**默认值。
|
||
//
|
||
// ⚠️ 它**不是**「配置文件里没写这个键」的断言:网关**不读** `backthink.json`
|
||
// (与 config-facts 的 isDefault 同一条诚实口径 —— 无从不区分「配了 4」与「没配」)。
|
||
DefaultLimit int `json:"defaultLimit"`
|
||
DefaultWindowSec int `json:"defaultWindowSec"`
|
||
LimitIsDefault bool `json:"limitIsDefault"`
|
||
WindowIsDefault bool `json:"windowIsDefault"`
|
||
}
|
||
|
||
// AdminSelfWakeStatus 是 `GET /api/v1/admin/backthink/self-wake` 的响应。
|
||
//
|
||
// 三态**必须可区分**(面板不得把「看不到」画成「0 次」):
|
||
// - `available:false` ⇒ 本进程给不出快照(服务未接线 / 实现不提供读口 / 闸不在),
|
||
// 此时 `gate` **缺席**、`reason` 说明原因;
|
||
// - `available:true, running:false` ⇒ 已接线但不在运行(未启动或已停止):`gate`
|
||
// 仍带配置面数字(上限/窗口/默认值),但此刻她排不出新的一轮 —— `reason` 说明;
|
||
// - `available:true, running:true` ⇒ 正常态,`reason` 缺席。
|
||
type AdminSelfWakeStatus struct {
|
||
Available bool `json:"available"`
|
||
Running bool `json:"running"`
|
||
Reason string `json:"reason,omitempty"`
|
||
// ConfigFile 是「要改哪个文件」的相对落点(`config/backthink.json`,相对 `$DATA_DIR`):
|
||
// 绝对路径随部署而变,接口只给相对落点,由面板拼上下文说明。
|
||
ConfigFile string `json:"configFile,omitempty"`
|
||
Gate *AdminSelfWakeGate `json:"gate,omitempty"`
|
||
}
|
||
|
||
// adminSelfWakeStatus 下发自排闸的只读快照(见文件头注释)。
|
||
func (s *Server) adminSelfWakeStatus(w http.ResponseWriter, r *http.Request) {
|
||
// 未接线:如实现状作答(与 adminListBackthink 返回空列表同口径:200 + 诚实说明,
|
||
// 不假装有一个 0/0 的闸)。
|
||
if s.backthink == nil {
|
||
writeJSON(w, http.StatusOK, AdminSelfWakeStatus{
|
||
Reason: "后台思考服务未接线:本进程不产生自排轮",
|
||
})
|
||
return
|
||
}
|
||
reader, ok := s.backthink.(selfWakeStatusReader)
|
||
if !ok {
|
||
writeJSON(w, http.StatusOK, AdminSelfWakeStatus{
|
||
Reason: "当前后端的后台思考实现不提供自排状态读口",
|
||
})
|
||
return
|
||
}
|
||
st, ok := reader.SelfWakeStatus()
|
||
if !ok {
|
||
writeJSON(w, http.StatusOK, AdminSelfWakeStatus{
|
||
Reason: "自排闸不在(后台思考服务未构造):没有可报的用量",
|
||
})
|
||
return
|
||
}
|
||
|
||
out := AdminSelfWakeStatus{
|
||
Available: true,
|
||
Running: st.Running,
|
||
ConfigFile: runtimeconfig.BackthinkConfigRelPath,
|
||
Gate: &AdminSelfWakeGate{
|
||
Pending: st.Pending,
|
||
Used: st.Used,
|
||
Limit: st.Limit,
|
||
WindowSec: st.WindowSec,
|
||
DefaultLimit: st.DefaultLimit,
|
||
DefaultWindowSec: st.DefaultWindowSec,
|
||
LimitIsDefault: st.LimitIsDefault,
|
||
WindowIsDefault: st.WindowIsDefault,
|
||
},
|
||
}
|
||
if !st.Running {
|
||
out.Reason = "后台思考未在运行(未启动或已停止):此刻她排不出新的一轮"
|
||
}
|
||
writeJSON(w, http.StatusOK, out)
|
||
}
|