@aliou/pi-neuralwatt 0.9.0 → 0.10.4

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.
@@ -3,6 +3,44 @@ import { fetchNeuralwattModels } from "../../../src/lib/neuralwatt-api";
3
3
  import type { NeuralwattApiModel } from "../../../src/types/models-api";
4
4
  import { NEURALWATT_MODELS } from "./public-models";
5
5
 
6
+ // Hidden aliases that work for authorized accounts but are omitted from the
7
+ // authenticated /v1/models response. Keep these gated by includeHiddenModels.
8
+ // Move an entry to public-models.ts once Neuralwatt advertises it publicly.
9
+ export const HIDDEN_NEURALWATT_MODELS: ProviderModelConfig[] = [
10
+ // DeepSeek V4 Flash Canary - early-access 284B/13B-active MoE served on B200s.
11
+ // Neuralwatt omits it from the authenticated catalog. Context and runtime
12
+ // capabilities were verified directly; pricing follows DeepSeek's upstream
13
+ // rates until Neuralwatt publishes model metadata.
14
+ {
15
+ id: "deepseek-v4-flash",
16
+ name: "DeepSeek V4 Flash (Canary)",
17
+ reasoning: true,
18
+ input: ["text", "image"],
19
+ cost: {
20
+ input: 0.14,
21
+ output: 0.28,
22
+ cacheRead: 0.0028,
23
+ cacheWrite: 0,
24
+ },
25
+ contextWindow: 1_000_000,
26
+ maxTokens: 384_000,
27
+ thinkingLevelMap: {
28
+ off: "none",
29
+ minimal: "low",
30
+ low: "low",
31
+ medium: "medium",
32
+ high: "high",
33
+ xhigh: null,
34
+ max: "max",
35
+ },
36
+ compat: {
37
+ supportsDeveloperRole: false,
38
+ maxTokensField: "max_tokens",
39
+ requiresReasoningContentOnAssistantMessages: true,
40
+ },
41
+ },
42
+ ];
43
+
6
44
  // Per-ID overrides for known hidden models. The authenticated /v1/models endpoint
7
45
  // exposes pricing and capabilities, but some Pi-specific behavior (thinking levels,
8
46
  // compat flags) has to be supplied by hand.
@@ -2,7 +2,7 @@ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
2
2
  import { buildLegacyNeuralwattModels } from "./legacy";
3
3
  import { NEURALWATT_MODELS } from "./public-models";
4
4
 
