@billjr99/pi-openai-compat 1.1.32 → 1.1.33
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 +7 -1
- package/add-provider.sh +11 -0
- package/index.ts +40 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,12 @@ If pi is already running when you install, type `/reload` first.
|
|
|
88
88
|
> 2. **Built-in fallback list** when no working catalog endpoint exists. Used
|
|
89
89
|
> when discovery fails or when the upstream simply has no `/models` at all
|
|
90
90
|
> (e.g. **Hugging Face** returns HTML, **Cloudflare AI Gateway** has no
|
|
91
|
-
> catalog endpoint).
|
|
91
|
+
> catalog endpoint, **Unbiased AI** has no such route).
|
|
92
|
+
>
|
|
93
|
+
> A fallback list stands in for a missing endpoint, never for a rejected
|
|
94
|
+
> credential: a catalog fetch that fails with 401 or 403 aborts the login
|
|
95
|
+
> instead, so a mistyped key cannot be saved as a provider that looks healthy
|
|
96
|
+
> in `/model` and then fails on every completion.
|
|
92
97
|
>
|
|
93
98
|
> | Provider | Default `/models` symptom | Handling |
|
|
94
99
|
> |---|---|---|
|
|
@@ -96,6 +101,7 @@ If pi is already running when you install, type `/reload` first.
|
|
|
96
101
|
> | **Cloudflare Workers AI** | HTTP 405 (no `GET /v1/models`; real catalog at `/ai/models/search`, id field is `name`, mixed task types) | Live discovery — `modelsUrl: …/ai/models/search`, `modelsIdField: name`, `modelsKeepTask: "Text Generation"` |
|
|
97
102
|
> | **Cloudflare AI Gateway** | HTTP 401 (token missing `AI Gateway: Run`) or HTTP 400 *"Please configure AI Gateway"* (gateway slug doesn't exist, or the upstream isn't configured on it) | Built-in fallback list |
|
|
98
103
|
> | **Hugging Face** | Returns HTML rather than JSON | Built-in fallback list |
|
|
104
|
+
> | **Unbiased AI** | HTTP 404 `unknown_url` with a valid key (no `/v1/models` at all; an *invalid* key returns 401 on every path, including ones that do not exist, so a 401 here proves nothing) | Built-in fallback list |
|
|
99
105
|
>
|
|
100
106
|
> **Auto-heal for older configs:** if you logged in before live discovery
|
|
101
107
|
> existed, your saved provider is missing these `modelsUrl`/`modelsIdField`/
|
package/add-provider.sh
CHANGED
|
@@ -465,6 +465,17 @@ fi
|
|
|
465
465
|
MODEL_COUNT=0
|
|
466
466
|
[ -n "$MODELS_JSON" ] && MODEL_COUNT="$(printf '%s' "$MODELS_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>process.stdout.write(String(JSON.parse(s).length)))')"
|
|
467
467
|
|
|
468
|
+
# Mirrors isAuthFailure() in index.ts: a fallback list stands in for an endpoint
|
|
469
|
+
# that is not there, never for a credential that was refused. Falling back on a
|
|
470
|
+
# 401/403 would write a provider that looks healthy in /model and fails on every
|
|
471
|
+
# completion, which is worse than saying the key was rejected.
|
|
472
|
+
if [ "$MODEL_COUNT" = "0" ] && { [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "403" ]; }; then
|
|
473
|
+
echo " The provider rejected the API key (HTTP $HTTP_CODE)."
|
|
474
|
+
echo " Not using the built-in model list: that would save a provider whose"
|
|
475
|
+
echo " every request fails. Re-run with a valid key."
|
|
476
|
+
exit 1
|
|
477
|
+
fi
|
|
478
|
+
|
|
468
479
|
if [ "$MODEL_COUNT" = "0" ]; then
|
|
469
480
|
if [ -n "$FALLBACK_MODELS" ]; then
|
|
470
481
|
echo " Using this template's built-in model list instead."
|
package/index.ts
CHANGED
|
@@ -413,9 +413,21 @@ export const TEMPLATES: Record<string, {
|
|
|
413
413
|
// answers both /v1/chat/completions and Anthropic's /v1/messages and takes
|
|
414
414
|
// either Authorization: Bearer or x-api-key; pi is registered with the
|
|
415
415
|
// OpenAI pair, which is what registerProvider and fetchModels already send.
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
//
|
|
416
|
+
//
|
|
417
|
+
// GET /v1/models does NOT exist: with a valid key it returns 404
|
|
418
|
+
// "unknown_url". An earlier note here claimed the endpoint was registered
|
|
419
|
+
// and key-gated, reasoning from a well-formed but invalid key getting
|
|
420
|
+
// "Invalid API key" rather than a 404. That inference was wrong. The
|
|
421
|
+
// gateway authenticates before it routes, so a path that certainly does
|
|
422
|
+
// not exist returns the same 401: GET /v1/definitely-not-a-real-endpoint
|
|
423
|
+
// answers "Invalid API key" with a bad key and "Missing API key" without
|
|
424
|
+
// one, exactly as /v1/models does. A 401 here says nothing about routing.
|
|
425
|
+
//
|
|
426
|
+
// Discovery therefore finds nothing, which is what fallbackModels is for.
|
|
427
|
+
// It is safe here because the catch that consumes it now refuses to fall
|
|
428
|
+
// back on a 401/403 (see isAuthFailure), so a mistyped key still fails the
|
|
429
|
+
// login instead of being saved as a provider that fails every completion.
|
|
430
|
+
fallbackModels: ["pareto"],
|
|
419
431
|
keyHint: "platform.unbiased.ai (signup is reviewed by hand; keys look like sk_...)",
|
|
420
432
|
},
|
|
421
433
|
kilo: {
|
|
@@ -656,6 +668,24 @@ export function isLocalUrl(url: string): boolean {
|
|
|
656
668
|
/** Cap on how much of an upstream error body is surfaced to the user. */
|
|
657
669
|
export const MAX_ERROR_BODY = 500;
|
|
658
670
|
|
|
671
|
+
/** An Error from fetchModels carrying the upstream HTTP status, when it had one. */
|
|
672
|
+
export type CatalogError = Error & { status?: number };
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* True when a catalog fetch failed because the credential was rejected.
|
|
676
|
+
*
|
|
677
|
+
* This is the distinction that makes fallbackModels safe to use. A fallback
|
|
678
|
+
* list exists for endpoints that publish no catalog, so it should stand in for
|
|
679
|
+
* a missing endpoint and never for a bad key: falling back on a 401 would save
|
|
680
|
+
* a provider that looks healthy in /model and fails on every completion, which
|
|
681
|
+
* is worse than refusing the login. A network error (no status) is treated as
|
|
682
|
+
* non-auth, so an offline or unreachable host still gets the fallback.
|
|
683
|
+
*/
|
|
684
|
+
export function isAuthFailure(err: unknown): boolean {
|
|
685
|
+
const status = (err as CatalogError | null)?.status;
|
|
686
|
+
return status === 401 || status === 403;
|
|
687
|
+
}
|
|
688
|
+
|
|
659
689
|
/** Optional per-provider overrides controlling how /models is fetched. */
|
|
660
690
|
interface FetchOverrides {
|
|
661
691
|
/** Full URL to fetch instead of `<baseUrl>/models`. */
|
|
@@ -692,7 +722,12 @@ export async function fetchModels(
|
|
|
692
722
|
const snippet = body.length > MAX_ERROR_BODY
|
|
693
723
|
? `${body.slice(0, MAX_ERROR_BODY)}… (truncated)`
|
|
694
724
|
: body;
|
|
695
|
-
|
|
725
|
+
// Carry the status on the error. Callers need to tell "this endpoint is
|
|
726
|
+
// not there" from "your key is wrong", and parsing it back out of the
|
|
727
|
+
// message would break the moment the wording changes.
|
|
728
|
+
const err = new Error(`HTTP ${resp.status} from ${url}: ${snippet}`) as CatalogError;
|
|
729
|
+
err.status = resp.status;
|
|
730
|
+
throw err;
|
|
696
731
|
}
|
|
697
732
|
|
|
698
733
|
// Normalize the various shapes /models can return:
|
|
@@ -1032,7 +1067,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
1032
1067
|
keepTask: tpl.modelsKeepTask,
|
|
1033
1068
|
});
|
|
1034
1069
|
} catch (err) {
|
|
1035
|
-
if (tpl.fallbackModels && tpl.fallbackModels.length > 0) {
|
|
1070
|
+
if (tpl.fallbackModels && tpl.fallbackModels.length > 0 && !isAuthFailure(err)) {
|
|
1036
1071
|
ctx.ui.notify(
|
|
1037
1072
|
`Could not fetch model list from ${tpl.displayName} (${err}).\nUsing built-in model list instead.`,
|
|
1038
1073
|
"warning"
|
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.33",
|
|
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",
|