pi-tensorix-provider 1.0.3 → 1.0.5

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/AGENTS.md CHANGED
@@ -7,6 +7,7 @@ The following files are **idempotent** and regenerated by `scripts/update-models
7
7
  | File | Why it's auto-generated |
8
8
  |------|------------------------|
9
9
  | `models.json` | Built from the provider API. `update-models.js` fetches models, preserves curated data for known IDs, and writes this file. |
10
+ | `deprecated-models.json` | Graveyard for models the API delisted. update-models.js stamps them with deprecatedAt and pi keeps serving them for a 2-week grace period, then evicts them. Never edit by hand. |
10
11
  | `README.md` (model table) | The table under `## Available Models` is replaced in-place by `update-models.js` after merging base models → patch → custom models. |
11
12
 
12
13
  ## Correct Files to Edit
package/README.md CHANGED
@@ -27,35 +27,24 @@ _32 models — DeepSeek, Kimi, GLM, Qwen, MiniMax, Llama — all **free** for [p
27
27
  | Model | Context | Vision | Reasoning | Input $/M | Output $/M |
28
28
  |-------|---------|--------|-----------|-----------|------------|
29
29
  | Chatterbox Turbo | 131K | ❌ | ❌ | **Free** | **Free** |
30
- | Deepseek Chat V3 0324 | 131K | ❌ | ❌ | **Free** | **Free** |
31
30
  | Deepseek Chat V3.1 | 131K | ❌ | ❌ | **Free** | **Free** |
32
31
  | Deepseek R1 0528 | 131K | ❌ | ✅ | **Free** | **Free** |
33
32
  | Deepseek V3.2 | 164K | ❌ | ✅ | **Free** | **Free** |
34
33
  | Deepseek V4 Flash | 1.0M | ❌ | ✅ | **Free** | **Free** |
35
- | Deepseek V4 Flash Backup | 131K | ❌ | ❌ | **Free** | **Free** |
36
34
  | Deepseek V4 Pro | 1.0M | ❌ | ✅ | **Free** | **Free** |
37
- | Glm 4.6 | 131K | ❌ | ❌ | **Free** | **Free** |
38
- | Glm 4.7 | 203K | ❌ | ✅ | **Free** | **Free** |
39
35
  | Glm 5 | 203K | ❌ | ✅ | **Free** | **Free** |
40
36
  | Glm 5 Turbo | 203K | ❌ | ✅ | **Free** | **Free** |
41
37
  | Glm 5.1 | 203K | ❌ | ✅ | **Free** | **Free** |
42
38
  | Glm 5.2 | 131K | ❌ | ❌ | **Free** | **Free** |
43
39
  | Glm 5v Turbo | 203K | ✅ | ✅ | **Free** | **Free** |
44
40
  | Gpt Oss 120b | 131K | ❌ | ✅ | **Free** | **Free** |
45
- | Gpt Oss 20b | 131K | ❌ | ✅ | **Free** | **Free** |
46
41
  | Kimi K2.5 | 262K | ✅ | ✅ | **Free** | **Free** |
47
42
  | Kimi K2.6 | 262K | ✅ | ✅ | **Free** | **Free** |
48
- | Kimi K2.6 | 262K | ✅ | ✅ | **Free** | **Free** |
49
43
  | Kimi K2.7 Code | 131K | ❌ | ❌ | **Free** | **Free** |
50
- | Llama 3.3 70b Instruct | 131K | ❌ | ❌ | **Free** | **Free** |
51
- | Llama 4 Maverick | 1.0M | ❌ | ❌ | **Free** | **Free** |
52
- | Minimax M2 | 131K | ❌ | ❌ | **Free** | **Free** |
53
- | Minimax M2.1 | 131K | ❌ | ❌ | **Free** | **Free** |
44
+ | Kimi K3 | 131K | ❌ | ❌ | **Free** | **Free** |
54
45
  | Minimax M2.5 | 205K | ❌ | ✅ | **Free** | **Free** |
55
- | Minimax M2.7 | 205K | ❌ | ✅ | **Free** | **Free** |
56
46
  | Minimax M3 | 131K | ❌ | ❌ | **Free** | **Free** |
57
47
  | Nemotron 3 Super 120b A12b | 4K | ❌ | ❌ | **Free** | **Free** |
58
- | Qwen 2.5 72b Instruct | 131K | ❌ | ❌ | **Free** | **Free** |
59
48
  | Qwen3 235b A22b 2507 | 262K | ❌ | ✅ | **Free** | **Free** |
60
49
  | Qwen3 Coder 30b A3b Instruct | 262K | ❌ | ✅ | **Free** | **Free** |
61
50
  | Qwen3 Vl 235b A22b Instruct | 262K | ✅ | ✅ | **Free** | **Free** |
@@ -0,0 +1 @@
1
+ {}
package/index.ts CHANGED
@@ -45,6 +45,7 @@ import { getAgentDir, type ExtensionAPI, type ModelRegistry } from "@earendil-wo
45
45
  import modelsData from "./models.json" with { type: "json" };
46
46
  import customModelsData from "./custom-models.json" with { type: "json" };
47
47
  import patchData from "./patch.json" with { type: "json" };
48
+ import deprecatedData from "./deprecated-models.json" with { type: "json" };
48
49
  import fs from "fs";
49
50
  import path from "path";
50
51
 
@@ -54,7 +55,7 @@ interface JsonModel {
54
55
  id: string;
55
56
  name: string;
56
57
  reasoning: boolean;
57
- input: string[];
58
+ input: ("text" | "image")[];
58
59
  cost: {
59
60
  input: number;
60
61
  output: number;
@@ -76,7 +77,7 @@ interface JsonModel {
76
77
  interface PatchEntry {
77
78
  name?: string;
78
79
  reasoning?: boolean;
79
- input?: string[];
80
+ input?: ("text" | "image")[];
80
81
  cost?: {
81
82
  input?: number;
82
83
  output?: number;
@@ -129,7 +130,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
129
130
  function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
130
131
  const modelMap = new Map<string, JsonModel>();
131
132
 
132
- for (const model of base) {
133
+ // Seed with the base list plus grace-period deprecated models so patch.json
134
+ // entries apply to deprecated models exactly as while the model was live
135
+ // (withDeprecated keeps live data on id conflicts).
136
+ for (const model of withDeprecated(base)) {
133
137
  modelMap.set(model.id, model);
134
138
  }
135
139
 
@@ -257,6 +261,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
257
261
  return result;
258
262
  }
259
263
 
264
+ // Grace period for delisted models. When the provider API stops listing a
265
+ // model, update-models.js moves its last-known definition into
266
+ // deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
267
+ // For 14 days the model keeps working here so in-flight sessions and saved
268
+ // model settings do not break; afterwards it is evicted permanently.
269
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
270
+
271
+ // Grace-period deprecated models with deprecation metadata stripped.
272
+ function activeDeprecatedModels(): JsonModel[] {
273
+ const now = Date.now();
274
+ const result: JsonModel[] = [];
275
+ for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
276
+ if (!entry?.id) continue;
277
+ const removedAt = Date.parse(entry.deprecatedAt ?? "");
278
+ if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
279
+ const model = { ...entry } as JsonModel & { deprecatedAt?: string };
280
+ delete model.deprecatedAt;
281
+ result.push(model);
282
+ }
283
+ return result;
284
+ }
285
+
286
+ // Append grace-period deprecated models the list does not already have (live data wins).
287
+ function withDeprecated(models: JsonModel[]): JsonModel[] {
288
+ const seen = new Set(models.map((m) => m.id));
289
+ const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
290
+ return extras.length > 0 ? [...models, ...extras] : models;
291
+ }
292
+
260
293
  function loadStaleModels(embeddedModels: JsonModel[]): JsonModel[] {
261
294
  const cached = loadCachedModels();
262
295
  if (!cached || cached.length === 0) return embeddedModels;
package/models.json CHANGED
@@ -15,38 +15,6 @@
15
15
  "contextWindow": 131072,
16
16
  "maxTokens": 131072
17
17
  },
18
- {
19
- "id": "deepseek-v4-flash-backup",
20
- "name": "Deepseek V4 Flash Backup",
21
- "reasoning": false,
22
- "input": [
23
- "text"
24
- ],
25
- "cost": {
26
- "input": 0,
27
- "output": 0,
28
- "cacheRead": 0,
29
- "cacheWrite": 0
30
- },
31
- "contextWindow": 131072,
32
- "maxTokens": 131072
33
- },
34
- {
35
- "id": "deepseek/deepseek-chat-v3-0324",
36
- "name": "Deepseek Chat V3 0324",
37
- "reasoning": false,
38
- "input": [
39
- "text"
40
- ],
41
- "cost": {
42
- "input": 0,
43
- "output": 0,
44
- "cacheRead": 0,
45
- "cacheWrite": 0
46
- },
47
- "contextWindow": 131072,
48
- "maxTokens": 131072
49
- },
50
18
  {
51
19
  "id": "deepseek/deepseek-chat-v3.1",
52
20
  "name": "Deepseek Chat V3.1",
@@ -127,70 +95,6 @@
127
95
  "contextWindow": 131072,
128
96
  "maxTokens": 131072
129
97
  },
130
- {
131
- "id": "meta-llama/llama-3.3-70b-instruct",
132
- "name": "Llama 3.3 70b Instruct",
133
- "reasoning": false,
134
- "input": [
135
- "text"
136
- ],
137
- "cost": {
138
- "input": 0,
139
- "output": 0,
140
- "cacheRead": 0,
141
- "cacheWrite": 0
142
- },
143
- "contextWindow": 131072,
144
- "maxTokens": 131072
145
- },
146
- {
147
- "id": "meta-llama/llama-4-maverick",
148
- "name": "Llama 4 Maverick",
149
- "reasoning": false,
150
- "input": [
151
- "text"
152
- ],
153
- "cost": {
154
- "input": 0,
155
- "output": 0,
156
- "cacheRead": 0,
157
- "cacheWrite": 0
158
- },
159
- "contextWindow": 131072,
160
- "maxTokens": 131072
161
- },
162
- {
163
- "id": "minimax/minimax-m2",
164
- "name": "Minimax M2",
165
- "reasoning": false,
166
- "input": [
167
- "text"
168
- ],
169
- "cost": {
170
- "input": 0,
171
- "output": 0,
172
- "cacheRead": 0,
173
- "cacheWrite": 0
174
- },
175
- "contextWindow": 131072,
176
- "maxTokens": 131072
177
- },
178
- {
179
- "id": "minimax/minimax-m2.1",
180
- "name": "Minimax M2.1",
181
- "reasoning": false,
182
- "input": [
183
- "text"
184
- ],
185
- "cost": {
186
- "input": 0,
187
- "output": 0,
188
- "cacheRead": 0,
189
- "cacheWrite": 0
190
- },
191
- "contextWindow": 131072,
192
- "maxTokens": 131072
193
- },
194
98
  {
195
99
  "id": "minimax/minimax-m2.5",
196
100
  "name": "Minimax M2.5",
@@ -207,22 +111,6 @@
207
111
  "contextWindow": 131072,
208
112
  "maxTokens": 131072
209
113
  },
210
- {
211
- "id": "minimax/minimax-m2.7",
212
- "name": "Minimax M2.7",
213
- "reasoning": false,
214
- "input": [
215
- "text"
216
- ],
217
- "cost": {
218
- "input": 0,
219
- "output": 0,
220
- "cacheRead": 0,
221
- "cacheWrite": 0
222
- },
223
- "contextWindow": 131072,
224
- "maxTokens": 131072
225
- },
226
114
  {
227
115
  "id": "minimax/minimax-m3",
228
116
  "name": "Minimax M3",
@@ -272,8 +160,8 @@
272
160
  "maxTokens": 131072
273
161
  },
274
162
  {
275
- "id": "moonshotai/Kimi-K2.6",
276
- "name": "Kimi K2.6",
163
+ "id": "moonshotai/kimi-k2.7-code",
164
+ "name": "Kimi K2.7 Code",
277
165
  "reasoning": false,
278
166
  "input": [
279
167
  "text"
@@ -288,8 +176,8 @@
288
176
  "maxTokens": 131072
289
177
  },
290
178
  {
291
- "id": "moonshotai/kimi-k2.7-code",
292
- "name": "Kimi K2.7 Code",
179
+ "id": "moonshotai/kimi-k3",
180
+ "name": "Kimi K3",
293
181
  "reasoning": false,
294
182
  "input": [
295
183
  "text"
@@ -335,38 +223,6 @@
335
223
  "contextWindow": 131072,
336
224
  "maxTokens": 131072
337
225
  },
338
- {
339
- "id": "openai/gpt-oss-20b",
340
- "name": "Gpt Oss 20b",
341
- "reasoning": false,
342
- "input": [
343
- "text"
344
- ],
345
- "cost": {
346
- "input": 0,
347
- "output": 0,
348
- "cacheRead": 0,
349
- "cacheWrite": 0
350
- },
351
- "contextWindow": 131072,
352
- "maxTokens": 131072
353
- },
354
- {
355
- "id": "qwen/qwen-2.5-72b-instruct",
356
- "name": "Qwen 2.5 72b Instruct",
357
- "reasoning": false,
358
- "input": [
359
- "text"
360
- ],
361
- "cost": {
362
- "input": 0,
363
- "output": 0,
364
- "cacheRead": 0,
365
- "cacheWrite": 0
366
- },
367
- "contextWindow": 131072,
368
- "maxTokens": 131072
369
- },
370
226
  {
371
227
  "id": "qwen/qwen3-235b-a22b-2507",
372
228
  "name": "Qwen3 235b A22b 2507",
@@ -447,38 +303,6 @@
447
303
  "contextWindow": 131072,
448
304
  "maxTokens": 131072
449
305
  },
450
- {
451
- "id": "z-ai/glm-4.6",
452
- "name": "Glm 4.6",
453
- "reasoning": false,
454
- "input": [
455
- "text"
456
- ],
457
- "cost": {
458
- "input": 0,
459
- "output": 0,
460
- "cacheRead": 0,
461
- "cacheWrite": 0
462
- },
463
- "contextWindow": 131072,
464
- "maxTokens": 131072
465
- },
466
- {
467
- "id": "z-ai/glm-4.7",
468
- "name": "Glm 4.7",
469
- "reasoning": false,
470
- "input": [
471
- "text"
472
- ],
473
- "cost": {
474
- "input": 0,
475
- "output": 0,
476
- "cacheRead": 0,
477
- "cacheWrite": 0
478
- },
479
- "contextWindow": 131072,
480
- "maxTokens": 131072
481
- },
482
306
  {
483
307
  "id": "z-ai/glm-5",
484
308
  "name": "Glm 5",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-tensorix-provider",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Tensorix provider extension for pi - Access DeepSeek, GLM, Kimi, Qwen, MiniMax, Llama, and more through the Tensorix API",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -198,6 +198,67 @@ function cleanModelForJson(model) {
198
198
 
199
199
  // ─── Main ──────────────────────────────────────────────────────────────────────
200
200
 
201
+ // Grace period for delisted models: update-models.js moves models the API no
202
+ // longer lists into deprecated-models.json (stamped with deprecatedAt) instead
203
+ // of dropping them; the runtime appends them back so sessions and saved model
204
+ // settings keep working, and after 14 days they are evicted permanently.
205
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
206
+
207
+ /**
208
+ * Reconcile deprecated-models.json against the freshly fetched model list.
209
+ * - in old models.json but not the API: moved into the deprecated file
210
+ * (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
211
+ * - back in the API: resurrected (dropped from the deprecated file)
212
+ * - deprecatedAt older than 14 days: evicted permanently
213
+ * Must run BEFORE the new models.json is written; it reads the old file itself.
214
+ */
215
+ function updateDeprecatedModels(modelsJsonPath, newModels) {
216
+ const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
217
+
218
+ let oldModels = [];
219
+ try {
220
+ const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
221
+ if (Array.isArray(parsed)) oldModels = parsed;
222
+ } catch { /* first run: no previous models.json */ }
223
+
224
+ let deprecated = {};
225
+ try {
226
+ const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
227
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
228
+ } catch { /* no graveyard yet */ }
229
+
230
+ const currentIds = new Set(newModels.map(m => m.id));
231
+ const now = new Date().toISOString();
232
+ const added = [];
233
+ const resurrected = [];
234
+ const evicted = [];
235
+
236
+ for (const old of oldModels) {
237
+ if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
238
+ deprecated[old.id] = { ...old, deprecatedAt: now };
239
+ added.push(old.id);
240
+ }
241
+ }
242
+
243
+ for (const [id, entry] of Object.entries(deprecated)) {
244
+ if (currentIds.has(id)) {
245
+ delete deprecated[id];
246
+ resurrected.push(id);
247
+ continue;
248
+ }
249
+ const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
250
+ if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
251
+ delete deprecated[id];
252
+ evicted.push(id);
253
+ }
254
+ }
255
+
256
+ if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
257
+ fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
258
+ console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
259
+ }
260
+ }
261
+
201
262
  async function main() {
202
263
  console.log(`Fetching models from ${MODELS_API_URL}...`);
203
264
 
@@ -244,6 +305,8 @@ async function main() {
244
305
 
245
306
  // Update models.json — API-derived model list with curated specs preserved
246
307
  const cleanModels = apiTransformed.map(cleanModelForJson);
308
+ // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
309
+ updateDeprecatedModels(MODELS_JSON_PATH, cleanModels);
247
310
  fs.writeFileSync(MODELS_JSON_PATH, JSON.stringify(cleanModels, null, 2) + '\n');
248
311
  console.log('✓ Updated models.json (API model list with curated specs)');
249
312