Download internal/pool/transition.go from a3216/gcli2api: direct link, hf CLI and curl.
- Browser
- Download file 5.67 kB
-
https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/pool/transition.go
- Command line
-
hf download hf://spaces/a3216/gcli2api/internal/pool/transition.go
-
curl -L -o transition.go https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/pool/transition.go
5.67 kB
| // 账号状态机迁移的唯一权威实现。 | |
| // | |
| // entry 的「可选择性」由四个正交维度决定:禁用(disabled)、账号级冷却(until/coolKind)、 | |
| // 模型级冷却(modelCooldowns)、熔断(breakerUntil)。维度之间以「迁移原语」收拢, | |
| // 禁止在其他文件散写这些字段——所有入口(applyErrorPolicy / refresh / keepalive / | |
| // 签到 / 选号)对状态的改动都必须经本文件的原语或经 Cooldown/NoteError/NoteSuccess 等 | |
| // 封装(它们在持锁下调用本文件原语)。 | |
| // | |
| // 迁移矩阵(事件 → 动作 → 字段): | |
| // | |
| // disabled ← disableLocked(Disable / NoteSessionDead 达阈) | |
| // until/coolKind ← Cooldown(CoolSoft/Hard) / CooldownSoftForModel 无解析分支 | |
| // modelCooldowns ← CooldownSoftForModel 有解析分支;被 disableLocked/Cooldown/clearCoolingLocked 清 | |
| // (reviveCoolingLocked 不清——余额恢复不构成限流解除证据) | |
| // breakerUntil ← recordBreakerFailureLocked(Cooldown/NoteError 喂入);NoteSuccess 清 | |
| // softStreak ← Cooldown(CoolSoft)/CooldownSoftForModel;NoteSuccess 清(revive 保留:与余额无关) | |
| // sessionDeadFails ← NoteSessionDead;ClearSessionDead/NoteSuccess/ReviveDisabled 清 | |
| // | |
| // 关键正交性(疑点 4 修正): | |
| // - 冷却域(until/coolKind/softStreak/modelCooldowns)与熔断器(fails/retryCount/ | |
| // breakerUntil)正交:冷却管「近期被限流/余额耗尽」,熔断管「反复 5xx 失败」。 | |
| // disableLocked 只清冷却域、不动熔断——禁用是授权/session 终态,不应覆盖熔断观测。 | |
| // - clearCoolingLocked 是「冷却域归零」的单一来源,被 disableLocked 共用 | |
| // (禁用是终态,冷却随之作废)。reviveCoolingLocked(签到/余额刷新解冻)**不再** | |
| // 走全清:余额恢复只解冻 CoolHard,软限流与模型级台账各有自身恢复时刻 | |
| // (详见 reviveCoolingLocked 注释)。 | |
| package pool | |
| import "time" | |
| // clearCoolingLocked 清冷却域:until/coolKind/softStreak/modelCooldowns 全归零, | |
| // reason 一并清空。熔断器(fails/retryCount/breakerUntil)不属冷却域,不动。 | |
| // 调用方必须已持有 p.mu。 | |
| func (e *entry) clearCoolingLocked() { | |
| e.until = time.Time{} | |
| e.coolKind = 0 | |
| e.reason = "" | |
| e.softStreak = 0 | |
| e.modelCooldowns = nil // 冷却域清零时一并清模型级独立冷却(模型豁免随之消失) | |
| } | |
| // disableLocked 禁用迁移:置 disabled 并清冷却域(禁用是比冷却更强的不可用终态)。 | |
| // | |
| // 旧 Disable 只置 disabled+reason,不碰 until/modelCooldowns/softStreak,会出现 | |
| // 「disabled=true 但 cooling=true / 残留 modelCooldowns」的一致性问题——一个先被 | |
| // 硬冷却(到次日 04:00)再被禁用的账号会同时呈现两种状态。禁用后冷却无意义 | |
| // (账号已退出选号,冷却截止不再被读取),故一并清空。 | |
| // | |
| // 熔断器保留:熔断是「连续 5xx 失败」信号(与授权/会话无关),禁用后再复活时 | |
| // 熔断观测仍有效,不应被禁用覆盖。 | |
| func (p *Pool) disableLocked(e *entry, reason string) { | |
| e.clearCoolingLocked() | |
| e.disabled = true | |
| e.paused = false // 禁用是比暂停更强的终态,二者不叠加(禁用后须 revive 才能复用) | |
| e.reason = reason | |
| p.dirty.Store(true) | |
| } | |
| // pauseLocked 暂停选号迁移:置 paused 使账号退出选号候选(healthy 判否), | |
| // 但**不动任何惩罚域**——paused 是「临时让位」,账号本身健康,恢复后冷却/熔断 | |
| // 观测仍有效。与 disableLocked 的对比:disabled 清冷却域且须人工 revive(终态), | |
| // paused 只置一个标志、随时可 resume(瞬时态)。 | |
| // 保号任务(scheduler)遍历只按 Disabled 过滤,故 paused 号天然继续参与签到/ | |
| // 活跃上报/保活/余额刷新——这正是「暂停选号但不掉保号」的实现基础。 | |
| func (p *Pool) pauseLocked(e *entry) { | |
| e.paused = true | |
| p.dirty.Store(true) | |
| } | |
| // resumeLocked 解除暂停选号(幂等):只清 paused,账号若不在其它惩罚期即恢复可选。 | |
| func (p *Pool) resumeLocked(e *entry) { | |
| e.paused = false | |
| p.dirty.Store(true) | |
| } | |
| // reviveCoolingLocked 余额恢复解冻:只清**余额耗尽冷却**(CoolHard 的 | |
| // until/coolKind/reason)并更新 credits/creditsTotal。 | |
| // | |
| // 不动 CoolSoft 软限流退避、softStreak 与 modelCooldowns(6004 模型级台账): | |
| // 后两者的恢复证据是上游重置墙钟到期或探测成功,不是「余额有钱」。余额刷新 | |
| // 周期任务(每 5 分钟)经 ReenableIfCredits 到达这里——若在此清空整个冷却域, | |
| // 任何限流冷却的实际寿命都被压到一个刷新周期内:6004 台账被抹后撞限号被误判 | |
| // 健康、重新选中再撞 429,全池冷却保护形同虚设(两号池实测复现)。softStreak | |
| // 亦保留:退避计数与余额无关,由 NoteSuccess(成功是最强恢复证据)或自然到期 | |
| // 收敛。硬冷却(CoolHard)的权威恢复证据正是余额恢复(remain>0),照旧解冻。 | |
| // 不动熔断器(fails/retryCount/breakerUntil)——签到成功只证明余额恢复与 | |
| // billing 通道健康,不证明 chat 通道健康。调用方必须已持有 p.mu。 | |
| func (p *Pool) reviveCoolingLocked(e *entry, credits, total int64) { | |
| e.credits = credits | |
| e.creditsTotal = total | |
| if e.coolKind == CoolHard { | |
| e.until = time.Time{} | |
| e.coolKind = 0 | |
| e.reason = "" | |
| } | |
| } | |