pi-coralbricks-provider 1.0.26 → 1.0.28
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 +5 -7
- package/index.ts +29 -9
- package/models.json +2 -2
- package/package.json +1 -1
- package/patch.json +2 -0
- package/scripts/update-models.js +21 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# 🪸 pi-coralbricks-provider
|
|
4
4
|
|
|
5
|
-
**GLM 5.3 & DeepSeek V4.1 Flash
|
|
5
|
+
**GLM 5.3 & DeepSeek V4.1 Flash 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
|
-
- **
|
|
26
|
-
- **1M token context** on GLM and DeepSeek, with vision (image input) on DeepSeek V4.1 Flash
|
|
25
|
+
- **2 reasoning models** from Coral's live catalog — GLM 5.3 and DeepSeek V4.1 Flash (the `-fp4` twin slugs Coral still serves are hidden as duplicates)
|
|
26
|
+
- **1M token context** on GLM and DeepSeek, with vision (image input) on DeepSeek V4.1 Flash
|
|
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**
|
|
@@ -85,10 +85,8 @@ pi
|
|
|
85
85
|
|
|
86
86
|
| Model | Context | Vision | Reasoning | Input $/M | Cache Read $/M | Cache Write $/M | Output $/M |
|
|
87
87
|
|-------|---------|--------|-----------|-----------|-----------------|------------------|------------|
|
|
88
|
-
| DeepSeek V4.1 Flash | 1.0M | ✅ | ✅ | $0.
|
|
89
|
-
| DeepSeek V4.1 Flash FP4 | 1.0M | ✅ | ✅ | $0.30 | — | $0.09 | $1.20 |
|
|
88
|
+
| DeepSeek V4.1 Flash | 1.0M | ✅ | ✅ | $0.01 | — | $0.09 | $1.20 |
|
|
90
89
|
| GLM 5.3 | 1.0M | ❌ | ✅ | $1.12 | — | $1.68 | $4.40 |
|
|
91
|
-
| GLM 5.3 FP4 | 1.0M | ❌ | ✅ | $1.12 | — | $1.68 | $4.40 |
|
|
92
90
|
|
|
93
91
|
*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).*
|
|
94
92
|
|
|
@@ -188,7 +186,7 @@ Coral's gateway follows the OpenAI Chat Completions API:
|
|
|
188
186
|
|
|
189
187
|
### Patch Overrides & Custom Models
|
|
190
188
|
|
|
191
|
-
- **`patch.json`** — per-model overrides applied on top of `models.json` (reasoning flags, pricing corrections, compat settings, thinking level maps).
|
|
189
|
+
- **`patch.json`** — per-model overrides applied on top of `models.json` (reasoning flags, pricing corrections, compat settings, thinking level maps, `"hidden"`). One entry per model Coral serves: GLM 5.3 and DeepSeek V4.1 Flash carry the reasoning and compat settings, and their `-fp4` twins set `"hidden": true` so `/model` and the table above list each model once.
|
|
192
190
|
- **`custom-models.json`** — full model definitions for models Coral doesn't list. Merged after patch.
|
|
193
191
|
|
|
194
192
|
Merge order: `[live|cache|embedded] → patch.json → custom-models.json`
|
package/index.ts
CHANGED
|
@@ -73,10 +73,13 @@ interface JsonModel {
|
|
|
73
73
|
supportsStrictMode?: boolean;
|
|
74
74
|
requiresReasoningContentOnAssistantMessages?: boolean;
|
|
75
75
|
};
|
|
76
|
+
/** Curation-only (patch.json): keep a served model out of the catalog. */
|
|
77
|
+
hidden?: boolean;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
interface PatchEntry {
|
|
79
81
|
name?: string;
|
|
82
|
+
hidden?: boolean;
|
|
80
83
|
reasoning?: boolean;
|
|
81
84
|
input?: ("text" | "image")[];
|
|
82
85
|
cost?: {
|
|
@@ -106,6 +109,7 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
|
|
|
106
109
|
};
|
|
107
110
|
|
|
108
111
|
if (patch.name !== undefined) result.name = patch.name;
|
|
112
|
+
if (patch.hidden !== undefined) result.hidden = patch.hidden;
|
|
109
113
|
if (patch.reasoning !== undefined) result.reasoning = patch.reasoning;
|
|
110
114
|
if (patch.input !== undefined) result.input = patch.input;
|
|
111
115
|
if (patch.contextWindow !== undefined) result.contextWindow = patch.contextWindow;
|
|
@@ -166,7 +170,11 @@ function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData):
|
|
|
166
170
|
}
|
|
167
171
|
}
|
|
168
172
|
|
|
169
|
-
|
|
173
|
+
// Hidden models stay out of the registered catalog — and therefore out of
|
|
174
|
+
// `/model` — while `models.json` keeps mirroring what Coral serves for the key.
|
|
175
|
+
return Array.from(modelMap.values())
|
|
176
|
+
.filter((m) => !m.hidden)
|
|
177
|
+
.map(({ hidden: _hidden, ...model }) => model);
|
|
170
178
|
}
|
|
171
179
|
|
|
172
180
|
// Stale-While-Revalidate Model Sync
|
|
@@ -189,6 +197,17 @@ function toNumber(value: unknown): number {
|
|
|
189
197
|
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
190
198
|
}
|
|
191
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Full price of a written prompt token. Since 2026-10-08 Coral states the cache write ON TOP
|
|
202
|
+
* of input (/v1/models cache_write_per_m, catalog cacheWritePerM), and a written token is
|
|
203
|
+
* billed input + that. pi prices a written token at cacheWrite alone, so add the two. 0 when
|
|
204
|
+
* the row carries no cache-write rate. Rounded so 1.12 + 0.56 is 1.68, not 1.6800000000000002.
|
|
205
|
+
*/
|
|
206
|
+
function writtenTokenCost(input: number, cacheWriteOnTop: unknown): number {
|
|
207
|
+
if (typeof cacheWriteOnTop !== "number" || !Number.isFinite(cacheWriteOnTop)) return 0;
|
|
208
|
+
return Math.round((input + cacheWriteOnTop) * 1e6) / 1e6;
|
|
209
|
+
}
|
|
210
|
+
|
|
192
211
|
function displayName(id: string): string {
|
|
193
212
|
return id
|
|
194
213
|
.split(/[-_/]/)
|
|
@@ -225,8 +244,9 @@ function baseCompat() {
|
|
|
225
244
|
/**
|
|
226
245
|
* Transform a Coral /v1/models row. Coral reports per-million USD pricing
|
|
227
246
|
* (input_per_m / output_per_m / cached_input_per_m — cached reads are $0 on
|
|
228
|
-
* every model — and cache_write_per_m, the
|
|
229
|
-
* the cache), context_length, and supports_image_input.
|
|
247
|
+
* every model — and cache_write_per_m, the cache write ON TOP of input for a
|
|
248
|
+
* prompt token written to the cache), context_length, and supports_image_input.
|
|
249
|
+
* pi's cacheWrite is the full price of a written token: input + cache_write_per_m.
|
|
230
250
|
*/
|
|
231
251
|
function transformApiModel(apiModel: any): JsonModel | null {
|
|
232
252
|
if (!apiModel?.id) return null;
|
|
@@ -240,7 +260,7 @@ function transformApiModel(apiModel: any): JsonModel | null {
|
|
|
240
260
|
input: toNumber(pricing.input_per_m),
|
|
241
261
|
output: toNumber(pricing.output_per_m),
|
|
242
262
|
cacheRead: toNumber(pricing.cached_input_per_m),
|
|
243
|
-
cacheWrite: toNumber(pricing.cache_write_per_m),
|
|
263
|
+
cacheWrite: writtenTokenCost(toNumber(pricing.input_per_m), pricing.cache_write_per_m),
|
|
244
264
|
},
|
|
245
265
|
contextWindow: parseContextWindow(apiModel.context_length),
|
|
246
266
|
maxTokens: DEFAULT_MAX_TOKENS[apiModel.id] ?? FALLBACK_MAX_TOKENS,
|
|
@@ -253,10 +273,10 @@ function transformApiModel(apiModel: any): JsonModel | null {
|
|
|
253
273
|
* Transform a row from the public model catalog
|
|
254
274
|
* (https://www.coralbricks.ai/api/public/models) — the no-auth mirror of the
|
|
255
275
|
* gateway control plane. Fields: slug, name, contextWindow ("1M"),
|
|
256
|
-
* inputPerM / outputPerM
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* embedded rate.
|
|
276
|
+
* inputPerM / outputPerM, and since 2026-10-08 cacheWritePerM (the cache write on
|
|
277
|
+
* top of input). Coral's own prices are authoritative here; the parity/field vendor
|
|
278
|
+
* comparison fields are ignored. A row without cacheWritePerM leaves cacheWrite 0 so
|
|
279
|
+
* mergeWithEmbedded keeps the embedded rate.
|
|
260
280
|
*/
|
|
261
281
|
function transformCatalogModel(entry: any): JsonModel | null {
|
|
262
282
|
if (!entry?.slug) return null;
|
|
@@ -269,7 +289,7 @@ function transformCatalogModel(entry: any): JsonModel | null {
|
|
|
269
289
|
input: toNumber(entry.inputPerM),
|
|
270
290
|
output: toNumber(entry.outputPerM),
|
|
271
291
|
cacheRead: 0,
|
|
272
|
-
cacheWrite:
|
|
292
|
+
cacheWrite: writtenTokenCost(toNumber(entry.inputPerM), entry.cacheWritePerM),
|
|
273
293
|
},
|
|
274
294
|
contextWindow: parseContextWindow(entry.contextWindow),
|
|
275
295
|
maxTokens: DEFAULT_MAX_TOKENS[entry.slug] ?? FALLBACK_MAX_TOKENS,
|
package/models.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"image"
|
|
9
9
|
],
|
|
10
10
|
"cost": {
|
|
11
|
-
"input": 0.
|
|
11
|
+
"input": 0.01,
|
|
12
12
|
"output": 1.2,
|
|
13
13
|
"cacheRead": 0,
|
|
14
14
|
"cacheWrite": 0.09
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"image"
|
|
31
31
|
],
|
|
32
32
|
"cost": {
|
|
33
|
-
"input": 0.
|
|
33
|
+
"input": 0.01,
|
|
34
34
|
"output": 1.2,
|
|
35
35
|
"cacheRead": 0,
|
|
36
36
|
"cacheWrite": 0.09
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-coralbricks-provider",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
4
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",
|
package/patch.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"deepseek-v4.1-flash-fast-fp4": {
|
|
10
10
|
"name": "DeepSeek V4.1 Flash FP4",
|
|
11
|
+
"hidden": true,
|
|
11
12
|
"reasoning": true,
|
|
12
13
|
"input": ["text", "image"],
|
|
13
14
|
"thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
},
|
|
24
25
|
"glm-5.3-fp4": {
|
|
25
26
|
"name": "GLM 5.3 FP4",
|
|
27
|
+
"hidden": true,
|
|
26
28
|
"reasoning": true,
|
|
27
29
|
"thinkingLevelMap": { "off": "none", "minimal": null, "low": "low", "medium": null, "high": "high", "xhigh": null, "max": "max" },
|
|
28
30
|
"maxTokens": 131072,
|
package/scripts/update-models.js
CHANGED
|
@@ -166,6 +166,13 @@ function saveJson(filePath, data) {
|
|
|
166
166
|
console.log(`✓ Saved ${path.basename(filePath)}`);
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
// Coral states the cache write ON TOP of input (2026-10-08); pi's cacheWrite is the full
|
|
170
|
+
// price of a written token, input + it. 0 when the row has no cache-write rate.
|
|
171
|
+
function writtenTokenCost(input, cacheWriteOnTop) {
|
|
172
|
+
if (typeof cacheWriteOnTop !== 'number' || !Number.isFinite(cacheWriteOnTop)) return 0;
|
|
173
|
+
return Math.round((input + cacheWriteOnTop) * 1e6) / 1e6;
|
|
174
|
+
}
|
|
175
|
+
|
|
169
176
|
function toNumber(value) {
|
|
170
177
|
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
171
178
|
}
|
|
@@ -274,7 +281,9 @@ function transformApiModel(apiModel, existingModelsMap) {
|
|
|
274
281
|
if (typeof pricing.input_per_m === 'number') existing.cost.input = pricing.input_per_m;
|
|
275
282
|
if (typeof pricing.output_per_m === 'number') existing.cost.output = pricing.output_per_m;
|
|
276
283
|
existing.cost.cacheRead = toNumber(pricing.cached_input_per_m);
|
|
277
|
-
if (typeof pricing.cache_write_per_m === 'number')
|
|
284
|
+
if (typeof pricing.cache_write_per_m === 'number') {
|
|
285
|
+
existing.cost.cacheWrite = writtenTokenCost(existing.cost.input, pricing.cache_write_per_m);
|
|
286
|
+
}
|
|
278
287
|
existing.input = input;
|
|
279
288
|
return existing;
|
|
280
289
|
}
|
|
@@ -289,7 +298,7 @@ function transformApiModel(apiModel, existingModelsMap) {
|
|
|
289
298
|
input: toNumber(pricing.input_per_m),
|
|
290
299
|
output: toNumber(pricing.output_per_m),
|
|
291
300
|
cacheRead: toNumber(pricing.cached_input_per_m),
|
|
292
|
-
cacheWrite: toNumber(pricing.cache_write_per_m),
|
|
301
|
+
cacheWrite: writtenTokenCost(toNumber(pricing.input_per_m), pricing.cache_write_per_m),
|
|
293
302
|
},
|
|
294
303
|
contextWindow,
|
|
295
304
|
maxTokens,
|
|
@@ -311,6 +320,9 @@ function transformCatalogModel(entry, existingModelsMap) {
|
|
|
311
320
|
if (contextWindow) existing.contextWindow = contextWindow;
|
|
312
321
|
if (typeof entry.inputPerM === 'number') existing.cost.input = entry.inputPerM;
|
|
313
322
|
if (typeof entry.outputPerM === 'number') existing.cost.output = entry.outputPerM;
|
|
323
|
+
if (typeof entry.cacheWritePerM === 'number') {
|
|
324
|
+
existing.cost.cacheWrite = writtenTokenCost(existing.cost.input, entry.cacheWritePerM);
|
|
325
|
+
}
|
|
314
326
|
return existing;
|
|
315
327
|
}
|
|
316
328
|
|
|
@@ -323,7 +335,7 @@ function transformCatalogModel(entry, existingModelsMap) {
|
|
|
323
335
|
input: toNumber(entry.inputPerM),
|
|
324
336
|
output: toNumber(entry.outputPerM),
|
|
325
337
|
cacheRead: 0,
|
|
326
|
-
cacheWrite:
|
|
338
|
+
cacheWrite: writtenTokenCost(toNumber(entry.inputPerM), entry.cacheWritePerM),
|
|
327
339
|
},
|
|
328
340
|
contextWindow,
|
|
329
341
|
maxTokens,
|
|
@@ -347,6 +359,7 @@ function applyPatch(model, patch) {
|
|
|
347
359
|
thinkingLevelMap: model.thinkingLevelMap ? { ...model.thinkingLevelMap } : undefined,
|
|
348
360
|
};
|
|
349
361
|
if (patch.name !== undefined) result.name = patch.name;
|
|
362
|
+
if (patch.hidden !== undefined) result.hidden = patch.hidden;
|
|
350
363
|
if (patch.reasoning !== undefined) result.reasoning = patch.reasoning;
|
|
351
364
|
if (patch.input !== undefined) result.input = patch.input;
|
|
352
365
|
if (patch.contextWindow !== undefined) result.contextWindow = patch.contextWindow;
|
|
@@ -400,7 +413,11 @@ function buildModels(baseModels, customModels, patchData) {
|
|
|
400
413
|
modelMap.set(model.id, model);
|
|
401
414
|
}
|
|
402
415
|
}
|
|
403
|
-
|
|
416
|
+
// Mirrors index.ts buildModels: hidden models are served by Coral but kept out
|
|
417
|
+
// of the catalog the provider offers, so the README table skips them too.
|
|
418
|
+
return Array.from(modelMap.values())
|
|
419
|
+
.filter((m) => !m.hidden)
|
|
420
|
+
.map(({ hidden: _hidden, ...model }) => model);
|
|
404
421
|
}
|
|
405
422
|
|
|
406
423
|
// README generation
|