pi-provider-litellm 0.1.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.1] - 2026-04-28
11
+
12
+ ### Added
13
+
14
+ - `pi-package` keyword so the package surfaces in the [pi.dev/packages](https://pi.dev/packages) gallery.
15
+
16
+ ### Changed
17
+
18
+ - Renamed package from `@balcsida/pi-provider-litellm` to unscoped `pi-provider-litellm` to match the `pi-provider-*` convention used by other extensions in the pi-mono ecosystem. The previous scoped name was published as 0.1.0 only.
19
+ - README install instructions now use `pi install npm:pi-provider-litellm` (and `pi -e` for ephemeral trials), with the source-clone path kept as a fallback.
20
+
21
+ ## [0.1.0] - 2026-04-27
22
+
23
+ ### Added
24
+
25
+ - Initial release.
26
+ - Discovers models from a self-hosted LiteLLM proxy via `/model/info` with fallback to `/v1/models` on 401/403/404.
27
+ - Sha256-fingerprinted disk cache with atomic writes.
28
+ - `/login litellm` interactive login (prompts for base URL + API key).
29
+ - `/litellm-refresh` slash command to force re-fetch.
30
+ - `LITELLM_BASE_URL`, `LITELLM_API_KEY`, `LITELLM_OFFLINE`, `LITELLM_DISCOVERY_TIMEOUT_MS` environment variables.
31
+ - `compat.cacheControlFormat: "anthropic"` for both `anthropic/*`-prefixed model IDs and bare Claude aliases (`claude-*`, `opus-*`, `sonnet-*`, `haiku-*`, case-insensitive). Common LiteLLM deployments alias Claude models without the `anthropic/` prefix; without this flag, prompt caching silently no-ops on Claude through the proxy.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dávid Balatoni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # pi-provider-litellm
2
+
3
+ [![npm version](https://img.shields.io/npm/v/pi-provider-litellm.svg)](https://www.npmjs.com/package/pi-provider-litellm)
4
+
5
+ LiteLLM proxy provider extension for [pi-mono](https://github.com/badlogic/pi-mono).
6
+
7
+ Discovers models from a self-hosted LiteLLM proxy and registers them under the `litellm` provider. Supports `/login litellm` and `/litellm-refresh`. Tries `/model/info` first (admin endpoint with rich metadata), falls back to `/v1/models` (OpenAI-compatible) on 401/403/404.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pi install npm:pi-provider-litellm
13
+ ```
14
+
15
+ Pi fetches the package from npm and registers it. Add `-l` to install into project settings (`.pi/settings.json`) instead of global.
16
+
17
+ To try it without installing (one-off, current run only):
18
+
19
+ ```bash
20
+ pi -e npm:pi-provider-litellm
21
+ ```
22
+
23
+ <details>
24
+ <summary>Alternative: install from source</summary>
25
+
26
+ ```bash
27
+ git clone https://github.com/balcsida/pi-provider-litellm.git ~/.pi/agent/extensions/pi-provider-litellm
28
+ cd ~/.pi/agent/extensions/pi-provider-litellm
29
+ npm install
30
+ npm run build
31
+ ```
32
+
33
+ </details>
34
+
35
+ ## Configure
36
+
37
+ ### Option A — environment variables
38
+
39
+ ```bash
40
+ export LITELLM_BASE_URL="https://litellm.your-domain.com"
41
+ export LITELLM_API_KEY="sk-..."
42
+ ```
43
+
44
+ ### Option B — interactive login
45
+
46
+ Inside pi:
47
+
48
+ ```
49
+ /login litellm
50
+ ```
51
+
52
+ You'll be prompted for the base URL and API key. Credentials are persisted to `~/.pi/agent/auth.json`.
53
+
54
+ ## Use
55
+
56
+ ```
57
+ /model litellm/anthropic/claude-3-5-sonnet
58
+ ```
59
+
60
+ (Whatever model IDs your proxy exposes — see the discovered list at startup.)
61
+
62
+ ## Optional environment variables
63
+
64
+ | Variable | Default | Effect |
65
+ |---|---|---|
66
+ | `LITELLM_OFFLINE` | unset | If `1`, skip discovery on this start; use cache only |
67
+ | `LITELLM_DISCOVERY_TIMEOUT_MS` | `5000` | Discovery fetch timeout in ms; `0` to skip discovery |
68
+
69
+ ## Slash commands
70
+
71
+ - `/litellm-refresh` — force re-fetch the model list, ignoring cache
72
+
73
+ ## Cache
74
+
75
+ The model list is cached at `~/.pi/agent/litellm-models.json` with a sha256 fingerprint of the base URL + API key. Changing either invalidates the cache automatically.
76
+
77
+ ## Troubleshooting
78
+
79
+ | Symptom | Likely cause |
80
+ |---|---|
81
+ | "no credentials" warning at startup | Env vars not set and no OAuth credential — run `/login litellm` |
82
+ | "discovered no models" | Proxy returned an empty list — check pi's startup log for the actual response |
83
+ | `/model/info` returning 401/403/404 | Expected behavior with virtual keys — extension falls back to `/v1/models` |
84
+ | Discovery times out | Increase `LITELLM_DISCOVERY_TIMEOUT_MS` or set `LITELLM_OFFLINE=1` to fall back on cached models |
85
+
86
+ ## License
87
+
88
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,6 @@
1
+ import type { CacheFile } from "./types.js";
2
+ export declare function fingerprint(apiKey: string): string;
3
+ export declare function isCacheValid(cache: CacheFile | null, baseUrl: string, apiKey: string): boolean;
4
+ export declare function readCache(path: string): Promise<CacheFile | null>;
5
+ export declare function writeCache(path: string, cache: CacheFile): Promise<void>;
6
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG9F;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAevE;AAcD,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9E"}
package/dist/cache.js ADDED
@@ -0,0 +1,47 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ export function fingerprint(apiKey) {
5
+ return createHash("sha256").update(apiKey).digest("hex");
6
+ }
7
+ export function isCacheValid(cache, baseUrl, apiKey) {
8
+ if (!cache)
9
+ return false;
10
+ return cache.baseUrl === baseUrl && cache.apiKeyFingerprint === fingerprint(apiKey);
11
+ }
12
+ export async function readCache(path) {
13
+ let raw;
14
+ try {
15
+ raw = await readFile(path, "utf8");
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ let parsed;
21
+ try {
22
+ parsed = JSON.parse(raw);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ if (!isCacheFileShape(parsed))
28
+ return null;
29
+ return parsed;
30
+ }
31
+ function isCacheFileShape(value) {
32
+ if (typeof value !== "object" || value === null)
33
+ return false;
34
+ const v = value;
35
+ return (typeof v.baseUrl === "string" &&
36
+ typeof v.apiKeyFingerprint === "string" &&
37
+ typeof v.fetchedAt === "number" &&
38
+ (v.source === "model_info" || v.source === "models_list") &&
39
+ Array.isArray(v.models));
40
+ }
41
+ export async function writeCache(path, cache) {
42
+ await mkdir(dirname(path), { recursive: true });
43
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
44
+ await writeFile(tmp, JSON.stringify(cache, null, 2), "utf8");
45
+ await rename(tmp, path);
46
+ }
47
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,UAAU,WAAW,CAAC,MAAc,EAAU;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC1D;AAED,MAAM,UAAU,YAAY,CAAC,KAAuB,EAAE,OAAe,EAAE,MAAc,EAAW;IAC9F,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,CAAC,OAAO,KAAK,OAAO,IAAI,KAAK,CAAC,iBAAiB,KAAK,WAAW,CAAC,MAAM,CAAC,CAAC;AAAA,CACrF;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAA6B;IACvE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,MAAM,CAAC;AAAA,CACf;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAsB;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;QAC7B,OAAO,CAAC,CAAC,iBAAiB,KAAK,QAAQ;QACvC,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ;QAC/B,CAAC,CAAC,CAAC,MAAM,KAAK,YAAY,IAAI,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CACxB,CAAC;AAAA,CACH;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,KAAgB,EAAiB;IAC9E,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC;IACvD,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC7D,MAAM,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAAA,CACzB"}
@@ -0,0 +1,6 @@
1
+ import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
2
+ import type { DiscoveryOptions, DiscoveryResult } from "./types.js";
3
+ export declare function normalizeBaseUrl(input: string): string;
4
+ export declare function buildCompat(modelId: string): ProviderModelConfig["compat"];
5
+ export declare function discoverModels(baseUrl: string, apiKey: string, options?: DiscoveryOptions): Promise<DiscoveryResult>;
6
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAKhB,MAAM,YAAY,CAAC;AAMpB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AASD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAK1E;AA4ED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAoB1B"}
@@ -0,0 +1,113 @@
1
+ const DEFAULT_TIMEOUT_MS = 5000;
2
+ const DEFAULT_CONTEXT_WINDOW = 128_000;
3
+ const DEFAULT_MAX_TOKENS = 16_384;
4
+ export function normalizeBaseUrl(input) {
5
+ return input.replace(/\/+$/, "").replace(/\/v1\/?$/i, "");
6
+ }
7
+ // Matches both the conventional `anthropic/...` prefix and bare aliases that
8
+ // LiteLLM deployments commonly assign to Anthropic-backed routes (e.g.
9
+ // `opus-4.7`, `sonnet-4.6`, `haiku-4.5`, `claude-3-5-sonnet`). Without the
10
+ // `cacheControlFormat: "anthropic"` flag, pi never relays cache_control markers
11
+ // through the proxy, so prompt caching silently no-ops on Claude models.
12
+ const ANTHROPIC_MODEL_PATTERN = /^(anthropic\/|claude|opus|sonnet|haiku)/i;
13
+ export function buildCompat(modelId) {
14
+ if (ANTHROPIC_MODEL_PATTERN.test(modelId)) {
15
+ return { supportsStore: false, cacheControlFormat: "anthropic" };
16
+ }
17
+ return { supportsStore: false };
18
+ }
19
+ function withTimeout(timeoutMs, signal) {
20
+ const controller = new AbortController();
21
+ const onAbort = () => controller.abort(signal?.reason);
22
+ if (signal) {
23
+ if (signal.aborted)
24
+ controller.abort(signal.reason);
25
+ else
26
+ signal.addEventListener("abort", onAbort, { once: true });
27
+ }
28
+ const timer = setTimeout(() => controller.abort(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
29
+ return {
30
+ signal: controller.signal,
31
+ cancel: () => {
32
+ clearTimeout(timer);
33
+ signal?.removeEventListener("abort", onAbort);
34
+ },
35
+ };
36
+ }
37
+ async function fetchJson(url, apiKey, options) {
38
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
39
+ const { signal, cancel } = withTimeout(timeoutMs, options.signal);
40
+ try {
41
+ const response = await fetch(url, {
42
+ headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
43
+ signal,
44
+ });
45
+ if (!response.ok)
46
+ return { ok: false, status: response.status };
47
+ const data = (await response.json());
48
+ return { ok: true, data };
49
+ }
50
+ finally {
51
+ cancel();
52
+ }
53
+ }
54
+ function mapFromModelInfo(entry) {
55
+ const id = entry.model_name;
56
+ if (!id)
57
+ return undefined;
58
+ const info = entry.model_info ?? {};
59
+ if (info.mode && info.mode !== "chat")
60
+ return undefined;
61
+ return {
62
+ id,
63
+ name: id,
64
+ reasoning: info.supports_reasoning ?? false,
65
+ input: info.supports_vision ? ["text", "image"] : ["text"],
66
+ cost: {
67
+ input: (info.input_cost_per_token ?? 0) * 1_000_000,
68
+ output: (info.output_cost_per_token ?? 0) * 1_000_000,
69
+ cacheRead: (info.cache_read_input_token_cost ?? 0) * 1_000_000,
70
+ cacheWrite: (info.cache_creation_input_token_cost ?? 0) * 1_000_000,
71
+ },
72
+ contextWindow: info.max_input_tokens ?? DEFAULT_CONTEXT_WINDOW,
73
+ maxTokens: info.max_output_tokens ?? DEFAULT_MAX_TOKENS,
74
+ compat: buildCompat(id),
75
+ };
76
+ }
77
+ function mapFromModelsList(entry) {
78
+ const id = entry.id;
79
+ if (!id)
80
+ return undefined;
81
+ return {
82
+ id,
83
+ name: `${id} (no metadata)`,
84
+ reasoning: false,
85
+ input: ["text"],
86
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
87
+ contextWindow: DEFAULT_CONTEXT_WINDOW,
88
+ maxTokens: DEFAULT_MAX_TOKENS,
89
+ compat: buildCompat(id),
90
+ };
91
+ }
92
+ export async function discoverModels(baseUrl, apiKey, options = {}) {
93
+ const base = normalizeBaseUrl(baseUrl);
94
+ const infoResult = await fetchJson(`${base}/model/info`, apiKey, options);
95
+ if (infoResult.ok) {
96
+ const models = (infoResult.data.data ?? [])
97
+ .map(mapFromModelInfo)
98
+ .filter((m) => m !== undefined);
99
+ return { source: "model_info", models };
100
+ }
101
+ if (![401, 403, 404].includes(infoResult.status)) {
102
+ throw new Error(`/model/info returned ${infoResult.status}`);
103
+ }
104
+ const listResult = await fetchJson(`${base}/v1/models`, apiKey, options);
105
+ if (!listResult.ok) {
106
+ throw new Error(`/v1/models returned ${listResult.status}`);
107
+ }
108
+ const models = (listResult.data.data ?? [])
109
+ .map(mapFromModelsList)
110
+ .filter((m) => m !== undefined);
111
+ return { source: "models_list", models };
112
+ }
113
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ export default function (pi: ExtensionAPI): Promise<void>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAuB,MAAM,+BAA+B,CAAC;AA0HvF,yBAA+B,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAoG9D"}
package/dist/index.js ADDED
@@ -0,0 +1,203 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { getAgentDir } from "@mariozechner/pi-coding-agent";
4
+ import { fingerprint, readCache, writeCache } from "./cache.js";
5
+ import { discoverModels, normalizeBaseUrl } from "./discover.js";
6
+ const PROVIDER_NAME = "litellm";
7
+ const ENV_BASE_URL = "LITELLM_BASE_URL";
8
+ const ENV_API_KEY = "LITELLM_API_KEY";
9
+ const ENV_TIMEOUT = "LITELLM_DISCOVERY_TIMEOUT_MS";
10
+ const ENV_OFFLINE = "LITELLM_OFFLINE";
11
+ const DEFAULT_TIMEOUT_MS = 5000;
12
+ const LOGIN_TIMEOUT_MS = 10_000;
13
+ const CACHE_FILENAME = "litellm-models.json";
14
+ function getAuthPath() {
15
+ return join(getAgentDir(), "auth.json");
16
+ }
17
+ function getCachePath() {
18
+ return join(getAgentDir(), CACHE_FILENAME);
19
+ }
20
+ async function readAuthEntry() {
21
+ try {
22
+ const raw = await readFile(getAuthPath(), "utf8");
23
+ const parsed = JSON.parse(raw);
24
+ return parsed?.[PROVIDER_NAME];
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ async function resolveCredentials() {
31
+ const entry = await readAuthEntry();
32
+ const envBase = process.env[ENV_BASE_URL]?.trim();
33
+ const envKey = process.env[ENV_API_KEY]?.trim();
34
+ const authBase = entry?.type === "oauth" ? entry.baseUrl?.trim() : undefined;
35
+ const authKey = entry?.type === "oauth" ? entry.access?.trim() : entry?.type === "api_key" ? entry.key?.trim() : undefined;
36
+ const rawBase = envBase || authBase;
37
+ return {
38
+ baseUrl: rawBase ? normalizeBaseUrl(rawBase) : undefined,
39
+ apiKey: envKey || authKey || undefined,
40
+ };
41
+ }
42
+ function getDiscoveryTimeoutMs() {
43
+ const raw = process.env[ENV_TIMEOUT];
44
+ if (raw === undefined)
45
+ return DEFAULT_TIMEOUT_MS;
46
+ const parsed = Number.parseInt(raw, 10);
47
+ if (Number.isNaN(parsed) || parsed < 0)
48
+ return DEFAULT_TIMEOUT_MS;
49
+ return parsed;
50
+ }
51
+ function isOffline() {
52
+ return process.env[ENV_OFFLINE] === "1";
53
+ }
54
+ function isListModelsMode() {
55
+ return process.argv.includes("--list-models");
56
+ }
57
+ async function discoverWithFallback(baseUrl, apiKey, options) {
58
+ try {
59
+ return { result: await discoverModels(baseUrl, apiKey, options) };
60
+ }
61
+ catch (error) {
62
+ const message = error instanceof Error ? error.message : String(error);
63
+ return {
64
+ result: { models: [], source: "models_list" },
65
+ warning: message,
66
+ };
67
+ }
68
+ }
69
+ async function loginLiteLLM(callbacks) {
70
+ const rawBaseUrl = (await callbacks.onPrompt({
71
+ message: "Enter LiteLLM proxy URL (no trailing /v1):",
72
+ placeholder: "https://litellm.example.com",
73
+ })).trim();
74
+ const apiKey = (await callbacks.onPrompt({ message: "Enter API key:" })).trim();
75
+ if (!rawBaseUrl || !apiKey)
76
+ throw new Error("Both base URL and API key are required");
77
+ const baseUrl = normalizeBaseUrl(rawBaseUrl);
78
+ const { models, source } = await discoverModels(baseUrl, apiKey, {
79
+ timeoutMs: LOGIN_TIMEOUT_MS,
80
+ signal: callbacks.signal,
81
+ });
82
+ await writeCache(getCachePath(), {
83
+ baseUrl,
84
+ apiKeyFingerprint: fingerprint(apiKey),
85
+ fetchedAt: Date.now(),
86
+ source,
87
+ models,
88
+ });
89
+ callbacks.onProgress?.(`LiteLLM: ${models.length} models discovered (source: ${source})`);
90
+ return {
91
+ access: apiKey,
92
+ refresh: "",
93
+ expires: Number.MAX_SAFE_INTEGER,
94
+ baseUrl,
95
+ };
96
+ }
97
+ async function refreshLiteLLM(credentials) {
98
+ return credentials;
99
+ }
100
+ function modifyLiteLLMModels(models, cred) {
101
+ const baseUrl = cred.baseUrl;
102
+ if (!baseUrl)
103
+ return models;
104
+ return models.map((m) => (m.provider === PROVIDER_NAME ? { ...m, baseUrl: `${baseUrl}/v1` } : m));
105
+ }
106
+ export default async function (pi) {
107
+ const creds = await resolveCredentials();
108
+ const cache = await readCache(getCachePath());
109
+ const fp = creds.apiKey ? fingerprint(creds.apiKey) : undefined;
110
+ const cacheValid = cache !== null &&
111
+ creds.baseUrl !== undefined &&
112
+ fp !== undefined &&
113
+ cache.baseUrl === creds.baseUrl &&
114
+ cache.apiKeyFingerprint === fp;
115
+ let models = cacheValid && cache ? cache.models : [];
116
+ const haveCreds = creds.baseUrl !== undefined && creds.apiKey !== undefined && fp !== undefined;
117
+ const shouldFetch = haveCreds && !isOffline() && (!cacheValid || isListModelsMode());
118
+ if (shouldFetch && creds.baseUrl && creds.apiKey && fp) {
119
+ const timeoutMs = getDiscoveryTimeoutMs();
120
+ if (timeoutMs > 0) {
121
+ const { result, warning } = await discoverWithFallback(creds.baseUrl, creds.apiKey, {
122
+ timeoutMs,
123
+ });
124
+ if (warning) {
125
+ if (cacheValid && cache) {
126
+ process.stderr.write(`LiteLLM: discovery failed (${warning}); using cached models.\n`);
127
+ models = cache.models;
128
+ }
129
+ else {
130
+ process.stderr.write(`LiteLLM: discovery failed (${warning}); registering provider with no models.\n`);
131
+ models = [];
132
+ }
133
+ }
134
+ else {
135
+ models = result.models;
136
+ const next = {
137
+ baseUrl: creds.baseUrl,
138
+ apiKeyFingerprint: fp,
139
+ fetchedAt: Date.now(),
140
+ source: result.source,
141
+ models: result.models,
142
+ };
143
+ await writeCache(getCachePath(), next);
144
+ if (isListModelsMode()) {
145
+ process.stderr.write(`LiteLLM: ${result.models.length} models discovered (source: ${result.source}).\n`);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ const oauth = {
151
+ name: "LiteLLM",
152
+ login: loginLiteLLM,
153
+ refreshToken: refreshLiteLLM,
154
+ getApiKey: (cred) => cred.access,
155
+ modifyModels: modifyLiteLLMModels,
156
+ };
157
+ pi.registerProvider(PROVIDER_NAME, {
158
+ baseUrl: creds.baseUrl ? `${creds.baseUrl}/v1` : "https://litellm.example.com/v1",
159
+ apiKey: ENV_API_KEY,
160
+ api: "openai-completions",
161
+ models,
162
+ oauth,
163
+ });
164
+ pi.registerCommand("litellm-refresh", {
165
+ description: "Re-discover models from the LiteLLM proxy.",
166
+ handler: async (_args, ctx) => {
167
+ if (isOffline()) {
168
+ ctx.ui.notify(`LiteLLM refresh disabled (${ENV_OFFLINE}=1)`, "warning");
169
+ return;
170
+ }
171
+ const fresh = await resolveCredentials();
172
+ const freshFp = fresh.apiKey ? fingerprint(fresh.apiKey) : undefined;
173
+ if (!fresh.baseUrl || !fresh.apiKey || !freshFp) {
174
+ ctx.ui.notify("LiteLLM refresh failed: no credentials. Run /login litellm or set env vars.", "error");
175
+ return;
176
+ }
177
+ const timeoutMs = getDiscoveryTimeoutMs() || DEFAULT_TIMEOUT_MS;
178
+ try {
179
+ const result = await discoverModels(fresh.baseUrl, fresh.apiKey, { timeoutMs });
180
+ await writeCache(getCachePath(), {
181
+ baseUrl: fresh.baseUrl,
182
+ apiKeyFingerprint: freshFp,
183
+ fetchedAt: Date.now(),
184
+ source: result.source,
185
+ models: result.models,
186
+ });
187
+ pi.registerProvider(PROVIDER_NAME, {
188
+ baseUrl: `${fresh.baseUrl}/v1`,
189
+ apiKey: ENV_API_KEY,
190
+ api: "openai-completions",
191
+ models: result.models,
192
+ oauth,
193
+ });
194
+ ctx.ui.notify(`LiteLLM: ${result.models.length} models refreshed (source: ${result.source})`, "info");
195
+ }
196
+ catch (error) {
197
+ const message = error instanceof Error ? error.message : String(error);
198
+ ctx.ui.notify(`LiteLLM refresh failed: ${message}`, "error");
199
+ }
200
+ },
201
+ });
202
+ }
203
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
2
+ export type DiscoverySource = "model_info" | "models_list";
3
+ export interface CacheFile {
4
+ baseUrl: string;
5
+ apiKeyFingerprint: string;
6
+ fetchedAt: number;
7
+ source: DiscoverySource;
8
+ models: ProviderModelConfig[];
9
+ }
10
+ export interface DiscoveryResult {
11
+ models: ProviderModelConfig[];
12
+ source: DiscoverySource;
13
+ }
14
+ export interface DiscoveryOptions {
15
+ timeoutMs?: number;
16
+ signal?: AbortSignal;
17
+ }
18
+ export interface ModelInfoEntry {
19
+ model_name?: string;
20
+ model_info?: {
21
+ mode?: string;
22
+ input_cost_per_token?: number;
23
+ output_cost_per_token?: number;
24
+ cache_read_input_token_cost?: number;
25
+ cache_creation_input_token_cost?: number;
26
+ max_input_tokens?: number;
27
+ max_output_tokens?: number;
28
+ supports_reasoning?: boolean;
29
+ supports_vision?: boolean;
30
+ };
31
+ }
32
+ export interface ModelInfoResponse {
33
+ data?: ModelInfoEntry[];
34
+ }
35
+ export interface ModelsListEntry {
36
+ id?: string;
37
+ }
38
+ export interface ModelsListResponse {
39
+ data?: ModelsListEntry[];
40
+ }
41
+ export type AuthFileEntry = {
42
+ type: "oauth";
43
+ access: string;
44
+ refresh: string;
45
+ expires: number;
46
+ baseUrl?: string;
47
+ } | {
48
+ type: "api_key";
49
+ key: string;
50
+ };
51
+ export interface ResolvedCredentials {
52
+ baseUrl?: string;
53
+ apiKey?: string;
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,aAAa,CAAC;AAE3D,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,mBAAmB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,+BAA+B,CAAC,EAAE,MAAM,CAAC;QACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACrF;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "pi-provider-litellm",
3
+ "version": "0.1.1",
4
+ "description": "LiteLLM proxy provider extension for pi-mono.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Dávid Balatoni <balcsida@gmail.com>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/balcsida/pi-provider-litellm.git"
11
+ },
12
+ "homepage": "https://github.com/balcsida/pi-provider-litellm",
13
+ "bugs": "https://github.com/balcsida/pi-provider-litellm/issues",
14
+ "keywords": [
15
+ "pi-mono",
16
+ "pi-package",
17
+ "litellm",
18
+ "llm",
19
+ "extension",
20
+ "provider",
21
+ "proxy"
22
+ ],
23
+ "main": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md",
34
+ "LICENSE",
35
+ "CHANGELOG.md"
36
+ ],
37
+ "pi": {
38
+ "extensions": [
39
+ "./dist/index.js"
40
+ ]
41
+ },
42
+ "scripts": {
43
+ "clean": "shx rm -rf dist",
44
+ "build": "tsgo -p tsconfig.build.json",
45
+ "dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
46
+ "typecheck": "tsgo --noEmit",
47
+ "lint": "biome check --error-on-warnings .",
48
+ "format": "biome check --write .",
49
+ "test": "vitest --run",
50
+ "check": "npm run lint && npm run typecheck && npm test",
51
+ "prepublishOnly": "npm run check && npm run clean && npm run build"
52
+ },
53
+ "engines": {
54
+ "node": ">=20.0.0"
55
+ },
56
+ "peerDependencies": {
57
+ "@mariozechner/pi-ai": "^0.70.0",
58
+ "@mariozechner/pi-coding-agent": "^0.70.0"
59
+ },
60
+ "devDependencies": {
61
+ "@biomejs/biome": "^1.9.4",
62
+ "@mariozechner/pi-ai": "^0.70.0",
63
+ "@mariozechner/pi-coding-agent": "^0.70.0",
64
+ "@types/node": "^24.3.0",
65
+ "@typescript/native-preview": "7.0.0-dev.20260120.1",
66
+ "shx": "^0.4.0",
67
+ "tsx": "^4.21.0",
68
+ "typescript": "^5.7.3",
69
+ "vitest": "^3.2.4"
70
+ }
71
+ }