pi-wafer-provider 1.1.2 → 1.1.3

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
@@ -80,6 +80,8 @@ pi
80
80
  | GLM 5.2 | Text | 1M | 16K | Free | Free | Free |
81
81
  | Glm5.2 Fast | Text | 1M | 16K | Free | Free | Free |
82
82
  | Kimi K2.6 | Text | 262K | 33K | $1.10 | $4.80 | $0.11 |
83
+ | Kimi K3 | Text | 912K | 16K | Free | Free | Free |
84
+ | Kimi K3 Fast | Text | 1M | 16K | Free | Free | Free |
83
85
  | MiniMax M3 | Text | 1M | 16K | Free | Free | Free |
84
86
  | Qwen 3.5 397B (A17B) | Text + Image | 262K | 33K | $0.60 | $3.60 | $0.06 |
85
87
 
@@ -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
 
@@ -123,7 +124,10 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
123
124
  function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData): JsonModel[] {
124
125
  const modelMap = new Map<string, JsonModel>();
125
126
 
126
- for (const model of base) {
127
+ // Seed with the base list plus grace-period deprecated models so patch.json
128
+ // entries apply to deprecated models exactly as while the model was live
129
+ // (withDeprecated keeps live data on id conflicts).
130
+ for (const model of withDeprecated(base)) {
127
131
  modelMap.set(model.id, model);
128
132
  }
129
133
 
@@ -275,6 +279,35 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
275
279
  return result;
276
280
  }
277
281
 
282
+ // Grace period for delisted models. When the provider API stops listing a
283
+ // model, update-models.js moves its last-known definition into
284
+ // deprecated-models.json (stamped with deprecatedAt) instead of dropping it.
285
+ // For 14 days the model keeps working here so in-flight sessions and saved
286
+ // model settings do not break; afterwards it is evicted permanently.
287
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
288
+
289
+ // Grace-period deprecated models with deprecation metadata stripped.
290
+ function activeDeprecatedModels(): JsonModel[] {
291
+ const now = Date.now();
292
+ const result: JsonModel[] = [];
293
+ for (const entry of Object.values(deprecatedData as Record<string, JsonModel & { deprecatedAt?: string }>)) {
294
+ if (!entry?.id) continue;
295
+ const removedAt = Date.parse(entry.deprecatedAt ?? "");
296
+ if (Number.isNaN(removedAt) || now - removedAt > DEPRECATED_MODEL_TTL_MS) continue;
297
+ const model = { ...entry } as JsonModel & { deprecatedAt?: string };
298
+ delete model.deprecatedAt;
299
+ result.push(model);
300
+ }
301
+ return result;
302
+ }
303
+
304
+ // Append grace-period deprecated models the list does not already have (live data wins).
305
+ function withDeprecated(models: JsonModel[]): JsonModel[] {
306
+ const seen = new Set(models.map((m) => m.id));
307
+ const extras = activeDeprecatedModels().filter((m) => !seen.has(m.id));
308
+ return extras.length > 0 ? [...models, ...extras] : models;
309
+ }
310
+
278
311
  function loadStaleModels(providerId: string, embeddedModels: JsonModel[]): JsonModel[] {
279
312
  const cached = loadCachedModels(providerId);
280
313
  if (!cached || cached.length === 0) return embeddedModels;
package/models.json CHANGED
@@ -85,6 +85,50 @@
85
85
  "supportsZdr": false
86
86
  }
87
87
  },
