@aliou/pi-neuralwatt 0.10.4 → 0.10.6

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 CHANGED
@@ -78,7 +78,7 @@ Configure features with `/neuralwatt:settings`:
78
78
  - **Quota warnings** — Enable/disable low quota notifications
79
79
  - **Sub-bar integration** — Show/hide usage in status bar
80
80
  - **Legacy model IDs** — Include deprecated model aliases
81
- - **Hidden models** — Include models available only to the configured API key
81
+ - **Early access models** — Include pre-release models available only to the configured API key
82
82
 
83
83
  The provider itself cannot be disabled — it is always loaded.
84
84
 
@@ -86,7 +86,7 @@ Configuration uses nested per-feature sections. Existing flat config files are m
86
86
 
87
87
  ### Model Refresh
88
88
 
89
- Neuralwatt registers its public models without network access. When hidden models are enabled, opening `/model` refreshes the authenticated catalog in the background. `pi update --models` forces an immediate refresh.
89
+ Neuralwatt registers its public models without network access. When early-access models are enabled, opening `/model` refreshes the authenticated catalog in the background. `pi update --models` forces an immediate refresh.
90
90
 
91
91
  Pi stores the complete effective Neuralwatt catalog in `~/.pi/agent/models-store.json` for offline startup. Current hardcoded public and legacy definitions remain authoritative when cached models are restored.
92
92
 
@@ -7,7 +7,6 @@ import type { SettingItem } from "@earendil-works/pi-tui";
7
7
  import {
8
8
  configLoader,
9
9
  type NeuralwattConfig,
10
- type NeuralwattRawConfig,
11
10
  type ResolvedNeuralwattConfig,
12
11
  } from "../../../../src/config";
13
12
  import {
@@ -50,51 +49,11 @@ function featureRow(
50
49
  };
51
50
  }
52
51
 
