free-coding-models 0.5.10 → 0.5.14
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 +40 -0
- package/bin/free-coding-models.js +6 -1
- package/changelog/v0.5.11.md +56 -0
- package/changelog/v0.5.12.md +68 -0
- package/changelog/v0.5.13.md +57 -0
- package/package.json +1 -1
- package/src/core/config.js +59 -1
- package/src/core/playground.js +502 -0
- package/src/core/router-daemon.js +861 -29
- package/src/core/utils.js +7 -0
- package/src/tui/app.js +9 -1
- package/src/tui/cli-help.js +2 -0
- package/src/tui/command-palette.js +1 -0
- package/src/tui/key-handler.js +22 -0
- package/src/tui/overlays.js +11 -0
- package/src/tui/render-helpers.js +35 -0
- package/src/tui/render-table.js +22 -7
- package/src/tui/theme.js +3 -0
- package/web/dist/assets/index-Bzqz7KbT.js +39 -0
- package/web/dist/assets/index-H9JWDRIh.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/server.js +203 -1
- package/web/src/App.jsx +15 -1
- package/web/src/components/dashboard/ModelTable.jsx +9 -0
- package/web/src/components/dashboard/ModelTable.module.css +13 -0
- package/web/src/components/layout/Header.jsx +2 -1
- package/web/src/components/palette/CommandPalette.jsx +1 -0
- package/web/src/components/playground/PlaygroundView.jsx +528 -0
- package/web/src/components/playground/PlaygroundView.module.css +413 -0
- package/web/src/components/router/RouterView.jsx +519 -35
- package/web/src/components/router/RouterView.module.css +355 -0
- package/web/dist/assets/index-BrJgRhTA.css +0 -1
- package/web/dist/assets/index-BwLMt0bw.js +0 -39
package/README.md
CHANGED
|
@@ -338,6 +338,40 @@ Configure tools with:
|
|
|
338
338
|
|
|
339
339
|
The daemon auto-creates a `fast-coding` set from your configured providers on first start. It stores router settings in `~/.free-coding-models.json`, writes lifecycle logs to `~/.free-coding-models-daemon.log`, and tracks token metadata in `~/.free-coding-models-tokens.json`.
|
|
340
340
|
|
|
341
|
+
### Playground — chat with the router
|
|
342
|
+
|
|
343
|
+
Every chat that goes through the FCM router starts with a configurable **pre-prompt** that introduces the assistant as the free-coding-models routing agent. The Playground is the fastest way to try the router without configuring a coding tool.
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
# 1. Start the router (if it isn't already)
|
|
347
|
+
free-coding-models --daemon-bg
|
|
348
|
+
|
|
349
|
+
# 2. Open the Playground in the TUI
|
|
350
|
+
free-coding-models --playground
|
|
351
|
+
# ... or just press ; inside the TUI
|
|
352
|
+
# ... or click "Playground" in the web dashboard header
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The Playground:
|
|
356
|
+
|
|
357
|
+
- Streams responses token-by-token (SSE).
|
|
358
|
+
- Shows the routed-via provider/model + latency + tokens on every reply.
|
|
359
|
+
- Lets you pin a specific model (`fcm` = auto-router, or `groq/<id>` / `cerebras/<id>` / etc.) for manual A/B testing.
|
|
360
|
+
- Lets you toggle the pre-prompt per session, so you can see what the model answers *with* and *without* the FCM persona.
|
|
361
|
+
|
|
362
|
+
The pre-prompt lives in the router config under `router.prePrompt` and can be edited from any surface (the daemon reloads it on its 10s config-refresh tick):
|
|
363
|
+
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"router": {
|
|
367
|
+
"prePrompt": {
|
|
368
|
+
"enabled": true,
|
|
369
|
+
"text": "You are free-coding-models, the free coding-model routing agent..."
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
341
375
|
Router endpoints:
|
|
342
376
|
|
|
343
377
|
| Endpoint | Purpose |
|
|
@@ -464,6 +498,7 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
|
|
|
464
498
|
| `X` | Clear active custom text filter |
|
|
465
499
|
| `G` | Cycle global theme (`Auto → Dark → Light`) |
|
|
466
500
|
| `Ctrl+P` | Open ⚡️ command palette (search + run actions) |
|
|
501
|
+
| `;` | Open the Playground chat overlay (chat with the FCM router) |
|
|
467
502
|
| `Ctrl+A` | Run AI Speed Test for the selected model |
|
|
468
503
|
| `Ctrl+U` | Run Global AI Speed Test (uses real provider requests) |
|
|
469
504
|
| `R/S/C/M/O/L/A/H/V/B/U` | Sort columns |
|
|
@@ -508,8 +543,13 @@ When a tool mode is active (via `Z`), models incompatible with that tool are hig
|
|
|
508
543
|
- **Favorites** — star models with `F`, persisted across sessions, default to normal rows, and switch display mode with `Y` (pinned+sticky vs normal rows)
|
|
509
544
|
- **Configured-only default** — only shows providers you have keys for
|
|
510
545
|
- **Keyless latency** — models ping even without an API key (show 🔑 NO KEY)
|
|
546
|
+
- **Unusable row fade** — rows in `NO KEY` or `AUTH FAIL` state are rendered at 80% opacity (20% less opaque) on every surface (TUI + Web + Desktop), so the user can scan the table and instantly see which models they cannot actually use. Composes cleanly with the favorite/recommended/incompatible background tints.
|
|
511
547
|
- **Smart Recommend** — questionnaire picks the best model for your task type
|
|
512
548
|
- **Smart Model Router** — local OpenAI-compatible daemon with model sets, failover, circuit breakers, health probes, and token stats
|
|
549
|
+
- **Playground chat** — multi-turn chat with the router on every surface (TUI `;` / Web Playground nav / `free-coding-models --playground`). Streams responses and shows the routed-via provider/model on every reply.
|
|
550
|
+
- **Auto-heal on startup** — the daemon replaces broken models in the active set (`AUTH_ERROR` / `STALE`) with working alternatives from the same provider first, then cross-provider. The first manual edit disables auto-heal so user choices are preserved. A new user with a half-broken key set lands on a usable default set by the time the dashboard renders.
|
|
551
|
+
- **Web router set manager** — add, remove, drag-and-drop, and probe-sync the active router set from inside the Web Dashboard. The "Sync best models" button re-pings every candidate with the user's actual API keys and rebuilds the set with only models that return 2xx, so a new user lands on a working default set instead of a hardcoded one that 401s.
|
|
552
|
+
- **Router pre-prompt** — a configurable first-class system message injected by the daemon on every `/v1/chat/completions` request it proxies. Default persona introduces the assistant as the FCM routing agent; editable from any surface.
|
|
513
553
|
- **⚡️ Command Palette** — `Ctrl+P` opens a searchable action launcher for filters, sorting, overlays, and quick toggles
|
|
514
554
|
- **Install Endpoints** — push a full provider catalog into any tool's config (from Settings `P` or ⚡️ Command Palette)
|
|
515
555
|
- **Missing tool bootstrap** — detect absent CLIs, offer one-click install, then continue the selected launch automatically
|
|
@@ -122,13 +122,18 @@ async function main() {
|
|
|
122
122
|
process.exit(result.ok ? 0 : 1);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
// 📖 --playground / playground subcommand — boot the TUI directly into the
|
|
126
|
+
// 📖 Playground chat overlay. Falls through to the TUI; the key handler
|
|
127
|
+
// 📖 opens the playground on first render.
|
|
128
|
+
const wantPlayground = cliArgs.playgroundMode === true
|
|
129
|
+
|
|
125
130
|
// Validate --tier early, before entering alternate screen
|
|
126
131
|
if (cliArgs.tierFilter && !TIER_LETTER_MAP[cliArgs.tierFilter]) {
|
|
127
132
|
console.error(chalk.red(` Unknown tier "${cliArgs.tierFilter}". Valid tiers: S, A, B, C`));
|
|
128
133
|
process.exit(1);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
await runApp(cliArgs, config, { startupUpdate, isDevMode });
|
|
136
|
+
await runApp(cliArgs, config, { startupUpdate, isDevMode, wantPlayground });
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
main().catch((err) => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog v0.5.11 - 2026-06-02
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Playground chat on every surface.** A first-class way to talk to the FCM router
|
|
5
|
+
without configuring a coding tool. Press `;` in the TUI, click the "Playground"
|
|
6
|
+
nav button in the web dashboard, or run `free-coding-models --playground` /
|
|
7
|
+
`free-coding-models playground` to open the in-TUI chat. Streams responses
|
|
8
|
+
token-by-token, shows the routed-via provider/model + latency + tokens on each
|
|
9
|
+
assistant message, and surfaces a friendly error if the router is offline.
|
|
10
|
+
- **Router pre-prompt (`router.prePrompt`).** A new configurable first-class
|
|
11
|
+
system message injected by the daemon on every `/v1/chat/completions` request
|
|
12
|
+
it proxies. Default text introduces the assistant as "free-coding-models, the
|
|
13
|
+
free coding-model routing agent" so every tool and every Playground reply is
|
|
14
|
+
framed as the FCM router. Always prepended (user `system` messages can still
|
|
15
|
+
override specific instructions). Disabled by setting `enabled: false`.
|
|
16
|
+
- **`GET /api/router/preprompt` and `PUT /api/router/preprompt`** on both the
|
|
17
|
+
router daemon and the web server. Same-origin only for PUT, same CORS policy
|
|
18
|
+
as the rest of the API. Persists to `~/.free-coding-models.json`; the daemon
|
|
19
|
+
picks up the new value on its 10s config-reload tick.
|
|
20
|
+
- **`/api/playground/chat` proxy** on the web server. Browser Playground never
|
|
21
|
+
talks to the daemon directly (no CORS, no exposed provider keys). Streams SSE
|
|
22
|
+
when the client asks for `stream: true`; returns the upstream JSON otherwise.
|
|
23
|
+
- **`fcm` virtual model support clarified.** `GET /v1/models` advertises `fcm`
|
|
24
|
+
(the auto-router) and `fcm:<set-name>` per set, so the Playground model
|
|
25
|
+
selector shows both options.
|
|
26
|
+
- **`--playground` flag and `playground` subcommand** for the TUI:
|
|
27
|
+
`free-coding-models --playground` boots the TUI directly into the chat
|
|
28
|
+
overlay. `--help` lists it under "Config Flags".
|
|
29
|
+
- **Default pre-prompt covers persona + dashboard pointer.** When a user asks
|
|
30
|
+
the model "which model served you?", the default persona tells them they
|
|
31
|
+
were routed through the local FCM router and points them at the dashboard.
|
|
32
|
+
- **9 unit tests for pre-prompt injection** (idempotence, no-mutation, disabled,
|
|
33
|
+
empty, already-set, body-merge, garbage input, length cap, default text).
|
|
34
|
+
- **4 unit tests for playground error extraction** so the OpenAI wire-format
|
|
35
|
+
`{ error: { message, type, code } }` is unwrapped into a string before
|
|
36
|
+
React renders it (the bug a beta tester hit when the router was down).
|
|
37
|
+
- **Robust M4 server smoke test.** The pre-existing test asserted the router
|
|
38
|
+
was offline, which fails on developer machines with a real daemon running.
|
|
39
|
+
Now it asserts the response shape and adds pre-prompt + playground-proxy
|
|
40
|
+
round-trips to the smoke checks.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **M4 server smoke test no longer fails on local dev machines.** The test
|
|
44
|
+
previously required the router to be offline; that is no longer a precondition.
|
|
45
|
+
- **M4 settings view is untouched** but the pre-prompt edit panel will land in
|
|
46
|
+
a follow-up. (The pre-prompt is already exposed through the daemon API and
|
|
47
|
+
the Web Settings round-trip; only the textarea is missing from the UI.)
|
|
48
|
+
|
|
49
|
+
### Notes
|
|
50
|
+
- The default pre-prompt is **~120 words** in English, code-first, and
|
|
51
|
+
references the local dashboard. Users can fully replace it from any
|
|
52
|
+
surface; the text is stored verbatim in `~/.free-coding-models.json`.
|
|
53
|
+
- Pre-prompt is **never** sent to telemetry and is not logged by the daemon.
|
|
54
|
+
- The Playground honors the configured pre-prompt and lets the user toggle it
|
|
55
|
+
off per-session. When off, the request is sent as-is and the assistant
|
|
56
|
+
reply will not be framed as the FCM router.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog v0.5.12 - 2026-06-02
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Web Router Dashboard: full set manager.** The M4 read-only "Model Health"
|
|
5
|
+
panel has been replaced with an interactive **Active Set** manager:
|
|
6
|
+
- **Add model** picker with search + per-provider filter (147 routeable
|
|
7
|
+
models from the new `/api/router/catalog` endpoint).
|
|
8
|
+
- **Remove** button per row.
|
|
9
|
+
- **Move up / Move down** buttons as a keyboard-accessible fallback.
|
|
10
|
+
- **HTML5 drag-and-drop** to reorder priorities. Visual drop indicator
|
|
11
|
+
(top/bottom border on the target row) shows where the row will land.
|
|
12
|
+
- **Active set switcher** in the same header (when more than one set
|
|
13
|
+
exists). Switches the daemon's `activeSet` via
|
|
14
|
+
`POST /sets/:name/activate`.
|
|
15
|
+
- **Save indicator** (`saving… / ✓ saved / ⚠ error`) shown next to the
|
|
16
|
+
Add button so the user always knows whether the last drag actually
|
|
17
|
+
persisted to disk.
|
|
18
|
+
- **"Sync best models" button.** One-click rebuild of the active set
|
|
19
|
+
using the probe-based `sync-set` pipeline. Re-pings every candidate
|
|
20
|
+
with the user's actual API keys, picks only the models that come back
|
|
21
|
+
2xx with a reasonable latency, and persists the result. The Web UI
|
|
22
|
+
surfaces a "Synced N working models from M probes" toast with the
|
|
23
|
+
probe count. The button shows a "saving…" state while the probe runs
|
|
24
|
+
(up to ~60s for 16 candidates).
|
|
25
|
+
- **Daemon endpoints for granular set management.**
|
|
26
|
+
- `POST /sets/:name/models` — append a single model to a set.
|
|
27
|
+
- `DELETE /sets/:name/models` — remove a single model by
|
|
28
|
+
`{ provider, model }`.
|
|
29
|
+
- `POST /sets/:name/reorder` — accept a full priority order
|
|
30
|
+
`{ order: ["provider/model", ...] }` and re-number the set.
|
|
31
|
+
- `POST /sets/:name/sync` — re-run `sync-set` against the named set
|
|
32
|
+
with `maxProbes: 16, targetCount: 5` so the Web UI never spends
|
|
33
|
+
more than ~60s on a sync.
|
|
34
|
+
- `GET /api/router/catalog` — lightweight catalog of routeable
|
|
35
|
+
models for the Add picker.
|
|
36
|
+
- **Default router set is now probe-driven.** `buildDefaultRouterSet`
|
|
37
|
+
is async and accepts a `probeFn` that POSTs a 1-token chat completion
|
|
38
|
+
to each candidate. Models that come back 2xx with low latency are
|
|
39
|
+
pinned to the top of the set; failing models fall back to the static
|
|
40
|
+
tier ordering. A new user with a half-broken key set now gets a
|
|
41
|
+
default router set made of models that actually work, instead of a
|
|
42
|
+
hardcoded NVIDIA-only list that 401s for everyone without a
|
|
43
|
+
NIM-specific grant.
|
|
44
|
+
- **Web proxy for the new endpoints.** `/api/router/sets/:name/models`
|
|
45
|
+
(POST/DELETE), `/api/router/sets/:name/reorder` (POST),
|
|
46
|
+
`/api/router/sets/:name/activate` (POST), `/api/router/sets/:name/sync`
|
|
47
|
+
(POST, 180s proxy timeout), `/api/router/catalog` (GET, with a
|
|
48
|
+
fallback path that synthesizes the catalog from `sources.js` when the
|
|
49
|
+
daemon is offline).
|
|
50
|
+
- **Switch case routing fix.** The `if/regex` blocks for parameterized
|
|
51
|
+
routes (`/api/router/sets/:name/...`) were previously inside the
|
|
52
|
+
request switch as `case` statements that never matched (JS switch
|
|
53
|
+
is exact-string equality). Moved them above the switch so they
|
|
54
|
+
actually run. This is why the original reorder/add/remove proxies
|
|
55
|
+
returned 404 in the v0.5.11 web build.
|
|
56
|
+
- **6 new unit tests** for the granular set-management endpoints
|
|
57
|
+
(add, duplicate, remove, reorder, reorder-with-missing-key,
|
|
58
|
+
catalog) and **2 new tests** for `buildDefaultRouterSet`'s probe
|
|
59
|
+
path (probe-preference + sync fallback). All 495 tests pass.
|
|
60
|
+
|
|
61
|
+
### Notes
|
|
62
|
+
- The TUI's `--sync-set` flag is unchanged — the Web "Sync best" button
|
|
63
|
+
is the same pipeline with a smaller `maxProbes` so the UI stays
|
|
64
|
+
snappy. CLI users still get the full 50-probe budget.
|
|
65
|
+
- The `fcm` virtual model that the Playground uses by default
|
|
66
|
+
automatically picks up the new working-models set, so the
|
|
67
|
+
Playground will start returning successful responses as soon as the
|
|
68
|
+
Web user clicks "Sync best" once.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog v0.5.13 - 2026-06-02
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- **Auto-heal the active router set on startup.** When the daemon starts,
|
|
5
|
+
it waits for the first probe burst to populate health data, then runs
|
|
6
|
+
`autoHealActiveSet()` to swap any broken model in the active set
|
|
7
|
+
(`AUTH_ERROR` or `STALE` state) for a working alternative. The picker
|
|
8
|
+
prefers the same provider first, then falls through to cross-provider
|
|
9
|
+
candidates, and skips providers whose every probe has been broken.
|
|
10
|
+
Three passes run at 8s, 24s, and 40s after startup so a freshly-added
|
|
11
|
+
replacement that turns out to be broken gets replaced again. The result:
|
|
12
|
+
a new user with a half-broken key set lands on a usable default set
|
|
13
|
+
by the time the Web Dashboard renders, and the Playground's `fcm`
|
|
14
|
+
virtual model starts returning successful responses immediately.
|
|
15
|
+
- **`router.userCustomized` + `router.autoHeal` config flags.** The first
|
|
16
|
+
manual edit to the active set (add / remove / reorder / sync /
|
|
17
|
+
activate / rename) flips `userCustomized` to `true` and `autoHeal`
|
|
18
|
+
to `false` so the user's manual choices are never undone on the next
|
|
19
|
+
daemon start. New users get `userCustomized: false` and `autoHeal:
|
|
20
|
+
true` by default, which is what powers the M6 "default to working
|
|
21
|
+
models" promise.
|
|
22
|
+
- **`/api/router/status` exposes `autoHeal`, `userCustomized`, and
|
|
23
|
+
`brokenModelCount`.** The Web Router Dashboard reads these to surface
|
|
24
|
+
an amber banner when broken models remain in the active set, with a
|
|
25
|
+
one-click "Fix now" button that re-runs the same `sync-set` probe
|
|
26
|
+
pipeline the CLI uses.
|
|
27
|
+
- **Amber "models not responding" banner in the Web Router Dashboard.**
|
|
28
|
+
Shown when the daemon reports `brokenModelCount > 0`, with a
|
|
29
|
+
dismissable X so the user can ignore it after acknowledging the
|
|
30
|
+
problem. The "Fix now" button re-runs the probe-based heal and reloads
|
|
31
|
+
the dashboard state.
|
|
32
|
+
- **4 new unit tests** for the auto-heal path: no-op when user-customized,
|
|
33
|
+
no-op when auto-heal is disabled, no-op when no broken models, and
|
|
34
|
+
the user-edit-flags-customization round-trip. **All 515 tests pass.**
|
|
35
|
+
|
|
36
|
+
### Notes
|
|
37
|
+
- The auto-heal is best-effort: if the user has only one working
|
|
38
|
+
provider, the healed set will shrink to that one provider's top
|
|
39
|
+
model. That's still better than the previous behavior of showing
|
|
40
|
+
three models that all 401.
|
|
41
|
+
- The new broken-model banner is intentionally subtle (amber, not red)
|
|
42
|
+
because the auto-heal already does its best to recover. It's there
|
|
43
|
+
for the "user has 0 working keys" edge case so the user can click
|
|
44
|
+
through to "Fix now" / "Sync best" and either get a working set or
|
|
45
|
+
see the toast explaining the situation.
|
|
46
|
+
|
|
47
|
+
### How the picker works (M6)
|
|
48
|
+
```
|
|
49
|
+
For each broken model in the active set:
|
|
50
|
+
1. Same provider — pick a working model of the same provider
|
|
51
|
+
(skipping models that the circuit breaker already knows are broken).
|
|
52
|
+
2. Cross-provider — fall through to any working model across all
|
|
53
|
+
providers, sorted by static tier + swe score.
|
|
54
|
+
3. If neither yields a working model, leave the broken entry in
|
|
55
|
+
place and log a warning (the Web UI surfaces this in the
|
|
56
|
+
"models not responding" banner).
|
|
57
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
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",
|
package/src/core/config.js
CHANGED
|
@@ -92,7 +92,8 @@
|
|
|
92
92
|
*
|
|
93
93
|
* @exports loadConfig, saveConfig, validateConfigFile, getApiKey, isProviderEnabled
|
|
94
94
|
* @exports addApiKey, removeApiKey, listApiKeys — multi-key management helpers
|
|
95
|
-
* @exports normalizeEndpointInstalls, normalizeRouterConfig,
|
|
95
|
+
* @exports normalizeEndpointInstalls, normalizeRouterConfig, normalizeRouterPrePrompt
|
|
96
|
+
* @exports defaultRouterPrePromptText, DEFAULT_ROUTER_SETTINGS
|
|
96
97
|
* @exports buildPersistedConfig, replaceConfigContents, persistApiKeysForProvider
|
|
97
98
|
* @exports CONFIG_PATH — path to the JSON config file
|
|
98
99
|
*
|
|
@@ -166,6 +167,20 @@ export const DEFAULT_ROUTER_SETTINGS = Object.freeze({
|
|
|
166
167
|
priorityWeight: 0.2,
|
|
167
168
|
}),
|
|
168
169
|
logLevel: 'info',
|
|
170
|
+
// 📖 autoHeal — when true (default), the daemon replaces broken models
|
|
171
|
+
// 📖 in the active set on startup (AUTH_ERROR / TIMEOUT / 5xx) with
|
|
172
|
+
// 📖 working alternatives from the same provider. Disabled the moment
|
|
173
|
+
// 📖 the user manually edits the set (reorder/add/remove), so the
|
|
174
|
+
// 📖 user always has the final say.
|
|
175
|
+
autoHeal: true,
|
|
176
|
+
// 📖 Default pre-prompt injected as the first system message on every
|
|
177
|
+
// 📖 /v1/chat/completions request the router proxies. Frames the assistant
|
|
178
|
+
// 📖 as the FCM routing agent. Users can disable it or replace it from the
|
|
179
|
+
// 📖 Settings page on every surface (TUI / Web / Desktop).
|
|
180
|
+
prePrompt: Object.freeze({
|
|
181
|
+
enabled: true,
|
|
182
|
+
text: 'You are free-coding-models, a free coding-model routing agent. The user is talking to you through a local router at http://localhost:19280/v1 that picks the best free coding model in real time across Groq, Cerebras, NVIDIA NIM, GitHub Models, OpenRouter, Mistral, Cloudflare, SambaNova, and more.\n\nBe concise, code-first, and direct. Prefer minimal patches over rewrites. When you show code, make it runnable. When you explain, keep it short. If the user asks which model served them, say you were routed through the local FCM router and point them at the dashboard for the live model list.',
|
|
183
|
+
}),
|
|
169
184
|
})
|
|
170
185
|
|
|
171
186
|
function isPlainObject(value) {
|
|
@@ -385,7 +400,50 @@ export function normalizeRouterConfig(router) {
|
|
|
385
400
|
failover: normalizeRouterFailover(router.failover),
|
|
386
401
|
scoring: normalizeRouterScoring(router.scoring),
|
|
387
402
|
logLevel,
|
|
403
|
+
prePrompt: normalizeRouterPrePrompt(router.prePrompt),
|
|
404
|
+
// 📖 autoHeal defaults to true. Once the user explicitly edits a set
|
|
405
|
+
// 📖 (reorder/add/remove/sync) the daemon flips this to false so the
|
|
406
|
+
// 📖 user's manual choices are not undone on the next start.
|
|
407
|
+
autoHeal: router.autoHeal !== false,
|
|
408
|
+
// 📖 userCustomized marks the active set as "edited by the user".
|
|
409
|
+
// 📖 It is set the first time any write to the active set goes
|
|
410
|
+
// 📖 through and disables auto-heal for that set. Cleared if the
|
|
411
|
+
// 📖 user explicitly resets the set.
|
|
412
|
+
userCustomized: router.userCustomized === true,
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 📖 Normalize the pre-prompt sub-tree. The text is capped at 4000 chars to
|
|
418
|
+
* 📖 keep the per-request overhead sane and to make sure the pre-prompt is
|
|
419
|
+
* 📖 always smaller than a typical user message. A disabled pre-prompt
|
|
420
|
+
* 📖 (`enabled: false`) still preserves the text so toggling back on is a
|
|
421
|
+
* 📖 no-op rather than a destructive replace.
|
|
422
|
+
*
|
|
423
|
+
* @param {unknown} prePrompt
|
|
424
|
+
* @returns {{ enabled: boolean, text: string }}
|
|
425
|
+
*/
|
|
426
|
+
export function normalizeRouterPrePrompt(prePrompt) {
|
|
427
|
+
const fallback = DEFAULT_ROUTER_SETTINGS.prePrompt
|
|
428
|
+
if (!isPlainObject(prePrompt)) {
|
|
429
|
+
return { enabled: fallback.enabled, text: fallback.text }
|
|
388
430
|
}
|
|
431
|
+
const rawText = typeof prePrompt.text === 'string' ? prePrompt.text : ''
|
|
432
|
+
const trimmedText = rawText.slice(0, 4000)
|
|
433
|
+
return {
|
|
434
|
+
enabled: prePrompt.enabled === true,
|
|
435
|
+
text: trimmedText,
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* 📖 Returns the default pre-prompt text. Surfaced by the Settings UI so the
|
|
441
|
+
* 📖 user can hit "Restore default" without retyping the whole persona.
|
|
442
|
+
*
|
|
443
|
+
* @returns {string}
|
|
444
|
+
*/
|
|
445
|
+
export function defaultRouterPrePromptText() {
|
|
446
|
+
return DEFAULT_ROUTER_SETTINGS.prePrompt.text
|
|
389
447
|
}
|
|
390
448
|
|
|
391
449
|
function normalizeProfileSettings(settings) {
|