5
- export { loadHiddenModels } from "./hidden";
5
+ export { HIDDEN_NEURALWATT_MODELS, loadHiddenModels } from "./hidden";
6
6
  export {
7
7
  buildLegacyNeuralwattModels,
8
8
  LEGACY_MODEL_ALIAS_MAP,
@@ -3,7 +3,48 @@ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
3
3
  // Public models returned by https://api.neuralwatt.com/v1/models (unauthenticated view).
4
4
  // Pricing, capabilities, and limits are sourced from the API metadata fields.
5
5
  export const NEURALWATT_MODELS: ProviderModelConfig[] = [
6
+ // Gemma 4 31B - NVIDIA NVFP4 checkpoint
7
+ {
8
+ id: "nvidia/Gemma-4-31B-IT-NVFP4",
9
+ name: "Gemma 4 31B (NVFP4)",
10
+ reasoning: false,
11
+ input: ["text", "image"],
12
+ cost: {
13
+ input: 0.144,
14
+ output: 0.42,
15
+ cacheRead: 0.036,
16
+ cacheWrite: 0,
17
+ },
18
+ contextWindow: 262128,
19
+ maxTokens: 16384,
20
+ compat: {
21
+ supportsDeveloperRole: false,
22
+ maxTokensField: "max_tokens",
23
+ },
24
+ },
25
+ // Gemma 4 31B - Google, served from NVIDIA's NVFP4 checkpoint
26
+ {
27
+ id: "gemma-4-31b",
28
+ name: "Gemma 4 31B",
29
+ reasoning: false,
30
+ input: ["text", "image"],
31
+ cost: {
32
+ input: 0.144,
33
+ output: 0.42,
34
+ cacheRead: 0.036,
35
+ cacheWrite: 0,
36
+ },
37
+ contextWindow: 262128,
38
+ maxTokens: 16384,
39
+ compat: {
40
+ supportsDeveloperRole: false,
41
+ maxTokensField: "max_tokens",
42
+ },
43
+ },
6
44
  // GLM-5.2 - ZhipuAI
45
+ // Native reasoning tiers: off (none), high, max. Exposes Pi's `max` level
46
+ // (introduced in Pi 0.80.6) for GLM's top tier; `xhigh` is an unsupported
47
+ // hole between `high` and `max`.
7
48
  {
8
49
  id: "glm-5.2",
9
50
  name: "GLM-5.2",
@@ -23,7 +64,8 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
23
64
  low: null,
24
65
  medium: null,
25
66
  high: "high",
26
- xhigh: "max",
67
+ xhigh: null,
68
+ max: "max",
27
69
  },
28
70
  compat: {
29
71
  supportsDeveloperRole: false,
@@ -63,14 +105,15 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
63
105
  cacheWrite: 0,
64
106
  },
65
107
  contextWindow: 199984,
66
- maxTokens: 65536,
108
+ maxTokens: 32000,
67
109
  thinkingLevelMap: {
68
110
  off: "none",
69
111
  minimal: null,
70
112
  low: null,
71
113
  medium: null,
72
114
  high: "high",
73
- xhigh: "max",
115
+ xhigh: null,
116
+ max: "max",
74
117
  },
75
118
  compat: {
76
119
  supportsDeveloperRole: false,
@@ -91,7 +134,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
91
134
  cacheWrite: 0,
92
135
  },
93
136
  contextWindow: 199984,
94
- maxTokens: 65536,
137
+ maxTokens: 32000,
95
138
  compat: {
96
139
  supportsDeveloperRole: false,
97
140
  maxTokensField: "max_tokens",
@@ -283,7 +326,8 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
283
326
  low: null,
284
327
  medium: null,
285
328
  high: "high",
286
- xhigh: "max",
329
+ xhigh: null,
330
+ max: "max",
287
331
  },
288
332
  compat: {
289
333
  supportsDeveloperRole: false,
@@ -366,7 +410,8 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
366
410
  low: null,
367
411
  medium: null,
368
412
  high: "high",
369
- xhigh: "max",
413
+ xhigh: null,
414
+ max: "max",
370
415
  },
371
416
  compat: {
372
417
  supportsDeveloperRole: false,
@@ -5,7 +5,7 @@ import type {
5
5
  RefreshModelsContext,
6
6
  } from "@earendil-works/pi-ai";
7
7
  import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
8
- import { loadHiddenModels } from "./hidden";
8
+ import { HIDDEN_NEURALWATT_MODELS, loadHiddenModels } from "./hidden";
9
9
  import { buildLegacyNeuralwattModels } from "./legacy";
10
10
  import { NEURALWATT_MODELS } from "./public-models";
11
11
 
@@ -67,6 +67,22 @@ function dedupeHiddenModels(
67
67
  return hiddenModels.filter((model) => !baselineIds.has(model.id));
68
68
  }
69
69
 
70
+ function configuredHiddenModels(
71
+ discoveredModels: ProviderModelConfig[],
72
+ baselineModels: ProviderModelConfig[],
73
+ ): ProviderModelConfig[] {
74
+ const hardcodedIds = new Set(
75
+ HIDDEN_NEURALWATT_MODELS.map((model) => model.id),
76
+ );
77
+ return dedupeHiddenModels(
78
+ [
79
+ ...HIDDEN_NEURALWATT_MODELS,
80
+ ...discoveredModels.filter((model) => !hardcodedIds.has(model.id)),
81
+ ],
82
+ baselineModels,
83
+ );
84
+ }
85
+
70
86
  function cachedHiddenModels(
71
87
  stored: ModelsStoreEntry | undefined,
72
88
  ): ProviderModelConfig[] {
@@ -104,7 +120,10 @@ export async function refreshNeuralwattModels(
104
120
  return baseline;
105
121
  }
106
122
 
107
- const cachedHidden = dedupeHiddenModels(cachedHiddenModels(stored), baseline);
123
+ const cachedHidden = configuredHiddenModels(
124
+ cachedHiddenModels(stored),
125
+ baseline,
126
+ );
108
127
  const cachedCatalog = [...baseline, ...cachedHidden];
109
128
 
110
129
  if (!context.allowNetwork || context.signal?.aborted) {
@@ -124,7 +143,7 @@ export async function refreshNeuralwattModels(
124
143
  throw new Error("Neuralwatt model catalog refresh failed");
125
144
  }
126
145
 
127
- const catalog = [...baseline, ...dedupeHiddenModels(hidden, baseline)];
146
+ const catalog = [...baseline, ...configuredHiddenModels(hidden, baseline)];
128
147
  await persistModels(context, catalog);
129
148
  return catalog;
130
149
  }
@@ -5,6 +5,13 @@ import { formatKwh, formatUsd } from "../../src/utils/quota-format";
5
5
  export type WarningSeverity = "warning" | "critical";
6
6
 
7
7
  const COOLDOWN_MS = 60 * 60 * 1000; // 60 minutes
8
+ const LOW_PCT = 25;
9
+ const CRITICAL_PCT = 10;
10
+
11
+ /** Per-kWh price once a subscription's included kWh are exhausted. */
12
+ const OVERAGE_RATE_PER_KWH_SUBSCRIBED = 5;
13
+ /** Per-kWh price when there is no active subscription (no included kWh). */
14
+ const OVERAGE_RATE_PER_KWH_UNSUBSCRIBED = 10;
8
15
 
9
16
  interface AlertState {
10
17
  lastSeverity: WarningSeverity;
@@ -19,14 +26,16 @@ export function clearAlertState(): void {
19
26
  alerts.clear();
20
27
  }
21
28
 
29
+ function severityForPct(pct: number): WarningSeverity {
30
+ return pct <= CRITICAL_PCT ? "critical" : "warning";
31
+ }
32
+
22
33
  function shouldNotify(key: string, severity: WarningSeverity): boolean {
23
34
  const state = alerts.get(key);
24
35
  if (!state) return true;
25
36
 
26
37
  const order: WarningSeverity[] = ["warning", "critical"];
27
- const currentIndex = order.indexOf(severity);
28
- const lastIndex = order.indexOf(state.lastSeverity);
29
- if (currentIndex > lastIndex) return true;
38
+ if (order.indexOf(severity) > order.indexOf(state.lastSeverity)) return true;
30
39
 
31
40
  return Date.now() - state.lastNotifiedAt >= COOLDOWN_MS;
32
41
  }
@@ -35,9 +44,121 @@ function markNotified(key: string, severity: WarningSeverity): void {
35
44
  alerts.set(key, { lastSeverity: severity, lastNotifiedAt: Date.now() });
36
45
  }
37
46
 
47
+ interface PendingWarning {
48
+ key: string;
49
+ severity: WarningSeverity;
50
+ message: string;
51
+ }
52
+
53
+ /** Subscription energy (kWh) — the primary billing pool while subscribed. */
54
+ function energyWarning(
55
+ sub: NonNullable<NeuralwattQuotas["subscription"]>,
56
+ ): PendingWarning | undefined {
57
+ if (sub.kwh_included <= 0) return;
58
+ const pct = (sub.kwh_remaining / sub.kwh_included) * 100;
59
+ if (pct > LOW_PCT) return;
60
+ return {
61
+ key: "energy",
62
+ severity: severityForPct(pct),
63
+ message: `Energy: ${pct.toFixed(0)}% remaining (${formatKwh(sub.kwh_remaining)} of ${formatKwh(sub.kwh_included)})`,
64
+ };
65
+ }
66
+
67
+ /** Balance credits (USD) — on-demand top-up pool. */
68
+ function creditsWarning(quotas: NeuralwattQuotas): PendingWarning | undefined {
69
+ const { credits_remaining_usd, total_credits_usd } = quotas.balance;
70
+ if (total_credits_usd <= 0) return;
71
+ const pct = (credits_remaining_usd / total_credits_usd) * 100;
72
+ if (pct > LOW_PCT) return;
73
+ return {
74
+ key: "credits",
75
+ severity: severityForPct(pct),
76
+ message: `Credits: ${pct.toFixed(0)}% remaining (${formatUsd(credits_remaining_usd)} of ${formatUsd(total_credits_usd)})`,
77
+ };
78
+ }
79
+
80
+ /** Overage usage billed against the overage cap, derived from kWh usage. */
81
+ interface OverageProgress {
82
+ /** kWh billed at the overage rate. */
83
+ overageKwh: number;
84
+ /** Per-kWh rate applied (USD). */
85
+ rate: number;
86
+ /** Cost of the overage kWh so far (USD). */
87
+ costUsd: number;
88
+ /** Configured overage cap (USD), or 0 when none. */
89
+ capUsd: number;
90
+ /** Remaining cap headroom (USD), clamped at 0. */
91
+ remainingUsd: number;
92
+ /** Remaining cap as a percentage of the cap (0-100). 0 when no cap. */
93
+ pctRemaining: number;
94
+ /** Cap exhausted (overage cost has reached or passed the cap). */
95
+ exhausted: boolean;
96
+ }
97
+
98
+ export function computeOverageProgress(
99
+ quotas: NeuralwattQuotas,
100
+ ): OverageProgress {
101
+ const capUsd = quotas.limits.overage_limit_usd ?? 0;
102
+ const hasSub = quotas.subscription !== null;
103
+
104
+ // Subscribed: only kWh beyond the included quota are billed at the overage
105
+ // rate. Unsubscribed: every kWh is billable — the monthly usage total is the
106
+ // overage pool (there is no included quota to subtract from).
107
+ const overageKwh = hasSub
108
+ ? Math.max(
109
+ 0,
110
+ (quotas.subscription?.kwh_used ?? 0) -
111
+ (quotas.subscription?.kwh_included ?? 0),
112
+ )
113
+ : quotas.usage.current_month.energy_kwh;
114
+
115
+ const rate = hasSub
116
+ ? OVERAGE_RATE_PER_KWH_SUBSCRIBED
117
+ : OVERAGE_RATE_PER_KWH_UNSUBSCRIBED;
118
+ const costUsd = overageKwh * rate;
119
+ const remainingUsd = Math.max(0, capUsd - costUsd);
120
+ const pctRemaining = capUsd > 0 ? (remainingUsd / capUsd) * 100 : 0;
121
+
122
+ return {
123
+ overageKwh,
124
+ rate,
125
+ costUsd,
126
+ capUsd,
127
+ remainingUsd,
128
+ pctRemaining,
129
+ exhausted: capUsd > 0 && remainingUsd <= 0,
130
+ };
131
+ }
132
+
133
+ function overageWarning(progress: OverageProgress): PendingWarning {
134
+ const pct = Math.max(0, Math.min(100, progress.pctRemaining));
135
+ return {
136
+ key: "overage",
137
+ // Entering overage is itself worth a critical alert; the % controls the
138
+ // message, not whether we notify.
139
+ severity: "critical",
140
+ message: `Overage cap: ${pct.toFixed(0)}% remaining (${formatUsd(progress.remainingUsd)} of ${formatUsd(progress.capUsd)}, ${formatKwh(progress.overageKwh)} over @ ${formatUsd(progress.rate)}/kWh)`,
141
+ };
142
+ }
143
+
38
144
  /**
39
- * When a subscription is active, energy is the primary billing method.
40
- * Credits are on-demand/top-up only don't warn for credits when subscribed.
145
+ * Warning progression mirrors Neuralwatt's billing order. Each stage uses its
146
+ * own alert key, so once a later stage starts the earlier one stops — the
147
+ * warning "moves on" instead of re-reporting a depleted pool forever.
148
+ *
149
+ * subscribed, not in overage → energy (kWh remaining of quota)
150
+ * subscribed, in overage, cap set → overage cap progress (credits unreachable)
151
+ * subscribed, in overage, cap exhausted→ balance credits
152
+ * subscribed, in overage, no cap → balance credits (overage draws them down)
153
+ * no subscription, cap set → overage cap progress (all kWh billable)
154
+ * no subscription, no cap → balance credits
155
+ *
156
+ * Overage cost is derived from kWh usage: subscribed pays $5/kWh for kWh
157
+ * beyond the included quota; unsubscribed pays $10/kWh for all usage. There is
158
+ * no overage-spent counter in the API, so progress is computed.
159
+ *
160
+ * Usage totals (monthly/lifetime cost in USD) are deliberately not used as a
161
+ * threshold basis — they are not directly tied to the subscription's kWh quota.
41
162
  */
42
163
  export function checkQuotas(
43
164
  ctx: ExtensionContext,
@@ -45,55 +166,59 @@ export function checkQuotas(
45
166
  ): void {
46
167
  if (!ctx.hasUI) return;
47
168
 
48
- const warnings: string[] = [];
49
- const hasSub = quotas.subscription !== null;
169
+ const pending: PendingWarning[] = [];
170
+ const sub = quotas.subscription;
50
171
 
51
- // Credits warning — only when no active subscription
52
- if (!hasSub) {
53
- const { credits_remaining_usd, total_credits_usd } = quotas.balance;
54
- if (total_credits_usd > 0) {
55
- const pct = (credits_remaining_usd / total_credits_usd) * 100;
56
- if (pct <= 25) {
57
- const severity: WarningSeverity = pct <= 10 ? "critical" : "warning";
58
- const key = "credits";
59
- if (shouldNotify(key, severity)) {
60
- markNotified(key, severity);
61
- warnings.push(
62
- `Credits: ${pct.toFixed(0)}% remaining (${formatUsd(credits_remaining_usd)} of ${formatUsd(total_credits_usd)})`,
63
- );
64
- }
172
+ if (sub?.in_overage) {
173
+ const cap = quotas.limits.overage_limit_usd;
174
+ if (cap !== null && cap > 0) {
175
+ const progress = computeOverageProgress(quotas);
176
+ if (progress.exhausted) {
177
+ // Cap spent fall through to the balance credits.
178
+ const cw = creditsWarning(quotas);
179
+ if (cw) pending.push(cw);
180
+ } else {
181
+ pending.push(overageWarning(progress));
65
182
  }
183
+ } else {
184
+ // No cap: overage spends down the balance credits directly.
185
+ const cw = creditsWarning(quotas);
186
+ if (cw) pending.push(cw);
66
187
  }
67
- }
68
-
69
- // Subscription energy check
70
- if (quotas.subscription) {
71
- const { kwh_included, kwh_remaining, in_overage } = quotas.subscription;
72
- if (kwh_included > 0) {
73
- const pct = (kwh_remaining / kwh_included) * 100;
74
- if (in_overage || pct <= 25) {
75
- const severity: WarningSeverity = in_overage
76
- ? "critical"
77
- : pct <= 10
78
- ? "critical"
79
- : "warning";
80
- const key = "energy";
81
- if (shouldNotify(key, severity)) {
82
- markNotified(key, severity);
83
- const tag = in_overage ? " [OVERAGE]" : "";
84
- warnings.push(
85
- `Energy${tag}: ${pct.toFixed(0)}% remaining (${formatKwh(kwh_remaining)} of ${formatKwh(kwh_included)})`,
86
- );
87
- }
188
+ } else if (sub) {
189
+ const ew = energyWarning(sub);
190
+ if (ew) pending.push(ew);
191
+ } else {
192
+ // No subscription. All kWh bill at the unsubscribed rate; warn on the
193
+ // overage cap when one is set, otherwise on the balance credits.
194
+ const cap = quotas.limits.overage_limit_usd;
195
+ if (cap !== null && cap > 0) {
196
+ const progress = computeOverageProgress(quotas);
197
+ if (progress.exhausted) {
198
+ const cw = creditsWarning(quotas);
199
+ if (cw) pending.push(cw);
200
+ } else if (progress.pctRemaining <= LOW_PCT) {
201
+ pending.push(overageWarning(progress));
88
202
  }
203
+ } else {
204
+ const cw = creditsWarning(quotas);
205
+ if (cw) pending.push(cw);
89
206
  }
90
207
  }
91
208
 
92
- if (warnings.length === 0) return;
209
+ const fired = pending.filter((w) => {
210
+ if (shouldNotify(w.key, w.severity)) {
211
+ markNotified(w.key, w.severity);
212
+ return true;
213
+ }
214
+ return false;
215
+ });
216
+
217
+ if (fired.length === 0) return;
93
218
 
94
- const hasCritical = warnings.some((w) => w.includes("[OVERAGE]"));
219
+ const hasCritical = fired.some((w) => w.severity === "critical");
95
220
  ctx.ui.notify(
96
- `Neuralwatt quota warning:\n${warnings.map((w) => ` - ${w}`).join("\n")}`,
221
+ `Neuralwatt quota warning:\n${fired.map((w) => ` - ${w.message}`).join("\n")}`,
97
222
  hasCritical ? "error" : "warning",
98
223
  );
99
224
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliou/pi-neuralwatt",
3
- "version": "0.9.0",
3
+ "version": "0.10.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "private": false,