pi-free 2.0.0 → 2.0.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 +37 -0
- package/README.md +393 -416
- package/config.ts +6 -2
- package/constants.ts +1 -0
- package/index.ts +6 -44
- package/lib/built-in-toggle.ts +206 -0
- package/package.json +67 -67
- package/provider-helper.ts +260 -260
- package/providers/cline/cline-models.ts +1 -1
- package/providers/cline/cline.ts +5 -7
- package/providers/dynamic-built-in/index.ts +432 -513
- package/providers/kilo/kilo.ts +5 -5
- package/providers/nvidia/nvidia.ts +1 -1
- package/providers/ollama/ollama.ts +3 -3
package/provider-helper.ts
CHANGED
|
@@ -1,260 +1,260 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared provider setup helpers for pi-free-providers.
|
|
3
|
-
* Extracts the common boilerplate pattern repeated across providers:
|
|
4
|
-
* -
|
|
5
|
-
* - model_select handler (clear status for other providers)
|
|
6
|
-
* - turn_end handler (provider-specific error hook)
|
|
7
|
-
* - before_agent_start handler (one-time ToS notice)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
ExtensionAPI,
|
|
12
|
-
ProviderModelConfig,
|
|
13
|
-
} from "@mariozechner/pi-coding-agent";
|
|
14
|
-
import { saveConfig } from "./config.ts";
|
|
15
|
-
import { createLogger } from "./lib/logger.ts";
|
|
16
|
-
import { enhanceModelNameWithCodingIndex } from "./provider-failover/benchmark-lookup.ts";
|
|
17
|
-
|
|
18
|
-
const _logger = createLogger("provider-helper");
|
|
19
|
-
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// Types
|
|
22
|
-
// =============================================================================
|
|
23
|
-
|
|
24
|
-
export interface ProviderSetupConfig {
|
|
25
|
-
/** Provider identifier (e.g., "kilo", "openrouter"). */
|
|
26
|
-
providerId: string;
|
|
27
|
-
/** Terms of service URL. If set, shows a one-time notice on first free use. */
|
|
28
|
-
tosUrl?: string;
|
|
29
|
-
/** When true, suppresses the "free models / set API key" ToS notice. */
|
|
30
|
-
hasKey?: boolean;
|
|
31
|
-
/** Initial mode - auto-detected from config at startup. */
|
|
32
|
-
initialShowPaid?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* Called by
|
|
35
|
-
* the provider with the given model set.
|
|
36
|
-
*/
|
|
37
|
-
reRegister: (models: ProviderModelConfig[], stored: StoredModels) => void;
|
|
38
|
-
/** Optional custom error handler. Return true if handled. */
|
|
39
|
-
onError?: (
|
|
40
|
-
error: unknown,
|
|
41
|
-
ctx: {
|
|
42
|
-
ui: { notify: (m: string, t: "info" | "warning" | "error") => void };
|
|
43
|
-
},
|
|
44
|
-
) => Promise<boolean>;
|
|
45
|
-
/** When true, skips creating the
|
|
46
|
-
skipToggle?: boolean;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface StoredModels {
|
|
50
|
-
free: ProviderModelConfig[];
|
|
51
|
-
all: ProviderModelConfig[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// =============================================================================
|
|
55
|
-
// Provider Registration Helpers
|
|
56
|
-
// =============================================================================
|
|
57
|
-
|
|
58
|
-
export interface OpenAICompatibleConfig {
|
|
59
|
-
/** Provider identifier (e.g., "nvidia", "modal") */
|
|
60
|
-
providerId: string;
|
|
61
|
-
/** Base URL for the API */
|
|
62
|
-
baseUrl: string;
|
|
63
|
-
/** Environment variable name for the API key */
|
|
64
|
-
apiKey: string;
|
|
65
|
-
/** Additional headers to include */
|
|
66
|
-
headers?: Record<string, string>;
|
|
67
|
-
/** OAuth configuration (optional) */
|
|
68
|
-
oauth?: {
|
|
69
|
-
name: string;
|
|
70
|
-
login: (callbacks: unknown) => Promise<unknown>;
|
|
71
|
-
refreshToken?: (cred: unknown) => Promise<unknown>;
|
|
72
|
-
getApiKey?: (cred: unknown) => string;
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Enhance all model names with Coding Index scores
|
|
78
|
-
* Use this for direct provider registration (not through setupProvider)
|
|
79
|
-
*/
|
|
80
|
-
export function enhanceWithCI(
|
|
81
|
-
models: ProviderModelConfig[],
|
|
82
|
-
): ProviderModelConfig[] {
|
|
83
|
-
return models.map((m) => ({
|
|
84
|
-
...m,
|
|
85
|
-
name: enhanceModelNameWithCodingIndex(m.name, m.id),
|
|
86
|
-
}));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Register an OpenAI-compatible provider with standard headers.
|
|
91
|
-
* Reduces boilerplate across providers that use the OpenAI API format.
|
|
92
|
-
*/
|
|
93
|
-
export function registerOpenAICompatible(
|
|
94
|
-
pi: ExtensionAPI,
|
|
95
|
-
config: OpenAICompatibleConfig,
|
|
96
|
-
models: ProviderModelConfig[],
|
|
97
|
-
): void {
|
|
98
|
-
const { providerId, baseUrl, apiKey, headers, oauth } = config;
|
|
99
|
-
|
|
100
|
-
pi.registerProvider(providerId, {
|
|
101
|
-
baseUrl,
|
|
102
|
-
apiKey,
|
|
103
|
-
api: "openai-completions" as const,
|
|
104
|
-
headers: {
|
|
105
|
-
"User-Agent": "pi-free-providers",
|
|
106
|
-
...headers,
|
|
107
|
-
},
|
|
108
|
-
models: enhanceWithCI(models),
|
|
109
|
-
...(oauth && { oauth: oauth as any }),
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Create a reRegister function for use with setupProvider.
|
|
115
|
-
* Returns a function that re-registers the provider with new models.
|
|
116
|
-
*/
|
|
117
|
-
export function createReRegister(
|
|
118
|
-
pi: ExtensionAPI,
|
|
119
|
-
config: OpenAICompatibleConfig,
|
|
120
|
-
): (models: ProviderModelConfig[]) => void {
|
|
121
|
-
return (models: ProviderModelConfig[]) => {
|
|
122
|
-
registerOpenAICompatible(pi, config, models);
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Create a reRegister function that uses ctx.modelRegistry.registerProvider.
|
|
128
|
-
* Used by providers that need to register with runtime context (session_start handlers).
|
|
129
|
-
*/
|
|
130
|
-
export function createCtxReRegister(
|
|
131
|
-
ctx: {
|
|
132
|
-
modelRegistry: { registerProvider: (id: string, config: unknown) => void };
|
|
133
|
-
},
|
|
134
|
-
config: OpenAICompatibleConfig,
|
|
135
|
-
): (models: ProviderModelConfig[]) => void {
|
|
136
|
-
const { providerId, baseUrl, apiKey, headers, oauth } = config;
|
|
137
|
-
|
|
138
|
-
return (models: ProviderModelConfig[]) => {
|
|
139
|
-
ctx.modelRegistry.registerProvider(providerId, {
|
|
140
|
-
baseUrl,
|
|
141
|
-
apiKey,
|
|
142
|
-
api: "openai-completions" as const,
|
|
143
|
-
headers: {
|
|
144
|
-
"User-Agent": "pi-free-providers",
|
|
145
|
-
...headers,
|
|
146
|
-
},
|
|
147
|
-
models: enhanceWithCI(models),
|
|
148
|
-
...(oauth && { oauth: oauth as any }),
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Get the config key name for a provider's show_paid setting.
|
|
155
|
-
*/
|
|
156
|
-
function getShowPaidConfigKey(providerId: string): string {
|
|
157
|
-
return `${providerId}_show_paid`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function setupProvider(
|
|
161
|
-
pi: ExtensionAPI,
|
|
162
|
-
config: ProviderSetupConfig,
|
|
163
|
-
stored: StoredModels,
|
|
164
|
-
): void {
|
|
165
|
-
const { providerId, tosUrl, initialShowPaid = false } = config;
|
|
166
|
-
|
|
167
|
-
// Track current mode (synced with config)
|
|
168
|
-
let currentShowPaid = initialShowPaid;
|
|
169
|
-
|
|
170
|
-
// Wrap reRegister to automatically add CI scores to all models
|
|
171
|
-
const reRegister = (models: ProviderModelConfig[], _s: StoredModels) => {
|
|
172
|
-
const enhanced = enhanceWithCI(models);
|
|
173
|
-
config.reRegister(enhanced, _s);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// ── Single toggle command (skip if requested) ──────────────────────
|
|
177
|
-
|
|
178
|
-
if (!config.skipToggle) {
|
|
179
|
-
pi.registerCommand(
|
|
180
|
-
description: `Toggle between free and all ${providerId} models`,
|
|
181
|
-
handler: async (_args, ctx) => {
|
|
182
|
-
// Toggle the mode
|
|
183
|
-
currentShowPaid = !currentShowPaid;
|
|
184
|
-
|
|
185
|
-
// Persist to config file
|
|
186
|
-
const configKey = getShowPaidConfigKey(providerId);
|
|
187
|
-
saveConfig({ [configKey]: currentShowPaid });
|
|
188
|
-
|
|
189
|
-
// Re-register with appropriate model set
|
|
190
|
-
if (currentShowPaid) {
|
|
191
|
-
if (stored.all.length === 0) {
|
|
192
|
-
ctx.ui.notify("No models available", "warning");
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
reRegister(stored.all, stored);
|
|
196
|
-
ctx.ui.notify(
|
|
197
|
-
`${providerId}: showing all ${stored.all.length} models (including paid)`,
|
|
198
|
-
"info",
|
|
199
|
-
);
|
|
200
|
-
} else {
|
|
201
|
-
if (stored.free.length === 0) {
|
|
202
|
-
ctx.ui.notify("No free models loaded", "warning");
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
reRegister(stored.free, stored);
|
|
206
|
-
ctx.ui.notify(
|
|
207
|
-
`${providerId}: showing ${stored.free.length} free models`,
|
|
208
|
-
"info",
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// ── Clear status when another provider is selected ───────────────────
|
|
216
|
-
|
|
217
|
-
pi.on("model_select", (_event, ctx) => {
|
|
218
|
-
if (_event.model?.provider !== providerId) {
|
|
219
|
-
ctx.ui.setStatus(`${providerId}-status`, undefined);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// ── Error handling / usage tracking are temporarily deprecated ─────────
|
|
224
|
-
|
|
225
|
-
pi.on("turn_end", async (event, ctx) => {
|
|
226
|
-
if (ctx.model?.provider !== providerId) return;
|
|
227
|
-
|
|
228
|
-
const msg = (
|
|
229
|
-
event as { message?: { role?: string; errorMessage?: string } }
|
|
230
|
-
).message;
|
|
231
|
-
|
|
232
|
-
if (msg?.role === "assistant" && msg.errorMessage) {
|
|
233
|
-
_logger.info("Provider error (auto model hopping disabled)", {
|
|
234
|
-
provider: providerId,
|
|
235
|
-
error: msg.errorMessage.slice(0, 100),
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Keep custom handlers working for provider-specific logic.
|
|
239
|
-
if (config.onError) {
|
|
240
|
-
await config.onError(msg.errorMessage, ctx);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// ── ToS notice on first use ────────────────────────────────
|
|
246
|
-
if (tosUrl) {
|
|
247
|
-
let tosShown = false;
|
|
248
|
-
pi.on("model_select", async (_event, ctx) => {
|
|
249
|
-
if (tosShown || ctx.model?.provider !== providerId) return;
|
|
250
|
-
tosShown = true;
|
|
251
|
-
if (config.hasKey) return;
|
|
252
|
-
const cred = ctx.modelRegistry.authStorage.get(providerId);
|
|
253
|
-
if (cred?.type === "oauth") return;
|
|
254
|
-
ctx.ui.notify(
|
|
255
|
-
`Using ${providerId} free models. Set API key for paid access. Terms: ${tosUrl}`,
|
|
256
|
-
"info",
|
|
257
|
-
);
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Shared provider setup helpers for pi-free-providers.
|
|
3
|
+
* Extracts the common boilerplate pattern repeated across providers:
|
|
4
|
+
* - toggle-{provider} command to switch between free/paid models
|
|
5
|
+
* - model_select handler (clear status for other providers)
|
|
6
|
+
* - turn_end handler (provider-specific error hook)
|
|
7
|
+
* - before_agent_start handler (one-time ToS notice)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ExtensionAPI,
|
|
12
|
+
ProviderModelConfig,
|
|
13
|
+
} from "@mariozechner/pi-coding-agent";
|
|
14
|
+
import { saveConfig } from "./config.ts";
|
|
15
|
+
import { createLogger } from "./lib/logger.ts";
|
|
16
|
+
import { enhanceModelNameWithCodingIndex } from "./provider-failover/benchmark-lookup.ts";
|
|
17
|
+
|
|
18
|
+
const _logger = createLogger("provider-helper");
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
export interface ProviderSetupConfig {
|
|
25
|
+
/** Provider identifier (e.g., "kilo", "openrouter"). */
|
|
26
|
+
providerId: string;
|
|
27
|
+
/** Terms of service URL. If set, shows a one-time notice on first free use. */
|
|
28
|
+
tosUrl?: string;
|
|
29
|
+
/** When true, suppresses the "free models / set API key" ToS notice. */
|
|
30
|
+
hasKey?: boolean;
|
|
31
|
+
/** Initial mode - auto-detected from config at startup. */
|
|
32
|
+
initialShowPaid?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Called by toggle-{provider} command to re-register
|
|
35
|
+
* the provider with the given model set.
|
|
36
|
+
*/
|
|
37
|
+
reRegister: (models: ProviderModelConfig[], stored: StoredModels) => void;
|
|
38
|
+
/** Optional custom error handler. Return true if handled. */
|
|
39
|
+
onError?: (
|
|
40
|
+
error: unknown,
|
|
41
|
+
ctx: {
|
|
42
|
+
ui: { notify: (m: string, t: "info" | "warning" | "error") => void };
|
|
43
|
+
},
|
|
44
|
+
) => Promise<boolean>;
|
|
45
|
+
/** When true, skips creating the toggle-{provider} command. Useful for providers with only one model. */
|
|
46
|
+
skipToggle?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StoredModels {
|
|
50
|
+
free: ProviderModelConfig[];
|
|
51
|
+
all: ProviderModelConfig[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// Provider Registration Helpers
|
|
56
|
+
// =============================================================================
|
|
57
|
+
|
|
58
|
+
export interface OpenAICompatibleConfig {
|
|
59
|
+
/** Provider identifier (e.g., "nvidia", "modal") */
|
|
60
|
+
providerId: string;
|
|
61
|
+
/** Base URL for the API */
|
|
62
|
+
baseUrl: string;
|
|
63
|
+
/** Environment variable name for the API key */
|
|
64
|
+
apiKey: string;
|
|
65
|
+
/** Additional headers to include */
|
|
66
|
+
headers?: Record<string, string>;
|
|
67
|
+
/** OAuth configuration (optional) */
|
|
68
|
+
oauth?: {
|
|
69
|
+
name: string;
|
|
70
|
+
login: (callbacks: unknown) => Promise<unknown>;
|
|
71
|
+
refreshToken?: (cred: unknown) => Promise<unknown>;
|
|
72
|
+
getApiKey?: (cred: unknown) => string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Enhance all model names with Coding Index scores
|
|
78
|
+
* Use this for direct provider registration (not through setupProvider)
|
|
79
|
+
*/
|
|
80
|
+
export function enhanceWithCI(
|
|
81
|
+
models: ProviderModelConfig[],
|
|
82
|
+
): ProviderModelConfig[] {
|
|
83
|
+
return models.map((m) => ({
|
|
84
|
+
...m,
|
|
85
|
+
name: enhanceModelNameWithCodingIndex(m.name, m.id),
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Register an OpenAI-compatible provider with standard headers.
|
|
91
|
+
* Reduces boilerplate across providers that use the OpenAI API format.
|
|
92
|
+
*/
|
|
93
|
+
export function registerOpenAICompatible(
|
|
94
|
+
pi: ExtensionAPI,
|
|
95
|
+
config: OpenAICompatibleConfig,
|
|
96
|
+
models: ProviderModelConfig[],
|
|
97
|
+
): void {
|
|
98
|
+
const { providerId, baseUrl, apiKey, headers, oauth } = config;
|
|
99
|
+
|
|
100
|
+
pi.registerProvider(providerId, {
|
|
101
|
+
baseUrl,
|
|
102
|
+
apiKey,
|
|
103
|
+
api: "openai-completions" as const,
|
|
104
|
+
headers: {
|
|
105
|
+
"User-Agent": "pi-free-providers",
|
|
106
|
+
...headers,
|
|
107
|
+
},
|
|
108
|
+
models: enhanceWithCI(models),
|
|
109
|
+
...(oauth && { oauth: oauth as any }),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create a reRegister function for use with setupProvider.
|
|
115
|
+
* Returns a function that re-registers the provider with new models.
|
|
116
|
+
*/
|
|
117
|
+
export function createReRegister(
|
|
118
|
+
pi: ExtensionAPI,
|
|
119
|
+
config: OpenAICompatibleConfig,
|
|
120
|
+
): (models: ProviderModelConfig[]) => void {
|
|
121
|
+
return (models: ProviderModelConfig[]) => {
|
|
122
|
+
registerOpenAICompatible(pi, config, models);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a reRegister function that uses ctx.modelRegistry.registerProvider.
|
|
128
|
+
* Used by providers that need to register with runtime context (session_start handlers).
|
|
129
|
+
*/
|
|
130
|
+
export function createCtxReRegister(
|
|
131
|
+
ctx: {
|
|
132
|
+
modelRegistry: { registerProvider: (id: string, config: unknown) => void };
|
|
133
|
+
},
|
|
134
|
+
config: OpenAICompatibleConfig,
|
|
135
|
+
): (models: ProviderModelConfig[]) => void {
|
|
136
|
+
const { providerId, baseUrl, apiKey, headers, oauth } = config;
|
|
137
|
+
|
|
138
|
+
return (models: ProviderModelConfig[]) => {
|
|
139
|
+
ctx.modelRegistry.registerProvider(providerId, {
|
|
140
|
+
baseUrl,
|
|
141
|
+
apiKey,
|
|
142
|
+
api: "openai-completions" as const,
|
|
143
|
+
headers: {
|
|
144
|
+
"User-Agent": "pi-free-providers",
|
|
145
|
+
...headers,
|
|
146
|
+
},
|
|
147
|
+
models: enhanceWithCI(models),
|
|
148
|
+
...(oauth && { oauth: oauth as any }),
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get the config key name for a provider's show_paid setting.
|
|
155
|
+
*/
|
|
156
|
+
function getShowPaidConfigKey(providerId: string): string {
|
|
157
|
+
return `${providerId}_show_paid`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function setupProvider(
|
|
161
|
+
pi: ExtensionAPI,
|
|
162
|
+
config: ProviderSetupConfig,
|
|
163
|
+
stored: StoredModels,
|
|
164
|
+
): void {
|
|
165
|
+
const { providerId, tosUrl, initialShowPaid = false } = config;
|
|
166
|
+
|
|
167
|
+
// Track current mode (synced with config)
|
|
168
|
+
let currentShowPaid = initialShowPaid;
|
|
169
|
+
|
|
170
|
+
// Wrap reRegister to automatically add CI scores to all models
|
|
171
|
+
const reRegister = (models: ProviderModelConfig[], _s: StoredModels) => {
|
|
172
|
+
const enhanced = enhanceWithCI(models);
|
|
173
|
+
config.reRegister(enhanced, _s);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// ── Single toggle command (skip if requested) ──────────────────────
|
|
177
|
+
|
|
178
|
+
if (!config.skipToggle) {
|
|
179
|
+
pi.registerCommand(`toggle-${providerId}`, {
|
|
180
|
+
description: `Toggle between free and all ${providerId} models`,
|
|
181
|
+
handler: async (_args, ctx) => {
|
|
182
|
+
// Toggle the mode
|
|
183
|
+
currentShowPaid = !currentShowPaid;
|
|
184
|
+
|
|
185
|
+
// Persist to config file
|
|
186
|
+
const configKey = getShowPaidConfigKey(providerId);
|
|
187
|
+
saveConfig({ [configKey]: currentShowPaid });
|
|
188
|
+
|
|
189
|
+
// Re-register with appropriate model set
|
|
190
|
+
if (currentShowPaid) {
|
|
191
|
+
if (stored.all.length === 0) {
|
|
192
|
+
ctx.ui.notify("No models available", "warning");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
reRegister(stored.all, stored);
|
|
196
|
+
ctx.ui.notify(
|
|
197
|
+
`${providerId}: showing all ${stored.all.length} models (including paid)`,
|
|
198
|
+
"info",
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
if (stored.free.length === 0) {
|
|
202
|
+
ctx.ui.notify("No free models loaded", "warning");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
reRegister(stored.free, stored);
|
|
206
|
+
ctx.ui.notify(
|
|
207
|
+
`${providerId}: showing ${stored.free.length} free models`,
|
|
208
|
+
"info",
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── Clear status when another provider is selected ───────────────────
|
|
216
|
+
|
|
217
|
+
pi.on("model_select", (_event, ctx) => {
|
|
218
|
+
if (_event.model?.provider !== providerId) {
|
|
219
|
+
ctx.ui.setStatus(`${providerId}-status`, undefined);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// ── Error handling / usage tracking are temporarily deprecated ─────────
|
|
224
|
+
|
|
225
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
226
|
+
if (ctx.model?.provider !== providerId) return;
|
|
227
|
+
|
|
228
|
+
const msg = (
|
|
229
|
+
event as { message?: { role?: string; errorMessage?: string } }
|
|
230
|
+
).message;
|
|
231
|
+
|
|
232
|
+
if (msg?.role === "assistant" && msg.errorMessage) {
|
|
233
|
+
_logger.info("Provider error (auto model hopping disabled)", {
|
|
234
|
+
provider: providerId,
|
|
235
|
+
error: msg.errorMessage.slice(0, 100),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Keep custom handlers working for provider-specific logic.
|
|
239
|
+
if (config.onError) {
|
|
240
|
+
await config.onError(msg.errorMessage, ctx);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// ── ToS notice on first use ────────────────────────────────
|
|
246
|
+
if (tosUrl) {
|
|
247
|
+
let tosShown = false;
|
|
248
|
+
pi.on("model_select", async (_event, ctx) => {
|
|
249
|
+
if (tosShown || ctx.model?.provider !== providerId) return;
|
|
250
|
+
tosShown = true;
|
|
251
|
+
if (config.hasKey) return;
|
|
252
|
+
const cred = ctx.modelRegistry.authStorage.get(providerId);
|
|
253
|
+
if (cred?.type === "oauth") return;
|
|
254
|
+
ctx.ui.notify(
|
|
255
|
+
`Using ${providerId} free models. Set API key for paid access. Terms: ${tosUrl}`,
|
|
256
|
+
"info",
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Cline model fetching.
|
|
3
3
|
*
|
|
4
4
|
* Fetches ALL models from OpenRouter (Cline's gateway).
|
|
5
|
-
* Free/paid filtering is handled by the global
|
|
5
|
+
* Free/paid filtering is handled by the global free-only filter.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { applyHidden } from "../../config.ts";
|
package/providers/cline/cline.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Auth flow based on pi-cline's proven implementation.
|
|
9
9
|
*
|
|
10
|
-
* Responds to global
|
|
10
|
+
* Responds to global free-only filter (though Cline only provides free models without auth).
|
|
11
11
|
*
|
|
12
12
|
* Usage:
|
|
13
13
|
* pi install git:github.com/apmantza/pi-free
|
|
@@ -193,13 +193,13 @@ function shapeMessagesForCline(messages: any[]): any[] {
|
|
|
193
193
|
|
|
194
194
|
export default async function (pi: ExtensionAPI) {
|
|
195
195
|
// Fetch ALL models from OpenRouter (free and paid)
|
|
196
|
-
// The global
|
|
196
|
+
// The global free-only filter will filter based on cost.input
|
|
197
197
|
let allModels = await fetchClineModels(false).catch((err) => {
|
|
198
198
|
logWarning("cline", "Failed to fetch models at startup", err);
|
|
199
199
|
return [];
|
|
200
200
|
});
|
|
201
201
|
|
|
202
|
-
// Also fetch free-only list for the global
|
|
202
|
+
// Also fetch free-only list for the global free-only filter
|
|
203
203
|
let freeModels = allModels.filter((m) => m.cost.input === 0);
|
|
204
204
|
|
|
205
205
|
// Create re-register function for global toggle
|
|
@@ -230,10 +230,9 @@ export default async function (pi: ExtensionAPI) {
|
|
|
230
230
|
// Initial registration with all models
|
|
231
231
|
reRegister(allModels);
|
|
232
232
|
|
|
233
|
-
// Per-provider toggle command
|
|
233
|
+
// Per-provider toggle command
|
|
234
234
|
let showPaidModels = false;
|
|
235
|
-
|
|
236
|
-
pi.registerCommand("cline-toggle", {
|
|
235
|
+
pi.registerCommand("toggle-cline", {
|
|
237
236
|
description: "Toggle between free and all Cline models",
|
|
238
237
|
handler: async (_args, ctx) => {
|
|
239
238
|
showPaidModels = !showPaidModels;
|
|
@@ -242,7 +241,6 @@ export default async function (pi: ExtensionAPI) {
|
|
|
242
241
|
const modelsToShow =
|
|
243
242
|
showPaidModels && allModels.length > 0 ? allModels : freeModels;
|
|
244
243
|
|
|
245
|
-
currentModels = modelsToShow;
|
|
246
244
|
reRegister(modelsToShow);
|
|
247
245
|
|
|
248
246
|
const freeCount = freeModels.length;
|