free-coding-models 0.5.71 → 0.5.73
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.
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Changelog v0.5.72 - 2026-08-15
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **HTTP 529 (Service overloaded) now triggers failover** — when an upstream provider returns 529, the router previously passed the error straight to the client instead of failing over to the next model in the priority chain. Clients like OpenCode saw `unknown [retrying attempt #3]` because every retry hit the same overloaded upstream. 529 is now part of the retryable status set (`[429, 500, 502, 503, 529]`) so it cascades to the next model, matching the behavior of 429/500/502/503 in both the streaming and non-streaming code paths. (Fixes GitHub issue #148)
|
|
5
|
+
|
|
6
|
+
### Tests
|
|
7
|
+
- 799/799 tests passing, including two new regression tests covering 529 failover for non-streaming and streaming requests (priority-1 model returns 529 → request succeeds via priority-2 model).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Changelog v0.5.73 - 2026-08-15
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Web dashboard (Docker/daemon) now updates model stats live** — the dashboard loaded the initial model table but latency, status, verdict, and health cells stayed frozen forever. The daemon broadcast a lightweight `probe` event after each ping, but the web frontend (`web/src/hooks/useSocket.js`) only listens for full `models` events — so results were computed and stored but never pushed to the browser. `recordProbeResult` now schedules a debounced full-state broadcast (max once per 250ms window during probe bursts), matching the payload shape the frontend already consumes. (Fixes GitHub issue #147)
|
|
5
|
+
|
|
6
|
+
### Tests
|
|
7
|
+
- 800/800 tests passing, including a new regression test that opens the SSE stream, records a probe result, and asserts an `event: models` payload arrives containing the probed model.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.73",
|
|
4
4
|
"description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nvidia",
|
|
@@ -124,7 +124,7 @@ const MAX_PROBE_WINDOW = 20
|
|
|
124
124
|
const TOKEN_FLUSH_INTERVAL_MS = 60000
|
|
125
125
|
const CONFIG_RELOAD_INTERVAL_MS = 10000
|
|
126
126
|
const STATS_RETENTION_DAYS = 90
|
|
127
|
-
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503])
|
|
127
|
+
const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 529])
|
|
128
128
|
const AUTH_STATUS_CODES = new Set([401, 403])
|
|
129
129
|
const RATE_LIMIT_HEADER_NAMES = [
|
|
130
130
|
'retry-after',
|
|
@@ -1101,6 +1101,12 @@ class RouterRuntime {
|
|
|
1101
1101
|
circuit_state: this.circuit.get(key)?.state || 'UNKNOWN',
|
|
1102
1102
|
})
|
|
1103
1103
|
|
|
1104
|
+
// 📖 Issue #147: the web dashboard (web/src/hooks/useSocket.js) only listens
|
|
1105
|
+
// 📖 for `models` events — it never consumes the lightweight `probe` event.
|
|
1106
|
+
// 📖 Broadcast the full models payload (debounced) so live stats actually
|
|
1107
|
+
// 📖 update in the Docker/daemon-served dashboard.
|
|
1108
|
+
this.scheduleWebStateBroadcast()
|
|
1109
|
+
|
|
1104
1110
|
// 📖 Probe-cache (t1): mirror the result into the persistent cross-session
|
|
1105
1111
|
// 📖 cache so the CLI TUI can skip fresh healthy models and auto-hide broken
|
|
1106
1112
|
// 📖 ones. key is `provider/modelId` — split on the first slash.
|
|
@@ -1415,6 +1421,20 @@ class RouterRuntime {
|
|
|
1415
1421
|
this.broadcast('models', getWebStatePayload(this))
|
|
1416
1422
|
}
|
|
1417
1423
|
|
|
1424
|
+
/**
|
|
1425
|
+
* 📖 scheduleWebStateBroadcast — debounced full-state broadcast so a probe
|
|
1426
|
+
* 📖 burst doesn't serialize the whole ~200-model payload once per result.
|
|
1427
|
+
* 📖 Fires at most once per 250ms window; unref'd so it never blocks exit.
|
|
1428
|
+
*/
|
|
1429
|
+
scheduleWebStateBroadcast() {
|
|
1430
|
+
if (this.webStateBroadcastTimer) return
|
|
1431
|
+
this.webStateBroadcastTimer = setTimeout(() => {
|
|
1432
|
+
this.webStateBroadcastTimer = null
|
|
1433
|
+
this.broadcastWebState()
|
|
1434
|
+
}, 250)
|
|
1435
|
+
if (typeof this.webStateBroadcastTimer.unref === 'function') this.webStateBroadcastTimer.unref()
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1418
1438
|
async runWebBenchmark(providerKey, modelId) {
|
|
1419
1439
|
const key = modelKey(providerKey, modelId)
|
|
1420
1440
|
if (this.webBenchmarkRunning.has(key)) return { skipped: true }
|