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/README.md
CHANGED
|
@@ -15,40 +15,41 @@ _Hyperoptimized coding models — DeepSeek, GLM, Kimi, Qwen, MiniMax, Gemma, GPT
|
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
|
-
- **
|
|
19
|
-
- **DeepSeek Native Thinking** — Uses the `deepseek` thinking format with `reasoning_effort`
|
|
18
|
+
- **24+ AI Models** including DeepSeek V4 Flash/Pro, GLM 5/5.1, Kimi K2.5/K2.6, Qwen3.6/3.7, MiniMax M2.7, Gemma 4, GPT-OSS, and Llama
|
|
19
|
+
- **DeepSeek Native Thinking** — Uses the `deepseek` thinking format for Charm Hyper requests, with native `reasoning_effort` on models that publish levels
|
|
20
20
|
- **OpenAI-compatible API** via Charm Hyper's `/v1/chat/completions` endpoint
|
|
21
|
-
- **
|
|
22
|
-
- **Reasoning Models** with
|
|
23
|
-
- **
|
|
21
|
+
- **Official Catalog Sync** from Charm's typed `/v1/provider` endpoint, matching `@charmland/pi-hyper-provider`
|
|
22
|
+
- **Reasoning Models** with provider-published on/off states and exact effort levels
|
|
23
|
+
- **Attachment Support** for models the official catalog marks as attachment-capable
|
|
24
24
|
|
|
25
25
|
## Available Models
|
|
26
26
|
|
|
27
27
|
| Model | Type | Context | Max Tokens | Input Cost | Output Cost |
|
|
28
28
|
|-------|------|---------|------------|------------|-------------|
|
|
29
29
|
| DeepSeek V4 Flash | Text | 1.0M | 384K | $0.20 | $0.40 |
|
|
30
|
+
| DeepSeek V4 Flash 0731 | Text | 1.0M | 384K | $0.15 | $0.30 |
|
|
30
31
|
| DeepSeek V4 Pro | Text | 1.0M | 384K | $2.40 | $4.80 |
|
|
31
|
-
| Gemma 4 26B A4B | Text
|
|
32
|
-
| GLM
|
|
33
|
-
| GLM-5 | Text | 203K |
|
|
32
|
+
| Gemma 4 26B A4B | Text | 256K | 26K | $0.13 | $0.43 |
|
|
33
|
+
| GLM-5 | Text | 203K | 20K | $0.85 | $2.62 |
|
|
34
|
+
| GLM-5.1 | Text | 203K | 131K | $1.52 | $4.79 |
|
|
34
35
|
| GLM-5.2 | Text | 1.0M | 128K | $1.40 | $4.40 |
|
|
35
|
-
|
|
|
36
|
-
| Kimi K2.5 | Text
|
|
36
|
+
| gpt-oss-120b | Text | 131K | 13K | $0.19 | $0.70 |
|
|
37
|
+
| Kimi K2.5 | Text | 262K | 26K | $0.56 | $2.82 |
|
|
37
38
|
| Kimi K2.6 | Text + Image | 262K | 262K | $0.95 | $4.00 |
|
|
38
|
-
| Kimi K2.7 Code | Text | 256K | 16K | $0.95 | $4.00 |
|
|
39
|
-
| Kimi K3 | Text | 1.0M | 131K |
|
|
40
|
-
| Llama 3.3 70B Instruct | Text | 128K | 13K | $0.
|
|
41
|
-
| Llama 4 Maverick 17B 128E FP8 | Text | 430K | 43K | $0.28 | $0.93 |
|
|
42
|
-
| MiniMax M2.7 | Text | 205K | 20K | $0.
|
|
43
|
-
| MiniMax M3 | Text | 512K | 512K |
|
|
44
|
-
| Qwen3 Coder 480B A35B INT4 | Text | 106K | 11K | $0.
|
|
45
|
-
| Qwen3 Next 80B A3B | Text | 262K | 26K | $0.12 | $1.14 |
|
|
39
|
+
| Kimi K2.7 Code | Text + Image | 256K | 16K | $0.95 | $4.00 |
|
|
40
|
+
| Kimi K3 | Text + Image | 1.0M | 131K | $3.27 | $16.33 |
|
|
41
|
+
| Llama 3.3 70B Instruct | Text | 128K | 13K | $0.51 | $1.04 |
|
|
42
|
+
| Llama 4 Maverick 17B 128E Instruct FP8 | Text | 430K | 43K | $0.28 | $0.93 |
|
|
43
|
+
| MiniMax M2.7 | Text | 205K | 20K | $0.44 | $1.72 |
|
|
44
|
+
| MiniMax M3 | Text + Image | 512K | 512K | $0.33 | $1.31 |
|
|
45
|
+
| Qwen3 Coder 480B A35B Instruct INT4 Mixed AR | Text | 106K | 11K | $0.57 | $2.13 |
|
|
46
|
+
| Qwen3 Next 80B A3B Instruct | Text | 262K | 26K | $0.12 | $1.14 |
|
|
46
47
|
| Qwen3.6-Flash | Text + Image | 1.0M | 64K | $1.00 | $4.00 |
|
|
47
|
-
| Qwen3.6-Max | Text
|
|
48
|
+
| Qwen3.6-Max | Text | 256K | 64K | $2.00 | $12.00 |
|
|
48
49
|
| Qwen3.6-Plus | Text + Image | 1.0M | 64K | $2.00 | $6.00 |
|
|
49
|
-
| Qwen3.7-Flash | Text | 1.0M | 64K |
|
|
50
|
-
| Qwen3.7-Max | Text
|
|
51
|
-
| Qwen3.7-Plus | Text | 1.0M | 64K | $1.20 | $4.80 |
|
|
50
|
+
| Qwen3.7-Flash | Text + Image | 1.0M | 64K | $0.20 | $0.80 |
|
|
51
|
+
| Qwen3.7-Max | Text | 1.0M | 64K | $2.50 | $7.50 |
|
|
52
|
+
| Qwen3.7-Plus | Text + Image | 1.0M | 64K | $1.20 | $4.80 |
|
|
52
53
|
*Costs are per million tokens. Prices subject to change — check [hyper.charm.land](https://hyper.charm.land) for current pricing.*
|
|
53
54
|
|
|
54
55
|
## Installation
|
|
@@ -118,7 +119,7 @@ For reasoning models that support `reasoning_effort`, control thinking depth:
|
|
|
118
119
|
pi --provider hypercharm --model deepseek-v4-pro --reasoning-effort max
|
|
119
120
|
```
|
|
120
121
|
|
|
121
|
-
|
|
122
|
+
Supported levels are model-specific and come from Charm's `/v1/provider` catalog (for example `low`/`medium`/`high` on Kimi K2.6). Models with no published levels support the Hyper thinking on state through Pi's `max` level.
|
|
122
123
|
|
|
123
124
|
### Thinking Mode
|
|
124
125
|
|
|
@@ -156,37 +157,21 @@ Add to your pi configuration for automatic loading:
|
|
|
156
157
|
}
|
|
157
158
|
```
|
|
158
159
|
|
|
159
|
-
### Compat Settings
|
|
160
|
+
### Catalog and Compat Settings
|
|
160
161
|
|
|
161
|
-
|
|
162
|
+
Model metadata matches Charm's official [`@charmland/pi-hyper-provider`](https://github.com/charmbracelet/pi-hyper-provider) transform:
|
|
162
163
|
|
|
163
|
-
|
|
164
|
+
- canonical `/v1/provider` names, prices, context windows, output caps, attachment flags, and `can_reason`
|
|
165
|
+
- `thinkingFormat: "deepseek"`, which maps Pi thinking levels onto Hyper's `thinking` envelope
|
|
166
|
+
- `reasoning_effort` only when the catalog publishes concrete level names
|
|
167
|
+
- `maxTokensField: "max_tokens"` and `supportsStore: false`
|
|
168
|
+
- zero `cacheWrite`, because Hyper reports discounted cached-output pricing, not cache-write cost
|
|
164
169
|
|
|
165
|
-
|
|
166
|
-
- **`supportsReasoningEffort: true`** — Supports `reasoning_effort: "high" | "max"` via `thinkingLevelMap`
|
|
167
|
-
- **`requiresReasoningContentOnAssistantMessages: true`** — Replayed assistant messages include empty `reasoning_content` when reasoning is enabled
|
|
168
|
-
- **`maxTokensField: "max_tokens"`** — Uses `max_tokens` (not `max_completion_tokens`)
|
|
169
|
-
|
|
170
|
-
**Other reasoning models** (GLM, Kimi, Qwen, MiniMax, Gemma, GPT-OSS) use the `openai` thinking format:
|
|
171
|
-
|
|
172
|
-
- **`thinkingFormat: "openai"`** — Returns `reasoning_content` in OpenAI format
|
|
173
|
-
- **`supportsReasoningEffort: true`** — Accepts `reasoning_effort` parameter on supported models
|
|
174
|
-
- **`maxTokensField: "max_tokens"`** — Uses `max_tokens` (not `max_completion_tokens`)
|
|
175
|
-
- **`supportsDeveloperRole: true`** — Developer role messages are accepted
|
|
176
|
-
- **`supportsStore: false`** — The `store` parameter is not supported
|
|
177
|
-
|
|
178
|
-
> **Note:** The HyperCharm `/v1/models` endpoint reports `supports_reasoning: true` for all models,
|
|
179
|
-
> including those that don't natively support extended thinking (e.g., Llama 3.3 70B). `models.json`
|
|
180
|
-
> curates reasoning flags based on known model capabilities; `patch.json` adds compat overrides.
|
|
170
|
+
`patch.json` is reserved only for a verified provider regression and is currently empty.
|
|
181
171
|
|
|
182
172
|
### Patch Overrides
|
|
183
173
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
- Correcting API-derived values (e.g., DeepSeek `maxTokens` from 8000 to 384000)
|
|
187
|
-
- Adding compat settings that the API doesn't provide
|
|
188
|
-
- Setting `thinkingFormat: "deepseek"` + `thinkingLevelMap` for DeepSeek V4 models
|
|
189
|
-
- Overriding pricing when official rates change
|
|
174
|
+
`patch.json` is applied on top of `models.json` only for verified endpoint corrections. It is currently empty because every live field comes from Charm's canonical provider catalog.
|
|
190
175
|
|
|
191
176
|
## Updating Models
|
|
192
177
|
|
|
@@ -199,10 +184,11 @@ node scripts/update-models.js
|
|
|
199
184
|
|
|
200
185
|
This will:
|
|
201
186
|
1. Fetch models from `https://hyper.charm.land/v1/models`
|
|
202
|
-
2.
|
|
203
|
-
3. Apply overrides from `patch.json`
|
|
187
|
+
2. Regenerate `models.json` as pure metadata from Charm's typed `/v1/provider` catalog
|
|
188
|
+
3. Apply overrides from `patch.json` only when building the README
|
|
204
189
|
4. Remove custom models now available upstream from `custom-models.json`
|
|
205
|
-
5.
|
|
190
|
+
5. Reconcile delisted models through the 14-day `deprecated-models.json` grace layer
|
|
191
|
+
6. Update `models.json` and the README model table
|
|
206
192
|
|
|
207
193
|
A GitHub Actions workflow runs this daily and creates a PR if models have changed.
|
|
208
194
|
|
package/index.ts
CHANGED
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
* Registers HyperCharm (hyper.charm.land) as a custom provider using the
|
|
5
5
|
* openai-completions API. Base URL: https://hyper.charm.land/v1
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* pricing, context
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* it reports true for Llama 3.3 70B which doesn't support extended thinking).
|
|
13
|
-
* The models.json embeds curated reasoning flags; patch.json corrects compat.
|
|
7
|
+
* Model metadata comes from Charm's typed official-catalog endpoint,
|
|
8
|
+
* /v1/provider, matching @charmland/pi-hyper-provider. It provides canonical
|
|
9
|
+
* names, pricing, context and output limits, reasoning levels, and attachment
|
|
10
|
+
* support. patch.json remains available for verified endpoint regressions, but
|
|
11
|
+
* currently contains no overrides.
|
|
14
12
|
*
|
|
15
13
|
* Model resolution strategy: Stale-While-Revalidate
|
|
16
14
|
* 1. Serve stale immediately: disk cache → embedded models.json (zero-latency)
|
|
@@ -114,6 +112,9 @@ function applyPatch(model: JsonModel, patch: PatchEntry): JsonModel {
|
|
|
114
112
|
if (!result.reasoning && result.compat?.thinkingFormat) {
|
|
115
113
|
delete result.compat.thinkingFormat;
|
|
116
114
|
}
|
|
115
|
+
if (!result.reasoning && result.thinkingLevelMap) {
|
|
116
|
+
delete result.thinkingLevelMap;
|
|
117
|
+
}
|
|
117
118
|
if (result.compat && Object.keys(result.compat).length === 0) {
|
|
118
119
|
delete result.compat;
|
|
119
120
|
}
|
|
@@ -153,81 +154,76 @@ function buildModels(base: JsonModel[], custom: JsonModel[], patch: PatchData):
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// Ensure DeepSeek reasoning models have required compat settings.
|
|
159
|
-
// Live-fetched models from the SWR pipeline may not have these set.
|
|
160
|
-
for (const model of result) {
|
|
161
|
-
if (!model.reasoning) continue;
|
|
162
|
-
if (isDeepSeekModel(model.id)) {
|
|
163
|
-
if (!model.compat) {
|
|
164
|
-
model.compat = {
|
|
165
|
-
thinkingFormat: "deepseek",
|
|
166
|
-
maxTokensField: "max_tokens",
|
|
167
|
-
supportsDeveloperRole: true,
|
|
168
|
-
supportsStore: false,
|
|
169
|
-
supportsReasoningEffort: true,
|
|
170
|
-
requiresReasoningContentOnAssistantMessages: true,
|
|
171
|
-
};
|
|
172
|
-
} else {
|
|
173
|
-
if (model.compat.thinkingFormat === undefined) {
|
|
174
|
-
model.compat.thinkingFormat = "deepseek";
|
|
175
|
-
}
|
|
176
|
-
if (model.compat.supportsReasoningEffort === undefined) {
|
|
177
|
-
model.compat.supportsReasoningEffort = true;
|
|
178
|
-
}
|
|
179
|
-
if ((model.compat as any).requiresReasoningContentOnAssistantMessages === undefined) {
|
|
180
|
-
(model.compat as any).requiresReasoningContentOnAssistantMessages = true;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
if (!model.thinkingLevelMap) {
|
|
184
|
-
model.thinkingLevelMap = {
|
|
185
|
-
minimal: null, low: null, medium: null, high: "high", max: "max",
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return result;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function isDeepSeekModel(id: string): boolean {
|
|
195
|
-
return /^deepseek-v/.test(id);
|
|
157
|
+
return Array.from(modelMap.values());
|
|
196
158
|
}
|
|
197
159
|
|
|
198
160
|
// ─── Stale-While-Revalidate Model Sync ────────────────────────────────────────
|
|
199
161
|
|
|
200
162
|
const PROVIDER_ID = "hypercharm";
|
|
201
163
|
const BASE_URL = "https://hyper.charm.land/v1";
|
|
202
|
-
const MODELS_URL = `${BASE_URL}/
|
|
164
|
+
const MODELS_URL = `${BASE_URL}/provider`;
|
|
203
165
|
const CACHE_DIR = path.join(getAgentDir(), "cache");
|
|
204
166
|
const CACHE_PATH = path.join(CACHE_DIR, `${PROVIDER_ID}-models.json`);
|
|
205
167
|
const LIVE_FETCH_TIMEOUT_MS = 8000;
|
|
206
168
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
169
|
+
const PI_THINKING_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
170
|
+
|
|
171
|
+
const ON_OFF_THINKING_LEVEL_MAP: Record<string, string | null> = {
|
|
172
|
+
off: "off",
|
|
173
|
+
minimal: null,
|
|
174
|
+
low: null,
|
|
175
|
+
medium: null,
|
|
176
|
+
high: null,
|
|
177
|
+
xhigh: null,
|
|
178
|
+
max: "max",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
function buildThinkingLevelMap(levels: string[]): Record<string, string | null> | undefined {
|
|
182
|
+
if (levels.length === 0) return undefined;
|
|
183
|
+
const available = new Set(levels);
|
|
184
|
+
const result: Record<string, string | null> = {
|
|
185
|
+
off: available.has("off") ? "off" : available.has("none") ? "none" : null,
|
|
216
186
|
};
|
|
187
|
+
for (const level of PI_THINKING_LEVELS) {
|
|
188
|
+
result[level] = available.has(level) ? level : null;
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Transform a model from Charm's official typed Hyper /v1/provider catalog. */
|
|
194
|
+
function transformApiModel(apiModel: any): JsonModel | null {
|
|
195
|
+
if (typeof apiModel.id !== "string" || apiModel.id.length === 0) return null;
|
|
196
|
+
|
|
197
|
+
const reasoningLevels = Array.isArray(apiModel.reasoning_levels)
|
|
198
|
+
? apiModel.reasoning_levels.filter((level: any) => typeof level === "string")
|
|
199
|
+
: [];
|
|
200
|
+
const supportsReasoningEffort = reasoningLevels.length > 0;
|
|
201
|
+
const thinkingLevelMap = supportsReasoningEffort
|
|
202
|
+
? buildThinkingLevelMap(reasoningLevels)
|
|
203
|
+
: apiModel.can_reason === true
|
|
204
|
+
? ON_OFF_THINKING_LEVEL_MAP
|
|
205
|
+
: undefined;
|
|
217
206
|
|
|
218
207
|
return {
|
|
219
208
|
id: apiModel.id,
|
|
220
|
-
name: apiModel.
|
|
221
|
-
reasoning:
|
|
222
|
-
|
|
209
|
+
name: apiModel.name || apiModel.id,
|
|
210
|
+
reasoning: apiModel.can_reason === true,
|
|
211
|
+
thinkingLevelMap,
|
|
212
|
+
input: apiModel.supports_attachments === true ? ["text", "image"] : ["text"],
|
|
223
213
|
cost: {
|
|
224
|
-
input:
|
|
225
|
-
output:
|
|
226
|
-
cacheRead:
|
|
227
|
-
cacheWrite:
|
|
214
|
+
input: apiModel.cost_per_1m_in || 0,
|
|
215
|
+
output: apiModel.cost_per_1m_out || 0,
|
|
216
|
+
cacheRead: apiModel.cost_per_1m_in_cached || 0,
|
|
217
|
+
cacheWrite: 0,
|
|
228
218
|
},
|
|
229
219
|
contextWindow: apiModel.context_window || 0,
|
|
230
|
-
maxTokens: apiModel.
|
|
220
|
+
maxTokens: apiModel.default_max_tokens || apiModel.context_window || 0,
|
|
221
|
+
compat: {
|
|
222
|
+
supportsStore: false,
|
|
223
|
+
supportsReasoningEffort,
|
|
224
|
+
thinkingFormat: "deepseek",
|
|
225
|
+
maxTokensField: "max_tokens",
|
|
226
|
+
},
|
|
231
227
|
};
|
|
232
228
|
}
|
|
233
229
|
|
|
@@ -239,7 +235,7 @@ async function fetchLiveModels(apiKey: string, signal?: AbortSignal): Promise<Js
|
|
|
239
235
|
});
|
|
240
236
|
if (!response.ok) return null;
|
|
241
237
|
const data = await response.json();
|
|
242
|
-
const apiModels = Array.isArray(data) ? data : (data.data || []);
|
|
238
|
+
const apiModels = Array.isArray(data) ? data : (data.models || data.data || []);
|
|
243
239
|
if (!Array.isArray(apiModels) || apiModels.length === 0) return null;
|
|
244
240
|
return apiModels.map(transformApiModel).filter((m): m is JsonModel => m !== null);
|
|
245
241
|
} catch {
|
|
@@ -273,20 +269,13 @@ function mergeWithEmbedded(liveModels: JsonModel[], embeddedModels: JsonModel[])
|
|
|
273
269
|
const embedded = embeddedMap.get(liveModel.id);
|
|
274
270
|
seen.add(liveModel.id);
|
|
275
271
|
if (embedded) {
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
// providers whose /models endpoint exposes no pricing keep their curated
|
|
280
|
-
// cost. Curation (reasoning/input/compat/name) still wins via ...embedded.
|
|
272
|
+
// The official /v1/provider catalog is authoritative for pricing, including
|
|
273
|
+
// legitimately zero-priced preview models. Curation (reasoning/input/compat/name)
|
|
274
|
+
// still wins via ...embedded.
|
|
281
275
|
result.push({
|
|
282
276
|
...liveModel,
|
|
283
277
|
...embedded,
|
|
284
|
-
cost:
|
|
285
|
-
input: liveModel.cost.input || embedded.cost.input,
|
|
286
|
-
output: liveModel.cost.output || embedded.cost.output,
|
|
287
|
-
cacheRead: liveModel.cost.cacheRead || embedded.cost.cacheRead,
|
|
288
|
-
cacheWrite: liveModel.cost.cacheWrite || embedded.cost.cacheWrite,
|
|
289
|
-
},
|
|
278
|
+
cost: liveModel.cost,
|
|
290
279
|
contextWindow: liveModel.contextWindow || embedded.contextWindow,
|
|
291
280
|
});
|
|
292
281
|
} else {
|