88
+ {
89
+ "id": "Kimi-K3",
90
+ "name": "Kimi K3",
91
+ "reasoning": false,
92
+ "input": [
93
+ "text"
94
+ ],
95
+ "cost": {
96
+ "input": 0,
97
+ "output": 0,
98
+ "cacheRead": 0,
99
+ "cacheWrite": 0
100
+ },
101
+ "contextWindow": 912384,
102
+ "maxTokens": 16384,
103
+ "compat": {
104
+ "maxTokensField": "max_completion_tokens",
105
+ "supportsDeveloperRole": false,
106
+ "supportsStore": false,
107
+ "supportsZdr": true
108
+ }
109
+ },
110
+ {
111
+ "id": "kimi-k3-fast",
112
+ "name": "Kimi K3 Fast",
113
+ "reasoning": false,
114
+ "input": [
115
+ "text"
116
+ ],
117
+ "cost": {
118
+ "input": 0,
119
+ "output": 0,
120
+ "cacheRead": 0,
121
+ "cacheWrite": 0
122
+ },
123
+ "contextWindow": 1048576,
124
+ "maxTokens": 16384,
125
+ "compat": {
126
+ "maxTokensField": "max_completion_tokens",
127
+ "supportsDeveloperRole": false,
128
+ "supportsStore": false,
129
+ "supportsZdr": true
130
+ }
131
+ },
88
132
  {
89
133
  "id": "MiniMax-M3",
90
134
  "name": "MiniMax M3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-wafer-provider",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Wafer Serverless provider extension for pi - Access Qwen3.5-397B-A17B, GLM-5.1, Kimi K2.6, and DeepSeek-V4 through the Wafer Serverless API",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -226,6 +226,67 @@ function updateReadme(models) {
226
226
 
227
227
  // ─── Main ────────────────────────────────────────────────────────────────────
228
228
 
229
+ // Grace period for delisted models: update-models.js moves models the API no
230
+ // longer lists into deprecated-models.json (stamped with deprecatedAt) instead
231
+ // of dropping them; the runtime appends them back so sessions and saved model
232
+ // settings keep working, and after 14 days they are evicted permanently.
233
+ const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
234
+
235
+ /**
236
+ * Reconcile deprecated-models.json against the freshly fetched model list.
237
+ * - in old models.json but not the API: moved into the deprecated file
238
+ * (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
239
+ * - back in the API: resurrected (dropped from the deprecated file)
240
+ * - deprecatedAt older than 14 days: evicted permanently
241
+ * Must run BEFORE the new models.json is written; it reads the old file itself.
242
+ */
243
+ function updateDeprecatedModels(modelsJsonPath, newModels) {
244
+ const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
245
+
246
+ let oldModels = [];
247
+ try {
248
+ const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
249
+ if (Array.isArray(parsed)) oldModels = parsed;
250
+ } catch { /* first run: no previous models.json */ }
251
+
252
+ let deprecated = {};
253
+ try {
254
+ const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
255
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
256
+ } catch { /* no graveyard yet */ }
257
+
258
+ const currentIds = new Set(newModels.map(m => m.id));
259
+ const now = new Date().toISOString();
260
+ const added = [];
261
+ const resurrected = [];
262
+ const evicted = [];
263
+
264
+ for (const old of oldModels) {
265
+ if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
266
+ deprecated[old.id] = { ...old, deprecatedAt: now };
267
+ added.push(old.id);
268
+ }
269
+ }
270
+
271
+ for (const [id, entry] of Object.entries(deprecated)) {
272
+ if (currentIds.has(id)) {
273
+ delete deprecated[id];
274
+ resurrected.push(id);
275
+ continue;
276
+ }
277
+ const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
278
+ if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
279
+ delete deprecated[id];
280
+ evicted.push(id);
281
+ }
282
+ }
283
+
284
+ if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
285
+ fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
286
+ console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
287
+ }
288
+ }
289
+
229
290
  async function main() {
230
291
  try {
231
292
  const apiModels = await fetchModels();
@@ -249,6 +310,8 @@ async function main() {
249
310
  models.sort((a, b) => a.name.localeCompare(b.name));
250
311
 
251
312
  // Save models.json (pure API output, no patch/custom baked in)
313
+ // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
314
+ updateDeprecatedModels(MODELS_JSON_PATH, models);
252
315
  saveJson(MODELS_JSON_PATH, models);
253
316
 
254
317
  // Build full model list for README: base → patch → custom