pi-hypercharm-provider 1.1.4 → 1.1.5
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 +37 -51
- package/index.ts +66 -77
- package/models.json +429 -119
- package/package.json +1 -1
- package/patch.json +1 -155
- package/scripts/update-models.js +73 -99
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-hypercharm-provider",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
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/patch.json
CHANGED
|
@@ -1,155 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"deepseek-v4-flash": {
|
|
3
|
-
"maxTokens": 384000,
|
|
4
|
-
"thinkingLevelMap": {
|
|
5
|
-
"minimal": null,
|
|
6
|
-
"low": null,
|
|
7
|
-
"medium": null,
|
|
8
|
-
"high": "high",
|
|
9
|
-
"max": "max"
|
|
10
|
-
},
|
|
11
|
-
"compat": {
|
|
12
|
-
"thinkingFormat": "deepseek",
|
|
13
|
-
"maxTokensField": "max_tokens",
|
|
14
|
-
"supportsDeveloperRole": true,
|
|
15
|
-
"supportsStore": false,
|
|
16
|
-
"supportsReasoningEffort": true,
|
|
17
|
-
"requiresReasoningContentOnAssistantMessages": true
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"deepseek-v4-pro": {
|
|
21
|
-
"maxTokens": 384000,
|
|
22
|
-
"thinkingLevelMap": {
|
|
23
|
-
"minimal": null,
|
|
24
|
-
"low": null,
|
|
25
|
-
"medium": null,
|
|
26
|
-
"high": "high",
|
|
27
|
-
"max": "max"
|
|
28
|
-
},
|
|
29
|
-
"compat": {
|
|
30
|
-
"thinkingFormat": "deepseek",
|
|
31
|
-
"maxTokensField": "max_tokens",
|
|
32
|
-
"supportsDeveloperRole": true,
|
|
33
|
-
"supportsStore": false,
|
|
34
|
-
"supportsReasoningEffort": true,
|
|
35
|
-
"requiresReasoningContentOnAssistantMessages": true
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"gemma-4-26b-a4b-it": {
|
|
39
|
-
"compat": {
|
|
40
|
-
"thinkingFormat": "openai",
|
|
41
|
-
"maxTokensField": "max_tokens",
|
|
42
|
-
"supportsDeveloperRole": true,
|
|
43
|
-
"supportsStore": false,
|
|
44
|
-
"supportsReasoningEffort": true
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"glm-5": {
|
|
48
|
-
"compat": {
|
|
49
|
-
"thinkingFormat": "openai",
|
|
50
|
-
"maxTokensField": "max_tokens",
|
|
51
|
-
"supportsDeveloperRole": true,
|
|
52
|
-
"supportsStore": false,
|
|
53
|
-
"supportsReasoningEffort": true
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"glm-5.1": {
|
|
57
|
-
"compat": {
|
|
58
|
-
"thinkingFormat": "openai",
|
|
59
|
-
"maxTokensField": "max_tokens",
|
|
60
|
-
"supportsDeveloperRole": true,
|
|
61
|
-
"supportsStore": false,
|
|
62
|
-
"supportsReasoningEffort": true
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
"gpt-oss-120b": {
|
|
66
|
-
"compat": {
|
|
67
|
-
"thinkingFormat": "openai",
|
|
68
|
-
"maxTokensField": "max_tokens",
|
|
69
|
-
"supportsDeveloperRole": true,
|
|
70
|
-
"supportsStore": false,
|
|
71
|
-
"supportsReasoningEffort": true
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
"kimi-k2.5": {
|
|
75
|
-
"compat": {
|
|
76
|
-
"thinkingFormat": "openai",
|
|
77
|
-
"maxTokensField": "max_tokens",
|
|
78
|
-
"supportsDeveloperRole": true,
|
|
79
|
-
"supportsStore": false,
|
|
80
|
-
"supportsReasoningEffort": true
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
"kimi-k2.6": {
|
|
84
|
-
"compat": {
|
|
85
|
-
"thinkingFormat": "openai",
|
|
86
|
-
"maxTokensField": "max_tokens",
|
|
87
|
-
"supportsDeveloperRole": true,
|
|
88
|
-
"supportsStore": false,
|
|
89
|
-
"supportsReasoningEffort": true
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"minimax-m2.7": {
|
|
93
|
-
"compat": {
|
|
94
|
-
"thinkingFormat": "openai",
|
|
95
|
-
"maxTokensField": "max_tokens",
|
|
96
|
-
"supportsDeveloperRole": true,
|
|
97
|
-
"supportsStore": false,
|
|
98
|
-
"supportsReasoningEffort": true
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
"qwen3.6-flash": {
|
|
102
|
-
"compat": {
|
|
103
|
-
"thinkingFormat": "openai",
|
|
104
|
-
"maxTokensField": "max_tokens",
|
|
105
|
-
"supportsDeveloperRole": true,
|
|
106
|
-
"supportsStore": false,
|
|
107
|
-
"supportsReasoningEffort": true
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
"qwen3.6-max": {
|
|
111
|
-
"compat": {
|
|
112
|
-
"thinkingFormat": "openai",
|
|
113
|
-
"maxTokensField": "max_tokens",
|
|
114
|
-
"supportsDeveloperRole": true,
|
|
115
|
-
"supportsStore": false,
|
|
116
|
-
"supportsReasoningEffort": true
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
"qwen3.6-plus": {
|
|
120
|
-
"compat": {
|
|
121
|
-
"thinkingFormat": "openai",
|
|
122
|
-
"maxTokensField": "max_tokens",
|
|
123
|
-
"supportsDeveloperRole": true,
|
|
124
|
-
"supportsStore": false,
|
|
125
|
-
"supportsReasoningEffort": true
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
"qwen3.7-max": {
|
|
129
|
-
"compat": {
|
|
130
|
-
"thinkingFormat": "openai",
|
|
131
|
-
"maxTokensField": "max_tokens",
|
|
132
|
-
"supportsDeveloperRole": true,
|
|
133
|
-
"supportsStore": false,
|
|
134
|
-
"supportsReasoningEffort": true
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
"qwen3-coder-480b-a35b-instruct-int4-mixed-ar": {
|
|
138
|
-
"compat": {
|
|
139
|
-
"thinkingFormat": "openai",
|
|
140
|
-
"maxTokensField": "max_tokens",
|
|
141
|
-
"supportsDeveloperRole": true,
|
|
142
|
-
"supportsStore": false,
|
|
143
|
-
"supportsReasoningEffort": true
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
"qwen3-next-80b-a3b-instruct": {
|
|
147
|
-
"compat": {
|
|
148
|
-
"thinkingFormat": "openai",
|
|
149
|
-
"maxTokensField": "max_tokens",
|
|
150
|
-
"supportsDeveloperRole": true,
|
|
151
|
-
"supportsStore": false,
|
|
152
|
-
"supportsReasoningEffort": true
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
1
|
+
{}
|
package/scripts/update-models.js
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Update HyperCharm models from
|
|
4
|
+
* Update HyperCharm models from Charm's official typed provider catalog
|
|
5
5
|
*
|
|
6
|
-
* Fetches models from https://hyper.charm.land/v1/
|
|
7
|
-
* - models.json:
|
|
8
|
-
* - README.md:
|
|
6
|
+
* Fetches models from https://hyper.charm.land/v1/provider and updates:
|
|
7
|
+
* - models.json: canonical API-owned metadata used by @charmland/pi-hyper-provider
|
|
8
|
+
* - README.md: model table with patch.json overrides applied
|
|
9
9
|
*
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* Note: supports_reasoning is unreliable for some models (reports true for
|
|
15
|
-
* Llama 3.3 70B which doesn't support extended thinking). models.json
|
|
16
|
-
* curates reasoning flags based on known model capabilities; patch.json
|
|
17
|
-
* adds compat flags and corrections.
|
|
10
|
+
* The endpoint provides canonical names, $/M pricing, context/output limits,
|
|
11
|
+
* can_reason, optional reasoning levels, and attachment support. models.json is
|
|
12
|
+
* pure API data. patch.json is reserved for verified endpoint regressions and
|
|
13
|
+
* currently contains no overrides.
|
|
18
14
|
*
|
|
19
15
|
* Merge order for README: models.json → apply patch.json → merge custom-models.json
|
|
20
16
|
*/
|
|
@@ -25,7 +21,7 @@ import { fileURLToPath } from 'url';
|
|
|
25
21
|
|
|
26
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
23
|
|
|
28
|
-
const MODELS_API_URL = 'https://hyper.charm.land/v1/
|
|
24
|
+
const MODELS_API_URL = 'https://hyper.charm.land/v1/provider';
|
|
29
25
|
const MODELS_JSON_PATH = path.join(__dirname, '..', 'models.json');
|
|
30
26
|
const PATCH_JSON_PATH = path.join(__dirname, '..', 'patch.json');
|
|
31
27
|
const CUSTOM_MODELS_JSON_PATH = path.join(__dirname, '..', 'custom-models.json');
|
|
@@ -76,6 +72,9 @@ function applyPatch(model, patch) {
|
|
|
76
72
|
if (!result.reasoning && result.compat?.thinkingFormat) {
|
|
77
73
|
delete result.compat.thinkingFormat;
|
|
78
74
|
}
|
|
75
|
+
if (!result.reasoning && result.thinkingLevelMap) {
|
|
76
|
+
delete result.thinkingLevelMap;
|
|
77
|
+
}
|
|
79
78
|
if (result.compat && Object.keys(result.compat).length === 0) {
|
|
80
79
|
delete result.compat;
|
|
81
80
|
}
|
|
@@ -102,91 +101,68 @@ function buildModels(baseModels, customModels, patchData) {
|
|
|
102
101
|
|
|
103
102
|
// ─── Model transformation ─────────────────────────────────────────────────────
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// Update fields from API that may change
|
|
131
|
-
const cost = apiModel.cost?.usd || {};
|
|
132
|
-
const inputCost = convertPricing(cost['1m_in']);
|
|
133
|
-
const outputCost = convertPricing(cost['1m_out']);
|
|
134
|
-
const cacheReadCost = convertPricing(cost['1m_in_cache']);
|
|
135
|
-
const cacheWriteCost = convertPricing(cost['1m_out_cache']);
|
|
136
|
-
|
|
137
|
-
if (inputCost > 0) existing.cost.input = inputCost;
|
|
138
|
-
if (outputCost > 0) existing.cost.output = outputCost;
|
|
139
|
-
if (cacheReadCost > 0) existing.cost.cacheRead = cacheReadCost;
|
|
140
|
-
if (cacheWriteCost > 0) existing.cost.cacheWrite = cacheWriteCost;
|
|
141
|
-
if (apiModel.context_window) existing.contextWindow = apiModel.context_window;
|
|
142
|
-
// Don't override maxTokens from API for DeepSeek — it reports 8000 but the
|
|
143
|
-
// actual max is 384K (set in models.json / patch.json)
|
|
144
|
-
if (apiModel.max_output_tokens && !/^deepseek-v/.test(modelId)) {
|
|
145
|
-
existing.maxTokens = apiModel.max_output_tokens;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return existing;
|
|
104
|
+
const PI_THINKING_LEVELS = ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
105
|
+
|
|
106
|
+
// Charm's official extension treats a reasoning-capable model with no levels as
|
|
107
|
+
// a boolean on/off model: Pi's max level selects the single on state.
|
|
108
|
+
const ON_OFF_THINKING_LEVEL_MAP = {
|
|
109
|
+
off: 'off',
|
|
110
|
+
minimal: null,
|
|
111
|
+
low: null,
|
|
112
|
+
medium: null,
|
|
113
|
+
high: null,
|
|
114
|
+
xhigh: null,
|
|
115
|
+
max: 'max',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
function buildThinkingLevelMap(levels) {
|
|
119
|
+
if (levels.length === 0) return undefined;
|
|
120
|
+
const available = new Set(levels);
|
|
121
|
+
const result = {
|
|
122
|
+
// The provider enum uses "none" for the off state on newer deployments;
|
|
123
|
+
// the official extension looked only for the older "off" spelling.
|
|
124
|
+
off: available.has('off') ? 'off' : available.has('none') ? 'none' : null,
|
|
125
|
+
};
|
|
126
|
+
for (const level of PI_THINKING_LEVELS) {
|
|
127
|
+
result[level] = available.has(level) ? level : null;
|
|
149
128
|
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
150
131
|
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
132
|
+
function transformModel(apiModel) {
|
|
133
|
+
const reasoningLevels = Array.isArray(apiModel.reasoning_levels)
|
|
134
|
+
? apiModel.reasoning_levels.filter(level => typeof level === 'string')
|
|
135
|
+
: [];
|
|
136
|
+
const supportsReasoningEffort = reasoningLevels.length > 0;
|
|
137
|
+
const thinkingLevelMap = supportsReasoningEffort
|
|
138
|
+
? buildThinkingLevelMap(reasoningLevels)
|
|
139
|
+
: apiModel.can_reason === true
|
|
140
|
+
? ON_OFF_THINKING_LEVEL_MAP
|
|
141
|
+
: undefined;
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
id: apiModel.id,
|
|
145
|
+
name: apiModel.name,
|
|
146
|
+
reasoning: apiModel.can_reason === true,
|
|
147
|
+
...(thinkingLevelMap ? { thinkingLevelMap } : {}),
|
|
148
|
+
input: apiModel.supports_attachments === true ? ['text', 'image'] : ['text'],
|
|
162
149
|
cost: {
|
|
163
|
-
input:
|
|
164
|
-
output:
|
|
165
|
-
cacheRead:
|
|
166
|
-
|
|
150
|
+
input: typeof apiModel.cost_per_1m_in === 'number' ? apiModel.cost_per_1m_in : 0,
|
|
151
|
+
output: typeof apiModel.cost_per_1m_out === 'number' ? apiModel.cost_per_1m_out : 0,
|
|
152
|
+
cacheRead: typeof apiModel.cost_per_1m_in_cached === 'number' ? apiModel.cost_per_1m_in_cached : 0,
|
|
153
|
+
// Hyper exposes discounted cached-output pricing, not cache-write pricing;
|
|
154
|
+
// pi's cost.cacheWrite field remains unused. This matches Charm's official extension.
|
|
155
|
+
cacheWrite: 0,
|
|
167
156
|
},
|
|
168
157
|
contextWindow: apiModel.context_window || 0,
|
|
169
|
-
maxTokens: apiModel.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// and add thinkingLevelMap + deepseek compat
|
|
174
|
-
if (isDeepSeek && isReasoning) {
|
|
175
|
-
model.maxTokens = 384000;
|
|
176
|
-
model.thinkingLevelMap = {
|
|
177
|
-
minimal: null, low: null, medium: null, high: 'high', max: 'max',
|
|
178
|
-
};
|
|
179
|
-
model.compat = {
|
|
158
|
+
maxTokens: apiModel.default_max_tokens || apiModel.context_window || 0,
|
|
159
|
+
compat: {
|
|
160
|
+
supportsStore: false,
|
|
161
|
+
supportsReasoningEffort,
|
|
180
162
|
thinkingFormat: 'deepseek',
|
|
181
163
|
maxTokensField: 'max_tokens',
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
supportsReasoningEffort: true,
|
|
185
|
-
requiresReasoningContentOnAssistantMessages: true,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return model;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
190
166
|
}
|
|
191
167
|
|
|
192
168
|
// ─── README generation ────────────────────────────────────────────────────────
|
|
@@ -317,7 +293,9 @@ async function main() {
|
|
|
317
293
|
}
|
|
318
294
|
|
|
319
295
|
const apiResponse = await response.json();
|
|
320
|
-
const apiModels =
|
|
296
|
+
const apiModels = Array.isArray(apiResponse)
|
|
297
|
+
? apiResponse
|
|
298
|
+
: (apiResponse.models || apiResponse.data || []);
|
|
321
299
|
|
|
322
300
|
if (!Array.isArray(apiModels)) {
|
|
323
301
|
throw new Error('API response does not contain an array of models');
|
|
@@ -338,16 +316,12 @@ async function main() {
|
|
|
338
316
|
}
|
|
339
317
|
|
|
340
318
|
// Transform models from API, preserving existing curated data
|
|
341
|
-
let apiTransformed = apiModels.map(m => transformModel(m
|
|
319
|
+
let apiTransformed = apiModels.map(m => transformModel(m));
|
|
342
320
|
apiTransformed.sort((a, b) => a.name.localeCompare(b.name));
|
|
343
321
|
|
|
344
|
-
//
|
|
322
|
+
// Load patch overrides for README rendering. Canonical metadata already
|
|
323
|
+
// comes from /v1/provider, so new models do not require a patch entry.
|
|
345
324
|
const patch = loadJson(PATCH_JSON_PATH);
|
|
346
|
-
for (const m of apiTransformed) {
|
|
347
|
-
if (!patch[m.id]) {
|
|
348
|
-
console.log(` 🆕 New model: ${m.id} (${m.name}) — add to patch.json for compat overrides`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
325
|
|
|
352
326
|
// Update models.json — curated API data
|
|
353
327
|
// Move delisted models to deprecated-models.json BEFORE models.json is overwritten
|