pi-provider-litellm 1.2.9 → 2.0.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 +40 -23
- package/dist/cache.d.ts +1 -5
- package/dist/cache.js +3 -42
- package/dist/cost.d.ts +2 -12
- package/dist/cost.js +0 -0
- package/dist/discover.d.ts +6 -8
- package/dist/discover.js +222 -84
- package/dist/index.js +457 -643
- package/dist/litellm.js +2 -4
- package/dist/mcp-tools.d.ts +4 -4
- package/dist/mcp-tools.js +24 -30
- package/dist/provider.d.ts +13 -0
- package/dist/provider.js +30 -0
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +11 -11
- package/dist/types.d.ts +13 -10
- package/package.json +7 -6
package/dist/litellm.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
1
2
|
export function getSessionIdFromFile(sessionFile) {
|
|
2
3
|
if (!sessionFile)
|
|
3
4
|
return undefined;
|
|
4
|
-
const filename = sessionFile
|
|
5
|
-
.split("/")
|
|
6
|
-
.pop()
|
|
7
|
-
?.replace(/\.jsonl$/i, "");
|
|
5
|
+
const filename = basename(sessionFile).replace(/\.jsonl$/i, "");
|
|
8
6
|
if (!filename)
|
|
9
7
|
return undefined;
|
|
10
8
|
const uuidMatch = filename.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
|
package/dist/mcp-tools.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { LiteLLMMcpTool } from "./types.js";
|
|
3
|
-
export declare function discoverMcpTools(baseUrl: string, apiKey: string, headers?: Record<string, string
|
|
1
|
+
import { type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { LiteLLMMcpTool, LiteLLMRuntimeAuth } from "./types.js";
|
|
3
|
+
export declare function discoverMcpTools(baseUrl: string, apiKey: string, headers?: Record<string, string>, onProgress?: (message: string) => void, parentSignal?: AbortSignal): Promise<LiteLLMMcpTool[]>;
|
|
4
4
|
export declare function executeMcpTool(baseUrl: string, apiKey: string, serverId: string, toolName: string, args: Record<string, unknown>, headers?: Record<string, string>): Promise<string>;
|
|
5
|
-
export declare function createMcpToolDefinitions(
|
|
5
|
+
export declare function createMcpToolDefinitions(getAuth: (ctx?: ExtensionContext) => Promise<LiteLLMRuntimeAuth>, onProgress?: (message: string) => void, signal?: AbortSignal): Promise<ToolDefinition[]>;
|
package/dist/mcp-tools.js
CHANGED
|
@@ -6,14 +6,6 @@ const CALL_TIMEOUT_MS = 30_000;
|
|
|
6
6
|
const MCP_RETRY_DELAY_MS = 350;
|
|
7
7
|
const MCP_RETRY_ATTEMPTS = 1;
|
|
8
8
|
const RETRYABLE_HTTP_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
9
|
-
function withTimeout(timeoutMs) {
|
|
10
|
-
const controller = new AbortController();
|
|
11
|
-
const timer = setTimeout(() => controller.abort(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
12
|
-
return {
|
|
13
|
-
signal: controller.signal,
|
|
14
|
-
cancel: () => clearTimeout(timer),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
9
|
function asRecord(value) {
|
|
18
10
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
19
11
|
}
|
|
@@ -57,29 +49,35 @@ function normalizeMcpTool(value) {
|
|
|
57
49
|
input_schema: inputSchema,
|
|
58
50
|
};
|
|
59
51
|
}
|
|
60
|
-
export async function discoverMcpTools(baseUrl, apiKey, headers) {
|
|
61
|
-
|
|
52
|
+
export async function discoverMcpTools(baseUrl, apiKey, headers, onProgress, parentSignal) {
|
|
53
|
+
onProgress?.("Discovering MCP tools from server...");
|
|
62
54
|
try {
|
|
55
|
+
onProgress?.("Querying MCP tools/list endpoint...");
|
|
63
56
|
const response = await fetch(`${normalizeBaseUrl(baseUrl)}/mcp-rest/tools/list`, {
|
|
64
57
|
headers: {
|
|
65
58
|
...headers,
|
|
66
59
|
Authorization: `Bearer ${apiKey}`,
|
|
67
60
|
"Content-Type": "application/json",
|
|
68
61
|
},
|
|
69
|
-
signal
|
|
62
|
+
signal: parentSignal
|
|
63
|
+
? AbortSignal.any([parentSignal, AbortSignal.timeout(LIST_TIMEOUT_MS)])
|
|
64
|
+
: AbortSignal.timeout(LIST_TIMEOUT_MS),
|
|
70
65
|
});
|
|
71
|
-
if (!response.ok)
|
|
72
|
-
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
onProgress?.(`MCP tools discovery failed with status ${response.status}`);
|
|
68
|
+
throw new Error(`HTTP ${response.status}`);
|
|
69
|
+
}
|
|
73
70
|
const body = (await response.json());
|
|
74
71
|
const bodyRecord = asRecord(body);
|
|
75
72
|
const rawTools = Array.isArray(body) ? body : Array.isArray(bodyRecord?.tools) ? bodyRecord.tools : [];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return
|
|
73
|
+
onProgress?.(`Found ${rawTools.length} raw MCP tools, normalizing...`);
|
|
74
|
+
const tools = rawTools.map(normalizeMcpTool).filter((tool) => tool !== undefined);
|
|
75
|
+
onProgress?.(`Successfully discovered ${tools.length} MCP tools`);
|
|
76
|
+
return tools;
|
|
80
77
|
}
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
catch (error) {
|
|
79
|
+
onProgress?.("MCP tools discovery encountered an error");
|
|
80
|
+
throw error;
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
export async function executeMcpTool(baseUrl, apiKey, serverId, toolName, args, headers) {
|
|
@@ -94,7 +92,6 @@ export async function executeMcpTool(baseUrl, apiKey, serverId, toolName, args,
|
|
|
94
92
|
return `Error calling ${toolName} on ${serverId}: retry attempts exhausted`;
|
|
95
93
|
}
|
|
96
94
|
async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, headers) {
|
|
97
|
-
const { signal, cancel } = withTimeout(CALL_TIMEOUT_MS);
|
|
98
95
|
try {
|
|
99
96
|
let response;
|
|
100
97
|
try {
|
|
@@ -106,7 +103,7 @@ async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, hea
|
|
|
106
103
|
"Content-Type": "application/json",
|
|
107
104
|
},
|
|
108
105
|
body: JSON.stringify({ server_id: serverId, name: toolName, arguments: args }),
|
|
109
|
-
signal,
|
|
106
|
+
signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
|
|
110
107
|
});
|
|
111
108
|
}
|
|
112
109
|
catch (error) {
|
|
@@ -136,9 +133,6 @@ async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, hea
|
|
|
136
133
|
catch (error) {
|
|
137
134
|
return { text: formatMcpToolError(toolName, serverId, error), retryable: false };
|
|
138
135
|
}
|
|
139
|
-
finally {
|
|
140
|
-
cancel();
|
|
141
|
-
}
|
|
142
136
|
}
|
|
143
137
|
function sanitizeName(name) {
|
|
144
138
|
const sanitized = name
|
|
@@ -156,9 +150,9 @@ function buildParameters(inputSchema) {
|
|
|
156
150
|
}
|
|
157
151
|
return Type.Unsafe(inputSchema);
|
|
158
152
|
}
|
|
159
|
-
export async function createMcpToolDefinitions(
|
|
160
|
-
const
|
|
161
|
-
const tools = await discoverMcpTools(baseUrl,
|
|
153
|
+
export async function createMcpToolDefinitions(getAuth, onProgress, signal) {
|
|
154
|
+
const discoveryAuth = await getAuth();
|
|
155
|
+
const tools = await discoverMcpTools(discoveryAuth.baseUrl, discoveryAuth.apiKey, discoveryAuth.headers, onProgress, signal);
|
|
162
156
|
return tools.map((mcpTool) => {
|
|
163
157
|
const safeServer = sanitizeName(mcpTool.server_name);
|
|
164
158
|
const safeTool = sanitizeName(mcpTool.name);
|
|
@@ -170,13 +164,13 @@ export async function createMcpToolDefinitions(baseUrl, getApiKey, headers) {
|
|
|
170
164
|
promptSnippet: `${mcpTool.description} via ${mcpTool.server_name} MCP server`,
|
|
171
165
|
executionMode: "parallel",
|
|
172
166
|
parameters,
|
|
173
|
-
async execute(_toolCallId, params) {
|
|
174
|
-
const
|
|
167
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
168
|
+
const auth = await getAuth(ctx);
|
|
175
169
|
const rawParams = params;
|
|
176
170
|
const args = Object.keys(rawParams).length === 1 && rawParams.args && typeof rawParams.args === "object"
|
|
177
171
|
? rawParams.args
|
|
178
172
|
: rawParams;
|
|
179
|
-
const text = await executeMcpTool(baseUrl, apiKey, mcpTool.server_id ?? mcpTool.server_name, mcpTool.name, args, headers);
|
|
173
|
+
const text = await executeMcpTool(auth.baseUrl, auth.apiKey, mcpTool.server_id ?? mcpTool.server_name, mcpTool.name, args, auth.headers);
|
|
180
174
|
return {
|
|
181
175
|
content: [{ type: "text", text }],
|
|
182
176
|
details: { server: mcpTool.server_name, serverId: mcpTool.server_id, tool: mcpTool.name },
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Credential, type Model, type Provider, type ProviderAuth } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { DiscoveredModel, DiscoveryResult, LiteLLMApi } from "./types.js";
|
|
3
|
+
export type LiteLLMProviderOptions = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
auth: ProviderAuth;
|
|
8
|
+
discover(credential: Credential, signal?: AbortSignal): Promise<DiscoveryResult & {
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
export declare function toNativeModels(provider: string, baseUrl: string, models: readonly DiscoveredModel[]): Model<LiteLLMApi>[];
|
|
13
|
+
export declare function createLiteLLMProvider(options: LiteLLMProviderOptions): Provider<LiteLLMApi>;
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createProvider } from "@earendil-works/pi-ai";
|
|
2
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
3
|
+
import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
|
|
4
|
+
export function toNativeModels(provider, baseUrl, models) {
|
|
5
|
+
return models.map((model) => ({
|
|
6
|
+
...model,
|
|
7
|
+
provider,
|
|
8
|
+
api: model.api ?? "openai-completions",
|
|
9
|
+
baseUrl,
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export function createLiteLLMProvider(options) {
|
|
13
|
+
return createProvider({
|
|
14
|
+
id: options.id,
|
|
15
|
+
name: options.name,
|
|
16
|
+
baseUrl: options.baseUrl,
|
|
17
|
+
auth: options.auth,
|
|
18
|
+
models: [],
|
|
19
|
+
async fetchModels(context) {
|
|
20
|
+
if (!context.credential)
|
|
21
|
+
throw new Error("LiteLLM model discovery requires a credential");
|
|
22
|
+
const result = await options.discover(context.credential, context.signal);
|
|
23
|
+
return toNativeModels(options.id, result.baseUrl ?? options.baseUrl, result.models);
|
|
24
|
+
},
|
|
25
|
+
api: {
|
|
26
|
+
"openai-completions": openAICompletionsApi(),
|
|
27
|
+
"openai-responses": openAIResponsesApi(),
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { LiteLLMSkill } from "./types.js";
|
|
1
|
+
import { type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { LiteLLMRuntimeAuth, LiteLLMSkill } from "./types.js";
|
|
3
3
|
export declare function listSkills(baseUrl: string, apiKey: string, headers?: Record<string, string>): Promise<LiteLLMSkill[]>;
|
|
4
4
|
export declare function createSkill(baseUrl: string, apiKey: string, input: {
|
|
5
5
|
name: string;
|
|
@@ -10,5 +10,5 @@ export declare function createSkill(baseUrl: string, apiKey: string, input: {
|
|
|
10
10
|
}, headers?: Record<string, string>): Promise<unknown>;
|
|
11
11
|
export declare function deleteSkill(baseUrl: string, apiKey: string, skillId: string, headers?: Record<string, string>): Promise<void>;
|
|
12
12
|
export declare function createSkillsPromptSection(skills: LiteLLMSkill[]): string | undefined;
|
|
13
|
-
export declare function createSkillToolDefinitions(
|
|
13
|
+
export declare function createSkillToolDefinitions(getAuth: (ctx?: ExtensionContext) => Promise<LiteLLMRuntimeAuth>): ToolDefinition[];
|
|
14
14
|
export declare function resetSkillsCache(): void;
|
package/dist/skills.js
CHANGED
|
@@ -172,7 +172,7 @@ function parseJsonObject(value, fieldName) {
|
|
|
172
172
|
}
|
|
173
173
|
return parsed;
|
|
174
174
|
}
|
|
175
|
-
export function createSkillToolDefinitions(
|
|
175
|
+
export function createSkillToolDefinitions(getAuth) {
|
|
176
176
|
return [
|
|
177
177
|
defineTool({
|
|
178
178
|
name: "litellm_skill_list",
|
|
@@ -180,9 +180,9 @@ export function createSkillToolDefinitions(baseUrl, getApiKey, headers) {
|
|
|
180
180
|
description: "List skills registered on the LiteLLM proxy Skills Gateway.",
|
|
181
181
|
promptSnippet: "List LiteLLM Skills Gateway skills",
|
|
182
182
|
parameters: Type.Object({}),
|
|
183
|
-
async execute() {
|
|
184
|
-
const
|
|
185
|
-
const skills = await listSkills(baseUrl, apiKey, headers);
|
|
183
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
184
|
+
const auth = await getAuth(ctx);
|
|
185
|
+
const skills = await listSkills(auth.baseUrl, auth.apiKey, auth.headers);
|
|
186
186
|
return { content: [{ type: "text", text: formatSkills(skills) }], details: { count: skills.length } };
|
|
187
187
|
},
|
|
188
188
|
}),
|
|
@@ -191,19 +191,19 @@ export function createSkillToolDefinitions(baseUrl, getApiKey, headers) {
|
|
|
191
191
|
label: "Create LiteLLM Skill",
|
|
192
192
|
description: "Create a skill on the LiteLLM proxy Skills Gateway.",
|
|
193
193
|
parameters: CreateSkillParams,
|
|
194
|
-
async execute(_toolCallId, params) {
|
|
195
|
-
const
|
|
194
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
195
|
+
const auth = await getAuth(ctx);
|
|
196
196
|
const source = params.sourceJson ? parseJsonObject(params.sourceJson, "sourceJson") : undefined;
|
|
197
197
|
const inputSchema = params.inputSchemaJson
|
|
198
198
|
? parseJsonObject(params.inputSchemaJson, "inputSchemaJson")
|
|
199
199
|
: undefined;
|
|
200
|
-
const result = await createSkill(baseUrl, apiKey, {
|
|
200
|
+
const result = await createSkill(auth.baseUrl, auth.apiKey, {
|
|
201
201
|
name: params.name,
|
|
202
202
|
description: params.description,
|
|
203
203
|
source,
|
|
204
204
|
code: params.code,
|
|
205
205
|
inputSchema,
|
|
206
|
-
}, headers);
|
|
206
|
+
}, auth.headers);
|
|
207
207
|
return { content: [{ type: "text", text: "LiteLLM skill created." }], details: { result } };
|
|
208
208
|
},
|
|
209
209
|
}),
|
|
@@ -212,9 +212,9 @@ export function createSkillToolDefinitions(baseUrl, getApiKey, headers) {
|
|
|
212
212
|
label: "Delete LiteLLM Skill",
|
|
213
213
|
description: "Delete a skill from the LiteLLM proxy Skills Gateway.",
|
|
214
214
|
parameters: DeleteSkillParams,
|
|
215
|
-
async execute(_toolCallId, params) {
|
|
216
|
-
const
|
|
217
|
-
await deleteSkill(baseUrl, apiKey, params.skillId, headers);
|
|
215
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
216
|
+
const auth = await getAuth(ctx);
|
|
217
|
+
await deleteSkill(auth.baseUrl, auth.apiKey, params.skillId, auth.headers);
|
|
218
218
|
return { content: [{ type: "text", text: `LiteLLM skill deleted: ${params.skillId}` }], details: {} };
|
|
219
219
|
},
|
|
220
220
|
}),
|
package/dist/types.d.ts
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
2
2
|
export type DiscoverySource = "model_info" | "models_list" | "health";
|
|
3
|
-
export
|
|
3
|
+
export type LiteLLMApi = "openai-completions" | "openai-responses";
|
|
4
|
+
export type LiteLLMRuntimeAuth = {
|
|
4
5
|
baseUrl: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
6
|
+
apiKey: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
export type DiscoveredModel = Omit<Model<"openai-completions">, "provider" | "api" | "baseUrl"> & {
|
|
10
|
+
api?: LiteLLMApi;
|
|
11
|
+
};
|
|
11
12
|
export interface DiscoveryResult {
|
|
12
|
-
models:
|
|
13
|
+
models: DiscoveredModel[];
|
|
13
14
|
source: DiscoverySource;
|
|
14
15
|
}
|
|
15
16
|
export interface DiscoveryOptions {
|
|
16
17
|
timeoutMs?: number;
|
|
17
18
|
signal?: AbortSignal;
|
|
18
19
|
headers?: Record<string, string>;
|
|
20
|
+
modelsDev?: boolean;
|
|
21
|
+
modelsDevCachePath?: string;
|
|
19
22
|
}
|
|
20
23
|
export interface ModelInfoEntry {
|
|
21
24
|
model_name?: string;
|
|
22
25
|
model_info?: {
|
|
23
|
-
mode?: string;
|
|
26
|
+
mode?: string | null;
|
|
24
27
|
input_cost_per_token?: number;
|
|
25
28
|
output_cost_per_token?: number;
|
|
26
29
|
cache_read_input_token_cost?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-provider-litellm",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "LiteLLM proxy provider extension for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,18 +55,19 @@
|
|
|
55
55
|
"node": ">=22.19.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@earendil-works/pi-ai": "
|
|
59
|
-
"@earendil-works/pi-coding-agent": "
|
|
58
|
+
"@earendil-works/pi-ai": ">=0.81.0",
|
|
59
|
+
"@earendil-works/pi-coding-agent": ">=0.81.0"
|
|
60
60
|
},
|
|
61
61
|
"overrides": {
|
|
62
62
|
"basic-ftp": "6.0.1",
|
|
63
63
|
"fast-xml-builder": "1.2.0",
|
|
64
|
-
"protobufjs": "8.
|
|
64
|
+
"protobufjs": "8.7.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@earendil-works/pi-ai": "^0.80.2",
|
|
68
|
-
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
69
67
|
"@biomejs/biome": "^2.4.15",
|
|
68
|
+
"@earendil-works/pi-ai": "^0.81.1",
|
|
69
|
+
"@earendil-works/pi-coding-agent": "^0.81.1",
|
|
70
|
+
"@kitlangton/terminal-control": "0.6.0",
|
|
70
71
|
"@types/node": "^26.0.0",
|
|
71
72
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
72
73
|
"tsx": "^4.21.0",
|