@aliou/pi-neuralwatt 0.7.0 → 0.7.1
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/package.json +1 -1
- package/schema.json +4 -0
- package/src/config.ts +25 -4
- package/src/extensions/command-quotas/command.ts +1 -1
- package/src/extensions/provider/index.ts +25 -8
- package/src/extensions/provider/models/hidden.ts +127 -0
- package/src/extensions/provider/models/index.ts +23 -0
- package/src/extensions/provider/models/legacy.ts +37 -0
- package/src/extensions/provider/{models.ts → models/public-models.ts} +14 -129
- package/src/extensions/provider/quota-store.ts +1 -1
- package/src/{utils/quotas.ts → lib/neuralwatt-api.ts} +56 -7
- package/src/types/models-api.ts +53 -0
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
"NeuralwattConfig": {
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"properties": {
|
|
8
|
+
"includeHiddenModels": {
|
|
9
|
+
"description": "Include hidden Neuralwatt models discovered via the authenticated API.",
|
|
10
|
+
"type": "boolean"
|
|
11
|
+
},
|
|
8
12
|
"includeLegacyModelIds": {
|
|
9
13
|
"description": "Include legacy Neuralwatt model IDs in the model picker.",
|
|
10
14
|
"type": "boolean"
|
package/src/config.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface NeuralwattConfig {
|
|
|
30
30
|
subBarIntegration?: boolean;
|
|
31
31
|
/** Include legacy Neuralwatt model IDs in the model picker. */
|
|
32
32
|
includeLegacyModelIds?: boolean;
|
|
33
|
+
/** Include hidden Neuralwatt models discovered via the authenticated API. */
|
|
34
|
+
includeHiddenModels?: boolean;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export interface ResolvedNeuralwattConfig {
|
|
@@ -37,6 +39,7 @@ export interface ResolvedNeuralwattConfig {
|
|
|
37
39
|
quotaWarnings: boolean;
|
|
38
40
|
subBarIntegration: boolean;
|
|
39
41
|
includeLegacyModelIds: boolean;
|
|
42
|
+
includeHiddenModels: boolean;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const DEFAULTS: ResolvedNeuralwattConfig = {
|
|
@@ -44,6 +47,7 @@ const DEFAULTS: ResolvedNeuralwattConfig = {
|
|
|
44
47
|
quotaWarnings: true,
|
|
45
48
|
subBarIntegration: true,
|
|
46
49
|
includeLegacyModelIds: false,
|
|
50
|
+
includeHiddenModels: false,
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
export const configLoader = new ConfigLoader<
|
|
@@ -157,19 +161,36 @@ export function registerNeuralwattSettings(
|
|
|
157
161
|
: "ignore",
|
|
158
162
|
values: ["include", "ignore"],
|
|
159
163
|
},
|
|
164
|
+
{
|
|
165
|
+
id: "includeHiddenModels",
|
|
166
|
+
label: "Hidden models",
|
|
167
|
+
description:
|
|
168
|
+
"Include Neuralwatt models that are accessible via API key but not advertised in the public model list",
|
|
169
|
+
currentValue:
|
|
170
|
+
(tabConfig?.includeHiddenModels ?? resolved.includeHiddenModels)
|
|
171
|
+
? "include"
|
|
172
|
+
: "ignore",
|
|
173
|
+
values: ["include", "ignore"],
|
|
174
|
+
},
|
|
160
175
|
],
|
|
161
176
|
},
|
|
162
177
|
];
|
|
163
178
|
},
|
|
164
179
|
onSettingChange: (id, newValue, config) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
180
|
+
// Non-feature toggles are handled first so they are not blocked by the
|
|
181
|
+
// loaded-features guard (they are managed directly by the provider).
|
|
169
182
|
if (id === "includeLegacyModelIds") {
|
|
170
183
|
return { ...config, includeLegacyModelIds: newValue === "include" };
|
|
171
184
|
}
|
|
172
185
|
|
|
186
|
+
if (id === "includeHiddenModels") {
|
|
187
|
+
return { ...config, includeHiddenModels: newValue === "include" };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!getLoadedFeatures().has(id as NeuralwattFeatureId)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
173
194
|
const enabled = newValue === "enabled";
|
|
174
195
|
switch (id) {
|
|
175
196
|
case "quotaCommand":
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getAgentDir,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { getNeuralwattApiKey } from "../../lib/env";
|
|
7
|
-
import { fetchQuotas } from "../../
|
|
7
|
+
import { fetchQuotas } from "../../lib/neuralwatt-api";
|
|
8
8
|
import { QuotasComponent } from "./components/quotas-display";
|
|
9
9
|
|
|
10
10
|
function missingAuthMessage(): string {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getApiProvider } from "@earendil-works/pi-ai";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ExtensionAPI,
|
|
4
|
+
ProviderModelConfig,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
3
6
|
import {
|
|
4
7
|
configLoader,
|
|
5
8
|
emitConfigUpdated,
|
|
@@ -10,15 +13,15 @@ import {
|
|
|
10
13
|
registerNeuralwattSettings,
|
|
11
14
|
} from "../../config";
|
|
12
15
|
import { getNeuralwattApiKey } from "../../lib/env";
|
|
16
|
+
import { fetchQuotas } from "../../lib/neuralwatt-api";
|
|
13
17
|
import type { NeuralwattQuotas } from "../../types/quota-api";
|
|
14
18
|
import {
|
|
15
19
|
NEURALWATT_QUOTAS_REQUEST_EVENT,
|
|
16
20
|
NEURALWATT_QUOTAS_UPDATED_EVENT,
|
|
17
21
|
type NeuralwattQuotasUpdatedPayload,
|
|
18
22
|
} from "../../types/quota-events";
|
|
19
|
-
import { fetchQuotas } from "../../utils/quotas";
|
|
20
23
|
import { normalizeNeuralwattContextOverflowError } from "./context-overflow";
|
|
21
|
-
import { getNeuralwattModels } from "./models";
|
|
24
|
+
import { getNeuralwattModels, loadHiddenModels } from "./models";
|
|
22
25
|
import { buildQuotasFromHeaders, fetchRequestedQuotas } from "./quota-store";
|
|
23
26
|
import {
|
|
24
27
|
type NeuralwattRateLimitInfo,
|
|
@@ -33,8 +36,10 @@ const HEADER_EMIT_THROTTLE_MS = 5_000;
|
|
|
33
36
|
function registerNeuralwattProvider(
|
|
34
37
|
pi: ExtensionAPI,
|
|
35
38
|
onSseQuota: (line: string) => void,
|
|
39
|
+
hiddenModels: ProviderModelConfig[] = [],
|
|
36
40
|
): void {
|
|
37
|
-
const { includeLegacyModelIds } =
|
|
41
|
+
const { includeLegacyModelIds, includeHiddenModels } =
|
|
42
|
+
configLoader.getConfig();
|
|
38
43
|
|
|
39
44
|
const config: Parameters<ExtensionAPI["registerProvider"]>[1] = {
|
|
40
45
|
baseUrl: "https://api.neuralwatt.com/v1",
|
|
@@ -45,9 +50,10 @@ function registerNeuralwattProvider(
|
|
|
45
50
|
Referer: "https://pi.dev",
|
|
46
51
|
"X-Title": "npm:@aliou/pi-neuralwatt",
|
|
47
52
|
},
|
|
48
|
-
models:
|
|
49
|
-
includeLegacyModelIds,
|
|
50
|
-
|
|
53
|
+
models: [
|
|
54
|
+
...getNeuralwattModels({ includeLegacyModelIds }),
|
|
55
|
+
...(includeHiddenModels ? hiddenModels : []),
|
|
56
|
+
],
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
const provider = getApiProvider("openai-completions");
|
|
@@ -66,6 +72,8 @@ export default async function (pi: ExtensionAPI) {
|
|
|
66
72
|
await configLoader.load();
|
|
67
73
|
|
|
68
74
|
let latestQuotas: NeuralwattQuotas | undefined;
|
|
75
|
+
let hiddenModels: ProviderModelConfig[] = [];
|
|
76
|
+
let hiddenModelsLoaded = false;
|
|
69
77
|
|
|
70
78
|
const handleSseQuota = (line: string) => {
|
|
71
79
|
const quotas = updateQuotasFromSseComment(latestQuotas, line);
|
|
@@ -87,7 +95,7 @@ export default async function (pi: ExtensionAPI) {
|
|
|
87
95
|
});
|
|
88
96
|
|
|
89
97
|
pi.events.on(NEURALWATT_CONFIG_UPDATED_EVENT, () => {
|
|
90
|
-
registerNeuralwattProvider(pi, handleSseQuota);
|
|
98
|
+
registerNeuralwattProvider(pi, handleSseQuota, hiddenModels);
|
|
91
99
|
});
|
|
92
100
|
|
|
93
101
|
let lastHeaderEmitAt = 0;
|
|
@@ -193,6 +201,15 @@ export default async function (pi: ExtensionAPI) {
|
|
|
193
201
|
pi.events.emit(NEURALWATT_EXTENSIONS_REQUEST_EVENT, undefined);
|
|
194
202
|
emitConfigUpdated(pi);
|
|
195
203
|
|
|
204
|
+
if (!hiddenModelsLoaded && configLoader.getConfig().includeHiddenModels) {
|
|
205
|
+
hiddenModelsLoaded = true;
|
|
206
|
+
const fetched = await loadHiddenModels(ctx.modelRegistry.authStorage);
|
|
207
|
+
if (fetched.length > 0) {
|
|
208
|
+
hiddenModels = fetched;
|
|
209
|
+
registerNeuralwattProvider(pi, handleSseQuota, hiddenModels);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
196
213
|
if (ctx.model?.provider !== "neuralwatt") return;
|
|
197
214
|
const apiKey = await getNeuralwattApiKey(ctx.modelRegistry.authStorage);
|
|
198
215
|
if (!apiKey) return;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AuthStorage,
|
|
3
|
+
ProviderModelConfig,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { getNeuralwattApiKey } from "../../../lib/env";
|
|
6
|
+
import { fetchNeuralwattModels } from "../../../lib/neuralwatt-api";
|
|
7
|
+
import type { NeuralwattApiModel } from "../../../types/models-api";
|
|
8
|
+
import { NEURALWATT_MODELS } from "./public-models";
|
|
9
|
+
|
|
10
|
+
// Per-ID overrides for known hidden models. The authenticated /v1/models endpoint
|
|
11
|
+
// exposes pricing and capabilities, but some Pi-specific behavior (thinking levels,
|
|
12
|
+
// compat flags) has to be supplied by hand.
|
|
13
|
+
const HIDDEN_MODEL_OVERRIDES: Partial<
|
|
14
|
+
Record<string, Partial<ProviderModelConfig>>
|
|
15
|
+
> = {
|
|
16
|
+
"glm-5.2-short": {
|
|
17
|
+
thinkingLevelMap: {
|
|
18
|
+
minimal: null,
|
|
19
|
+
low: null,
|
|
20
|
+
medium: null,
|
|
21
|
+
high: "high",
|
|
22
|
+
xhigh: "max",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function buildHiddenModel(apiModel: NeuralwattApiModel): ProviderModelConfig {
|
|
28
|
+
const meta = apiModel.metadata;
|
|
29
|
+
const reasoning = meta?.capabilities.reasoning ?? false;
|
|
30
|
+
const override = HIDDEN_MODEL_OVERRIDES[apiModel.id];
|
|
31
|
+
|
|
32
|
+
const compat: NonNullable<ProviderModelConfig["compat"]> = {
|
|
33
|
+
supportsDeveloperRole: false,
|
|
34
|
+
maxTokensField: "max_tokens",
|
|
35
|
+
};
|
|
36
|
+
if (reasoning) {
|
|
37
|
+
compat.requiresReasoningContentOnAssistantMessages = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const model: ProviderModelConfig = {
|
|
41
|
+
id: apiModel.id,
|
|
42
|
+
name: meta?.display_name ?? apiModel.id,
|
|
43
|
+
reasoning,
|
|
44
|
+
input: (meta?.capabilities.vision ? ["text", "image"] : ["text"]) as (
|
|
45
|
+
| "text"
|
|
46
|
+
| "image"
|
|
47
|
+
)[],
|
|
48
|
+
cost: {
|
|
49
|
+
input: meta?.pricing.input_per_million ?? 0,
|
|
50
|
+
output: meta?.pricing.output_per_million ?? 0,
|
|
51
|
+
cacheRead: meta?.pricing.cached_input_per_million ?? 0,
|
|
52
|
+
cacheWrite: meta?.pricing.cached_output_per_million ?? 0,
|
|
53
|
+
},
|
|
54
|
+
contextWindow: apiModel.max_model_len,
|
|
55
|
+
maxTokens: meta?.limits.max_output_tokens ?? 65536,
|
|
56
|
+
compat,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (reasoning) {
|
|
60
|
+
model.thinkingLevelMap = override?.thinkingLevelMap ?? {
|
|
61
|
+
minimal: null,
|
|
62
|
+
low: null,
|
|
63
|
+
medium: "medium",
|
|
64
|
+
high: null,
|
|
65
|
+
xhigh: null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (override) {
|
|
70
|
+
return applyHiddenOverride(model, override);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return model;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function applyHiddenOverride(
|
|
77
|
+
model: ProviderModelConfig,
|
|
78
|
+
override: Partial<ProviderModelConfig>,
|
|
79
|
+
): ProviderModelConfig {
|
|
80
|
+
const result: ProviderModelConfig = { ...model };
|
|
81
|
+
|
|
82
|
+
if (override.name !== undefined) result.name = override.name;
|
|
83
|
+
if (override.reasoning !== undefined) result.reasoning = override.reasoning;
|
|
84
|
+
if (override.input !== undefined) result.input = override.input;
|
|
85
|
+
if (override.thinkingLevelMap !== undefined) {
|
|
86
|
+
result.thinkingLevelMap = override.thinkingLevelMap;
|
|
87
|
+
}
|
|
88
|
+
if (override.contextWindow !== undefined) {
|
|
89
|
+
result.contextWindow = override.contextWindow;
|
|
90
|
+
}
|
|
91
|
+
if (override.maxTokens !== undefined) result.maxTokens = override.maxTokens;
|
|
92
|
+
if (override.cost !== undefined) {
|
|
93
|
+
result.cost = { ...model.cost, ...override.cost };
|
|
94
|
+
}
|
|
95
|
+
if (override.compat !== undefined) {
|
|
96
|
+
result.compat = { ...model.compat, ...override.compat };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Load hidden models from the authenticated /v1/models endpoint.
|
|
104
|
+
*
|
|
105
|
+
* Hidden models are any models returned by the API that are not already part of
|
|
106
|
+
* the public hardcoded list. If the API key is missing or the request fails, an
|
|
107
|
+
* empty array is returned silently.
|
|
108
|
+
*/
|
|
109
|
+
export async function loadHiddenModels(
|
|
110
|
+
authStorage: AuthStorage,
|
|
111
|
+
): Promise<ProviderModelConfig[]> {
|
|
112
|
+
const apiKey = await getNeuralwattApiKey(authStorage);
|
|
113
|
+
if (!apiKey) return [];
|
|
114
|
+
|
|
115
|
+
const result = await fetchNeuralwattModels(apiKey);
|
|
116
|
+
if (!result.success) return [];
|
|
117
|
+
|
|
118
|
+
const publicIds = new Set(NEURALWATT_MODELS.map((model) => model.id));
|
|
119
|
+
|
|
120
|
+
return result.data
|
|
121
|
+
.filter(
|
|
122
|
+
(model) =>
|
|
123
|
+
!model.metadata?.deprecated && !model.metadata?.pricing.pricing_tbd,
|
|
124
|
+
)
|
|
125
|
+
.filter((model) => !publicIds.has(model.id))
|
|
126
|
+
.map(buildHiddenModel);
|
|
127
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { buildLegacyNeuralwattModels } from "./legacy";
|
|
3
|
+
import { NEURALWATT_MODELS } from "./public-models";
|
|
4
|
+
|
|
5
|
+
export { loadHiddenModels } from "./hidden";
|
|
6
|
+
export {
|
|
7
|
+
buildLegacyNeuralwattModels,
|
|
8
|
+
LEGACY_MODEL_ALIAS_MAP,
|
|
9
|
+
LEGACY_NEURALWATT_MODEL_IDS,
|
|
10
|
+
} from "./legacy";
|
|
11
|
+
export { NEURALWATT_MODELS } from "./public-models";
|
|
12
|
+
|
|
13
|
+
export function getNeuralwattModels(options?: {
|
|
14
|
+
includeLegacyModelIds?: boolean;
|
|
15
|
+
}): ProviderModelConfig[] {
|
|
16
|
+
const models: ProviderModelConfig[] = [...NEURALWATT_MODELS];
|
|
17
|
+
|
|
18
|
+
if (options?.includeLegacyModelIds) {
|
|
19
|
+
models.push(...buildLegacyNeuralwattModels());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return models;
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { NEURALWATT_MODELS } from "./public-models";
|
|
3
|
+
|
|
4
|
+
// Legacy model IDs that should resolve to a canonical public model.
|
|
5
|
+
// These are phased out over time and are only included when `includeLegacyModelIds` is enabled.
|
|
6
|
+
export const LEGACY_MODEL_ALIAS_MAP = {
|
|
7
|
+
"glm-5.1": "glm-5.2",
|
|
8
|
+
"glm-5.1-fast": "glm-5.2-fast",
|
|
9
|
+
"zai-org/GLM-5.1-FP8": "glm-5.2",
|
|
10
|
+
"moonshotai/Kimi-K2.6": "kimi-k2.6",
|
|
11
|
+
"Qwen/Qwen3.5-397B-A17B-FP8": "qwen3.5-397b",
|
|
12
|
+
"Qwen/Qwen3.6-35B-A3B": "qwen3.6-35b",
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export const LEGACY_NEURALWATT_MODEL_IDS = new Set<string>(
|
|
16
|
+
Object.keys(LEGACY_MODEL_ALIAS_MAP),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export function buildLegacyNeuralwattModels(): ProviderModelConfig[] {
|
|
20
|
+
return Object.entries(LEGACY_MODEL_ALIAS_MAP).map(
|
|
21
|
+
([legacyId, canonicalId]) => {
|
|
22
|
+
const canonical = NEURALWATT_MODELS.find(
|
|
23
|
+
(model) => model.id === canonicalId,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (!canonical) {
|
|
27
|
+
throw new Error(`Missing canonical model for legacy alias ${legacyId}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...canonical,
|
|
32
|
+
id: legacyId,
|
|
33
|
+
name: `${canonical.name} (legacy ID)`,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -1,86 +1,9 @@
|
|
|
1
|
-
// Hardcoded models from Neuralwatt API
|
|
2
|
-
// Source: https://api.neuralwatt.com/v1/models
|
|
3
|
-
// Pricing, capabilities, and limits from metadata fields in /v1/models
|
|
4
|
-
|
|
5
1
|
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
6
2
|
|
|
3
|
+
// Public models returned by https://api.neuralwatt.com/v1/models (unauthenticated view).
|
|
4
|
+
// Pricing, capabilities, and limits are sourced from the API metadata fields.
|
|
7
5
|
export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
8
|
-
// GLM-5.
|
|
9
|
-
// Legacy id previously aliased to glm-5.1; now serving a GLM-5.2 test build.
|
|
10
|
-
{
|
|
11
|
-
id: "zai-org/GLM-5.1-FP8",
|
|
12
|
-
name: "GLM-5.2 (test)",
|
|
13
|
-
reasoning: true,
|
|
14
|
-
input: ["text"],
|
|
15
|
-
cost: {
|
|
16
|
-
input: 1.1,
|
|
17
|
-
output: 3.6,
|
|
18
|
-
cacheRead: 0,
|
|
19
|
-
cacheWrite: 0,
|
|
20
|
-
},
|
|
21
|
-
contextWindow: 1048560,
|
|
22
|
-
maxTokens: 65536,
|
|
23
|
-
thinkingLevelMap: {
|
|
24
|
-
minimal: null,
|
|
25
|
-
low: null,
|
|
26
|
-
medium: "medium",
|
|
27
|
-
high: null,
|
|
28
|
-
xhigh: null,
|
|
29
|
-
},
|
|
30
|
-
compat: {
|
|
31
|
-
supportsDeveloperRole: false,
|
|
32
|
-
maxTokensField: "max_tokens",
|
|
33
|
-
requiresReasoningContentOnAssistantMessages: true,
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
// GLM-5.1 - ZhipuAI
|
|
37
|
-
// Backed by the 1048K GLM-5.2 deployment (GLM-5.1 redirect in effect). Deprecated.
|
|
38
|
-
{
|
|
39
|
-
id: "glm-5.1",
|
|
40
|
-
name: "GLM-5.1",
|
|
41
|
-
reasoning: true,
|
|
42
|
-
input: ["text"],
|
|
43
|
-
cost: {
|
|
44
|
-
input: 1.1,
|
|
45
|
-
output: 3.6,
|
|
46
|
-
cacheRead: 0,
|
|
47
|
-
cacheWrite: 0,
|
|
48
|
-
},
|
|
49
|
-
contextWindow: 1048560,
|
|
50
|
-
maxTokens: 65536,
|
|
51
|
-
thinkingLevelMap: {
|
|
52
|
-
minimal: null,
|
|
53
|
-
low: null,
|
|
54
|
-
medium: "medium",
|
|
55
|
-
high: null,
|
|
56
|
-
xhigh: null,
|
|
57
|
-
},
|
|
58
|
-
compat: {
|
|
59
|
-
supportsDeveloperRole: false,
|
|
60
|
-
maxTokensField: "max_tokens",
|
|
61
|
-
requiresReasoningContentOnAssistantMessages: true,
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
// GLM-5.1 Fast - ZhipuAI
|
|
65
|
-
{
|
|
66
|
-
id: "glm-5.1-fast",
|
|
67
|
-
name: "GLM-5.1 Fast",
|
|
68
|
-
reasoning: false,
|
|
69
|
-
input: ["text"],
|
|
70
|
-
cost: {
|
|
71
|
-
input: 1.1,
|
|
72
|
-
output: 3.6,
|
|
73
|
-
cacheRead: 0,
|
|
74
|
-
cacheWrite: 0,
|
|
75
|
-
},
|
|
76
|
-
contextWindow: 1048560,
|
|
77
|
-
maxTokens: 65536,
|
|
78
|
-
compat: {
|
|
79
|
-
supportsDeveloperRole: false,
|
|
80
|
-
maxTokensField: "max_tokens",
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
// GLM-5.2 - ZhipuAI (test canary)
|
|
6
|
+
// GLM-5.2 - ZhipuAI
|
|
84
7
|
{
|
|
85
8
|
id: "glm-5.2",
|
|
86
9
|
name: "GLM-5.2",
|
|
@@ -89,14 +12,11 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
89
12
|
cost: {
|
|
90
13
|
input: 1.45,
|
|
91
14
|
output: 4.5,
|
|
92
|
-
cacheRead: 0,
|
|
15
|
+
cacheRead: 0.3625,
|
|
93
16
|
cacheWrite: 0,
|
|
94
17
|
},
|
|
95
18
|
contextWindow: 1048560,
|
|
96
19
|
maxTokens: 65536,
|
|
97
|
-
// GLM-5.2 has two native reasoning depths (high, max) plus thinking-off.
|
|
98
|
-
// Pi levels below high disable thinking; high -> high, xhigh -> max.
|
|
99
|
-
// See https://portal.neuralwatt.com/docs/api/chat-completions#reasoning-effort
|
|
100
20
|
thinkingLevelMap: {
|
|
101
21
|
minimal: null,
|
|
102
22
|
low: null,
|
|
@@ -119,7 +39,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
119
39
|
cost: {
|
|
120
40
|
input: 1.45,
|
|
121
41
|
output: 4.5,
|
|
122
|
-
cacheRead: 0,
|
|
42
|
+
cacheRead: 0.3625,
|
|
123
43
|
cacheWrite: 0,
|
|
124
44
|
},
|
|
125
45
|
contextWindow: 1048560,
|
|
@@ -138,7 +58,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
138
58
|
cost: {
|
|
139
59
|
input: 0.52,
|
|
140
60
|
output: 2.59,
|
|
141
|
-
cacheRead: 0,
|
|
61
|
+
cacheRead: 0.13,
|
|
142
62
|
cacheWrite: 0,
|
|
143
63
|
},
|
|
144
64
|
contextWindow: 262128,
|
|
@@ -165,7 +85,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
165
85
|
cost: {
|
|
166
86
|
input: 0.52,
|
|
167
87
|
output: 2.59,
|
|
168
|
-
cacheRead: 0,
|
|
88
|
+
cacheRead: 0.13,
|
|
169
89
|
cacheWrite: 0,
|
|
170
90
|
},
|
|
171
91
|
contextWindow: 262128,
|
|
@@ -184,7 +104,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
184
104
|
cost: {
|
|
185
105
|
input: 0.69,
|
|
186
106
|
output: 3.22,
|
|
187
|
-
cacheRead: 0,
|
|
107
|
+
cacheRead: 0.1725,
|
|
188
108
|
cacheWrite: 0,
|
|
189
109
|
},
|
|
190
110
|
contextWindow: 262128,
|
|
@@ -211,7 +131,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
211
131
|
cost: {
|
|
212
132
|
input: 0.69,
|
|
213
133
|
output: 3.22,
|
|
214
|
-
cacheRead: 0,
|
|
134
|
+
cacheRead: 0.1725,
|
|
215
135
|
cacheWrite: 0,
|
|
216
136
|
},
|
|
217
137
|
contextWindow: 262128,
|
|
@@ -230,7 +150,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
230
150
|
cost: {
|
|
231
151
|
input: 0.69,
|
|
232
152
|
output: 4.14,
|
|
233
|
-
cacheRead: 0,
|
|
153
|
+
cacheRead: 0.1725,
|
|
234
154
|
cacheWrite: 0,
|
|
235
155
|
},
|
|
236
156
|
contextWindow: 262128,
|
|
@@ -257,7 +177,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
257
177
|
cost: {
|
|
258
178
|
input: 0.69,
|
|
259
179
|
output: 4.14,
|
|
260
|
-
cacheRead: 0,
|
|
180
|
+
cacheRead: 0.1725,
|
|
261
181
|
cacheWrite: 0,
|
|
262
182
|
},
|
|
263
183
|
contextWindow: 262128,
|
|
@@ -276,7 +196,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
276
196
|
cost: {
|
|
277
197
|
input: 0.29,
|
|
278
198
|
output: 1.15,
|
|
279
|
-
cacheRead: 0,
|
|
199
|
+
cacheRead: 0.0725,
|
|
280
200
|
cacheWrite: 0,
|
|
281
201
|
},
|
|
282
202
|
contextWindow: 131056,
|
|
@@ -303,7 +223,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
303
223
|
cost: {
|
|
304
224
|
input: 0.95,
|
|
305
225
|
output: 4.0,
|
|
306
|
-
cacheRead: 0,
|
|
226
|
+
cacheRead: 0.2375,
|
|
307
227
|
cacheWrite: 0,
|
|
308
228
|
},
|
|
309
229
|
contextWindow: 262128,
|
|
@@ -331,7 +251,7 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
331
251
|
cost: {
|
|
332
252
|
input: 0.29,
|
|
333
253
|
output: 1.15,
|
|
334
|
-
cacheRead: 0,
|
|
254
|
+
cacheRead: 0.0725,
|
|
335
255
|
cacheWrite: 0,
|
|
336
256
|
},
|
|
337
257
|
contextWindow: 131056,
|
|
@@ -342,38 +262,3 @@ export const NEURALWATT_MODELS: ProviderModelConfig[] = [
|
|
|
342
262
|
},
|
|
343
263
|
},
|
|
344
264
|
];
|
|
345
|
-
|
|
346
|
-
const LEGACY_MODEL_ALIAS_MAP = {
|
|
347
|
-
"moonshotai/Kimi-K2.6": "kimi-k2.6",
|
|
348
|
-
"Qwen/Qwen3.5-397B-A17B-FP8": "qwen3.5-397b",
|
|
349
|
-
"Qwen/Qwen3.6-35B-A3B": "qwen3.6-35b",
|
|
350
|
-
} as const;
|
|
351
|
-
|
|
352
|
-
export const LEGACY_NEURALWATT_MODEL_IDS = new Set<string>(
|
|
353
|
-
Object.keys(LEGACY_MODEL_ALIAS_MAP),
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
const LEGACY_NEURALWATT_MODELS: ProviderModelConfig[] = Object.entries(
|
|
357
|
-
LEGACY_MODEL_ALIAS_MAP,
|
|
358
|
-
).map(([legacyId, canonicalId]) => {
|
|
359
|
-
const canonical = NEURALWATT_MODELS.find((model) => model.id === canonicalId);
|
|
360
|
-
|
|
361
|
-
if (!canonical) {
|
|
362
|
-
throw new Error(`Missing canonical model for legacy alias ${legacyId}`);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
...canonical,
|
|
367
|
-
id: legacyId,
|
|
368
|
-
name: `${canonical.name} (legacy ID)`,
|
|
369
|
-
};
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
export function getNeuralwattModels(options?: {
|
|
373
|
-
includeLegacyModelIds?: boolean;
|
|
374
|
-
}): ProviderModelConfig[] {
|
|
375
|
-
if (options?.includeLegacyModelIds)
|
|
376
|
-
return [...NEURALWATT_MODELS, ...LEGACY_NEURALWATT_MODELS];
|
|
377
|
-
|
|
378
|
-
return NEURALWATT_MODELS;
|
|
379
|
-
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AuthStorage } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { getNeuralwattApiKey } from "../../lib/env";
|
|
3
|
+
import { fetchQuotas } from "../../lib/neuralwatt-api";
|
|
3
4
|
import type { NeuralwattQuotas } from "../../types/quota-api";
|
|
4
5
|
import { parseQuotaHeaders } from "../../types/quota-events";
|
|
5
|
-
import { fetchQuotas } from "../../utils/quotas";
|
|
6
6
|
|
|
7
7
|
export function buildQuotasFromHeaders(
|
|
8
8
|
headers: Record<string, string>,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NeuralwattApiModel,
|
|
3
|
+
NeuralwattApiModelsResponse,
|
|
4
|
+
} from "../types/models-api";
|
|
1
5
|
import type { NeuralwattQuotas } from "../types/quota-api";
|
|
2
6
|
import type { QuotasResult } from "../types/quota-events";
|
|
3
7
|
|
|
8
|
+
const BASE_URL = "https://api.neuralwatt.com/v1";
|
|
4
9
|
const FETCH_TIMEOUT_MS = 15_000;
|
|
5
10
|
|
|
6
11
|
function isTimeoutReason(reason: unknown): boolean {
|
|
@@ -10,6 +15,55 @@ function isTimeoutReason(reason: unknown): boolean {
|
|
|
10
15
|
);
|
|
11
16
|
}
|
|
12
17
|
|
|
18
|
+
function combineSignals(signal?: AbortSignal): AbortSignal {
|
|
19
|
+
const signals: AbortSignal[] = [AbortSignal.timeout(FETCH_TIMEOUT_MS)];
|
|
20
|
+
if (signal) signals.push(signal);
|
|
21
|
+
return AbortSignal.any(signals);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function neuralwattFetch(
|
|
25
|
+
path: string,
|
|
26
|
+
apiKey: string,
|
|
27
|
+
signal?: AbortSignal,
|
|
28
|
+
headers?: Record<string, string>,
|
|
29
|
+
): Promise<Response> {
|
|
30
|
+
return fetch(`${BASE_URL}${path}`, {
|
|
31
|
+
headers: { Authorization: `Bearer ${apiKey}`, ...headers },
|
|
32
|
+
signal: combineSignals(signal),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type NeuralwattModelsResult =
|
|
37
|
+
| { success: true; data: NeuralwattApiModel[] }
|
|
38
|
+
| { success: false };
|
|
39
|
+
|
|
40
|
+
export async function fetchNeuralwattModels(
|
|
41
|
+
apiKey: string,
|
|
42
|
+
signal?: AbortSignal,
|
|
43
|
+
): Promise<NeuralwattModelsResult> {
|
|
44
|
+
if (!apiKey) {
|
|
45
|
+
return { success: false };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const combined = combineSignals(signal);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const response = await neuralwattFetch("/models", apiKey, combined, {
|
|
52
|
+
Referer: "https://pi.dev",
|
|
53
|
+
"X-Title": "npm:@aliou/pi-neuralwatt",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
return { success: false };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const data: NeuralwattApiModelsResponse = await response.json();
|
|
61
|
+
return { success: true, data: data.data };
|
|
62
|
+
} catch {
|
|
63
|
+
return { success: false };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
13
67
|
export async function fetchQuotas(
|
|
14
68
|
apiKey: string,
|
|
15
69
|
signal?: AbortSignal,
|
|
@@ -21,15 +75,10 @@ export async function fetchQuotas(
|
|
|
21
75
|
};
|
|
22
76
|
}
|
|
23
77
|
|
|
24
|
-
const
|
|
25
|
-
if (signal) signals.push(signal);
|
|
26
|
-
const combined = AbortSignal.any(signals);
|
|
78
|
+
const combined = combineSignals(signal);
|
|
27
79
|
|
|
28
80
|
try {
|
|
29
|
-
const response = await
|
|
30
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
31
|
-
signal: combined,
|
|
32
|
-
});
|
|
81
|
+
const response = await neuralwattFetch("/quota", apiKey, combined);
|
|
33
82
|
|
|
34
83
|
if (!response.ok) {
|
|
35
84
|
let message = response.statusText;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface NeuralwattApiModelPricing {
|
|
2
|
+
input_per_million: number;
|
|
3
|
+
output_per_million: number;
|
|
4
|
+
cached_input_per_million: number | null;
|
|
5
|
+
cached_output_per_million: number | null;
|
|
6
|
+
currency: string;
|
|
7
|
+
pricing_tbd: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface NeuralwattApiModelCapabilities {
|
|
11
|
+
tools: boolean;
|
|
12
|
+
json_mode: boolean;
|
|
13
|
+
vision: boolean;
|
|
14
|
+
reasoning: boolean;
|
|
15
|
+
reasoning_effort: boolean;
|
|
16
|
+
streaming: boolean;
|
|
17
|
+
system_role: boolean;
|
|
18
|
+
developer_role: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface NeuralwattApiModelLimits {
|
|
22
|
+
max_context_length: number;
|
|
23
|
+
max_output_tokens: number | null;
|
|
24
|
+
max_images: number | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface NeuralwattApiModelMetadata {
|
|
28
|
+
display_name: string;
|
|
29
|
+
description: string | null;
|
|
30
|
+
provider: string;
|
|
31
|
+
huggingface_id: string | null;
|
|
32
|
+
pricing: NeuralwattApiModelPricing;
|
|
33
|
+
capabilities: NeuralwattApiModelCapabilities;
|
|
34
|
+
limits: NeuralwattApiModelLimits;
|
|
35
|
+
deprecated: boolean;
|
|
36
|
+
deprecated_message: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface NeuralwattApiModel {
|
|
40
|
+
id: string;
|
|
41
|
+
object: string;
|
|
42
|
+
created: number;
|
|
43
|
+
owned_by: string;
|
|
44
|
+
root?: string;
|
|
45
|
+
parent?: string | null;
|
|
46
|
+
max_model_len: number;
|
|
47
|
+
metadata?: NeuralwattApiModelMetadata;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface NeuralwattApiModelsResponse {
|
|
51
|
+
object: "list";
|
|
52
|
+
data: NeuralwattApiModel[];
|
|
53
|
+
}
|