pi-free 2.0.2 → 2.0.4
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 +84 -12
- package/README.md +44 -97
- package/config.ts +24 -52
- package/constants.ts +6 -0
- package/index.ts +175 -148
- package/lib/built-in-toggle.ts +40 -1
- package/lib/model-enhancer.ts +20 -20
- package/lib/open-browser.ts +1 -1
- package/lib/provider-compat.ts +46 -0
- package/lib/registry.ts +193 -144
- package/lib/types.ts +101 -108
- package/lib/util.ts +256 -256
- package/package.json +8 -8
- package/provider-failover/benchmark-lookup.ts +2 -2
- package/provider-helper.ts +19 -1
- package/providers/cline/cline-auth.ts +473 -473
- package/providers/cline/cline.ts +31 -4
- package/providers/crofai/crofai.ts +170 -0
- package/providers/dynamic-built-in/index.ts +258 -308
- package/providers/kilo/kilo-auth.ts +155 -155
- package/providers/kilo/kilo.ts +263 -235
- package/providers/nvidia/nvidia.ts +476 -415
- package/providers/ollama/ollama.ts +295 -280
- package/providers/opencode-session.ts +3 -4
- package/providers/qwen/qwen-models.ts +101 -101
- package/providers/zenmux/zenmux.ts +176 -0
- package/scripts/check-extensions.mjs +64 -55
- package/provider-factory.ts +0 -207
- package/providers/cloudflare/cloudflare.ts +0 -526
- package/providers/modal/modal.ts +0 -47
|
@@ -1,415 +1,476 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NVIDIA NIM Provider Extension
|
|
3
|
-
*
|
|
4
|
-
* Provides access to NVIDIA-hosted large models via integrate.api.nvidia.com.
|
|
5
|
-
* All models use NVIDIA's free credit system — requires NVIDIA_API_KEY.
|
|
6
|
-
* Get a free key at: https://build.nvidia.com
|
|
7
|
-
*
|
|
8
|
-
* Small models (< 70B) are filtered out to keep the list focused on useful
|
|
9
|
-
* chat/coding models. Non-chat models (embedding, speech-to-text, OCR,
|
|
10
|
-
* image-gen) are filtered by their modalities (output must be ["text"],
|
|
11
|
-
* input must include "text").
|
|
12
|
-
*
|
|
13
|
-
* Responds to global free-only filter for free/paid model filtering.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import type {
|
|
17
|
-
ExtensionAPI,
|
|
18
|
-
ProviderModelConfig,
|
|
19
|
-
} from "@mariozechner/pi-coding-agent";
|
|
20
|
-
import {
|
|
21
|
-
applyHidden,
|
|
22
|
-
getNvidiaApiKey,
|
|
23
|
-
loadConfigFile,
|
|
24
|
-
PROVIDER_NVIDIA,
|
|
25
|
-
saveConfig,
|
|
26
|
-
} from "../../config.ts";
|
|
27
|
-
import {
|
|
28
|
-
BASE_URL_NVIDIA,
|
|
29
|
-
DEFAULT_FETCH_TIMEOUT_MS,
|
|
30
|
-
NVIDIA_MIN_SIZE_B,
|
|
31
|
-
URL_MODELS_DEV,
|
|
32
|
-
} from "../../constants.ts";
|
|
33
|
-
import {
|
|
34
|
-
import
|
|
35
|
-
import {
|
|
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
|
-
"google/codegemma-7b",
|
|
87
|
-
"google/
|
|
88
|
-
"google/
|
|
89
|
-
"google/
|
|
90
|
-
"
|
|
91
|
-
"ibm/granite-3.0-
|
|
92
|
-
"ibm/granite-
|
|
93
|
-
"ibm/granite-
|
|
94
|
-
"
|
|
95
|
-
"meta/
|
|
96
|
-
"
|
|
97
|
-
"microsoft/
|
|
98
|
-
"microsoft/phi-3
|
|
99
|
-
"
|
|
100
|
-
"mistralai/
|
|
101
|
-
"mistralai/mistral-
|
|
102
|
-
"mistralai/mistral-large
|
|
103
|
-
"mistralai/
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"nvidia/
|
|
107
|
-
"nvidia/
|
|
108
|
-
"nvidia/llama-3.1-nemotron-
|
|
109
|
-
"nvidia/llama-3.1-nemotron-
|
|
110
|
-
"nvidia/llama-3.
|
|
111
|
-
"nvidia/llama-3.2-nemoretriever-
|
|
112
|
-
"nvidia/llama-3.2-
|
|
113
|
-
"nvidia/llama-3.2-nv-embedqa-1b-
|
|
114
|
-
"nvidia/llama-
|
|
115
|
-
"nvidia/llama-nemotron-embed-
|
|
116
|
-
"nvidia/
|
|
117
|
-
"nvidia/
|
|
118
|
-
"nvidia/
|
|
119
|
-
"nvidia/nemotron-4-340b-
|
|
120
|
-
"nvidia/nemotron-
|
|
121
|
-
"nvidia/
|
|
122
|
-
"nvidia/
|
|
123
|
-
"nvidia/nv-
|
|
124
|
-
"nvidia/nv-
|
|
125
|
-
"nvidia/nv-embedqa-
|
|
126
|
-
"nvidia/
|
|
127
|
-
"nvidia/
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"writer/palmyra-
|
|
131
|
-
"writer/palmyra-
|
|
132
|
-
"writer/palmyra-med-70b
|
|
133
|
-
"
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
*
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
.
|
|
149
|
-
.replace(
|
|
150
|
-
.replace(/\b
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
{
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
.filter((m) =>
|
|
242
|
-
.filter((m) =>
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
if (!
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
*
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
{
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
"
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* NVIDIA NIM Provider Extension
|
|
3
|
+
*
|
|
4
|
+
* Provides access to NVIDIA-hosted large models via integrate.api.nvidia.com.
|
|
5
|
+
* All models use NVIDIA's free credit system — requires NVIDIA_API_KEY.
|
|
6
|
+
* Get a free key at: https://build.nvidia.com
|
|
7
|
+
*
|
|
8
|
+
* Small models (< 70B) are filtered out to keep the list focused on useful
|
|
9
|
+
* chat/coding models. Non-chat models (embedding, speech-to-text, OCR,
|
|
10
|
+
* image-gen) are filtered by their modalities (output must be ["text"],
|
|
11
|
+
* input must include "text").
|
|
12
|
+
*
|
|
13
|
+
* Responds to global free-only filter for free/paid model filtering.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type {
|
|
17
|
+
ExtensionAPI,
|
|
18
|
+
ProviderModelConfig,
|
|
19
|
+
} from "@mariozechner/pi-coding-agent";
|
|
20
|
+
import {
|
|
21
|
+
applyHidden,
|
|
22
|
+
getNvidiaApiKey,
|
|
23
|
+
loadConfigFile,
|
|
24
|
+
PROVIDER_NVIDIA,
|
|
25
|
+
saveConfig,
|
|
26
|
+
} from "../../config.ts";
|
|
27
|
+
import {
|
|
28
|
+
BASE_URL_NVIDIA,
|
|
29
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
30
|
+
NVIDIA_MIN_SIZE_B,
|
|
31
|
+
URL_MODELS_DEV,
|
|
32
|
+
} from "../../constants.ts";
|
|
33
|
+
import { createLogger } from "../../lib/logger.ts";
|
|
34
|
+
import { isFreeModel, registerWithGlobalToggle } from "../../lib/registry.ts";
|
|
35
|
+
import type { ModelsDevModel, ModelsDevProvider } from "../../lib/types.ts";
|
|
36
|
+
import {
|
|
37
|
+
fetchWithRetry,
|
|
38
|
+
fetchWithTimeout,
|
|
39
|
+
isUsableModel,
|
|
40
|
+
} from "../../lib/util.ts";
|
|
41
|
+
import { createReRegister, enhanceWithCI } from "../../provider-helper.ts";
|
|
42
|
+
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// Non-chat model heuristics for models not in models.dev
|
|
45
|
+
// =============================================================================
|
|
46
|
+
|
|
47
|
+
const NVIDIA_NON_CHAT_PATTERNS: RegExp[] = [
|
|
48
|
+
/embed(?!.*instruct)/i,
|
|
49
|
+
/whisper/i,
|
|
50
|
+
/reward/i,
|
|
51
|
+
/ocr(?!.*instruct)/i,
|
|
52
|
+
/safety-guard|content-safety|nemoguard/i,
|
|
53
|
+
/retriever-parse|nemotron-parse(?!.*instruct)/i,
|
|
54
|
+
/detector/i,
|
|
55
|
+
/deplot/i,
|
|
56
|
+
/nvclip/i,
|
|
57
|
+
/vila$/i,
|
|
58
|
+
/neva(?!.*instruct)/i,
|
|
59
|
+
/translate/i,
|
|
60
|
+
/cosmos-reason/i,
|
|
61
|
+
/kosmos/i,
|
|
62
|
+
/bge-/i,
|
|
63
|
+
/arctic-embed/i,
|
|
64
|
+
/gliner/i,
|
|
65
|
+
/nv-embed/i,
|
|
66
|
+
/embedqa/i,
|
|
67
|
+
/embedcode/i,
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Models that appear in NVIDIA's /v1/models but return 404 "Function not found"
|
|
72
|
+
* on /v1/chat/completions. These are listed but not actually provisioned for
|
|
73
|
+
* hosted chat inference. Community-reported; add new IDs as they surface.
|
|
74
|
+
*
|
|
75
|
+
* Users can also hide individual models via hidden_models in ~/.pi/free.json.
|
|
76
|
+
*/
|
|
77
|
+
const NVIDIA_KNOWN_404_MODELS: ReadonlySet<string> = new Set([
|
|
78
|
+
"01-ai/yi-large",
|
|
79
|
+
"adept/fuyu-8b",
|
|
80
|
+
"ai21labs/jamba-1.5-large-instruct",
|
|
81
|
+
"aisingapore/sea-lion-7b-instruct",
|
|
82
|
+
"baai/bge-m3",
|
|
83
|
+
"bigcode/starcoder2-15b",
|
|
84
|
+
"databricks/dbrx-instruct",
|
|
85
|
+
"deepseek-ai/deepseek-coder-6.7b-instruct",
|
|
86
|
+
"google/codegemma-1.1-7b",
|
|
87
|
+
"google/codegemma-7b",
|
|
88
|
+
"google/deplot",
|
|
89
|
+
"google/gemma-2b",
|
|
90
|
+
"google/recurrentgemma-2b",
|
|
91
|
+
"ibm/granite-3.0-3b-a800m-instruct",
|
|
92
|
+
"ibm/granite-3.0-8b-instruct",
|
|
93
|
+
"ibm/granite-34b-code-instruct",
|
|
94
|
+
"ibm/granite-8b-code-instruct",
|
|
95
|
+
"meta/codellama-70b",
|
|
96
|
+
"meta/llama2-70b",
|
|
97
|
+
"microsoft/kosmos-2",
|
|
98
|
+
"microsoft/phi-3-vision-128k-instruct",
|
|
99
|
+
"microsoft/phi-3.5-moe-instruct",
|
|
100
|
+
"mistralai/codestral-22b-instruct-v0.1",
|
|
101
|
+
"mistralai/mistral-7b-instruct-v0.3",
|
|
102
|
+
"mistralai/mistral-large",
|
|
103
|
+
"mistralai/mistral-large-2-instruct",
|
|
104
|
+
"mistralai/mixtral-8x22b-v0.1",
|
|
105
|
+
"nv-mistralai/mistral-nemo-12b-instruct",
|
|
106
|
+
"nvidia/cosmos-reason2-8b",
|
|
107
|
+
"nvidia/embed-qa-4",
|
|
108
|
+
"nvidia/llama-3.1-nemotron-51b-instruct",
|
|
109
|
+
"nvidia/llama-3.1-nemotron-70b-instruct",
|
|
110
|
+
"nvidia/llama-3.1-nemotron-ultra-253b-v1",
|
|
111
|
+
"nvidia/llama-3.2-nemoretriever-1b-vlm-embed-v1",
|
|
112
|
+
"nvidia/llama-3.2-nemoretriever-300m-embed-v1",
|
|
113
|
+
"nvidia/llama-3.2-nv-embedqa-1b-v1",
|
|
114
|
+
"nvidia/llama-3.2-nv-embedqa-1b-v2",
|
|
115
|
+
"nvidia/llama-nemotron-embed-1b-v2",
|
|
116
|
+
"nvidia/llama-nemotron-embed-vl-1b-v2",
|
|
117
|
+
"nvidia/llama3-chatqa-1.5-70b",
|
|
118
|
+
"nvidia/mistral-nemo-minitron-8b-8k-instruct",
|
|
119
|
+
"nvidia/nemotron-4-340b-instruct",
|
|
120
|
+
"nvidia/nemotron-4-340b-reward",
|
|
121
|
+
"nvidia/nemotron-nano-3-30b-a3b",
|
|
122
|
+
"nvidia/neva-22b",
|
|
123
|
+
"nvidia/nv-embed-v1",
|
|
124
|
+
"nvidia/nv-embedcode-7b-v1",
|
|
125
|
+
"nvidia/nv-embedqa-e5-v5",
|
|
126
|
+
"nvidia/nv-embedqa-mistral-7b-v2",
|
|
127
|
+
"nvidia/nvclip",
|
|
128
|
+
"nvidia/riva-translate-4b-instruct",
|
|
129
|
+
"snowflake/arctic-embed-l",
|
|
130
|
+
"writer/palmyra-creative-122b",
|
|
131
|
+
"writer/palmyra-fin-70b-32k",
|
|
132
|
+
"writer/palmyra-med-70b",
|
|
133
|
+
"writer/palmyra-med-70b-32k",
|
|
134
|
+
"zyphra/zamba2-7b-instruct",
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Infer model metadata from a NVIDIA model ID for models not present in
|
|
139
|
+
* models.dev. Returns null if the ID matches known non-chat patterns.
|
|
140
|
+
*/
|
|
141
|
+
function inferModelFromId(id: string): ModelsDevModel | null {
|
|
142
|
+
for (const pattern of NVIDIA_NON_CHAT_PATTERNS) {
|
|
143
|
+
if (pattern.test(id)) return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const name = id
|
|
147
|
+
.split("/")
|
|
148
|
+
.pop()!
|
|
149
|
+
.replace(/-/g, " ")
|
|
150
|
+
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
151
|
+
.replace(/\b(\d+(?:\.\d+)?)b\b/gi, "$1B");
|
|
152
|
+
|
|
153
|
+
const hasVision = /vision|multimodal|vl/i.test(id);
|
|
154
|
+
const hasReasoning = /reason|r1|thinking/i.test(id);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
id,
|
|
158
|
+
name,
|
|
159
|
+
reasoning: hasReasoning,
|
|
160
|
+
limit: { context: 128_000, output: 4096 },
|
|
161
|
+
modalities: {
|
|
162
|
+
input: hasVision ? ["text", "image"] : ["text"],
|
|
163
|
+
output: ["text"],
|
|
164
|
+
},
|
|
165
|
+
cost: { input: 0, output: 0 },
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// Fetch + map
|
|
171
|
+
// =============================================================================
|
|
172
|
+
|
|
173
|
+
async function fetchNvidiaModels(
|
|
174
|
+
apiKey?: string,
|
|
175
|
+
): Promise<ProviderModelConfig[]> {
|
|
176
|
+
// ── 1. Query NVIDIA's actual API (source of truth) ─────────────────
|
|
177
|
+
let apiModelIds = new Set<string>();
|
|
178
|
+
if (apiKey) {
|
|
179
|
+
try {
|
|
180
|
+
const response = await fetchWithRetry(
|
|
181
|
+
`${BASE_URL_NVIDIA}/models`,
|
|
182
|
+
{
|
|
183
|
+
headers: {
|
|
184
|
+
Authorization: `Bearer ${apiKey}`,
|
|
185
|
+
"User-Agent": "pi-free-providers",
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
3,
|
|
189
|
+
1000,
|
|
190
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
191
|
+
);
|
|
192
|
+
if (response.ok) {
|
|
193
|
+
const json = (await response.json()) as {
|
|
194
|
+
data?: Array<{ id: string }>;
|
|
195
|
+
};
|
|
196
|
+
if (json.data) {
|
|
197
|
+
apiModelIds = new Set(json.data.map((m) => m.id));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error("[nvidia] Failed to fetch models from NVIDIA API", error);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ── 2. Fetch models.dev for rich metadata (cost, limits, etc.) ─────
|
|
206
|
+
const devModels = new Map<string, ModelsDevModel>();
|
|
207
|
+
try {
|
|
208
|
+
const response = await fetchWithRetry(
|
|
209
|
+
URL_MODELS_DEV,
|
|
210
|
+
{
|
|
211
|
+
headers: { "User-Agent": "pi-free-providers" },
|
|
212
|
+
},
|
|
213
|
+
3,
|
|
214
|
+
1000,
|
|
215
|
+
DEFAULT_FETCH_TIMEOUT_MS,
|
|
216
|
+
);
|
|
217
|
+
if (response.ok) {
|
|
218
|
+
const json = (await response.json()) as Record<string, ModelsDevProvider>;
|
|
219
|
+
const provider = Object.values(json).find((p) => p?.id === "nvidia");
|
|
220
|
+
if (provider?.models) {
|
|
221
|
+
for (const m of Object.values(provider.models)) {
|
|
222
|
+
devModels.set(m.id, m);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error("[nvidia] Failed to fetch models.dev", error);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── 3. Build unified list (NVIDIA API wins; fallback to models.dev) ─
|
|
231
|
+
const modelIds =
|
|
232
|
+
apiModelIds.size > 0 ? [...apiModelIds] : [...devModels.keys()];
|
|
233
|
+
|
|
234
|
+
const result = applyHidden(
|
|
235
|
+
modelIds
|
|
236
|
+
.map((id) => {
|
|
237
|
+
const dev = devModels.get(id);
|
|
238
|
+
if (dev) return dev;
|
|
239
|
+
return inferModelFromId(id);
|
|
240
|
+
})
|
|
241
|
+
.filter((m): m is ModelsDevModel => m !== null)
|
|
242
|
+
.filter((m) => isUsableModel(m.id, NVIDIA_MIN_SIZE_B))
|
|
243
|
+
.filter((m) => {
|
|
244
|
+
const modalities = m.modalities;
|
|
245
|
+
if (modalities) {
|
|
246
|
+
const output = modalities.output ?? [];
|
|
247
|
+
const input = modalities.input ?? [];
|
|
248
|
+
if (!output.includes("text")) return false;
|
|
249
|
+
if (!input.includes("text")) return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
})
|
|
253
|
+
// Filter out known 404 models (listed but not provisioned for chat)
|
|
254
|
+
.filter((m) => {
|
|
255
|
+
if (NVIDIA_KNOWN_404_MODELS.has(m.id)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
})
|
|
260
|
+
// NVIDIA is freemium — all models are usable with free credits.
|
|
261
|
+
// No cost filtering applied.
|
|
262
|
+
.map(
|
|
263
|
+
(m): ProviderModelConfig => ({
|
|
264
|
+
id: m.id,
|
|
265
|
+
name: m.name,
|
|
266
|
+
reasoning: m.reasoning,
|
|
267
|
+
input: m.modalities?.input?.includes("image")
|
|
268
|
+
? ["text", "image"]
|
|
269
|
+
: ["text"],
|
|
270
|
+
cost: {
|
|
271
|
+
input: m.cost?.input ?? 0,
|
|
272
|
+
output: m.cost?.output ?? 0,
|
|
273
|
+
cacheRead: m.cost?.cache_read ?? 0,
|
|
274
|
+
cacheWrite: m.cost?.cache_write ?? 0,
|
|
275
|
+
},
|
|
276
|
+
contextWindow: m.limit.context,
|
|
277
|
+
maxTokens: m.limit.output,
|
|
278
|
+
}),
|
|
279
|
+
),
|
|
280
|
+
PROVIDER_NVIDIA,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// =============================================================================
|
|
287
|
+
// Extension Entry Point
|
|
288
|
+
// =============================================================================
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Probe a single NVIDIA model with a minimal chat request.
|
|
292
|
+
* Returns true if the model is routable (not 404), false if it 404s.
|
|
293
|
+
*/
|
|
294
|
+
async function probeNvidiaModel(
|
|
295
|
+
apiKey: string,
|
|
296
|
+
modelId: string,
|
|
297
|
+
): Promise<boolean> {
|
|
298
|
+
try {
|
|
299
|
+
const response = await fetchWithTimeout(
|
|
300
|
+
`${BASE_URL_NVIDIA}/chat/completions`,
|
|
301
|
+
{
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: {
|
|
304
|
+
Authorization: `Bearer ${apiKey}`,
|
|
305
|
+
"Content-Type": "application/json",
|
|
306
|
+
"User-Agent": "pi-free-providers",
|
|
307
|
+
},
|
|
308
|
+
body: JSON.stringify({
|
|
309
|
+
model: modelId,
|
|
310
|
+
messages: [{ role: "user", content: "hi" }],
|
|
311
|
+
max_tokens: 1,
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
10000, // 10 second timeout
|
|
315
|
+
);
|
|
316
|
+
// 404 = function not found (model not provisioned)
|
|
317
|
+
// 200/400/401/etc = at least routable
|
|
318
|
+
return response.status !== 404;
|
|
319
|
+
} catch {
|
|
320
|
+
return true; // Network errors / timeouts are not "model not found"
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const _nvidiaLogger = createLogger("nvidia");
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Run probe on a list of models and auto-hide 404s.
|
|
328
|
+
* Shared between the /probe-nvidia command and auto-probe on session_start.
|
|
329
|
+
*/
|
|
330
|
+
async function runNvidiaProbe(
|
|
331
|
+
apiKey: string,
|
|
332
|
+
modelsToTest: ProviderModelConfig[],
|
|
333
|
+
stored: { free: ProviderModelConfig[]; all: ProviderModelConfig[] },
|
|
334
|
+
reRegister: (models: ProviderModelConfig[]) => void,
|
|
335
|
+
): Promise<void> {
|
|
336
|
+
const notFound: string[] = [];
|
|
337
|
+
const batchSize = 5;
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < modelsToTest.length; i += batchSize) {
|
|
340
|
+
const batch = modelsToTest.slice(i, i + batchSize);
|
|
341
|
+
const results = await Promise.all(
|
|
342
|
+
batch.map(async (m) => {
|
|
343
|
+
const ok = await probeNvidiaModel(apiKey, m.id);
|
|
344
|
+
return { id: m.id, ok };
|
|
345
|
+
}),
|
|
346
|
+
);
|
|
347
|
+
for (const r of results) {
|
|
348
|
+
if (!r.ok) notFound.push(r.id);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (notFound.length === 0) {
|
|
353
|
+
_nvidiaLogger.info("Auto-probe: all NVIDIA models are routable");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Auto-hide 404 models in config (provider-scoped)
|
|
358
|
+
const cfg = loadConfigFile();
|
|
359
|
+
const existingHidden = new Set(cfg.hidden_models ?? []);
|
|
360
|
+
for (const id of notFound) existingHidden.add(`${PROVIDER_NVIDIA}/${id}`);
|
|
361
|
+
saveConfig({ hidden_models: Array.from(existingHidden) });
|
|
362
|
+
|
|
363
|
+
// Re-register so hidden models disappear immediately
|
|
364
|
+
const filtered = await fetchNvidiaModels(apiKey);
|
|
365
|
+
stored.free = filtered;
|
|
366
|
+
stored.all = filtered;
|
|
367
|
+
reRegister(filtered);
|
|
368
|
+
|
|
369
|
+
_nvidiaLogger.info(
|
|
370
|
+
`Auto-probe: found ${notFound.length} broken models (auto-hidden)`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export default async function (pi: ExtensionAPI) {
|
|
375
|
+
const apiKey = getNvidiaApiKey();
|
|
376
|
+
const hasKey = !!apiKey;
|
|
377
|
+
|
|
378
|
+
let allModels: ProviderModelConfig[] = [];
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
allModels = await fetchNvidiaModels(apiKey);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
console.error("[nvidia] Failed to fetch models at startup", error);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Store both sets for global toggle using consistent isFreeModel helper
|
|
388
|
+
// NVIDIA uses Route B (name-based): only models with "free" in name are marked free
|
|
389
|
+
const freeModels = allModels.filter((m) =>
|
|
390
|
+
isFreeModel({ ...m, provider: PROVIDER_NVIDIA }),
|
|
391
|
+
);
|
|
392
|
+
const stored = { free: freeModels, all: allModels };
|
|
393
|
+
|
|
394
|
+
// Create re-register function
|
|
395
|
+
const reRegister = createReRegister(pi, {
|
|
396
|
+
providerId: PROVIDER_NVIDIA,
|
|
397
|
+
baseUrl: BASE_URL_NVIDIA,
|
|
398
|
+
apiKey: apiKey || "NVIDIA_API_KEY",
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Register with global toggle system
|
|
402
|
+
registerWithGlobalToggle(PROVIDER_NVIDIA, stored, reRegister, hasKey);
|
|
403
|
+
|
|
404
|
+
// Register initial models (global toggle will apply filter if needed)
|
|
405
|
+
const initialModels = allModels;
|
|
406
|
+
pi.registerProvider(PROVIDER_NVIDIA, {
|
|
407
|
+
baseUrl: BASE_URL_NVIDIA,
|
|
408
|
+
apiKey: apiKey || "NVIDIA_API_KEY",
|
|
409
|
+
api: "openai-completions" as const,
|
|
410
|
+
authHeader: true,
|
|
411
|
+
headers: {
|
|
412
|
+
"User-Agent": "pi-free-providers",
|
|
413
|
+
},
|
|
414
|
+
models: enhanceWithCI(initialModels),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// ── Lazy auto-probe on first session_start ──────────────────────
|
|
418
|
+
let _autoProbeDone = false;
|
|
419
|
+
pi.on("session_start", async () => {
|
|
420
|
+
if (_autoProbeDone || !apiKey) return;
|
|
421
|
+
_autoProbeDone = true;
|
|
422
|
+
_nvidiaLogger.info("Starting lazy auto-probe of NVIDIA models...");
|
|
423
|
+
runNvidiaProbe(apiKey, allModels, stored, reRegister).catch((err) => {
|
|
424
|
+
_nvidiaLogger.warn("Auto-probe failed", {
|
|
425
|
+
error: err instanceof Error ? err.message : String(err),
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// ── Probe command: test all registered models for 404s ─────────────
|
|
431
|
+
pi.registerCommand("probe-nvidia", {
|
|
432
|
+
description: "Test all NVIDIA models for 404 'Function not found' errors",
|
|
433
|
+
handler: async (_args, ctx) => {
|
|
434
|
+
if (!apiKey) {
|
|
435
|
+
ctx.ui.notify("NVIDIA_API_KEY not set", "error");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const modelsToTest = allModels;
|
|
440
|
+
ctx.ui.notify(`Probing ${modelsToTest.length} NVIDIA models…`, "info");
|
|
441
|
+
|
|
442
|
+
await runNvidiaProbe(apiKey, modelsToTest, stored, reRegister);
|
|
443
|
+
|
|
444
|
+
// Check if any were hidden (re-read config)
|
|
445
|
+
const cfgAfter = loadConfigFile();
|
|
446
|
+
const newHidden = (cfgAfter.hidden_models ?? []).filter((h) =>
|
|
447
|
+
h.startsWith(`${PROVIDER_NVIDIA}/`),
|
|
448
|
+
);
|
|
449
|
+
if (newHidden.length > 0) {
|
|
450
|
+
ctx.ui.notify(
|
|
451
|
+
`Found ${newHidden.length} broken models (auto-hidden):\n${newHidden.join("\n")}`,
|
|
452
|
+
"warning",
|
|
453
|
+
);
|
|
454
|
+
} else {
|
|
455
|
+
ctx.ui.notify("All NVIDIA models are routable ✅", "info");
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// ── Status bar for provider selection ─────────────────────────
|
|
461
|
+
|
|
462
|
+
pi.on("model_select", (_event, ctx) => {
|
|
463
|
+
if (_event.model?.provider !== PROVIDER_NVIDIA) {
|
|
464
|
+
ctx.ui.setStatus(`${PROVIDER_NVIDIA}-status`, undefined);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const count = allModels.length;
|
|
469
|
+
ctx.ui.setStatus(
|
|
470
|
+
`${PROVIDER_NVIDIA}-status`,
|
|
471
|
+
`nvidia: ${count} models (freemium)`,
|
|
472
|
+
);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Registration complete - models registered silently (use LOG_LEVEL=info to see details)
|
|
476
|
+
}
|