pi-ccswitch-auto-switch 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -130,7 +130,7 @@ CCS v0.3.5 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
130
130
  - `⛔0`: no manually disabled models.
131
131
  - `🔄3`: **本次 pi session 成功切换的模型次数**(衡量扩展有效程度)。`/new`、`/fork`、`/resume` 等新 session 开始时归零;但**失败记录与冷却状态不重置**(它们是物理事实,跨 session 保留)。累计切换数(`state.switches`)与最近 20 条切换日志会持久化到状态文件,可在 `/ccswitch` 面板和 `/ccswitch-test` 中查看。
132
132
  - `provider/model-id`: 当前实际生效的模型(`provider/id`,切换后立即更新,长名自动截断)。
133
- - During a switch, `CCS ↻2/5 provider/model` means the second of at most five attempts is being made.
133
+ - During a switch, `CCS ↻2 provider/model` means this is the second switch attempt of the current round. Attempts are only bounded by the round time window, not a fixed count; a round fails only once every candidate has been tried.
134
134
 
135
135
  四个状态(健康/冷却/禁用/切换)即使为 0 也始终显示,便于确认扩展处于监控中。All-healthy still shows zero counters, e.g. `CCS ✓131/131 · ⏳0 · ⛔0 · 🔄0 · provider/model-id`. Use `/ccswitch` or `/ccswitch-test` to see Pi's raw scope entry count, the deduplicated model count, affected model counts, and the underlying breaker-record count.
136
136
 
package/README.zh-CN.md CHANGED
@@ -118,7 +118,7 @@ CCS v0.3.5 ✓70/131 · ⏳61 · ⛔0 · 🔄3 · provider/model-id
118
118
  - `⛔0`:没有被手动禁用的模型。
119
119
  - `🔄3`:**本次 pi session 成功切换的模型次数**(衡量扩展有效程度,0 时也显示图标)。`/new`、`/fork`、`/resume` 等新 session 开始时归零;但**失败记录与冷却状态不重置**(它们是物理事实,跨 session 保留)。累计切换数(`state.switches`)与最近 20 条切换日志会持久化到状态文件,可在 `/ccswitch` 面板和 `/ccswitch-test` 中查看。
120
120
  - `provider/model-id`:当前实际生效的模型(`provider/id`,切换后立即更新,长名自动截断)。
121
- - 切换中出现 `CCS ↻2/5 provider/model`,表示正在进行最多 5 次尝试中的第 2 次。
121
+ - 切换中出现 `CCS ↻2 provider/model`,表示这是本轮的第 2 次切换尝试。切换次数只受本轮时间窗口限制,不再有固定次数上限;只有当所有候选都尝试过仍无可用模型时才会判定本轮失败。
122
122
 
123
123
  四个状态(健康/冷却/禁用/切换)即使为 0 也始终显示,便于确认扩展处于监控中;全部健康时仍显示零计数,例如 `CCS ✓131/131 · ⏳0 · ⛔0 · 🔄0 · provider/model-id`。通过 `/ccswitch` 或 `/ccswitch-test` 可同时查看 Pi 原始 scope 条目数、去重模型数、受影响模型数、底层熔断记录数、本 session 切换数与累计切换数。
124
124
 
package/index.ts CHANGED
@@ -10,7 +10,6 @@ import { HealthStore, endpointKey, modelKey, type HealthState } from './health.t
10
10
 
11
11
  const FIRST_RESPONSE_TIMEOUT = 90_000
12
12
  const STREAM_IDLE_TIMEOUT = 120_000
13
- const MAX_ATTEMPTS = 5
14
13
  const ROUND_LIMIT = 8 * 60_000
15
14
  const RPC_PROTOCOL_VERSION = 1
16
15
  const EXTENSION_VERSION = '0.3.6'
@@ -41,7 +40,7 @@ interface Round {
41
40
  endpointFails?: EndpointFailTracker
42
41
  /** 本轮内容审查故障转移中必须避开的模型系列(包含持久化学到的约束)。 */
43
42
  avoidFamilies?: Set<string>
44
- /** 本轮内最后一次成功切换的时间;用于刷新 ROUND_LIMIT 窗口,避免供应商内部重试耗时导致误判“达到上限” */
43
+ /** 本轮内最后一次成功切换的时间;用于刷新 ROUND_LIMIT 窗口,避免供应商内部重试耗时导致误判“超过本轮时间限制” */
45
44
  lastSwitchAt?: number
46
45
  }
47
46
 
