pi-parasail-provider 1.0.4 → 1.0.6

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,26 +72,31 @@ 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 |
75
+ | Deepseek V4 Flash 0731 | 1M | ✅ | Text | 16K | $0.14 | $0.28 | $0.07 |
76
76
  | DeepSeek V4 Pro | 1M | ✅ | Text | 384K | $1.74 | $3.48 | $0.10 |
77
77
  | Gemma 3 27B | 131K | ❌ | Text + Image | 16K | $0.08 | $0.45 | $0.04 |
78
- | Gemma 4 26B (A4B) | 262K | ❌ | Text + Image | 16K | $0.13 | $0.40 | $0.05 |
79
- | Gemma 4 31B | 262K | ❌ | Text + Image | 16K | $0.15 | $0.40 | $0.06 |
78
+ | Gemma 4 26B (A4B) | 262K | ✅ | Text + Image | 16K | $0.13 | $0.40 | $0.05 |
79
+ | Gemma 4 31B | 262K | ✅ | Text + Image | 16K | $0.15 | $0.40 | $0.06 |
80
80
  | GLM 5 | 203K | ✅ | Text | 16K | $1.00 | $3.20 | $0.20 |
81
81
  | GLM 5.1 | 203K | ✅ | Text | 16K | $1.40 | $4.40 | $0.26 |
82
- | Glm 52 | 262K | ❌ | Text | 16K | $1.40 | $4.40 | $0.26 |
83
- | Gpt Oss 120b Fast | 131K | ❌ | Text | 16K | $0.15 | $0.60 | Free |
84
- | GPT-OSS 120B | 131K | ❌ | Text | 16K | $0.10 | $0.75 | $0.06 |
85
- | GPT-OSS 20B | 131K | ❌ | Text | 16K | $0.03 | $0.15 | $0.02 |
82
+ | Glm 52 | 262K | ✅ | Text | 16K | $1.40 | $4.40 | $0.26 |
83
+ | Googlegemma 3 4b It | 131K | ❌ | Text | 16K | $0.05 | Free | Free |
84
+ | Gpt Oss 120b Fast | 131K | ✅ | Text | 16K | $0.15 | $0.60 | Free |
85
+ | GPT-OSS 120B | 131K | ✅ | Text | 16K | $0.10 | $0.75 | $0.06 |
86
+ | GPT-OSS 20B | 131K | ✅ | Text | 16K | $0.03 | $0.15 | $0.02 |
86
87
  | Kimi K2.6 | 262K | ✅ | Text + Image | 16K | $0.75 | $3.50 | $0.16 |
87
- | Kimi K27 Code | 262K | ❌ | Text | 16K | $0.76 | $3.50 | $0.16 |
88
+ | Kimi K27 Code | 262K | ✅ | Text + Image | 16K | $0.76 | $3.50 | $0.16 |
89
+ | Kimi K3 | 1M | ✅ | Text + Image | 16K | $3.00 | $15.00 | $0.30 |
90
+ | Kimi K3 Fast | 1M | ✅ | Text + Image | 16K | $3.00 | $15.00 | $0.30 |
88
91
  | Llama 3.3 70B | 131K | ❌ | Text | 16K | $0.22 | $0.50 | $0.11 |
89
92
  | Llama 32 3b Instruct | 131K | ❌ | Text | 16K | $0.05 | $0.33 | Free |
90
93
  | Llama 4 Maverick 17B-128E | 524K | ❌ | Text + Image | 16K | $0.35 | $1.00 | $0.17 |
91
- | Mimo V25 | 1M | ❌ | Text | 16K | $0.14 | $0.28 | $0.05 |
94
+ | Mimo V25 | 1M | ✅ | Text | 16K | $0.14 | $0.28 | $0.05 |
92
95
  | MiniMax M2.5 | 197K | ✅ | Text | 16K | $0.30 | $1.20 | $0.03 |
93
- | 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 |
96
+ | Minimax M3 | 1M | ✅ | Text + Image | 16K | $0.30 | $1.20 | $0.06 |
97
+ | Mistral Small 32 24b | 131K | ❌ | Text | 16K | $0.09 | $0.30 | $0.05 |
98
+ | Mistralaimistral Nemo | 131K | ❌ | Text | 16K | $0.03 | $0.03 | Free |
99
+ | Mythomax 13b | 4K | ❌ | Text | 16K | $0.08 | $0.11 | Free |
95
100
  | Qwen 3.5 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
