@aliou/pi-ts-aperture 0.3.1 → 0.4.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/package.json +9 -8
- package/src/commands/settings.ts +36 -0
- package/src/commands/setup.ts +144 -237
- package/src/config.ts +3 -0
- package/src/core/index.ts +7 -0
- package/src/core/plan.test.ts +253 -0
- package/src/core/plan.ts +107 -0
- package/src/core/types.ts +33 -0
- package/src/core/url.test.ts +130 -0
- package/src/core/url.ts +42 -0
- package/src/index.ts +88 -14
- package/src/lib/health.ts +15 -0
- package/src/providers/aperture.ts +40 -128
- package/src/lib/aperture-api.ts +0 -32
- package/src/providers/model-config.ts +0 -54
- package/src/state/provider-model-cache.ts +0 -18
package/src/index.ts
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Keeps the entry point focused on orchestration:
|
|
5
5
|
* - load config
|
|
6
|
-
* - bootstrap provider/model visibility
|
|
7
6
|
* - register lifecycle hooks
|
|
8
7
|
* - register user commands
|
|
9
8
|
*/
|
|
@@ -15,40 +14,114 @@ import type {
|
|
|
15
14
|
import { registerApertureSettings } from "./commands/settings";
|
|
16
15
|
import { registerSetupCommand } from "./commands/setup";
|
|
17
16
|
import { configLoader } from "./config";
|
|
17
|
+
import { planConfigChange, resolveProviderBaseUrl } from "./core";
|
|
18
18
|
import {
|
|
19
19
|
applyAperture,
|
|
20
|
-
|
|
20
|
+
checkGatewayModels,
|
|
21
21
|
refreshActiveModel,
|
|
22
|
-
resetApertureModelsCache,
|
|
23
22
|
} from "./providers/aperture";
|
|
24
23
|
|
|
25
|
-
function
|
|
24
|
+
function notifyMissingModelsOnce(
|
|
25
|
+
ctx: ExtensionContext,
|
|
26
|
+
missingModels: string[],
|
|
27
|
+
warnedModels: Set<string>,
|
|
28
|
+
): void {
|
|
29
|
+
const newMissing = missingModels.filter((id) => !warnedModels.has(id));
|
|
30
|
+
if (newMissing.length > 0) {
|
|
31
|
+
for (const id of newMissing) warnedModels.add(id);
|
|
32
|
+
ctx.ui.notify(
|
|
33
|
+
`[aperture] models not available on gateway: ${newMissing.join(", ")}. Add them to the gateway configuration.`,
|
|
34
|
+
"warning",
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function registerApertureLifecycleHook(
|
|
40
|
+
pi: ExtensionAPI,
|
|
41
|
+
warnedModels: Set<string>,
|
|
42
|
+
): void {
|
|
26
43
|
pi.on("before_agent_start", async (_event, ctx) => {
|
|
27
44
|
if (!ctx?.modelRegistry) return;
|
|
28
45
|
|
|
29
|
-
const overriddenProviders = await applyAperture(
|
|
46
|
+
const { providers: overriddenProviders, gatewayUrl } = await applyAperture(
|
|
47
|
+
pi,
|
|
48
|
+
ctx.modelRegistry,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
ctx.model &&
|
|
53
|
+
overriddenProviders.includes(ctx.model.provider) &&
|
|
54
|
+
gatewayUrl !== null &&
|
|
55
|
+
configLoader.getConfig().checkGatewayModels.includes(ctx.model.provider)
|
|
56
|
+
) {
|
|
57
|
+
const { missingModels } = await checkGatewayModels(
|
|
58
|
+
gatewayUrl,
|
|
59
|
+
ctx.modelRegistry,
|
|
60
|
+
);
|
|
61
|
+
notifyMissingModelsOnce(ctx, missingModels, warnedModels);
|
|
62
|
+
}
|
|
63
|
+
|
|
30
64
|
if (!ctx.model || !overriddenProviders.includes(ctx.model.provider)) return;
|
|
31
65
|
|
|
32
66
|
await refreshActiveModel(pi, ctx);
|
|
33
67
|
});
|
|
68
|
+
|
|
69
|
+
// Also check when user switches to a model whose provider uses aperture
|
|
70
|
+
pi.on("model_select", async (_event, ctx) => {
|
|
71
|
+
if (!ctx?.model) return;
|
|
72
|
+
|
|
73
|
+
const config = configLoader.getConfig();
|
|
74
|
+
if (!config.providers.includes(ctx.model.provider)) return;
|
|
75
|
+
|
|
76
|
+
const gatewayUrl = resolveProviderBaseUrl(config)?.replace("/v1", "");
|
|
77
|
+
if (!gatewayUrl) return;
|
|
78
|
+
|
|
79
|
+
if (config.checkGatewayModels.includes(ctx.model.provider)) {
|
|
80
|
+
const { missingModels } = await checkGatewayModels(
|
|
81
|
+
gatewayUrl,
|
|
82
|
+
ctx.modelRegistry,
|
|
83
|
+
);
|
|
84
|
+
notifyMissingModelsOnce(ctx, missingModels, warnedModels);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
34
87
|
}
|
|
35
88
|
|
|
36
89
|
function createConfigChangeHandler(
|
|
37
90
|
pi: ExtensionAPI,
|
|
91
|
+
warnedModels: Set<string>,
|
|
38
92
|
): (ctx: ExtensionContext) => void {
|
|
39
93
|
let lastRegisteredProviders = [...configLoader.getConfig().providers];
|
|
40
94
|
|
|
41
95
|
return (ctx: ExtensionContext) => {
|
|
42
96
|
const { providers } = configLoader.getConfig();
|
|
43
|
-
|
|
44
|
-
|
|
97
|
+
|
|
98
|
+
const plan = planConfigChange(
|
|
99
|
+
lastRegisteredProviders,
|
|
100
|
+
providers,
|
|
101
|
+
ctx.model?.provider,
|
|
45
102
|
);
|
|
46
103
|
|
|
47
|
-
|
|
48
|
-
|
|
104
|
+
void applyAperture(pi, ctx.modelRegistry).then(
|
|
105
|
+
async ({ providers, gatewayUrl }) => {
|
|
106
|
+
if (
|
|
107
|
+
ctx.model &&
|
|
108
|
+
providers.includes(ctx.model.provider) &&
|
|
109
|
+
gatewayUrl !== null &&
|
|
110
|
+
configLoader
|
|
111
|
+
.getConfig()
|
|
112
|
+
.checkGatewayModels.includes(ctx.model.provider)
|
|
113
|
+
) {
|
|
114
|
+
const { missingModels } = await checkGatewayModels(
|
|
115
|
+
gatewayUrl,
|
|
116
|
+
ctx.modelRegistry,
|
|
117
|
+
);
|
|
118
|
+
notifyMissingModelsOnce(ctx, missingModels, warnedModels);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
);
|
|
49
122
|
lastRegisteredProviders = [...providers];
|
|
50
123
|
|
|
51
|
-
if (
|
|
124
|
+
if (plan.shouldRefreshModel) {
|
|
52
125
|
void refreshActiveModel(pi, ctx).then((updated) => {
|
|
53
126
|
if (!updated) return;
|
|
54
127
|
ctx.ui.notify(
|
|
@@ -58,7 +131,7 @@ function createConfigChangeHandler(
|
|
|
58
131
|
});
|
|
59
132
|
}
|
|
60
133
|
|
|
61
|
-
for (const provider of removedProviders) {
|
|
134
|
+
for (const provider of plan.removedProviders) {
|
|
62
135
|
pi.unregisterProvider(provider);
|
|
63
136
|
}
|
|
64
137
|
};
|
|
@@ -66,11 +139,12 @@ function createConfigChangeHandler(
|
|
|
66
139
|
|
|
67
140
|
export default async function (pi: ExtensionAPI): Promise<void> {
|
|
68
141
|
await configLoader.load();
|
|
69
|
-
await bootstrapProvidersFromAperture(pi);
|
|
70
142
|
|
|
71
|
-
|
|
143
|
+
const warnedModels = new Set<string>();
|
|
144
|
+
|
|
145
|
+
registerApertureLifecycleHook(pi, warnedModels);
|
|
72
146
|
|
|
73
|
-
const onConfigChange = createConfigChangeHandler(pi);
|
|
147
|
+
const onConfigChange = createConfigChangeHandler(pi, warnedModels);
|
|
74
148
|
registerSetupCommand(pi, onConfigChange);
|
|
75
149
|
registerApertureSettings(pi, onConfigChange);
|
|
76
150
|
}
|
package/src/lib/health.ts
CHANGED
|
@@ -28,3 +28,18 @@ export async function checkApertureHealth(
|
|
|
28
28
|
return { ok: false, error: msg };
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
|
|
32
|
+
export async function fetchGatewayModelIds(baseUrl: string): Promise<string[]> {
|
|
33
|
+
const url = `${baseUrl.replace(/\/+$/, "")}/v1/models`;
|
|
34
|
+
try {
|
|
35
|
+
const res = await fetch(url, {
|
|
36
|
+
method: "GET",
|
|
37
|
+
signal: AbortSignal.timeout(5000),
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) return [];
|
|
40
|
+
const body = (await res.json()) as { data?: { id: string }[] };
|
|
41
|
+
return body.data?.map((m) => m.id) ?? [];
|
|
42
|
+
} catch {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -1,156 +1,68 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ExtensionAPI,
|
|
3
3
|
ExtensionContext,
|
|
4
|
-
ProviderModelConfig,
|
|
5
4
|
} from "@mariozechner/pi-coding-agent";
|
|
6
5
|
import { configLoader } from "../config";
|
|
7
|
-
import { fetchApertureProviderModels } from "../lib/aperture-api";
|
|
8
6
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} from "../
|
|
13
|
-
import {
|
|
7
|
+
buildApplyPlan,
|
|
8
|
+
resolveGatewayUrl,
|
|
9
|
+
resolveProviderBaseUrl,
|
|
10
|
+
} from "../core";
|
|
11
|
+
import { fetchGatewayModelIds } from "../lib/health";
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
* Preserve provenance similarly to pi-synthetic so downstream providers can
|
|
17
|
-
* attribute traffic to Pi / this extension.
|
|
18
|
-
*/
|
|
19
|
-
const APERTURE_PROVENANCE_HEADERS = {
|
|
20
|
-
Referer: "https://pi.dev",
|
|
21
|
-
"X-Title": "npm:@aliou/pi-ts-aperture",
|
|
22
|
-
};
|
|
13
|
+
export { resolveGatewayUrl } from "../core";
|
|
23
14
|
|
|
24
15
|
/**
|
|
25
|
-
*
|
|
26
|
-
* to make CLI model selection deterministic.
|
|
27
|
-
*/
|
|
28
|
-
const BOOTSTRAP_DISCOVERY_PROVIDERS = new Set(["openrouter"]);
|
|
29
|
-
|
|
30
|
-
/** Returns configured gateway URL without trailing slash. */
|
|
31
|
-
export function resolveGatewayUrl(): string | null {
|
|
32
|
-
const { baseUrl, providers } = configLoader.getConfig();
|
|
33
|
-
if (!baseUrl || providers.length === 0) return null;
|
|
34
|
-
return baseUrl.replace(/\/+$/, "");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Returns the Aperture provider base URL used for provider registration.
|
|
16
|
+
* Apply Aperture override to configured providers.
|
|
39
17
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const gateway = resolveGatewayUrl();
|
|
46
|
-
if (!gateway) return null;
|
|
47
|
-
return `${gateway}/v1`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function resolveProviderHeaders(
|
|
51
|
-
models: ProviderModelConfig[],
|
|
52
|
-
): Record<string, string> {
|
|
53
|
-
const modelHeaders = models.find((m) => m.headers)?.headers ?? {};
|
|
54
|
-
return {
|
|
55
|
-
...APERTURE_PROVENANCE_HEADERS,
|
|
56
|
-
...modelHeaders,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function getOrLoadProviderModelsCache(
|
|
61
|
-
gatewayUrl: string,
|
|
62
|
-
providers: string[],
|
|
63
|
-
): Promise<Map<string, string[]>> {
|
|
64
|
-
const current = getProviderModelsCache();
|
|
65
|
-
if (current) return current;
|
|
66
|
-
|
|
67
|
-
const loaded = await fetchApertureProviderModels(gatewayUrl, providers);
|
|
68
|
-
setProviderModelsCache(loaded);
|
|
69
|
-
return loaded;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function resetApertureModelsCache(): void {
|
|
73
|
-
clearProviderModelsCache();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Apply Aperture override to configured providers:
|
|
78
|
-
* - provider baseUrl -> aperture /v1 endpoint
|
|
79
|
-
* - apiKey -> dummy token (Aperture injects real key server-side)
|
|
80
|
-
* - headers -> provenance + provider/model headers
|
|
18
|
+
* Only patches baseUrl, apiKey, and headers. Models are left exactly as
|
|
19
|
+
* registered by Pi built-ins or other extensions -- Aperture never touches
|
|
20
|
+
* model definitions.
|
|
21
|
+
*
|
|
22
|
+
* Providers with no models in the registry are skipped (nothing to reroute).
|
|
81
23
|
*/
|
|
82
24
|
export async function applyAperture(
|
|
83
25
|
pi: ExtensionAPI,
|
|
84
26
|
registry: ExtensionContext["modelRegistry"],
|
|
85
|
-
): Promise<string[]> {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
if (!baseUrl
|
|
27
|
+
): Promise<{ providers: string[]; gatewayUrl: string | null }> {
|
|
28
|
+
const config = configLoader.getConfig();
|
|
29
|
+
const baseUrl = resolveProviderBaseUrl(config);
|
|
30
|
+
if (!baseUrl) return { providers: [], gatewayUrl: null };
|
|
89
31
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
let modelCache: Map<string, string[]>;
|
|
93
|
-
try {
|
|
94
|
-
modelCache = await getOrLoadProviderModelsCache(gatewayUrl, providers);
|
|
95
|
-
} catch {
|
|
96
|
-
modelCache = new Map();
|
|
97
|
-
}
|
|
32
|
+
const gatewayUrl = resolveGatewayUrl(config);
|
|
98
33
|
|
|
99
|
-
|
|
100
|
-
const existingModels = registry
|
|
101
|
-
.getAll()
|
|
102
|
-
.filter((m) => m.provider === provider) as ProviderModelConfig[];
|
|
34
|
+
const registryModels = registry.getAll();
|
|
103
35
|
|
|
104
|
-
|
|
36
|
+
const plan = buildApplyPlan(config, registryModels, baseUrl, []);
|
|
105
37
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
38
|
+
for (const reg of plan.registrations) {
|
|
39
|
+
pi.registerProvider(reg.provider, {
|
|
40
|
+
baseUrl: reg.baseUrl,
|
|
41
|
+
apiKey: reg.apiKey,
|
|
42
|
+
headers: reg.headers,
|
|
43
|
+
api: reg.api,
|
|
44
|
+
models: reg.models,
|
|
111
45
|
});
|
|
112
46
|
}
|
|
113
47
|
|
|
114
|
-
return providers;
|
|
48
|
+
return { providers: config.providers, gatewayUrl };
|
|
115
49
|
}
|
|
116
50
|
|
|
117
51
|
/**
|
|
118
|
-
*
|
|
119
|
-
* resolution works even when a model is not present in Pi built-ins.
|
|
52
|
+
* Fetch gateway models and return missing ones relative to the plan.
|
|
120
53
|
*/
|
|
121
|
-
export async function
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
setProviderModelsCache(modelCache);
|
|
134
|
-
} catch {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
for (const provider of providers) {
|
|
139
|
-
if (!BOOTSTRAP_DISCOVERY_PROVIDERS.has(provider)) continue;
|
|
140
|
-
|
|
141
|
-
const modelIds = modelCache.get(provider) ?? [];
|
|
142
|
-
if (modelIds.length === 0) continue;
|
|
143
|
-
|
|
144
|
-
const models = modelIds.map((id) => toModelConfig(id));
|
|
145
|
-
|
|
146
|
-
pi.registerProvider(provider, {
|
|
147
|
-
baseUrl,
|
|
148
|
-
apiKey: "-",
|
|
149
|
-
api: "openai-completions",
|
|
150
|
-
headers: resolveProviderHeaders(models),
|
|
151
|
-
models,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
54
|
+
export async function checkGatewayModels(
|
|
55
|
+
gatewayUrl: string,
|
|
56
|
+
registry: ExtensionContext["modelRegistry"],
|
|
57
|
+
): Promise<{ missingModels: string[] }> {
|
|
58
|
+
const config = configLoader.getConfig();
|
|
59
|
+
const baseUrl = resolveProviderBaseUrl(config);
|
|
60
|
+
if (!baseUrl) return { missingModels: [] };
|
|
61
|
+
|
|
62
|
+
const gatewayModelIds = await fetchGatewayModelIds(gatewayUrl);
|
|
63
|
+
const registryModels = registry.getAll();
|
|
64
|
+
const plan = buildApplyPlan(config, registryModels, baseUrl, gatewayModelIds);
|
|
65
|
+
return { missingModels: plan.missingModels };
|
|
154
66
|
}
|
|
155
67
|
|
|
156
68
|
/** Re-resolve and set current model after provider registry updates. */
|
package/src/lib/aperture-api.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export interface ApertureProviderInfo {
|
|
2
|
-
id?: string;
|
|
3
|
-
models?: string[];
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Fetch provider -> model list mapping from Aperture and keep only selected
|
|
8
|
-
* providers configured by the user.
|
|
9
|
-
*/
|
|
10
|
-
export async function fetchApertureProviderModels(
|
|
11
|
-
gatewayUrl: string,
|
|
12
|
-
providers: string[],
|
|
13
|
-
): Promise<Map<string, string[]>> {
|
|
14
|
-
const response = await fetch(`${gatewayUrl}/api/providers`, {
|
|
15
|
-
signal: AbortSignal.timeout(4000),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
if (!response.ok) {
|
|
19
|
-
return new Map();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const data = (await response.json()) as ApertureProviderInfo[];
|
|
23
|
-
const selectedProviders = new Set(providers);
|
|
24
|
-
const modelsByProvider = new Map<string, string[]>();
|
|
25
|
-
|
|
26
|
-
for (const provider of data) {
|
|
27
|
-
if (!provider.id || !selectedProviders.has(provider.id)) continue;
|
|
28
|
-
modelsByProvider.set(provider.id, provider.models ?? []);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return modelsByProvider;
|
|
32
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ProviderModelConfig } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
const DEFAULT_COST = {
|
|
4
|
-
input: 0,
|
|
5
|
-
output: 0,
|
|
6
|
-
cacheRead: 0,
|
|
7
|
-
cacheWrite: 0,
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Build a ProviderModelConfig for Aperture-discovered model IDs.
|
|
12
|
-
*
|
|
13
|
-
* When a template model is available (same provider), preserve its API/compat
|
|
14
|
-
* shape so behavior stays consistent after rerouting.
|
|
15
|
-
*/
|
|
16
|
-
export function toModelConfig(
|
|
17
|
-
id: string,
|
|
18
|
-
template?: ProviderModelConfig,
|
|
19
|
-
): ProviderModelConfig {
|
|
20
|
-
return {
|
|
21
|
-
id,
|
|
22
|
-
name: template?.name ?? id,
|
|
23
|
-
api: template?.api ?? "openai-completions",
|
|
24
|
-
reasoning: template?.reasoning ?? false,
|
|
25
|
-
input: template?.input ?? ["text"],
|
|
26
|
-
cost: template?.cost ?? DEFAULT_COST,
|
|
27
|
-
contextWindow: template?.contextWindow ?? 128000,
|
|
28
|
-
maxTokens: template?.maxTokens ?? 16384,
|
|
29
|
-
headers: template?.headers,
|
|
30
|
-
compat: template?.compat,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Merge known provider models with Aperture-discovered model IDs.
|
|
36
|
-
* Existing models win; missing IDs are synthesized from template defaults.
|
|
37
|
-
*/
|
|
38
|
-
export function mergeModels(
|
|
39
|
-
existingModels: ProviderModelConfig[],
|
|
40
|
-
apertureModelIds: string[] | undefined,
|
|
41
|
-
): ProviderModelConfig[] {
|
|
42
|
-
if (!apertureModelIds || apertureModelIds.length === 0) return existingModels;
|
|
43
|
-
|
|
44
|
-
const modelsById = new Map(existingModels.map((m) => [m.id, m]));
|
|
45
|
-
const template = existingModels[0];
|
|
46
|
-
|
|
47
|
-
for (const modelId of apertureModelIds) {
|
|
48
|
-
if (!modelsById.has(modelId)) {
|
|
49
|
-
modelsById.set(modelId, toModelConfig(modelId, template));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return [...modelsById.values()];
|
|
54
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory cache for Aperture provider model discovery.
|
|
3
|
-
*
|
|
4
|
-
* Ephemeral by design: reset on config changes and process restart.
|
|
5
|
-
*/
|
|
6
|
-
let providerModelsCache: Map<string, string[]> | null = null;
|
|
7
|
-
|
|
8
|
-
export function getProviderModelsCache(): Map<string, string[]> | null {
|
|
9
|
-
return providerModelsCache;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function setProviderModelsCache(models: Map<string, string[]>): void {
|
|
13
|
-
providerModelsCache = models;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function clearProviderModelsCache(): void {
|
|
17
|
-
providerModelsCache = null;
|
|
18
|
-
}
|