myapikey 0.28.0 → 0.30.0
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 +1 -1
- package/packages/core/src/server/admin.ts +46 -5
- package/packages/core/src/shared/types.ts +5 -0
- package/packages/web/dist/assets/index-BeuMCLzh.js +314 -0
- package/packages/web/dist/assets/index-DY9NvKj9.css +1 -0
- package/packages/web/dist/index.html +2 -2
- package/packages/web/dist/assets/index-BF3r4RuG.js +0 -314
- package/packages/web/dist/assets/index-DKyZQ_Y4.css +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myapikey",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
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": [
|
|
@@ -76,6 +76,7 @@ function toPublic(p: Provider) {
|
|
|
76
76
|
apiKey: mask(p.apiKey),
|
|
77
77
|
rpm: p.rpm ?? 0,
|
|
78
78
|
discoveredModels: p.discoveredModels ?? [],
|
|
79
|
+
extraModels: p.extraModels ?? [],
|
|
79
80
|
discoveredAt: p.discoveredAt ?? null,
|
|
80
81
|
createdAt: p.createdAt,
|
|
81
82
|
};
|
|
@@ -323,6 +324,33 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
|
|
|
323
324
|
}
|
|
324
325
|
});
|
|
325
326
|
|
|
327
|
+
// Manual supplement to discovery: upstream model ids a backend's /models list
|
|
328
|
+
// doesn't include but that still work (delisted, unlisted, behind a different
|
|
329
|
+
// endpoint). Wholesale replace — same one-shot-save convention as PUT
|
|
330
|
+
// /admin/models/:name. Discovery refreshes only ever rewrite
|
|
331
|
+
// `discoveredModels`, so anything stored here survives every re-scan.
|
|
332
|
+
app.put("/providers/:id/extra-models", async (c) => {
|
|
333
|
+
const id = c.req.param("id");
|
|
334
|
+
const body = await readJson<{ models?: unknown }>(c.req.raw);
|
|
335
|
+
if (!body || !Array.isArray(body.models) || body.models.some((m) => typeof m !== "string")) {
|
|
336
|
+
return c.json({ error: { message: "models (string[]) is required" } }, 400);
|
|
337
|
+
}
|
|
338
|
+
// Canonical form: trimmed, deduped, case-insensitively sorted for scanning.
|
|
339
|
+
const models = [...new Set((body.models as string[]).map((m) => m.trim()).filter(Boolean))].sort((a, b) =>
|
|
340
|
+
a.toLowerCase().localeCompare(b.toLowerCase()),
|
|
341
|
+
);
|
|
342
|
+
let found = false;
|
|
343
|
+
await store.update((d) => {
|
|
344
|
+
const p = d.providers.find((x) => x.id === id);
|
|
345
|
+
if (!p) return;
|
|
346
|
+
found = true;
|
|
347
|
+
if (models.length) p.extraModels = models;
|
|
348
|
+
else delete p.extraModels;
|
|
349
|
+
});
|
|
350
|
+
if (!found) return c.json({ error: { message: "provider not found" } }, 404);
|
|
351
|
+
return c.json({ provider: toPublic(store.get().providers.find((x) => x.id === id)!) });
|
|
352
|
+
});
|
|
353
|
+
|
|
326
354
|
// Test a SOURCE directly: one minimal ping per selected protocol, straight to
|
|
327
355
|
// the upstream with this source's own key + base URL. Unlike the model tests
|
|
328
356
|
// (which loop back through dispatch) this needs no routing config, so an
|
|
@@ -381,21 +409,26 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
|
|
|
381
409
|
// successful call per (model, format) from the log tail (newest first,
|
|
382
410
|
// so the first hit wins). The UI shows this as the model's in-use slot —
|
|
383
411
|
// after a failover the chip follows the source that really served.
|
|
412
|
+
// Companion map for FAILURES (latest 4xx/5xx per model+format) so the
|
|
413
|
+
// chain popover can flag the slots that recently errored.
|
|
384
414
|
// Legacy rows carry only the provider NAME; resolve it to the stable id
|
|
385
415
|
// so they can still match a slot.
|
|
386
416
|
const byName = new Map(d.providers.map((p) => [p.name, p.id]));
|
|
387
|
-
const
|
|
417
|
+
const okLog = new Map<string, Map<string, LogEntry>>();
|
|
418
|
+
const badLog = new Map<string, Map<string, LogEntry>>();
|
|
388
419
|
for (const row of store.getLogs()) {
|
|
389
|
-
if (row.status < 200 || row.status >= 400) continue;
|
|
390
420
|
const pid = row.providerId ?? (row.provider ? byName.get(row.provider) : undefined);
|
|
391
421
|
if (!pid) continue;
|
|
392
|
-
|
|
393
|
-
|
|
422
|
+
const target = row.status >= 200 && row.status < 400 ? okLog : badLog;
|
|
423
|
+
let perFmt = target.get(row.model);
|
|
424
|
+
if (!perFmt) target.set(row.model, (perFmt = new Map()));
|
|
394
425
|
if (!perFmt.has(row.format)) perFmt.set(row.format, row);
|
|
395
426
|
}
|
|
427
|
+
const lastEntries = (src: Map<string, Map<string, LogEntry>>, name: string) =>
|
|
428
|
+
[...src.get(name)?.entries() ?? []];
|
|
396
429
|
const models = Object.entries(d.models).map(([name, e]) => ({
|
|
397
430
|
...projectModel(name, e, byId),
|
|
398
|
-
lastRoute:
|
|
431
|
+
lastRoute: lastEntries(okLog, name).map(([format, row]) => ({
|
|
399
432
|
format,
|
|
400
433
|
providerId: row.providerId ?? byName.get(row.provider) ?? "",
|
|
401
434
|
// The upstream name that was actually forwarded (a per-slot rewrite,
|
|
@@ -403,6 +436,14 @@ export function adminApi(store: Store, auth: MiddlewareHandler, openai: Hono, an
|
|
|
403
436
|
model: row.upstreamModel ?? row.model,
|
|
404
437
|
ts: row.ts,
|
|
405
438
|
})),
|
|
439
|
+
lastFail: lastEntries(badLog, name).map(([format, row]) => ({
|
|
440
|
+
format,
|
|
441
|
+
providerId: row.providerId ?? byName.get(row.provider) ?? "",
|
|
442
|
+
model: row.upstreamModel ?? row.model,
|
|
443
|
+
status: row.status,
|
|
444
|
+
error: row.error,
|
|
445
|
+
ts: row.ts,
|
|
446
|
+
})),
|
|
406
447
|
}));
|
|
407
448
|
return c.json({ models });
|
|
408
449
|
});
|
|
@@ -31,6 +31,11 @@ export interface Provider {
|
|
|
31
31
|
supportsResponses?: boolean;
|
|
32
32
|
/** Model ids this provider offered at last discovery (cached, may be stale). */
|
|
33
33
|
discoveredModels?: string[];
|
|
34
|
+
/** Manually supplemented upstream model ids — names a backend's /models list
|
|
35
|
+
* doesn't include but that still work (delisted, unlisted, behind a
|
|
36
|
+
* different endpoint). Merged with `discoveredModels` for editor suggestions
|
|
37
|
+
* and the UI's staleness check; discovery refreshes never touch this list. */
|
|
38
|
+
extraModels?: string[];
|
|
34
39
|
/** Epoch ms of the last successful/attempted discovery. */
|
|
35
40
|
discoveredAt?: number;
|
|
36
41
|
createdAt: number;
|