96
101
  | Qwen 3.5 397B (A17B) | 262K | ✅ | Text + Image | 33K | $0.50 | $3.60 | $0.30 |
97
102
  | Qwen 3.6 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
@@ -105,8 +110,10 @@ Get your API key at [parasail.io](https://parasail.io/).
105
110
  | Qwen3-VL 8B | 262K | ✅ | Text + Image | 8K | $0.25 | $0.75 | $0.12 |
106
111
  | Qwen35 9b | 262K | ❌ | Text | 16K | $0.10 | $0.25 | Free |
107
112
  | Rocinante 12b | 66K | ❌ | Text | 16K | $0.25 | $0.50 | Free |
113
+ | Sao10kl3 Lunaris 8b | 8K | ❌ | Text | 16K | $0.04 | $0.05 | Free |
108
114
  | Skyfall 36b V2 Fp8 | 33K | ❌ | Text | 16K | $0.55 | $0.80 | $0.25 |
109
- | Trinity Large Thinking | 262K | ❌ | Text | 16K | $0.22 | $0.85 | $0.06 |
115
+ | Trinity Large Thinking | 262K | ✅ | Text | 16K | $0.22 | $0.85 | $0.06 |
116
+ | Unslopnemo 12b | 1M | ❌ | Text | 16K | $0.40 | $0.40 | Free |
110
117
 
111
118
  *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
119
 
@@ -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
 
@@ -53,12 +54,15 @@ interface JsonModel {
53
54
  };
54
55
  contextWindow: number;
55
56
  maxTokens: number;
57
+ thinkingLevelMap?: Record<string, string | null>;
56
58
  compat?: {
57
59
  supportsDeveloperRole?: boolean;
58
60
  supportsStore?: boolean;
59
61
  maxTokensField?: "max_completion_tokens" | "max_tokens";
60
- thinkingFormat?: "openai" | "zai" | "qwen" | "qwen-chat-template";
62
+ thinkingFormat?: "openai" | "zai" | "qwen" | "qwen-chat-template" | "chat-template";
61
63
  supportsReasoningEffort?: boolean;
64
+ requiresReasoningContentOnAssistantMessages?: boolean;
65
+ chatTemplateKwargs?: Record<string, unknown>;
62
66
  };
63
67
  }
64
68
 
@@ -74,6 +78,7 @@ interface PatchEntry {
74
78
  };
75
79
  contextWindow?: number;
76
80
  maxTokens?: number;
81
+ thinkingLevelMap?: Record<string, string | null>;
77
82
  compat?: Record<string, unknown>;
78
83
  }
79
84
 
@@ -89,6 +94,7 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
89
94
  if (patch.input !== undefined) result.input = patch.input;
90
95
  if (patch.contextWindow !== undefined) result.contextWindow = patch.contextWindow;
91
96
  if (patch.maxTokens !== undefined) result.maxTokens = patch.maxTokens;
97
+ if (patch.thinkingLevelMap !== undefined) result.thinkingLevelMap = { ...patch.thinkingLevelMap };
92
98
 
93
99
  if (patch.cost) {
94
100
  result.cost = {
@@ -105,6 +111,9 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
105
111
  if (!result.reasoning && result.compat?.thinkingFormat) {
106
112
  delete result.compat.thinkingFormat;
107
113
  }
114
+ if (!result.reasoning && result.thinkingLevelMap) {
115
+ delete result.thinkingLevelMap;
116
+ }
108
117
  if (result.compat && Object.keys(result.compat).length === 0) {
109
118
  delete result.compat;
110
119
  }
@@ -116,7 +125,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
116
125
  function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
117
126
  const modelMap = new Map<string, JsonModel>();
118
127
 
119
- for (const model of base) {
128
+ // Seed with the base list plus grace-period deprecated models so patch.json
129
+ // entries apply to deprecated models exactly as while the model was live
130
+ // (withDeprecated keeps live data on id conflicts).
131
+ for (const model of withDeprecated(base)) {
120
132
  modelMap.set(model.id, model);
121
133
  }
122
134
 
@@ -319,6 +331,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
319
331
  return result;
320
332
  }
321
333
 
334
+ // Grace period for delisted models. When the provider API stops listing a
335
+ // model, update-models.js moves its last-known definition into
336
+ // deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
337
+ // For 14 days the model keeps working here so in-flight sessions and saved
338
+ // model settings do not break; afterwards it is evicted permanently.
339
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
340
+
341
+ // Grace-period deprecated models with deprecation metadata stripped.
342
+ function activeDeprecatedModels(): JsonModel[] {
343
+ const now = Date.now();
344
+ const result: JsonModel[] = [];
345
+ for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
346
+ if (!entry?.id) continue;
347
+ const removedAt = Date.parse(entry.deprecatedAt ?? "");
348
+ if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
349
+ const model = { ...entry } as JsonModel & { deprecatedAt?: string };
350
+ delete model.deprecatedAt;
351
+ result.push(model);
352
+ }
353
+ return result;
354
+ }
355
+
356
+ // Append grace-period deprecated models the list does not already have (live data wins).
357
+ function withDeprecated(models: JsonModel[]): JsonModel[] {
358
+ const seen = new Set(models.map((m) => m.id));
359
+ const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
360
+ return extras.length > 0 ? [...models, ...extras] : models;
361
+ }
362
+
322
363
  function loadStaleModels(embeddedModels: JsonModel[]): JsonModel[] {
323
364
  const cached = loadCachedModels();
324
365
  if (!cached || cached.length === 0) return embeddedModels;
package/models.json CHANGED
@@ -311,16 +311,58 @@
311
311
  }
312
312
  },
