llm-switcher 1.2.5 → 1.2.6
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/proxy.mjs +6 -1
- package/switch.cmd +2 -2
- package/tests/gateway.e2e.test.mjs +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog — LLM Switcher
|
|
2
2
|
|
|
3
|
+
## Release 1.2.6
|
|
4
|
+
|
|
5
|
+
- **Deactivate in the dashboard:** The Deactivate button did not change the configuration, but the dashboard showed "deactivated". The dashboard sends `deactivate: true` and the profile key in `profile`. The gateway read `deactivate` as the profile key, so no target matched. Now `POST /api/switch` accepts both forms: `{"profile": "x", "deactivate": true}` and `{"deactivate": "x"}`.
|
|
6
|
+
- **No silent success:** If the profile is not active, `POST /api/switch` now answers `404` with the error `Profile "x" is not active`. If `deactivate` is `true` and no profile is named, it answers `400`. Before, both requests answered `200` with `success: true`.
|
|
7
|
+
|
|
3
8
|
## Release 1.2.5
|
|
4
9
|
|
|
5
10
|
- **Dashboard controls:** The dashboard controls use MIT elements from Uiverse.io galaxy as a base. The CSS gives the name of each author.
|
package/package.json
CHANGED
package/proxy.mjs
CHANGED
|
@@ -1367,7 +1367,12 @@ async function routeConfigApi(res, method, pathname, body) {
|
|
|
1367
1367
|
if (pathname === '/api/switch') {
|
|
1368
1368
|
let err = null;
|
|
1369
1369
|
if (body.deactivate) {
|
|
1370
|
-
|
|
1370
|
+
// The dashboard sends `deactivate: true` with the key in `profile`; the CLI sends the key itself.
|
|
1371
|
+
const key = body.deactivate === true ? body.profile : body.deactivate;
|
|
1372
|
+
if (typeof key !== 'string' || !key) return sendJson(res, 400, { error: 'deactivate needs a profile key' });
|
|
1373
|
+
// A no-op must not answer success: the dashboard trusts `success` alone.
|
|
1374
|
+
if (!isProfileActive(cfg, key)) return sendJson(res, 404, { error: `Profile "${key}" is not active` });
|
|
1375
|
+
deactivateProfile(cfg, key);
|
|
1371
1376
|
} else if (body.target) {
|
|
1372
1377
|
err = setTargetProfile(cfg, body.target, body.profile || null);
|
|
1373
1378
|
} else if (body.profile) {
|
package/switch.cmd
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
node "%~dp0switch.mjs" %*
|
|
1
|
+
@echo off
|
|
2
|
+
node "%~dp0switch.mjs" %*
|
|
@@ -1208,3 +1208,30 @@ test('Admin test-upstream reports latency and a sample from the upstream', async
|
|
|
1208
1208
|
assert.match(json.sample, /Final answer/);
|
|
1209
1209
|
assert.equal(typeof json.latency, 'number');
|
|
1210
1210
|
});
|
|
1211
|
+
|
|
1212
|
+
// Runs last: turning `codex` off and on stops and restarts the shared interceptor, which the
|
|
1213
|
+
// Codex WebSocket tests above depend on.
|
|
1214
|
+
test('/api/switch deactivate: the dashboard payload works, the CLI payload works, a no-op is an error', async () => {
|
|
1215
|
+
try {
|
|
1216
|
+
// What the dashboard sends: the profile in `profile`, a boolean in `deactivate`.
|
|
1217
|
+
const dash = await post('/api/switch', { profile: 'chat', deactivate: true });
|
|
1218
|
+
assert.equal(dash.status, 200);
|
|
1219
|
+
assert.deepEqual((await dash.json()).activeProfiles, { claude: null, codex: null });
|
|
1220
|
+
|
|
1221
|
+
// The same profile is now inactive, so asking again must not report success.
|
|
1222
|
+
const again = await post('/api/switch', { profile: 'chat', deactivate: true });
|
|
1223
|
+
assert.equal(again.status, 404);
|
|
1224
|
+
assert.match((await again.json()).error, /not active/);
|
|
1225
|
+
|
|
1226
|
+
// What the CLI sends: the profile key in `deactivate`.
|
|
1227
|
+
assert.equal((await post('/api/switch', { profile: 'chat' })).status, 200);
|
|
1228
|
+
const cli = await post('/api/switch', { deactivate: 'chat' });
|
|
1229
|
+
assert.equal(cli.status, 200);
|
|
1230
|
+
assert.deepEqual((await cli.json()).activeProfiles, { claude: null, codex: null });
|
|
1231
|
+
|
|
1232
|
+
// `deactivate: true` with no profile names nothing.
|
|
1233
|
+
assert.equal((await post('/api/switch', { deactivate: true })).status, 400);
|
|
1234
|
+
} finally {
|
|
1235
|
+
await post('/api/switch', { profile: 'chat' });
|
|
1236
|
+
}
|
|
1237
|
+
});
|