pi-opencode-go-provider 1.0.8 → 1.0.10
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 +6 -5
- package/package.json +5 -1
- package/.github/FUNDING.yml +0 -4
- package/AGENTS.md +0 -57
- package/custom-models.json +0 -1
- package/deprecated-models.json +0 -1
- package/models.json +0 -336
- package/patch.json +0 -176
- package/scripts/update-models.js +0 -293
package/README.md
CHANGED
|
@@ -68,10 +68,11 @@ pi
|
|
|
68
68
|
|
|
69
69
|
| Model | API | Type | Context | Max Tokens | Input Cost | Output Cost |
|
|
70
70
|
|-------|-----|------|---------|------------|------------|-------------|
|
|
71
|
-
| DeepSeek V4 Flash | Completions | Text | 1.0M | 384K | $0.
|
|
72
|
-
| DeepSeek V4 Pro | Completions | Text | 1.0M | 384K | $0.
|
|
71
|
+
| DeepSeek V4 Flash | Completions | Text | 1.0M | 384K | $0.22 | $0.66 |
|
|
72
|
+
| DeepSeek V4 Pro (New) | Completions | Text | 1.0M | 384K | $0.66 | $1.98 |
|
|
73
73
|
| GLM-5.1 | Completions | Text | 203K | 33K | $1.40 | $4.40 |
|
|
74
74
|
| GLM-5.2 | Completions | Text | 1.0M | 131K | $1.40 | $4.40 |
|
|
75
|
+
| GLM-5.3 | Completions | Text | 1.0M | 131K | $1.40 | $4.40 |
|
|
75
76
|
| GPT-5.6 Luna (2x usage) | Responses | Text + Image | 1.1M | 128K | $0.10 | $0.60 |
|
|
76
77
|
| Grok 4.5 | Responses | Text + Image | 500K | 500K | $2.00 | $6.00 |
|
|
77
78
|
| Hy3 | Completions | Text | 256K | 64K | $0.14 | $0.58 |
|
|
@@ -83,9 +84,9 @@ pi
|
|
|
83
84
|
| MiniMax-M2.7 | Completions | Text | 205K | 131K | $0.30 | $1.20 |
|
|
84
85
|
| MiniMax-M3 | Anthropic | Text + Image | 1.0M | 131K | $0.30 | $1.20 |
|
|
85
86
|
| Qwen3.6 Plus | Completions | Text + Image | 1.0M | 66K | $0.50 | $3.00 |
|
|
86
|
-
| Qwen3.7 Max |
|
|
87
|
-
| Qwen3.7 Plus |
|
|
88
|
-
| Qwen3.8 Max |
|
|
87
|
+
| Qwen3.7 Max | Completions | Text | 1.0M | 66K | $2.50 | $7.50 |
|
|
88
|
+
| Qwen3.7 Plus | Completions | Text + Image | 1.0M | 66K | $0.40 | $1.60 |
|
|
89
|
+
| Qwen3.8 Max | Completions | Text + Image | 1.0M | 131K | $2.00 | $6.00 |
|
|
89
90
|
*Costs are per million tokens. Prices subject to change - check [opencode.ai](https://opencode.ai) for current pricing.*
|
|
90
91
|
|
|
91
92
|
## Usage
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-opencode-go-provider",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Opencode Go provider extension for pi - Fast, efficient GLM, Kimi, and MiniMax models through the opencode.ai API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"./index.ts"
|
|
24
24
|
]
|
|
25
25
|
},
|
|
26
|
+
"files": [
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
26
30
|
"scripts": {
|
|
27
31
|
"clean": "echo 'nothing to clean'",
|
|
28
32
|
"build": "echo 'nothing to build'",
|
package/.github/FUNDING.yml
DELETED
package/AGENTS.md
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
## DO NOT EDIT — Auto-generated Files
|
|
4
|
-
|
|
5
|
-
The following files are **idempotent** and regenerated by `scripts/update-models.js`. Never edit them directly — your changes will be overwritten on the next model sync.
|
|
6
|
-
|
|
7
|
-
| File | Why it's auto-generated |
|
|
8
|
-
|------|------------------------|
|
|
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. |
|
|
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. |
|
|
12
|
-
|
|
13
|
-
## Correct Files to Edit
|
|
14
|
-
|
|
15
|
-
When a model needs overrides, new properties, or corrections, edit the appropriate source file below. These are the **source of truth** that the update script reads but never writes.
|
|
16
|
-
|
|
17
|
-
| File | Purpose |
|
|
18
|
-
|------|---------|
|
|
19
|
-
| `patch.json` | Per-model overrides keyed by model ID. Add reasoning flags, compat settings, pricing corrections, thinking level maps, etc. Applied on top of `models.json` at runtime and for README generation. |
|
|
20
|
-
| `custom-models.json` | Models that don't exist in the provider API (hidden models, router endpoints, cross-provider aliases). Merged after patch. Format: array of full model objects (same schema as `models.json` entries). |
|
|
21
|
-
| `index.ts` | Provider extension code. |
|
|
22
|
-
| `scripts/update-models.js` | The sync script itself (edit only if changing how models are fetched/transformed). |
|
|
23
|
-
|
|
24
|
-
## Data Flow
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
Provider API ──fetch──► models.json ──apply──► patch.json ──merge──► custom-models.json
|
|
28
|
-
│ │ │
|
|
29
|
-
└────────────────────────────┴──────────────────────┘
|
|
30
|
-
│
|
|
31
|
-
README model table
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
1. `models.json` — base data from the provider API (auto-generated, DO NOT EDIT)
|
|
35
|
-
2. `patch.json` — overrides applied on top (EDIT THIS for corrections/enrichments)
|
|
36
|
-
3. `custom-models.json` — additional models not in the API (EDIT THIS for new models)
|
|
37
|
-
4. README table — rendered from the merged result of all three (auto-generated, DO NOT EDIT)
|
|
38
|
-
|
|
39
|
-
## Common Tasks
|
|
40
|
-
|
|
41
|
-
### Add a compat setting or override pricing for an existing model
|
|
42
|
-
→ Edit `patch.json`. Add an entry keyed by the model's `id`.
|
|
43
|
-
|
|
44
|
-
### Add a model not available in the provider API
|
|
45
|
-
→ Edit `custom-models.json`. Add a full model object to the array.
|
|
46
|
-
|
|
47
|
-
### Update models from the provider API
|
|
48
|
-
→ Run `node scripts/update-models.js` (may require an API key env var).
|
|
49
|
-
|
|
50
|
-
### Regenerate the README model table
|
|
51
|
-
→ Run `node scripts/update-models.js` — it updates both `models.json` and the README table.
|
|
52
|
-
|
|
53
|
-
## TL;DR
|
|
54
|
-
|
|
55
|
-
- **Never edit `models.json`** — edit `patch.json` instead.
|
|
56
|
-
- **Never edit the README model table** — run the update script instead.
|
|
57
|
-
- `patch.json` and `custom-models.json` are the source files you should modify.
|
package/custom-models.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
[]
|
package/deprecated-models.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
package/models.json
DELETED
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "qwen3.7-plus",
|
|
4
|
-
"name": "Qwen3.7 Plus",
|
|
5
|
-
"api": "anthropic-messages",
|
|
6
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
7
|
-
"reasoning": true,
|
|
8
|
-
"input": [
|
|
9
|
-
"text",
|
|
10
|
-
"image"
|
|
11
|
-
],
|
|
12
|
-
"cost": {
|
|
13
|
-
"input": 0.4,
|
|
14
|
-
"output": 1.6,
|
|
15
|
-
"cacheRead": 0.04,
|
|
16
|
-
"cacheWrite": 0.5
|
|
17
|
-
},
|
|
18
|
-
"contextWindow": 1000000,
|
|
19
|
-
"maxTokens": 65536
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"id": "glm-5.1",
|
|
23
|
-
"name": "GLM-5.1",
|
|
24
|
-
"api": "openai-completions",
|
|
25
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
26
|
-
"reasoning": true,
|
|
27
|
-
"input": [
|
|
28
|
-
"text"
|
|
29
|
-
],
|
|
30
|
-
"cost": {
|
|
31
|
-
"input": 1.4,
|
|
32
|
-
"output": 4.4,
|
|
33
|
-
"cacheRead": 0.26,
|
|
34
|
-
"cacheWrite": 0
|
|
35
|
-
},
|
|
36
|
-
"contextWindow": 202752,
|
|
37
|
-
"maxTokens": 32768
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"id": "deepseek-v4-flash",
|
|
41
|
-
"name": "DeepSeek V4 Flash (2x usage)",
|
|
42
|
-
"api": "openai-completions",
|
|
43
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
44
|
-
"reasoning": true,
|
|
45
|
-
"input": [
|
|
46
|
-
"text"
|
|
47
|
-
],
|
|
48
|
-
"cost": {
|
|
49
|
-
"input": 0.07,
|
|
50
|
-
"output": 0.14,
|
|
51
|
-
"cacheRead": 0.0014,
|
|
52
|
-
"cacheWrite": 0
|
|
53
|
-
},
|
|
54
|
-
"contextWindow": 1000000,
|
|
55
|
-
"maxTokens": 384000
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"id": "minimax-m2.7",
|
|
59
|
-
"name": "MiniMax-M2.7",
|
|
60
|
-
"api": "anthropic-messages",
|
|
61
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
62
|
-
"reasoning": true,
|
|
63
|
-
"input": [
|
|
64
|
-
"text"
|
|
65
|
-
],
|
|
66
|
-
"cost": {
|
|
67
|
-
"input": 0.3,
|
|
68
|
-
"output": 1.2,
|
|
69
|
-
"cacheRead": 0.06,
|
|
70
|
-
"cacheWrite": 0
|
|
71
|
-
},
|
|
72
|
-
"contextWindow": 204800,
|
|
73
|
-
"maxTokens": 131072
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"id": "glm-5.2",
|
|
77
|
-
"name": "GLM-5.2",
|
|
78
|
-
"api": "openai-completions",
|
|
79
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
80
|
-
"reasoning": true,
|
|
81
|
-
"input": [
|
|
82
|
-
"text"
|
|
83
|
-
],
|
|
84
|
-
"cost": {
|
|
85
|
-
"input": 1.4,
|
|
86
|
-
"output": 4.4,
|
|
87
|
-
"cacheRead": 0.26,
|
|
88
|
-
"cacheWrite": 0
|
|
89
|
-
},
|
|
90
|
-
"contextWindow": 1000000,
|
|
91
|
-
"maxTokens": 131072
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
"id": "qwen3.7-max",
|
|
95
|
-
"name": "Qwen3.7 Max",
|
|
96
|
-
"api": "anthropic-messages",
|
|
97
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
98
|
-
"reasoning": true,
|
|
99
|
-
"input": [
|
|
100
|
-
"text"
|
|
101
|
-
],
|
|
102
|
-
"cost": {
|
|
103
|
-
"input": 2.5,
|
|
104
|
-
"output": 7.5,
|
|
105
|
-
"cacheRead": 0.5,
|
|
106
|
-
"cacheWrite": 3.125
|
|
107
|
-
},
|
|
108
|
-
"contextWindow": 1000000,
|
|
109
|
-
"maxTokens": 65536
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"id": "kimi-k2.6",
|
|
113
|
-
"name": "Kimi K2.6",
|
|
114
|
-
"api": "openai-completions",
|
|
115
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
116
|
-
"reasoning": true,
|
|
117
|
-
"input": [
|
|
118
|
-
"text",
|
|
119
|
-
"image"
|
|
120
|
-
],
|
|
121
|
-
"cost": {
|
|
122
|
-
"input": 0.95,
|
|
123
|
-
"output": 4,
|
|
124
|
-
"cacheRead": 0.16,
|
|
125
|
-
"cacheWrite": 0
|
|
126
|
-
},
|
|
127
|
-
"contextWindow": 262144,
|
|
128
|
-
"maxTokens": 65536
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
"id": "minimax-m3",
|
|
132
|
-
"name": "MiniMax-M3",
|
|
133
|
-
"api": "anthropic-messages",
|
|
134
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
135
|
-
"reasoning": true,
|
|
136
|
-
"input": [
|
|
137
|
-
"text",
|
|
138
|
-
"image"
|
|
139
|
-
],
|
|
140
|
-
"cost": {
|
|
141
|
-
"input": 0.3,
|
|
142
|
-
"output": 1.2,
|
|
143
|
-
"cacheRead": 0.06,
|
|
144
|
-
"cacheWrite": 0
|
|
145
|
-
},
|
|
146
|
-
"contextWindow": 1000000,
|
|
147
|
-
"maxTokens": 131072
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
"id": "hy3",
|
|
151
|
-
"name": "Hy3",
|
|
152
|
-
"api": "openai-completions",
|
|
153
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
154
|
-
"reasoning": true,
|
|
155
|
-
"input": [
|
|
156
|
-
"text"
|
|
157
|
-
],
|
|
158
|
-
"cost": {
|
|
159
|
-
"input": 0.14,
|
|
160
|
-
"output": 0.58,
|
|
161
|
-
"cacheRead": 0.035,
|
|
162
|
-
"cacheWrite": 0
|
|
163
|
-
},
|
|
164
|
-
"contextWindow": 256000,
|
|
165
|
-
"maxTokens": 64000
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
"id": "deepseek-v4-pro",
|
|
169
|
-
"name": "DeepSeek V4 Pro",
|
|
170
|
-
"api": "openai-completions",
|
|
171
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
172
|
-
"reasoning": true,
|
|
173
|
-
"input": [
|
|
174
|
-
"text"
|
|
175
|
-
],
|
|
176
|
-
"cost": {
|
|
177
|
-
"input": 0.435,
|
|
178
|
-
"output": 0.87,
|
|
179
|
-
"cacheRead": 0.003625,
|
|
180
|
-
"cacheWrite": 0
|
|
181
|
-
},
|
|
182
|
-
"contextWindow": 1000000,
|
|
183
|
-
"maxTokens": 384000
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
"id": "qwen3.8-max",
|
|
187
|
-
"name": "Qwen3.8 Max",
|
|
188
|
-
"api": "anthropic-messages",
|
|
189
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
190
|
-
"reasoning": true,
|
|
191
|
-
"input": [
|
|
192
|
-
"text",
|
|
193
|
-
"image"
|
|
194
|
-
],
|
|
195
|
-
"cost": {
|
|
196
|
-
"input": 2,
|
|
197
|
-
"output": 6,
|
|
198
|
-
"cacheRead": 0.25,
|
|
199
|
-
"cacheWrite": 2.5
|
|
200
|
-
},
|
|
201
|
-
"contextWindow": 1000000,
|
|
202
|
-
"maxTokens": 131072
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
"id": "mimo-v2.5",
|
|
206
|
-
"name": "MiMo V2.5",
|
|
207
|
-
"api": "openai-completions",
|
|
208
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
209
|
-
"reasoning": true,
|
|
210
|
-
"input": [
|
|
211
|
-
"text",
|
|
212
|
-
"image"
|
|
213
|
-
],
|
|
214
|
-
"cost": {
|
|
215
|
-
"input": 0.14,
|
|
216
|
-
"output": 0.28,
|
|
217
|
-
"cacheRead": 0.0028,
|
|
218
|
-
"cacheWrite": 0
|
|
219
|
-
},
|
|
220
|
-
"contextWindow": 1000000,
|
|
221
|
-
"maxTokens": 128000
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
"id": "gpt-5.6-luna",
|
|
225
|
-
"name": "GPT-5.6 Luna (2x usage)",
|
|
226
|
-
"api": "openai-completions",
|
|
227
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
228
|
-
"reasoning": true,
|
|
229
|
-
"input": [
|
|
230
|
-
"text",
|
|
231
|
-
"image"
|
|
232
|
-
],
|
|
233
|
-
"cost": {
|
|
234
|
-
"input": 0.1,
|
|
235
|
-
"output": 0.6,
|
|
236
|
-
"cacheRead": 0.01,
|
|
237
|
-
"cacheWrite": 0.125
|
|
238
|
-
},
|
|
239
|
-
"contextWindow": 1050000,
|
|
240
|
-
"maxTokens": 128000
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
"id": "grok-4.5",
|
|
244
|
-
"name": "Grok 4.5",
|
|
245
|
-
"api": "openai-completions",
|
|
246
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
247
|
-
"reasoning": true,
|
|
248
|
-
"input": [
|
|
249
|
-
"text",
|
|
250
|
-
"image"
|
|
251
|
-
],
|
|
252
|
-
"cost": {
|
|
253
|
-
"input": 2,
|
|
254
|
-
"output": 6,
|
|
255
|
-
"cacheRead": 0.5,
|
|
256
|
-
"cacheWrite": 0
|
|
257
|
-
},
|
|
258
|
-
"contextWindow": 500000,
|
|
259
|
-
"maxTokens": 500000
|
|
260
|
-
},
|
|
261
|
-
{
|
|
262
|
-
"id": "kimi-k2.7-code",
|
|
263
|
-
"name": "Kimi K2.7 Code",
|
|
264
|
-
"api": "openai-completions",
|
|
265
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
266
|
-
"reasoning": true,
|
|
267
|
-
"input": [
|
|
268
|
-
"text",
|
|
269
|
-
"image"
|
|
270
|
-
],
|
|
271
|
-
"cost": {
|
|
272
|
-
"input": 0.95,
|
|
273
|
-
"output": 4,
|
|
274
|
-
"cacheRead": 0.19,
|
|
275
|
-
"cacheWrite": 0
|
|
276
|
-
},
|
|
277
|
-
"contextWindow": 262144,
|
|
278
|
-
"maxTokens": 262144
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
"id": "kimi-k3",
|
|
282
|
-
"name": "Kimi K3",
|
|
283
|
-
"api": "openai-completions",
|
|
284
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
285
|
-
"reasoning": true,
|
|
286
|
-
"input": [
|
|
287
|
-
"text",
|
|
288
|
-
"image"
|
|
289
|
-
],
|
|
290
|
-
"cost": {
|
|
291
|
-
"input": 3,
|
|
292
|
-
"output": 15,
|
|
293
|
-
"cacheRead": 0.3,
|
|
294
|
-
"cacheWrite": 0
|
|
295
|
-
},
|
|
296
|
-
"contextWindow": 1048576,
|
|
297
|
-
"maxTokens": 131072
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
"id": "mimo-v2.5-pro",
|
|
301
|
-
"name": "MiMo V2.5 Pro",
|
|
302
|
-
"api": "openai-completions",
|
|
303
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
304
|
-
"reasoning": true,
|
|
305
|
-
"input": [
|
|
306
|
-
"text"
|
|
307
|
-
],
|
|
308
|
-
"cost": {
|
|
309
|
-
"input": 0.435,
|
|
310
|
-
"output": 0.87,
|
|
311
|
-
"cacheRead": 0.003625,
|
|
312
|
-
"cacheWrite": 0
|
|
313
|
-
},
|
|
314
|
-
"contextWindow": 1048576,
|
|
315
|
-
"maxTokens": 128000
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
"id": "qwen3.6-plus",
|
|
319
|
-
"name": "Qwen3.6 Plus",
|
|
320
|
-
"api": "anthropic-messages",
|
|
321
|
-
"baseUrl": "https://opencode.ai/zen/go",
|
|
322
|
-
"reasoning": true,
|
|
323
|
-
"input": [
|
|
324
|
-
"text",
|
|
325
|
-
"image"
|
|
326
|
-
],
|
|
327
|
-
"cost": {
|
|
328
|
-
"input": 0.5,
|
|
329
|
-
"output": 3,
|
|
330
|
-
"cacheRead": 0.05,
|
|
331
|
-
"cacheWrite": 0.625
|
|
332
|
-
},
|
|
333
|
-
"contextWindow": 1000000,
|
|
334
|
-
"maxTokens": 65536
|
|
335
|
-
}
|
|
336
|
-
]
|
package/patch.json
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"glm-5.1": {
|
|
3
|
-
"compat": {
|
|
4
|
-
"supportsStore": false,
|
|
5
|
-
"supportsDeveloperRole": false,
|
|
6
|
-
"maxTokensField": "max_tokens"
|
|
7
|
-
}
|
|
8
|
-
},
|
|
9
|
-
"deepseek-v4-flash": {
|
|
10
|
-
"name": "DeepSeek V4 Flash",
|
|
11
|
-
"thinkingLevelMap": {
|
|
12
|
-
"minimal": null,
|
|
13
|
-
"low": null,
|
|
14
|
-
"medium": null,
|
|
15
|
-
"high": "high",
|
|
16
|
-
"max": "max"
|
|
17
|
-
},
|
|
18
|
-
"compat": {
|
|
19
|
-
"supportsStore": false,
|
|
20
|
-
"supportsDeveloperRole": false,
|
|
21
|
-
"maxTokensField": "max_tokens",
|
|
22
|
-
"requiresReasoningContentOnAssistantMessages": true,
|
|
23
|
-
"thinkingFormat": "deepseek"
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"minimax-m2.7": {
|
|
27
|
-
"api": "openai-completions",
|
|
28
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
29
|
-
"compat": {
|
|
30
|
-
"supportsStore": false,
|
|
31
|
-
"supportsDeveloperRole": false,
|
|
32
|
-
"maxTokensField": "max_tokens"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"glm-5.2": {
|
|
36
|
-
"thinkingLevelMap": {
|
|
37
|
-
"off": null,
|
|
38
|
-
"minimal": null,
|
|
39
|
-
"low": null,
|
|
40
|
-
"medium": null,
|
|
41
|
-
"high": "high",
|
|
42
|
-
"xhigh": null,
|
|
43
|
-
"max": "max"
|
|
44
|
-
},
|
|
45
|
-
"compat": {
|
|
46
|
-
"supportsStore": false,
|
|
47
|
-
"supportsDeveloperRole": false,
|
|
48
|
-
"maxTokensField": "max_tokens"
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"kimi-k2.6": {
|
|
52
|
-
"thinkingLevelMap": {
|
|
53
|
-
"minimal": null,
|
|
54
|
-
"low": null,
|
|
55
|
-
"medium": null
|
|
56
|
-
},
|
|
57
|
-
"compat": {
|
|
58
|
-
"supportsStore": false,
|
|
59
|
-
"supportsDeveloperRole": false,
|
|
60
|
-
"thinkingFormat": "deepseek",
|
|
61
|
-
"supportsReasoningEffort": false,
|
|
62
|
-
"maxTokensField": "max_tokens",
|
|
63
|
-
"supportsLongCacheRetention": false
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
"hy3": {
|
|
67
|
-
"thinkingLevelMap": {
|
|
68
|
-
"off": "none",
|
|
69
|
-
"minimal": null,
|
|
70
|
-
"low": "low",
|
|
71
|
-
"medium": null,
|
|
72
|
-
"high": "high",
|
|
73
|
-
"xhigh": null,
|
|
74
|
-
"max": null
|
|
75
|
-
},
|
|
76
|
-
"compat": {
|
|
77
|
-
"supportsStore": false,
|
|
78
|
-
"supportsDeveloperRole": false,
|
|
79
|
-
"maxTokensField": "max_tokens"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"deepseek-v4-pro": {
|
|
83
|
-
"thinkingLevelMap": {
|
|
84
|
-
"minimal": null,
|
|
85
|
-
"low": null,
|
|
86
|
-
"medium": null,
|
|
87
|
-
"high": "high",
|
|
88
|
-
"max": "max"
|
|
89
|
-
},
|
|
90
|
-
"compat": {
|
|
91
|
-
"supportsStore": false,
|
|
92
|
-
"supportsDeveloperRole": false,
|
|
93
|
-
"maxTokensField": "max_tokens",
|
|
94
|
-
"requiresReasoningContentOnAssistantMessages": true,
|
|
95
|
-
"thinkingFormat": "deepseek"
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
"mimo-v2.5": {
|
|
99
|
-
"compat": {
|
|
100
|
-
"supportsStore": false,
|
|
101
|
-
"supportsDeveloperRole": false,
|
|
102
|
-
"maxTokensField": "max_tokens"
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
"grok-4.5": {
|
|
106
|
-
"api": "openai-responses",
|
|
107
|
-
"thinkingLevelMap": {
|
|
108
|
-
"off": null,
|
|
109
|
-
"minimal": null,
|
|
110
|
-
"low": "low",
|
|
111
|
-
"medium": "medium",
|
|
112
|
-
"high": "high",
|
|
113
|
-
"xhigh": null,
|
|
114
|
-
"max": null
|
|
115
|
-
},
|
|
116
|
-
"compat": {
|
|
117
|
-
"sessionAffinityFormat": "openai-nosession"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
"kimi-k2.7-code": {
|
|
121
|
-
"compat": {
|
|
122
|
-
"supportsStore": false,
|
|
123
|
-
"supportsDeveloperRole": false,
|
|
124
|
-
"maxTokensField": "max_tokens"
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
"kimi-k3": {
|
|
128
|
-
"name": "Kimi K3 (2x usage)",
|
|
129
|
-
"thinkingLevelMap": {
|
|
130
|
-
"off": null,
|
|
131
|
-
"minimal": null,
|
|
132
|
-
"low": null,
|
|
133
|
-
"medium": null,
|
|
134
|
-
"high": null,
|
|
135
|
-
"xhigh": null,
|
|
136
|
-
"max": "max"
|
|
137
|
-
},
|
|
138
|
-
"compat": {
|
|
139
|
-
"supportsStore": false,
|
|
140
|
-
"supportsDeveloperRole": false,
|
|
141
|
-
"maxTokensField": "max_tokens"
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
"mimo-v2.5-pro": {
|
|
145
|
-
"compat": {
|
|
146
|
-
"supportsStore": false,
|
|
147
|
-
"supportsDeveloperRole": false,
|
|
148
|
-
"maxTokensField": "max_tokens"
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
"gpt-5.6-luna": {
|
|
152
|
-
"api": "openai-responses",
|
|
153
|
-
"thinkingLevelMap": {
|
|
154
|
-
"off": null,
|
|
155
|
-
"minimal": null,
|
|
156
|
-
"low": "low",
|
|
157
|
-
"medium": "medium",
|
|
158
|
-
"high": "high",
|
|
159
|
-
"xhigh": "xhigh",
|
|
160
|
-
"max": "max"
|
|
161
|
-
},
|
|
162
|
-
"compat": {
|
|
163
|
-
"sessionAffinityFormat": "openai-nosession"
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
"qwen3.6-plus": {
|
|
167
|
-
"api": "openai-completions",
|
|
168
|
-
"baseUrl": "https://opencode.ai/zen/go/v1",
|
|
169
|
-
"compat": {
|
|
170
|
-
"supportsStore": false,
|
|
171
|
-
"supportsDeveloperRole": false,
|
|
172
|
-
"thinkingFormat": "qwen",
|
|
173
|
-
"maxTokensField": "max_tokens"
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
package/scripts/update-models.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Script to update opencode-go models from models.dev API
|
|
5
|
-
* Writes to models.json (Pi-native format) and updates README.md.
|
|
6
|
-
*
|
|
7
|
-
* Pipeline: models.dev API → models.json (idempotent, re-runnable)
|
|
8
|
-
* patch.json + custom-models.json for our layered overrides
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import https from 'https';
|
|
12
|
-
import fs from 'fs';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = path.dirname(__filename);
|
|
18
|
-
|
|
19
|
-
const API_URL = 'https://models.dev/api.json';
|
|
20
|
-
const PROVIDER_ID = 'opencode-go';
|
|
21
|
-
|
|
22
|
-
// Map models.dev provider.npm to pi API type and base URL
|
|
23
|
-
const NPM_TO_API = {
|
|
24
|
-
'@ai-sdk/anthropic': { api: 'anthropic-messages', baseUrl: 'https://opencode.ai/zen/go' },
|
|
25
|
-
};
|
|
26
|
-
const DEFAULT_API = { api: 'openai-completions', baseUrl: 'https://opencode.ai/zen/go/v1' };
|
|
27
|
-
|
|
28
|
-
// Fetch JSON from URL
|
|
29
|
-
function fetchJSON(url) {
|
|
30
|
-
return new Promise((resolve, reject) => {
|
|
31
|
-
https.get(url, (res) => {
|
|
32
|
-
let data = '';
|
|
33
|
-
res.on('data', chunk => data += chunk);
|
|
34
|
-
res.on('end', () => {
|
|
35
|
-
try {
|
|
36
|
-
resolve(JSON.parse(data));
|
|
37
|
-
} catch (e) {
|
|
38
|
-
reject(new Error(`Failed to parse JSON: ${e.message}`));
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}).on('error', reject);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Format cost for display
|
|
46
|
-
function formatCost(cost) {
|
|
47
|
-
if (cost === 0) return 'Free';
|
|
48
|
-
return `$${cost.toFixed(2)}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Format number with K/M suffix
|
|
52
|
-
function formatNumber(num) {
|
|
53
|
-
if (num === null || num === undefined) return '-';
|
|
54
|
-
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
|
|
55
|
-
if (num >= 1000) return `${(num / 1000).toFixed(0)}K`;
|
|
56
|
-
return num.toString();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Get input types from modalities (pi supports "text" and "image" only)
|
|
60
|
-
function getInputTypes(modalities) {
|
|
61
|
-
const raw = modalities?.input || ['text'];
|
|
62
|
-
const filtered = raw.filter(m => m === 'text' || m === 'image');
|
|
63
|
-
if (!filtered.includes('text')) filtered.unshift('text');
|
|
64
|
-
return filtered;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Get API label for display
|
|
68
|
-
function getApiLabel(api) {
|
|
69
|
-
const labels = {
|
|
70
|
-
'anthropic-messages': 'Anthropic',
|
|
71
|
-
'openai-responses': 'Responses',
|
|
72
|
-
'openai-completions': 'Completions',
|
|
73
|
-
'google-generative-ai': 'Gemini',
|
|
74
|
-
};
|
|
75
|
-
return labels[api] || api;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Convert API model to Pi-native format with per-model api/baseUrl
|
|
79
|
-
function convertModel(model) {
|
|
80
|
-
const npm = model.provider?.npm;
|
|
81
|
-
const { api, baseUrl } = (npm && NPM_TO_API[npm]) || DEFAULT_API;
|
|
82
|
-
const inputTypes = getInputTypes(model.modalities);
|
|
83
|
-
const cost = model.cost || {};
|
|
84
|
-
const limit = model.limit || {};
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
id: model.id,
|
|
88
|
-
name: model.name,
|
|
89
|
-
api,
|
|
90
|
-
baseUrl,
|
|
91
|
-
reasoning: model.reasoning || false,
|
|
92
|
-
input: inputTypes,
|
|
93
|
-
cost: {
|
|
94
|
-
input: cost.input || 0,
|
|
95
|
-
output: cost.output || 0,
|
|
96
|
-
cacheRead: cost.cache_read || 0,
|
|
97
|
-
cacheWrite: cost.cache_write || 0,
|
|
98
|
-
},
|
|
99
|
-
contextWindow: limit.context || 0,
|
|
100
|
-
maxTokens: limit.output || 0,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Load a layered JSON model file.
|
|
105
|
-
function loadJson(fileName) {
|
|
106
|
-
try {
|
|
107
|
-
return JSON.parse(fs.readFileSync(path.join(process.cwd(), fileName), 'utf8'));
|
|
108
|
-
} catch {
|
|
109
|
-
return fileName === 'patch.json' ? {} : [];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Deep-merge patch overrides into a model for README documentation. The
|
|
114
|
-
// generated models.json remains API-derived; only the displayed model map uses patches.
|
|
115
|
-
function applyPatch(model, patch) {
|
|
116
|
-
if (!patch) return model;
|
|
117
|
-
const result = { ...model };
|
|
118
|
-
for (const [key, value] of Object.entries(patch)) {
|
|
119
|
-
if (key === 'cost' || key === 'compat' || key === 'thinkingLevelMap') {
|
|
120
|
-
result[key] = { ...(result[key] || {}), ...value };
|
|
121
|
-
} else {
|
|
122
|
-
result[key] = value;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (!result.reasoning) {
|
|
126
|
-
delete result.thinkingLevelMap;
|
|
127
|
-
if (result.compat?.thinkingFormat) delete result.compat.thinkingFormat;
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Merge API models, patches, and custom models in the same order as index.ts.
|
|
133
|
-
function buildModels(baseModels, customModels, patch) {
|
|
134
|
-
const byId = new Map(baseModels.map(model => [model.id, model]));
|
|
135
|
-
for (const [id, entry] of Object.entries(patch)) {
|
|
136
|
-
if (byId.has(id)) byId.set(id, applyPatch(byId.get(id), entry));
|
|
137
|
-
}
|
|
138
|
-
for (const model of customModels) {
|
|
139
|
-
byId.set(model.id, applyPatch(model, patch[model.id]));
|
|
140
|
-
}
|
|
141
|
-
return Array.from(byId.values());
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Generate README model table row
|
|
145
|
-
function generateReadmeRow(model) {
|
|
146
|
-
const cost = model.cost || {};
|
|
147
|
-
const hasImage = model.input.includes('image');
|
|
148
|
-
const typeLabel = hasImage ? 'Text + Image' : 'Text';
|
|
149
|
-
return `| ${model.name} | ${getApiLabel(model.api)} | ${typeLabel} | ${formatNumber(model.contextWindow)} | ${formatNumber(model.maxTokens)} | ${formatCost(cost.input)} | ${formatCost(cost.output)} |`;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Update README model table
|
|
153
|
-
function updateReadme(models) {
|
|
154
|
-
const readmePath = path.join(process.cwd(), 'README.md');
|
|
155
|
-
let readme;
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
readme = fs.readFileSync(readmePath, 'utf8');
|
|
159
|
-
} catch {
|
|
160
|
-
console.log(' No README.md found, skipping README update');
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Sort models by name
|
|
165
|
-
const sortedModels = [...models].sort((a, b) => a.name.localeCompare(b.name));
|
|
166
|
-
|
|
167
|
-
// Generate table rows
|
|
168
|
-
const tableRows = sortedModels.map(generateReadmeRow).join('\n');
|
|
169
|
-
const newTable = `| Model | API | Type | Context | Max Tokens | Input Cost | Output Cost |
|
|
170
|
-
|-------|-----|------|---------|------------|------------|-------------|
|
|
171
|
-
${tableRows}`;
|
|
172
|
-
|
|
173
|
-
// Replace table in README
|
|
174
|
-
const tableRegex = /\| Model \| API \| Type \| Context \| Max Tokens \| Input Cost \| Output Cost \|[\s\S]*?(?=\n\*Costs are per million)/;
|
|
175
|
-
if (readme.match(tableRegex)) {
|
|
176
|
-
readme = readme.replace(tableRegex, newTable);
|
|
177
|
-
} else {
|
|
178
|
-
// Fallback: replace old 6-column table format
|
|
179
|
-
const oldTableRegex = /\| Model \| Type \| Context \| Max Tokens \| Input Cost \| Output Cost \|[\s\S]*?(?=\n\*Costs are per million)/;
|
|
180
|
-
if (readme.match(oldTableRegex)) {
|
|
181
|
-
readme = readme.replace(oldTableRegex, newTable);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Update model count in features
|
|
186
|
-
readme = readme.replace(/\*\*\d+ Optimized AI Models\*\*/, `**${models.length} Optimized AI Models**`);
|
|
187
|
-
|
|
188
|
-
fs.writeFileSync(readmePath, readme);
|
|
189
|
-
console.log(` Updated README.md with ${models.length} models`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Grace period for delisted models: update-models.js moves models the API no
|
|
193
|
-
// longer lists into deprecated-models.json (stamped with deprecatedAt) instead
|
|
194
|
-
// of dropping them; the runtime appends them back so sessions and saved model
|
|
195
|
-
// settings keep working, and after 14 days they are evicted permanently.
|
|
196
|
-
const DEPRECATED_MODEL_TTL_MS = 14 * 24 * 60 * 60 * 1000;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Reconcile deprecated-models.json against the freshly fetched model list.
|
|
200
|
-
* - in old models.json but not the API: moved into the deprecated file
|
|
201
|
-
* (deprecatedAt = now; preserved on repeat runs so the grace clock is not reset)
|
|
202
|
-
* - back in the API: resurrected (dropped from the deprecated file)
|
|
203
|
-
* - deprecatedAt older than 14 days: evicted permanently
|
|
204
|
-
* Must run BEFORE the new models.json is written; it reads the old file itself.
|
|
205
|
-
*/
|
|
206
|
-
function updateDeprecatedModels(modelsJsonPath, newModels) {
|
|
207
|
-
const deprecatedPath = path.join(path.dirname(modelsJsonPath), 'deprecated-models.json');
|
|
208
|
-
|
|
209
|
-
let oldModels = [];
|
|
210
|
-
try {
|
|
211
|
-
const parsed = JSON.parse(fs.readFileSync(modelsJsonPath, 'utf8'));
|
|
212
|
-
if (Array.isArray(parsed)) oldModels = parsed;
|
|
213
|
-
} catch { /* first run: no previous models.json */ }
|
|
214
|
-
|
|
215
|
-
let deprecated = {};
|
|
216
|
-
try {
|
|
217
|
-
const parsed = JSON.parse(fs.readFileSync(deprecatedPath, 'utf8'));
|
|
218
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) deprecated = parsed;
|
|
219
|
-
} catch { /* no graveyard yet */ }
|
|
220
|
-
|
|
221
|
-
const currentIds = new Set(newModels.map(m => m.id));
|
|
222
|
-
const now = new Date().toISOString();
|
|
223
|
-
const added = [];
|
|
224
|
-
const resurrected = [];
|
|
225
|
-
const evicted = [];
|
|
226
|
-
|
|
227
|
-
for (const old of oldModels) {
|
|
228
|
-
if (old && old.id && !currentIds.has(old.id) && !deprecated[old.id]) {
|
|
229
|
-
deprecated[old.id] = { ...old, deprecatedAt: now };
|
|
230
|
-
added.push(old.id);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
for (const [id, entry] of Object.entries(deprecated)) {
|
|
235
|
-
if (currentIds.has(id)) {
|
|
236
|
-
delete deprecated[id];
|
|
237
|
-
resurrected.push(id);
|
|
238
|
-
continue;
|
|
239
|
-
}
|
|
240
|
-
const removedAt = Date.parse(entry && entry.deprecatedAt ? entry.deprecatedAt : '');
|
|
241
|
-
if (Number.isNaN(removedAt) || Date.now() - removedAt > DEPRECATED_MODEL_TTL_MS) {
|
|
242
|
-
delete deprecated[id];
|
|
243
|
-
evicted.push(id);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (added.length > 0 || resurrected.length > 0 || evicted.length > 0) {
|
|
248
|
-
fs.writeFileSync(deprecatedPath, JSON.stringify(deprecated, null, 2) + '\n');
|
|
249
|
-
console.log('Updated deprecated-models.json ' + JSON.stringify({ added, resurrected, evicted }));
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
async function main() {
|
|
254
|
-
console.log('Fetching models from API...');
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const data = await fetchJSON(API_URL);
|
|
258
|
-
const provider = data[PROVIDER_ID];
|
|
259
|
-
|
|
260
|
-
if (!provider) {
|
|
261
|
-
throw new Error(`Provider "${PROVIDER_ID}" not found in API`);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (!provider.models) {
|
|
265
|
-
throw new Error(`No models found for provider "${PROVIDER_ID}"`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Convert models object to array and filter out deprecated
|
|
269
|
-
const apiModels = Object.values(provider.models).filter(m => m.status !== 'deprecated');
|
|
270
|
-
|
|
271
|
-
console.log(`Found ${apiModels.length} active models`);
|
|
272
|
-
|
|
273
|
-
// Convert to Pi-native format and save to models.json
|
|
274
|
-
const models = apiModels.map(convertModel);
|
|
275
|
-
const modelsPath = path.join(process.cwd(), 'models.json');
|
|
276
|
-
// Move delisted models to deprecated-models.json BEFORE models.json is overwritten
|
|
277
|
-
updateDeprecatedModels(modelsPath, models);
|
|
278
|
-
fs.writeFileSync(modelsPath, JSON.stringify(models, null, 2) + '\n');
|
|
279
|
-
console.log(` Saved ${models.length} models to models.json`);
|
|
280
|
-
|
|
281
|
-
// Apply layered overrides for documentation: models.json → patch.json → custom-models.json.
|
|
282
|
-
const patch = loadJson('patch.json');
|
|
283
|
-
const customModels = Array.isArray(loadJson('custom-models.json')) ? loadJson('custom-models.json') : [];
|
|
284
|
-
updateReadme(buildModels(models, customModels, patch));
|
|
285
|
-
|
|
286
|
-
console.log('\nDone!');
|
|
287
|
-
} catch (error) {
|
|
288
|
-
console.error('Error:', error.message);
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
main();
|