@billjr99/pi-openai-compat 1.1.13 → 1.1.14
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 +9 -0
- package/index.ts +95 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,15 @@ If pi is already running when you install, type `/reload` first.
|
|
|
77
77
|
> | **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"` |
|
|
78
78
|
> | **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 |
|
|
79
79
|
> | **Hugging Face** | Returns HTML rather than JSON | Built-in fallback list |
|
|
80
|
+
>
|
|
81
|
+
> **Auto-heal for older configs:** if you logged in before live discovery
|
|
82
|
+
> existed, your saved provider is missing these `modelsUrl`/`modelsIdField`/
|
|
83
|
+
> `modelsKeepTask` fields. On startup the extension backfills them from the
|
|
84
|
+
> template whenever it can do so without prompting — i.e. when your saved base
|
|
85
|
+
> URL still matches the template (Cloudflare Workers AI). When the base URL
|
|
86
|
+
> itself changed (e.g. GitHub Models' retired `models.inference.ai.azure.com`
|
|
87
|
+
> host), it can't be migrated silently and you'll get a one-time notice to run
|
|
88
|
+
> `/compat-login`.
|
|
80
89
|
|
|
81
90
|
---
|
|
82
91
|
|
package/index.ts
CHANGED
|
@@ -349,6 +349,84 @@ function saveConfig(config: ExtensionConfig): void {
|
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
353
|
+
// Discovery-field auto-heal
|
|
354
|
+
//
|
|
355
|
+
// Providers saved before model-discovery overrides existed (modelsUrl/
|
|
356
|
+
// modelsIdField/modelsKeepTask) have none on their config, so /compat-refresh
|
|
357
|
+
// and the session_start rehydrate hit the broken default <baseUrl>/models path
|
|
358
|
+
// (e.g. Cloudflare Workers AI's 405). When a provider's saved baseUrl still
|
|
359
|
+
// matches its template — so we can recover any account-id/slug placeholders —
|
|
360
|
+
// we backfill the discovery fields from the template. Providers whose baseUrl
|
|
361
|
+
// no longer matches the template (e.g. GitHub Models' retired Azure host) can't
|
|
362
|
+
// be healed safely and are reported so the user can re-run /compat-login.
|
|
363
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
/** Placeholders that may appear in a template's baseUrl / modelsUrl. */
|
|
366
|
+
const URL_PLACEHOLDERS = ["YOUR_ACCOUNT_ID", "YOUR_GATEWAY_SLUG", "YOUR_PROVIDER"];
|
|
367
|
+
|
|
368
|
+
function escapeRegex(s: string): string {
|
|
369
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Recover placeholder values by matching a saved URL against a template URL.
|
|
374
|
+
* Returns a map (possibly empty when the template has no placeholders) when the
|
|
375
|
+
* saved URL is consistent with the template, or null when it isn't — which we
|
|
376
|
+
* treat as "this provider can't be healed automatically".
|
|
377
|
+
*/
|
|
378
|
+
function recoverPlaceholders(templateUrl: string, savedUrl: string): Record<string, string> | null {
|
|
379
|
+
const order: string[] = [];
|
|
380
|
+
const placeholderRe = new RegExp(URL_PLACEHOLDERS.join("|"), "g");
|
|
381
|
+
let source = "^";
|
|
382
|
+
let lastIndex = 0;
|
|
383
|
+
let m: RegExpExecArray | null;
|
|
384
|
+
while ((m = placeholderRe.exec(templateUrl)) !== null) {
|
|
385
|
+
source += escapeRegex(templateUrl.slice(lastIndex, m.index)) + "([^/]+)";
|
|
386
|
+
order.push(m[0]);
|
|
387
|
+
lastIndex = m.index + m[0].length;
|
|
388
|
+
}
|
|
389
|
+
source += escapeRegex(templateUrl.slice(lastIndex)) + "$";
|
|
390
|
+
|
|
391
|
+
const match = new RegExp(source).exec(savedUrl);
|
|
392
|
+
if (!match) return null;
|
|
393
|
+
const out: Record<string, string> = {};
|
|
394
|
+
order.forEach((name, i) => { out[name] = match[i + 1]; });
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function applyPlaceholders(url: string, values: Record<string, string>): string {
|
|
399
|
+
let out = url;
|
|
400
|
+
for (const [name, value] of Object.entries(values)) out = out.split(name).join(value);
|
|
401
|
+
return out;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Backfill missing discovery fields on saved providers from their template.
|
|
406
|
+
* Mutates `config` in place; the caller is responsible for persisting when
|
|
407
|
+
* `healed` is non-empty. `stale` lists providers that need a manual re-login.
|
|
408
|
+
*/
|
|
409
|
+
function migrateDiscoveryFields(config: ExtensionConfig): { healed: string[]; stale: string[] } {
|
|
410
|
+
const healed: string[] = [];
|
|
411
|
+
const stale: string[] = [];
|
|
412
|
+
|
|
413
|
+
for (const [key, p] of Object.entries(config.providers)) {
|
|
414
|
+
const tpl = TEMPLATES[key];
|
|
415
|
+
if (!tpl?.modelsUrl) continue; // no matching template, or template needs no overrides
|
|
416
|
+
if (p.modelsUrl) continue; // already set (fresh login or manual edit) — never clobber
|
|
417
|
+
|
|
418
|
+
const values = recoverPlaceholders(tpl.baseUrl, p.baseUrl);
|
|
419
|
+
if (!values) { stale.push(p.displayName); continue; }
|
|
420
|
+
|
|
421
|
+
p.modelsUrl = applyPlaceholders(tpl.modelsUrl, values);
|
|
422
|
+
p.modelsIdField = tpl.modelsIdField;
|
|
423
|
+
p.modelsKeepTask = tpl.modelsKeepTask;
|
|
424
|
+
healed.push(p.displayName);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return { healed, stale };
|
|
428
|
+
}
|
|
429
|
+
|
|
352
430
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
353
431
|
// Networking
|
|
354
432
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -472,6 +550,11 @@ function registerProvider(pi: ExtensionAPI, key: string, p: ProviderConfig): voi
|
|
|
472
550
|
export default async function (pi: ExtensionAPI) {
|
|
473
551
|
let config = loadConfig();
|
|
474
552
|
|
|
553
|
+
// Self-heal older configs: backfill discovery fields (modelsUrl/idField/
|
|
554
|
+
// keepTask) we can derive from the template without prompting. Persist once
|
|
555
|
+
// so /compat-refresh and the session_start rehydrate use the fixed values.
|
|
556
|
+
if (migrateDiscoveryFields(config).healed.length > 0) saveConfig(config);
|
|
557
|
+
|
|
475
558
|
// Register all saved providers immediately using cached model lists.
|
|
476
559
|
// The factory is async, so pi waits for this to finish before startup
|
|
477
560
|
// continues — providers are visible in /model from the very first render.
|
|
@@ -487,6 +570,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
487
570
|
pi.on("session_start", async (_event, ctx) => {
|
|
488
571
|
config = loadConfig();
|
|
489
572
|
|
|
573
|
+
// Self-heal what we can (silently), and collect providers whose saved
|
|
574
|
+
// baseUrl no longer matches the template — those need a manual re-login.
|
|
575
|
+
const { healed, stale } = migrateDiscoveryFields(config);
|
|
576
|
+
if (healed.length > 0) saveConfig(config);
|
|
577
|
+
|
|
490
578
|
const registered: string[] = [];
|
|
491
579
|
const failed: string[] = [];
|
|
492
580
|
|
|
@@ -527,6 +615,13 @@ export default async function (pi: ExtensionAPI) {
|
|
|
527
615
|
"warning"
|
|
528
616
|
);
|
|
529
617
|
}
|
|
618
|
+
if (stale.length > 0) {
|
|
619
|
+
ctx.ui.notify(
|
|
620
|
+
`OpenAI-compat: ${stale.join(", ")} ${stale.length === 1 ? "has" : "have"} an out-of-date ` +
|
|
621
|
+
`base URL — run /compat-login to update (its endpoint changed and can't be migrated automatically).`,
|
|
622
|
+
"warning"
|
|
623
|
+
);
|
|
624
|
+
}
|
|
530
625
|
});
|
|
531
626
|
|
|
532
627
|
// ── model_select ───────────────────────────────────────────────────────────
|
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.14",
|
|
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",
|