@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.
@@ -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 { fetchQuotas } from "../../src/lib/neuralwatt-api";
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 { getNeuralwattModels } from "./models";
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 { provider: providerConfig } = configLoader.getConfig();
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
- includeLegacyModelIds:
65
- configLoader.getConfig().provider.includeLegacyModelIds,
66
- includeAliasedModelIds:
67
- configLoader.getConfig().provider.includeAliasedModelIds,
68
- includeEarlyAccessModels:
69
- configLoader.getConfig().provider.includeEarlyAccessModels,
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
- ALIAS_MODEL_MAP,
8
- ALIAS_NEURALWATT_MODEL_IDS,
9
- buildAliasNeuralwattModels,
10
- } from "./aliases";
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 { refreshNeuralwattModels } from "./refresh";
23
-
24
- export function getNeuralwattModels(options?: {
25
- includeLegacyModelIds?: boolean;
26
- includeAliasedModelIds?: boolean;
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 { ProviderModelConfig } from "@earendil-works/pi-coding-agent";
8
- import {
9
- ALIAS_NEURALWATT_MODEL_IDS,
10
- buildAliasNeuralwattModels,
11
- } from "./aliases";
12
- import {
13
- EARLY_ACCESS_NEURALWATT_MODELS,
14
- loadEarlyAccessModels,
15
- } from "./early-access";
16
- import { buildLegacyNeuralwattModels } from "./legacy";
17
- import { NEURALWATT_MODELS } from "./public-models";
18
-
19
- const PROVIDER_ID = "neuralwatt";
20
- const BASE_URL = "https://api.neuralwatt.com/v1";
21
- const API = "openai-completions" as const;
22
-
23
- export type StoredProviderModels = readonly Model<Api>[];
24
-
25
- export interface RefreshNeuralwattModelsOptions {
26
- includeLegacyModelIds: boolean;
27
- includeAliasedModelIds: boolean;
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 toProviderModel(model: Model<Api>): ProviderModelConfig {
61
- return {
62
- id: model.id,
63
- name: model.name,
64
- api: model.api,
65
- baseUrl: model.baseUrl,
66
- reasoning: model.reasoning,
67
- thinkingLevelMap: model.thinkingLevelMap,
68
- input: [...model.input],
69
- cost: structuredClone(model.cost),
70
- contextWindow: model.contextWindow,
71
- maxTokens: model.maxTokens,
72
- headers: model.headers
73
- ? Object.fromEntries(
74
- Object.entries(model.headers).filter(
75
- (entry): entry is [string, string] => entry[1] !== null,
76
- ),
77
- )
78
- : undefined,
79
- compat: model.compat ? structuredClone(model.compat) : undefined,
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
- }