fix(coding-agent): defer startup model catalog refresh
This commit is contained in:
@@ -62,7 +62,9 @@ export interface CreateModelRuntimeOptions {
|
||||
modelsPath?: string | null;
|
||||
modelsStore?: ModelsStore;
|
||||
modelsStorePath?: string;
|
||||
/** Allow create() to refresh model catalogs over the network. Defaults to false. */
|
||||
allowModelNetwork?: boolean;
|
||||
/** Timeout for the create-time network model refresh. */
|
||||
modelRefreshTimeoutMs?: number;
|
||||
catalogBaseUrl?: string;
|
||||
}
|
||||
@@ -98,7 +100,7 @@ export class ModelRuntime implements Models {
|
||||
private readonly extensionProviders = new Map<string, ProviderConfigInput>();
|
||||
private readonly compositionErrors = new Map<string, string>();
|
||||
private readonly modelsPath: string | undefined;
|
||||
private readonly allowModelNetwork: boolean;
|
||||
private readonly modelNetworkEnabled: boolean;
|
||||
private config: ModelConfig;
|
||||
private snapshot: ModelRuntimeSnapshot = {
|
||||
all: [],
|
||||
@@ -116,12 +118,12 @@ export class ModelRuntime implements Models {
|
||||
modelsPath: string | undefined,
|
||||
modelsStore: ModelsStore,
|
||||
providers: readonly Provider[],
|
||||
allowModelNetwork: boolean,
|
||||
modelNetworkEnabled: boolean,
|
||||
) {
|
||||
this.credentials = credentials;
|
||||
this.config = config;
|
||||
this.modelsPath = modelsPath;
|
||||
this.allowModelNetwork = allowModelNetwork;
|
||||
this.modelNetworkEnabled = modelNetworkEnabled;
|
||||
this.defaultBuiltins = new Map(providers.map((provider) => [provider.id, provider]));
|
||||
for (const [providerId, provider] of this.defaultBuiltins) this.builtins.set(providerId, provider);
|
||||
this.models = createModels({ credentials, modelsStore });
|
||||
@@ -149,16 +151,17 @@ export class ModelRuntime implements Models {
|
||||
modelsPath,
|
||||
modelsStore,
|
||||
providers,
|
||||
options.allowModelNetwork ?? process.env.PI_OFFLINE === undefined,
|
||||
process.env.PI_OFFLINE === undefined,
|
||||
);
|
||||
runtime.configureRadiusProviders();
|
||||
runtime.rebuildProviders();
|
||||
const controller = new AbortController();
|
||||
const timeout = runtime.allowModelNetwork
|
||||
const refreshFromNetwork = runtime.modelNetworkEnabled && options.allowModelNetwork === true;
|
||||
const controller = refreshFromNetwork ? new AbortController() : undefined;
|
||||
const timeout = controller
|
||||
? setTimeout(() => controller.abort(), options.modelRefreshTimeoutMs ?? 15_000)
|
||||
: undefined;
|
||||
try {
|
||||
await runtime.refresh({ allowNetwork: runtime.allowModelNetwork, signal: controller.signal });
|
||||
await runtime.refresh({ allowNetwork: refreshFromNetwork, signal: controller?.signal });
|
||||
} finally {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
}
|
||||
@@ -389,7 +392,11 @@ export class ModelRuntime implements Models {
|
||||
};
|
||||
}
|
||||
|
||||
async setRuntimeApiKey(providerId: string, apiKey: string): Promise<void> {
|
||||
async setRuntimeApiKey(
|
||||
providerId: string,
|
||||
apiKey: string,
|
||||
refreshOptions: ModelsRefreshOptions = {},
|
||||
): Promise<void> {
|
||||
this.credentials.setRuntimeApiKey(providerId, apiKey);
|
||||
const auth = new Map(this.snapshot.auth).set(providerId, { type: "api_key", source: "runtime API key" });
|
||||
const configuredProviders = new Set(this.snapshot.configuredProviders).add(providerId);
|
||||
@@ -401,12 +408,12 @@ export class ModelRuntime implements Models {
|
||||
storedProviders,
|
||||
available: this.snapshot.all.filter((model) => configuredProviders.has(model.provider)),
|
||||
};
|
||||
await this.refresh({ allowNetwork: this.allowModelNetwork });
|
||||
await this.refresh(refreshOptions);
|
||||
}
|
||||
|
||||
async removeRuntimeApiKey(providerId: string): Promise<void> {
|
||||
this.credentials.removeRuntimeApiKey(providerId);
|
||||
await this.refresh({ allowNetwork: this.allowModelNetwork });
|
||||
await this.refresh({ allowNetwork: this.modelNetworkEnabled });
|
||||
}
|
||||
|
||||
listCredentials(): Promise<readonly CredentialInfo[]> {
|
||||
@@ -492,7 +499,7 @@ export class ModelRuntime implements Models {
|
||||
|
||||
async login(providerId: string, type: AuthType, interaction: AuthInteraction): Promise<Credential> {
|
||||
const credential = await this.models.login(providerId, type, interaction);
|
||||
await this.refresh({ allowNetwork: this.allowModelNetwork });
|
||||
await this.refresh({ allowNetwork: this.modelNetworkEnabled });
|
||||
return credential;
|
||||
}
|
||||
|
||||
@@ -500,20 +507,20 @@ export class ModelRuntime implements Models {
|
||||
await this.models.logout(providerId);
|
||||
// Reset credential-dependent compatibility projections before the unconfigured provider is skipped by refresh.
|
||||
this.recomposeProvider(providerId);
|
||||
await this.refresh({ allowNetwork: this.allowModelNetwork });
|
||||
await this.refresh({ allowNetwork: this.modelNetworkEnabled });
|
||||
}
|
||||
|
||||
async reloadConfig(): Promise<void> {
|
||||
this.config = await ModelConfig.load(this.modelsPath);
|
||||
this.configureRadiusProviders();
|
||||
this.rebuildProviders();
|
||||
await this.refresh({ allowNetwork: this.allowModelNetwork });
|
||||
await this.refresh({ allowNetwork: this.modelNetworkEnabled });
|
||||
}
|
||||
|
||||
async refresh(options: ModelsRefreshOptions = {}): Promise<ModelsRefreshResult> {
|
||||
const refreshOptions = {
|
||||
...options,
|
||||
allowNetwork: options.allowNetwork ?? this.allowModelNetwork,
|
||||
allowNetwork: options.allowNetwork ?? this.modelNetworkEnabled,
|
||||
};
|
||||
// Published pi-ai builds before ModelsStore returned void and accepted a provider ID.
|
||||
// The fallback keeps source-mode CLI tests working without rebuilding workspace dependencies.
|
||||
|
||||
@@ -709,7 +709,7 @@ export async function main(args: string[], options?: MainOptions) {
|
||||
message: "--api-key requires a model to be specified via --model, --provider/--model, or --models",
|
||||
});
|
||||
} else {
|
||||
await modelRuntime.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);
|
||||
await modelRuntime.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey, { allowNetwork: false });
|
||||
await services.modelRuntime.getAvailable();
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,10 @@ export async function main(args: string[], options?: MainOptions) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!offlineMode && (appMode === "interactive" || appMode === "rpc")) {
|
||||
void modelRuntime.refresh().catch(() => {});
|
||||
}
|
||||
|
||||
if (appMode === "rpc") {
|
||||
printTimings();
|
||||
await runRpcMode(runtime);
|
||||
|
||||
Reference in New Issue
Block a user