myapikey 0.15.0 → 0.16.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myapikey",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Personal LLM API gateway & proxy — one address + one API key for all your models. Forwards OpenAI & Anthropic calls to your backends with failover and a circuit breaker. Pure passthrough, no format translation. Self-hosted (CLI + web UI).",
|
|
6
6
|
"keywords": [
|
|
@@ -356,6 +356,28 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
|
|
|
356
356
|
return c.json({ ok: true }, 201);
|
|
357
357
|
});
|
|
358
358
|
|
|
359
|
+
// Rename a model - changes the ROUTING KEY, the name clients request. The
|
|
360
|
+
// config entry (enabled slots, chains, upstream mappings) moves as-is, so
|
|
361
|
+
// an unmapped slot now forwards the NEW name upstream (that's the point of
|
|
362
|
+
// renaming); slots with an explicit upstream mapping keep it. Renaming to
|
|
363
|
+
// an existing name is rejected; key order in data.json is preserved.
|
|
364
|
+
app.post("/models/:name/rename", async (c) => {
|
|
365
|
+
const name = c.req.param("name");
|
|
366
|
+
const body = await readJson<{ name?: string }>(c.req.raw);
|
|
367
|
+
const next = body?.name?.trim();
|
|
368
|
+
if (!next) return c.json({ error: { message: "name is required" } }, 400);
|
|
369
|
+
if (next === name) return c.json({ error: { message: "new name is the same as the current one" } }, 400);
|
|
370
|
+
const cfg = store.get();
|
|
371
|
+
if (!cfg.models[name]) return c.json({ error: { message: "model not found" } }, 404);
|
|
372
|
+
if (cfg.models[next]) return c.json({ error: { message: `model already exists: ${next}` } }, 409);
|
|
373
|
+
await store.update((d) => {
|
|
374
|
+
const rebuilt: typeof d.models = {};
|
|
375
|
+
for (const [k, v] of Object.entries(d.models)) rebuilt[k === name ? next : k] = v;
|
|
376
|
+
d.models = rebuilt;
|
|
377
|
+
});
|
|
378
|
+
return c.json({ ok: true });
|
|
379
|
+
});
|
|
380
|
+
|
|
359
381
|
app.post("/models/:name/providers", async (c) => {
|
|
360
382
|
const name = c.req.param("name");
|
|
361
383
|
const body = await readJson<{ format?: RouteKey; providerId?: string; model?: string }>(c.req.raw);
|
|
@@ -4,8 +4,10 @@ import type { Format, Provider, RouteKey, Usage } from "../shared/types";
|
|
|
4
4
|
import type { Store } from "./store";
|
|
5
5
|
import { UsageCollector } from "./tokens";
|
|
6
6
|
|
|
7
|
-
/** HTTP statuses that should trigger failover to the next provider.
|
|
8
|
-
|
|
7
|
+
/** HTTP statuses that should trigger failover to the next provider. 401/403
|
|
8
|
+
* included: a banned/invalid credential (e.g. "User has been banned") is dead
|
|
9
|
+
* for THIS source only - the same request may be fine on the next one. */
|
|
10
|
+
const RETRYABLE = new Set([401, 403, 408, 425, 429, 500, 502, 503, 504]);
|
|
9
11
|
|
|
10
12
|
/** Headers copied from upstream back to the client. */
|
|
11
13
|
const COPY_DOWN = ["content-type", "cache-control", "x-request-id", "openai-organization", "anthropic-ratelimit-requests-reset"];
|