pi-provider-litellm 1.3.0 → 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 +18 -20
- package/dist/cache.d.ts +1 -5
- package/dist/cache.js +3 -42
- package/dist/cost.d.ts +2 -6
- package/dist/cost.js +0 -0
- package/dist/discover.d.ts +3 -7
- package/dist/discover.js +183 -77
- package/dist/index.js +443 -616
- package/dist/litellm.js +2 -4
- package/dist/mcp-tools.d.ts +4 -4
- package/dist/mcp-tools.js +14 -28
- 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 +12 -9
- package/package.json +7 -7
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>, onProgress?: (message: string) => void): Promise<LiteLLMMcpTool[]>;
|
|
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,9 +49,8 @@ function normalizeMcpTool(value) {
|
|
|
57
49
|
input_schema: inputSchema,
|
|
58
50
|
};
|
|
59
51
|
}
|
|
60
|
-
export async function discoverMcpTools(baseUrl, apiKey, headers, onProgress) {
|
|
52
|
+
export async function discoverMcpTools(baseUrl, apiKey, headers, onProgress, parentSignal) {
|
|
61
53
|
onProgress?.("Discovering MCP tools from server...");
|
|
62
|
-
const { signal, cancel } = withTimeout(LIST_TIMEOUT_MS);
|
|
63
54
|
try {
|
|
64
55
|
onProgress?.("Querying MCP tools/list endpoint...");
|
|
65
56
|
const response = await fetch(`${normalizeBaseUrl(baseUrl)}/mcp-rest/tools/list`, {
|
|
@@ -68,11 +59,13 @@ export async function discoverMcpTools(baseUrl, apiKey, headers, onProgress) {
|
|
|
68
59
|
Authorization: `Bearer ${apiKey}`,
|
|
69
60
|
"Content-Type": "application/json",
|
|
70
61
|
},
|
|
71
|
-
signal
|
|
62
|
+
signal: parentSignal
|
|
63
|
+
? AbortSignal.any([parentSignal, AbortSignal.timeout(LIST_TIMEOUT_MS)])
|
|
64
|
+
: AbortSignal.timeout(LIST_TIMEOUT_MS),
|
|
72
65
|
});
|
|
73
66
|
if (!response.ok) {
|
|
74
67
|
onProgress?.(`MCP tools discovery failed with status ${response.status}`);
|
|
75
|
-
|
|
68
|
+
throw new Error(`HTTP ${response.status}`);
|
|
76
69
|
}
|
|
77
70
|
const body = (await response.json());
|
|
78
71
|
const bodyRecord = asRecord(body);
|
|
@@ -82,12 +75,9 @@ export async function discoverMcpTools(baseUrl, apiKey, headers, onProgress) {
|
|
|
82
75
|
onProgress?.(`Successfully discovered ${tools.length} MCP tools`);
|
|
83
76
|
return tools;
|
|
84
77
|
}
|
|
85
|
-
catch {
|
|
78
|
+
catch (error) {
|
|
86
79
|
onProgress?.("MCP tools discovery encountered an error");
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
cancel();
|
|
80
|
+
throw error;
|
|
91
81
|
}
|
|
92
82
|
}
|
|
93
83
|
export async function executeMcpTool(baseUrl, apiKey, serverId, toolName, args, headers) {
|
|
@@ -102,7 +92,6 @@ export async function executeMcpTool(baseUrl, apiKey, serverId, toolName, args,
|
|
|
102
92
|
return `Error calling ${toolName} on ${serverId}: retry attempts exhausted`;
|
|
103
93
|
}
|
|
104
94
|
async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, headers) {
|
|
105
|
-
const { signal, cancel } = withTimeout(CALL_TIMEOUT_MS);
|
|
106
95
|
try {
|
|
107
96
|
let response;
|
|
108
97
|
try {
|
|
@@ -114,7 +103,7 @@ async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, hea
|
|
|
114
103
|
"Content-Type": "application/json",
|
|
115
104
|
},
|
|
116
105
|
body: JSON.stringify({ server_id: serverId, name: toolName, arguments: args }),
|
|
117
|
-
signal,
|
|
106
|
+
signal: AbortSignal.timeout(CALL_TIMEOUT_MS),
|
|
118
107
|
});
|
|
119
108
|
}
|
|
120
109
|
catch (error) {
|
|
@@ -144,9 +133,6 @@ async function executeMcpToolOnce(baseUrl, apiKey, serverId, toolName, args, hea
|
|
|
144
133
|
catch (error) {
|
|
145
134
|
return { text: formatMcpToolError(toolName, serverId, error), retryable: false };
|
|
146
135
|
}
|
|
147
|
-
finally {
|
|
148
|
-
cancel();
|
|
149
|
-
}
|
|
150
136
|
}
|
|
151
137
|
function sanitizeName(name) {
|
|
152
138
|
const sanitized = name
|
|
@@ -164,9 +150,9 @@ function buildParameters(inputSchema) {
|
|
|
164
150
|
}
|
|
165
151
|
return Type.Unsafe(inputSchema);
|
|
166
152
|
}
|
|
167
|
-
export async function createMcpToolDefinitions(
|
|
168
|
-
const
|
|
169
|
-
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);
|
|
170
156
|
return tools.map((mcpTool) => {
|
|
171
157
|
const safeServer = sanitizeName(mcpTool.server_name);
|
|
172
158
|
const safeTool = sanitizeName(mcpTool.name);
|
|
@@ -178,13 +164,13 @@ export async function createMcpToolDefinitions(baseUrl, getApiKey, headers, onPr
|
|
|
178
164
|
promptSnippet: `${mcpTool.description} via ${mcpTool.server_name} MCP server`,
|
|
179
165
|
executionMode: "parallel",
|
|
180
166
|
parameters,
|
|
181
|
-
async execute(_toolCallId, params) {
|
|
182
|
-
const
|
|
167
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
168
|
+
const auth = await getAuth(ctx);
|
|
183
169
|
const rawParams = params;
|
|
184
170
|
const args = Object.keys(rawParams).length === 1 && rawParams.args && typeof rawParams.args === "object"
|
|
185
171
|
? rawParams.args
|
|
186
172
|
: rawParams;
|
|
187
|
-
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);
|
|
188
174
|
return {
|
|
189
175
|
content: [{ type: "text", text }],
|
|
190
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,21 +1,24 @@
|
|
|
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;
|
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,19 +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
67
|
"@biomejs/biome": "^2.4.15",
|
|
68
|
-
"@earendil-works/pi-ai": "^0.
|
|
69
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
70
|
-
"@kitlangton/terminal-control": "0.
|
|
68
|
+
"@earendil-works/pi-ai": "^0.81.1",
|
|
69
|
+
"@earendil-works/pi-coding-agent": "^0.81.1",
|
|
70
|
+
"@kitlangton/terminal-control": "0.6.0",
|
|
71
71
|
"@types/node": "^26.0.0",
|
|
72
72
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
73
73
|
"tsx": "^4.21.0",
|