@billjr99/pi-openai-compat 1.1.15 → 1.1.16
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 +28 -0
- package/index.ts +48 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,8 +56,27 @@ If pi is already running when you install, type `/reload` first.
|
|
|
56
56
|
| **OpenCode Zen** | `https://opencode.ai/zen/v1` | API key from opencode.ai |
|
|
57
57
|
| **Ollama (local)** | `http://localhost:11434/v1` | Keyless |
|
|
58
58
|
| **Ollama Cloud** | `https://ollama.com/v1` | Ollama Cloud API key from ollama.com |
|
|
59
|
+
| **llmproxy** | `http://localhost:8080/v1` (editable) | Keyless by default; bearer token if your instance requires one |
|
|
59
60
|
| **Custom** | Any URL you supply | Optional bearer token |
|
|
60
61
|
|
|
62
|
+
> **llmproxy and the `__` model-id rewrite.**
|
|
63
|
+
> [llmproxy](https://github.com/BillJr99/llmproxy) advertises model ids in the
|
|
64
|
+
> `provider__model` form (e.g. `openrouter__gpt-4`) and exposes virtual models
|
|
65
|
+
> such as `llmproxy__free` and `llmproxy__loadbalanced`. pi rejects model ids that
|
|
66
|
+
> contain `__`, so without special handling **every llmproxy model is silently
|
|
67
|
+
> dropped from `/model`**. The llmproxy template therefore sets
|
|
68
|
+
> `rewriteDoubleUnderscore: true`: the extension rewrites the first `__` of each id
|
|
69
|
+
> to `/` (e.g. `openrouter__gpt-4` → `openrouter/gpt-4`, `llmproxy__free` →
|
|
70
|
+
> `llmproxy/free`) before registering with pi. llmproxy canonicalizes that slash
|
|
71
|
+
> form back to `__` on each request, so routing still works.
|
|
72
|
+
>
|
|
73
|
+
> The flag is a per-provider config field (`rewriteDoubleUnderscore`, default
|
|
74
|
+
> **false**) and is enabled automatically only for the llmproxy template — other
|
|
75
|
+
> providers are unaffected. If you added llmproxy as a **Custom** endpoint instead
|
|
76
|
+
> of via the llmproxy template, set `"rewriteDoubleUnderscore": true` on that
|
|
77
|
+
> provider in `~/.config/pi-openai-compat/config.json` (or re-run `/compat-login`
|
|
78
|
+
> and pick **llmproxy (local)**).
|
|
79
|
+
|
|
61
80
|
> **Providers whose model catalog lives at a non-standard `/models` path (as of June 2026)**
|
|
62
81
|
> Some providers don't return models at `<base_url>/models`. The extension
|
|
63
82
|
> handles them in one of two ways:
|
|
@@ -327,6 +346,15 @@ For Ollama: pull at least one model first (`ollama pull llama3`).
|
|
|
327
346
|
For OpenRouter: some keys are restricted to free-tier models only.
|
|
328
347
|
For NIM: confirm your account has inference access enabled.
|
|
329
348
|
|
|
349
|
+
**`/compat-login` reports N models but far fewer appear in `/model`**
|
|
350
|
+
This is the classic llmproxy symptom: pi drops every model id containing `__`, so
|
|
351
|
+
llmproxy's `provider__model` ids (and `llmproxy__free` / `llmproxy__loadbalanced`)
|
|
352
|
+
never show. Make sure you logged in via the **llmproxy (local)** template (it sets
|
|
353
|
+
`rewriteDoubleUnderscore: true` automatically). If you used **Custom**, add
|
|
354
|
+
`"rewriteDoubleUnderscore": true` to that provider in
|
|
355
|
+
`~/.config/pi-openai-compat/config.json` and re-run `/compat-refresh`. After the
|
|
356
|
+
fix the ids appear in slash form (`openrouter/gpt-4`, `llmproxy/free`).
|
|
357
|
+
|
|
330
358
|
**Models appear in `/model` but requests fail**
|
|
331
359
|
Check `/compat-login` ran successfully (no error message).
|
|
332
360
|
Verify Ollama is still running if using a local endpoint.
|
package/index.ts
CHANGED
|
@@ -48,6 +48,13 @@ interface ProviderConfig {
|
|
|
48
48
|
// matches its template and can't be auto-migrated, so the notice is shown
|
|
49
49
|
// only once rather than on every session_start while the config stays stale.
|
|
50
50
|
staleNotified?: boolean;
|
|
51
|
+
// When true, rewrite the FIRST "__" of every model id to "/" before
|
|
52
|
+
// registering with pi. pi rejects model ids containing "__", so providers like
|
|
53
|
+
// llmproxy (which uses "provider__model" ids) would otherwise have their entire
|
|
54
|
+
// catalog dropped from /model. The rewrite is the inverse of llmproxy's own
|
|
55
|
+
// request-side canonicalization, so requests still round-trip. Defaults false;
|
|
56
|
+
// the wizard turns it on only for the llmproxy template.
|
|
57
|
+
rewriteDoubleUnderscore?: boolean;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
interface ExtensionConfig {
|
|
@@ -97,6 +104,12 @@ const TEMPLATES: Record<string, {
|
|
|
97
104
|
modelsIdField?: string;
|
|
98
105
|
/** Keep only models whose task.name matches this string (case-insensitive). */
|
|
99
106
|
modelsKeepTask?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Rewrite the first "__" of each model id to "/" before registering with pi.
|
|
109
|
+
* pi drops model ids containing "__", so this is required for llmproxy (whose
|
|
110
|
+
* ids are "provider__model"). Default false; set true only where pi needs it.
|
|
111
|
+
*/
|
|
112
|
+
rewriteDoubleUnderscore?: boolean;
|
|
100
113
|
}> = {
|
|
101
114
|
openrouter: {
|
|
102
115
|
displayName: "OpenRouter",
|
|
@@ -288,6 +301,10 @@ const TEMPLATES: Record<string, {
|
|
|
288
301
|
baseUrl: "http://localhost:8080/v1",
|
|
289
302
|
keyless: true,
|
|
290
303
|
promptUrl: true,
|
|
304
|
+
// llmproxy advertises "provider__model" ids (and virtuals like
|
|
305
|
+
// "llmproxy__free"). pi rejects "__" in model ids, so rewrite the first
|
|
306
|
+
// "__" to "/" here; llmproxy canonicalizes the slash form back on requests.
|
|
307
|
+
rewriteDoubleUnderscore: true,
|
|
291
308
|
},
|
|
292
309
|
vercel: {
|
|
293
310
|
displayName: "Vercel AI Gateway",
|
|
@@ -535,16 +552,31 @@ async function fetchModels(
|
|
|
535
552
|
// Provider registration helpers
|
|
536
553
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
537
554
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
})
|
|
555
|
+
/**
|
|
556
|
+
* Rewrite the FIRST "__" of a model id to "/". Only the first occurrence is
|
|
557
|
+
* touched: the provider/virtual segment that precedes it never contains "__" or
|
|
558
|
+
* "/", so this is the exact inverse of the slash→"__" canonicalization llmproxy
|
|
559
|
+
* applies on the request side, keeping ids round-trippable. Ids without "__" are
|
|
560
|
+
* returned unchanged.
|
|
561
|
+
*/
|
|
562
|
+
function rewriteFirstDoubleUnderscore(id: string): string {
|
|
563
|
+
const i = id.indexOf("__");
|
|
564
|
+
return i === -1 ? id : `${id.slice(0, i)}/${id.slice(i + 2)}`;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function buildProviderModels(models: CachedModel[], rewriteDoubleUnderscore = false) {
|
|
568
|
+
return models.map((m) => {
|
|
569
|
+
const id = rewriteDoubleUnderscore ? rewriteFirstDoubleUnderscore(m.id) : m.id;
|
|
570
|
+
return {
|
|
571
|
+
id,
|
|
572
|
+
name: id,
|
|
573
|
+
reasoning: false,
|
|
574
|
+
input: ["text"] as string[],
|
|
575
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
576
|
+
contextWindow: m.contextWindow ?? 128_000,
|
|
577
|
+
maxTokens: m.maxTokens ?? 4_096,
|
|
578
|
+
};
|
|
579
|
+
});
|
|
548
580
|
}
|
|
549
581
|
|
|
550
582
|
function compatKey(key: string): string {
|
|
@@ -552,12 +584,16 @@ function compatKey(key: string): string {
|
|
|
552
584
|
}
|
|
553
585
|
|
|
554
586
|
function registerProvider(pi: ExtensionAPI, key: string, p: ProviderConfig): void {
|
|
587
|
+
// Honor the persisted per-provider flag; fall back to the template default for
|
|
588
|
+
// this key so providers logged in before the flag existed (e.g. an existing
|
|
589
|
+
// llmproxy provider) still get the rewrite without a re-login.
|
|
590
|
+
const rewrite = p.rewriteDoubleUnderscore ?? TEMPLATES[key]?.rewriteDoubleUnderscore ?? false;
|
|
555
591
|
pi.registerProvider(compatKey(key), {
|
|
556
592
|
name: `compat/${key.replace(/_/g, "-")}`,
|
|
557
593
|
baseUrl: p.baseUrl,
|
|
558
594
|
apiKey: p.apiKey ?? (isLocalUrl(p.baseUrl) ? "local" : ""),
|
|
559
595
|
api: "openai-completions" as const,
|
|
560
|
-
models: buildProviderModels(p.cachedModels),
|
|
596
|
+
models: buildProviderModels(p.cachedModels, rewrite),
|
|
561
597
|
});
|
|
562
598
|
}
|
|
563
599
|
|
|
@@ -800,6 +836,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
800
836
|
modelsUrl,
|
|
801
837
|
modelsIdField: tpl.modelsIdField,
|
|
802
838
|
modelsKeepTask: tpl.modelsKeepTask,
|
|
839
|
+
rewriteDoubleUnderscore: tpl.rewriteDoubleUnderscore,
|
|
803
840
|
};
|
|
804
841
|
saveConfig(config);
|
|
805
842
|
registerProvider(pi, key, config.providers[key]);
|
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.16",
|
|
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",
|