pi-free 2.0.8 → 2.0.10
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 +29 -1
- package/README.md +588 -572
- package/banner.jpg +0 -0
- package/banner.png +0 -0
- package/banner.svg +12 -10
- package/config.ts +349 -337
- package/constants.ts +106 -103
- package/index.ts +242 -239
- package/lib/built-in-toggle.ts +2 -2
- package/lib/model-detection.ts +1 -1
- package/lib/model-enhancer.ts +20 -20
- package/lib/provider-compat.ts +1 -1
- package/lib/registry.ts +1 -1
- package/lib/util.ts +524 -460
- package/package.json +70 -68
- package/provider-helper.ts +1 -1
- package/providers/cline/cline-auth.ts +1 -1
- package/providers/cline/cline.ts +2 -2
- package/providers/codestral/codestral.ts +1 -1
- package/providers/crofai/crofai.ts +190 -99
- package/providers/deepinfra/deepinfra.ts +206 -109
- package/providers/dynamic-built-in/index.ts +1 -1
- package/providers/kilo/kilo-auth.ts +1 -1
- package/providers/kilo/kilo.ts +2 -2
- package/providers/llm7/llm7.ts +1 -1
- package/providers/nvidia/nvidia.ts +1 -1
- package/providers/ollama/ollama.ts +610 -295
- package/providers/ollama/thinking-levels.ts +96 -0
- package/providers/qwen/qwen-auth.ts +1 -1
- package/providers/qwen/qwen-models.ts +101 -101
- package/providers/qwen/qwen.ts +2 -2
- package/providers/sambanova/sambanova.ts +1 -1
- package/providers/together/together.ts +197 -0
- package/providers/zenmux/zenmux.ts +194 -179
|
@@ -1,179 +1,194 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ZenMux Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* Provides access to ZenMux AI gateway - unified API for 200+ models from
|
|
5
|
-
* OpenAI, Anthropic, Google, and other providers.
|
|
6
|
-
*
|
|
7
|
-
* Setup:
|
|
8
|
-
* 1. Get API key from https://zenmux.ai
|
|
9
|
-
* 2. Set ZENMUX_API_KEY env var or add to ~/.pi/free.json
|
|
10
|
-
*
|
|
11
|
-
* Responds to global free-only filter.
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* pi install git:github.com/apmantza/pi-free
|
|
15
|
-
* # Set ZENMUX_API_KEY env var
|
|
16
|
-
* # Models appear in /model selector
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import type {
|
|
20
|
-
ExtensionAPI,
|
|
21
|
-
ProviderModelConfig,
|
|
22
|
-
} from "@
|
|
23
|
-
import { getZenmuxApiKey, getZenmuxShowPaid } from "../../config.ts";
|
|
24
|
-
import {
|
|
25
|
-
BASE_URL_ZENMUX,
|
|
26
|
-
DEFAULT_FETCH_TIMEOUT_MS,
|
|
27
|
-
PROVIDER_ZENMUX,
|
|
28
|
-
} from "../../constants.ts";
|
|
29
|
-
import { createLogger } from "../../lib/logger.ts";
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
} from "../../
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
_logger.
|
|
121
|
-
|
|
122
|
-
);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ZenMux Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Provides access to ZenMux AI gateway - unified API for 200+ models from
|
|
5
|
+
* OpenAI, Anthropic, Google, and other providers.
|
|
6
|
+
*
|
|
7
|
+
* Setup:
|
|
8
|
+
* 1. Get API key from https://zenmux.ai
|
|
9
|
+
* 2. Set ZENMUX_API_KEY env var or add to ~/.pi/free.json
|
|
10
|
+
*
|
|
11
|
+
* Responds to global free-only filter.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* pi install git:github.com/apmantza/pi-free
|
|
15
|
+
* # Set ZENMUX_API_KEY env var
|
|
16
|
+
* # Models appear in /model selector
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type {
|
|
20
|
+
ExtensionAPI,
|
|
21
|
+
ProviderModelConfig,
|
|
22
|
+
} from "@earendil-works/pi-coding-agent";
|
|
23
|
+
import { getZenmuxApiKey, getZenmuxShowPaid } from "../../config.ts";
|
|
24
|
+
import {
|
|
25
|
+
BASE_URL_ZENMUX,
|
|
26
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
27
|
+
PROVIDER_ZENMUX,
|
|
28
|
+
} from "../../constants.ts";
|
|
29
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
30
|
+
import { getProxyModelCompat } from "../../lib/provider-compat.ts";
|
|
31
|
+
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
32
|
+
import { fetchWithRetry } from "../../lib/util.ts";
|
|
33
|
+
import { createReRegister, setupProvider } from "../../provider-helper.ts";
|
|
34
|
+
|
|
35
|
+
const _logger = createLogger("zenmux");
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// Fetch ZenMux models
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
interface ZenMuxModel {
|
|
42
|
+
id: string;
|
|
43
|
+
display_name?: string;
|
|
44
|
+
context_length?: number;
|
|
45
|
+
input_modalities?: string[];
|
|
46
|
+
output_modalities?: string[];
|
|
47
|
+
capabilities?: {
|
|
48
|
+
reasoning?: boolean;
|
|
49
|
+
};
|
|
50
|
+
pricings?: {
|
|
51
|
+
prompt?: Array<{ value: number }>;
|
|
52
|
+
completion?: Array<{ value: number }>;
|
|
53
|
+
input_cache_read?: Array<{ value: number }>;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Extract the first pricing value from a ZenMux pricings array.
|
|
59
|
+
* ZenMux uses a structured format: pricings.prompt[0].value (per-million-tokens).
|
|
60
|
+
* We divide by 1_000_000 to convert to per-token price (Pi's convention).
|
|
61
|
+
* Returns 0 if pricing is missing or empty.
|
|
62
|
+
*/
|
|
63
|
+
function extractZenmuxPrice(
|
|
64
|
+
pricings: ZenMuxModel["pricings"],
|
|
65
|
+
key: "prompt" | "completion" | "input_cache_read",
|
|
66
|
+
): number {
|
|
67
|
+
const entries = pricings?.[key];
|
|
68
|
+
if (!entries || entries.length === 0) return 0;
|
|
69
|
+
return (entries[0].value ?? 0) / 1_000_000;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function fetchZenmuxModels(
|
|
73
|
+
apiKey: string,
|
|
74
|
+
): Promise<ProviderModelConfig[]> {
|
|
75
|
+
_logger.info("[zenmux] Fetching models from ZenMux API...");
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetchWithRetry(
|
|
79
|
+
`${BASE_URL_ZENMUX}/models`,
|
|
80
|
+
{
|
|
81
|
+
headers: {
|
|
82
|
+
Authorization: `Bearer ${apiKey}`,
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
3,
|
|
87
|
+
1000,
|
|
88
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(`ZenMux API error: ${response.status}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const data = (await response.json()) as { data?: ZenMuxModel[] };
|
|
96
|
+
const models = data.data ?? [];
|
|
97
|
+
|
|
98
|
+
_logger.info(`[zenmux] Fetched ${models.length} models`);
|
|
99
|
+
|
|
100
|
+
return models.map(
|
|
101
|
+
(m): ProviderModelConfig => ({
|
|
102
|
+
id: m.id,
|
|
103
|
+
name: m.display_name || m.id,
|
|
104
|
+
reasoning: m.capabilities?.reasoning ?? false,
|
|
105
|
+
input: m.input_modalities?.includes("image")
|
|
106
|
+
? ["text", "image"]
|
|
107
|
+
: ["text"],
|
|
108
|
+
cost: {
|
|
109
|
+
input: extractZenmuxPrice(m.pricings, "prompt"),
|
|
110
|
+
output: extractZenmuxPrice(m.pricings, "completion"),
|
|
111
|
+
cacheRead: extractZenmuxPrice(m.pricings, "input_cache_read"),
|
|
112
|
+
cacheWrite: 0,
|
|
113
|
+
},
|
|
114
|
+
contextWindow: m.context_length || 128000,
|
|
115
|
+
maxTokens: m.context_length ? Math.floor(m.context_length / 2) : 4096,
|
|
116
|
+
compat: getProxyModelCompat(m),
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
_logger.error("[zenmux] Failed to fetch models:", {
|
|
121
|
+
error: error instanceof Error ? error.message : String(error),
|
|
122
|
+
});
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// =============================================================================
|
|
128
|
+
// Extension Entry Point
|
|
129
|
+
// =============================================================================
|
|
130
|
+
|
|
131
|
+
export default async function zenmuxProvider(pi: ExtensionAPI) {
|
|
132
|
+
const apiKey = getZenmuxApiKey();
|
|
133
|
+
|
|
134
|
+
if (!apiKey) {
|
|
135
|
+
_logger.info(
|
|
136
|
+
"[zenmux] Skipping - ZENMUX_API_KEY not set (env var or ~/.pi/free.json)",
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fetch models
|
|
142
|
+
const allModels = await fetchZenmuxModels(apiKey);
|
|
143
|
+
|
|
144
|
+
if (allModels.length === 0) {
|
|
145
|
+
_logger.warn("[zenmux] No models available");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Use isFreeModel with allModels for proper detection
|
|
150
|
+
// ZenMux exposes pricing, so Route A (OR logic) will be used:
|
|
151
|
+
// FREE if cost=0 OR "free" in name
|
|
152
|
+
const freeModels = allModels.filter((m) =>
|
|
153
|
+
isFreeModel({ ...m, provider: PROVIDER_ZENMUX }, allModels),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const stored = { free: freeModels, all: allModels };
|
|
157
|
+
|
|
158
|
+
_logger.info(
|
|
159
|
+
`[zenmux] Registered ${allModels.length} models (${freeModels.length} free)`,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Create re-register function
|
|
163
|
+
const reRegister = createReRegister(pi, {
|
|
164
|
+
providerId: PROVIDER_ZENMUX,
|
|
165
|
+
baseUrl: BASE_URL_ZENMUX,
|
|
166
|
+
apiKey,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Register with global toggle
|
|
170
|
+
registerWithGlobalToggle(PROVIDER_ZENMUX, stored, reRegister, true);
|
|
171
|
+
|
|
172
|
+
// Setup provider with toggle command
|
|
173
|
+
setupProvider(
|
|
174
|
+
pi,
|
|
175
|
+
{
|
|
176
|
+
providerId: PROVIDER_ZENMUX,
|
|
177
|
+
initialShowPaid: getZenmuxShowPaid(),
|
|
178
|
+
reRegister: (models, _stored) => {
|
|
179
|
+
if (_stored) {
|
|
180
|
+
stored.free = _stored.free;
|
|
181
|
+
stored.all = _stored.all;
|
|
182
|
+
}
|
|
183
|
+
reRegister(models);
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
stored,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// Initial registration — respect persisted toggle state
|
|
190
|
+
const showPaid = getZenmuxShowPaid();
|
|
191
|
+
const initialModels =
|
|
192
|
+
showPaid && stored.all.length > 0 ? stored.all : freeModels;
|
|
193
|
+
reRegister(initialModels);
|
|
194
|
+
}
|