llm-switcher 1.2.10 → 1.2.11

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog — LLM Switcher
2
2
 
3
+ ## Release 1.2.11
4
+
5
+ - **Test connection on a Bifrost model:** The test sent a plain `ping` request, and Anthropic answers that with a fake 429 for a Claude Code account. Now, when intact names `bifrost_ua` for the model, the test reports that the key works and the model is served, and it sends no message.
6
+
3
7
  ## Release 1.2.10
4
8
 
5
9
  - **Bifrost in the log:** The log line and the request inspector showed `anthropic -> openai-chat` for a Bifrost request, because the gateway logged the profile's `outFormat` before it checked Bifrost. Now both show `bifrost`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-switcher",
3
- "version": "1.2.10",
3
+ "version": "1.2.11",
4
4
  "description": "Zero-dependency multi-protocol edge gateway & provider switcher for Claude Code and Codex, with OpenAI-compatible, Anthropic and Vertex upstreams",
5
5
  "keywords": [
6
6
  "llm",
package/proxy.mjs CHANGED
@@ -1123,6 +1123,14 @@ async function testUpstream(body, cfg) {
1123
1123
  const apiKey = resolveApiKey(cfg, body.key, body.apiKey, baseURL);
1124
1124
  const model = body.model || 'default';
1125
1125
  const profile = { baseURL, apiKey, mode: body.mode, outFormat: body.outFormat || undefined };
1126
+ // A Bifrost model answers only its own client: a plain test request would draw a fake 429.
1127
+ // The model lookup itself proves the key and the model, so it is the test.
1128
+ const lookupStart = Date.now();
1129
+ const bifrostUA = await bifrostUAFor(profile, model);
1130
+ if (bifrostUA) {
1131
+ return { status: 200, json: { ok: true, latency: Date.now() - lookupStart, outFormat: 'bifrost',
1132
+ sample: `Bifrost: the key works and ${model} is served. Only the real client (${bifrostUA}) can call it, so no test message was sent.` } };
1133
+ }
1126
1134
  const outFormat = resolveOutFormat(profile, model);
1127
1135
  const { url, headers } = upstreamEndpoint(profile, outFormat, model, false, null);
1128
1136
  const ir = { model, system: '', messages: [{ role: 'user', content: 'ping' }], tools: [], toolChoice: null, params: { maxTokens: 16, temperature: null, topP: null, topK: null, stop: [] }, thinking: { type: 'disabled' }, stream: false };
@@ -1251,6 +1251,17 @@ test('Codex WS: conversation.item.create is echoed and response.cancel stops the
1251
1251
  ws.socket.destroy();
1252
1252
  });
1253
1253
 
1254
+ test('Test connection on a Bifrost model checks the model and the key, and sends no fake request', async () => {
1255
+ const before = received.length;
1256
+ const r = await post('/api/test-upstream', { baseURL: `http://127.0.0.1:${upstreamPort}/intact/v1`, apiKey: 'k', model: 'claude/claude-opus-5', mode: 'convert' });
1257
+ const j = await r.json();
1258
+ assert.equal(j.ok, true);
1259
+ assert.equal(j.outFormat, 'bifrost');
1260
+ assert.match(j.sample, /claude-cli\//);
1261
+ const sent = received.slice(before).map(x => x.url);
1262
+ assert.ok(!sent.some(u => u.includes('/messages') || u.includes('/chat/completions')), `sent ${sent}`);
1263
+ });
1264
+
1254
1265
  test('Admin test-upstream reports latency and a sample from the upstream', async () => {
1255
1266
  const r = await post('/api/test-upstream', { baseURL: `http://127.0.0.1:${upstreamPort}/chat/v1`, apiKey: 'k', model: 'm', mode: 'convert' });
1256
1267
  assert.equal(r.status, 200);