pi-free 2.0.0 → 2.0.2
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 +143 -2
- package/README.md +261 -129
- package/config.ts +28 -4
- package/constants.ts +1 -0
- package/index.ts +148 -186
- package/lib/built-in-toggle.ts +187 -0
- package/lib/toggle-state.ts +86 -0
- package/lib/util.ts +256 -256
- package/package.json +67 -67
- package/provider-failover/benchmark-lookup.ts +637 -247
- package/provider-helper.ts +9 -8
- package/providers/cline/cline-models.ts +129 -128
- package/providers/cline/cline.ts +284 -300
- package/providers/cloudflare/cloudflare.ts +292 -134
- package/providers/dynamic-built-in/index.ts +433 -513
- package/providers/kilo/kilo-models.ts +2 -1
- package/providers/kilo/kilo.ts +5 -5
- package/providers/modal/modal.ts +47 -44
- package/providers/nvidia/nvidia.ts +297 -34
- package/providers/ollama/ollama.ts +280 -172
package/provider-helper.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared provider setup helpers for pi-free-providers.
|
|
3
3
|
* Extracts the common boilerplate pattern repeated across providers:
|
|
4
|
-
* -
|
|
4
|
+
* - toggle-{provider} command to switch between free/paid models
|
|
5
5
|
* - model_select handler (clear status for other providers)
|
|
6
6
|
* - turn_end handler (provider-specific error hook)
|
|
7
7
|
* - before_agent_start handler (one-time ToS notice)
|
|
@@ -31,7 +31,7 @@ export interface ProviderSetupConfig {
|
|
|
31
31
|
/** Initial mode - auto-detected from config at startup. */
|
|
32
32
|
initialShowPaid?: boolean;
|
|
33
33
|
/**
|
|
34
|
-
* Called by
|
|
34
|
+
* Called by toggle-{provider} command to re-register
|
|
35
35
|
* the provider with the given model set.
|
|
36
36
|
*/
|
|
37
37
|
reRegister: (models: ProviderModelConfig[], stored: StoredModels) => void;
|
|
@@ -42,7 +42,7 @@ export interface ProviderSetupConfig {
|
|
|
42
42
|
ui: { notify: (m: string, t: "info" | "warning" | "error") => void };
|
|
43
43
|
},
|
|
44
44
|
) => Promise<boolean>;
|
|
45
|
-
/** When true, skips creating the
|
|
45
|
+
/** When true, skips creating the toggle-{provider} command. Useful for providers with only one model. */
|
|
46
46
|
skipToggle?: boolean;
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -79,10 +79,11 @@ export interface OpenAICompatibleConfig {
|
|
|
79
79
|
*/
|
|
80
80
|
export function enhanceWithCI(
|
|
81
81
|
models: ProviderModelConfig[],
|
|
82
|
+
providerId?: string,
|
|
82
83
|
): ProviderModelConfig[] {
|
|
83
84
|
return models.map((m) => ({
|
|
84
85
|
...m,
|
|
85
|
-
name: enhanceModelNameWithCodingIndex(m.name, m.id),
|
|
86
|
+
name: enhanceModelNameWithCodingIndex(m.name, m.id, providerId),
|
|
86
87
|
}));
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -105,7 +106,7 @@ export function registerOpenAICompatible(
|
|
|
105
106
|
"User-Agent": "pi-free-providers",
|
|
106
107
|
...headers,
|
|
107
108
|
},
|
|
108
|
-
models: enhanceWithCI(models),
|
|
109
|
+
models: enhanceWithCI(models, providerId),
|
|
109
110
|
...(oauth && { oauth: oauth as any }),
|
|
110
111
|
});
|
|
111
112
|
}
|
|
@@ -144,7 +145,7 @@ export function createCtxReRegister(
|
|
|
144
145
|
"User-Agent": "pi-free-providers",
|
|
145
146
|
...headers,
|
|
146
147
|
},
|
|
147
|
-
models: enhanceWithCI(models),
|
|
148
|
+
models: enhanceWithCI(models, providerId),
|
|
148
149
|
...(oauth && { oauth: oauth as any }),
|
|
149
150
|
});
|
|
150
151
|
};
|
|
@@ -169,14 +170,14 @@ export function setupProvider(
|
|
|
169
170
|
|
|
170
171
|
// Wrap reRegister to automatically add CI scores to all models
|
|
171
172
|
const reRegister = (models: ProviderModelConfig[], _s: StoredModels) => {
|
|
172
|
-
const enhanced = enhanceWithCI(models);
|
|
173
|
+
const enhanced = enhanceWithCI(models, providerId);
|
|
173
174
|
config.reRegister(enhanced, _s);
|
|
174
175
|
};
|
|
175
176
|
|
|
176
177
|
// ── Single toggle command (skip if requested) ──────────────────────
|
|
177
178
|
|
|
178
179
|
if (!config.skipToggle) {
|
|
179
|
-
pi.registerCommand(
|
|
180
|
+
pi.registerCommand(`toggle-${providerId}`, {
|
|
180
181
|
description: `Toggle between free and all ${providerId} models`,
|
|
181
182
|
handler: async (_args, ctx) => {
|
|
182
183
|
// Toggle the mode
|
|
@@ -1,128 +1,129 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cline model fetching.
|
|
3
|
-
*
|
|
4
|
-
* Fetches ALL models from OpenRouter (Cline's gateway).
|
|
5
|
-
* Free/paid filtering is handled by the global
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { applyHidden } from "../../config.ts";
|
|
9
|
-
import {
|
|
10
|
-
BASE_URL_OPENROUTER,
|
|
11
|
-
DEFAULT_FETCH_TIMEOUT_MS,
|
|
12
|
-
DEFAULT_MIN_SIZE_B,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
info.supported_parameters?.includes("
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Cline model fetching.
|
|
3
|
+
*
|
|
4
|
+
* Fetches ALL models from OpenRouter (Cline's gateway).
|
|
5
|
+
* Free/paid filtering is handled by the global free-only filter.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { applyHidden } from "../../config.ts";
|
|
9
|
+
import {
|
|
10
|
+
BASE_URL_OPENROUTER,
|
|
11
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
12
|
+
DEFAULT_MIN_SIZE_B,
|
|
13
|
+
PROVIDER_CLINE,
|
|
14
|
+
} from "../../constants.ts";
|
|
15
|
+
import type { ProviderModelConfig } from "../../lib/types.ts";
|
|
16
|
+
import {
|
|
17
|
+
cleanModelName,
|
|
18
|
+
fetchWithRetry,
|
|
19
|
+
isUsableModel,
|
|
20
|
+
} from "../../lib/util.ts";
|
|
21
|
+
|
|
22
|
+
interface OpenRouterRaw {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
context_length?: number;
|
|
26
|
+
supported_parameters?: string[];
|
|
27
|
+
architecture?: { input_modalities?: string[]; output_modalities?: string[] };
|
|
28
|
+
top_provider?: { max_completion_tokens?: number | null };
|
|
29
|
+
pricing?: { prompt?: string; completion?: string };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function extractNameFromId(id: string): string {
|
|
33
|
+
const part = id.split("/")[1] ?? id;
|
|
34
|
+
return part
|
|
35
|
+
.split(/[-_]/)
|
|
36
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
37
|
+
.join(" ");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse pricing string to cost per million tokens.
|
|
42
|
+
* OpenRouter returns pricing as string (e.g., "0.0001" or "0").
|
|
43
|
+
*/
|
|
44
|
+
function parsePricing(pricingStr: string | undefined): number {
|
|
45
|
+
if (!pricingStr || pricingStr === "0") return 0;
|
|
46
|
+
const parsed = parseFloat(pricingStr);
|
|
47
|
+
return isNaN(parsed) ? 0 : parsed * 1_000_000; // Convert to per-million
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if a model is free (both prompt and completion pricing is 0).
|
|
52
|
+
*/
|
|
53
|
+
function isFreeModel(info: OpenRouterRaw): boolean {
|
|
54
|
+
return info.pricing?.prompt === "0" && info.pricing?.completion === "0";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fetch ALL models from OpenRouter.
|
|
59
|
+
* @param freeOnly - If true, return only free models
|
|
60
|
+
*/
|
|
61
|
+
export async function fetchClineModels(
|
|
62
|
+
freeOnly = false,
|
|
63
|
+
): Promise<ProviderModelConfig[]> {
|
|
64
|
+
const response = await fetchWithRetry(
|
|
65
|
+
`${BASE_URL_OPENROUTER}/models`,
|
|
66
|
+
{},
|
|
67
|
+
3,
|
|
68
|
+
1000,
|
|
69
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (!response.ok)
|
|
73
|
+
throw new Error(`Failed to fetch OpenRouter models: ${response.status}`);
|
|
74
|
+
|
|
75
|
+
const json = (await response.json()) as { data?: OpenRouterRaw[] };
|
|
76
|
+
|
|
77
|
+
// Filter to usable models (chat-capable, size threshold)
|
|
78
|
+
let usableModels = (json.data ?? []).filter((m) =>
|
|
79
|
+
isUsableModel(m.id, DEFAULT_MIN_SIZE_B),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// If freeOnly, filter to free models
|
|
83
|
+
if (freeOnly) {
|
|
84
|
+
usableModels = usableModels.filter(isFreeModel);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const models: ProviderModelConfig[] = [];
|
|
88
|
+
for (const info of usableModels) {
|
|
89
|
+
const isReasoning = !!(
|
|
90
|
+
info.supported_parameters?.includes("include_reasoning") ||
|
|
91
|
+
info.supported_parameters?.includes("reasoning")
|
|
92
|
+
);
|
|
93
|
+
const hasImage =
|
|
94
|
+
info.architecture?.input_modalities?.includes("image") ?? false;
|
|
95
|
+
|
|
96
|
+
// Calculate cost per million tokens
|
|
97
|
+
const inputCost = parsePricing(info.pricing?.prompt);
|
|
98
|
+
const outputCost = parsePricing(info.pricing?.completion);
|
|
99
|
+
const isFree = inputCost === 0 && outputCost === 0;
|
|
100
|
+
|
|
101
|
+
const cleanName = info.name
|
|
102
|
+
? cleanModelName(info.name)
|
|
103
|
+
: extractNameFromId(info.id);
|
|
104
|
+
|
|
105
|
+
models.push({
|
|
106
|
+
id: info.id,
|
|
107
|
+
name: `${cleanName} (Cline)${isFree ? "" : " 💰"}`,
|
|
108
|
+
reasoning: isReasoning,
|
|
109
|
+
input: hasImage ? ["text", "image"] : ["text"],
|
|
110
|
+
cost: {
|
|
111
|
+
input: inputCost,
|
|
112
|
+
output: outputCost,
|
|
113
|
+
cacheRead: 0,
|
|
114
|
+
cacheWrite: 0,
|
|
115
|
+
},
|
|
116
|
+
contextWindow: info.context_length ?? 128_000,
|
|
117
|
+
maxTokens: info.top_provider?.max_completion_tokens ?? 8_192,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return applyHidden(models, PROVIDER_CLINE);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Fetch only free models (backward compatibility).
|
|
126
|
+
*/
|
|
127
|
+
export async function fetchClineFreeModels(): Promise<ProviderModelConfig[]> {
|
|
128
|
+
return fetchClineModels(true);
|
|
129
|
+
}
|