@billjr99/pi-openai-compat 1.1.21 → 1.1.23
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 +36 -1
- package/index.ts +55 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -129,7 +129,10 @@ dropped) since you logged in, without restarting your session.
|
|
|
129
129
|
If you have multiple providers registered, you are asked which one to refresh
|
|
130
130
|
(or choose **All providers**); with a single provider it refreshes directly.
|
|
131
131
|
A failed or empty refresh leaves the existing model list untouched, so a flaky
|
|
132
|
-
network call can't blank out a working provider.
|
|
132
|
+
network call can't blank out a working provider. Capability metadata you have
|
|
133
|
+
hand-edited into `cachedModels` (see [Config file](#config-file)) survives a
|
|
134
|
+
refresh: the fetched list decides which model IDs exist, and any field the
|
|
135
|
+
provider's catalog omits is carried forward from the previous cache.
|
|
133
136
|
|
|
134
137
|
### `/compat-logout`
|
|
135
138
|
|
|
@@ -199,6 +202,38 @@ Credentials and cached model lists are stored at:
|
|
|
199
202
|
API keys are stored in plaintext. Protect the file with `chmod 600` if
|
|
200
203
|
needed, or delete it to clear all saved credentials.
|
|
201
204
|
|
|
205
|
+
Each provider's `cachedModels` array holds one entry per model. Only `id` is
|
|
206
|
+
required; the rest are optional and fall back to conservative defaults when the
|
|
207
|
+
provider's `/models` catalog does not report them:
|
|
208
|
+
|
|
209
|
+
| Field | Type | Default | Notes |
|
|
210
|
+
|---|---|---|---|
|
|
211
|
+
| `id` | string | required | Model ID as sent in requests. |
|
|
212
|
+
| `contextWindow` | number | `128000` | Context window in tokens. |
|
|
213
|
+
| `maxTokens` | number | `4096` | Maximum output tokens per response. |
|
|
214
|
+
| `reasoning` | boolean | `false` | Enables pi's thinking mode for the model. |
|
|
215
|
+
| `input` | `["text"]` or `["text","image"]` | `["text"]` | Modalities pi may send; `image` lets pi attach image blocks. Any other value is ignored. |
|
|
216
|
+
|
|
217
|
+
Most catalogs report none of these beyond the ID, so aggregators and proxies
|
|
218
|
+
(CLIProxyAPI, for example) register every model as a 128K, text-only,
|
|
219
|
+
non-reasoning model. To correct that, edit the entry by hand and `/reload`:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"providers": {
|
|
224
|
+
"cpa": {
|
|
225
|
+
"cachedModels": [
|
|
226
|
+
{ "id": "claude-sonnet-4-6", "contextWindow": 1000000, "maxTokens": 128000,
|
|
227
|
+
"reasoning": true, "input": ["text", "image"] }
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`/compat-refresh` keeps these hand-edited fields for any model ID that is still
|
|
235
|
+
present in the refreshed catalog; a field the provider does report always wins.
|
|
236
|
+
|
|
202
237
|
### Adding a provider without pi — `add-provider.sh`
|
|
203
238
|
|
|
204
239
|
`/compat-login` is the normal path, but the repo also ships a standalone script
|
package/index.ts
CHANGED
|
@@ -25,10 +25,18 @@ import * as os from "node:os";
|
|
|
25
25
|
// Types
|
|
26
26
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
27
|
|
|
28
|
+
/** Input modalities pi understands; anything else is dropped at fetch time. */
|
|
29
|
+
type ModelInput = "text" | "image";
|
|
30
|
+
|
|
28
31
|
interface CachedModel {
|
|
29
32
|
id: string;
|
|
30
33
|
contextWindow?: number;
|
|
31
34
|
maxTokens?: number;
|
|
35
|
+
// Optional capability metadata. Rarely present in a /models payload, but a
|
|
36
|
+
// user may hand-edit them into cachedModels for providers whose catalog
|
|
37
|
+
// omits them (see README "Config file"); /compat-refresh preserves them.
|
|
38
|
+
reasoning?: boolean;
|
|
39
|
+
input?: ModelInput[];
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
interface ProviderConfig {
|
|
@@ -67,9 +75,25 @@ type RawModel = {
|
|
|
67
75
|
name?: string;
|
|
68
76
|
context_window?: number;
|
|
69
77
|
max_tokens?: number;
|
|
78
|
+
reasoning?: unknown;
|
|
79
|
+
input?: unknown;
|
|
70
80
|
task?: { name?: string };
|
|
71
81
|
};
|
|
72
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Coerce an upstream `input` value into pi's modality list. Unknown or
|
|
85
|
+
* malformed values yield undefined so buildProviderModels falls back to the
|
|
86
|
+
* default, rather than caching something pi would choke on (it calls
|
|
87
|
+
* `model.input.includes("image")` at tool time).
|
|
88
|
+
*/
|
|
89
|
+
function normalizeInput(value: unknown): ModelInput[] | undefined {
|
|
90
|
+
if (!Array.isArray(value)) return undefined;
|
|
91
|
+
const kept = value.filter(
|
|
92
|
+
(v): v is ModelInput => v === "text" || v === "image",
|
|
93
|
+
);
|
|
94
|
+
return kept.length > 0 ? kept : undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
73
97
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
74
98
|
// Provider templates
|
|
75
99
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -551,7 +575,13 @@ async function fetchModels(
|
|
|
551
575
|
typeof rawId === "string" ? rawId :
|
|
552
576
|
typeof rawId === "number" ? String(rawId) :
|
|
553
577
|
"";
|
|
554
|
-
return {
|
|
578
|
+
return {
|
|
579
|
+
id,
|
|
580
|
+
contextWindow: m.context_window,
|
|
581
|
+
maxTokens: m.max_tokens,
|
|
582
|
+
reasoning: typeof m.reasoning === "boolean" ? m.reasoning : undefined,
|
|
583
|
+
input: normalizeInput(m.input),
|
|
584
|
+
};
|
|
555
585
|
})
|
|
556
586
|
.filter((m) => Boolean(m.id))
|
|
557
587
|
.sort((a, b) => a.id.localeCompare(b.id));
|
|
@@ -567,8 +597,8 @@ function buildProviderModels(models: CachedModel[]) {
|
|
|
567
597
|
return {
|
|
568
598
|
id,
|
|
569
599
|
name: id,
|
|
570
|
-
reasoning: false,
|
|
571
|
-
input: ["text"] as
|
|
600
|
+
reasoning: m.reasoning ?? false,
|
|
601
|
+
input: m.input ?? (["text"] as ModelInput[]),
|
|
572
602
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
573
603
|
contextWindow: m.contextWindow ?? 128_000,
|
|
574
604
|
maxTokens: m.maxTokens ?? 4_096,
|
|
@@ -576,6 +606,27 @@ function buildProviderModels(models: CachedModel[]) {
|
|
|
576
606
|
});
|
|
577
607
|
}
|
|
578
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Carry hand-edited capability metadata (reasoning / input / contextWindow /
|
|
611
|
+
* maxTokens) forward from the previous cache when a fresh /models fetch omits
|
|
612
|
+
* it. The fetched list decides which ids exist; the old cache only fills gaps,
|
|
613
|
+
* so a provider that does report a field always wins.
|
|
614
|
+
*/
|
|
615
|
+
function mergeModelMetadata(previous: CachedModel[], fetched: CachedModel[]): CachedModel[] {
|
|
616
|
+
const prior = new Map(previous.map((m) => [m.id, m]));
|
|
617
|
+
return fetched.map((m) => {
|
|
618
|
+
const old = prior.get(m.id);
|
|
619
|
+
if (!old) return m;
|
|
620
|
+
return {
|
|
621
|
+
...m,
|
|
622
|
+
contextWindow: m.contextWindow ?? old.contextWindow,
|
|
623
|
+
maxTokens: m.maxTokens ?? old.maxTokens,
|
|
624
|
+
reasoning: m.reasoning ?? old.reasoning,
|
|
625
|
+
input: m.input ?? old.input,
|
|
626
|
+
};
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
579
630
|
function compatKey(key: string): string {
|
|
580
631
|
return `compat-${key}`;
|
|
581
632
|
}
|
|
@@ -888,7 +939,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
888
939
|
if (models.length > 0) {
|
|
889
940
|
// Only overwrite the cache on a successful, non-empty fetch — a
|
|
890
941
|
// flaky refresh must never blank out a working provider's models.
|
|
891
|
-
p.cachedModels = models;
|
|
942
|
+
p.cachedModels = mergeModelMetadata(p.cachedModels, models);
|
|
892
943
|
saveConfig(config);
|
|
893
944
|
registerProvider(pi, key, p);
|
|
894
945
|
refreshed.push(`${p.displayName} (${models.length})`);
|
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.23",
|
|
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",
|