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/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);
@@ -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(baseUrl: string, getApiKey: () => Promise<string>, headers?: Record<string, string>, onProgress?: (message: string) => void): Promise<ToolDefinition[]>;
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
- return [];
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
- return [];
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(baseUrl, getApiKey, headers, onProgress) {
168
- const discoveryApiKey = await getApiKey();
169
- const tools = await discoverMcpTools(baseUrl, discoveryApiKey, headers, onProgress);
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 apiKey = await getApiKey();
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>;
@@ -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(baseUrl: string, getApiKey: () => Promise<string>, headers?: Record<string, string>): ToolDefinition[];
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(baseUrl, getApiKey, headers) {
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 apiKey = await getApiKey();
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 apiKey = await getApiKey();
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 apiKey = await getApiKey();
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 { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
1
+ import type { Model } from "@earendil-works/pi-ai";
2
2
  export type DiscoverySource = "model_info" | "models_list" | "health";
3
- export interface CacheFile {
3
+ export type LiteLLMApi = "openai-completions" | "openai-responses";
4
+ export type LiteLLMRuntimeAuth = {
4
5
  baseUrl: string;
5
- apiKeyFingerprint: string;
6
- headersFingerprint?: string;
7
- fetchedAt: number;
8
- source: DiscoverySource;
9
- models: ProviderModelConfig[];
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: ProviderModelConfig[];
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": "1.3.0",
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.2.1"
64
+ "protobufjs": "8.7.1"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@biomejs/biome": "^2.4.15",
68
- "@earendil-works/pi-ai": "^0.80.2",
69
- "@earendil-works/pi-coding-agent": "^0.80.2",
70
- "@kitlangton/terminal-control": "0.3.1",
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",