pi-parasail-provider 1.0.4 → 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
@@ -72,7 +72,6 @@ Get your API key at [parasail.io](https://parasail.io/).
72
72
  |-------|---------|-----------|-------|------------|-----------|------------|-----------|
73
73
  | Cydonia 24B v4.1 | 131K | ❌ | Text | 16K | $0.30 | $0.50 | $0.15 |
74
74
  | DeepSeek V4 Flash | 1M | ✅ | Text | 384K | $0.14 | $0.28 | $0.07 |
75
- | Deepseek V4 Flash H100 | 1M | ❌ | Text | 16K | Free | Free | Free |
76
75
  | DeepSeek V4 Pro | 1M | ✅ | Text | 384K | $1.74 | $3.48 | $0.10 |
77
76
  | Gemma 3 27B | 131K | ❌ | Text + Image | 16K | $0.08 | $0.45 | $0.04 |
78
77
  | Gemma 4 26B (A4B) | 262K | ❌ | Text + Image | 16K | $0.13 | $0.40 | $0.05 |
@@ -85,13 +84,16 @@ Get your API key at [parasail.io](https://parasail.io/).
85
84
  | GPT-OSS 20B | 131K | ❌ | Text | 16K | $0.03 | $0.15 | $0.02 |
86
85
  | Kimi K2.6 | 262K | ✅ | Text + Image | 16K | $0.75 | $3.50 | $0.16 |
87
86
  | Kimi K27 Code | 262K | ❌ | Text | 16K | $0.76 | $3.50 | $0.16 |
87
+ | Kimi K3 | 1M | ❌ | Text | 16K | $3.00 | $15.00 | $0.30 |
88
+ | Kimi K3 Fast | 1M | ❌ | Text | 16K | $3.00 | $15.00 | $0.30 |
88
89
  | Llama 3.3 70B | 131K | ❌ | Text | 16K | $0.22 | $0.50 | $0.11 |
89
90
  | Llama 32 3b Instruct | 131K | ❌ | Text | 16K | $0.05 | $0.33 | Free |
90
91
  | Llama 4 Maverick 17B-128E | 524K | ❌ | Text + Image | 16K | $0.35 | $1.00 | $0.17 |
91
92
  | Mimo V25 | 1M | ❌ | Text | 16K | $0.14 | $0.28 | $0.05 |
92
93
  | MiniMax M2.5 | 197K | ✅ | Text | 16K | $0.30 | $1.20 | $0.03 |
93
94
  | Minimax M3 | 1M | ❌ | Text | 16K | $0.30 | $1.20 | $0.06 |
94
- | Mistral Small 3.2 24B | 131K | ❌ | Text + Image | 16K | $0.09 | $0.30 | $0.05 |
95
+ | Mistral Small 32 24b | 131K | ❌ | Text | 16K | $0.09 | $0.30 | $0.05 |
96
+ | Mythomax 13b | 4K | ❌ | Text | 16K | $0.08 | $0.11 | Free |
95
97
  | Qwen 3.5 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
96
98
  | Qwen 3.5 397B (A17B) | 262K | ✅ | Text + Image | 33K | $0.50 | $3.60 | $0.30 |
97
99
  | Qwen 3.6 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
@@ -107,6 +109,7 @@ Get your API key at [parasail.io](https://parasail.io/).
107
109
  | Rocinante 12b | 66K | ❌ | Text | 16K | $0.25 | $0.50 | Free |
108
110
  | Skyfall 36b V2 Fp8 | 33K | ❌ | Text | 16K | $0.55 | $0.80 | $0.25 |
109
111
  | Trinity Large Thinking | 262K | ❌ | Text | 16K | $0.22 | $0.85 | $0.06 |
112
+ | Unslopnemo 12b | 1M | ❌ | Text | 16K | $0.40 | $0.40 | Free |
110
113
 
111
114
  *Pricing fetched live from [Parasail's pricing API](https://www.saas.parasail.io/api/v1/prices/serverlessEndpoints). Prices are per million tokens and subject to change.*
112
115
 
@@ -0,0 +1 @@
1
+ {}
package/index.ts CHANGED
@@ -35,6 +35,7 @@ import { getAgentDir, type ExtensionAPI, type ModelRegistry } from "@earendil-wo
35
35
  import modelsData from "./models.json" with { type: "json" };
36
36
  import customModelsData from "./custom-models.json" with { type: "json" };
37
37
  import patchData from "./patch.json" with { type: "json" };
38
+ import deprecatedData from "./deprecated-models.json" with { type: "json" };
38
39
  import fs from "fs";
39
40
  import path from "path";
40
41
 
@@ -116,7 +117,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
116
117
  function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
117
118
  const modelMap = new Map<string, JsonModel>();
118
119
 
119
- for (const model of base) {
120
+ // Seed with the base list plus grace-period deprecated models so patch.json
121
+ // entries apply to deprecated models exactly as while the model was live
122
+ // (withDeprecated keeps live data on id conflicts).
123
+ for (const model of withDeprecated(base)) {
120
124
  modelMap.set(model.id, model);
121
125
  }
122
126
 
@@ -319,6 +323,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
319
323
  return result;
320
324
  }
321
325
 
326
+ // Grace period for delisted models. When the provider API stops listing a
327
+ // model, update-models.js moves its last-known definition into
328
+ // deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
329
+ // For 14 days the model keeps working here so in-flight sessions and saved
330
+ // model settings do not break; afterwards it is evicted permanently.
331
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
332
+
333
+ // Grace-period deprecated models with deprecation metadata stripped.
334
+ function activeDeprecatedModels(): JsonModel[] {
335
+ const now = Date.now();
336
+ const result: JsonModel[] = [];
337
+ for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
338
+ if (!entry?.id) continue;
339
+ const removedAt = Date.parse(entry.deprecatedAt ?? "");
340
+ if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
341
+ const model = { ...entry } as JsonModel & { deprecatedAt?: string };
342
+ delete model.deprecatedAt;
343
+ result.push(model);
344
+ }
345
+ return result;
346
+ }
347
+
348
+ // Append grace-period deprecated models the list does not already have (live data wins).
349
+ function withDeprecated(models: JsonModel[]): JsonModel[] {
350
+ const seen = new Set(models.map((m) => m.id));
351
+ const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
352
+ return extras.length > 0 ? [...models, ...extras] : models;
353
+ }
354
+
322
355
  function loadStaleModels(embeddedModels: JsonModel[]): JsonModel[] {
323
356
  const cached = loadCachedModels();
324
357
  if (!cached || cached.length === 0) return embeddedModels;
package/models.json CHANGED
@@ -311,16 +311,37 @@
311
311
  }
312
312
  },
313
313
  {
314
- "id": "parasail-deepseek-v4-flash-h100",
315
- "name": "Deepseek V4 Flash H100",
314
+ "id": "parasail-kimi-k3",
315
+ "name": "Kimi K3",
316
316
  "reasoning": false,
317
317
  "input": [
318
318
  "text"
319
319
  ],
320
320
  "cost": {
321
- "input": 0,
322
- "output": 0,
323
- "cacheRead": 0,
321
+ "input": 3,
322
+ "output": 15,
323
+ "cacheRead": 0.3,
324
+ "cacheWrite": 0
325
+ },
326
+ "contextWindow": 1048576,
327
+ "maxTokens": 16384,
328
+ "compat": {
329
+ "maxTokensField": "max_completion_tokens",
330
+ "supportsDeveloperRole": false,
331
+ "supportsStore": false
332
+ }
333
+ },
334
+ {
335
+ "id": "parasail-kimi-k3-fast",
336
+ "name": "Kimi K3 Fast",
337
+ "reasoning": false,
338
+ "input": [
339
+ "text"
340
+ ],
341
+ "cost": {
342
+ "input": 3,
343
+ "output": 15,
344
+ "cacheRead": 0.3,
324
345
  "cacheWrite": 0
325
346
  },
326
347
  "contextWindow": 1048576,
@@ -373,6 +394,27 @@
373
394
  "supportsStore": false
374
395
  }
375
396
  },
397
+ {
398
+ "id": "parasail-unslopnemo-12b",
399
+ "name": "Unslopnemo 12b",
400
+ "reasoning": false,
401
+ "input": [
402
+ "text"
403
+ ],
404
+ "cost": {
405
+ "input": 0.4,
406
+ "output": 0.4,
407
+ "cacheRead": 0,
408
+ "cacheWrite": 0
409
+ },
410
+ "contextWindow": 1024000,
411
+ "maxTokens": 16384,
412
+ "compat": {
413
+ "maxTokensField": "max_completion_tokens",
414
+ "supportsDeveloperRole": false,
415
+ "supportsStore": false
416
+ }
417
+ },
376
418
  {
377
419
  "id": "parasail-llama-4-maverick-instruct-fp8",
378
420
  "name": "Llama 4 Maverick 17B-128E",
@@ -673,11 +715,10 @@
673
715
  },
674
716
  {
675
717
  "id": "parasail-mistral-small-32-24b",
676
- "name": "Mistral Small 3.2 24B",
718
+ "name": "Mistral Small 32 24b",
677
719
  "reasoning": false,
678
720
  "input": [
679
- "text",
680
- "image"
721
+ "text"
681
722
  ],
682
723
  "cost": {
683
724
  "input": 0.09,
@@ -798,5 +839,26 @@
798
839
  "supportsDeveloperRole": false,
799
840
  "supportsStore": false
800
841
  }
842
+ },
843
+ {
844
+ "id": "parasail-mythomax-13b",
845
+ "name": "Mythomax 13b",
846
+ "reasoning": false,
847
+ "input": [
848
+ "text"
849
+ ],
850
+ "cost": {
851
+ "input": 0.08,
852
+ "output": 0.11,
853
+ "cacheRead": 0,
854
+ "cacheWrite": 0
855
+ },
856
+ "contextWindow": 4096,
857
+ "maxTokens": 16384,
858
+ "compat": {
859
+ "maxTokensField": "max_completion_tokens",
860
+ "supportsDeveloperRole": false,
861
+ "supportsStore": false
862
+ }
801
863
  }
802
864
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-parasail-provider",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Parasail provider extension for pi - Access top open-source models (DeepSeek, Qwen, GLM, Kimi, Llama, Gemma, Mistral) through the Parasail API",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -278,6 +278,67 @@ function updateReadme(models) {
278
278
 
279
279
  // ─── Main ────────────────────────────────────────────────────────────────────
280
280
 
281
+ // Grace period for delisted models: update-models.js moves models the API no
282
+ // longer lists into deprecated-models.json (stamped with deprecatedAt) instead
283
+ // of dropping them; the runtime appends them back so sessions and saved model
284
+ // settings keep working, and after 14 days they are evicted permanently.
285
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
286
+
287
+ /**
288
+ * Reconcile deprecated-models.json against the freshly fetched model list.
289
+ * - in old models.json but not the API: moved into the deprecated file
290
+ * (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
291
+ * - back in the API: resurrected (dropped from the deprecated file)
292
+ * - deprecatedAt older than 14 days: evicted permanently
293
+ * Must run BEFORE the new models.json is written; it reads the old file itself.
294
+ */
295
+ function updateDeprecatedModels(modelsJsonPath, newModels) {
296
+ const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
297
+
298
+ let oldModels = [];
299
+ try {
300
+ const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
301
+ if (Array.isArray(parsed)) oldModels = parsed;
302
+ } catch { /* first run: no previous models.json */ }
303
+
304
+ let deprecated = {};
305
+ try {
306
+ const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
307
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
308
+ } catch { /* no graveyard yet */ }
309
+
310
+ const currentIds = new Set(newModels.map(m => m.id));
311
+ const now = new Date().toISOString();
312
+ const added = [];
313
+ const resurrected = [];
314
+ const evicted = [];
315
+
316
+ for (const old of oldModels) {
317
+ if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
318
+ deprecated[old.id] = { ...old, deprecatedAt: now };
319
+ added.push(old.id);
320
+ }
321
+ }
322
+
323
+ for (const [id, entry] of Object.entries(deprecated)) {
324
+ if (currentIds.has(id)) {
325
+ delete deprecated[id];
326
+ resurrected.push(id);
327
+ continue;
328
+ }
329
+ const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
330
+ if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
331
+ delete deprecated[id];
332
+ evicted.push(id);
333
+ }
334
+ }
335
+
336
+ if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
337
+ fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
338
+ console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
339
+ }
340
+ }
341
+
281
342
  async function main() {
282
343
  try {
283
344
  const [apiModels, pricing] = await Promise.all([
@@ -308,6 +369,8 @@ async function main() {
308
369
  });
309
370
 
310
371
  // Save models.json (pure API output, no patch/custom baked in)
372
+ // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
373
+ updateDeprecatedModels(MODELS_JSON_PATH, models);
311
374
  saveJson(MODELS_JSON_PATH, models);
312
375
 
313
376
  // Build full model list for README: base → patch → custom