pi-free 1.0.8 → 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/CHANGELOG.md +107 -1
- package/README.md +95 -46
- package/config.ts +165 -120
- package/constants.ts +22 -61
- package/index.ts +186 -0
- package/lib/json-persistence.ts +11 -10
- package/lib/logger.ts +2 -2
- package/lib/model-enhancer.ts +20 -20
- package/lib/open-browser.ts +41 -0
- package/lib/provider-cache.ts +106 -0
- package/lib/registry.ts +144 -0
- package/package.json +67 -82
- package/provider-factory.ts +25 -41
- package/provider-failover/benchmark-lookup.ts +247 -0
- package/provider-failover/benchmarks-chunk-0.ts +2010 -0
- package/provider-failover/benchmarks-chunk-1.ts +1988 -0
- package/provider-failover/benchmarks-chunk-2.ts +2010 -0
- package/provider-failover/benchmarks-chunk-3.ts +2010 -0
- package/provider-failover/benchmarks-chunk-4.ts +1969 -0
- package/provider-failover/hardcoded-benchmarks.ts +22 -10025
- package/provider-helper.ts +38 -37
- package/providers/{cline-auth.ts → cline/cline-auth.ts} +2 -2
- package/providers/cline/cline-models.ts +128 -0
- package/providers/{cline.ts → cline/cline.ts} +300 -257
- package/providers/cloudflare/cloudflare.ts +368 -0
- package/providers/dynamic-built-in/index.ts +513 -0
- package/providers/{kilo-auth.ts → kilo/kilo-auth.ts} +3 -20
- package/providers/{kilo-models.ts → kilo/kilo-models.ts} +2 -2
- package/providers/kilo/kilo.ts +235 -0
- package/providers/{modal.ts → modal/modal.ts} +4 -3
- package/providers/{nvidia.ts → nvidia/nvidia.ts} +152 -113
- package/providers/ollama/ollama.ts +172 -0
- package/providers/opencode-session.ts +34 -34
- package/providers/{qwen-auth.ts → qwen/qwen-auth.ts} +24 -40
- package/providers/{qwen-models.ts → qwen/qwen-models.ts} +101 -95
- package/providers/qwen/qwen.ts +202 -0
- package/provider-failover/auto-switch.ts +0 -350
- package/provider-failover/errors.ts +0 -275
- package/provider-failover/index.ts +0 -238
- package/providers/cline-models.ts +0 -77
- package/providers/factory.ts +0 -125
- package/providers/fireworks.ts +0 -49
- package/providers/go.ts +0 -216
- package/providers/kilo.ts +0 -146
- package/providers/mistral.ts +0 -144
- package/providers/ollama.ts +0 -113
- package/providers/openrouter.ts +0 -175
- package/providers/qwen.ts +0 -127
- package/providers/zen.ts +0 -371
- package/usage/commands.ts +0 -17
- package/usage/cumulative.ts +0 -193
- package/usage/formatters.ts +0 -115
- package/usage/index.ts +0 -46
- package/usage/limits.ts +0 -148
- package/usage/metrics.ts +0 -222
- package/usage/sessions.ts +0 -355
- package/usage/store.ts +0 -99
- package/usage/tracking.ts +0 -329
- package/usage/types.ts +0 -26
- package/usage/widget.ts +0 -90
- package/widget/data.ts +0 -113
- package/widget/format.ts +0 -26
- package/widget/render.ts +0 -117
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers AI Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Provides access to Cloudflare's serverless GPU network with 18+ models.
|
|
5
|
+
* All models use Cloudflare's "Neurons" pricing system:
|
|
6
|
+
* - 10,000 Neurons per day FREE (resets daily at 00:00 UTC)
|
|
7
|
+
* - $0.011 per 1,000 Neurons beyond free allocation
|
|
8
|
+
*
|
|
9
|
+
* Setup:
|
|
10
|
+
* 1. Create API token at https://dash.cloudflare.com/profile/api-tokens
|
|
11
|
+
* with "Cloudflare AI" > "Read" permission
|
|
12
|
+
* 2. Get Account ID from https://dash.cloudflare.com (right sidebar)
|
|
13
|
+
* 3. Add credentials to ~/.pi/agent/auth.json or set env vars
|
|
14
|
+
*
|
|
15
|
+
* Auth (in order of priority):
|
|
16
|
+
* - Environment: CF_API_TOKEN and CF_ACCOUNT_ID
|
|
17
|
+
* - Config file: ~/.pi/agent/auth.json
|
|
18
|
+
* { "cloudflare-ai": { "access": "token", "account_id": "id" } }
|
|
19
|
+
* - Legacy: CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID env vars
|
|
20
|
+
*
|
|
21
|
+
* Models can be customized via ~/.pi/cloudflare-models.json
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { join } from "node:path";
|
|
27
|
+
import type {
|
|
28
|
+
ExtensionAPI,
|
|
29
|
+
ProviderModelConfig,
|
|
30
|
+
} from "@mariozechner/pi-coding-agent";
|
|
31
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
32
|
+
|
|
33
|
+
const _logger = createLogger("cloudflare");
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Auth Resolution
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
interface CloudflareAuth {
|
|
40
|
+
token?: string;
|
|
41
|
+
accountId?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getCloudflareAuth(): CloudflareAuth {
|
|
45
|
+
const result: CloudflareAuth = {};
|
|
46
|
+
|
|
47
|
+
// Check new env var names first
|
|
48
|
+
if (process.env.CF_API_TOKEN) result.token = process.env.CF_API_TOKEN;
|
|
49
|
+
if (process.env.CF_ACCOUNT_ID) result.accountId = process.env.CF_ACCOUNT_ID;
|
|
50
|
+
|
|
51
|
+
// Check legacy env var names
|
|
52
|
+
if (!result.token && process.env.CLOUDFLARE_API_TOKEN) {
|
|
53
|
+
result.token = process.env.CLOUDFLARE_API_TOKEN;
|
|
54
|
+
}
|
|
55
|
+
if (!result.accountId && process.env.CLOUDFLARE_ACCOUNT_ID) {
|
|
56
|
+
result.accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (result.token && result.accountId) return result;
|
|
60
|
+
|
|
61
|
+
// Check ~/.pi/free.json (pi-free config)
|
|
62
|
+
try {
|
|
63
|
+
const configPath = join(homedir(), ".pi", "free.json");
|
|
64
|
+
if (existsSync(configPath)) {
|
|
65
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
66
|
+
if (!result.token && config.cloudflare_api_token) {
|
|
67
|
+
result.token = config.cloudflare_api_token;
|
|
68
|
+
}
|
|
69
|
+
if (!result.accountId && config.cloudflare_account_id) {
|
|
70
|
+
result.accountId = config.cloudflare_account_id;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Ignore config file errors
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Compatibility Settings
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Cloudflare Workers AI compatibility settings.
|
|
86
|
+
* Prevents 413 Payload Too Large errors by disabling unsupported parameters.
|
|
87
|
+
*/
|
|
88
|
+
const CLOUDFLARE_COMPAT: {
|
|
89
|
+
supportsStore?: boolean;
|
|
90
|
+
supportsDeveloperRole?: boolean;
|
|
91
|
+
supportsReasoningEffort?: boolean;
|
|
92
|
+
supportsStrictMode?: boolean;
|
|
93
|
+
maxTokensField?: "max_tokens" | "max_completion_tokens";
|
|
94
|
+
requiresThinkingAsText?: boolean;
|
|
95
|
+
} = {
|
|
96
|
+
supportsStore: false,
|
|
97
|
+
supportsDeveloperRole: false,
|
|
98
|
+
supportsReasoningEffort: false,
|
|
99
|
+
supportsStrictMode: false,
|
|
100
|
+
maxTokensField: "max_tokens",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Default Models (18 models from Cloudflare Workers AI)
|
|
105
|
+
// =============================================================================
|
|
106
|
+
|
|
107
|
+
interface ModelConfig extends ProviderModelConfig {
|
|
108
|
+
compat?: { requiresThinkingAsText?: boolean };
|
|
109
|
+
_remove?: boolean;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const DEFAULT_MODELS: ModelConfig[] = [
|
|
113
|
+
// Frontier models
|
|
114
|
+
{
|
|
115
|
+
id: "@cf/moonshotai/kimi-k2.5",
|
|
116
|
+
name: "Kimi K2.5",
|
|
117
|
+
reasoning: true,
|
|
118
|
+
input: ["text", "image"],
|
|
119
|
+
cost: { input: 0.6, output: 3.0, cacheRead: 0.1, cacheWrite: 0 },
|
|
120
|
+
contextWindow: 256000,
|
|
121
|
+
maxTokens: 8192,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "@cf/meta/llama-4-scout-17b-16e-instruct",
|
|
125
|
+
name: "Llama 4 Scout 17B",
|
|
126
|
+
reasoning: false,
|
|
127
|
+
input: ["text", "image"],
|
|
128
|
+
cost: { input: 0.27, output: 0.85, cacheRead: 0, cacheWrite: 0 },
|
|
129
|
+
contextWindow: 131072,
|
|
130
|
+
maxTokens: 8192,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: "@cf/nvidia/nemotron-3-120b-a12b",
|
|
134
|
+
name: "Nemotron 3 Super 120B",
|
|
135
|
+
reasoning: true,
|
|
136
|
+
input: ["text"],
|
|
137
|
+
cost: { input: 0.5, output: 1.5, cacheRead: 0, cacheWrite: 0 },
|
|
138
|
+
contextWindow: 256000,
|
|
139
|
+
maxTokens: 8192,
|
|
140
|
+
compat: { requiresThinkingAsText: true },
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "@cf/google/gemma-4-26b-a4b-it",
|
|
144
|
+
name: "Gemma 4 26B",
|
|
145
|
+
reasoning: true,
|
|
146
|
+
input: ["text", "image"],
|
|
147
|
+
cost: { input: 0.1, output: 0.3, cacheRead: 0, cacheWrite: 0 },
|
|
148
|
+
contextWindow: 256000,
|
|
149
|
+
maxTokens: 8192,
|
|
150
|
+
compat: { requiresThinkingAsText: true },
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: "@cf/google/gemma-3-12b-it",
|
|
154
|
+
name: "Gemma 3 12B",
|
|
155
|
+
reasoning: false,
|
|
156
|
+
input: ["text", "image"],
|
|
157
|
+
cost: { input: 0.345, output: 0.556, cacheRead: 0, cacheWrite: 0 },
|
|
158
|
+
contextWindow: 80000,
|
|
159
|
+
maxTokens: 8192,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: "@cf/qwen/qwen3-30b-a3b-fp8",
|
|
163
|
+
name: "Qwen3 30B A3B",
|
|
164
|
+
reasoning: true,
|
|
165
|
+
input: ["text"],
|
|
166
|
+
cost: { input: 0.051, output: 0.34, cacheRead: 0, cacheWrite: 0 },
|
|
167
|
+
contextWindow: 32768,
|
|
168
|
+
maxTokens: 8192,
|
|
169
|
+
compat: { requiresThinkingAsText: true },
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: "@cf/zai-org/glm-4.7-flash",
|
|
173
|
+
name: "GLM-4.7 Flash",
|
|
174
|
+
reasoning: false,
|
|
175
|
+
input: ["text"],
|
|
176
|
+
cost: { input: 0.06, output: 0.4, cacheRead: 0, cacheWrite: 0 },
|
|
177
|
+
contextWindow: 131072,
|
|
178
|
+
maxTokens: 8192,
|
|
179
|
+
},
|
|
180
|
+
// Popular models
|
|
181
|
+
{
|
|
182
|
+
id: "@cf/meta/llama-3.3-70b-instruct-fp8-fast",
|
|
183
|
+
name: "Llama 3.3 70B (Fast)",
|
|
184
|
+
reasoning: false,
|
|
185
|
+
input: ["text"],
|
|
186
|
+
cost: { input: 0.5, output: 0.5, cacheRead: 0, cacheWrite: 0 },
|
|
187
|
+
contextWindow: 131072,
|
|
188
|
+
maxTokens: 8192,
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: "@cf/meta/llama-3.1-8b-instruct",
|
|
192
|
+
name: "Llama 3.1 8B",
|
|
193
|
+
reasoning: false,
|
|
194
|
+
input: ["text"],
|
|
195
|
+
cost: { input: 0.1, output: 0.1, cacheRead: 0, cacheWrite: 0 },
|
|
196
|
+
contextWindow: 131072,
|
|
197
|
+
maxTokens: 8192,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "@cf/meta/llama-3.1-70b-instruct",
|
|
201
|
+
name: "Llama 3.1 70B",
|
|
202
|
+
reasoning: false,
|
|
203
|
+
input: ["text"],
|
|
204
|
+
cost: { input: 0.5, output: 0.5, cacheRead: 0, cacheWrite: 0 },
|
|
205
|
+
contextWindow: 131072,
|
|
206
|
+
maxTokens: 8192,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "@cf/meta/llama-3.1-405b-instruct",
|
|
210
|
+
name: "Llama 3.1 405B",
|
|
211
|
+
reasoning: false,
|
|
212
|
+
input: ["text"],
|
|
213
|
+
cost: { input: 2.0, output: 2.0, cacheRead: 0, cacheWrite: 0 },
|
|
214
|
+
contextWindow: 131072,
|
|
215
|
+
maxTokens: 8192,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "@cf/deepseek-ai/deepseek-r1-distill-qwen-32b",
|
|
219
|
+
name: "DeepSeek R1 Distill Qwen 32B",
|
|
220
|
+
reasoning: true,
|
|
221
|
+
input: ["text"],
|
|
222
|
+
cost: { input: 0.3, output: 0.3, cacheRead: 0, cacheWrite: 0 },
|
|
223
|
+
contextWindow: 32768,
|
|
224
|
+
maxTokens: 8192,
|
|
225
|
+
compat: { requiresThinkingAsText: true },
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "@cf/deepseek-ai/deepseek-math-7b-instruct",
|
|
229
|
+
name: "DeepSeek Math 7B",
|
|
230
|
+
reasoning: true,
|
|
231
|
+
input: ["text"],
|
|
232
|
+
cost: { input: 0.1, output: 0.1, cacheRead: 0, cacheWrite: 0 },
|
|
233
|
+
contextWindow: 16384,
|
|
234
|
+
maxTokens: 4096,
|
|
235
|
+
},
|
|
236
|
+
// Mistral models
|
|
237
|
+
{
|
|
238
|
+
id: "@cf/mistral/mistral-small-3.1-24b-instruct",
|
|
239
|
+
name: "Mistral Small 3.1 24B",
|
|
240
|
+
reasoning: false,
|
|
241
|
+
input: ["text", "image"],
|
|
242
|
+
cost: { input: 0.3, output: 0.3, cacheRead: 0, cacheWrite: 0 },
|
|
243
|
+
contextWindow: 32768,
|
|
244
|
+
maxTokens: 8192,
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "@cf/mistral/mistral-7b-instruct-v0.2-lora",
|
|
248
|
+
name: "Mistral 7B Instruct",
|
|
249
|
+
reasoning: false,
|
|
250
|
+
input: ["text"],
|
|
251
|
+
cost: { input: 0.1, output: 0.1, cacheRead: 0, cacheWrite: 0 },
|
|
252
|
+
contextWindow: 32768,
|
|
253
|
+
maxTokens: 4096,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: "@cf/mistral/mixtral-8x7b-instruct-v0.1-awq",
|
|
257
|
+
name: "Mixtral 8x7B Instruct",
|
|
258
|
+
reasoning: false,
|
|
259
|
+
input: ["text"],
|
|
260
|
+
cost: { input: 0.3, output: 0.3, cacheRead: 0, cacheWrite: 0 },
|
|
261
|
+
contextWindow: 32768,
|
|
262
|
+
maxTokens: 4096,
|
|
263
|
+
},
|
|
264
|
+
// Qwen and Gemma
|
|
265
|
+
{
|
|
266
|
+
id: "@cf/qwen/qwen1.5-14b-chat-awq",
|
|
267
|
+
name: "Qwen 1.5 14B Chat",
|
|
268
|
+
reasoning: false,
|
|
269
|
+
input: ["text"],
|
|
270
|
+
cost: { input: 0.2, output: 0.2, cacheRead: 0, cacheWrite: 0 },
|
|
271
|
+
contextWindow: 32768,
|
|
272
|
+
maxTokens: 8192,
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: "@cf/google/gemma-2b-it-lora",
|
|
276
|
+
name: "Gemma 2B",
|
|
277
|
+
reasoning: false,
|
|
278
|
+
input: ["text"],
|
|
279
|
+
cost: { input: 0.05, output: 0.05, cacheRead: 0, cacheWrite: 0 },
|
|
280
|
+
contextWindow: 8192,
|
|
281
|
+
maxTokens: 2048,
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: "@cf/google/gemma-7b-it-lora",
|
|
285
|
+
name: "Gemma 7B",
|
|
286
|
+
reasoning: false,
|
|
287
|
+
input: ["text"],
|
|
288
|
+
cost: { input: 0.1, output: 0.1, cacheRead: 0, cacheWrite: 0 },
|
|
289
|
+
contextWindow: 8192,
|
|
290
|
+
maxTokens: 2048,
|
|
291
|
+
},
|
|
292
|
+
];
|
|
293
|
+
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// Model Customization (user overrides)
|
|
296
|
+
// =============================================================================
|
|
297
|
+
|
|
298
|
+
function getModels(): ProviderModelConfig[] {
|
|
299
|
+
// Apply Cloudflare compat settings to all default models
|
|
300
|
+
const defaults = DEFAULT_MODELS.map((m) => ({
|
|
301
|
+
...m,
|
|
302
|
+
compat: { ...CLOUDFLARE_COMPAT, ...m.compat },
|
|
303
|
+
})) as ProviderModelConfig[];
|
|
304
|
+
|
|
305
|
+
// Check for user overrides
|
|
306
|
+
const overridePath = join(homedir(), ".pi", "cloudflare-models.json");
|
|
307
|
+
if (!existsSync(overridePath)) return defaults;
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const override = JSON.parse(
|
|
311
|
+
readFileSync(overridePath, "utf-8"),
|
|
312
|
+
) as ModelConfig[];
|
|
313
|
+
const modelMap = new Map<string, any>(defaults.map((m) => [m.id, m]));
|
|
314
|
+
|
|
315
|
+
for (const model of override) {
|
|
316
|
+
if (model._remove) {
|
|
317
|
+
modelMap.delete(model.id);
|
|
318
|
+
} else {
|
|
319
|
+
// Apply Cloudflare compat settings to user overrides
|
|
320
|
+
model.compat = { ...CLOUDFLARE_COMPAT, ...model.compat };
|
|
321
|
+
modelMap.set(model.id, model);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return Array.from(modelMap.values()) as ProviderModelConfig[];
|
|
326
|
+
} catch (e) {
|
|
327
|
+
_logger.warn(
|
|
328
|
+
`[cloudflare] Failed to load ~/.pi/cloudflare-models.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
329
|
+
);
|
|
330
|
+
return defaults;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// =============================================================================
|
|
335
|
+
// Extension Entry Point
|
|
336
|
+
// =============================================================================
|
|
337
|
+
|
|
338
|
+
export default async function cloudflareProvider(pi: ExtensionAPI) {
|
|
339
|
+
const { token: apiToken, accountId } = getCloudflareAuth();
|
|
340
|
+
|
|
341
|
+
if (!apiToken) {
|
|
342
|
+
_logger.info(
|
|
343
|
+
"[cloudflare] CF_API_TOKEN or CLOUDFLARE_API_TOKEN not set. Provider will not be available.",
|
|
344
|
+
);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (!accountId) {
|
|
349
|
+
_logger.info(
|
|
350
|
+
"[cloudflare] CF_ACCOUNT_ID or CLOUDFLARE_ACCOUNT_ID not set. Provider will not be available.",
|
|
351
|
+
);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const models = getModels();
|
|
356
|
+
|
|
357
|
+
pi.registerProvider("cloudflare", {
|
|
358
|
+
baseUrl: `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`,
|
|
359
|
+
apiKey: apiToken,
|
|
360
|
+
api: "openai-completions",
|
|
361
|
+
authHeader: true,
|
|
362
|
+
models,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
_logger.info(
|
|
366
|
+
`[cloudflare] Provider registered with account: ${accountId.slice(0, 8)}... (${models.length} models)`,
|
|
367
|
+
);
|
|
368
|
+
}
|