@aliou/pi-neuralwatt 0.14.2 → 0.15.0
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/extensions/provider/commands/settings/index.ts +0 -73
- package/extensions/provider/index.ts +13 -35
- package/extensions/provider/models/catalog.ts +148 -0
- package/extensions/provider/models/index.ts +10 -37
- package/extensions/provider/models/refresh.ts +65 -183
- package/extensions/provider/provider.ts +19 -26
- package/package.json +1 -1
- package/schema.json +0 -22
- package/src/config/defaults.ts +0 -5
- package/src/config/loader.ts +0 -11
- package/src/config/migration/01-disable-legacy-model-ids-by-default.ts +15 -28
- package/src/config/migration/02-flat-to-nested-config.ts +35 -71
- package/src/config/migration/03-rename-hidden-to-early-access.ts +33 -38
- package/src/config/migration/04-enable-aliases-for-legacy-users.ts +16 -25
- package/src/config/migration/index.ts +5 -2
- package/src/config/types.ts +0 -19
- package/src/lib/neuralwatt-api.ts +7 -7
- package/extensions/provider/models/aliases.ts +0 -35
- package/extensions/provider/models/early-access.ts +0 -158
- package/extensions/provider/models/legacy.ts +0 -47
|
@@ -104,82 +104,9 @@ export function registerNeuralwattSettings(
|
|
|
104
104
|
),
|
|
105
105
|
],
|
|
106
106
|
},
|
|
107
|
-
{
|
|
108
|
-
label: "Other settings",
|
|
109
|
-
items: [
|
|
110
|
-
{
|
|
111
|
-
id: "includeLegacyModelIds",
|
|
112
|
-
label: "Legacy model IDs",
|
|
113
|
-
description:
|
|
114
|
-
"Include deprecated Neuralwatt model IDs as aliases in the model picker",
|
|
115
|
-
currentValue:
|
|
116
|
-
(tabConfig?.provider?.includeLegacyModelIds ??
|
|
117
|
-
resolved.provider.includeLegacyModelIds)
|
|
118
|
-
? "include"
|
|
119
|
-
: "ignore",
|
|
120
|
-
values: ["include", "ignore"],
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
id: "includeAliasedModelIds",
|
|
124
|
-
label: "Alias model IDs",
|
|
125
|
-
description:
|
|
126
|
-
"Include active creator-scoped model IDs as aliases in the model picker",
|
|
127
|
-
currentValue:
|
|
128
|
-
(tabConfig?.provider?.includeAliasedModelIds ??
|
|
129
|
-
resolved.provider.includeAliasedModelIds)
|
|
130
|
-
? "include"
|
|
131
|
-
: "ignore",
|
|
132
|
-
values: ["include", "ignore"],
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
id: "includeEarlyAccessModels",
|
|
136
|
-
label: "Early access models",
|
|
137
|
-
description:
|
|
138
|
-
"Include pre-release Neuralwatt models that your API key can reach but that are not yet in the public model list",
|
|
139
|
-
currentValue:
|
|
140
|
-
(tabConfig?.provider?.includeEarlyAccessModels ??
|
|
141
|
-
resolved.provider.includeEarlyAccessModels)
|
|
142
|
-
? "include"
|
|
143
|
-
: "ignore",
|
|
144
|
-
values: ["include", "ignore"],
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
},
|
|
148
107
|
];
|
|
149
108
|
},
|
|
150
109
|
onSettingChange: (id, newValue, config) => {
|
|
151
|
-
// Non-feature toggles are handled first so they are not blocked by the
|
|
152
|
-
// loaded-features guard (they are managed directly by the provider).
|
|
153
|
-
if (id === "includeLegacyModelIds") {
|
|
154
|
-
return {
|
|
155
|
-
...config,
|
|
156
|
-
provider: {
|
|
157
|
-
...config.provider,
|
|
158
|
-
includeLegacyModelIds: newValue === "include",
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (id === "includeAliasedModelIds") {
|
|
164
|
-
return {
|
|
165
|
-
...config,
|
|
166
|
-
provider: {
|
|
167
|
-
...config.provider,
|
|
168
|
-
includeAliasedModelIds: newValue === "include",
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (id === "includeEarlyAccessModels") {
|
|
174
|
-
return {
|
|
175
|
-
...config,
|
|
176
|
-
provider: {
|
|
177
|
-
...config.provider,
|
|
178
|
-
includeEarlyAccessModels: newValue === "include",
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
110
|
if (!getLoadedFeatures().has(id as NeuralwattFeatureId)) {
|
|
184
111
|
return null;
|
|
185
112
|
}
|
|
@@ -13,12 +13,15 @@ import {
|
|
|
13
13
|
type NeuralwattFeatureId,
|
|
14
14
|
type NeuralwattQuotasUpdatedPayload,
|
|
15
15
|
} from "../../src/events";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
fetchNeuralwattModels,
|
|
18
|
+
fetchQuotas,
|
|
19
|
+
} from "../../src/lib/neuralwatt-api";
|
|
17
20
|
import type { NeuralwattQuotas } from "../../src/types/quota-api";
|
|
18
21
|
import { getNeuralwattApiKey } from "../_shared/auth";
|
|
19
22
|
import { registerNeuralwattSettings } from "./commands/settings";
|
|
20
23
|
import { normalizeNeuralwattContextOverflowError } from "./context-overflow";
|
|
21
|
-
import {
|
|
24
|
+
import { buildNeuralwattProviderModels } from "./models";
|
|
22
25
|
import { createNeuralwattProvider } from "./provider";
|
|
23
26
|
import { buildQuotasFromHeaders, fetchRequestedQuotas } from "./quota-store";
|
|
24
27
|
import {
|
|
@@ -41,12 +44,7 @@ function registerNeuralwattProvider(
|
|
|
41
44
|
pi: ExtensionAPI,
|
|
42
45
|
onSseQuota: (line: string) => void,
|
|
43
46
|
): void {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
const staticModels = getNeuralwattModels({
|
|
47
|
-
includeLegacyModelIds: providerConfig.includeLegacyModelIds,
|
|
48
|
-
includeAliasedModelIds: providerConfig.includeAliasedModelIds,
|
|
49
|
-
});
|
|
47
|
+
const staticModels = buildNeuralwattProviderModels();
|
|
50
48
|
|
|
51
49
|
const apiProvider = getApiProvider("openai-completions");
|
|
52
50
|
const baseStreamSimple = apiProvider?.streamSimple;
|
|
@@ -60,14 +58,13 @@ function registerNeuralwattProvider(
|
|
|
60
58
|
pi.registerProvider(
|
|
61
59
|
createNeuralwattProvider(
|
|
62
60
|
staticModels,
|
|
63
|
-
() =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}),
|
|
61
|
+
async (apiKey, signal) => {
|
|
62
|
+
const result = await fetchNeuralwattModels(apiKey, signal);
|
|
63
|
+
if (!result.success) {
|
|
64
|
+
throw new Error("Neuralwatt models API request failed");
|
|
65
|
+
}
|
|
66
|
+
return result.data;
|
|
67
|
+
},
|
|
71
68
|
streamSimple,
|
|
72
69
|
),
|
|
73
70
|
);
|
|
@@ -92,9 +89,6 @@ export default async function (pi: ExtensionAPI) {
|
|
|
92
89
|
};
|
|
93
90
|
|
|
94
91
|
registerNeuralwattProvider(pi, handleSseQuota);
|
|
95
|
-
let registeredProviderSettings = {
|
|
96
|
-
...configLoader.getConfig().provider,
|
|
97
|
-
};
|
|
98
92
|
|
|
99
93
|
const loadedFeatures = new Set<NeuralwattFeatureId>();
|
|
100
94
|
|
|
@@ -103,22 +97,6 @@ export default async function (pi: ExtensionAPI) {
|
|
|
103
97
|
getLoadedFeatures: () => loadedFeatures,
|
|
104
98
|
});
|
|
105
99
|
|
|
106
|
-
pi.events.on(NEURALWATT_CONFIG_UPDATED_EVENT, () => {
|
|
107
|
-
const next = configLoader.getConfig().provider;
|
|
108
|
-
if (
|
|
109
|
-
next.includeLegacyModelIds ===
|
|
110
|
-
registeredProviderSettings.includeLegacyModelIds &&
|
|
111
|
-
next.includeAliasedModelIds ===
|
|
112
|
-
registeredProviderSettings.includeAliasedModelIds &&
|
|
113
|
-
next.includeEarlyAccessModels ===
|
|
114
|
-
registeredProviderSettings.includeEarlyAccessModels
|
|
115
|
-
) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
registeredProviderSettings = { ...next };
|
|
119
|
-
registerNeuralwattProvider(pi, handleSseQuota);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
100
|
let lastHeaderEmitAt = 0;
|
|
123
101
|
let quotaRequestInFlight = false;
|
|
124
102
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { NeuralwattApiModel } from "../../../src/types/models-api";
|
|
3
|
+
import {
|
|
4
|
+
buildThinkingLevelMap,
|
|
5
|
+
FLEX_COST_MULTIPLIER,
|
|
6
|
+
resolveMaxTokens,
|
|
7
|
+
type ThinkingLevelMap,
|
|
8
|
+
} from "./build";
|
|
9
|
+
import { NEURALWATT_MODELS } from "./public-models";
|
|
10
|
+
|
|
11
|
+
export type NeuralwattModel = ProviderModelConfig;
|
|
12
|
+
|
|
13
|
+
const CONTEXT_WINDOW_OVERRIDES: ReadonlyMap<string, number> = new Map([
|
|
14
|
+
["kimi-k3", 327_680],
|
|
15
|
+
["kimi-k3-fast", 327_680],
|
|
16
|
+
["kimi-k3-flex", 327_680],
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
// Chat-template thinking: the API exposes a `reasoning` block, but the
|
|
20
|
+
// underlying mechanism is chat_template_kwargs, so Pi needs the mapping.
|
|
21
|
+
const COMPAT_OVERRIDES: Partial<
|
|
22
|
+
Record<string, Partial<NonNullable<ProviderModelConfig["compat"]>>>
|
|
23
|
+
> = {
|
|
24
|
+
"Qwen/Qwen3.8-27B-FP8": {
|
|
25
|
+
thinkingFormat: "chat-template",
|
|
26
|
+
chatTemplateKwargs: {
|
|
27
|
+
enable_thinking: { $var: "thinking.enabled" },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const HARDCODED_ALIASES: Record<string, string> = {
|
|
33
|
+
"zai-org/GLM-5.2-FP8": "glm-5.2",
|
|
34
|
+
"moonshotai/Kimi-K2.7-Code": "kimi-k2.7-code",
|
|
35
|
+
"Qwen/Qwen3.6-35B-A3B": "qwen3.6-35b",
|
|
36
|
+
"deepseek-ai/DeepSeek-V4-Flash": "deepseek-v4-flash",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function isFlexModelId(id: string): boolean {
|
|
40
|
+
return id.endsWith("-flex");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isVariantId(id: string): boolean {
|
|
44
|
+
return id.includes("-fast") || id.includes("-flex") || id.includes("-short");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function apiModelToProviderModel(model: NeuralwattApiModel): NeuralwattModel {
|
|
48
|
+
const meta = model.metadata;
|
|
49
|
+
if (!meta)
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Neuralwatt API returned model "${model.id}" without metadata`,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const reasoning = meta.capabilities.reasoning;
|
|
55
|
+
// Flex variants are billed at 0.65x when streaming (35% off).
|
|
56
|
+
const multiplier = isFlexModelId(model.id) ? FLEX_COST_MULTIPLIER : 1;
|
|
57
|
+
|
|
58
|
+
const compat: NonNullable<ProviderModelConfig["compat"]> = {
|
|
59
|
+
supportsDeveloperRole: meta.capabilities.developer_role,
|
|
60
|
+
maxTokensField: "max_tokens",
|
|
61
|
+
};
|
|
62
|
+
if (reasoning) compat.requiresReasoningContentOnAssistantMessages = true;
|
|
63
|
+
Object.assign(compat, COMPAT_OVERRIDES[model.id]);
|
|
64
|
+
|
|
65
|
+
const contextWindow =
|
|
66
|
+
CONTEXT_WINDOW_OVERRIDES.get(model.id) ?? model.max_model_len;
|
|
67
|
+
|
|
68
|
+
const result: NeuralwattModel = {
|
|
69
|
+
id: model.id,
|
|
70
|
+
name: meta.display_name ?? model.id,
|
|
71
|
+
reasoning,
|
|
72
|
+
input: meta.capabilities.vision
|
|
73
|
+
? (["text", "image"] as const)
|
|
74
|
+
: (["text"] as const),
|
|
75
|
+
cost: {
|
|
76
|
+
input: meta.pricing.input_per_million * multiplier,
|
|
77
|
+
output: meta.pricing.output_per_million * multiplier,
|
|
78
|
+
cacheRead: (meta.pricing.cached_input_per_million ?? 0) * multiplier,
|
|
79
|
+
cacheWrite: (meta.pricing.cached_output_per_million ?? 0) * multiplier,
|
|
80
|
+
},
|
|
81
|
+
contextWindow,
|
|
82
|
+
maxTokens: resolveMaxTokens(meta.limits.max_output_tokens, contextWindow),
|
|
83
|
+
compat,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (reasoning) {
|
|
87
|
+
result.thinkingLevelMap = buildThinkingLevelMap(
|
|
88
|
+
meta.reasoning,
|
|
89
|
+
) as ThinkingLevelMap;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function buildAliases(
|
|
96
|
+
models: NeuralwattModel[],
|
|
97
|
+
apiModels: readonly NeuralwattApiModel[],
|
|
98
|
+
): NeuralwattModel[] {
|
|
99
|
+
const existingIds = new Set(models.map((m) => m.id));
|
|
100
|
+
const aliases: NeuralwattModel[] = [];
|
|
101
|
+
const seen = new Set<string>();
|
|
102
|
+
|
|
103
|
+
const addAlias = (aliasId: string, canonicalId: string): void => {
|
|
104
|
+
if (seen.has(aliasId) || existingIds.has(aliasId)) return;
|
|
105
|
+
const canonical = models.find((m) => m.id === canonicalId);
|
|
106
|
+
if (!canonical) return;
|
|
107
|
+
seen.add(aliasId);
|
|
108
|
+
aliases.push({
|
|
109
|
+
...canonical,
|
|
110
|
+
id: aliasId,
|
|
111
|
+
name: `${canonical.name} (alias ID)`,
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
for (const [aliasId, canonicalId] of Object.entries(HARDCODED_ALIASES)) {
|
|
116
|
+
addAlias(aliasId, canonicalId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
for (const apiModel of apiModels) {
|
|
120
|
+
const hfId = apiModel.metadata?.huggingface_id;
|
|
121
|
+
if (!hfId || hfId === apiModel.id || isVariantId(apiModel.id)) continue;
|
|
122
|
+
addAlias(hfId, apiModel.id);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return aliases;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function buildNeuralwattProviderModels(): NeuralwattModel[] {
|
|
129
|
+
return NEURALWATT_MODELS.map((model) => ({ ...model }));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildNeuralwattProviderModelsFromApi(
|
|
133
|
+
apiModels: readonly NeuralwattApiModel[],
|
|
134
|
+
): NeuralwattModel[] {
|
|
135
|
+
const models = apiModels
|
|
136
|
+
.filter(
|
|
137
|
+
(m) =>
|
|
138
|
+
m.metadata && !m.metadata.deprecated && !m.metadata.pricing.pricing_tbd,
|
|
139
|
+
)
|
|
140
|
+
.map(apiModelToProviderModel);
|
|
141
|
+
return [...models, ...buildAliases(models, apiModels)];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function buildNeuralwattProviderModelsFromStore(
|
|
145
|
+
storedModels: readonly NeuralwattModel[],
|
|
146
|
+
): NeuralwattModel[] {
|
|
147
|
+
return storedModels.map((model) => ({ ...model }));
|
|
148
|
+
}
|
|
@@ -1,39 +1,12 @@
|
|
|
1
|
-
import type { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { buildAliasNeuralwattModels } from "./aliases";
|
|
3
|
-
import { buildLegacyNeuralwattModels } from "./legacy";
|
|
4
|
-
import { NEURALWATT_MODELS } from "./public-models";
|
|
5
|
-
|
|
6
1
|
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
EARLY_ACCESS_NEURALWATT_MODELS,
|
|
14
|
-
loadEarlyAccessModels,
|
|
15
|
-
} from "./early-access";
|
|
16
|
-
export {
|
|
17
|
-
buildLegacyNeuralwattModels,
|
|
18
|
-
LEGACY_MODEL_ALIAS_MAP,
|
|
19
|
-
LEGACY_NEURALWATT_MODEL_IDS,
|
|
20
|
-
} from "./legacy";
|
|
2
|
+
buildNeuralwattProviderModels,
|
|
3
|
+
buildNeuralwattProviderModelsFromApi,
|
|
4
|
+
buildNeuralwattProviderModelsFromStore,
|
|
5
|
+
type NeuralwattModel,
|
|
6
|
+
} from "./catalog";
|
|
21
7
|
export { NEURALWATT_MODELS } from "./public-models";
|
|
22
|
-
export {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}): ProviderModelConfig[] {
|
|
28
|
-
const models: ProviderModelConfig[] = [...NEURALWATT_MODELS];
|
|
29
|
-
|
|
30
|
-
if (options?.includeLegacyModelIds) {
|
|
31
|
-
models.push(...buildLegacyNeuralwattModels());
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (options?.includeAliasedModelIds) {
|
|
35
|
-
models.push(...buildAliasNeuralwattModels());
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return models;
|
|
39
|
-
}
|
|
8
|
+
export {
|
|
9
|
+
createNeuralwattRefreshModels,
|
|
10
|
+
type FetchNeuralwattApiModels,
|
|
11
|
+
MODEL_STORE_TTL_MS,
|
|
12
|
+
} from "./refresh";
|
|
@@ -1,191 +1,73 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
Api,
|
|
3
|
-
Model,
|
|
4
2
|
ModelsStoreEntry,
|
|
5
3
|
RefreshModelsContext,
|
|
6
4
|
} from "@earendil-works/pi-ai";
|
|
7
|
-
import type {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
includeEarlyAccessModels: boolean;
|
|
29
|
-
loadEarlyAccess?: typeof loadEarlyAccessModels;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function configuredModels(
|
|
33
|
-
includeLegacyModelIds: boolean,
|
|
34
|
-
includeAliasedModelIds: boolean,
|
|
35
|
-
extraCanonicalModels: ProviderModelConfig[] = [],
|
|
36
|
-
): ProviderModelConfig[] {
|
|
37
|
-
const canonicalModels = [...NEURALWATT_MODELS, ...extraCanonicalModels];
|
|
38
|
-
const models: ProviderModelConfig[] = [...canonicalModels];
|
|
39
|
-
|
|
40
|
-
if (includeLegacyModelIds) {
|
|
41
|
-
models.push(...buildLegacyNeuralwattModels());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (includeAliasedModelIds) {
|
|
45
|
-
models.push(...buildAliasNeuralwattModels(canonicalModels));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return models;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function toStoredModel(model: ProviderModelConfig): Model<Api> {
|
|
52
|
-
return {
|
|
53
|
-
...model,
|
|
54
|
-
provider: PROVIDER_ID,
|
|
55
|
-
api: model.api ?? API,
|
|
56
|
-
baseUrl: model.baseUrl ?? BASE_URL,
|
|
57
|
-
};
|
|
5
|
+
import type { NeuralwattApiModel } from "../../../src/types/models-api";
|
|
6
|
+
import type {
|
|
7
|
+
buildNeuralwattProviderModels,
|
|
8
|
+
buildNeuralwattProviderModelsFromApi,
|
|
9
|
+
buildNeuralwattProviderModelsFromStore,
|
|
10
|
+
NeuralwattModel,
|
|
11
|
+
} from "./catalog";
|
|
12
|
+
|
|
13
|
+
export const MODEL_STORE_TTL_MS = 4 * 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
export type FetchNeuralwattApiModels = (
|
|
16
|
+
apiKey: string | undefined,
|
|
17
|
+
signal?: AbortSignal,
|
|
18
|
+
) => Promise<readonly NeuralwattApiModel[]>;
|
|
19
|
+
|
|
20
|
+
function isFreshStoreEntry(
|
|
21
|
+
entry: Readonly<ModelsStoreEntry> | undefined,
|
|
22
|
+
): entry is ModelsStoreEntry {
|
|
23
|
+
if (!entry) return false;
|
|
24
|
+
const checkedAt = entry.checkedAt ?? Date.now();
|
|
25
|
+
return Date.now() - checkedAt < MODEL_STORE_TTL_MS;
|
|
58
26
|
}
|
|
59
27
|
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
28
|
+
export function createNeuralwattRefreshModels(
|
|
29
|
+
staticModels: ReturnType<typeof buildNeuralwattProviderModels>,
|
|
30
|
+
fetchApiModels: FetchNeuralwattApiModels,
|
|
31
|
+
buildFromApi: typeof buildNeuralwattProviderModelsFromApi,
|
|
32
|
+
buildFromStore: typeof buildNeuralwattProviderModelsFromStore,
|
|
33
|
+
) {
|
|
34
|
+
return async (context: RefreshModelsContext): Promise<NeuralwattModel[]> => {
|
|
35
|
+
context.signal.throwIfAborted();
|
|
36
|
+
const fallback = buildFromStore(staticModels);
|
|
37
|
+
try {
|
|
38
|
+
if (!context.allowNetwork) {
|
|
39
|
+
return context.stored
|
|
40
|
+
? buildFromStore(context.stored.models)
|
|
41
|
+
: fallback;
|
|
42
|
+
}
|
|
43
|
+
if (!context.force && isFreshStoreEntry(context.stored)) {
|
|
44
|
+
return buildFromStore(context.stored.models);
|
|
45
|
+
}
|
|
46
|
+
const apiKey =
|
|
47
|
+
context.credential?.type === "api_key"
|
|
48
|
+
? context.credential.key
|
|
49
|
+
: undefined;
|
|
50
|
+
const apiModels = await fetchApiModels(apiKey, context.signal);
|
|
51
|
+
context.signal.throwIfAborted();
|
|
52
|
+
const models = buildFromApi(apiModels);
|
|
53
|
+
await context
|
|
54
|
+
.publish({
|
|
55
|
+
persist: {
|
|
56
|
+
models: models as unknown as ModelsStoreEntry["models"],
|
|
57
|
+
checkedAt: Date.now(),
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
.catch(() => undefined);
|
|
61
|
+
context.signal.throwIfAborted();
|
|
62
|
+
return models;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if (
|
|
65
|
+
context.signal.aborted ||
|
|
66
|
+
(error instanceof Error && error.name === "AbortError")
|
|
67
|
+
) {
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
return fallback;
|
|
71
|
+
}
|
|
80
72
|
};
|
|
81
73
|
}
|
|
82
|
-
|
|
83
|
-
function dedupeEarlyAccessModels(
|
|
84
|
-
earlyAccessModels: ProviderModelConfig[],
|
|
85
|
-
baselineModels: ProviderModelConfig[],
|
|
86
|
-
): ProviderModelConfig[] {
|
|
87
|
-
const baselineIds = new Set(baselineModels.map((model) => model.id));
|
|
88
|
-
return earlyAccessModels.filter((model) => !baselineIds.has(model.id));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function configuredEarlyAccessModels(
|
|
92
|
-
discoveredModels: ProviderModelConfig[],
|
|
93
|
-
baselineModels: ProviderModelConfig[],
|
|
94
|
-
): ProviderModelConfig[] {
|
|
95
|
-
const hardcodedIds = new Set(
|
|
96
|
-
EARLY_ACCESS_NEURALWATT_MODELS.map((model) => model.id),
|
|
97
|
-
);
|
|
98
|
-
return dedupeEarlyAccessModels(
|
|
99
|
-
[
|
|
100
|
-
...EARLY_ACCESS_NEURALWATT_MODELS,
|
|
101
|
-
...discoveredModels.filter((model) => !hardcodedIds.has(model.id)),
|
|
102
|
-
],
|
|
103
|
-
baselineModels,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function cachedEarlyAccessModels(
|
|
108
|
-
stored: ModelsStoreEntry | undefined,
|
|
109
|
-
): ProviderModelConfig[] {
|
|
110
|
-
if (!stored) return [];
|
|
111
|
-
|
|
112
|
-
const allStaticIds = new Set(
|
|
113
|
-
configuredModels(true, true).map((model) => model.id),
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
return stored.models
|
|
117
|
-
.filter(
|
|
118
|
-
(model) =>
|
|
119
|
-
model.provider === PROVIDER_ID &&
|
|
120
|
-
!allStaticIds.has(model.id) &&
|
|
121
|
-
!ALIAS_NEURALWATT_MODEL_IDS.has(model.id),
|
|
122
|
-
)
|
|
123
|
-
.map(toProviderModel);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function persistCatalog(
|
|
127
|
-
context: RefreshModelsContext,
|
|
128
|
-
models: ProviderModelConfig[],
|
|
129
|
-
): Promise<boolean> {
|
|
130
|
-
return context.publish({
|
|
131
|
-
persist: {
|
|
132
|
-
models: models.map(toStoredModel),
|
|
133
|
-
checkedAt: Date.now(),
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Refresh the complete Neuralwatt catalog; undefined = failed (stale store kept). */
|
|
139
|
-
export async function refreshNeuralwattModels(
|
|
140
|
-
context: RefreshModelsContext,
|
|
141
|
-
options: RefreshNeuralwattModelsOptions,
|
|
142
|
-
): Promise<ProviderModelConfig[] | undefined> {
|
|
143
|
-
const baseline = configuredModels(
|
|
144
|
-
options.includeLegacyModelIds,
|
|
145
|
-
options.includeAliasedModelIds,
|
|
146
|
-
);
|
|
147
|
-
const stored = context.stored;
|
|
148
|
-
|
|
149
|
-
if (!options.includeEarlyAccessModels) {
|
|
150
|
-
await persistCatalog(context, baseline).catch(() => false);
|
|
151
|
-
return baseline;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const cachedEarlyAccess = configuredEarlyAccessModels(
|
|
155
|
-
cachedEarlyAccessModels(stored),
|
|
156
|
-
baseline,
|
|
157
|
-
);
|
|
158
|
-
const cachedCatalog = configuredModels(
|
|
159
|
-
options.includeLegacyModelIds,
|
|
160
|
-
options.includeAliasedModelIds,
|
|
161
|
-
cachedEarlyAccess,
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
if (!context.allowNetwork || context.signal.aborted) {
|
|
165
|
-
return cachedCatalog;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Anonymous credential (empty or missing key): keep the public catalog and
|
|
169
|
-
// skip discovery, which requires a real key.
|
|
170
|
-
const apiKey =
|
|
171
|
-
context.credential?.type === "api_key" && context.credential.key
|
|
172
|
-
? context.credential.key
|
|
173
|
-
: undefined;
|
|
174
|
-
if (!apiKey) return cachedCatalog;
|
|
175
|
-
|
|
176
|
-
const earlyAccess = await (options.loadEarlyAccess ?? loadEarlyAccessModels)(
|
|
177
|
-
apiKey,
|
|
178
|
-
context.signal,
|
|
179
|
-
);
|
|
180
|
-
if (context.signal.aborted) return cachedCatalog;
|
|
181
|
-
if (!earlyAccess) return undefined;
|
|
182
|
-
|
|
183
|
-
const catalog = configuredModels(
|
|
184
|
-
options.includeLegacyModelIds,
|
|
185
|
-
options.includeAliasedModelIds,
|
|
186
|
-
configuredEarlyAccessModels(earlyAccess, baseline),
|
|
187
|
-
);
|
|
188
|
-
context.signal.throwIfAborted();
|
|
189
|
-
await persistCatalog(context, catalog).catch(() => false);
|
|
190
|
-
return catalog;
|
|
191
|
-
}
|