313
313
  {
314
- "id": "parasail-deepseek-v4-flash-h100",
315
- "name": "Deepseek V4 Flash H100",
314
+ "id": "parasail-deepseek-v4-flash-0731",
315
+ "name": "Deepseek V4 Flash 0731",
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": 0.14,
322
+ "output": 0.28,
323
+ "cacheRead": 0.07,
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",
336
+ "name": "Kimi K3",
337
+ "reasoning": false,
338
+ "input": [
339
+ "text"
340
+ ],
341
+ "cost": {
342
+ "input": 3,
343
+ "output": 15,
344
+ "cacheRead": 0.3,
345
+ "cacheWrite": 0
346
+ },
347
+ "contextWindow": 1048576,
348
+ "maxTokens": 16384,
349
+ "compat": {
350
+ "maxTokensField": "max_completion_tokens",
351
+ "supportsDeveloperRole": false,
352
+ "supportsStore": false
353
+ }
354
+ },
355
+ {
356
+ "id": "parasail-kimi-k3-fast",
357
+ "name": "Kimi K3 Fast",
358
+ "reasoning": false,
359
+ "input": [
360
+ "text"
361
+ ],
362
+ "cost": {
363
+ "input": 3,
364
+ "output": 15,
365
+ "cacheRead": 0.3,
324
366
  "cacheWrite": 0
325
367
  },
326
368
  "contextWindow": 1048576,
@@ -373,6 +415,27 @@
373
415
  "supportsStore": false
374
416
  }
375
417
  },
418
+ {
419
+ "id": "parasail-unslopnemo-12b",
420
+ "name": "Unslopnemo 12b",
421
+ "reasoning": false,
422
+ "input": [
423
+ "text"
424
+ ],
425
+ "cost": {
426
+ "input": 0.4,
427
+ "output": 0.4,
428
+ "cacheRead": 0,
429
+ "cacheWrite": 0
430
+ },
431
+ "contextWindow": 1024000,
432
+ "maxTokens": 16384,
433
+ "compat": {
434
+ "maxTokensField": "max_completion_tokens",
435
+ "supportsDeveloperRole": false,
436
+ "supportsStore": false
437
+ }
438
+ },
376
439
  {
377
440
  "id": "parasail-llama-4-maverick-instruct-fp8",
378
441
  "name": "Llama 4 Maverick 17B-128E",
@@ -566,6 +629,27 @@
566
629
  "supportsStore": false
567
630
  }
568
631
  },
632
+ {
633
+ "id": "parasail-googlegemma-3-4b-it",
634
+ "name": "Googlegemma 3 4b It",
635
+ "reasoning": false,
636
+ "input": [
637
+ "text"
638
+ ],
639
+ "cost": {
640
+ "input": 0.05,
641
+ "output": 0,
642
+ "cacheRead": 0,
643
+ "cacheWrite": 0
644
+ },
645
+ "contextWindow": 131072,
646
+ "maxTokens": 16384,
647
+ "compat": {
648
+ "maxTokensField": "max_completion_tokens",
649
+ "supportsDeveloperRole": false,
650
+ "supportsStore": false
651
+ }
652
+ },
569
653
  {
570
654
  "id": "parasail-gpt-oss-120b-fast",
571
655
  "name": "Gpt Oss 120b Fast",
@@ -673,11 +757,10 @@
673
757
  },
