pi-provider-litellm 2.0.4 → 2.0.6
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 +4 -0
- package/dist/discover.d.ts +2 -0
- package/dist/discover.js +57 -1
- package/dist/gcloud-token.js +1 -1
- package/dist/index.js +2 -7
- package/dist/provider.js +12 -1
- package/dist/types.d.ts +7 -0
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -137,6 +137,8 @@ LiteLLM Skills and MCP integration are enabled by default. Disable either featur
|
|
|
137
137
|
|
|
138
138
|
Setting `skills.enabled` to `false` disables the Skills Gateway management tools, skill fetching, and system-prompt injection. Setting `mcp.enabled` to `false` disables LiteLLM MCP discovery and tool registration. Restart Pi after changing these settings so previously registered tools are removed.
|
|
139
139
|
|
|
140
|
+
Treat the configured LiteLLM proxy as trusted: Skills can add instructions to the system prompt, and MCP can expose tools the agent may call. Disable these integrations when the proxy, its administrators, or its configured content are not fully trusted.
|
|
141
|
+
|
|
140
142
|
## Use
|
|
141
143
|
|
|
142
144
|
```
|
|
@@ -156,6 +158,8 @@ Setting `skills.enabled` to `false` disables the Skills Gateway management tools
|
|
|
156
158
|
| `LITELLM_VERBOSE_DISCOVERY` | unset | If `1`, enable progress messages during model and MCP discovery (login, refresh, startup); discovery is silent by default |
|
|
157
159
|
| `LITELLM_MODELS_DEV` | enabled | Set to `0` to disable models.dev metadata enrichment, including its cache and network request; `/v1/models` still uses Pi catalog metadata and defaults |
|
|
158
160
|
|
|
161
|
+
Only use a trusted `LITELLM_API_KEY_HELPER` or `!command`. Prefer an absolute executable path, keep secrets out of command arguments, and print only the token to stdout without logging it to stderr.
|
|
162
|
+
|
|
159
163
|
`LITELLM_DISCOVERY_TIMEOUT_MS=0` disables automatic and explicit refresh model discovery. It does not replace the base URL or API key settings required to send requests when you are not using `/login litellm`.
|
|
160
164
|
|
|
161
165
|
Models.dev metadata is cached in `litellm-models-dev.json` under the Pi agent directory for 28 days. Fresh data avoids the public request; stale data is used immediately while one background refresh updates the cache. Set `LITELLM_MODELS_DEV=0` when your LiteLLM metadata is authoritative and no external enrichment is wanted.
|
package/dist/discover.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
1
2
|
import type { DiscoveredModel, DiscoveryOptions, DiscoveryResult } from "./types.js";
|
|
2
3
|
export declare function normalizeBaseUrl(input: string): string;
|
|
3
4
|
export declare function isMoonshotModel(modelId: string): boolean;
|
|
4
5
|
export declare function isGpt55Model(modelId: string): boolean;
|
|
5
6
|
export declare function shouldSuppressReasoningContent(modelId: string): boolean;
|
|
6
7
|
export declare function buildCompat(modelId: string): DiscoveredModel["compat"];
|
|
8
|
+
export declare function enrichCachedModel(model: Model<Api>): Model<Api>;
|
|
7
9
|
export declare function discoverModels(baseUrl: string, apiKey: string, options?: DiscoveryOptions & {
|
|
8
10
|
onProgress?: (message: string) => void;
|
|
9
11
|
silent?: boolean;
|
package/dist/discover.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { isIP } from "node:net";
|
|
2
3
|
import { getModels, getProviders } from "@earendil-works/pi-ai/compat";
|
|
3
4
|
import { writeJsonAtomic } from "./cache.js";
|
|
4
5
|
const DEFAULT_TIMEOUT_MS = 5000;
|
|
@@ -10,6 +11,12 @@ const MODELS_DEV_CACHE_TTL_MS = 28 * 24 * 60 * 60 * 1000;
|
|
|
10
11
|
const modelsDevCaches = new Map();
|
|
11
12
|
const modelsDevRefreshes = new Map();
|
|
12
13
|
export function normalizeBaseUrl(input) {
|
|
14
|
+
const url = new URL(input);
|
|
15
|
+
const hostname = url.hostname.toLowerCase();
|
|
16
|
+
const loopback = hostname === "localhost" || hostname === "[::1]" || (isIP(hostname) === 4 && hostname.startsWith("127."));
|
|
17
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
18
|
+
throw new Error("LiteLLM base URL must use HTTPS except for loopback hosts");
|
|
19
|
+
}
|
|
13
20
|
return input.replace(/\/+$/, "").replace(/\/v1\/?$/i, "");
|
|
14
21
|
}
|
|
15
22
|
const RESPONSES_MODE_PATTERN = /^responses?$/i;
|
|
@@ -77,6 +84,36 @@ function findCatalogModel(id, ownedBy) {
|
|
|
77
84
|
}
|
|
78
85
|
return undefined;
|
|
79
86
|
}
|
|
87
|
+
export function enrichCachedModel(model) {
|
|
88
|
+
// ponytail: legacy cache lacks field provenance; add per-field cache provenance if strict preservation becomes necessary.
|
|
89
|
+
if (!model.name.endsWith(" (no metadata)") ||
|
|
90
|
+
model.reasoning ||
|
|
91
|
+
model.thinkingLevelMap !== undefined ||
|
|
92
|
+
model.input.length !== 1 ||
|
|
93
|
+
model.input[0] !== "text" ||
|
|
94
|
+
model.cost.input !== 0 ||
|
|
95
|
+
model.cost.output !== 0 ||
|
|
96
|
+
model.cost.cacheRead !== 0 ||
|
|
97
|
+
model.cost.cacheWrite !== 0 ||
|
|
98
|
+
model.cost.tiers !== undefined ||
|
|
99
|
+
model.contextWindow !== DEFAULT_CONTEXT_WINDOW ||
|
|
100
|
+
model.maxTokens !== DEFAULT_MAX_TOKENS) {
|
|
101
|
+
return model;
|
|
102
|
+
}
|
|
103
|
+
const catalogModel = findCatalogModel(model.id);
|
|
104
|
+
if (!catalogModel)
|
|
105
|
+
return model;
|
|
106
|
+
return {
|
|
107
|
+
...model,
|
|
108
|
+
name: catalogModel.name,
|
|
109
|
+
reasoning: catalogModel.reasoning,
|
|
110
|
+
thinkingLevelMap: catalogModel.thinkingLevelMap,
|
|
111
|
+
input: catalogModel.input,
|
|
112
|
+
cost: catalogModel.cost,
|
|
113
|
+
contextWindow: catalogModel.contextWindow,
|
|
114
|
+
maxTokens: catalogModel.maxTokens,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
80
117
|
function catalogLookupIds(id) {
|
|
81
118
|
const lookupIds = new Set([id]);
|
|
82
119
|
const unprefixed = id.includes("/") ? id.slice(id.indexOf("/") + 1) : id;
|
|
@@ -325,6 +362,21 @@ function mapModelsDevMetadata(model) {
|
|
|
325
362
|
}
|
|
326
363
|
return metadata;
|
|
327
364
|
}
|
|
365
|
+
function mapReasoningEfforts(info) {
|
|
366
|
+
const flags = [
|
|
367
|
+
["off", "none", info.supports_none_reasoning_effort],
|
|
368
|
+
["minimal", "minimal", info.supports_minimal_reasoning_effort],
|
|
369
|
+
["low", "low", info.supports_low_reasoning_effort],
|
|
370
|
+
["medium", "medium", info.supports_medium_reasoning_effort],
|
|
371
|
+
["high", "high", info.supports_high_reasoning_effort],
|
|
372
|
+
["xhigh", "xhigh", info.supports_xhigh_reasoning_effort],
|
|
373
|
+
["max", "max", info.supports_max_reasoning_effort],
|
|
374
|
+
];
|
|
375
|
+
const map = Object.fromEntries(flags
|
|
376
|
+
.filter(([, , supported]) => supported !== undefined)
|
|
377
|
+
.map(([level, value, supported]) => [level, supported ? value : null]));
|
|
378
|
+
return Object.keys(map).length > 0 ? map : undefined;
|
|
379
|
+
}
|
|
328
380
|
function mapFromModelInfo(entry) {
|
|
329
381
|
const id = entry.model_name;
|
|
330
382
|
if (!id)
|
|
@@ -334,11 +386,15 @@ function mapFromModelInfo(entry) {
|
|
|
334
386
|
return undefined;
|
|
335
387
|
const responsesMode = isResponsesMode(info.mode);
|
|
336
388
|
const catalogModel = findCatalogModel(id);
|
|
389
|
+
const reasoningEffortMap = mapReasoningEfforts(info);
|
|
390
|
+
const thinkingLevelMap = catalogModel?.thinkingLevelMap || reasoningEffortMap
|
|
391
|
+
? { ...catalogModel?.thinkingLevelMap, ...reasoningEffortMap }
|
|
392
|
+
: undefined;
|
|
337
393
|
return {
|
|
338
394
|
id,
|
|
339
395
|
name: id,
|
|
340
396
|
reasoning: info.supports_reasoning ?? false,
|
|
341
|
-
...(
|
|
397
|
+
...(thinkingLevelMap ? { thinkingLevelMap } : {}),
|
|
342
398
|
input: info.supports_vision ? ["text", "image"] : ["text"],
|
|
343
399
|
cost: mapModelInfoCost(info, catalogModel?.cost),
|
|
344
400
|
contextWindow: info.max_input_tokens ?? DEFAULT_CONTEXT_WINDOW,
|
package/dist/gcloud-token.js
CHANGED
|
@@ -85,7 +85,7 @@ async function exchangeRefreshToken(credentials) {
|
|
|
85
85
|
signal: AbortSignal.timeout(10_000),
|
|
86
86
|
});
|
|
87
87
|
if (!response.ok) {
|
|
88
|
-
console.warn(`LiteLLM gcloud auth: Token exchange failed (${response.status})
|
|
88
|
+
console.warn(`LiteLLM gcloud auth: Token exchange failed (${response.status})`);
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
const data = (await response.json());
|
package/dist/index.js
CHANGED
|
@@ -140,8 +140,7 @@ async function generateVirtualKey(baseUrl, userToken, signal, headers) {
|
|
|
140
140
|
signal: boundedSignal,
|
|
141
141
|
});
|
|
142
142
|
if (!response.ok) {
|
|
143
|
-
|
|
144
|
-
throw new Error(`Virtual key generation failed (${response.status}): ${text}`);
|
|
143
|
+
throw new Error(`Virtual key generation failed (${response.status})`);
|
|
145
144
|
}
|
|
146
145
|
const data = (await response.json());
|
|
147
146
|
if (typeof data.key !== "string" || !data.key)
|
|
@@ -948,11 +947,7 @@ export default async function (pi) {
|
|
|
948
947
|
context.credential) {
|
|
949
948
|
const auth = await authForCredential(definition, context.credential);
|
|
950
949
|
defaultRuntimeAuth = auth;
|
|
951
|
-
|
|
952
|
-
if (context.signal)
|
|
953
|
-
await registration;
|
|
954
|
-
else
|
|
955
|
-
void registration.catch(() => undefined);
|
|
950
|
+
void registerMcpTools(auth, context.signal).catch(() => undefined);
|
|
956
951
|
}
|
|
957
952
|
}
|
|
958
953
|
};
|
package/dist/provider.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createProvider } from "@earendil-works/pi-ai";
|
|
2
2
|
import { openAICompletionsApi, openAIResponsesApi } from "@earendil-works/pi-ai/compat";
|
|
3
|
+
import { enrichCachedModel } from "./discover.js";
|
|
3
4
|
export function toNativeModels(provider, baseUrl, models) {
|
|
4
5
|
return models.map((model) => ({
|
|
5
6
|
...model,
|
|
@@ -9,7 +10,7 @@ export function toNativeModels(provider, baseUrl, models) {
|
|
|
9
10
|
}));
|
|
10
11
|
}
|
|
11
12
|
export function createLiteLLMProvider(options) {
|
|
12
|
-
|
|
13
|
+
const provider = createProvider({
|
|
13
14
|
id: options.id,
|
|
14
15
|
name: options.name,
|
|
15
16
|
baseUrl: options.baseUrl,
|
|
@@ -26,4 +27,14 @@ export function createLiteLLMProvider(options) {
|
|
|
26
27
|
"openai-responses": openAIResponsesApi(),
|
|
27
28
|
},
|
|
28
29
|
});
|
|
30
|
+
const refreshModels = provider.refreshModels;
|
|
31
|
+
if (!refreshModels)
|
|
32
|
+
return provider;
|
|
33
|
+
return {
|
|
34
|
+
...provider,
|
|
35
|
+
refreshModels: (context) => refreshModels({
|
|
36
|
+
...context,
|
|
37
|
+
stored: context.stored && { ...context.stored, models: context.stored.models.map(enrichCachedModel) },
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
29
40
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -31,6 +31,13 @@ export interface ModelInfoEntry {
|
|
|
31
31
|
max_input_tokens?: number;
|
|
32
32
|
max_output_tokens?: number;
|
|
33
33
|
supports_reasoning?: boolean;
|
|
34
|
+
supports_none_reasoning_effort?: boolean;
|
|
35
|
+
supports_minimal_reasoning_effort?: boolean;
|
|
36
|
+
supports_low_reasoning_effort?: boolean;
|
|
37
|
+
supports_medium_reasoning_effort?: boolean;
|
|
38
|
+
supports_high_reasoning_effort?: boolean;
|
|
39
|
+
supports_xhigh_reasoning_effort?: boolean;
|
|
40
|
+
supports_max_reasoning_effort?: boolean;
|
|
34
41
|
supports_vision?: boolean;
|
|
35
42
|
};
|
|
36
43
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-litellm",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "LiteLLM proxy provider extension for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,15 +58,24 @@
|
|
|
58
58
|
"@earendil-works/pi-ai": ">=0.81.0",
|
|
59
59
|
"@earendil-works/pi-coding-agent": ">=0.81.0"
|
|
60
60
|
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@earendil-works/pi-ai": {
|
|
63
|
+
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"@earendil-works/pi-coding-agent": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
61
69
|
"overrides": {
|
|
62
70
|
"basic-ftp": "6.0.1",
|
|
63
|
-
"
|
|
64
|
-
"protobufjs": "8.7.1"
|
|
71
|
+
"nanoid": "3.3.18",
|
|
72
|
+
"protobufjs": "8.7.1",
|
|
73
|
+
"undici": "8.9.0"
|
|
65
74
|
},
|
|
66
75
|
"devDependencies": {
|
|
67
76
|
"@biomejs/biome": "^2.4.15",
|
|
68
|
-
"@earendil-works/pi-ai": "^0.
|
|
69
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
77
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
78
|
+
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
70
79
|
"@kitlangton/terminal-control": "0.6.0",
|
|
71
80
|
"@types/node": "^26.0.0",
|
|
72
81
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|