53
- function optionalFeatureValue(value: unknown): boolean | undefined {
54
- if (typeof value === "boolean") return value;
55
- if (value && typeof value === "object") {
56
- const enabled = (value as { enabled?: boolean }).enabled;
57
- if (typeof enabled === "boolean") return enabled;
58
- }
59
- return undefined;
60
- }
61
-
62
- function featureValue(value: unknown, fallback: boolean): boolean {
63
- return optionalFeatureValue(value) ?? fallback;
64
- }
65
-
66
- function toNestedConfig(config: NeuralwattRawConfig): NeuralwattConfig {
67
- const provider = "provider" in config ? config.provider : undefined;
68
-
69
- return {
70
- provider: {
71
- ...(provider ?? {}),
72
- includeLegacyModelIds:
73
- provider?.includeLegacyModelIds ??
74
- ("includeLegacyModelIds" in config
75
- ? config.includeLegacyModelIds
76
- : undefined),
77
- includeHiddenModels:
78
- provider?.includeHiddenModels ??
79
- ("includeHiddenModels" in config
80
- ? config.includeHiddenModels
81
- : undefined),
82
- },
83
- quotaCommand: {
84
- ...(typeof config.quotaCommand === "object" ? config.quotaCommand : {}),
85
- enabled: optionalFeatureValue(config.quotaCommand),
86
- },
87
- quotaWarnings: {
88
- ...(typeof config.quotaWarnings === "object" ? config.quotaWarnings : {}),
89
- enabled: optionalFeatureValue(config.quotaWarnings),
90
- },
91
- subBarIntegration: {
92
- ...(typeof config.subBarIntegration === "object"
93
- ? config.subBarIntegration
94
- : {}),
95
- enabled: optionalFeatureValue(config.subBarIntegration),
96
- },
97
- };
52
+ function featureValue(
53
+ section: { enabled?: boolean } | undefined,
54
+ fallback: boolean,
55
+ ): boolean {
56
+ return section?.enabled ?? fallback;
98
57
  }
99
58
 
100
59
  export function registerNeuralwattSettings(
@@ -103,7 +62,7 @@ export function registerNeuralwattSettings(
103
62
  ): void {
104
63
  const { getLoadedFeatures } = options;
105
64
 
106
- registerSettingsCommand<NeuralwattRawConfig, ResolvedNeuralwattConfig>(pi, {
65
+ registerSettingsCommand<NeuralwattConfig, ResolvedNeuralwattConfig>(pi, {
107
66
  commandName: "neuralwatt:settings",
108
67
  title: "Neuralwatt Settings",
109
68
  configStore: configLoader,
@@ -154,30 +113,20 @@ export function registerNeuralwattSettings(
154
113
  description:
155
114
  "Include deprecated Neuralwatt model IDs as aliases in the model picker",
156
115
  currentValue:
157
- ((tabConfig &&
158
- "provider" in tabConfig &&
159
- tabConfig.provider?.includeLegacyModelIds) ??
160
- (tabConfig &&
161
- "includeLegacyModelIds" in tabConfig &&
162
- tabConfig.includeLegacyModelIds) ??
116
+ (tabConfig?.provider?.includeLegacyModelIds ??
163
117
  resolved.provider.includeLegacyModelIds)
164
118
  ? "include"
165
119
  : "ignore",
166
120
  values: ["include", "ignore"],
167
121
  },
168
122
  {
169
- id: "includeHiddenModels",
170
- label: "Hidden models",
123
+ id: "includeEarlyAccessModels",
124
+ label: "Early access models",
171
125
  description:
172
- "Include Neuralwatt models that are accessible via API key but not advertised in the public model list",
126
+ "Include pre-release Neuralwatt models that your API key can reach but that are not yet in the public model list",
173
127
  currentValue:
174
- ((tabConfig &&
175
- "provider" in tabConfig &&
176
- tabConfig.provider?.includeHiddenModels) ??
177
- (tabConfig &&
178
- "includeHiddenModels" in tabConfig &&
179
- tabConfig.includeHiddenModels) ??
180
- resolved.provider.includeHiddenModels)
128
+ (tabConfig?.provider?.includeEarlyAccessModels ??
129
+ resolved.provider.includeEarlyAccessModels)
181
130
  ? "include"
182
131
  : "ignore",
183
132
  values: ["include", "ignore"],
@@ -190,23 +139,21 @@ export function registerNeuralwattSettings(
190
139
  // Non-feature toggles are handled first so they are not blocked by the
191
140
  // loaded-features guard (they are managed directly by the provider).
192
141
  if (id === "includeLegacyModelIds") {
193
- const nestedConfig = toNestedConfig(config);
194
142
  return {
195
- ...nestedConfig,
143
+ ...config,
196
144
  provider: {
197
- ...nestedConfig.provider,
145
+ ...config.provider,
198
146
  includeLegacyModelIds: newValue === "include",
199
147
  },
200
148
  };
201
149
  }
202
150
 
203
- if (id === "includeHiddenModels") {
204
- const nestedConfig = toNestedConfig(config);
151
+ if (id === "includeEarlyAccessModels") {
205
152
  return {
206
- ...nestedConfig,
153
+ ...config,
207
154
  provider: {
208
- ...nestedConfig.provider,
209
- includeHiddenModels: newValue === "include",
155
+ ...config.provider,
156
+ includeEarlyAccessModels: newValue === "include",
210
157
  },
211
158
  };
212
159
  }
@@ -219,21 +166,18 @@ export function registerNeuralwattSettings(
219
166
  switch (id) {
220
167
  case "quotaCommand":
221
168
  return {
222
- ...toNestedConfig(config),
223
- quotaCommand: { ...toNestedConfig(config).quotaCommand, enabled },
169
+ ...config,
170
+ quotaCommand: { ...config.quotaCommand, enabled },
224
171
  };
225
172
  case "quotaWarnings":
226
173
  return {
227
- ...toNestedConfig(config),
228
- quotaWarnings: { ...toNestedConfig(config).quotaWarnings, enabled },
174
+ ...config,
175
+ quotaWarnings: { ...config.quotaWarnings, enabled },
229
176
  };
230
177
  case "subBarIntegration":
231
178
  return {
232
- ...toNestedConfig(config),
233
- subBarIntegration: {
234
- ...toNestedConfig(config).subBarIntegration,
235
- enabled,
236
- },
179
+ ...config,
180
+ subBarIntegration: { ...config.subBarIntegration, enabled },
237
181
  };
238
182
  default:
239
183
  return null;
@@ -61,8 +61,8 @@ function registerNeuralwattProvider(
61
61
  refreshNeuralwattModels(context, {
62
62
  includeLegacyModelIds:
63
63
  configLoader.getConfig().provider.includeLegacyModelIds,
64
- includeHiddenModels:
65
- configLoader.getConfig().provider.includeHiddenModels,
64
+ includeEarlyAccessModels:
65
+ configLoader.getConfig().provider.includeEarlyAccessModels,
66
66
  }),
67
67
  };
68
68
 
@@ -113,8 +113,8 @@ export default async function (pi: ExtensionAPI) {
113
113
  if (
114
114
  next.includeLegacyModelIds ===
115
115
  registeredProviderSettings.includeLegacyModelIds &&
116
- next.includeHiddenModels ===
117
- registeredProviderSettings.includeHiddenModels
116
+ next.includeEarlyAccessModels ===
117
+ registeredProviderSettings.includeEarlyAccessModels
118
118
  ) {
119
119
  return;
120
120
  }
@@ -0,0 +1,119 @@
1
+ import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
2
+
3
+ export type ThinkingLevelMap = NonNullable<
4
+ ProviderModelConfig["thinkingLevelMap"]
5
+ >;
6
+
7
+ /**
8
+ * Flex tier is billed at 65% of standard pricing (35% off) when the request
9
+ * streams. A non-streaming request to a `-flex` model silently falls back to
10
+ * the standard tier and the standard price.
11
+ *
12
+ * https://portal.neuralwatt.com/docs/guides/flex-tier
13
+ */
14
+ export const FLEX_COST_MULTIPLIER = 0.65;
15
+
16
+ export interface NeuralwattCost {
17
+ input: number;
18
+ output: number;
19
+ cacheRead: number;
20
+ }
21
+
22
+ /**
23
+ * Shared metadata for every variant of a Neuralwatt model (base, `-fast`,
24
+ * `-flex`, `-short`, ...). Variants only declare what differs.
25
+ */
26
+ export interface NeuralwattModelFamily {
27
+ cost: NeuralwattCost;
28
+ vision: boolean;
29
+ /** Thinking levels used by reasoning variants of this family. */
30
+ thinkingLevelMap?: ThinkingLevelMap;
31
+ }
32
+
33
+ export interface NeuralwattVariantSpec {
34
+ id: string;
35
+ name: string;
36
+ /** `max_model_len` from /v1/models. */
37
+ contextWindow: number;
38
+ /**
39
+ * `metadata.limits.max_output_tokens` from /v1/models. `null` means the API
40
+ * imposes no separate output cap, so output is bounded by the context window.
41
+ */
42
+ maxOutputTokens: number | null;
43
+ reasoning: boolean;
44
+ cost?: Partial<NeuralwattCost>;
45
+ /**
46
+ * Multiplier applied to the family cost, e.g. the Flex tier discount.
47
+ * Applied after any per-variant `cost` override.
48
+ */
49
+ costMultiplier?: number;
50
+ vision?: boolean;
51
+ thinkingLevelMap?: ThinkingLevelMap;
52
+ }
53
+
54
+ /**
55
+ * Neuralwatt reports `max_output_tokens: null` for models whose output is only
56
+ * bounded by the context window. Mirror the API instead of inventing a cap.
57
+ */
58
+ export function resolveMaxTokens(
59
+ maxOutputTokens: number | null | undefined,
60
+ contextWindow: number,
61
+ ): number {
62
+ return maxOutputTokens ?? contextWindow;
63
+ }
64
+
65
+ export function buildNeuralwattModel(
66
+ family: NeuralwattModelFamily,
67
+ variant: NeuralwattVariantSpec,
68
+ ): ProviderModelConfig {
69
+ const vision = variant.vision ?? family.vision;
70
+
71
+ const compat: NonNullable<ProviderModelConfig["compat"]> = {
72
+ supportsDeveloperRole: false,
73
+ maxTokensField: "max_tokens",
74
+ };
75
+ if (variant.reasoning) {
76
+ compat.requiresReasoningContentOnAssistantMessages = true;
77
+ }
78
+
79
+ const multiplier = variant.costMultiplier ?? 1;
80
+ const scale = (value: number): number =>
81
+ multiplier === 1 ? value : Number((value * multiplier).toFixed(6));
82
+
83
+ const model: ProviderModelConfig = {
84
+ id: variant.id,
85
+ name: variant.name,
86
+ reasoning: variant.reasoning,
87
+ input: vision ? ["text", "image"] : ["text"],
88
+ cost: {
89
+ input: scale(variant.cost?.input ?? family.cost.input),
90
+ output: scale(variant.cost?.output ?? family.cost.output),
91
+ cacheRead: scale(variant.cost?.cacheRead ?? family.cost.cacheRead),
92
+ cacheWrite: 0,
93
+ },
94
+ contextWindow: variant.contextWindow,
95
+ maxTokens: resolveMaxTokens(variant.maxOutputTokens, variant.contextWindow),
96
+ compat,
97
+ };
98
+
99
+ if (variant.reasoning) {
100
+ const thinkingLevelMap =
101
+ variant.thinkingLevelMap ?? family.thinkingLevelMap;
102
+ if (!thinkingLevelMap) {
103
+ throw new Error(
104
+ `Missing thinkingLevelMap for reasoning model ${variant.id}`,
105
+ );
106
+ }
107
+ // Clone so variants never share a family map instance.
108
+ model.thinkingLevelMap = { ...thinkingLevelMap };
109
+ }
110
+
111
+ return model;
112
+ }
113
+
114
+ export function buildNeuralwattFamily(
115
+ family: NeuralwattModelFamily,
116
+ variants: NeuralwattVariantSpec[],
117
+ ): ProviderModelConfig[] {
118
+ return variants.map((variant) => buildNeuralwattModel(family, variant));
119
+ }
@@ -1,58 +1,53 @@
1
1
  import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
2
2
  import { fetchNeuralwattModels } from "../../../src/lib/neuralwatt-api";
3
3
  import type { NeuralwattApiModel } from "../../../src/types/models-api";
4
+ import { buildNeuralwattModel, resolveMaxTokens } from "./build";
4
5
  import { NEURALWATT_MODELS } from "./public-models";
5
6
 
6
- // Hidden aliases that work for authorized accounts but are omitted from the
7
- // authenticated /v1/models response. Keep these gated by includeHiddenModels.
7
+ // Pre-release models. Neuralwatt ships these to authorized accounts before they
8
+ // reach the public /v1/models response; most go public eventually. Keep them
9
+ // gated by includeEarlyAccessModels and hardcode entries so they remain
10
+ // available from the offline catalog.
8
11
  // 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,
12
+ export const EARLY_ACCESS_NEURALWATT_MODELS: ProviderModelConfig[] = [
13
+ // Kimi K3 - early-access MoonshotAI multimodal MoE.
14
+ // Metadata is sourced from Neuralwatt's authenticated model catalog.
15
+ buildNeuralwattModel(
16
+ {
17
+ cost: { input: 3, output: 15, cacheRead: 0.3 },
18
+ vision: true,
19
+ thinkingLevelMap: {
20
+ minimal: null,
21
+ low: null,
22
+ medium: "medium",
23
+ high: null,
24
+ xhigh: null,
25
+ },
24
26
  },
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",
27
+ {
28
+ id: "kimi-k3",
29
+ name: "Kimi K3",
30
+ contextWindow: 1048560,
31
+ maxOutputTokens: null,
32
+ reasoning: true,
35
33
  },
36
- compat: {
37
- supportsDeveloperRole: false,
38
- maxTokensField: "max_tokens",
39
- requiresReasoningContentOnAssistantMessages: true,
40
- },
41
- },
34
+ ),
42
35
  ];
43
36
 
44
- // Per-ID overrides for known hidden models. The authenticated /v1/models endpoint
45
- // exposes pricing and capabilities, but some Pi-specific behavior (thinking levels,
46
- // compat flags) has to be supplied by hand.
47
- // Previously hidden models that have since gone public now live in public-models.ts.
48
- const HIDDEN_MODEL_OVERRIDES: Partial<
37
+ // Per-ID overrides for known early-access models. The authenticated /v1/models
38
+ // endpoint exposes pricing and capabilities, but some Pi-specific behavior
39
+ // (thinking levels, compat flags) has to be supplied by hand.
40
+ // Models that have since gone public now live in public-models.ts.
41
+ const EARLY_ACCESS_MODEL_OVERRIDES: Partial<
49
42
  Record<string, Partial<ProviderModelConfig>>
50
43
  > = {};
51
44
 
52
- function buildHiddenModel(apiModel: NeuralwattApiModel): ProviderModelConfig {
45
+ function buildEarlyAccessModel(
46
+ apiModel: NeuralwattApiModel,
47
+ ): ProviderModelConfig {
53
48
  const meta = apiModel.metadata;
54
49
  const reasoning = meta?.capabilities.reasoning ?? false;
55
- const override = HIDDEN_MODEL_OVERRIDES[apiModel.id];
50
+ const override = EARLY_ACCESS_MODEL_OVERRIDES[apiModel.id];
56
51
 
57
52
  const compat: NonNullable<ProviderModelConfig["compat"]> = {
58
53
  supportsDeveloperRole: false,
@@ -77,7 +72,10 @@ function buildHiddenModel(apiModel: NeuralwattApiModel): ProviderModelConfig {
77
72
  cacheWrite: meta?.pricing.cached_output_per_million ?? 0,
78
73
  },
79
74
  contextWindow: apiModel.max_model_len,
80
- maxTokens: meta?.limits.max_output_tokens ?? 65536,
75
+ maxTokens: resolveMaxTokens(
76
+ meta?.limits.max_output_tokens,
77
+ apiModel.max_model_len,
78
+ ),
81
79
  compat,
82
80
  };
83
81
 
@@ -92,13 +90,13 @@ function buildHiddenModel(apiModel: NeuralwattApiModel): ProviderModelConfig {
92
90
  }
93
91
 
94
92
  if (override) {
95
- return applyHiddenOverride(model, override);
93
+ return applyEarlyAccessOverride(model, override);
96
94
  }
97
95
 
98
96
  return model;
99
97
  }
100
98
 
101
- function applyHiddenOverride(
99
+ function applyEarlyAccessOverride(
102
100
  model: ProviderModelConfig,
103
101
  override: Partial<ProviderModelConfig>,
104
102
  ): ProviderModelConfig {
@@ -125,14 +123,14 @@ function applyHiddenOverride(
125
123
  }
126
124
 
127
125
  /**
128
- * Load hidden models from the authenticated /v1/models endpoint.
126
+ * Load early-access models from the authenticated /v1/models endpoint.
129
127
  *
130
- * Hidden models are any models returned by the API that are not already part of
131
- * the public hardcoded list. If the API key is missing or the request fails, an
132
- * `undefined` distinguishes an unavailable/failed request from a successful
133
- * empty hidden-model list, allowing refresh callers to preserve stale cache.
128
+ * Early-access models are any models returned by the API that are not already
129
+ * part of the public hardcoded list. If the API key is missing or the request
130
+ * fails, an `undefined` distinguishes an unavailable/failed request from a
131
+ * successful empty list, allowing refresh callers to preserve stale cache.
134
132
  */
135
- export async function loadHiddenModels(
133
+ export async function loadEarlyAccessModels(
136
134
  apiKey: string,
137
135
  signal?: AbortSignal,
138
136
  ): Promise<ProviderModelConfig[] | undefined> {
@@ -149,5 +147,5 @@ export async function loadHiddenModels(
149
147
  !model.metadata?.deprecated && !model.metadata?.pricing.pricing_tbd,
150
148
  )
151
149
  .filter((model) => !publicIds.has(model.id))
152
- .map(buildHiddenModel);
150
+ .map(buildEarlyAccessModel);
153
151
  }
@@ -2,7 +2,10 @@ 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 { HIDDEN_NEURALWATT_MODELS, loadHiddenModels } from "./hidden";
5
+ export {
6
+ EARLY_ACCESS_NEURALWATT_MODELS,
7
+ loadEarlyAccessModels,
8
+ } from "./early-access";
6
9
  export {
7
10
  buildLegacyNeuralwattModels,
8
11
  LEGACY_MODEL_ALIAS_MAP,
@@ -12,6 +12,7 @@ export const LEGACY_MODEL_ALIAS_MAP = {
12
12
  "moonshotai/Kimi-K2.6": "kimi-k2.6",
13
13
  "Qwen/Qwen3.5-397B-A17B-FP8": "qwen3.5-397b",
14
14
  "Qwen/Qwen3.6-35B-A3B": "qwen3.6-35b",
15
+ "nvidia/Gemma-4-31B-IT-NVFP4": "gemma-4-31b",
15
16
  } as const;
16
17
 
17
18
  export const LEGACY_NEURALWATT_MODEL_IDS = new Set<string>(