674
758
  {
675
759
  "id": "parasail-mistral-small-32-24b",
676
- "name": "Mistral Small 3.2 24B",
760
+ "name": "Mistral Small 32 24b",
677
761
  "reasoning": false,
678
762
  "input": [
679
- "text",
680
- "image"
763
+ "text"
681
764
  ],
682
765
  "cost": {
683
766
  "input": 0.09,
@@ -693,6 +776,27 @@
693
776
  "supportsStore": false
694
777
  }
695
778
  },
779
+ {
780
+ "id": "parasail-mistralaimistral-nemo",
781
+ "name": "Mistralaimistral Nemo",
782
+ "reasoning": false,
783
+ "input": [
784
+ "text"
785
+ ],
786
+ "cost": {
787
+ "input": 0.03,
788
+ "output": 0.03,
789
+ "cacheRead": 0,
790
+ "cacheWrite": 0
791
+ },
792
+ "contextWindow": 131072,
793
+ "maxTokens": 16384,
794
+ "compat": {
795
+ "maxTokensField": "max_completion_tokens",
796
+ "supportsDeveloperRole": false,
797
+ "supportsStore": false
798
+ }
799
+ },
696
800
  {
697
801
  "id": "parasail-qwen25-vl-72b-instruct",
698
802
  "name": "Qwen2.5-VL 72B",
@@ -798,5 +902,47 @@
798
902
  "supportsDeveloperRole": false,
799
903
  "supportsStore": false
800
904
  }
905
+ },
906
+ {
907
+ "id": "parasail-sao10kl3-lunaris-8b",
908
+ "name": "Sao10kl3 Lunaris 8b",
909
+ "reasoning": false,
910
+ "input": [
911
+ "text"
912
+ ],
913
+ "cost": {
914
+ "input": 0.04,
915
+ "output": 0.05,
916
+ "cacheRead": 0,
917
+ "cacheWrite": 0
918
+ },
919
+ "contextWindow": 8192,
920
+ "maxTokens": 16384,
921
+ "compat": {
922
+ "maxTokensField": "max_completion_tokens",
923
+ "supportsDeveloperRole": false,
924
+ "supportsStore": false
925
+ }
926
+ },
927
+ {
928
+ "id": "parasail-mythomax-13b",
929
+ "name": "Mythomax 13b",
930
+ "reasoning": false,
931
+ "input": [
932
+ "text"
933
+ ],
934
+ "cost": {
935
+ "input": 0.08,
936
+ "output": 0.11,
937
+ "cacheRead": 0,
938
+ "cacheWrite": 0
939
+ },
940
+ "contextWindow": 4096,
941
+ "maxTokens": 16384,
942
+ "compat": {
943
+ "maxTokensField": "max_completion_tokens",
944
+ "supportsDeveloperRole": false,
945
+ "supportsStore": false
946
+ }
801
947
  }
