pi-parasail-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 +1 -0
- package/README.md +12 -3
- package/deprecated-models.json +1 -0
- package/index.ts +36 -3
- package/models.json +194 -6
- package/package.json +1 -1
- package/scripts/update-models.js +63 -0
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
|
@@ -81,26 +81,35 @@ Get your API key at [parasail.io](https://parasail.io/).
|
|
|
81
81
|
| Glm 52 | 262K | ❌ | Text | 16K | $1.40 | $4.40 | $0.26 |
|
|
82
82
|
| Gpt Oss 120b Fast | 131K | ❌ | Text | 16K | $0.15 | $0.60 | Free |
|
|
83
83
|
| GPT-OSS 120B | 131K | ❌ | Text | 16K | $0.10 | $0.75 | $0.06 |
|
|
84
|
-
| GPT-OSS 20B | 131K | ❌ | Text | 16K | $0.
|
|
84
|
+
| GPT-OSS 20B | 131K | ❌ | Text | 16K | $0.03 | $0.15 | $0.02 |
|
|
85
85
|
| Kimi K2.6 | 262K | ✅ | Text + Image | 16K | $0.75 | $3.50 | $0.16 |
|
|
86
|
-
| Kimi K27 Code | 262K | ❌ | Text | 16K | $0.
|
|
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 |
|
|
87
89
|
| Llama 3.3 70B | 131K | ❌ | Text | 16K | $0.22 | $0.50 | $0.11 |
|
|
90
|
+
| Llama 32 3b Instruct | 131K | ❌ | Text | 16K | $0.05 | $0.33 | Free |
|
|
88
91
|
| Llama 4 Maverick 17B-128E | 524K | ❌ | Text + Image | 16K | $0.35 | $1.00 | $0.17 |
|
|
89
92
|
| Mimo V25 | 1M | ❌ | Text | 16K | $0.14 | $0.28 | $0.05 |
|
|
90
93
|
| MiniMax M2.5 | 197K | ✅ | Text | 16K | $0.30 | $1.20 | $0.03 |
|
|
91
94
|
| Minimax M3 | 1M | ❌ | Text | 16K | $0.30 | $1.20 | $0.06 |
|
|
92
|
-
| Mistral Small
|
|
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 |
|
|
93
97
|
| Qwen 3.5 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
|
|
94
98
|
| Qwen 3.5 397B (A17B) | 262K | ✅ | Text + Image | 33K | $0.50 | $3.60 | $0.30 |
|
|
95
99
|
| Qwen 3.6 35B (A3B) | 262K | ✅ | Text + Image | 33K | $0.15 | $1.00 | $0.05 |
|
|
96
100
|
| Qwen2.5-VL 72B | 128K | ❌ | Text + Image | 8K | $0.80 | $1.00 | $0.40 |
|
|
97
101
|
| Qwen3 235B (A22B) | 131K | ✅ | Text | 33K | $0.14 | $0.80 | $0.05 |
|
|
98
102
|
| Qwen3 Coder Next | 262K | ✅ | Text | 33K | $0.12 | $0.80 | $0.07 |
|
|
103
|
+
| Qwen3 Embedding 4b | 41K | ❌ | Text | 16K | $0.02 | Free | Free |
|
|
104
|
+
| Qwen3 Embedding 8b | 41K | ❌ | Text | 16K | $0.03 | Free | Free |
|
|
99
105
|
| Qwen3 Next 80B (A3B) | 262K | ✅ | Text | 33K | $0.10 | $1.10 | $0.07 |
|
|
100
106
|
| Qwen3-VL 235B (A22B) | 131K | ✅ | Text + Image | 33K | $0.21 | $1.90 | $0.10 |
|
|
101
107
|
| Qwen3-VL 8B | 262K | ✅ | Text + Image | 8K | $0.25 | $0.75 | $0.12 |
|
|
108
|
+
| Qwen35 9b | 262K | ❌ | Text | 16K | $0.10 | $0.25 | Free |
|
|
109
|
+
| Rocinante 12b | 66K | ❌ | Text | 16K | $0.25 | $0.50 | Free |
|
|
102
110
|
| Skyfall 36b V2 Fp8 | 33K | ❌ | Text | 16K | $0.55 | $0.80 | $0.25 |
|
|
103
111
|
| Trinity Large Thinking | 262K | ❌ | Text | 16K | $0.22 | $0.85 | $0.06 |
|
|
112
|
+
| Unslopnemo 12b | 1M | ❌ | Text | 16K | $0.40 | $0.40 | Free |
|
|
104
113
|
|
|
105
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.*
|
|
106
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
|
|
|
@@ -44,7 +45,7 @@ interface JsonModel {
|
|
|
44
45
|
id: string;
|
|
45
46
|
name: string;
|
|
46
47
|
reasoning: boolean;
|
|
47
|
-
input:
|
|
48
|
+
input: ("text" | "image")[];
|
|
48
49
|
cost: {
|
|
49
50
|
input: number;
|
|
50
51
|
output: number;
|
|
@@ -65,7 +66,7 @@ interface JsonModel {
|
|
|
65
66
|
interface PatchEntry {
|
|
66
67
|
name?: string;
|
|
67
68
|
reasoning?: boolean;
|
|
68
|
-
input?:
|
|
69
|
+
input?: ("text" | "image")[];
|
|
69
70
|
cost?: {
|
|
70
71
|
input?: number;
|
|
71
72
|
output?: number;
|
|
@@ -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
|
-
|
|
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
|
@@ -310,6 +310,48 @@
|
|
|
310
310
|
"supportsStore": false
|
|
311
311
|
}
|
|
312
312
|
},
|
|
313
|
+
{
|
|
314
|
+
"id": "parasail-kimi-k3",
|
|
315
|
+
"name": "Kimi K3",
|
|
316
|
+
"reasoning": false,
|
|
317
|
+
"input": [
|
|
318
|
+
"text"
|
|
319
|
+
],
|
|
320
|
+
"cost": {
|
|
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,
|
|
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
|
+
},
|
|
313
355
|
{
|
|
314
356
|
"id": "parasail-mimo-v25",
|
|
315
357
|
"name": "Mimo V25",
|
|
@@ -352,6 +394,27 @@
|
|
|
352
394
|
"supportsStore": false
|
|
353
395
|
}
|
|
354
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
|
+
},
|
|
355
418
|
{
|
|
356
419
|
"id": "parasail-llama-4-maverick-instruct-fp8",
|
|
357
420
|
"name": "Llama 4 Maverick 17B-128E",
|
|
@@ -447,7 +510,7 @@
|
|
|
447
510
|
"text"
|
|
448
511
|
],
|
|
449
512
|
"cost": {
|
|
450
|
-
"input": 0.
|
|
513
|
+
"input": 0.76,
|
|
451
514
|
"output": 3.5,
|
|
452
515
|
"cacheRead": 0.16,
|
|
453
516
|
"cacheWrite": 0
|
|
@@ -460,6 +523,27 @@
|
|
|
460
523
|
"supportsStore": false
|
|
461
524
|
}
|
|
462
525
|
},
|
|
526
|
+
{
|
|
527
|
+
"id": "parasail-qwen35-9b",
|
|
528
|
+
"name": "Qwen35 9b",
|
|
529
|
+
"reasoning": false,
|
|
530
|
+
"input": [
|
|
531
|
+
"text"
|
|
532
|
+
],
|
|
533
|
+
"cost": {
|
|
534
|
+
"input": 0.1,
|
|
535
|
+
"output": 0.25,
|
|
536
|
+
"cacheRead": 0,
|
|
537
|
+
"cacheWrite": 0
|
|
538
|
+
},
|
|
539
|
+
"contextWindow": 262144,
|
|
540
|
+
"maxTokens": 16384,
|
|
541
|
+
"compat": {
|
|
542
|
+
"maxTokensField": "max_completion_tokens",
|
|
543
|
+
"supportsDeveloperRole": false,
|
|
544
|
+
"supportsStore": false
|
|
545
|
+
}
|
|
546
|
+
},
|
|
463
547
|
{
|
|
464
548
|
"id": "parasail-trinity-large-thinking",
|
|
465
549
|
"name": "Trinity Large Thinking",
|
|
@@ -574,8 +658,8 @@
|
|
|
574
658
|
"text"
|
|
575
659
|
],
|
|
576
660
|
"cost": {
|
|
577
|
-
"input": 0.
|
|
578
|
-
"output": 0.
|
|
661
|
+
"input": 0.03,
|
|
662
|
+
"output": 0.15,
|
|
579
663
|
"cacheRead": 0.02,
|
|
580
664
|
"cacheWrite": 0
|
|
581
665
|
},
|
|
@@ -608,13 +692,33 @@
|
|
|
608
692
|
"supportsStore": false
|
|
609
693
|
}
|
|
610
694
|
},
|
|
695
|
+
{
|
|
696
|
+
"id": "parasail-llama-32-3b-instruct",
|
|
697
|
+
"name": "Llama 32 3b Instruct",
|
|
698
|
+
"reasoning": false,
|
|
699
|
+
"input": [
|
|
700
|
+
"text"
|
|
701
|
+
],
|
|
702
|
+
"cost": {
|
|
703
|
+
"input": 0.05,
|
|
704
|
+
"output": 0.33,
|
|
705
|
+
"cacheRead": 0,
|
|
706
|
+
"cacheWrite": 0
|
|
707
|
+
},
|
|
708
|
+
"contextWindow": 131072,
|
|
709
|
+
"maxTokens": 16384,
|
|
710
|
+
"compat": {
|
|
711
|
+
"maxTokensField": "max_completion_tokens",
|
|
712
|
+
"supportsDeveloperRole": false,
|
|
713
|
+
"supportsStore": false
|
|
714
|
+
}
|
|
715
|
+
},
|
|
611
716
|
{
|
|
612
717
|
"id": "parasail-mistral-small-32-24b",
|
|
613
|
-
"name": "Mistral Small
|
|
718
|
+
"name": "Mistral Small 32 24b",
|
|
614
719
|
"reasoning": false,
|
|
615
720
|
"input": [
|
|
616
|
-
"text"
|
|
617
|
-
"image"
|
|
721
|
+
"text"
|
|
618
722
|
],
|
|
619
723
|
"cost": {
|
|
620
724
|
"input": 0.09,
|
|
@@ -652,6 +756,69 @@
|
|
|
652
756
|
"supportsStore": false
|
|
653
757
|
}
|
|
654
758
|
},
|
|
759
|
+
{
|
|
760
|
+
"id": "parasail-rocinante-12b",
|
|
761
|
+
"name": "Rocinante 12b",
|
|
762
|
+
"reasoning": false,
|
|
763
|
+
"input": [
|
|
764
|
+
"text"
|
|
765
|
+
],
|
|
766
|
+
"cost": {
|
|
767
|
+
"input": 0.25,
|
|
768
|
+
"output": 0.5,
|
|
769
|
+
"cacheRead": 0,
|
|
770
|
+
"cacheWrite": 0
|
|
771
|
+
},
|
|
772
|
+
"contextWindow": 65536,
|
|
773
|
+
"maxTokens": 16384,
|
|
774
|
+
"compat": {
|
|
775
|
+
"maxTokensField": "max_completion_tokens",
|
|
776
|
+
"supportsDeveloperRole": false,
|
|
777
|
+
"supportsStore": false
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"id": "parasail-qwen3-embedding-4b",
|
|
782
|
+
"name": "Qwen3 Embedding 4b",
|
|
783
|
+
"reasoning": false,
|
|
784
|
+
"input": [
|
|
785
|
+
"text"
|
|
786
|
+
],
|
|
787
|
+
"cost": {
|
|
788
|
+
"input": 0.019,
|
|
789
|
+
"output": 0,
|
|
790
|
+
"cacheRead": 0,
|
|
791
|
+
"cacheWrite": 0
|
|
792
|
+
},
|
|
793
|
+
"contextWindow": 40960,
|
|
794
|
+
"maxTokens": 16384,
|
|
795
|
+
"compat": {
|
|
796
|
+
"maxTokensField": "max_completion_tokens",
|
|
797
|
+
"supportsDeveloperRole": false,
|
|
798
|
+
"supportsStore": false
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
"id": "parasail-qwen3-embedding-8b",
|
|
803
|
+
"name": "Qwen3 Embedding 8b",
|
|
804
|
+
"reasoning": false,
|
|
805
|
+
"input": [
|
|
806
|
+
"text"
|
|
807
|
+
],
|
|
808
|
+
"cost": {
|
|
809
|
+
"input": 0.03,
|
|
810
|
+
"output": 0,
|
|
811
|
+
"cacheRead": 0,
|
|
812
|
+
"cacheWrite": 0
|
|
813
|
+
},
|
|
814
|
+
"contextWindow": 40960,
|
|
815
|
+
"maxTokens": 16384,
|
|
816
|
+
"compat": {
|
|
817
|
+
"maxTokensField": "max_completion_tokens",
|
|
818
|
+
"supportsDeveloperRole": false,
|
|
819
|
+
"supportsStore": false
|
|
820
|
+
}
|
|
821
|
+
},
|
|
655
822
|
{
|
|
656
823
|
"id": "parasail-skyfall-36b-v2-fp8",
|
|
657
824
|
"name": "Skyfall 36b V2 Fp8",
|
|
@@ -672,5 +839,26 @@
|
|
|
672
839
|
"supportsDeveloperRole": false,
|
|
673
840
|
"supportsStore": false
|
|
674
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
|
+
}
|
|
675
863
|
}
|
|
676
864
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-parasail-provider",
|
|
3
|
-
"version": "1.0.
|
|
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",
|
package/scripts/update-models.js
CHANGED
|
@@ -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
|