pi-provider-litellm 2.2.1 → 2.3.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 +25 -1
- package/dist/discover.d.ts +1 -1
- package/dist/discover.js +56 -9
- package/dist/index.js +62 -20
- package/dist/provider.d.ts +4 -4
- package/dist/types.d.ts +8 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,12 +44,14 @@ To configure an API key, run `/login`, choose `Sign in with an API key`, then ch
|
|
|
44
44
|
|
|
45
45
|
You'll be prompted for the base URL and API key. Credentials are persisted to `~/.pi/agent/auth.json`.
|
|
46
46
|
|
|
47
|
+
If a proxy URL is already known — from `providers.<name>.baseUrl`, `LITELLM_BASE_URL`, or a previous login — pi offers it as the first option instead of asking you to retype it. Pick `Enter a different URL…` to point at another proxy.
|
|
48
|
+
|
|
47
49
|
#### Enterprise SSO login
|
|
48
50
|
|
|
49
51
|
If your LiteLLM proxy requires SSO/OAuth authentication (enterprise deployments), you can authenticate through LiteLLM's CLI SSO flow:
|
|
50
52
|
|
|
51
53
|
1. Run `/login litellm` inside pi and select `Sign in with LiteLLM SSO`
|
|
52
|
-
2.
|
|
54
|
+
2. Confirm the offered proxy URL, or enter one if this is the first login
|
|
53
55
|
3. Pi displays a verification code and opens the LiteLLM SSO login URL automatically
|
|
54
56
|
4. Authenticate in the browser and confirm the displayed code
|
|
55
57
|
5. If your account belongs to multiple teams, select the team for this session in Pi
|
|
@@ -236,6 +238,7 @@ Opening `/model` refreshes configured provider catalogs in the background using
|
|
|
236
238
|
| Symptom | Likely cause |
|
|
237
239
|
|---|---|
|
|
238
240
|
| "no credentials" warning at startup | Env vars not set and no OAuth credential — run `/login litellm` |
|
|
241
|
+
| `No models available` at startup, gone after a restart | A Pi startup race, not discovery — see [`No models available` at startup](#no-models-available-at-startup) |
|
|
239
242
|
| "discovered no models" | Proxy returned an empty list — check pi's startup log and verify `/model/info`, `/v1/models`, or `/health` responds |
|
|
240
243
|
| `/model/info` returning 401/403/404 | Expected behavior with virtual keys — extension falls back to `/v1/models` |
|
|
241
244
|
| Discovery times out | Increase `LITELLM_DISCOVERY_TIMEOUT_MS` or set `LITELLM_OFFLINE=1` to fall back on cached models |
|
|
@@ -248,6 +251,27 @@ Opening `/model` refreshes configured provider catalogs in the background using
|
|
|
248
251
|
| MCP tools not showing | Verify the proxy exposes `/mcp-rest/tools/list` and open `/model` after fixing the proxy |
|
|
249
252
|
| Skills not affecting prompts | Verify the proxy exposes `/claude-code/marketplace.json` or `/v1/skills` and returns enabled skills |
|
|
250
253
|
|
|
254
|
+
### `No models available` at startup
|
|
255
|
+
|
|
256
|
+
Pi 0.84.0 and later can finish startup before the provider availability snapshot is written, so the
|
|
257
|
+
initial model pick sees nothing even though discovery succeeded. The warning is intermittent and a
|
|
258
|
+
restart usually clears it. Nothing is wrong with the catalog: `/model` in the same session still
|
|
259
|
+
lists every discovered model, and picking one there fixes that session.
|
|
260
|
+
|
|
261
|
+
Scoping models keeps selection off that path, because Pi resolves the scope with its own awaited
|
|
262
|
+
availability pass before it picks a model. In `~/.pi/agent/settings.json`:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"enabledModels": ["litellm/*"]
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Your `defaultProvider` and `defaultModel` still win as long as they match a pattern. `/model` then
|
|
271
|
+
opens on the scoped list, with a toggle inside the picker for the full catalog.
|
|
272
|
+
|
|
273
|
+
The extension cannot set this for you — Pi reads `enabledModels` before extensions activate.
|
|
274
|
+
|
|
251
275
|
## License
|
|
252
276
|
|
|
253
277
|
MIT — see [LICENSE](./LICENSE).
|
package/dist/discover.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { DiscoveredModel, DiscoveryOptions, DiscoveryResult } from "./types
|
|
|
3
3
|
export declare function normalizeBaseUrl(input: string, allowInsecureHttp?: boolean): string;
|
|
4
4
|
export declare function isMoonshotModel(modelId: string): boolean;
|
|
5
5
|
export declare function isGpt55Model(modelId: string): boolean;
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function emitsThinkTags(modelId: string): boolean;
|
|
7
7
|
export declare function buildCompat(modelId: string): DiscoveredModel["compat"];
|
|
8
8
|
export declare function enrichCachedModel(model: Model<Api>): Model<Api>;
|
|
9
9
|
export declare function discoverModels(baseUrl: string, apiKey: string, options?: DiscoveryOptions & {
|
package/dist/discover.js
CHANGED
|
@@ -38,7 +38,36 @@ export function isMoonshotModel(modelId) {
|
|
|
38
38
|
export function isGpt55Model(modelId) {
|
|
39
39
|
return GPT55_MODEL_PATTERN.test(modelId);
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
const MOONSHOT_ROUTE_PROVIDERS = new Set(["moonshot", "moonshotai"]);
|
|
42
|
+
function isMoonshotRoute(entry) {
|
|
43
|
+
const params = entry.litellm_params;
|
|
44
|
+
if (!params)
|
|
45
|
+
return false;
|
|
46
|
+
const model = params.model?.trim();
|
|
47
|
+
if (!model)
|
|
48
|
+
return false;
|
|
49
|
+
const providers = [
|
|
50
|
+
params.custom_llm_provider?.trim(),
|
|
51
|
+
model.includes("/") ? model.split("/", 1)[0] : undefined,
|
|
52
|
+
].filter((provider) => Boolean(provider));
|
|
53
|
+
return providers.length > 0 && providers.every((provider) => MOONSHOT_ROUTE_PROVIDERS.has(provider.toLowerCase()));
|
|
54
|
+
}
|
|
55
|
+
function shouldSuppressReasoningContent(modelId, entry) {
|
|
56
|
+
const routeModelId = entry.litellm_params?.model;
|
|
57
|
+
return (isMoonshotRoute(entry) &&
|
|
58
|
+
!FORCED_THINKING_MODEL_PATTERN.test(modelId) &&
|
|
59
|
+
!(routeModelId && FORCED_THINKING_MODEL_PATTERN.test(routeModelId)));
|
|
60
|
+
}
|
|
61
|
+
function aggregateSuppressionEvidence(evidence) {
|
|
62
|
+
let hasEvidence = false;
|
|
63
|
+
for (const suppress of evidence) {
|
|
64
|
+
hasEvidence = true;
|
|
65
|
+
if (!suppress)
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return hasEvidence;
|
|
69
|
+
}
|
|
70
|
+
export function emitsThinkTags(modelId) {
|
|
42
71
|
return isMoonshotModel(modelId) && !FORCED_THINKING_MODEL_PATTERN.test(modelId);
|
|
43
72
|
}
|
|
44
73
|
export function buildCompat(modelId) {
|
|
@@ -172,7 +201,7 @@ function mapReasoningEfforts(info) {
|
|
|
172
201
|
.map(([level, value, supported]) => [level, supported ? value : null]));
|
|
173
202
|
return Object.keys(map).length > 0 ? map : undefined;
|
|
174
203
|
}
|
|
175
|
-
function mapFromModelInfo(entry) {
|
|
204
|
+
function mapFromModelInfo(entry, suppressReasoningContent = shouldSuppressReasoningContent(entry.model_name ?? "", entry)) {
|
|
176
205
|
const id = entry.model_name;
|
|
177
206
|
if (!id)
|
|
178
207
|
return undefined;
|
|
@@ -195,6 +224,7 @@ function mapFromModelInfo(entry) {
|
|
|
195
224
|
contextWindow: info.max_input_tokens ?? DEFAULT_CONTEXT_WINDOW,
|
|
196
225
|
maxTokens: info.max_output_tokens ?? DEFAULT_MAX_TOKENS,
|
|
197
226
|
compat: buildCompat(id),
|
|
227
|
+
...(suppressReasoningContent ? { suppressReasoningContent: true } : {}),
|
|
198
228
|
...(responsesMode ? { api: "openai-responses" } : {}),
|
|
199
229
|
};
|
|
200
230
|
}
|
|
@@ -266,12 +296,23 @@ async function discoverFromHealth(base, apiKey, options) {
|
|
|
266
296
|
return models.filter((model) => model !== undefined);
|
|
267
297
|
}
|
|
268
298
|
function deduplicateModels(models) {
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
299
|
+
const entries = new Map();
|
|
300
|
+
for (const model of models) {
|
|
301
|
+
const existing = entries.get(model.id);
|
|
302
|
+
if (existing) {
|
|
303
|
+
existing.suppressions.push(model.suppressReasoningContent === true);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
entries.set(model.id, { model, suppressions: [model.suppressReasoningContent === true] });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return [...entries.values()].map(({ model, suppressions }) => {
|
|
310
|
+
const deduplicated = { ...model };
|
|
311
|
+
if (aggregateSuppressionEvidence(suppressions))
|
|
312
|
+
deduplicated.suppressReasoningContent = true;
|
|
313
|
+
else
|
|
314
|
+
delete deduplicated.suppressReasoningContent;
|
|
315
|
+
return deduplicated;
|
|
275
316
|
});
|
|
276
317
|
}
|
|
277
318
|
export async function discoverModels(baseUrl, apiKey, options = {}) {
|
|
@@ -281,17 +322,23 @@ export async function discoverModels(baseUrl, apiKey, options = {}) {
|
|
|
281
322
|
const infoResult = await fetchJson(`${base}/model/info`, apiKey, options);
|
|
282
323
|
if (infoResult.ok) {
|
|
283
324
|
const entries = new Map();
|
|
325
|
+
const suppressionEvidence = new Map();
|
|
284
326
|
for (const entry of infoResult.data.data ?? []) {
|
|
285
327
|
if (!entry.model_name)
|
|
286
328
|
continue;
|
|
287
329
|
const previous = entries.get(entry.model_name);
|
|
330
|
+
const suppressions = suppressionEvidence.get(entry.model_name) ?? new Set();
|
|
331
|
+
suppressions.add(shouldSuppressReasoningContent(entry.model_name, entry));
|
|
332
|
+
suppressionEvidence.set(entry.model_name, suppressions);
|
|
288
333
|
entries.set(entry.model_name, {
|
|
289
334
|
...previous,
|
|
290
335
|
...entry,
|
|
291
336
|
model_info: { ...previous?.model_info, ...entry.model_info },
|
|
292
337
|
});
|
|
293
338
|
}
|
|
294
|
-
let models = [...entries.
|
|
339
|
+
let models = [...entries.entries()]
|
|
340
|
+
.map(([id, entry]) => mapFromModelInfo(entry, aggregateSuppressionEvidence(suppressionEvidence.get(id))))
|
|
341
|
+
.filter((m) => m !== undefined);
|
|
295
342
|
// LiteLLM's /model/info does NOT expand wildcard model_name entries (e.g.
|
|
296
343
|
// "lemonade/*" backed by model: openai/* + check_provider_endpoint: true)
|
|
297
344
|
// — it returns the literal wildcard only. The discovered ids live in
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { setTimeout as delay } from "node:timers/promises";
|
|
5
5
|
import { getAgentDir, readStoredCredential } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { setupLiteLLMCostTracking } from "./cost.js";
|
|
7
|
-
import { discoverModels, isGpt55Model, isMoonshotModel, normalizeBaseUrl
|
|
7
|
+
import { discoverModels, emitsThinkTags, isGpt55Model, isMoonshotModel, normalizeBaseUrl } from "./discover.js";
|
|
8
8
|
import { getGcloudToken, getGcloudTokenCacheKey, getGcloudTokenCommand, isGcloudTokenAuthEnabled, } from "./gcloud-token.js";
|
|
9
9
|
import { getSessionIdFromFile } from "./litellm.js";
|
|
10
10
|
import { createMcpToolDefinitions } from "./mcp-tools.js";
|
|
@@ -406,7 +406,51 @@ function getProviderDefinitions(settings) {
|
|
|
406
406
|
}
|
|
407
407
|
return definitions;
|
|
408
408
|
}
|
|
409
|
-
|
|
409
|
+
const DIFFERENT_BASE_URL = "enter-a-different-url";
|
|
410
|
+
/**
|
|
411
|
+
* The proxy URL is the one thing a user has to retype on every re-login, and an expired SSO token is
|
|
412
|
+
* the common reason to re-login. Offer whatever we already know: settings, env, or the credential the
|
|
413
|
+
* dead session was issued against.
|
|
414
|
+
*/
|
|
415
|
+
function knownBaseUrl(definition) {
|
|
416
|
+
const stored = readStoredCredential(definition.name, join(getAgentDir(), "auth.json"));
|
|
417
|
+
const candidates = [
|
|
418
|
+
[cleanConfig(definition.baseUrl), "configured"],
|
|
419
|
+
[definition.useDefaultEnv ? cleanConfig(process.env[ENV_BASE_URL]) : undefined, `$${ENV_BASE_URL}`],
|
|
420
|
+
[
|
|
421
|
+
stored?.type === "oauth"
|
|
422
|
+
? cleanConfig(typeof stored.baseUrl === "string" ? stored.baseUrl : undefined)
|
|
423
|
+
: cleanConfig(stored?.env?.[ENV_BASE_URL]),
|
|
424
|
+
"previous login",
|
|
425
|
+
],
|
|
426
|
+
];
|
|
427
|
+
for (const [candidate, source] of candidates) {
|
|
428
|
+
if (!candidate)
|
|
429
|
+
continue;
|
|
430
|
+
try {
|
|
431
|
+
return { url: normalizeBaseUrl(candidate, definition.allowInsecureHttp), source };
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// A URL we could not use anyway is not worth offering; try the next candidate.
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
async function promptBaseUrl(interaction, definition) {
|
|
440
|
+
const known = knownBaseUrl(definition);
|
|
441
|
+
if (known) {
|
|
442
|
+
const choice = await interaction.prompt({
|
|
443
|
+
type: "select",
|
|
444
|
+
message: "LiteLLM proxy URL:",
|
|
445
|
+
// Pi's login selector renders labels only, so the source has to ride along in the label.
|
|
446
|
+
options: [
|
|
447
|
+
{ id: known.url, label: `${known.url} (${known.source})` },
|
|
448
|
+
{ id: DIFFERENT_BASE_URL, label: "Enter a different URL…" },
|
|
449
|
+
],
|
|
450
|
+
});
|
|
451
|
+
if (choice === known.url)
|
|
452
|
+
return known.url;
|
|
453
|
+
}
|
|
410
454
|
const rawBaseUrl = (await interaction.prompt({
|
|
411
455
|
type: "text",
|
|
412
456
|
message: "Enter LiteLLM proxy URL (no trailing /v1):",
|
|
@@ -414,10 +458,14 @@ async function loginApiKey(interaction, allowInsecureHttp = false) {
|
|
|
414
458
|
})).trim();
|
|
415
459
|
if (!rawBaseUrl)
|
|
416
460
|
throw new Error("Base URL is required");
|
|
461
|
+
return normalizeBaseUrl(rawBaseUrl, definition.allowInsecureHttp);
|
|
462
|
+
}
|
|
463
|
+
async function loginApiKey(interaction, definition) {
|
|
464
|
+
const baseUrl = await promptBaseUrl(interaction, definition);
|
|
417
465
|
const key = (await interaction.prompt({ type: "secret", message: "Enter API key:" })).trim();
|
|
418
466
|
if (!key)
|
|
419
467
|
throw new Error("Both base URL and API key are required");
|
|
420
|
-
return { type: "api_key", key, env: { [ENV_BASE_URL]:
|
|
468
|
+
return { type: "api_key", key, env: { [ENV_BASE_URL]: baseUrl } };
|
|
421
469
|
}
|
|
422
470
|
async function startCliSso(baseUrl, signal, headers) {
|
|
423
471
|
const response = await fetch(`${baseUrl}/sso/cli/start`, {
|
|
@@ -560,15 +608,9 @@ async function loginWithPastedToken(interaction, baseUrl, headers) {
|
|
|
560
608
|
}
|
|
561
609
|
return { type: "oauth", access, refresh: "", expires, baseUrl };
|
|
562
610
|
}
|
|
563
|
-
async function loginOAuth(interaction,
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
message: "Enter LiteLLM proxy URL (no trailing /v1):",
|
|
567
|
-
placeholder: "https://litellm.example.com",
|
|
568
|
-
})).trim();
|
|
569
|
-
if (!rawBaseUrl)
|
|
570
|
-
throw new Error("Base URL is required");
|
|
571
|
-
const baseUrl = normalizeBaseUrl(rawBaseUrl, allowInsecureHttp);
|
|
611
|
+
async function loginOAuth(interaction, definition) {
|
|
612
|
+
const headers = resolveHeaders(definition);
|
|
613
|
+
const baseUrl = await promptBaseUrl(interaction, definition);
|
|
572
614
|
const cliSso = await startCliSso(baseUrl, interaction.signal, headers);
|
|
573
615
|
if (!cliSso)
|
|
574
616
|
return loginWithPastedToken(interaction, baseUrl, headers);
|
|
@@ -666,9 +708,7 @@ function createProviderAuth(definition) {
|
|
|
666
708
|
return {
|
|
667
709
|
apiKey: {
|
|
668
710
|
name: `${definition.displayName} API key`,
|
|
669
|
-
login: definition.name === PROVIDER_NAME
|
|
670
|
-
? (interaction) => loginApiKey(interaction, definition.allowInsecureHttp)
|
|
671
|
-
: undefined,
|
|
711
|
+
login: definition.name === PROVIDER_NAME ? (interaction) => loginApiKey(interaction, definition) : undefined,
|
|
672
712
|
check: async ({ ctx, credential }) => {
|
|
673
713
|
const baseUrl = credential?.env?.[ENV_BASE_URL] ??
|
|
674
714
|
definition.baseUrl ??
|
|
@@ -704,7 +744,7 @@ function createProviderAuth(definition) {
|
|
|
704
744
|
? {
|
|
705
745
|
name: "LiteLLM SSO",
|
|
706
746
|
loginLabel: "Sign in with LiteLLM SSO",
|
|
707
|
-
login: (interaction) => loginOAuth(interaction,
|
|
747
|
+
login: (interaction) => loginOAuth(interaction, definition),
|
|
708
748
|
refresh: async (credential, signal) => ({
|
|
709
749
|
...(await refreshLiteLLM(credential, signal)),
|
|
710
750
|
type: "oauth",
|
|
@@ -782,7 +822,9 @@ const REASONING_SUPPRESSION_DEFAULTS = {
|
|
|
782
822
|
thinking: { type: "disabled" },
|
|
783
823
|
};
|
|
784
824
|
const GEMINI_MODEL_PATTERN = /(?:^|[-_/.:])gemini(?=$|[-_/.:])/i;
|
|
785
|
-
function prepareLiteLLMRequestPayload(payload,
|
|
825
|
+
function prepareLiteLLMRequestPayload(payload, model, sessionId) {
|
|
826
|
+
const modelId = model?.id;
|
|
827
|
+
const api = model?.api;
|
|
786
828
|
let next;
|
|
787
829
|
const update = (key, value) => {
|
|
788
830
|
if (payload[key] !== undefined)
|
|
@@ -790,7 +832,7 @@ function prepareLiteLLMRequestPayload(payload, modelId, api, sessionId) {
|
|
|
790
832
|
next ??= { ...payload };
|
|
791
833
|
next[key] = value;
|
|
792
834
|
};
|
|
793
|
-
if (api !== "openai-responses" &&
|
|
835
|
+
if (api !== "openai-responses" && model?.suppressReasoningContent === true) {
|
|
794
836
|
for (const [key, value] of Object.entries(REASONING_SUPPRESSION_DEFAULTS)) {
|
|
795
837
|
if (key !== "thinking")
|
|
796
838
|
update(key, value);
|
|
@@ -867,7 +909,7 @@ function prepareLiteLLMRequestPayload(payload, modelId, api, sessionId) {
|
|
|
867
909
|
return next;
|
|
868
910
|
}
|
|
869
911
|
function normalizeThinkTags(message, litellmProviderNames) {
|
|
870
|
-
if (!litellmProviderNames.has(message.provider) || !
|
|
912
|
+
if (!litellmProviderNames.has(message.provider) || !emitsThinkTags(message.model))
|
|
871
913
|
return;
|
|
872
914
|
let changed = false;
|
|
873
915
|
const content = [];
|
|
@@ -1146,7 +1188,7 @@ export default async function (pi) {
|
|
|
1146
1188
|
return;
|
|
1147
1189
|
if (typeof event.payload !== "object" || event.payload === null)
|
|
1148
1190
|
return;
|
|
1149
|
-
return prepareLiteLLMRequestPayload(event.payload, ctx.model
|
|
1191
|
+
return prepareLiteLLMRequestPayload(event.payload, ctx.model, sessionId);
|
|
1150
1192
|
});
|
|
1151
1193
|
// Skills enrichment is best-effort: an expired credential or an unreachable proxy must not
|
|
1152
1194
|
// report an extension error on every turn. The failing model request states the real problem.
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Credential, type
|
|
2
|
-
import type { DiscoveredModel, DiscoveryResult, LiteLLMApi } from "./types.js";
|
|
1
|
+
import { type Credential, type Provider, type ProviderAuth } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { DiscoveredModel, DiscoveryResult, LiteLLMApi, LiteLLMModel } from "./types.js";
|
|
3
3
|
export type LiteLLMProviderOptions = {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
|
@@ -7,10 +7,10 @@ export type LiteLLMProviderOptions = {
|
|
|
7
7
|
auth: ProviderAuth;
|
|
8
8
|
/** Catalog discovered during activation. Pi's startup refresh never allows network access, so
|
|
9
9
|
* without a seed the provider stays empty until the user opens /model. */
|
|
10
|
-
models?: readonly
|
|
10
|
+
models?: readonly LiteLLMModel[];
|
|
11
11
|
discover(credential: Credential, signal?: AbortSignal): Promise<DiscoveryResult & {
|
|
12
12
|
baseUrl?: string;
|
|
13
13
|
}>;
|
|
14
14
|
};
|
|
15
|
-
export declare function toNativeModels(provider: string, baseUrl: string, models: readonly DiscoveredModel[]):
|
|
15
|
+
export declare function toNativeModels(provider: string, baseUrl: string, models: readonly DiscoveredModel[]): LiteLLMModel[];
|
|
16
16
|
export declare function createLiteLLMProvider(options: LiteLLMProviderOptions): Provider<LiteLLMApi>;
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ export type LiteLLMRuntimeAuth = {
|
|
|
9
9
|
};
|
|
10
10
|
export type DiscoveredModel = Omit<Model<"openai-completions">, "provider" | "api" | "baseUrl"> & {
|
|
11
11
|
api?: LiteLLMApi;
|
|
12
|
+
suppressReasoningContent?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type LiteLLMModel = Model<LiteLLMApi> & {
|
|
15
|
+
suppressReasoningContent?: boolean;
|
|
12
16
|
};
|
|
13
17
|
export interface DiscoveryResult {
|
|
14
18
|
models: DiscoveredModel[];
|
|
@@ -22,6 +26,10 @@ export interface DiscoveryOptions {
|
|
|
22
26
|
}
|
|
23
27
|
export interface ModelInfoEntry {
|
|
24
28
|
model_name?: string;
|
|
29
|
+
litellm_params?: {
|
|
30
|
+
model?: string;
|
|
31
|
+
custom_llm_provider?: string;
|
|
32
|
+
};
|
|
25
33
|
model_info?: {
|
|
26
34
|
mode?: string | null;
|
|
27
35
|
input_cost_per_token?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-litellm",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "LiteLLM proxy provider extension for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsgo --noEmit",
|
|
47
47
|
"lint": "biome check --error-on-warnings .",
|
|
48
48
|
"format": "biome check --write .",
|
|
49
|
-
"test": "vitest --run",
|
|
49
|
+
"test": "vitest --run --dir tests",
|
|
50
50
|
"supply-chain:guard": "tsx scripts/supply-chain-guard.ts",
|
|
51
51
|
"check": "npm run lint && npm run typecheck && npm test",
|
|
52
52
|
"prepublishOnly": "npm run check && npm run clean && npm run build && npm run supply-chain:guard"
|