@billjr99/pi-openai-compat 1.1.30 → 1.1.32
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 +5 -0
- package/add-provider.sh +37 -1
- package/index.ts +63 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,6 +67,11 @@ If pi is already running when you install, type `/reload` first.
|
|
|
67
67
|
| **GMI Cloud** | `https://api.gmi-serving.com/v1` | API key from console.gmicloud.ai → Organization Settings → API Keys |
|
|
68
68
|
| **Token Harbor** | `https://tokenharbor.ai/v1` | `thk_live_...` Universal Key from tokenharbor.ai/dashboard/api-keys |
|
|
69
69
|
| **Atria ASI** | `https://api.atria-asi.ai/v1` | `atr_...` key from api.atria-asi.ai/console/keys (Google sign-in) |
|
|
70
|
+
| **Unbiased AI** | `https://api.unbiased.ai/v1` | `sk_...` key from platform.unbiased.ai (signup reviewed by hand) |
|
|
71
|
+
| **Kilo AI** | `https://api.kilo.ai/api/gateway` | API key from app.kilo.ai → Your Profile (personal account), at the bottom of the page; needs account credits |
|
|
72
|
+
| **ModelScope** | `https://api-inference.modelscope.cn/v1` | `ms-...` SDK token from modelscope.cn/my/myaccesstoken |
|
|
73
|
+
| **Aion Labs** | `https://api.aionlabs.ai/v1` | API key from aionlabs.ai/app/api-keys (aionlabs.ai/docs) |
|
|
74
|
+
| **Agnes AI** | `https://apihub.agnes-ai.com/v1` | `sk-...` key from the Agnes console at platform.agnes-ai.com (wiki.agnes-ai.com) |
|
|
70
75
|
| **Ollama (local)** | `http://localhost:11434/v1` (editable) | Optional bearer token; leave blank for a default local install |
|
|
71
76
|
| **Ollama Cloud** | `https://ollama.com/v1` | Ollama Cloud API key from ollama.com |
|
|
72
77
|
| **llmproxy** | `http://localhost:8080/v1` (editable) | Keyless by default; bearer token if your instance requires one |
|
package/add-provider.sh
CHANGED
|
@@ -66,6 +66,36 @@ const BUILTIN = {
|
|
|
66
66
|
keyless: false,
|
|
67
67
|
keyHint: "api.atria-asi.ai/console/keys (Google sign-in; keys look like atr_...; docs at api.atria-asi.ai/docs)",
|
|
68
68
|
},
|
|
69
|
+
unbiased_ai: {
|
|
70
|
+
displayName: "Unbiased AI",
|
|
71
|
+
baseUrl: "https://api.unbiased.ai/v1",
|
|
72
|
+
keyless: false,
|
|
73
|
+
keyHint: "platform.unbiased.ai (signup is reviewed by hand; keys look like sk_...)",
|
|
74
|
+
},
|
|
75
|
+
kilo: {
|
|
76
|
+
displayName: "Kilo AI",
|
|
77
|
+
baseUrl: "https://api.kilo.ai/api/gateway",
|
|
78
|
+
keyless: false,
|
|
79
|
+
keyHint: "app.kilo.ai → Your Profile on your personal account (not an organization), at the bottom of the page; needs account credits (docs at kilo.ai/docs/gateway)",
|
|
80
|
+
},
|
|
81
|
+
modelscope: {
|
|
82
|
+
displayName: "ModelScope",
|
|
83
|
+
baseUrl: "https://api-inference.modelscope.cn/v1",
|
|
84
|
+
keyless: false,
|
|
85
|
+
keyHint: "modelscope.cn/my/myaccesstoken (SDK token, format ms-...; docs at modelscope.cn/docs/model-service/API-Inference/intro)",
|
|
86
|
+
},
|
|
87
|
+
aion_labs: {
|
|
88
|
+
displayName: "Aion Labs",
|
|
89
|
+
baseUrl: "https://api.aionlabs.ai/v1",
|
|
90
|
+
keyless: false,
|
|
91
|
+
keyHint: "aionlabs.ai/app/api-keys (docs at aionlabs.ai/docs/api-reference)",
|
|
92
|
+
},
|
|
93
|
+
agnes_ai: {
|
|
94
|
+
displayName: "Agnes AI",
|
|
95
|
+
baseUrl: "https://apihub.agnes-ai.com/v1",
|
|
96
|
+
keyless: false,
|
|
97
|
+
keyHint: "platform.agnes-ai.com → API Key management (keys look like sk-...; docs at wiki.agnes-ai.com)",
|
|
98
|
+
},
|
|
69
99
|
};
|
|
70
100
|
|
|
71
101
|
// README "Auth" column text for the built-in providers, used only by patchreadme.
|
|
@@ -75,6 +105,11 @@ const BUILTIN_AUTH = {
|
|
|
75
105
|
gmi: "API key from console.gmicloud.ai → Organization Settings → API Keys",
|
|
76
106
|
tokenharbor: "`thk_live_...` Universal Key from tokenharbor.ai/dashboard/api-keys",
|
|
77
107
|
atria_asi: "`atr_...` key from api.atria-asi.ai/console/keys (Google sign-in)",
|
|
108
|
+
unbiased_ai: "`sk_...` key from platform.unbiased.ai (signup reviewed by hand)",
|
|
109
|
+
kilo: "API key from app.kilo.ai → Your Profile (personal account), at the bottom of the page; needs account credits",
|
|
110
|
+
modelscope: "`ms-...` SDK token from modelscope.cn/my/myaccesstoken",
|
|
111
|
+
aion_labs: "API key from aionlabs.ai/app/api-keys (aionlabs.ai/docs)",
|
|
112
|
+
agnes_ai: "`sk-...` key from the Agnes console at platform.agnes-ai.com (wiki.agnes-ai.com)",
|
|
78
113
|
};
|
|
79
114
|
|
|
80
115
|
/**
|
|
@@ -151,8 +186,9 @@ function normalizeModels(body, idField, keepTask) {
|
|
|
151
186
|
else if (json && typeof json === "object") {
|
|
152
187
|
if (Array.isArray(json.data)) raw = json.data;
|
|
153
188
|
else if (Array.isArray(json.result)) raw = json.result;
|
|
189
|
+
else if (Array.isArray(json.models)) raw = json.models;
|
|
154
190
|
}
|
|
155
|
-
if (!raw) throw new Error('expected an array or an object with a "data" or "
|
|
191
|
+
if (!raw) throw new Error('expected an array or an object with a "data", "result" or "models" array');
|
|
156
192
|
const field = idField || "id";
|
|
157
193
|
return raw
|
|
158
194
|
.filter((m) => {
|
package/index.ts
CHANGED
|
@@ -405,6 +405,65 @@ export const TEMPLATES: Record<string, {
|
|
|
405
405
|
// already sends, so no modelsUrl or fallbackModels override is needed.
|
|
406
406
|
keyHint: "api.atria-asi.ai/console/keys (Google sign-in; keys look like atr_...; docs at api.atria-asi.ai/docs)",
|
|
407
407
|
},
|
|
408
|
+
unbiased_ai: {
|
|
409
|
+
displayName: "Unbiased AI",
|
|
410
|
+
baseUrl: "https://api.unbiased.ai/v1",
|
|
411
|
+
keyless: false,
|
|
412
|
+
// One model, "pareto", a blend of frontier and open models. The endpoint
|
|
413
|
+
// answers both /v1/chat/completions and Anthropic's /v1/messages and takes
|
|
414
|
+
// either Authorization: Bearer or x-api-key; pi is registered with the
|
|
415
|
+
// OpenAI pair, which is what registerProvider and fetchModels already send.
|
|
416
|
+
// GET /v1/models is registered and key-gated (a well-formed sk_ key gets
|
|
417
|
+
// "Invalid API key" rather than a format complaint), so no modelsUrl or
|
|
418
|
+
// fallbackModels override is needed.
|
|
419
|
+
keyHint: "platform.unbiased.ai (signup is reviewed by hand; keys look like sk_...)",
|
|
420
|
+
},
|
|
421
|
+
kilo: {
|
|
422
|
+
displayName: "Kilo AI",
|
|
423
|
+
baseUrl: "https://api.kilo.ai/api/gateway",
|
|
424
|
+
keyless: false,
|
|
425
|
+
// Multi-vendor gateway; the base path is /api/gateway, not /v1. GET
|
|
426
|
+
// /api/gateway/models answers 200 without a key and returns an
|
|
427
|
+
// OpenRouter-shaped {"data": [...]} catalog (~380 models, ids like
|
|
428
|
+
// anthropic/claude-sonnet-5), so no modelsUrl or fallbackModels override
|
|
429
|
+
// is needed. Inference itself needs a key and account credits.
|
|
430
|
+
keyHint: "app.kilo.ai → Your Profile on your personal account (not an organization), at the bottom of the page; needs account credits (docs at kilo.ai/docs/gateway)",
|
|
431
|
+
},
|
|
432
|
+
modelscope: {
|
|
433
|
+
displayName: "ModelScope",
|
|
434
|
+
baseUrl: "https://api-inference.modelscope.cn/v1",
|
|
435
|
+
keyless: false,
|
|
436
|
+
// Alibaba's API-Inference service. GET /v1/models answers 200 without a
|
|
437
|
+
// key in the plain OpenAI {"data": [...]} shape, so the default discovery
|
|
438
|
+
// path works as is. Free tier is an account-wide 2,000 requests/day.
|
|
439
|
+
keyHint: "modelscope.cn/my/myaccesstoken (SDK token, format ms-...; docs at modelscope.cn/docs/model-service/API-Inference/intro)",
|
|
440
|
+
},
|
|
441
|
+
aion_labs: {
|
|
442
|
+
displayName: "Aion Labs",
|
|
443
|
+
baseUrl: "https://api.aionlabs.ai/v1",
|
|
444
|
+
keyless: false,
|
|
445
|
+
// Aion's own API reference documents GET /v1/models as needing no
|
|
446
|
+
// authentication and returning its array under a top-level "models" key
|
|
447
|
+
// rather than "data". fetchModels normalizes that shape, so no modelsUrl
|
|
448
|
+
// or fallbackModels override is needed. Four models as of
|
|
449
|
+
// September 2026 (aion-2.0, aion-3.0, aion-3.0-mini, aion-rp-llama-3.1-8b).
|
|
450
|
+
keyHint: "aionlabs.ai/app/api-keys (docs at aionlabs.ai/docs/api-reference)",
|
|
451
|
+
},
|
|
452
|
+
agnes_ai: {
|
|
453
|
+
displayName: "Agnes AI",
|
|
454
|
+
baseUrl: "https://apihub.agnes-ai.com/v1",
|
|
455
|
+
keyless: false,
|
|
456
|
+
// Multimodal gateway. GET /v1/models is key-gated (it answers 401 "Token
|
|
457
|
+
// not provided", where an unknown /v1 path answers 404), which is exactly
|
|
458
|
+
// what fetchModels already sends, so no override is needed. The catalog
|
|
459
|
+
// also carries image and video model ids (agnes-image-*, agnes-video-*);
|
|
460
|
+
// they are registered too and will show up in /model, but only the text
|
|
461
|
+
// models serve chat/completions: the free flash tier (agnes-3.0-flash,
|
|
462
|
+
// agnes-2.5-flash, agnes-2.0-flash) and the paid agnes-2.5-pro pair. No
|
|
463
|
+
// modelFilter is set because the live id list could not be confirmed
|
|
464
|
+
// without a key, and a filter that misses would register nothing.
|
|
465
|
+
keyHint: "platform.agnes-ai.com → API Key management (keys look like sk-...; docs at wiki.agnes-ai.com)",
|
|
466
|
+
},
|
|
408
467
|
custom: {
|
|
409
468
|
displayName: "Custom Endpoint",
|
|
410
469
|
baseUrl: "",
|
|
@@ -639,22 +698,24 @@ export async function fetchModels(
|
|
|
639
698
|
// Normalize the various shapes /models can return:
|
|
640
699
|
// - OpenAI style: {"data": [...]}
|
|
641
700
|
// - Cloudflare / some gateways: {"result": [...]}
|
|
701
|
+
// - Aion Labs: {"models": [...]}
|
|
642
702
|
// - Together (and a few others): [...] (bare JSON array)
|
|
643
703
|
const json = (await resp.json()) as unknown;
|
|
644
704
|
let raw: RawModel[] | undefined;
|
|
645
705
|
if (Array.isArray(json)) {
|
|
646
706
|
raw = json as RawModel[];
|
|
647
707
|
} else if (json && typeof json === "object") {
|
|
648
|
-
const obj = json as { data?: unknown; result?: unknown };
|
|
708
|
+
const obj = json as { data?: unknown; result?: unknown; models?: unknown };
|
|
649
709
|
if (Array.isArray(obj.data)) raw = obj.data as RawModel[];
|
|
650
710
|
else if (Array.isArray(obj.result)) raw = obj.result as RawModel[];
|
|
711
|
+
else if (Array.isArray(obj.models)) raw = obj.models as RawModel[];
|
|
651
712
|
}
|
|
652
713
|
if (!raw) {
|
|
653
714
|
// `url` may be an override (e.g. /catalog/models, /ai/models/search), so
|
|
654
715
|
// keep the wording generic rather than referring specifically to /models.
|
|
655
716
|
throw new Error(
|
|
656
717
|
`Unexpected model catalog payload shape from ${url} ` +
|
|
657
|
-
`(expected an array or an object with a "data" or "
|
|
718
|
+
`(expected an array or an object with a "data", "result" or "models" array).`
|
|
658
719
|
);
|
|
659
720
|
}
|
|
660
721
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@billjr99/pi-openai-compat",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.32",
|
|
4
4
|
"description": "pi-coding-agent extension: OpenAI-compatible endpoint support (OpenRouter, NVIDIA NIM, Nous Portal, Ollama, custom)",
|
|
5
5
|
"author": "Bill Mongan <https://github.com/BillJr99>",
|
|
6
6
|
"license": "MIT",
|