pi-provider-litellm 2.0.5 → 2.1.0
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 +13 -6
- package/dist/discover.js +27 -1
- package/dist/gcloud-token.js +1 -1
- package/dist/index.js +148 -19
- package/dist/provider.js +1 -8
- package/dist/types.d.ts +7 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -46,17 +46,17 @@ You'll be prompted for the base URL and API key. Credentials are persisted to `~
|
|
|
46
46
|
|
|
47
47
|
#### Enterprise SSO login
|
|
48
48
|
|
|
49
|
-
If your LiteLLM proxy requires SSO/OAuth authentication (enterprise deployments), you can authenticate
|
|
49
|
+
If your LiteLLM proxy requires SSO/OAuth authentication (enterprise deployments), you can authenticate through LiteLLM's CLI SSO flow:
|
|
50
50
|
|
|
51
51
|
1. Run `/login litellm` inside pi and select `Sign in with LiteLLM SSO`
|
|
52
52
|
2. Enter the proxy URL
|
|
53
|
-
3.
|
|
54
|
-
4.
|
|
55
|
-
5.
|
|
53
|
+
3. Pi displays a verification code and opens the LiteLLM SSO login URL automatically
|
|
54
|
+
4. Authenticate in the browser and confirm the displayed code
|
|
55
|
+
5. If your account belongs to multiple teams, select the team for this session in Pi
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Pi polls the proxy until authentication completes and stores the returned short-lived CLI token. Newer proxies advertise the token lifetime; for older proxies, Pi uses `LITELLM_CLI_JWT_EXPIRATION_HOURS` or LiteLLM's 24-hour default. On LiteLLM versions that gate CLI SSO, start the proxy with `EXPERIMENTAL_UI_LOGIN=True`.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Older proxies without `/sso/cli/start` fall back to the legacy browser flow: copy the token from the LiteLLM UI, paste it into Pi, and optionally exchange it for a virtual key. Pi reads JWT expiry claims and prompts you to run `/login litellm` again when re-authentication is required.
|
|
60
60
|
|
|
61
61
|
### Option B — environment variables
|
|
62
62
|
|
|
@@ -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
|
```
|
|
@@ -153,9 +155,12 @@ Setting `skills.enabled` to `false` disables the Skills Gateway management tools
|
|
|
153
155
|
| `GOOGLE_APPLICATION_CREDENTIALS` | Google default ADC path | Optional path to an ADC JSON file used by `LITELLM_GCLOUD_TOKEN_AUTH`. If unset, the extension checks the default gcloud ADC locations. |
|
|
154
156
|
| `LITELLM_OFFLINE` | unset | If `1`, disable all model and MCP discovery, including post-login discovery; use cached models only |
|
|
155
157
|
| `LITELLM_DISCOVERY_TIMEOUT_MS` | `5000` | Background and explicit discovery fetch timeout in ms; `0` disables automatic discovery |
|
|
158
|
+
| `LITELLM_CLI_JWT_EXPIRATION_HOURS` | `24` | CLI SSO token lifetime fallback for older proxies whose poll response omits `expires_in`; mirror a non-default proxy setting locally |
|
|
156
159
|
| `LITELLM_VERBOSE_DISCOVERY` | unset | If `1`, enable progress messages during model and MCP discovery (login, refresh, startup); discovery is silent by default |
|
|
157
160
|
| `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
161
|
|
|
162
|
+
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.
|
|
163
|
+
|
|
159
164
|
`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
165
|
|
|
161
166
|
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.
|
|
@@ -238,6 +243,8 @@ Opening `/model` refreshes configured provider catalogs in the background using
|
|
|
238
243
|
| Discovery times out | Increase `LITELLM_DISCOVERY_TIMEOUT_MS` or set `LITELLM_OFFLINE=1` to fall back on cached models |
|
|
239
244
|
| `401 Token expired` | Set `LITELLM_API_KEY_HELPER`. |
|
|
240
245
|
| No models with gcloud auth | Verify `gcloud auth application-default login` has been run or set `GOOGLE_APPLICATION_CREDENTIALS` to an `authorized_user` ADC file |
|
|
246
|
+
| Enterprise SSO waits for token insertion | The proxy returned 404/405 for `/sso/cli/start`, so Pi used the legacy flow — upgrade LiteLLM or paste the UI token |
|
|
247
|
+
| Enterprise CLI SSO start/poll fails | Check the proxy logs and verify `/sso/cli/start` and `/sso/cli/poll/{login_id}` are reachable; only 404/405 falls back to legacy login |
|
|
241
248
|
| Enterprise SSO login shows "virtual key generation failed" | The LiteLLM instance may lack a database (`/key/generate` requires one), your user account may lack key-generation permission, or the request timed out; the JWT is used directly as a fallback |
|
|
242
249
|
| Enterprise SSO token prompt fails with "SSO token is required" | The token field was left empty — paste the token copied from the LiteLLM UI |
|
|
243
250
|
| MCP tools not showing | Verify the proxy exposes `/mcp-rest/tools/list` and open `/model` after fixing the proxy |
|
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;
|
|
@@ -355,6 +362,21 @@ function mapModelsDevMetadata(model) {
|
|
|
355
362
|
}
|
|
356
363
|
return metadata;
|
|
357
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
|
+
}
|
|
358
380
|
function mapFromModelInfo(entry) {
|
|
359
381
|
const id = entry.model_name;
|
|
360
382
|
if (!id)
|
|
@@ -364,11 +386,15 @@ function mapFromModelInfo(entry) {
|
|
|
364
386
|
return undefined;
|
|
365
387
|
const responsesMode = isResponsesMode(info.mode);
|
|
366
388
|
const catalogModel = findCatalogModel(id);
|
|
389
|
+
const reasoningEffortMap = mapReasoningEfforts(info);
|
|
390
|
+
const thinkingLevelMap = catalogModel?.thinkingLevelMap || reasoningEffortMap
|
|
391
|
+
? { ...catalogModel?.thinkingLevelMap, ...reasoningEffortMap }
|
|
392
|
+
: undefined;
|
|
367
393
|
return {
|
|
368
394
|
id,
|
|
369
395
|
name: id,
|
|
370
396
|
reasoning: info.supports_reasoning ?? false,
|
|
371
|
-
...(
|
|
397
|
+
...(thinkingLevelMap ? { thinkingLevelMap } : {}),
|
|
372
398
|
input: info.supports_vision ? ["text", "image"] : ["text"],
|
|
373
399
|
cost: mapModelInfoCost(info, catalogModel?.cost),
|
|
374
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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
4
5
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
6
|
import { setupLiteLLMCostTracking } from "./cost.js";
|
|
6
7
|
import { discoverModels, isGpt55Model, isMoonshotModel, normalizeBaseUrl, shouldSuppressReasoningContent, } from "./discover.js";
|
|
@@ -16,11 +17,15 @@ const ENV_API_KEY = "LITELLM_API_KEY";
|
|
|
16
17
|
const ENV_API_KEY_HELPER = "LITELLM_API_KEY_HELPER";
|
|
17
18
|
const ENV_HEADERS = "LITELLM_HEADERS";
|
|
18
19
|
const ENV_TIMEOUT = "LITELLM_DISCOVERY_TIMEOUT_MS";
|
|
20
|
+
const ENV_CLI_JWT_EXPIRATION_HOURS = "LITELLM_CLI_JWT_EXPIRATION_HOURS";
|
|
19
21
|
const ENV_OFFLINE = "LITELLM_OFFLINE";
|
|
20
22
|
const ENV_VERBOSE_DISCOVERY = "LITELLM_VERBOSE_DISCOVERY";
|
|
21
23
|
const ENV_MODELS_DEV = "LITELLM_MODELS_DEV";
|
|
22
24
|
const DEFAULT_TIMEOUT_MS = 5000;
|
|
23
25
|
const LOGIN_TIMEOUT_MS = 10_000;
|
|
26
|
+
const CLI_SSO_POLL_INTERVAL_MS = 2_000;
|
|
27
|
+
const CLI_SSO_EXPIRES_IN_SECONDS = 600;
|
|
28
|
+
const DEFAULT_CLI_JWT_EXPIRATION_HOURS = 24;
|
|
24
29
|
const MODELS_DEV_CACHE_FILENAME = "litellm-models-dev.json";
|
|
25
30
|
const TOKEN_REFRESH_LEAD_MS = 5 * 60 * 1000;
|
|
26
31
|
const PERMANENT_TOKEN_EXPIRES_AT = Number.MAX_SAFE_INTEGER;
|
|
@@ -125,10 +130,17 @@ function tokenExpiresAt(apiKey, opaqueFallback = PERMANENT_TOKEN_EXPIRES_AT) {
|
|
|
125
130
|
return opaqueFallback;
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
|
-
|
|
129
|
-
const
|
|
133
|
+
function configuredCliJwtExpiresAt() {
|
|
134
|
+
const configured = Number(process.env[ENV_CLI_JWT_EXPIRATION_HOURS]);
|
|
135
|
+
const hours = Number.isFinite(configured) && configured > 0 ? configured : DEFAULT_CLI_JWT_EXPIRATION_HOURS;
|
|
136
|
+
return Date.now() + hours * 60 * 60 * 1_000;
|
|
137
|
+
}
|
|
138
|
+
function boundedLoginSignal(signal) {
|
|
139
|
+
return signal
|
|
130
140
|
? AbortSignal.any([signal, AbortSignal.timeout(LOGIN_TIMEOUT_MS)])
|
|
131
141
|
: AbortSignal.timeout(LOGIN_TIMEOUT_MS);
|
|
142
|
+
}
|
|
143
|
+
async function generateVirtualKey(baseUrl, userToken, signal, headers) {
|
|
132
144
|
const response = await fetch(`${baseUrl}/key/generate`, {
|
|
133
145
|
method: "POST",
|
|
134
146
|
headers: {
|
|
@@ -137,11 +149,10 @@ async function generateVirtualKey(baseUrl, userToken, signal, headers) {
|
|
|
137
149
|
"Content-Type": "application/json",
|
|
138
150
|
},
|
|
139
151
|
body: JSON.stringify({}),
|
|
140
|
-
signal:
|
|
152
|
+
signal: boundedLoginSignal(signal),
|
|
141
153
|
});
|
|
142
154
|
if (!response.ok) {
|
|
143
|
-
|
|
144
|
-
throw new Error(`Virtual key generation failed (${response.status}): ${text}`);
|
|
155
|
+
throw new Error(`Virtual key generation failed (${response.status})`);
|
|
145
156
|
}
|
|
146
157
|
const data = (await response.json());
|
|
147
158
|
if (typeof data.key !== "string" || !data.key)
|
|
@@ -401,15 +412,108 @@ async function loginApiKey(interaction) {
|
|
|
401
412
|
throw new Error("Both base URL and API key are required");
|
|
402
413
|
return { type: "api_key", key, env: { [ENV_BASE_URL]: normalizeBaseUrl(rawBaseUrl) } };
|
|
403
414
|
}
|
|
404
|
-
async function
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
})
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
415
|
+
async function startCliSso(baseUrl, signal, headers) {
|
|
416
|
+
const response = await fetch(`${baseUrl}/sso/cli/start`, {
|
|
417
|
+
method: "POST",
|
|
418
|
+
headers,
|
|
419
|
+
signal: boundedLoginSignal(signal),
|
|
420
|
+
});
|
|
421
|
+
if (response.status === 404 || response.status === 405)
|
|
422
|
+
return undefined;
|
|
423
|
+
if (!response.ok)
|
|
424
|
+
throw new Error(`LiteLLM CLI SSO start failed (HTTP ${response.status})`);
|
|
425
|
+
const data = (await response.json());
|
|
426
|
+
if (typeof data.login_id !== "string" ||
|
|
427
|
+
!data.login_id ||
|
|
428
|
+
typeof data.poll_secret !== "string" ||
|
|
429
|
+
!data.poll_secret ||
|
|
430
|
+
typeof data.user_code !== "string" ||
|
|
431
|
+
!data.user_code) {
|
|
432
|
+
throw new Error("LiteLLM CLI SSO start returned an invalid response");
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
loginId: data.login_id,
|
|
436
|
+
pollSecret: data.poll_secret,
|
|
437
|
+
userCode: data.user_code,
|
|
438
|
+
expiresInSeconds: typeof data.expires_in === "number" && Number.isFinite(data.expires_in) && data.expires_in > 0
|
|
439
|
+
? data.expires_in
|
|
440
|
+
: CLI_SSO_EXPIRES_IN_SECONDS,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
async function waitForNextCliSsoPoll(interaction) {
|
|
444
|
+
try {
|
|
445
|
+
await delay(CLI_SSO_POLL_INTERVAL_MS, undefined, interaction.signal ? { signal: interaction.signal } : undefined);
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
if (interaction.signal?.aborted)
|
|
449
|
+
throw interaction.signal.reason;
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
async function pollCliSso(baseUrl, start, interaction, headers) {
|
|
454
|
+
const deadline = Date.now() + start.expiresInSeconds * 1_000;
|
|
455
|
+
const pollUrl = new URL(`${baseUrl}/sso/cli/poll/${encodeURIComponent(start.loginId)}`);
|
|
456
|
+
let teamId;
|
|
457
|
+
while (Date.now() < deadline) {
|
|
458
|
+
if (teamId)
|
|
459
|
+
pollUrl.searchParams.set("team_id", teamId);
|
|
460
|
+
let response;
|
|
461
|
+
try {
|
|
462
|
+
response = await fetch(pollUrl, {
|
|
463
|
+
headers: { ...headers, "x-litellm-cli-poll-secret": start.pollSecret },
|
|
464
|
+
signal: boundedLoginSignal(interaction.signal),
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
if (interaction.signal?.aborted)
|
|
469
|
+
throw interaction.signal.reason;
|
|
470
|
+
await waitForNextCliSsoPoll(interaction);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (response.status === 429 || response.status >= 500) {
|
|
474
|
+
await waitForNextCliSsoPoll(interaction);
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (response.status === 400)
|
|
478
|
+
throw new Error("LiteLLM CLI SSO login expired or is invalid");
|
|
479
|
+
if (!response.ok)
|
|
480
|
+
throw new Error(`LiteLLM CLI SSO polling failed (HTTP ${response.status})`);
|
|
481
|
+
const data = (await response.json());
|
|
482
|
+
if (data.status === "ready" && typeof data.key === "string" && data.key) {
|
|
483
|
+
return {
|
|
484
|
+
access: data.key,
|
|
485
|
+
expiresInSeconds: typeof data.expires_in === "number" && Number.isFinite(data.expires_in) && data.expires_in > 0
|
|
486
|
+
? data.expires_in
|
|
487
|
+
: undefined,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if (data.status === "ready" && data.requires_team_selection === true && !teamId) {
|
|
491
|
+
const details = Array.isArray(data.team_details) ? data.team_details : [];
|
|
492
|
+
const teams = details.flatMap((team) => {
|
|
493
|
+
if (!team || typeof team !== "object")
|
|
494
|
+
return [];
|
|
495
|
+
const record = team;
|
|
496
|
+
const id = typeof record.team_id === "string" ? record.team_id : record.id;
|
|
497
|
+
const alias = record.team_alias;
|
|
498
|
+
return typeof id === "string" && id ? [{ id, label: typeof alias === "string" && alias ? alias : id }] : [];
|
|
499
|
+
});
|
|
500
|
+
if (teams.length === 0 && Array.isArray(data.teams))
|
|
501
|
+
teams.push(...data.teams.flatMap((id) => (typeof id === "string" && id ? [{ id, label: id }] : [])));
|
|
502
|
+
if (teams.length === 0)
|
|
503
|
+
throw new Error("LiteLLM CLI SSO requested team selection without any teams");
|
|
504
|
+
const selected = await interaction.prompt({ type: "select", message: "Select a LiteLLM team:", options: teams });
|
|
505
|
+
if (!teams.some((team) => team.id === selected))
|
|
506
|
+
throw new Error("Invalid LiteLLM team selection");
|
|
507
|
+
teamId = selected;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
if (data.status !== "pending")
|
|
511
|
+
throw new Error("LiteLLM CLI SSO polling returned an invalid response");
|
|
512
|
+
await waitForNextCliSsoPoll(interaction);
|
|
513
|
+
}
|
|
514
|
+
throw new Error("LiteLLM CLI SSO login expired");
|
|
515
|
+
}
|
|
516
|
+
async function loginWithPastedToken(interaction, baseUrl, headers) {
|
|
413
517
|
interaction.notify({
|
|
414
518
|
type: "auth_url",
|
|
415
519
|
url: `${baseUrl}/sso/key/generate`,
|
|
@@ -449,6 +553,35 @@ async function loginOAuth(interaction, headers) {
|
|
|
449
553
|
}
|
|
450
554
|
return { type: "oauth", access, refresh: "", expires, baseUrl };
|
|
451
555
|
}
|
|
556
|
+
async function loginOAuth(interaction, headers) {
|
|
557
|
+
const rawBaseUrl = (await interaction.prompt({
|
|
558
|
+
type: "text",
|
|
559
|
+
message: "Enter LiteLLM proxy URL (no trailing /v1):",
|
|
560
|
+
placeholder: "https://litellm.example.com",
|
|
561
|
+
})).trim();
|
|
562
|
+
if (!rawBaseUrl)
|
|
563
|
+
throw new Error("Base URL is required");
|
|
564
|
+
const baseUrl = normalizeBaseUrl(rawBaseUrl);
|
|
565
|
+
const cliSso = await startCliSso(baseUrl, interaction.signal, headers);
|
|
566
|
+
if (!cliSso)
|
|
567
|
+
return loginWithPastedToken(interaction, baseUrl, headers);
|
|
568
|
+
interaction.notify({
|
|
569
|
+
type: "device_code",
|
|
570
|
+
userCode: cliSso.userCode,
|
|
571
|
+
verificationUri: `${baseUrl}/sso/key/generate?${new URLSearchParams({ source: "litellm-cli", key: cliSso.loginId })}`,
|
|
572
|
+
expiresInSeconds: cliSso.expiresInSeconds,
|
|
573
|
+
});
|
|
574
|
+
const result = await pollCliSso(baseUrl, cliSso, interaction, headers);
|
|
575
|
+
return {
|
|
576
|
+
type: "oauth",
|
|
577
|
+
access: result.access,
|
|
578
|
+
refresh: "",
|
|
579
|
+
expires: result.expiresInSeconds
|
|
580
|
+
? Date.now() + result.expiresInSeconds * 1_000
|
|
581
|
+
: tokenExpiresAt(result.access, configuredCliJwtExpiresAt()),
|
|
582
|
+
baseUrl,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
452
585
|
async function refreshLiteLLM(credentials, _signal) {
|
|
453
586
|
if (!credentials.refresh.startsWith("!")) {
|
|
454
587
|
if (credentials.expires < PERMANENT_TOKEN_EXPIRES_AT) {
|
|
@@ -948,11 +1081,7 @@ export default async function (pi) {
|
|
|
948
1081
|
context.credential) {
|
|
949
1082
|
const auth = await authForCredential(definition, context.credential);
|
|
950
1083
|
defaultRuntimeAuth = auth;
|
|
951
|
-
|
|
952
|
-
if (context.signal)
|
|
953
|
-
await registration;
|
|
954
|
-
else
|
|
955
|
-
void registration.catch(() => undefined);
|
|
1084
|
+
void registerMcpTools(auth, context.signal).catch(() => undefined);
|
|
956
1085
|
}
|
|
957
1086
|
}
|
|
958
1087
|
};
|
package/dist/provider.js
CHANGED
|
@@ -34,14 +34,7 @@ export function createLiteLLMProvider(options) {
|
|
|
34
34
|
...provider,
|
|
35
35
|
refreshModels: (context) => refreshModels({
|
|
36
36
|
...context,
|
|
37
|
-
|
|
38
|
-
async read() {
|
|
39
|
-
const entry = await context.store.read();
|
|
40
|
-
return entry && { ...entry, models: entry.models.map(enrichCachedModel) };
|
|
41
|
-
},
|
|
42
|
-
write: (entry) => context.store.write(entry),
|
|
43
|
-
delete: () => context.store.delete(),
|
|
44
|
-
},
|
|
37
|
+
stored: context.stored && { ...context.stored, models: context.stored.models.map(enrichCachedModel) },
|
|
45
38
|
}),
|
|
46
39
|
};
|
|
47
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.1.0",
|
|
4
4
|
"description": "LiteLLM proxy provider extension for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -68,13 +68,14 @@
|
|
|
68
68
|
},
|
|
69
69
|
"overrides": {
|
|
70
70
|
"basic-ftp": "6.0.1",
|
|
71
|
+
"nanoid": "3.3.18",
|
|
71
72
|
"protobufjs": "8.7.1",
|
|
72
73
|
"undici": "8.9.0"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
76
|
"@biomejs/biome": "^2.4.15",
|
|
76
|
-
"@earendil-works/pi-ai": "^0.
|
|
77
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
77
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
78
|
+
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
78
79
|
"@kitlangton/terminal-control": "0.6.0",
|
|
79
80
|
"@types/node": "^26.0.0",
|
|
80
81
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|