pi-coralbricks-provider 1.0.25 → 1.0.26

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # 🪸 pi-coralbricks-provider
4
4
 
5
- **GLM 5.3, GLM 5.3 Flash & DeepSeek V4.1 Flash through [Coral Bricks](https://www.coralbricks.ai)**
5
+ **GLM 5.3 & DeepSeek V4.1 Flash (FP4 variants included) through [Coral Bricks](https://www.coralbricks.ai)**
6
6
 
7
7
  _A [pi](https://github.com/earendil-works/pi-coding-agent) provider extension for Coral's OpenAI-compatible inference gateway — up to **1M context** on open models._
8
8
 
@@ -22,8 +22,8 @@ Run `bun run test:pi` for offline manifest, catalog, lifecycle and streaming che
22
22
 
23
23
  ## Features
24
24
 
25
- - **3 reasoning models** from Coral's live catalog — GLM 5.3 FP4, GLM 5.3 Flash, and DeepSeek V4.1 Flash
26
- - **1M token context** on GLM and DeepSeek, with vision (image input) on GLM 5.3 Flash and DeepSeek V4.1 Flash
25
+ - **4 reasoning models** from Coral's live catalog — GLM 5.3 and its FP4 variant, DeepSeek V4.1 Flash and its FP4 variant
26
+ - **1M token context** on GLM and DeepSeek, with vision (image input) on DeepSeek V4.1 Flash and its FP4 variant
27
27
  - **OpenAI-compatible API** — standard `/v1/chat/completions`, streaming, and tool calling
28
28
  - **Per-family thinking levels** — zai-style `thinking` control for GLM (including a *real* off switch), `reasoning_effort` for DeepSeek V4.1 Flash
29
29
  - **Accurate cost tracking** — input, cache-write and output rates mirror Coral's [published pricing](https://www.coralbricks.ai/pricing), and cached reads are **$0 on every model**
@@ -86,7 +86,9 @@ pi
86
86
  | Model | Context | Vision | Reasoning | Input $/M | Cache Read $/M | Cache Write $/M | Output $/M |
87
87
  |-------|---------|--------|-----------|-----------|-----------------|------------------|------------|
88
88
  | DeepSeek V4.1 Flash | 1.0M | ✅ | ✅ | $0.30 | — | $0.09 | $1.20 |
89
+ | DeepSeek V4.1 Flash FP4 | 1.0M | ✅ | ✅ | $0.30 | — | $0.09 | $1.20 |
89
90
  | GLM 5.3 | 1.0M | ❌ | ✅ | $1.12 | — | $1.68 | $4.40 |
91
+ | GLM 5.3 FP4 | 1.0M | ❌ | ✅ | $1.12 | — | $1.68 | $4.40 |
90
92
 
91
93
  *Costs are per million tokens. Cache Read shows — because Coral bills cached input at **$0** on every model. Prompt tokens Coral has not cached yet are billed once at the Cache Write rate, in place of the Input rate. Prices subject to change — check [Coral's live catalog](https://www.coralbricks.ai/api/public/models).*
92
94
 
@@ -168,7 +170,7 @@ Verified against the live gateway:
168
170
  | Model | Format | off | low | medium | high | max |
169
171
  |-------|--------|-----|-----|--------|------|-----|
170
172
  | GLM 5.3 FP4 | `thinking: {type}` + `reasoning_effort` | ✅ | ✅ | — | ✅ | ✅ |
171
- | GLM 5.3 Flash | `thinking: {type}` + `reasoning_effort` | ✅ | ✅ | — | ✅ | ✅ |
173
+ | GLM 5.3 Flash (retired) | `thinking: {type}` + `reasoning_effort` | ✅ | ✅ | — | ✅ | ✅ |
172
174
  | DeepSeek V4.1 Flash | `reasoning_effort` | ✅ | ✅ | — | ✅ | ✅ |
173
175
 
174
176
  - **GLM** accepts zai-style `thinking: {type: "disabled"}` on Coral — pi's *off* level turns thinking off, though a short preamble of a few dozen tokens can still appear (the upstream Z.ai API has no off at all, so this differs from the canonical Z.ai map).
@@ -186,7 +188,7 @@ Coral's gateway follows the OpenAI Chat Completions API:
186
188
 
187
189
  ### Patch Overrides & Custom Models
188
190
 
189
- - **`patch.json`** — per-model overrides applied on top of `models.json` (reasoning flags, pricing corrections, compat settings, thinking level maps). Currently carries two entries: GLM 5.3 Flash (reasoning, image input, its thinking level map and compat settings) and DeepSeek V4.1 Flash (reasoning, its thinking level map and compat settings).
191
+ - **`patch.json`** — per-model overrides applied on top of `models.json` (reasoning flags, pricing corrections, compat settings, thinking level maps). Currently one entry per live model: GLM 5.3 and its FP4 twin (reasoning, thinking level map, zai-style compat settings) and DeepSeek V4.1 Flash and its FP4 twin (reasoning, image input, thinking level map, `openai`-style compat settings).
190
192
  - **`custom-models.json`** — full model definitions for models Coral doesn't list. Merged after patch.
191
193
 
192
194
  Merge order: `[live|cache|embedded] → patch.json → custom-models.json`
@@ -198,7 +200,7 @@ Validated with [synbad](https://github.com/synthetic-lab/synbad) — Synthetic's
198
200
  | Model | Unary | Stream | Notes |
199
201
  |-------|-------|--------|-------|
200
202
  | GLM 5.3 FP4 | 13/13 ✅ | 13/13 ✅ | 5/5 runs |
201
- | GLM 5.3 Flash | 12/13 ⚠️ | 12/13 ⚠️ | `reasoning/reasoning-parsing` passes 4 of 5 runs; see below |
203
+ | GLM 5.3 Flash (retired) | 12/13 ⚠️ | 12/13 ⚠️ | `reasoning/reasoning-parsing` passes 4 of 5 runs; see below |
202
204
  | GPT-OSS 120B (retired) | 12/13 ⚠️ | 12/13 ⚠️ | `tools/parallel-tool` 0 of 5 runs, before Coral retired the model |
203
205
  | DeepSeek V4.1 Flash | 12/13 ⚠️ | 12/13 ⚠️ | `tools/octo-list-no-optional-args` passes 1 of 5 runs; see below |
204
206
  | Kimi K3 (retired) | 13/13 ✅ | 13/13 ✅ | 5/5 runs, before Coral retired the model |
@@ -1,103 +1 @@
1
- {
2
- "deepseek-v4.1-flash-fast-fp4": {
3
- "id": "deepseek-v4.1-flash-fast-fp4",
4
- "name": "DeepSeek V4.1 Flash",
5
- "reasoning": false,
6
- "input": [
7
- "text",
8
- "image"
9
- ],
10
- "cost": {
11
- "input": 0.3,
12
- "output": 1.2,
13
- "cacheRead": 0,
14
- "cacheWrite": 0.09
15
- },
16
- "contextWindow": 1048576,
17
- "maxTokens": 32768,
18
- "compat": {
19
- "supportsDeveloperRole": false,
20
- "supportsStore": false,
21
- "maxTokensField": "max_tokens"
22
- },
23
- "deprecatedAt": "2026-10-05T00:00:07.034Z"
24
- },
25
- "glm-5.3-flash-fp4": {
26
- "id": "glm-5.3-flash-fp4",
27
- "name": "GLM 5.3 Flash",
28
- "reasoning": false,
29
- "input": [
30
- "text",
31
- "image"
32
- ],
33
- "cost": {
34
- "input": 0.15,
35
- "output": 0.5,
36
- "cacheRead": 0,
37
- "cacheWrite": 0.23
38
- },
39
- "contextWindow": 1048576,
40
- "maxTokens": 32768,
41
- "compat": {
42
- "supportsDeveloperRole": false,
43
- "supportsStore": false,
44
- "maxTokensField": "max_tokens"
45
- },
46
- "deprecatedAt": "2026-10-05T00:00:07.034Z"
47
- },
48
- "glm-5.3-fp4": {
49
- "id": "glm-5.3-fp4",
50
- "name": "GLM 5.3 FP4",
51
- "reasoning": true,
52
- "input": [
53
- "text"
54
- ],
55
- "cost": {
56
- "input": 1.12,
57
- "output": 4.4,
58
- "cacheRead": 0,
59
- "cacheWrite": 1.68
60
- },
61
- "contextWindow": 1048576,
62
- "maxTokens": 131072,
63
- "thinkingLevelMap": {
64
- "off": "none",
65
- "minimal": null,
66
- "low": "low",
67
- "medium": null,
68
- "high": "high",
69
- "xhigh": null,
70
- "max": "max"
71
- },
72
- "compat": {
73
- "supportsStore": false,
74
- "supportsDeveloperRole": false,
75
- "supportsReasoningEffort": true,
76
- "maxTokensField": "max_tokens",
77
- "thinkingFormat": "zai"
78
- },
79
- "deprecatedAt": "2026-10-05T00:00:07.034Z"
80
- },
81
- "glm-5.3-flash-fast": {
82
- "id": "glm-5.3-flash-fast",
83
- "name": "GLM 5.3 Flash",
84
- "reasoning": false,
85
- "input": [
86
- "text"
87
- ],
88
- "cost": {
89
- "input": 0.15,
90
- "output": 0.5,
91
- "cacheRead": 0,
92
- "cacheWrite": 0
93
- },
94
- "contextWindow": 1048576,
95
- "maxTokens": 32768,
96
- "compat": {
97
- "supportsDeveloperRole": false,
98
- "supportsStore": false,
99
- "maxTokensField": "max_tokens"
100
- },
101
- "deprecatedAt": "2026-10-07T00:00:09.219Z"
102
- }
103
- }
1
+ {}
package/models.json CHANGED
@@ -4,13 +4,36 @@
4
4
  "name": "DeepSeek V4.1 Flash",
5
5
  "reasoning": false,
6
6
  "input": [
7
- "text"
7
+ "text",
8
+ "image"
9
+ ],
10
+ "cost": {
11
+ "input": 0.3,
12
+ "output": 1.2,
13
+ "cacheRead": 0,
14
+ "cacheWrite": 0.09
15
+ },
16
+ "contextWindow": 1048576,
17
+ "maxTokens": 32768,
18
+ "compat": {
19
+ "supportsDeveloperRole": false,
20
+ "supportsStore": false,
21
+ "maxTokensField": "max_tokens"
22
+ }
23
+ },
24
+ {
25
+ "id": "deepseek-v4.1-flash-fast-fp4",
26
+ "name": "Deepseek V4.1 Flash Fast Fp4",
27
+ "reasoning": false,
28
+ "input": [
29
+ "text",
30
+ "image"
8
31
  ],
9
32
  "cost": {
10
33
  "input": 0.3,
11
34
  "output": 1.2,
12
35
  "cacheRead": 0,
13
- "cacheWrite": 0
36
+ "cacheWrite": 0.09
14
37
  },
15
38
  "contextWindow": 1048576,
16
39
  "maxTokens": 32768,
@@ -31,7 +54,28 @@
31
54
  "input": 1.12,
32
55
  "output": 4.4,
33
56
  "cacheRead": 0,
34
- "cacheWrite": 0
57
+ "cacheWrite": 1.68
58
+ },
59
+ "contextWindow": 1048576,
60
+ "maxTokens": 32768,
61
+ "compat": {
62
+ "supportsDeveloperRole": false,
63
+ "supportsStore": false,
64
+ "maxTokensField": "max_tokens"
65
+ }
66
+ },
67
+ {
68
+ "id": "glm-5.3-fp4",
69
+ "name": "Glm 5.3 Fp4",
70
+ "reasoning": false,
71
+ "input": [
72
+ "text"
73
+ ],
74
+ "cost": {
75
+ "input": 1.12,
76
+ "output": 4.4,
77
+ "cacheRead": 0,
78
+ "cacheWrite": 1.68
35
79
  },
36
80
  "contextWindow": 1048576,
37
81
  "maxTokens": 32768,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-coralbricks-provider",
3
- "version": "1.0.25",
4
- "description": "CoralBricks provider extension for pi - Access GLM 5.3, GLM 5.3 Flash, and DeepSeek V4.1 Flash through the Coral Inference API",
3
+ "version": "1.0.26",
4
+ "description": "CoralBricks provider extension for pi - Access GLM 5.3 and DeepSeek V4.1 Flash (FP4 variants included) through the Coral Inference API",
5
5
  "author": "monotykamary",
6
6
  "homepage": "https://github.com/monotykamary/pi-coralbricks-provider#readme",
7
7
  "repository": {
package/patch.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "compat": { "supportsReasoningEffort": true, "thinkingFormat": "openai" }
8
8
  },
9
9
  "deepseek-v4.1-flash-fast-fp4": {
10
+ "name": "DeepSeek V4.1 Flash FP4",
10
11
  "reasoning": true,
11
12
  "input": ["text", "image"],
12
13
  "thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
@@ -21,26 +22,11 @@
21
22
  "compat": { "supportsReasoningEffort": true, "thinkingFormat": "zai" }
22
23
  },
23
24
  "glm-5.3-fp4": {
25
+ "name": "GLM 5.3 FP4",
24
26
  "reasoning": true,
25
27
  "thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
26
28
  "maxTokens": 131072,
27
29
  "cost": { "cacheWrite": 1.68 },
28
30
  "compat": { "supportsReasoningEffort": true, "thinkingFormat": "zai" }
29
- },
30
- "glm-5.3-flash-fast": {
31
- "reasoning": true,
32
- "input": ["text", "image"],
33
- "thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
34
- "maxTokens": 131072,
35
- "cost": { "cacheWrite": 0.23 },
36
- "compat": { "supportsReasoningEffort": true, "thinkingFormat": "zai" }
37
- },
38
- "glm-5.3-flash-fp4": {
39
- "reasoning": true,
40
- "input": ["text", "image"],
41
- "thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
42
- "maxTokens": 131072,
43
- "cost": { "cacheWrite": 0.23 },
44
- "compat": { "supportsReasoningEffort": true, "thinkingFormat": "zai" }
45
31
  }
46
32
  }
@@ -14,6 +14,10 @@
14
14
  * same control-plane data without a key (contextWindow like "1M", inputPerM,
15
15
  * outputPerM). Used when no key is configured or /v1/models fails.
16
16
  *
17
+ * The catalog is a subset: it lists each model's canonical slug, not every
18
+ * variant enabled for a key, so a catalog run only ever adds or keeps models —
19
+ * it never delists one (see keepUnlistedModels).
20
+ *
17
21
  * models.json is the source of truth for curated specs — the script preserves
18
22
  * existing data and only adds new models with API-derived defaults.
19
23
  * Curate models.json manually after new model discovery.
@@ -219,18 +223,38 @@ async function fetchPublicCatalog() {
219
223
  return models;
220
224
  }
221
225
 
222
- async function fetchModels() {
223
- const apiKey = resolveApiKey();
226
+ async function fetchModels(apiKey) {
224
227
  if (apiKey) {
225
228
  try {
226
- return await fetchV1Models(apiKey);
229
+ return { models: await fetchV1Models(apiKey), authoritative: true };
227
230
  } catch (error) {
228
231
  console.warn(`⚠ /v1/models failed (${error.message}); falling back to public catalog`);
229
232
  }
230
233
  } else {
231
234
  console.warn('⚠ No API key found (no `coralbricks` credential in ' + AUTH_JSON_PATH + ', CORALBRICKS_API_KEY unset); using the public catalog');
232
235
  }
233
- return fetchPublicCatalog();
236
+ return { models: await fetchPublicCatalog(), authoritative: false };
237
+ }
238
+
239
+ /**
240
+ * The public catalog is a subset of the per-key /v1/models set: it advertises a
241
+ * model's canonical slug, not every variant enabled for the key (the -fp4 twins
242
+ * are listed by /v1/models only). It can vouch for what exists, never for what
243
+ * is gone, so a non-authoritative fetch keeps every already-known model it does
244
+ * not list: a keyless run may discover models, it may not delist them. Returns
245
+ * the ids it kept, in the order they were appended.
246
+ */
247
+ function keepUnlistedModels(models, existingModelsMap, authoritative) {
248
+ if (authoritative) return [];
249
+ const seen = new Set(models.map((m) => m.id));
250
+ const kept = [];
251
+ for (const [id, existing] of Object.entries(existingModelsMap)) {
252
+ if (!seen.has(id)) {
253
+ models.push(existing);
254
+ kept.push(id);
255
+ }
256
+ }
257
+ return kept;
234
258
  }
235
259
 
236
260
  // Transform API model → models.json entry
@@ -547,7 +571,8 @@ async function main() {
547
571
  return;
548
572
  }
549
573
 
550
- const apiModels = await fetchModels();
574
+ const apiKey = resolveApiKey();
575
+ const { models: apiModels, authoritative } = await fetchModels(apiKey);
551
576
 
552
577
  // Load existing models.json — source of truth for curated specs
553
578
  const existingModels = loadJson(MODELS_JSON_PATH);
@@ -562,15 +587,20 @@ async function main() {
562
587
  isCatalogShape ? transformCatalogModel(m, existingModelsMap) : transformApiModel(m, existingModelsMap)
563
588
  );
564
589
 
565
- // Live API is authoritative — models absent from API are removed
566
- // (embedded data is already used for enrichment in transformApiModel)
590
+ // Live API is authoritative — models absent from it are removed (embedded
591
+ // data is already used for enrichment in transformApiModel). A catalog run
592
+ // is not: keep every known model it does not list.
593
+ const kept = keepUnlistedModels(models, existingModelsMap, authoritative);
594
+ if (kept.length > 0) {
595
+ console.warn(`⚠ Public catalog listed ${kept.length} fewer model(s) than models.json; kept: ${kept.join(', ')}`);
596
+ }
567
597
 
568
598
  // Sort by model name
569
599
  models.sort((a, b) => a.name.localeCompare(b.name));
570
600
 
571
601
  // Save models.json (pure API output, no patch/custom baked in)
572
602
  // Move delisted models to deprecated-models.json BEFORE models.json is overwritten
573
- await updateDeprecatedModels(MODELS_JSON_PATH, models, resolveApiKey());
603
+ await updateDeprecatedModels(MODELS_JSON_PATH, models, apiKey);
574
604
  saveJson(MODELS_JSON_PATH, models);
575
605
 
576
606
  // Build the README model list: base → patch → custom. Grace-period models stay
@@ -608,4 +638,4 @@ if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToP
608
638
  main();
609
639
  }
610
640
 
611
- export { isRetiredUpstream, updateDeprecatedModels };
641
+ export { isRetiredUpstream, keepUnlistedModels, updateDeprecatedModels };