802
948
  ]
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.6",
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",
package/patch.json CHANGED
@@ -1,85 +1,441 @@
1
1
  {
2
- "parasail-kimi-k25": {
3
- "input": ["text", "image"],
4
- "compat": {
5
- "supportsReasoningEffort": true
6
- }
7
- },
8
2
  "parasail-kimi-k26": {
9
- "input": ["text", "image"],
10
- "compat": {
11
- "supportsReasoningEffort": true
12
- }
13
- },
14
- "parasail-kimi-k26-nvfp4": {
15
- "input": ["text", "image"],
3
+ "input": [
4
+ "text",
5
+ "image"
6
+ ],
7
+ "thinkingLevelMap": {
8
+ "off": "none",
9
+ "minimal": null,
10
+ "low": null,
11
+ "medium": null,
12
+ "high": "high",
13
+ "xhigh": null,
14
+ "max": null
15
+ },
16
16
  "compat": {
17
- "supportsReasoningEffort": true
17
+ "thinkingFormat": "qwen-chat-template",
18
+ "supportsReasoningEffort": false,
19
+ "requiresReasoningContentOnAssistantMessages": true
18
20
  }
19
21
  },
20
22
  "parasail-qwen3p5-35b-a3b": {
21
- "input": ["text", "image"],
23
+ "input": [
24
+ "text",
25
+ "image"
26
+ ],
27
+ "thinkingLevelMap": {
28
+ "off": "none",
29
+ "minimal": null,
30
+ "low": "low",
31
+ "medium": "medium",
32
+ "high": "high",
33
+ "xhigh": null,
34
+ "max": null
35
+ },
22
36
  "compat": {
23
- "supportsReasoningEffort": true
37
+ "thinkingFormat": "qwen-chat-template",
38
+ "supportsReasoningEffort": false,
39
+ "requiresReasoningContentOnAssistantMessages": true
24
40
  }
25
41
  },
26
42
  "parasail-qwen3p6-35b-a3b": {
27
- "input": ["text", "image"],
43
+ "input": [
44
+ "text",
45
+ "image"
46
+ ],
47
+ "thinkingLevelMap": {
48
+ "off": "none",
49
+ "minimal": null,
50
+ "low": "low",
51
+ "medium": "medium",
52
+ "high": "high",
53
+ "xhigh": null,
54
+ "max": null
55
+ },
28
56
  "compat": {
29
- "supportsReasoningEffort": true
57
+ "thinkingFormat": "qwen-chat-template",
58
+ "supportsReasoningEffort": false,
59
+ "requiresReasoningContentOnAssistantMessages": true
30
60
  }
31
61
  },
32
62
  "parasail-qwen35-397b-a17b": {
63
+ "input": [
64
+ "text",
65
+ "image"
66
+ ],
67
+ "thinkingLevelMap": {
68
+ "off": "none",
69
+ "minimal": null,
70
+ "low": "low",
71
+ "medium": "medium",
72
+ "high": "high",
73
+ "xhigh": null,
74
+ "max": null
75
+ },
33
76
  "compat": {
34
- "supportsReasoningEffort": true
77
+ "thinkingFormat": "qwen-chat-template",
78
+ "supportsReasoningEffort": false,
79
+ "requiresReasoningContentOnAssistantMessages": true
35
80
  }
36
81
  },
37
82
  "parasail-qwen3-coder-next": {
83
+ "thinkingLevelMap": {
84
+ "off": "none",
85
+ "minimal": null,
86
+ "low": "low",
87
+ "medium": "medium",
88
+ "high": "high",
89
+ "xhigh": null,
90
+ "max": null
91
+ },
38
92
  "compat": {
39
- "supportsReasoningEffort": true
93
+ "thinkingFormat": "qwen-chat-template",
94
+ "supportsReasoningEffort": false,
95
+ "requiresReasoningContentOnAssistantMessages": true
40
96
  }
41
97
  },
42
98
  "parasail-qwen-3-next-80b-instruct": {
99
+ "thinkingLevelMap": {
100
+ "off": "none",
101
+ "minimal": null,
102
+ "low": "low",
103
+ "medium": "medium",
104
+ "high": "high",
105
+ "xhigh": null,
106
+ "max": null
107
+ },
43
108
  "compat": {
44
- "supportsReasoningEffort": true
109
+ "thinkingFormat": "qwen-chat-template",
110
+ "supportsReasoningEffort": false,
111
+ "requiresReasoningContentOnAssistantMessages": true
45
112
  }
46
113
  },
47
114
  "parasail-qwen3vl-8b-instruct": {
115
+ "thinkingLevelMap": {
116
+ "off": "none",
117
+ "minimal": null,
118
+ "low": "low",
119
+ "medium": "medium",
120
+ "high": "high",
121
+ "xhigh": null,
122
+ "max": null
123
+ },
48
124
  "compat": {
49
- "supportsReasoningEffort": true
125
+ "thinkingFormat": "qwen-chat-template",
126
+ "supportsReasoningEffort": false,
127
+ "requiresReasoningContentOnAssistantMessages": true
50
128
  }
51
129
  },
52
130
  "parasail-trinity-large-thinking": {
131
+ "reasoning": true,
132
+ "thinkingLevelMap": {
133
+ "off": "none",
134
+ "minimal": null,
135
+ "low": "low",
136
+ "medium": "medium",
137
+ "high": "high",
138
+ "xhigh": null,
139
+ "max": null
140
+ },
53
141
  "compat": {
54
- "supportsReasoningEffort": true
55
- }
56
- },
57
- "parasail-glm47": {
58
- "compat": {
142
+ "thinkingFormat": "openai",
59
143
  "supportsReasoningEffort": true
60
144
  }
61
145
  },
62
146
  "parasail-glm-5": {
147
+ "thinkingLevelMap": {
148
+ "off": "none",
149
+ "minimal": null,
150
+ "low": null,
151
+ "medium": null,
152
+ "high": "high",
153
+ "xhigh": null,
154
+ "max": null
155
+ },
63
156
  "compat": {
64
- "supportsReasoningEffort": true
157
+ "thinkingFormat": "qwen-chat-template",
158
+ "requiresReasoningContentOnAssistantMessages": true
65
159
  }
66
160
  },
67
161
  "parasail-glm-51": {
162
+ "thinkingLevelMap": {
163
+ "off": "none",
164
+ "minimal": null,
165
+ "low": null,
166
+ "medium": null,
167
+ "high": "high",
168
+ "xhigh": null,
169
+ "max": null
170
+ },
68
171
  "compat": {
69
- "supportsReasoningEffort": true
172
+ "thinkingFormat": "qwen-chat-template",
173
+ "requiresReasoningContentOnAssistantMessages": true
70
174
  }
71
175
  },
72
176
  "parasail-minimax-m25": {
177
+ "thinkingLevelMap": {
178
+ "off": "none",
179
+ "minimal": null,
180
+ "low": null,
181
+ "medium": null,
182
+ "high": "high",
183
+ "xhigh": null,
184
+ "max": null
185
+ },
73
186
  "compat": {
74
- "supportsReasoningEffort": true
187
+ "thinkingFormat": "qwen-chat-template",
188
+ "supportsReasoningEffort": false,
189
+ "requiresReasoningContentOnAssistantMessages": true
75
190
  }
76
191
  },
77
192
  "parasail-qwen3-235b-a22b-instruct-2507": {
193
+ "thinkingLevelMap": {
194
+ "off": "none",
195
+ "minimal": null,
196
+ "low": "low",
197
+ "medium": "medium",
198
+ "high": "high",
199
+ "xhigh": null,
200
+ "max": null
201
+ },
78
202
  "compat": {
79
- "supportsReasoningEffort": true
203
+ "thinkingFormat": "qwen-chat-template",
204
+ "supportsReasoningEffort": false,
205
+ "requiresReasoningContentOnAssistantMessages": true
80
206
  }
81
207
  },
82
208
  "parasail-qwen3-vl-235b-a22b-instruct": {
209
+ "thinkingLevelMap": {
210
+ "off": "none",
211
+ "minimal": null,
212
+ "low": "low",
213
+ "medium": "medium",
214
+ "high": "high",
215
+ "xhigh": null,
216
+ "max": null
217
+ },
218
+ "compat": {
219
+ "thinkingFormat": "qwen-chat-template",
220
+ "supportsReasoningEffort": false,
221
+ "requiresReasoningContentOnAssistantMessages": true
222
+ }
223
+ },
224
+ "parasail-deepseek-v4-flash-0731": {
225
+ "reasoning": true,
226
+ "thinkingLevelMap": {
227
+ "off": "none",
228
+ "minimal": null,
229
+ "low": null,
230
+ "medium": null,
231
+ "high": "high",
232
+ "xhigh": null,
233
+ "max": null
234
+ },
235
+ "compat": {
236
+ "thinkingFormat": "deepseek",
237
+ "supportsReasoningEffort": false,
238
+ "requiresReasoningContentOnAssistantMessages": true
239
+ }
240
+ },
241
+ "parasail-kimi-k3": {
242
+ "reasoning": true,
243
+ "input": [
244
+ "text",
245
+ "image"
246
+ ],
247
+ "thinkingLevelMap": {
248
+ "off": "none",
249
+ "minimal": null,
250
+ "low": null,
251
+ "medium": null,
252
+ "high": "high",
253
+ "xhigh": null,
254
+ "max": null
255
+ },
256
+ "compat": {
257
+ "thinkingFormat": "chat-template",
258
+ "supportsReasoningEffort": false,
259
+ "requiresReasoningContentOnAssistantMessages": true
260
+ }
261
+ },
262
+ "parasail-kimi-k3-fast": {
263
+ "reasoning": true,
264
+ "input": [
265
+ "text",
266
+ "image"
267
+ ],
268
+ "thinkingLevelMap": {
269
+ "off": "none",
270
+ "minimal": null,
271
+ "low": null,
272
+ "medium": null,
273
+ "high": "high",
274
+ "xhigh": null,
275
+ "max": null
276
+ },
277
+ "compat": {
278
+ "thinkingFormat": "chat-template",
279
+ "supportsReasoningEffort": false,
280
+ "requiresReasoningContentOnAssistantMessages": true
281
+ }
282
+ },
283
+ "parasail-mimo-v25": {
284
+ "reasoning": true,
285
+ "thinkingLevelMap": {
286
+ "off": "none",
287
+ "minimal": null,
288
+ "low": null,
289
+ "medium": null,
290
+ "high": "high",
291
+ "xhigh": null,
292
+ "max": null
293
+ },
294
+ "compat": {
295
+ "thinkingFormat": "chat-template",
296
+ "supportsReasoningEffort": false,
297
+ "requiresReasoningContentOnAssistantMessages": true
298
+ }
299
+ },
300
+ "parasail-minimax-m3": {
301
+ "reasoning": true,
302
+ "input": [
303
+ "text",
304
+ "image"
305
+ ],
306
+ "thinkingLevelMap": {
307
+ "off": "disabled",
308
+ "minimal": "adaptive",
309
+ "low": "adaptive",
310
+ "medium": "adaptive",
311
+ "high": "enabled",
312
+ "xhigh": null,
313
+ "max": null
314
+ },
315
+ "compat": {
316
+ "thinkingFormat": "chat-template",
317
+ "supportsReasoningEffort": false,
318
+ "requiresReasoningContentOnAssistantMessages": true,
319
+ "chatTemplateKwargs": {
320
+ "thinking_mode": {
321
+ "$var": "thinking.effort"
322
+ }
323
+ }
324
+ }
325
+ },
326
+ "parasail-gemma-4-26b-a4b-it": {
327
+ "reasoning": true,
328
+ "thinkingLevelMap": {
329
+ "off": "none",
330
+ "minimal": null,
331
+ "low": null,
332
+ "medium": null,
333
+ "high": "high",
334
+ "xhigh": null,
335
+ "max": null
336
+ },
337
+ "compat": {
338
+ "thinkingFormat": "chat-template",
339
+ "supportsReasoningEffort": false,
340
+ "requiresReasoningContentOnAssistantMessages": true
341
+ }
342
+ },
343
+ "parasail-gemma-4-31b-it": {
344
+ "reasoning": true,
345
+ "thinkingLevelMap": {
346
+ "off": "none",
347
+ "minimal": null,
348
+ "low": null,
349
+ "medium": null,
350
+ "high": "high",
351
+ "xhigh": null,
352
+ "max": null
353
+ },
354
+ "compat": {
355
+ "thinkingFormat": "chat-template",
356
+ "supportsReasoningEffort": false,
357
+ "requiresReasoningContentOnAssistantMessages": true
358
+ }
359
+ },
360
+ "parasail-glm-52": {
361
+ "reasoning": true,
362
+ "thinkingLevelMap": {
363
+ "off": "none",
364
+ "minimal": null,
365
+ "low": null,
366
+ "medium": null,
367
+ "high": "high",
368
+ "xhigh": null,
369
+ "max": null
370
+ },
371
+ "compat": {
372
+ "thinkingFormat": "chat-template",
373
+ "supportsReasoningEffort": false,
374
+ "requiresReasoningContentOnAssistantMessages": true
375
+ }
376
+ },
377
+ "parasail-kimi-k27-code": {
378
+ "reasoning": true,
379
+ "input": [
380
+ "text",
381
+ "image"
382
+ ],
383
+ "thinkingLevelMap": {
384
+ "off": "none",
385
+ "minimal": null,
386
+ "low": null,
387
+ "medium": null,
388
+ "high": "high",
389
+ "xhigh": null,
390
+ "max": null
391
+ },
392
+ "compat": {
393
+ "thinkingFormat": "chat-template",
394
+ "supportsReasoningEffort": false,
395
+ "requiresReasoningContentOnAssistantMessages": true
396
+ }
397
+ },
398
+ "parasail-gpt-oss-120b-fast": {
399
+ "reasoning": true,
400
+ "thinkingLevelMap": {
401
+ "off": null,
402
+ "minimal": null,
403
+ "low": "low",
404
+ "medium": "medium",
405
+ "high": "high",
406
+ "xhigh": null,
407
+ "max": null
408
+ },
409
+ "compat": {
410
+ "supportsReasoningEffort": true
411
+ }
412
+ },
413
+ "parasail-gpt-oss-120b": {
414
+ "reasoning": true,
415
+ "thinkingLevelMap": {
416
+ "off": null,
417
+ "minimal": null,
418
+ "low": "low",
419
+ "medium": "medium",
420
+ "high": "high",
421
+ "xhigh": null,
422
+ "max": null
423
+ },
424
+ "compat": {
425
+ "supportsReasoningEffort": true
426
+ }
427
+ },
428
+ "parasail-gpt-oss-20b": {
429
+ "reasoning": true,
430
+ "thinkingLevelMap": {
431
+ "off": null,
432
+ "minimal": null,
433
+ "low": "low",
434
+ "medium": "medium",
435
+ "high": "high",
436
+ "xhigh": null,
437
+ "max": null
438
+ },
83
439
  "compat": {
84
440
  "supportsReasoningEffort": true
85
441
  }
@@ -173,6 +173,7 @@ function generateDisplayName(id) {
173
173
 
174
174
  function applyPatch(model, patch) {
175
175
  const result = { ...model };
176
+ if (patch.thinkingLevelMap !== undefined) result.thinkingLevelMap = { ...patch.thinkingLevelMap };
176
177
  if (patch.name !== undefined) result.name = patch.name;
177
178
  if (patch.reasoning !== undefined) result.reasoning = patch.reasoning;
178
179
  if (patch.input !== undefined) result.input = patch.input;
@@ -192,6 +193,9 @@ function applyPatch(model, patch) {
192
193
  if (!result.reasoning && result.compat?.thinkingFormat) {
193
194
  delete result.compat.thinkingFormat;
194
195
  }
196
+ if (!result.reasoning && result.thinkingLevelMap) {
197
+ delete result.thinkingLevelMap;
198
+ }
195
199
  if (result.compat && Object.keys(result.compat).length === 0) {
196
200
  delete result.compat;
197
201
  }
@@ -278,6 +282,67 @@ function updateReadme(models) {
278
282
 
279
283
  // ─── Main ────────────────────────────────────────────────────────────────────
280
284
 
285
+ // Grace period for delisted models: update-models.js moves models the API no
286
+ // longer lists into deprecated-models.json (stamped with deprecatedAt) instead
287
+ // of dropping them; the runtime appends them back so sessions and saved model
288
+ // settings keep working, and after 14 days they are evicted permanently.
289
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
290
+
291
+ /**
292
+ * Reconcile deprecated-models.json against the freshly fetched model list.
293
+ * - in old models.json but not the API: moved into the deprecated file
294
+ * (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
295
+ * - back in the API: resurrected (dropped from the deprecated file)
296
+ * - deprecatedAt older than 14 days: evicted permanently
297
+ * Must run BEFORE the new models.json is written; it reads the old file itself.
298
+ */
299
+ function updateDeprecatedModels(modelsJsonPath, newModels) {
300
+ const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
301
+
302
+ let oldModels = [];
303
+ try {
304
+ const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
305
+ if (Array.isArray(parsed)) oldModels = parsed;
306
+ } catch { /* first run: no previous models.json */ }
307
+
308
+ let deprecated = {};
309
+ try {
310
+ const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
311
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
312
+ } catch { /* no graveyard yet */ }
313
+
314
+ const currentIds = new Set(newModels.map(m => m.id));
315
+ const now = new Date().toISOString();
316
+ const added = [];
317
+ const resurrected = [];
318
+ const evicted = [];
319
+
320
+ for (const old of oldModels) {
321
+ if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
322
+ deprecated[old.id] = { ...old, deprecatedAt: now };
323
+ added.push(old.id);
324
+ }
325
+ }
326
+
327
+ for (const [id, entry] of Object.entries(deprecated)) {
328
+ if (currentIds.has(id)) {
329
+ delete deprecated[id];
330
+ resurrected.push(id);
331
+ continue;
332
+ }
333
+ const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
334
+ if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
335
+ delete deprecated[id];
336
+ evicted.push(id);
337
+ }
338
+ }
339
+
340
+ if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
341
+ fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
342
+ console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
343
+ }
344
+ }
345
+
281
346
  async function main() {
282
347
  try {
283
348
  const [apiModels, pricing] = await Promise.all([
@@ -308,6 +373,8 @@ async function main() {
308
373
  });
309
374
 
310
375
  // Save models.json (pure API output, no patch/custom baked in)
376
+ // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
377
+ updateDeprecatedModels(MODELS_JSON_PATH, models);
311
378
  saveJson(MODELS_JSON_PATH, models);
312
379
 
313
380
  // Build full model list for README: base → patch → custom