@@ -104,7 +103,7 @@ export default function (pi: ExtensionAPI) {
104
103
  const activeModel = round?.model ?? ctx.model
105
104
  // 恒显完整状态:健康/总数 · 冷却 · 禁用 · 本session切换 · 当前模型(均为 0 时也显示,便于确认扩展在监控中)
106
105
  const prefix = `CCS v${EXTENSION_VERSION}`
107
- const plain = round?.phase === 'switching' ? `${prefix} ↻${round.attempts}/${MAX_ATTEMPTS} ${modelTag(round.model)}` :
106
+ const plain = round?.phase === 'switching' ? `${prefix} ↻${round.attempts} ${modelTag(round.model)}` :
108
107
  round?.phase === 'exhausted' ? `${prefix} ⏸切换停止 ${modelTag(round.model ?? ctx.model)}` :
109
108
  `${prefix} ✓${counts.healthy}/${counts.total} · ⏳${counts.cooling} · ⛔${counts.disabled}${switchSuffix()} · ${modelTag(activeModel)}`
110
109
  const theme = ctx.ui.theme
@@ -246,7 +245,7 @@ export default function (pi: ExtensionAPI) {
246
245
  if (!round || !round.observation || !round.model || !canRetry(ctx)) return
247
246
  // 窗口从上一次成功切换(或本轮开始)起算:供应商内部重试耗时不应消耗整轮限额
248
247
  const windowStart = Math.max(round.startedAt, round.lastSwitchAt ?? 0)
249
- if (round.attempts >= MAX_ATTEMPTS || Date.now() - windowStart >= ROUND_LIMIT) return exhaust(ctx, '达到本轮切换上限')
248
+ if (Date.now() - windowStart >= ROUND_LIMIT) return exhaust(ctx, '超过本轮时间限制')
250
249
  const classification = classifyFailure(round.observation)
251
250
  if (round.observation.aborted && !round.observation.watchdog) { round.phase = 'idle'; clearWatchdog(); status(ctx); return }
252
251
  round.phase = 'switching'
@@ -294,14 +293,14 @@ export default function (pi: ExtensionAPI) {
294
293
  }
295
294
  round.model = next
296
295
  round.phase = 'redispatching'
297
- // 刷新本轮切换时间窗:成功切换后重新起算 ROUND_LIMIT,避免长重试轮被误判为“达到上限”
296
+ // 刷新本轮切换时间窗:成功切换后重新起算 ROUND_LIMIT,避免长重试轮被误判为“超过时间限制”
298
297
  round.lastSwitchAt = Date.now()
299
298
  // 本次 session 成功切换计数 + 持久化累计/日志(衡量扩展有效程度)
300
299
  sessionSwitches += 1
301
300
  const fromKey = key(previousModel ?? ctx.model)
302
301
  health.recordSwitch(fromKey ?? '', modelKey(next), classification.kind)
303
302
  await health.flush()
304
- notify(ctx, `CCSwitch:已切换至 ${modelKey(next)}(${round.attempts}/${MAX_ATTEMPTS})`, 'info')
303
+ notify(ctx, `CCSwitch:已切换至 ${modelKey(next)}(第${round.attempts}次切换)`, 'info')
305
304
  // 触发 TUI 底栏/界面重绘:appendEntry 会发出 entry_appended 事件进入 session.subscribe 流,
306
305
  // interactive-mode 收到后执行 footer.invalidate() + requestRender(),footer 从 session.state.model 重新读取,
307
306
  // 从而让右下角模型名同步显示新模型(setModel 只改 state,不直接触发 footer 刷新)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ccswitch-auto-switch",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Provider-first automatic model failover extension for Pi and CC Switch",
5
5
  "license": "MIT",
6
6
  "keywords": [
package/runner.mjs CHANGED
@@ -189,7 +189,7 @@ export async function run(options, runtime = {}) {
189
189
  if (entry.data?.protocolVersion !== 1) return
190
190
  if (entry.type === 'ccswitch-switch') {
191
191
  retryPending = true
192
- log(`switch ${entry.data.from || '?'} → ${entry.data.to || '?'} (${entry.data.attempts ?? '?'}/5)`)
192
+ log(`switch ${entry.data.from || '?'} → ${entry.data.to || '?'} (${entry.data.attempts ?? '?'})`)
193
193
  } else if (entry.type === 'ccswitch-complete') {
194
194
  terminal = { kind: 'complete', data: entry.data }
195
195
  } else if (entry.type === 'ccswitch-exhausted') {