@billjr99/pi-openai-compat 1.1.24 → 1.1.26
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 +3 -0
- package/index.ts +22 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -213,6 +213,9 @@ provider's `/models` catalog does not report them:
|
|
|
213
213
|
| `maxTokens` | number | `4096` | Maximum output tokens per response. |
|
|
214
214
|
| `reasoning` | boolean | `false` | Enables pi's thinking mode for the model. |
|
|
215
215
|
| `input` | `["text"]` or `["text","image"]` | `["text"]` | Modalities pi may send; `image` lets pi attach image blocks. Any other value is ignored. |
|
|
216
|
+
| `thinkingLevelMap` | object | omitted | pi thinking-level remap, passed through to the registered model. Keys are pi thinking levels (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`); string values are sent to the provider, `null` hides an unsupported level. See [pi's docs](https://pi.dev/docs/latest/models#thinking-level-map). |
|
|
217
|
+
| `samplingParams` | object | omitted | Free-form object merged verbatim into every request body for the model (e.g. `top_k`, `min_p`, `presence_penalty`). Only OpenAI-compatible APIs apply it. |
|
|
218
|
+
| `compat` | object | omitted | OpenAI compatibility flags for the model (`thinkingFormat`, `chatTemplateKwargs`, `maxTokensField`, `supportsDeveloperRole`, …). See [pi's docs](https://pi.dev/docs/latest/models#openai-compatibility). |
|
|
216
219
|
|
|
217
220
|
Most catalogs report none of these beyond the ID, so aggregators and proxies
|
|
218
221
|
(CLIProxyAPI, for example) register every model as a 128K, text-only,
|
package/index.ts
CHANGED
|
@@ -37,6 +37,11 @@ interface CachedModel {
|
|
|
37
37
|
// omits them (see README "Config file"); /compat-refresh preserves them.
|
|
38
38
|
reasoning?: boolean;
|
|
39
39
|
input?: ModelInput[];
|
|
40
|
+
// Optional provider-model passthrough (mirrors pi's ProviderModelConfig):
|
|
41
|
+
// thinking-level remap, verbatim sampling params, and OpenAI compat flags.
|
|
42
|
+
thinkingLevelMap?: Record<string, string | null>;
|
|
43
|
+
samplingParams?: Record<string, unknown>;
|
|
44
|
+
compat?: Record<string, unknown>;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
interface ProviderConfig {
|
|
@@ -602,6 +607,9 @@ function buildProviderModels(models: CachedModel[]) {
|
|
|
602
607
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
603
608
|
contextWindow: m.contextWindow ?? 128_000,
|
|
604
609
|
maxTokens: m.maxTokens ?? 4_096,
|
|
610
|
+
...(m.thinkingLevelMap ? { thinkingLevelMap: m.thinkingLevelMap } : {}),
|
|
611
|
+
...(m.samplingParams ? { samplingParams: m.samplingParams } : {}),
|
|
612
|
+
...(m.compat ? { compat: m.compat } : {}),
|
|
605
613
|
};
|
|
606
614
|
});
|
|
607
615
|
}
|
|
@@ -623,6 +631,9 @@ function mergeModelMetadata(previous: CachedModel[], fetched: CachedModel[]): Ca
|
|
|
623
631
|
maxTokens: m.maxTokens ?? old.maxTokens,
|
|
624
632
|
reasoning: m.reasoning ?? old.reasoning,
|
|
625
633
|
input: m.input ?? old.input,
|
|
634
|
+
thinkingLevelMap: m.thinkingLevelMap ?? old.thinkingLevelMap,
|
|
635
|
+
samplingParams: m.samplingParams ?? old.samplingParams,
|
|
636
|
+
compat: m.compat ?? old.compat,
|
|
626
637
|
};
|
|
627
638
|
});
|
|
628
639
|
}
|
|
@@ -760,8 +771,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
760
771
|
let modelsUrl: string | undefined = tpl.modelsUrl;
|
|
761
772
|
if (key === "cloudflare_workers") {
|
|
762
773
|
const entered = await ctx.ui.input(
|
|
763
|
-
"Account ID"
|
|
764
|
-
"Your Cloudflare Account ID (find it on the Cloudflare dashboard overview page):"
|
|
774
|
+
"Account ID: your Cloudflare Account ID (find it on the Cloudflare dashboard overview page)"
|
|
765
775
|
);
|
|
766
776
|
if (entered == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
767
777
|
const accountId = entered.trim();
|
|
@@ -770,24 +780,21 @@ export default async function (pi: ExtensionAPI) {
|
|
|
770
780
|
if (modelsUrl) modelsUrl = modelsUrl.replace("YOUR_ACCOUNT_ID", accountId);
|
|
771
781
|
} else if (key === "cloudflare_ai_gateway") {
|
|
772
782
|
const accountIdInput = await ctx.ui.input(
|
|
773
|
-
"Account ID"
|
|
774
|
-
"Your Cloudflare Account ID (find it on the Cloudflare dashboard overview page):"
|
|
783
|
+
"Account ID: your Cloudflare Account ID (find it on the Cloudflare dashboard overview page)"
|
|
775
784
|
);
|
|
776
785
|
if (accountIdInput == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
777
786
|
const accountId = accountIdInput.trim();
|
|
778
787
|
if (!accountId) { ctx.ui.notify("Account ID cannot be empty.", "error"); return; }
|
|
779
788
|
|
|
780
789
|
const gatewayInput = await ctx.ui.input(
|
|
781
|
-
"Gateway Name"
|
|
782
|
-
"Your AI Gateway name/slug (find it under AI → AI Gateway in the Cloudflare dashboard):"
|
|
790
|
+
"Gateway Name: your AI Gateway name/slug (find it under AI → AI Gateway in the Cloudflare dashboard)"
|
|
783
791
|
);
|
|
784
792
|
if (gatewayInput == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
785
793
|
const gatewaySlug = gatewayInput.trim();
|
|
786
794
|
if (!gatewaySlug) { ctx.ui.notify("Gateway name cannot be empty.", "error"); return; }
|
|
787
795
|
|
|
788
796
|
const providerInput = await ctx.ui.input(
|
|
789
|
-
"Provider"
|
|
790
|
-
"Upstream provider slug (e.g. openai, workers-ai, anthropic — must match your gateway config):"
|
|
797
|
+
"Provider: upstream provider slug (e.g. openai, workers-ai, anthropic — must match your gateway config; press Enter for openai)"
|
|
791
798
|
);
|
|
792
799
|
if (providerInput == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
793
800
|
const provider = providerInput.trim() || "openai";
|
|
@@ -804,10 +811,11 @@ export default async function (pi: ExtensionAPI) {
|
|
|
804
811
|
}
|
|
805
812
|
} else if (tpl.promptUrl) {
|
|
806
813
|
const defaultUrl = tpl.baseUrl;
|
|
814
|
+
// pi renders only the dialog title, so the guidance goes there.
|
|
807
815
|
const prompt = isLocalUrl(defaultUrl)
|
|
808
|
-
? `Base URL
|
|
809
|
-
: "Base URL
|
|
810
|
-
const entered = await ctx.ui.input(
|
|
816
|
+
? `Base URL: press Enter for default (${defaultUrl})`
|
|
817
|
+
: "Base URL: your endpoint (e.g. https://api.example.com/v1)";
|
|
818
|
+
const entered = await ctx.ui.input(prompt);
|
|
811
819
|
if (entered == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
812
820
|
baseUrl = (entered.trim() || defaultUrl).replace(/\/+$/, "");
|
|
813
821
|
if (!baseUrl) { ctx.ui.notify("Base URL cannot be empty.", "error"); return; }
|
|
@@ -817,9 +825,9 @@ export default async function (pi: ExtensionAPI) {
|
|
|
817
825
|
let apiKey: string | null = null;
|
|
818
826
|
if (!tpl.keyless && !isLocalUrl(baseUrl)) {
|
|
819
827
|
const keyPrompt = tpl.keyHint
|
|
820
|
-
? `
|
|
821
|
-
: "
|
|
822
|
-
const entered = await ctx.ui.input(
|
|
828
|
+
? `API Key: required — get it at ${tpl.keyHint}`
|
|
829
|
+
: "API Key: leave blank if keyless";
|
|
830
|
+
const entered = await ctx.ui.input(keyPrompt);
|
|
823
831
|
if (entered == null) { ctx.ui.notify("Login cancelled.", "info"); return; }
|
|
824
832
|
apiKey = entered.trim() || null;
|
|
825
833
|
}
|
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.26",
|
|
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",
|