pi-hypercharm-provider 1.1.2 → 1.1.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/AGENTS.md +1 -0
- package/README.md +14 -11
- package/deprecated-models.json +1 -0
- package/index.ts +36 -3
- package/models.json +77 -29
- 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
|
@@ -15,7 +15,7 @@ _Hyperoptimized coding models — DeepSeek, GLM, Kimi, Qwen, MiniMax, Gemma, GPT
|
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
|
-
- **
|
|
18
|
+
- **23+ AI Models** including DeepSeek V4 Flash/Pro, GLM 5/5.1, Kimi K2.5/K2.6, Qwen3.6/3.7, MiniMax M2.7, Gemma 4, GPT-OSS, and Llama
|
|
19
19
|
- **DeepSeek Native Thinking** — Uses the `deepseek` thinking format with `reasoning_effort` and 384K max output for DeepSeek V4 models
|
|
20
20
|
- **OpenAI-compatible API** via Charm Hyper's `/v1/chat/completions` endpoint
|
|
21
21
|
- **Cost Tracking** with per-model pricing from the API
|
|
@@ -26,24 +26,27 @@ _Hyperoptimized coding models — DeepSeek, GLM, Kimi, Qwen, MiniMax, Gemma, GPT
|
|
|
26
26
|
|
|
27
27
|
| Model | Type | Context | Max Tokens | Input Cost | Output Cost |
|
|
28
28
|
|-------|------|---------|------------|------------|-------------|
|
|
29
|
-
| DeepSeek V4 Flash | Text | 1.0M | 384K | $0.
|
|
30
|
-
| DeepSeek V4 Pro | Text | 1.0M | 384K | $
|
|
31
|
-
| Gemma 4 26B A4B | Text + Image | 256K | 26K | $0.12 | $0.
|
|
29
|
+
| DeepSeek V4 Flash | Text | 1.0M | 384K | $0.20 | $0.40 |
|
|
30
|
+
| DeepSeek V4 Pro | Text | 1.0M | 384K | $2.40 | $4.80 |
|
|
31
|
+
| Gemma 4 26B A4B | Text + Image | 256K | 26K | $0.12 | $0.41 |
|
|
32
32
|
| GLM 5.1 | Text + Image | 203K | 131K | $1.40 | $4.40 |
|
|
33
|
-
| GLM-5 | Text | 203K | 20K | $0.
|
|
34
|
-
| GLM-5.2 | Text | 1.0M |
|
|
35
|
-
| GPT-OSS 120B | Text | 131K | 13K | $0.
|
|
36
|
-
| Kimi K2.5 | Text + Image | 262K | 26K | $0.
|
|
33
|
+
| GLM-5 | Text | 203K | 20K | $0.81 | $2.59 |
|
|
34
|
+
| GLM-5.2 | Text | 1.0M | 128K | $1.40 | $4.40 |
|
|
35
|
+
| GPT-OSS 120B | Text | 131K | 13K | $0.17 | $0.56 |
|
|
36
|
+
| Kimi K2.5 | Text + Image | 262K | 26K | $0.52 | $2.72 |
|
|
37
37
|
| Kimi K2.6 | Text + Image | 262K | 262K | $0.95 | $4.00 |
|
|
38
|
-
| Kimi K2.7 Code | Text |
|
|
38
|
+
| Kimi K2.7 Code | Text | 256K | 16K | $0.95 | $4.00 |
|
|
39
|
+
| Kimi K3 | Text | 1.0M | 131K | Free | Free |
|
|
39
40
|
| Llama 3.3 70B Instruct | Text | 128K | 13K | $0.61 | $1.04 |
|
|
40
|
-
| Llama 4 Maverick 17B 128E FP8 | Text | 430K | 43K | $0.
|
|
41
|
-
| MiniMax M2.7 | Text | 205K | 20K | $0.
|
|
41
|
+
| Llama 4 Maverick 17B 128E FP8 | Text | 430K | 43K | $0.28 | $0.93 |
|
|
42
|
+
| MiniMax M2.7 | Text | 205K | 20K | $0.42 | $1.56 |
|
|
43
|
+
| MiniMax M3 | Text | 512K | 512K | Free | Free |
|
|
42
44
|
| Qwen3 Coder 480B A35B INT4 | Text | 106K | 11K | $0.60 | $2.08 |
|
|
43
45
|
| Qwen3 Next 80B A3B | Text | 262K | 26K | $0.12 | $1.14 |
|
|
44
46
|
| Qwen3.6-Flash | Text + Image | 1.0M | 64K | $1.00 | $4.00 |
|
|
45
47
|
| Qwen3.6-Max | Text + Image | 256K | 64K | $2.00 | $12.00 |
|
|
46
48
|
| Qwen3.6-Plus | Text + Image | 1.0M | 64K | $2.00 | $6.00 |
|
|
49
|
+
| Qwen3.7-Flash | Text | 1.0M | 64K | Free | Free |
|
|
47
50
|
| Qwen3.7-Max | Text + Image | 1.0M | 64K | $2.50 | $7.50 |
|
|
48
51
|
| Qwen3.7-Plus | Text | 1.0M | 64K | $1.20 | $4.80 |
|
|
49
52
|
*Costs are per million tokens. Prices subject to change — check [hyper.charm.land](https://hyper.charm.land) for current pricing.*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { getAgentDir, type ExtensionAPI, type ModelRegistry } from "@earendil-wo
|
|
|
39
39
|
import modelsData from "./models.json" with { type: "json" };
|
|
40
40
|
import customModelsData from "./custom-models.json" with { type: "json" };
|
|
41
41
|
import patchData from "./patch.json" with { type: "json" };
|
|
42
|
+
import deprecatedData from "./deprecated-models.json" with { type: "json" };
|
|
42
43
|
import fs from "fs";
|
|
43
44
|
import path from "path";
|
|
44
45
|
|
|
@@ -48,7 +49,7 @@ interface JsonModel {
|
|
|
48
49
|
id: string;
|
|
49
50
|
name: string;
|
|
50
51
|
reasoning: boolean;
|
|
51
|
-
input:
|
|
52
|
+
input: ("text" | "image")[];
|
|
52
53
|
cost: {
|
|
53
54
|
input: number;
|
|
54
55
|
output: number;
|
|
@@ -71,7 +72,7 @@ interface JsonModel {
|
|
|
71
72
|
interface PatchEntry {
|
|
72
73
|
name?: string;
|
|
73
74
|
reasoning?: boolean;
|
|
74
|
-
input?:
|
|
75
|
+
input?: ("text" | "image")[];
|
|
75
76
|
cost?: {
|
|
76
77
|
input?: number;
|
|
77
78
|
output?: number;
|
|
@@ -124,7 +125,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
|
|
|
124
125
|
function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
|
|
125
126
|
const modelMap = new Map<string, JsonModel>();
|
|
126
127
|
|
|
127
|
-
|
|
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)) {
|
|
128
132
|
modelMap.set(model.id, model);
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -298,6 +302,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
|
|
|
298
302
|
return result;
|
|
299
303
|
}
|
|
300
304
|
|
|
305
|
+
// Grace period for delisted models. When the provider API stops listing a
|
|
306
|
+
// model, update-models.js moves its last-known definition into
|
|
307
|
+
// deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
|
|
308
|
+
// For 14 days the model keeps working here so in-flight sessions and saved
|
|
309
|
+
// model settings do not break; afterwards it is evicted permanently.
|
|
310
|
+
const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
|
|
311
|
+
|
|
312
|
+
// Grace-period deprecated models with deprecation metadata stripped.
|
|
313
|
+
function activeDeprecatedModels(): JsonModel[] {
|
|
314
|
+
const now = Date.now();
|
|
315
|
+
const result: JsonModel[] = [];
|
|
316
|
+
for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
|
|
317
|
+
if (!entry?.id) continue;
|
|
318
|
+
const removedAt = Date.parse(entry.deprecatedAt ?? "");
|
|
319
|
+
if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
|
|
320
|
+
const model = { ...entry } as JsonModel & { deprecatedAt?: string };
|
|
321
|
+
delete model.deprecatedAt;
|
|
322
|
+
result.push(model);
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Append grace-period deprecated models the list does not already have (live data wins).
|
|
328
|
+
function withDeprecated(models: JsonModel[]): JsonModel[] {
|
|
329
|
+
const seen = new Set(models.map((m) => m.id));
|
|
330
|
+
const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
|
|
331
|
+
return extras.length > 0 ? [...models, ...extras] : models;
|
|
332
|
+
}
|
|
333
|
+
|
|
301
334
|
function loadStaleModels(embeddedModels: JsonModel[]): JsonModel[] {
|
|
302
335
|
const cached = loadCachedModels();
|
|
303
336
|
if (!cached || cached.length === 0) return embeddedModels;
|
package/models.json
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"text"
|
|
8
8
|
],
|
|
9
9
|
"cost": {
|
|
10
|
-
"input": 0.
|
|
11
|
-
"output": 0.
|
|
10
|
+
"input": 0.2,
|
|
11
|
+
"output": 0.4,
|
|
12
12
|
"cacheRead": 0.03,
|
|
13
|
-
"cacheWrite": 0.
|
|
13
|
+
"cacheWrite": 0.04
|
|
14
14
|
},
|
|
15
15
|
"contextWindow": 1000000,
|
|
16
16
|
"maxTokens": 384000,
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"text"
|
|
39
39
|
],
|
|
40
40
|
"cost": {
|
|
41
|
-
"input":
|
|
42
|
-
"output":
|
|
41
|
+
"input": 2.4,
|
|
42
|
+
"output": 4.8,
|
|
43
43
|
"cacheRead": 0.15,
|
|
44
|
-
"cacheWrite": 0.
|
|
44
|
+
"cacheWrite": 0.2
|
|
45
45
|
},
|
|
46
46
|
"contextWindow": 1000000,
|
|
47
47
|
"maxTokens": 384000,
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"image"
|
|
71
71
|
],
|
|
72
72
|
"cost": {
|
|
73
|
-
"input": 0.
|
|
74
|
-
"output": 0.
|
|
75
|
-
"cacheRead": 0.
|
|
73
|
+
"input": 0.116,
|
|
74
|
+
"output": 0.408,
|
|
75
|
+
"cacheRead": 0.058,
|
|
76
76
|
"cacheWrite": 0
|
|
77
77
|
},
|
|
78
78
|
"contextWindow": 256000,
|
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
"text"
|
|
104
104
|
],
|
|
105
105
|
"cost": {
|
|
106
|
-
"input": 0.
|
|
107
|
-
"output": 2.
|
|
108
|
-
"cacheRead": 0.
|
|
106
|
+
"input": 0.81,
|
|
107
|
+
"output": 2.592,
|
|
108
|
+
"cacheRead": 0.405,
|
|
109
109
|
"cacheWrite": 0
|
|
110
110
|
},
|
|
111
111
|
"contextWindow": 202752,
|
|
@@ -122,10 +122,10 @@
|
|
|
122
122
|
"input": 1.4,
|
|
123
123
|
"output": 4.4,
|
|
124
124
|
"cacheRead": 0.26,
|
|
125
|
-
"cacheWrite": 0.
|
|
125
|
+
"cacheWrite": 0.28
|
|
126
126
|
},
|
|
127
|
-
"contextWindow":
|
|
128
|
-
"maxTokens":
|
|
127
|
+
"contextWindow": 1000000,
|
|
128
|
+
"maxTokens": 128000
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"id": "gpt-oss-120b",
|
|
@@ -135,9 +135,9 @@
|
|
|
135
135
|
"text"
|
|
136
136
|
],
|
|
137
137
|
"cost": {
|
|
138
|
-
"input": 0.
|
|
139
|
-
"output": 0.
|
|
140
|
-
"cacheRead": 0.
|
|
138
|
+
"input": 0.168,
|
|
139
|
+
"output": 0.562,
|
|
140
|
+
"cacheRead": 0.084,
|
|
141
141
|
"cacheWrite": 0
|
|
142
142
|
},
|
|
143
143
|
"contextWindow": 131072,
|
|
@@ -152,9 +152,9 @@
|
|
|
152
152
|
"image"
|
|
153
153
|
],
|
|
154
154
|
"cost": {
|
|
155
|
-
"input": 0.
|
|
156
|
-
"output": 2.
|
|
157
|
-
"cacheRead": 0.
|
|
155
|
+
"input": 0.518,
|
|
156
|
+
"output": 2.72,
|
|
157
|
+
"cacheRead": 0.259,
|
|
158
158
|
"cacheWrite": 0
|
|
159
159
|
},
|
|
160
160
|
"contextWindow": 262144,
|
|
@@ -190,8 +190,24 @@
|
|
|
190
190
|
"cacheRead": 0.16,
|
|
191
191
|
"cacheWrite": 0.19
|
|
192
192
|
},
|
|
193
|
-
"contextWindow":
|
|
194
|
-
"maxTokens":
|
|
193
|
+
"contextWindow": 256000,
|
|
194
|
+
"maxTokens": 16000
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"id": "kimi-k3",
|
|
198
|
+
"name": "Kimi K3",
|
|
199
|
+
"reasoning": false,
|
|
200
|
+
"input": [
|
|
201
|
+
"text"
|
|
202
|
+
],
|
|
203
|
+
"cost": {
|
|
204
|
+
"input": 0,
|
|
205
|
+
"output": 0,
|
|
206
|
+
"cacheRead": 0,
|
|
207
|
+
"cacheWrite": 0
|
|
208
|
+
},
|
|
209
|
+
"contextWindow": 1048576,
|
|
210
|
+
"maxTokens": 131072
|
|
195
211
|
},
|
|
196
212
|
{
|
|
197
213
|
"id": "llama-3.3-70b-instruct",
|
|
@@ -217,9 +233,9 @@
|
|
|
217
233
|
"text"
|
|
218
234
|
],
|
|
219
235
|
"cost": {
|
|
220
|
-
"input": 0.
|
|
221
|
-
"output": 0.
|
|
222
|
-
"cacheRead": 0.
|
|
236
|
+
"input": 0.284,
|
|
237
|
+
"output": 0.934,
|
|
238
|
+
"cacheRead": 0.142,
|
|
223
239
|
"cacheWrite": 0
|
|
224
240
|
},
|
|
225
241
|
"contextWindow": 430000,
|
|
@@ -233,14 +249,30 @@
|
|
|
233
249
|
"text"
|
|
234
250
|
],
|
|
235
251
|
"cost": {
|
|
236
|
-
"input": 0.
|
|
237
|
-
"output":
|
|
238
|
-
"cacheRead": 0.
|
|
252
|
+
"input": 0.42,
|
|
253
|
+
"output": 1.56,
|
|
254
|
+
"cacheRead": 0.21,
|
|
239
255
|
"cacheWrite": 0.06
|
|
240
256
|
},
|
|
241
257
|
"contextWindow": 204800,
|
|
242
258
|
"maxTokens": 20480
|
|
243
259
|
},
|
|
260
|
+
{
|
|
261
|
+
"id": "minimax-m3",
|
|
262
|
+
"name": "MiniMax M3",
|
|
263
|
+
"reasoning": false,
|
|
264
|
+
"input": [
|
|
265
|
+
"text"
|
|
266
|
+
],
|
|
267
|
+
"cost": {
|
|
268
|
+
"input": 0,
|
|
269
|
+
"output": 0,
|
|
270
|
+
"cacheRead": 0,
|
|
271
|
+
"cacheWrite": 0
|
|
272
|
+
},
|
|
273
|
+
"contextWindow": 512000,
|
|
274
|
+
"maxTokens": 512000
|
|
275
|
+
},
|
|
244
276
|
{
|
|
245
277
|
"id": "qwen3-coder-480b-a35b-instruct-int4-mixed-ar",
|
|
246
278
|
"name": "Qwen3 Coder 480B A35B INT4",
|
|
@@ -324,6 +356,22 @@
|
|
|
324
356
|
"contextWindow": 1000000,
|
|
325
357
|
"maxTokens": 64000
|
|
326
358
|
},
|
|
359
|
+
{
|
|
360
|
+
"id": "qwen3.7-flash",
|
|
361
|
+
"name": "Qwen3.7-Flash",
|
|
362
|
+
"reasoning": false,
|
|
363
|
+
"input": [
|
|
364
|
+
"text"
|
|
365
|
+
],
|
|
366
|
+
"cost": {
|
|
367
|
+
"input": 0,
|
|
368
|
+
"output": 0,
|
|
369
|
+
"cacheRead": 0,
|
|
370
|
+
"cacheWrite": 0
|
|
371
|
+
},
|
|
372
|
+
"contextWindow": 1000000,
|
|
373
|
+
"maxTokens": 64000
|
|
374
|
+
},
|
|
327
375
|
{
|
|
328
376
|
"id": "qwen3.7-max",
|
|
329
377
|
"name": "Qwen3.7-Max",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hypercharm-provider",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "HyperCharm provider extension for pi - Access DeepSeek, GLM, Kimi, Qwen, MiniMax, Gemma, and GPT-OSS models through the Charm Hyper API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
package/scripts/update-models.js
CHANGED
|
@@ -237,6 +237,67 @@ ${tableRows}`;
|
|
|
237
237
|
|
|
238
238
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
239
239
|
|
|
240
|
+
// Grace period for delisted models: update-models.js moves models the API no
|
|
241
|
+
// longer lists into deprecated-models.json (stamped with deprecatedAt) instead
|
|
242
|
+
// of dropping them; the runtime appends them back so sessions and saved model
|
|
243
|
+
// settings keep working, and after 14 days they are evicted permanently.
|
|
244
|
+
const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Reconcile deprecated-models.json against the freshly fetched model list.
|
|
248
|
+
* - in old models.json but not the API: moved into the deprecated file
|
|
249
|
+
* (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
|
|
250
|
+
* - back in the API: resurrected (dropped from the deprecated file)
|
|
251
|
+
* - deprecatedAt older than 14 days: evicted permanently
|
|
252
|
+
* Must run BEFORE the new models.json is written; it reads the old file itself.
|
|
253
|
+
*/
|
|
254
|
+
function updateDeprecatedModels(modelsJsonPath, newModels) {
|
|
255
|
+
const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
|
|
256
|
+
|
|
257
|
+
let oldModels = [];
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
|
|
260
|
+
if (Array.isArray(parsed)) oldModels = parsed;
|
|
261
|
+
} catch { /* first run: no previous models.json */ }
|
|
262
|
+
|
|
263
|
+
let deprecated = {};
|
|
264
|
+
try {
|
|
265
|
+
const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
|
|
266
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
|
|
267
|
+
} catch { /* no graveyard yet */ }
|
|
268
|
+
|
|
269
|
+
const currentIds = new Set(newModels.map(m => m.id));
|
|
270
|
+
const now = new Date().toISOString();
|
|
271
|
+
const added = [];
|
|
272
|
+
const resurrected = [];
|
|
273
|
+
const evicted = [];
|
|
274
|
+
|
|
275
|
+
for (const old of oldModels) {
|
|
276
|
+
if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
|
|
277
|
+
deprecated[old.id] = { ...old, deprecatedAt: now };
|
|
278
|
+
added.push(old.id);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
for (const [id, entry] of Object.entries(deprecated)) {
|
|
283
|
+
if (currentIds.has(id)) {
|
|
284
|
+
delete deprecated[id];
|
|
285
|
+
resurrected.push(id);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
|
|
289
|
+
if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
|
|
290
|
+
delete deprecated[id];
|
|
291
|
+
evicted.push(id);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
|
|
296
|
+
fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
|
|
297
|
+
console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
240
301
|
async function main() {
|
|
241
302
|
const apiKey = process.env.HYPERCHARM_API_KEY;
|
|
242
303
|
if (!apiKey) {
|
|
@@ -289,6 +350,8 @@ async function main() {
|
|
|
289
350
|
}
|
|
290
351
|
|
|
291
352
|
// Update models.json — curated API data
|
|
353
|
+
// Move delisted models to deprecated-models.json BEFORE models.json is overwritten
|
|
354
|
+
updateDeprecatedModels(MODELS_JSON_PATH, apiTransformed);
|
|
292
355
|
fs.writeFileSync(MODELS_JSON_PATH, JSON.stringify(apiTransformed, null, 2) + '\n');
|
|
293
356
|
console.log(`✓ Updated models.json (${apiTransformed.length} models)`);
|
|
294
357
|
|