@averyyy/pi-coding-agent 0.99.1-piclient.1 → 1.0.0-piclient.2
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/CHANGELOG.md +94 -0
- package/dist/bundle/chunks/anthropic-messages-5Z4I6I54.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/{azure-openai-responses-YZ3Q2WVP.js → azure-openai-responses-TXH53UDQ.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/{chunk-7BINYQ2V.js → chunk-43NAB75E.js} +54 -49
- package/dist/bundle/chunks/{chunk-V7V43FNB.js → chunk-4ILNOXGR.js} +2 -2
- package/dist/bundle/chunks/chunk-AV6LYFGH.js +2 -0
- package/dist/bundle/chunks/{chunk-7JGR3GZN.js → chunk-F42GKQ7R.js} +1 -1
- package/dist/bundle/chunks/chunk-GIUN2MS4.js +16 -0
- package/dist/bundle/chunks/{chunk-W2DOMZSC.js → chunk-JTLRGL3E.js} +2 -2
- package/dist/bundle/chunks/{chunk-F6ORSP6L.js → chunk-KYK7L7MP.js} +18 -78
- package/dist/bundle/chunks/chunk-LD2P6SFZ.js +2 -0
- package/dist/bundle/chunks/chunk-LDPPAHPJ.js +4 -0
- package/dist/bundle/chunks/{chunk-3YXHSP6S.js → chunk-NEUJZQL3.js} +7 -9
- package/dist/bundle/chunks/chunk-OI6YFHHK.js +82 -0
- package/dist/bundle/chunks/{chunk-J4CWPC4N.js → chunk-PXGUCLIP.js} +1 -1
- package/dist/bundle/chunks/chunk-VIV3CWXG.js +2 -0
- package/dist/bundle/chunks/chunk-WGS2XTDX.js +2 -0
- package/dist/bundle/chunks/{chunk-FJTXMIZI.js → chunk-Y5O5BZGX.js} +15 -29
- package/dist/bundle/chunks/{cli-WF6HDHBO.js → cli-JE36AHMI.js} +8 -6
- package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-XJQQYGGU.js → cloudflare-workers-ai-system-one-TF36V7KG.js} +1 -1
- package/dist/bundle/chunks/codemode-worker.js +67 -8
- package/dist/bundle/chunks/execute-UX73LR4F.js +367 -0
- package/dist/bundle/chunks/{google-generative-ai-CQAKKJTA.js → google-generative-ai-XQU2LTWM.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-AE36YXJU.js → google-vertex-F6Q2CN55.js} +1 -1
- package/dist/bundle/chunks/{https-proxy-agent-2VXB7436.js → https-proxy-agent-UDEHOBGS.js} +1 -1
- package/dist/bundle/chunks/{llama-cpp-classify-PJJYKPLD.js → llama-cpp-classify-XT44PI3C.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-KEYTXJM7.js → mistral-conversations-XAJWHQFR.js} +1 -1
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-FUE7WESF.js → openai-codex-responses-3EHOMZYM.js} +1 -1
- package/dist/bundle/chunks/openai-codex.js +1 -1
- package/dist/bundle/chunks/{openai-completions-XW2Q5HVC.js → openai-completions-QC2IQHSC.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-ZUXFVYPI.js → openai-responses-JPLD2GJS.js} +1 -1
- package/dist/bundle/chunks/{openrouter-images-TPQ4V6OR.js → openrouter-images-ZVXWCSIM.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +1 -1
- package/dist/bundle/chunks/pi-logo-animation-APS4JS6B.js +2 -0
- package/dist/bundle/chunks/radius.js +1 -1
- package/dist/bundle/chunks/runtime-MPXPM7VL.js +2 -0
- package/dist/bundle/chunks/{typesafe-system-one-E7A4HNJI.js → typesafe-system-one-7DJTDUKY.js} +1 -1
- package/dist/bundle/chunks/virtual-modules-X5AB6TM6.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +5 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -13
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +15 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +80 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/cache-warmer.d.ts.map +1 -1
- package/dist/core/cache-warmer.js +11 -3
- package/dist/core/cache-warmer.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +13 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +6 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/mcp-servers.d.ts +34 -7
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js +52 -11
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/model-registry.d.ts +3 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +4 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +22 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +347 -49
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/pi-server-client.d.ts +21 -1
- package/dist/core/pi-server-client.d.ts.map +1 -1
- package/dist/core/pi-server-client.js +308 -22
- package/dist/core/pi-server-client.js.map +1 -1
- package/dist/core/pi-server-protocol.d.ts +12 -1
- package/dist/core/pi-server-protocol.d.ts.map +1 -1
- package/dist/core/pi-server-protocol.js +1 -0
- package/dist/core/pi-server-protocol.js.map +1 -1
- package/dist/core/radius.d.ts +2 -0
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/radius.js +2 -0
- package/dist/core/radius.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +4 -9
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +52 -18
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +7 -5
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +3 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts.map +1 -1
- package/dist/core/tools/renderers/bash.js +11 -31
- package/dist/core/tools/renderers/bash.js.map +1 -1
- package/dist/core/virtual-models.d.ts +2 -0
- package/dist/core/virtual-models.d.ts.map +1 -1
- package/dist/core/virtual-models.js +19 -12
- package/dist/core/virtual-models.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +197 -51
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/codemode/renderer.d.ts.map +1 -1
- package/dist/extensions/codemode/renderer.js +43 -25
- package/dist/extensions/codemode/renderer.js.map +1 -1
- package/dist/extensions/codemode/tool.d.ts +12 -11
- package/dist/extensions/codemode/tool.d.ts.map +1 -1
- package/dist/extensions/codemode/tool.js +73 -129
- package/dist/extensions/codemode/tool.js.map +1 -1
- package/dist/extensions/codemode/worker.d.ts +1 -1
- package/dist/extensions/codemode/worker.js +1 -1
- package/dist/extensions/codemode/worker.js.map +1 -1
- package/dist/extensions/mcp/cli.d.ts.map +1 -1
- package/dist/extensions/mcp/cli.js +20 -5
- package/dist/extensions/mcp/cli.js.map +1 -1
- package/dist/extensions/mcp/config.d.ts +4 -2
- package/dist/extensions/mcp/config.d.ts.map +1 -1
- package/dist/extensions/mcp/config.js +14 -2
- package/dist/extensions/mcp/config.js.map +1 -1
- package/dist/extensions/mcp/index.d.ts +32 -8
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +220 -56
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/oauth.d.ts +10 -7
- package/dist/extensions/mcp/oauth.d.ts.map +1 -1
- package/dist/extensions/mcp/oauth.js +56 -31
- package/dist/extensions/mcp/oauth.js.map +1 -1
- package/dist/extensions/mcp/runtime.d.ts +2 -0
- package/dist/extensions/mcp/runtime.d.ts.map +1 -1
- package/dist/extensions/mcp/runtime.js +16 -9
- package/dist/extensions/mcp/runtime.js.map +1 -1
- package/dist/extensions/mcp/tools.d.ts +5 -4
- package/dist/extensions/mcp/tools.d.ts.map +1 -1
- package/dist/extensions/mcp/tools.js +33 -15
- package/dist/extensions/mcp/tools.js.map +1 -1
- package/dist/extensions/mcp/ui.d.ts.map +1 -1
- package/dist/extensions/mcp/ui.js +4 -2
- package/dist/extensions/mcp/ui.js.map +1 -1
- package/dist/extensions/tool-search/tool.d.ts +5 -4
- package/dist/extensions/tool-search/tool.d.ts.map +1 -1
- package/dist/extensions/tool-search/tool.js +7 -28
- package/dist/extensions/tool-search/tool.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +24 -11
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +8 -2
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +24 -21
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/pi-logo-animation.d.ts +68 -0
- package/dist/modes/interactive/components/pi-logo-animation.d.ts.map +1 -0
- package/dist/modes/interactive/components/pi-logo-animation.js +1086 -0
- package/dist/modes/interactive/components/pi-logo-animation.js.map +1 -0
- package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts +7 -0
- package/dist/modes/interactive/components/pi-logo-animation.lazy.d.ts.map +1 -0
- package/dist/modes/interactive/components/pi-logo-animation.lazy.js +12 -0
- package/dist/modes/interactive/components/pi-logo-animation.lazy.js.map +1 -0
- package/dist/modes/interactive/components/pi-logo.d.ts +7 -0
- package/dist/modes/interactive/components/pi-logo.d.ts.map +1 -1
- package/dist/modes/interactive/components/pi-logo.js +13 -1
- package/dist/modes/interactive/components/pi-logo.js.map +1 -1
- package/dist/modes/interactive/components/radius-login-selector.d.ts +15 -0
- package/dist/modes/interactive/components/radius-login-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/radius-login-selector.js +85 -0
- package/dist/modes/interactive/components/radius-login-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +5 -5
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +5 -4
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts +27 -3
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js +39 -6
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +10 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +153 -40
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/system-theme.d.ts +2 -1
- package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/system-theme.js +16 -5
- package/dist/modes/interactive/theme/system-theme.js.map +1 -1
- package/docs/cli.md +8 -18
- package/docs/codemode.md +207 -0
- package/docs/docs.json +5 -1
- package/docs/environment-variables.md +2 -2
- package/docs/extensions.md +3 -3
- package/docs/index.md +1 -1
- package/docs/mcp.md +142 -82
- package/docs/models.md +22 -1
- package/docs/providers.md +18 -6
- package/docs/sdk.md +2 -2
- package/docs/settings.md +5 -3
- package/docs/usage.md +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +16 -40
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +24 -96
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/14-codemode-mcp.ts +2 -2
- package/package.json +10 -10
- package/dist/bundle/chunks/anthropic-messages-A6EXUBG6.js +0 -6
- package/dist/bundle/chunks/chunk-HSPCVFST.js +0 -2
- package/dist/bundle/chunks/chunk-KYJKN5BJ.js +0 -2
- package/dist/bundle/chunks/chunk-RO6I2QFQ.js +0 -4
- package/dist/bundle/chunks/chunk-WVKBMRQX.js +0 -82
- package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
- package/dist/bundle/chunks/execute-AVGAUHMT.js +0 -308
- package/dist/bundle/chunks/runtime-6ZC4MGWD.js +0 -2
- package/dist/bundle/chunks/virtual-modules-H7OCXN6W.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
|
-
import { clampThinkingLevel, createModels, lazyStream, ModelsError, normalizeContext, } from "@earendil-works/pi-ai";
|
|
2
|
+
import { clampThinkingLevel, createModels, isModelType, lazyStream, ModelsError, normalizeContext, } from "@earendil-works/pi-ai";
|
|
3
3
|
import * as builtinProviderCatalog from "@earendil-works/pi-ai/providers/all";
|
|
4
4
|
import { assertChatModel, assertClassifierModel, assertImageModel, classifierErrorResult, imageErrorResult, } from "@earendil-works/pi-ai/utils/model-operations";
|
|
5
5
|
import { getAgentDir } from "../config.js";
|
|
@@ -7,6 +7,7 @@ import { operationSignal, raceWithAbortSignal } from "../utils/abort.js";
|
|
|
7
7
|
import { AuthStorage as DefaultAuthStorage } from "./auth-storage.js";
|
|
8
8
|
import { ModelConfig } from "./model-config.js";
|
|
9
9
|
import { FileModelsStore, InMemoryCodingAgentModelsStore } from "./models-store.js";
|
|
10
|
+
import { cancelDeferredPiServer, classifyPiServer, fetchDeferredPiServer, fetchPiServerModels, generateImagesPiServer, streamPiServer, streamRawPiServer, } from "./pi-server-client.js";
|
|
10
11
|
import { composeModelProvider, configuredRequestAuthStatus, resolveCompatibilityRequestConfig, resolveConfiguredModelHeaders, validateExtensionProvider, } from "./provider-composer.js";
|
|
11
12
|
import { withRemoteCatalog } from "./remote-catalog-provider.js";
|
|
12
13
|
import { RuntimeCredentials } from "./runtime-credentials.js";
|
|
@@ -43,6 +44,7 @@ export class ModelRuntime {
|
|
|
43
44
|
models;
|
|
44
45
|
credentials;
|
|
45
46
|
defaultBuiltins;
|
|
47
|
+
nativeCatalogProviders;
|
|
46
48
|
builtins = new Map();
|
|
47
49
|
nativeExtensionProviders = new Map();
|
|
48
50
|
extensionProviders = new Map();
|
|
@@ -51,6 +53,19 @@ export class ModelRuntime {
|
|
|
51
53
|
compositionErrors = new Map();
|
|
52
54
|
modelsPath;
|
|
53
55
|
modelNetworkEnabled;
|
|
56
|
+
piServerMode = process.env.PI_SERVER_MODE === "true";
|
|
57
|
+
remoteCatalog = {
|
|
58
|
+
models: [],
|
|
59
|
+
available: [],
|
|
60
|
+
};
|
|
61
|
+
remoteProviderCapabilities = [];
|
|
62
|
+
remoteCatalogError;
|
|
63
|
+
registrationRefresh = Promise.resolve();
|
|
64
|
+
registrationRefreshErrors = new Map();
|
|
65
|
+
remoteCatalogRefreshSeq = 0;
|
|
66
|
+
remoteCatalogLoaded = false;
|
|
67
|
+
localOperationAvailable = [];
|
|
68
|
+
remoteProjection;
|
|
54
69
|
config;
|
|
55
70
|
snapshot = {
|
|
56
71
|
all: [],
|
|
@@ -64,11 +79,12 @@ export class ModelRuntime {
|
|
|
64
79
|
providerAvailabilitySeq = new Map();
|
|
65
80
|
availabilityError;
|
|
66
81
|
credentialOperations = new Map();
|
|
67
|
-
constructor(credentials, config, modelsPath, modelsStore, providers, modelNetworkEnabled) {
|
|
82
|
+
constructor(credentials, config, modelsPath, modelsStore, providers, modelNetworkEnabled, nativeCatalogProviders) {
|
|
68
83
|
this.credentials = credentials;
|
|
69
84
|
this.config = config;
|
|
70
85
|
this.modelsPath = modelsPath;
|
|
71
86
|
this.modelNetworkEnabled = modelNetworkEnabled;
|
|
87
|
+
this.nativeCatalogProviders = nativeCatalogProviders;
|
|
72
88
|
this.defaultBuiltins = new Map(providers.map((provider) => [provider.id, provider]));
|
|
73
89
|
for (const [providerId, provider] of this.defaultBuiltins)
|
|
74
90
|
this.builtins.set(providerId, provider);
|
|
@@ -84,12 +100,11 @@ export class ModelRuntime {
|
|
|
84
100
|
? new FileModelsStore(options.modelsStorePath ?? join(dirname(modelsPath), "models-store.json"))
|
|
85
101
|
: new InMemoryCodingAgentModelsStore());
|
|
86
102
|
const builtinModelDataGeneratedAt = builtinProviderCatalog.getBuiltinModelDataGeneratedAt();
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
.map((provider) => provider.refreshModels !== undefined
|
|
103
|
+
const builtinProviders = builtinProviderCatalog.builtinProviders();
|
|
104
|
+
const providers = builtinProviders.map((provider) => provider.refreshModels !== undefined
|
|
90
105
|
? provider
|
|
91
106
|
: withRemoteCatalog(provider, options.catalogBaseUrl, builtinModelDataGeneratedAt));
|
|
92
|
-
const runtime = new ModelRuntime(credentials, config, modelsPath, modelsStore, providers, process.env.PI_OFFLINE === undefined);
|
|
107
|
+
const runtime = new ModelRuntime(credentials, config, modelsPath, modelsStore, providers, process.env.PI_OFFLINE === undefined, new Set(builtinProviders.filter((provider) => provider.refreshModels !== undefined).map((provider) => provider.id)));
|
|
93
108
|
runtime.configureRadiusProviders();
|
|
94
109
|
runtime.rebuildProviders();
|
|
95
110
|
const refreshFromNetwork = runtime.modelNetworkEnabled && options.allowModelNetwork === true;
|
|
@@ -102,7 +117,12 @@ export class ModelRuntime {
|
|
|
102
117
|
: options.signal;
|
|
103
118
|
try {
|
|
104
119
|
if (options.refreshOnCreate !== false) {
|
|
105
|
-
await runtime.refresh({ allowNetwork: refreshFromNetwork, signal });
|
|
120
|
+
const result = await runtime.refresh({ allowNetwork: refreshFromNetwork, signal });
|
|
121
|
+
const remoteError = result.errors.get("pi-server");
|
|
122
|
+
if (remoteError)
|
|
123
|
+
throw remoteError;
|
|
124
|
+
if (runtime.piServerMode && result.aborted)
|
|
125
|
+
signal?.throwIfAborted();
|
|
106
126
|
}
|
|
107
127
|
}
|
|
108
128
|
finally {
|
|
@@ -174,6 +194,7 @@ export class ModelRuntime {
|
|
|
174
194
|
this.updateModelSnapshot();
|
|
175
195
|
}
|
|
176
196
|
updateModelSnapshot() {
|
|
197
|
+
this.remoteProjection = undefined;
|
|
177
198
|
const all = [...this.models.getModels()];
|
|
178
199
|
this.snapshot = {
|
|
179
200
|
...this.snapshot,
|
|
@@ -183,8 +204,8 @@ export class ModelRuntime {
|
|
|
183
204
|
}
|
|
184
205
|
async runAvailabilityRefresh(seq, errorSeq, signal) {
|
|
185
206
|
const providers = this.models.getProviders();
|
|
186
|
-
const [
|
|
187
|
-
this.models.
|
|
207
|
+
const [allAvailable, checks, credentials] = await Promise.all([
|
|
208
|
+
this.models.getAllAvailable(undefined, { signal }),
|
|
188
209
|
Promise.all(providers.map(async (provider) => [
|
|
189
210
|
provider.id,
|
|
190
211
|
await this.models.checkAuth(provider.id, { signal }),
|
|
@@ -193,13 +214,15 @@ export class ModelRuntime {
|
|
|
193
214
|
]);
|
|
194
215
|
if (seq !== this.availabilityRefreshSeq)
|
|
195
216
|
return;
|
|
217
|
+
this.localOperationAvailable = allAvailable;
|
|
218
|
+
this.remoteProjection = undefined;
|
|
196
219
|
const auth = new Map(checks);
|
|
197
220
|
const configuredProviders = new Set(checks
|
|
198
221
|
.filter((entry) => entry[1] !== undefined)
|
|
199
222
|
.map(([providerId]) => providerId));
|
|
200
223
|
this.snapshot = {
|
|
201
224
|
all: [...this.models.getModels()],
|
|
202
|
-
available:
|
|
225
|
+
available: allAvailable.filter((model) => isModelType(model, "chat")),
|
|
203
226
|
configuredProviders,
|
|
204
227
|
storedProviders: new Set(credentials.map((entry) => entry.providerId)),
|
|
205
228
|
auth,
|
|
@@ -228,14 +251,20 @@ export class ModelRuntime {
|
|
|
228
251
|
this.providerAvailabilitySeq.set(providerId, providerSeq);
|
|
229
252
|
const errorSeq = ++this.availabilityErrorSeq;
|
|
230
253
|
try {
|
|
231
|
-
const [
|
|
232
|
-
this.models.
|
|
254
|
+
const [allAvailable, auth, credential] = await Promise.all([
|
|
255
|
+
this.models.getAllAvailable(providerId, { signal }),
|
|
233
256
|
this.models.checkAuth(providerId, { signal }),
|
|
234
257
|
this.credentials.read(providerId, { signal }),
|
|
235
258
|
]);
|
|
236
259
|
signal.throwIfAborted();
|
|
237
260
|
if (this.providerAvailabilitySeq.get(providerId) !== providerSeq)
|
|
238
261
|
return;
|
|
262
|
+
this.localOperationAvailable = [
|
|
263
|
+
...this.localOperationAvailable.filter((model) => model.provider !== providerId),
|
|
264
|
+
...allAvailable,
|
|
265
|
+
];
|
|
266
|
+
this.remoteProjection = undefined;
|
|
267
|
+
const available = allAvailable.filter((model) => isModelType(model, "chat"));
|
|
239
268
|
const configuredProviders = new Set(this.snapshot.configuredProviders);
|
|
240
269
|
const storedProviders = new Set(this.snapshot.storedProviders);
|
|
241
270
|
const authByProvider = new Map(this.snapshot.auth);
|
|
@@ -276,36 +305,167 @@ export class ModelRuntime {
|
|
|
276
305
|
}
|
|
277
306
|
}
|
|
278
307
|
getProviders() {
|
|
308
|
+
if (this.piServerMode) {
|
|
309
|
+
const ids = new Set([
|
|
310
|
+
...this.models.getProviders().map((provider) => provider.id),
|
|
311
|
+
...this.getAllModels().map((model) => model.provider),
|
|
312
|
+
]);
|
|
313
|
+
return [...ids].flatMap((id) => this.getProvider(id) ?? []);
|
|
314
|
+
}
|
|
279
315
|
return this.models.getProviders();
|
|
280
316
|
}
|
|
281
317
|
getProvider(providerId) {
|
|
282
|
-
|
|
318
|
+
const local = this.models.getProvider(providerId);
|
|
319
|
+
if (!this.piServerMode)
|
|
320
|
+
return local;
|
|
321
|
+
const catalog = this.getAllModels(providerId);
|
|
322
|
+
if (!local && catalog.length === 0)
|
|
323
|
+
return undefined;
|
|
324
|
+
const capabilities = this.remoteProviderCapabilities.find((provider) => provider.id === providerId);
|
|
325
|
+
return {
|
|
326
|
+
...local,
|
|
327
|
+
id: providerId,
|
|
328
|
+
name: local?.name ?? providerId,
|
|
329
|
+
auth: local?.auth ?? {},
|
|
330
|
+
getModels: () => this.getModels(providerId),
|
|
331
|
+
getAllModels: () => this.getAllModels(providerId),
|
|
332
|
+
stream: (model, context, options) => this.stream(model, context, options),
|
|
333
|
+
streamSimple: (model, context, options) => this.streamSimple(model, context, options),
|
|
334
|
+
generateImages: local?.generateImages || catalog.some((model) => isModelType(model, "image"))
|
|
335
|
+
? (model, context, options) => this.generateImages(model, context, options)
|
|
336
|
+
: undefined,
|
|
337
|
+
classify: local?.classify || catalog.some((model) => isModelType(model, "classifier"))
|
|
338
|
+
? (model, context, options) => this.classify(model, context, options)
|
|
339
|
+
: undefined,
|
|
340
|
+
fetchDeferred: local?.fetchDeferred || capabilities?.fetchDeferred
|
|
341
|
+
? (model, handle, options) => this.streamDeferred(model, handle, options)
|
|
342
|
+
: undefined,
|
|
343
|
+
cancelDeferred: local?.cancelDeferred || capabilities?.cancelDeferred
|
|
344
|
+
? (model, handle, options) => this.cancelDeferred(model, handle, options)
|
|
345
|
+
: undefined,
|
|
346
|
+
};
|
|
283
347
|
}
|
|
284
348
|
getModels(providerId) {
|
|
349
|
+
if (this.piServerMode)
|
|
350
|
+
return this.getModelsOfType("chat", providerId);
|
|
285
351
|
return this.models.getModels(providerId);
|
|
286
352
|
}
|
|
287
353
|
getModel(providerId, modelId) {
|
|
354
|
+
if (this.piServerMode)
|
|
355
|
+
return this.getModelOfType("chat", providerId, modelId);
|
|
288
356
|
return this.models.getModel(providerId, modelId);
|
|
289
357
|
}
|
|
290
358
|
getModelsOfType(type, providerId) {
|
|
359
|
+
if (this.piServerMode)
|
|
360
|
+
return this.getRemoteModels(false, providerId).filter((model) => isModelType(model, type));
|
|
291
361
|
return this.models.getModelsOfType(type, providerId);
|
|
292
362
|
}
|
|
293
363
|
getModelOfType(type, providerId, modelId) {
|
|
364
|
+
if (this.piServerMode)
|
|
365
|
+
return this.getModelsOfType(type, providerId).find((model) => model.id === modelId);
|
|
294
366
|
return this.models.getModelOfType(type, providerId, modelId);
|
|
295
367
|
}
|
|
296
368
|
getAllModels(providerId) {
|
|
369
|
+
if (this.piServerMode)
|
|
370
|
+
return this.getRemoteModels(false, providerId);
|
|
297
371
|
return this.models.getAllModels(providerId);
|
|
298
372
|
}
|
|
299
|
-
getAvailableOfType(type, providerId, options) {
|
|
373
|
+
async getAvailableOfType(type, providerId, options) {
|
|
374
|
+
if (this.piServerMode) {
|
|
375
|
+
options?.signal?.throwIfAborted();
|
|
376
|
+
await raceWithAbortSignal(this.registrationRefresh, options?.signal);
|
|
377
|
+
if (!this.remoteCatalogLoaded)
|
|
378
|
+
await this.refreshRemoteCatalog(options?.signal);
|
|
379
|
+
return this.getRemoteModels(true, providerId).filter((model) => isModelType(model, type));
|
|
380
|
+
}
|
|
300
381
|
return this.models.getAvailableOfType(type, providerId, options);
|
|
301
382
|
}
|
|
302
|
-
getAllAvailable(providerId, options) {
|
|
383
|
+
async getAllAvailable(providerId, options) {
|
|
384
|
+
if (this.piServerMode) {
|
|
385
|
+
options?.signal?.throwIfAborted();
|
|
386
|
+
await raceWithAbortSignal(this.registrationRefresh, options?.signal);
|
|
387
|
+
if (!this.remoteCatalogLoaded)
|
|
388
|
+
await this.refreshRemoteCatalog(options?.signal);
|
|
389
|
+
return this.getRemoteModels(true, providerId);
|
|
390
|
+
}
|
|
303
391
|
return this.models.getAllAvailable(providerId, options);
|
|
304
392
|
}
|
|
393
|
+
getRemoteModels(available, providerId) {
|
|
394
|
+
if (this.remoteProjection) {
|
|
395
|
+
const models = available ? this.remoteProjection.available : this.remoteProjection.models;
|
|
396
|
+
return providerId === undefined ? models : models.filter((model) => model.provider === providerId);
|
|
397
|
+
}
|
|
398
|
+
const catalog = new Map(this.remoteCatalog.models.map((model) => [`${model.type ?? "chat"}\0${model.provider}\0${model.id}`, model]));
|
|
399
|
+
for (const model of this.localOperationAvailable) {
|
|
400
|
+
const key = `${model.type ?? "chat"}\0${model.provider}\0${model.id}`;
|
|
401
|
+
if (!catalog.has(key))
|
|
402
|
+
catalog.set(key, model);
|
|
403
|
+
}
|
|
404
|
+
const localAvailableKeys = new Set(this.localOperationAvailable.map((model) => `${model.type ?? "chat"}\0${model.provider}\0${model.id}`));
|
|
405
|
+
for (const id of this.nativeCatalogProviders) {
|
|
406
|
+
for (const model of this.models.getAllModels(id)) {
|
|
407
|
+
const key = `${model.type ?? "chat"}\0${model.provider}\0${model.id}`;
|
|
408
|
+
if (!catalog.has(key) || localAvailableKeys.has(key))
|
|
409
|
+
catalog.set(key, model);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const availableKeys = new Set(this.remoteCatalog.available.map((model) => `${model.type ?? "chat"}\0${model.provider}\0${model.id}`));
|
|
413
|
+
for (const model of this.localOperationAvailable) {
|
|
414
|
+
availableKeys.add(`${model.type ?? "chat"}\0${model.provider}\0${model.id}`);
|
|
415
|
+
}
|
|
416
|
+
const overlayProviders = new Set([...this.config.getProviderIds(), ...this.getRegisteredProviderIds()]);
|
|
417
|
+
for (const id of overlayProviders) {
|
|
418
|
+
const native = this.nativeExtensionProviders.get(id);
|
|
419
|
+
const base = native ?? this.builtins.get(id);
|
|
420
|
+
const baseModels = native
|
|
421
|
+
? (native.getAllModels?.() ?? native.getModels())
|
|
422
|
+
: [...catalog.values()].filter((model) => model.provider === id);
|
|
423
|
+
const provider = composeModelProvider(id, base && {
|
|
424
|
+
...base,
|
|
425
|
+
getModels: () => baseModels.filter((model) => isModelType(model, "chat")),
|
|
426
|
+
getAllModels: () => baseModels,
|
|
427
|
+
}, this.config, this.extensionProviders.get(id));
|
|
428
|
+
for (const [key, model] of catalog)
|
|
429
|
+
if (model.provider === id)
|
|
430
|
+
catalog.delete(key);
|
|
431
|
+
for (const model of provider.getAllModels?.() ?? provider.getModels()) {
|
|
432
|
+
catalog.set(`${model.type ?? "chat"}\0${model.provider}\0${model.id}`, model);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const models = [...catalog.values()];
|
|
436
|
+
const availableProviders = new Set(models
|
|
437
|
+
.filter((model) => availableKeys.has(`${model.type ?? "chat"}\0${model.provider}\0${model.id}`))
|
|
438
|
+
.map((model) => model.provider));
|
|
439
|
+
const virtualModels = [...this.virtualModels.values()].flatMap((models) => [...models.values()].map((entry) => entry.model));
|
|
440
|
+
const virtualIds = new Set(virtualModels.map((model) => `${model.provider}\0${model.id}`));
|
|
441
|
+
const physical = models.filter((model) => !isModelType(model, "chat") || !virtualIds.has(`${model.provider}\0${model.id}`));
|
|
442
|
+
const eligibleVirtual = virtualModels.filter((model) => !models.some((entry) => entry.provider === model.provider) || availableProviders.has(model.provider));
|
|
443
|
+
this.remoteProjection = {
|
|
444
|
+
models: [...physical, ...virtualModels],
|
|
445
|
+
available: [
|
|
446
|
+
...physical.filter((model) => availableKeys.has(`${model.type ?? "chat"}\0${model.provider}\0${model.id}`)),
|
|
447
|
+
...eligibleVirtual,
|
|
448
|
+
],
|
|
449
|
+
};
|
|
450
|
+
return this.getRemoteModels(available, providerId);
|
|
451
|
+
}
|
|
452
|
+
async refreshRemoteCatalog(signal) {
|
|
453
|
+
const seq = ++this.remoteCatalogRefreshSeq;
|
|
454
|
+
const catalog = await fetchPiServerModels({ signal });
|
|
455
|
+
if (seq !== this.remoteCatalogRefreshSeq)
|
|
456
|
+
return;
|
|
457
|
+
this.remoteCatalog = catalog;
|
|
458
|
+
this.remoteProviderCapabilities = catalog.providers;
|
|
459
|
+
this.remoteCatalogLoaded = true;
|
|
460
|
+
this.remoteCatalogError = undefined;
|
|
461
|
+
this.remoteProjection = undefined;
|
|
462
|
+
}
|
|
305
463
|
async checkAuth(providerId, options) {
|
|
306
464
|
return this.models.checkAuth(providerId, options);
|
|
307
465
|
}
|
|
308
466
|
async getAvailable(providerId, options) {
|
|
467
|
+
if (this.piServerMode)
|
|
468
|
+
return this.getAvailableOfType("chat", providerId, options);
|
|
309
469
|
if (providerId) {
|
|
310
470
|
const errorSeq = ++this.availabilityErrorSeq;
|
|
311
471
|
try {
|
|
@@ -325,6 +485,8 @@ export class ModelRuntime {
|
|
|
325
485
|
return this.snapshot.available;
|
|
326
486
|
}
|
|
327
487
|
getAvailableSnapshot() {
|
|
488
|
+
if (this.piServerMode)
|
|
489
|
+
return this.getRemoteModels(true).filter((model) => isModelType(model, "chat"));
|
|
328
490
|
return this.snapshot.available;
|
|
329
491
|
}
|
|
330
492
|
getError() {
|
|
@@ -337,6 +499,10 @@ export class ModelRuntime {
|
|
|
337
499
|
}
|
|
338
500
|
if (this.availabilityError)
|
|
339
501
|
errors.push(`Availability refresh: ${this.availabilityError}`);
|
|
502
|
+
if (this.remoteCatalogError)
|
|
503
|
+
errors.push(`pi-server model catalog: ${this.remoteCatalogError}`);
|
|
504
|
+
for (const [providerId, error] of this.registrationRefreshErrors)
|
|
505
|
+
errors.push(`Provider "${providerId}" refresh: ${error}`);
|
|
340
506
|
return errors.length > 0 ? errors.join("\n\n") : undefined;
|
|
341
507
|
}
|
|
342
508
|
getRegisteredProviderConfig(providerId) {
|
|
@@ -359,6 +525,8 @@ export class ModelRuntime {
|
|
|
359
525
|
return this.isUsingOAuth(providerId) && this.models.getProvider(providerId)?.auth.oauth?.isSubscription === true;
|
|
360
526
|
}
|
|
361
527
|
hasConfiguredAuth(providerId) {
|
|
528
|
+
if (this.piServerMode)
|
|
529
|
+
return this.getRemoteModels(true, providerId).length > 0;
|
|
362
530
|
return this.snapshot.configuredProviders.has(providerId);
|
|
363
531
|
}
|
|
364
532
|
async getAuth(providerOrModel, overrides = {}) {
|
|
@@ -475,11 +643,52 @@ export class ModelRuntime {
|
|
|
475
643
|
},
|
|
476
644
|
};
|
|
477
645
|
}
|
|
646
|
+
async prepareRemoteRequest(model, options) {
|
|
647
|
+
if (options?.fetch !== undefined)
|
|
648
|
+
throw new ModelsError("stream", "pi-server does not support custom fetch implementations");
|
|
649
|
+
if (options &&
|
|
650
|
+
"transformPreparedStreamOptions" in options &&
|
|
651
|
+
options.transformPreparedStreamOptions !== undefined)
|
|
652
|
+
throw new ModelsError("stream", "pi-server does not support client prepared stream option transforms");
|
|
653
|
+
const resolution = this.models.getProvider(model.provider)
|
|
654
|
+
? await this.getAuth(model, { apiKey: options?.apiKey, env: options?.env, signal: options?.signal })
|
|
655
|
+
: undefined;
|
|
656
|
+
let headers = mergeHeaders(mergeHeaders(model.headers, resolution?.auth.headers), options?.headers);
|
|
657
|
+
const { transformHeaders, ...remoteOptions } = options ?? {};
|
|
658
|
+
if (transformHeaders)
|
|
659
|
+
headers = await transformHeaders(headers ?? {});
|
|
660
|
+
return {
|
|
661
|
+
model: resolution?.auth.baseUrl ? { ...model, baseUrl: resolution.auth.baseUrl } : model,
|
|
662
|
+
options: {
|
|
663
|
+
...remoteOptions,
|
|
664
|
+
apiKey: options?.apiKey ?? resolution?.auth.apiKey,
|
|
665
|
+
headers,
|
|
666
|
+
env: resolution?.env || options?.env ? { ...resolution?.env, ...options?.env } : undefined,
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
async prepareStreamRequest(model, options) {
|
|
671
|
+
const { transformPreparedStreamOptions, ...authOptions } = options ?? {};
|
|
672
|
+
const prepared = await this.prepareRequest(model, authOptions);
|
|
673
|
+
return {
|
|
674
|
+
provider: prepared.provider,
|
|
675
|
+
model: prepared.model,
|
|
676
|
+
options: transformPreparedStreamOptions
|
|
677
|
+
? await transformPreparedStreamOptions(prepared.options, prepared.model)
|
|
678
|
+
: prepared.options,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
478
681
|
stream(model, context, options) {
|
|
479
682
|
const transcript = normalizeContext(context);
|
|
480
683
|
return lazyStream(model, async () => {
|
|
481
684
|
assertChatModel(model);
|
|
482
|
-
|
|
685
|
+
if (this.piServerMode) {
|
|
686
|
+
if (isVirtualModel(model))
|
|
687
|
+
throw new ModelsError("stream", `Virtual model ${model.provider}/${model.id} must be routed before streaming`);
|
|
688
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
689
|
+
return streamRawPiServer(prepared.model, transcript, prepared.options);
|
|
690
|
+
}
|
|
691
|
+
const prepared = await this.prepareStreamRequest(model, options);
|
|
483
692
|
return prepared.provider.stream(prepared.model, transcript, prepared.options);
|
|
484
693
|
});
|
|
485
694
|
}
|
|
@@ -509,7 +718,11 @@ export class ModelRuntime {
|
|
|
509
718
|
}
|
|
510
719
|
return lazyStream(model, async () => {
|
|
511
720
|
assertChatModel(model);
|
|
512
|
-
|
|
721
|
+
if (this.piServerMode) {
|
|
722
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
723
|
+
return streamPiServer(prepared.model, transcript, prepared.options);
|
|
724
|
+
}
|
|
725
|
+
const prepared = await this.prepareStreamRequest(model, options);
|
|
513
726
|
return prepared.provider.streamSimple(prepared.model, transcript, prepared.options);
|
|
514
727
|
});
|
|
515
728
|
}
|
|
@@ -519,7 +732,11 @@ export class ModelRuntime {
|
|
|
519
732
|
streamDeferred(model, handle, options) {
|
|
520
733
|
return lazyStream(model, async () => {
|
|
521
734
|
assertChatModel(model);
|
|
522
|
-
|
|
735
|
+
if (this.piServerMode) {
|
|
736
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
737
|
+
return fetchDeferredPiServer(prepared.model, handle, prepared.options);
|
|
738
|
+
}
|
|
739
|
+
const prepared = await this.prepareStreamRequest(model, options);
|
|
523
740
|
if (!prepared.provider.fetchDeferred) {
|
|
524
741
|
throw new ModelsError("provider", `Provider ${model.provider} does not support deferred responses`);
|
|
525
742
|
}
|
|
@@ -531,6 +748,11 @@ export class ModelRuntime {
|
|
|
531
748
|
}
|
|
532
749
|
async cancelDeferred(model, handle, options) {
|
|
533
750
|
assertChatModel(model);
|
|
751
|
+
if (this.piServerMode) {
|
|
752
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
753
|
+
await cancelDeferredPiServer(prepared.model, handle, prepared.options);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
534
756
|
const prepared = await this.prepareRequest(model, options);
|
|
535
757
|
if (!prepared.provider.cancelDeferred) {
|
|
536
758
|
throw new ModelsError("provider", `Provider ${model.provider} does not support deferred responses`);
|
|
@@ -540,6 +762,10 @@ export class ModelRuntime {
|
|
|
540
762
|
async generateImages(model, context, options) {
|
|
541
763
|
try {
|
|
542
764
|
assertImageModel(model);
|
|
765
|
+
if (this.piServerMode) {
|
|
766
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
767
|
+
return await generateImagesPiServer(prepared.model, context, prepared.options);
|
|
768
|
+
}
|
|
543
769
|
const prepared = await this.prepareRequest(model, options);
|
|
544
770
|
if (!prepared.provider.generateImages) {
|
|
545
771
|
throw new ModelsError("provider", `Provider ${model.provider} does not support image generation`);
|
|
@@ -553,6 +779,10 @@ export class ModelRuntime {
|
|
|
553
779
|
async classify(model, context, options) {
|
|
554
780
|
try {
|
|
555
781
|
assertClassifierModel(model);
|
|
782
|
+
if (this.piServerMode) {
|
|
783
|
+
const prepared = await this.prepareRemoteRequest(model, options);
|
|
784
|
+
return await classifyPiServer(prepared.model, context, prepared.options);
|
|
785
|
+
}
|
|
556
786
|
const prepared = await this.prepareRequest(model, options);
|
|
557
787
|
if (!prepared.provider.classify) {
|
|
558
788
|
throw new ModelsError("provider", `Provider ${model.provider} does not support classification`);
|
|
@@ -579,6 +809,60 @@ export class ModelRuntime {
|
|
|
579
809
|
});
|
|
580
810
|
}
|
|
581
811
|
async refresh(options = {}) {
|
|
812
|
+
if (options.signal?.aborted)
|
|
813
|
+
return { aborted: true, errors: new Map() };
|
|
814
|
+
try {
|
|
815
|
+
await raceWithAbortSignal(this.registrationRefresh, options.signal);
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
if (options.signal?.aborted)
|
|
819
|
+
return { aborted: true, errors: new Map() };
|
|
820
|
+
throw error;
|
|
821
|
+
}
|
|
822
|
+
if (this.piServerMode) {
|
|
823
|
+
this.config = await ModelConfig.load(this.modelsPath);
|
|
824
|
+
this.configureRadiusProviders();
|
|
825
|
+
this.rebuildProviders();
|
|
826
|
+
let result = await this.models.refresh({ ...options, allowNetwork: false });
|
|
827
|
+
const nativeProviders = [
|
|
828
|
+
...new Set([...this.nativeCatalogProviders, ...this.nativeExtensionProviders.keys()]),
|
|
829
|
+
].filter((id) => options.providers === undefined || options.providers.includes(id));
|
|
830
|
+
if ((options.allowNetwork ?? this.modelNetworkEnabled) && nativeProviders.length > 0) {
|
|
831
|
+
const nativeResult = await this.models.refresh({
|
|
832
|
+
...options,
|
|
833
|
+
providers: nativeProviders,
|
|
834
|
+
allowNetwork: true,
|
|
835
|
+
});
|
|
836
|
+
result = {
|
|
837
|
+
aborted: result.aborted || nativeResult.aborted,
|
|
838
|
+
errors: new Map([...result.errors, ...nativeResult.errors]),
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
const errors = new Map(result.errors);
|
|
842
|
+
try {
|
|
843
|
+
await this.queueAvailabilityRefresh(options.signal);
|
|
844
|
+
}
|
|
845
|
+
catch (error) {
|
|
846
|
+
if (!options.signal?.aborted)
|
|
847
|
+
errors.set("availability", error instanceof Error ? error : new Error(String(error)));
|
|
848
|
+
}
|
|
849
|
+
try {
|
|
850
|
+
if (!options.signal?.aborted)
|
|
851
|
+
await this.refreshRemoteCatalog(options.signal);
|
|
852
|
+
}
|
|
853
|
+
catch (error) {
|
|
854
|
+
if (!options.signal?.aborted) {
|
|
855
|
+
const remoteError = error instanceof Error ? error : new Error(String(error));
|
|
856
|
+
this.remoteCatalogError = remoteError.message;
|
|
857
|
+
errors.set("pi-server", remoteError);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
for (const id of this.registrationRefreshErrors.keys()) {
|
|
861
|
+
if (!errors.has(id) && (options.providers === undefined || options.providers.includes(id)))
|
|
862
|
+
this.registrationRefreshErrors.delete(id);
|
|
863
|
+
}
|
|
864
|
+
return { aborted: result.aborted || options.signal?.aborted === true, errors };
|
|
865
|
+
}
|
|
582
866
|
this.config = await ModelConfig.load(this.modelsPath);
|
|
583
867
|
this.configureRadiusProviders();
|
|
584
868
|
if (options.providers) {
|
|
@@ -593,12 +877,7 @@ export class ModelRuntime {
|
|
|
593
877
|
...options,
|
|
594
878
|
allowNetwork: options.allowNetwork ?? this.modelNetworkEnabled,
|
|
595
879
|
};
|
|
596
|
-
|
|
597
|
-
// The fallback keeps source-mode CLI tests working without rebuilding workspace dependencies.
|
|
598
|
-
const result = (await this.models.refresh(refreshOptions)) ?? {
|
|
599
|
-
aborted: refreshOptions.signal?.aborted ?? false,
|
|
600
|
-
errors: new Map(),
|
|
601
|
-
};
|
|
880
|
+
const result = await this.models.refresh(refreshOptions);
|
|
602
881
|
const errors = new Map(result.errors);
|
|
603
882
|
this.updateModelSnapshot();
|
|
604
883
|
if (options.providers) {
|
|
@@ -623,6 +902,21 @@ export class ModelRuntime {
|
|
|
623
902
|
}
|
|
624
903
|
return { aborted: result.aborted || (options.signal?.aborted ?? false), errors };
|
|
625
904
|
}
|
|
905
|
+
queueRegistrationRefresh(providerId) {
|
|
906
|
+
this.registrationRefresh = this.registrationRefresh
|
|
907
|
+
.then(async () => {
|
|
908
|
+
const result = await this.models.refresh({ allowNetwork: false, providers: [providerId] });
|
|
909
|
+
const error = result.errors.get(providerId);
|
|
910
|
+
if (error)
|
|
911
|
+
throw error;
|
|
912
|
+
this.updateModelSnapshot();
|
|
913
|
+
await this.refreshProviderAvailability(providerId, operationSignal());
|
|
914
|
+
this.registrationRefreshErrors.delete(providerId);
|
|
915
|
+
})
|
|
916
|
+
.catch((error) => {
|
|
917
|
+
this.registrationRefreshErrors.set(providerId, error instanceof Error ? error.message : String(error));
|
|
918
|
+
});
|
|
919
|
+
}
|
|
626
920
|
registerNativeProvider(provider) {
|
|
627
921
|
if (!provider.id.trim())
|
|
628
922
|
throw new Error("Provider id must not be empty.");
|
|
@@ -630,7 +924,28 @@ export class ModelRuntime {
|
|
|
630
924
|
this.nativeExtensionProviders.set(provider.id, provider);
|
|
631
925
|
this.recomposeProvider(provider.id);
|
|
632
926
|
this.updateModelSnapshot();
|
|
633
|
-
|
|
927
|
+
this.markProvisionallyConfigured(provider.id, configuredRequestAuthStatus(this.config.getProvider(provider.id), undefined), provider.auth.oauth && !provider.auth.apiKey ? "oauth" : "api_key");
|
|
928
|
+
this.queueRegistrationRefresh(provider.id);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Mark a newly registered provider as configured when it has a stored credential or a configured
|
|
932
|
+
* API key. Availability checks run asynchronously, and callers such as initial model selection
|
|
933
|
+
* read the snapshot before they finish. The next availability pass replaces this entry.
|
|
934
|
+
*/
|
|
935
|
+
markProvisionallyConfigured(providerId, configuredStatus, type) {
|
|
936
|
+
if (!this.snapshot.storedProviders.has(providerId) && !configuredStatus?.configured)
|
|
937
|
+
return;
|
|
938
|
+
const configuredProviders = new Set(this.snapshot.configuredProviders).add(providerId);
|
|
939
|
+
const auth = new Map(this.snapshot.auth);
|
|
940
|
+
// Never clobber a real check result.
|
|
941
|
+
if (!auth.get(providerId))
|
|
942
|
+
auth.set(providerId, { type, source: "configured provider" });
|
|
943
|
+
this.snapshot = {
|
|
944
|
+
...this.snapshot,
|
|
945
|
+
auth,
|
|
946
|
+
configuredProviders,
|
|
947
|
+
available: this.snapshot.all.filter((model) => configuredProviders.has(model.provider)),
|
|
948
|
+
};
|
|
634
949
|
}
|
|
635
950
|
registerProvider(providerId, config) {
|
|
636
951
|
// Validate the incoming registration on its own, like the legacy registry:
|
|
@@ -648,32 +963,15 @@ export class ModelRuntime {
|
|
|
648
963
|
this.extensionProviders.set(providerId, effective);
|
|
649
964
|
this.recomposeProvider(providerId);
|
|
650
965
|
this.updateModelSnapshot();
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const configuredProviders = new Set(this.snapshot.configuredProviders).add(providerId);
|
|
654
|
-
const auth = new Map(this.snapshot.auth);
|
|
655
|
-
// Provisional entry until the async refresh lands; never clobber a real check result.
|
|
656
|
-
if (!auth.get(providerId)) {
|
|
657
|
-
auth.set(providerId, {
|
|
658
|
-
type: effective.oauth && !effective.apiKey ? "oauth" : "api_key",
|
|
659
|
-
source: "configured provider",
|
|
660
|
-
});
|
|
661
|
-
}
|
|
662
|
-
this.snapshot = {
|
|
663
|
-
...this.snapshot,
|
|
664
|
-
auth,
|
|
665
|
-
configuredProviders,
|
|
666
|
-
available: this.snapshot.all.filter((model) => configuredProviders.has(model.provider)),
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
void this.refresh({ allowNetwork: false });
|
|
966
|
+
this.markProvisionallyConfigured(providerId, configuredRequestAuthStatus(this.config.getProvider(providerId), effective), effective.oauth && !effective.apiKey ? "oauth" : "api_key");
|
|
967
|
+
this.queueRegistrationRefresh(providerId);
|
|
670
968
|
}
|
|
671
969
|
unregisterProvider(providerId) {
|
|
672
970
|
this.extensionProviders.delete(providerId);
|
|
673
971
|
this.nativeExtensionProviders.delete(providerId);
|
|
674
972
|
this.recomposeProvider(providerId);
|
|
675
973
|
this.updateModelSnapshot();
|
|
676
|
-
|
|
974
|
+
this.queueRegistrationRefresh(providerId);
|
|
677
975
|
}
|
|
678
976
|
/**
|
|
679
977
|
* Register a virtual model under `definition.provider`, which may also list physical models or
|
|
@@ -684,7 +982,7 @@ export class ModelRuntime {
|
|
|
684
982
|
const { provider: providerId, id } = definition;
|
|
685
983
|
if (!providerId.trim() || !id.trim())
|
|
686
984
|
throw new Error("Virtual model provider and id must not be empty.");
|
|
687
|
-
const existing = this.
|
|
985
|
+
const existing = this.getModel(providerId, id);
|
|
688
986
|
if (existing && !isVirtualModel(existing)) {
|
|
689
987
|
throw new Error(`Virtual model ${providerId}/${id} conflicts with a physical model.`);
|
|
690
988
|
}
|
|
@@ -699,7 +997,7 @@ export class ModelRuntime {
|
|
|
699
997
|
this.snapshot = { ...this.snapshot, auth, configuredProviders };
|
|
700
998
|
}
|
|
701
999
|
this.updateModelSnapshot();
|
|
702
|
-
|
|
1000
|
+
this.queueRegistrationRefresh(providerId);
|
|
703
1001
|
}
|
|
704
1002
|
unregisterVirtualModel(providerId, id) {
|
|
705
1003
|
const models = this.virtualModels.get(providerId);
|
|
@@ -709,7 +1007,7 @@ export class ModelRuntime {
|
|
|
709
1007
|
this.virtualModels.delete(providerId);
|
|
710
1008
|
this.recomposeProvider(providerId);
|
|
711
1009
|
this.updateModelSnapshot();
|
|
712
|
-
|
|
1010
|
+
this.queueRegistrationRefresh(providerId);
|
|
713
1011
|
}
|
|
714
1012
|
/**
|
|
715
1013
|
* Ask a virtual model's router for the model and thinking level of one request. The router must
|
|
@@ -747,7 +1045,7 @@ export class ModelRuntime {
|
|
|
747
1045
|
}
|
|
748
1046
|
/** A catalog chat model that is not virtual. */
|
|
749
1047
|
getPhysicalModel(providerId, modelId) {
|
|
750
|
-
const model = this.
|
|
1048
|
+
const model = this.getModel(providerId, modelId);
|
|
751
1049
|
return model && !isVirtualModel(model) ? model : undefined;
|
|
752
1050
|
}
|
|
753
1051
|
}
|