From f4b4c04dfc028d23c4dd4c2cb30a43ce933d8887 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 20 May 2025 10:26:04 +1000 Subject: [PATCH 1/4] Ability to track the user selected Environment --- src/client/chat/installPackagesTool.ts | 2 - src/client/chat/lastUsedEnvs.ts | 81 -------------- src/client/chat/listPackagesTool.ts | 2 - src/client/chat/utils.ts | 2 - src/client/common/utils/async.ts | 23 ++++ src/client/extension.ts | 5 + .../commands/setInterpreter.ts | 6 +- .../configuration/pythonPathUpdaterService.ts | 10 +- .../recommededEnvironmentService.ts | 102 ++++++++++++++++++ src/client/interpreter/configuration/types.ts | 12 +++ src/client/interpreter/serviceRegistry.ts | 6 ++ src/client/jupyter/jupyterIntegration.ts | 31 ++++-- 12 files changed, 187 insertions(+), 95 deletions(-) delete mode 100644 src/client/chat/lastUsedEnvs.ts create mode 100644 src/client/interpreter/configuration/recommededEnvironmentService.ts diff --git a/src/client/chat/installPackagesTool.ts b/src/client/chat/installPackagesTool.ts index 177c63077cbe..7b34b71ed556 100644 --- a/src/client/chat/installPackagesTool.ts +++ b/src/client/chat/installPackagesTool.ts @@ -19,7 +19,6 @@ import { resolveFilePath } from './utils'; import { IModuleInstaller } from '../common/installer/types'; import { ModuleInstallerType } from '../pythonEnvironments/info'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; -import { trackEnvUsedByTool } from './lastUsedEnvs'; export interface IInstallPackageArgs { resourcePath?: string; @@ -67,7 +66,6 @@ export class InstallPackagesTool implements LanguageModelTool= 0; i--) { - if (urisEqual(lastUsedEnvs[i].uri, uri)) { - lastUsedEnvs.splice(i, 1); - } - } - // Add the new entry - lastUsedEnvs.push({ uri, env, dateTime: now }); - // Prune - pruneLastUsedEnvs(); -} - -/** - * Get the last used environment for a given resource (uri), or undefined if not found or expired. - */ -export function getLastEnvUsedByTool( - uri: Uri | undefined, - api: PythonExtension['environments'], -): EnvironmentPath | undefined { - pruneLastUsedEnvs(); - // Find the most recent entry for this uri that is not expired - const item = lastUsedEnvs.find((item) => urisEqual(item.uri, uri)); - if (item) { - return item.env; - } - const envPath = api.getActiveEnvironmentPath(uri); - if (lastUsedEnvs.some((item) => item.env.id === envPath.id)) { - // If this env was already used, return it - return envPath; - } - return undefined; -} - -/** - * Compare two uris (or undefined) for equality. - */ -function urisEqual(a: Uri | undefined, b: Uri | undefined): boolean { - if (a === b) { - return true; - } - if (!a || !b) { - return false; - } - return a.toString() === b.toString(); -} - -/** - * Remove items older than 60 minutes or if the list grows over 100. - */ -function pruneLastUsedEnvs() { - const now = Date.now(); - // Remove items older than 60 minutes - for (let i = lastUsedEnvs.length - 1; i >= 0; i--) { - if (now - lastUsedEnvs[i].dateTime > MAX_TRACKED_AGE) { - lastUsedEnvs.splice(i, 1); - } - } - // If still over 100, remove oldest - if (lastUsedEnvs.length > MAX_TRACKED_URIS) { - lastUsedEnvs.sort((a, b) => b.dateTime - a.dateTime); - lastUsedEnvs.length = MAX_TRACKED_URIS; - } -} diff --git a/src/client/chat/listPackagesTool.ts b/src/client/chat/listPackagesTool.ts index 0e410593de6c..659ce2b5bb0d 100644 --- a/src/client/chat/listPackagesTool.ts +++ b/src/client/chat/listPackagesTool.ts @@ -22,7 +22,6 @@ import { parsePipList } from './pipListUtils'; import { Conda } from '../pythonEnvironments/common/environmentManagers/conda'; import { traceError } from '../logging'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; -import { trackEnvUsedByTool } from './lastUsedEnvs'; export interface IResourceReference { resourcePath?: string; @@ -109,7 +108,6 @@ export async function getPythonPackagesResponse( if (!packages.length) { return 'No packages found'; } - trackEnvUsedByTool(resourcePath, environment); // Installed Python packages, each in the format or (). The version may be omitted if unknown. Returns an empty array if no packages are installed. const response = [ 'Below is a list of the Python packages, each in the format or (). The version may be omitted if unknown: ', diff --git a/src/client/chat/utils.ts b/src/client/chat/utils.ts index 00a3fbb8393c..2973e748aee4 100644 --- a/src/client/chat/utils.ts +++ b/src/client/chat/utils.ts @@ -7,7 +7,6 @@ import { PythonExtension, ResolvedEnvironment } from '../api/types'; import { ITerminalHelper, TerminalShellType } from '../common/terminal/types'; import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/terminalCodeExecution'; import { Conda } from '../pythonEnvironments/common/environmentManagers/conda'; -import { trackEnvUsedByTool } from './lastUsedEnvs'; export function resolveFilePath(filepath?: string): Uri | undefined { if (!filepath) { @@ -71,7 +70,6 @@ export async function getEnvironmentDetails( getTerminalCommand(environment, resourcePath, terminalExecutionService, terminalHelper), token, ); - trackEnvUsedByTool(resourcePath, environment); const message = [ `Following is the information about the Python environment:`, `1. Environment Type: ${environment.environment?.type || 'unknown'}`, diff --git a/src/client/common/utils/async.ts b/src/client/common/utils/async.ts index cabea8225ac9..a44425f8f1a3 100644 --- a/src/client/common/utils/async.ts +++ b/src/client/common/utils/async.ts @@ -268,3 +268,26 @@ export async function waitForCondition( }, 10); }); } + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isPromiseLike(v: any): v is PromiseLike { + return typeof v?.then === 'function'; +} + +export function raceTimeout(timeout: number, ...promises: Promise[]): Promise; +export function raceTimeout(timeout: number, defaultValue: T, ...promises: Promise[]): Promise; +export function raceTimeout(timeout: number, defaultValue: T, ...promises: Promise[]): Promise { + const resolveValue = isPromiseLike(defaultValue) ? undefined : defaultValue; + if (isPromiseLike(defaultValue)) { + promises.push((defaultValue as unknown) as Promise); + } + + let promiseResolve: ((value: T) => void) | undefined = undefined; + + const timer = setTimeout(() => promiseResolve?.((resolveValue as unknown) as T), timeout); + + return Promise.race([ + Promise.race(promises).finally(() => clearTimeout(timer)), + new Promise((resolve) => (promiseResolve = resolve)), + ]); +} diff --git a/src/client/extension.ts b/src/client/extension.ts index 1a07a4b1e3b2..b9db9a258ec6 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -44,6 +44,8 @@ import { ProposedExtensionAPI } from './proposedApiTypes'; import { buildProposedApi } from './proposedApi'; import { GLOBAL_PERSISTENT_KEYS } from './common/persistentState'; import { registerTools } from './chat'; +import { RecommendedEnvironmentService } from './interpreter/configuration/recommededEnvironmentService'; +import { IRecommendedEnvironmentService } from './interpreter/configuration/types'; durations.codeLoadingTime = stopWatch.elapsedTime; @@ -164,6 +166,9 @@ async function activateUnsafe( ); const proposedApi = buildProposedApi(components.pythonEnvs, ext.legacyIOC.serviceContainer); registerTools(context, components.pythonEnvs, api.environments, ext.legacyIOC.serviceContainer); + ext.legacyIOC.serviceContainer + .get(RecommendedEnvironmentService) + .registerEnvApi(api.environments); return [{ ...api, ...proposedApi }, activationPromise, ext.legacyIOC.serviceContainer]; } diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts index 313b638313df..912a9c66b0dd 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/setInterpreter.ts @@ -565,8 +565,11 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand implem return Promise.resolve(); } + /** + * @returns true when an interpreter was set, undefined if the user cancelled the quickpick. + */ @captureTelemetry(EventName.SELECT_INTERPRETER) - public async setInterpreter(): Promise { + public async setInterpreter(): Promise { const targetConfig = await this.getConfigTargets(); if (!targetConfig) { return; @@ -588,6 +591,7 @@ export class SetInterpreterCommand extends BaseInterpreterSelectorCommand implem if (useEnvExtension()) { await setInterpreterLegacy(interpreterState.path, wkspace); } + return true; } } diff --git a/src/client/interpreter/configuration/pythonPathUpdaterService.ts b/src/client/interpreter/configuration/pythonPathUpdaterService.ts index 9b9cc26f845f..9814ff6ee4cb 100644 --- a/src/client/interpreter/configuration/pythonPathUpdaterService.ts +++ b/src/client/interpreter/configuration/pythonPathUpdaterService.ts @@ -7,7 +7,11 @@ import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { PythonInterpreterTelemetry } from '../../telemetry/types'; import { IComponentAdapter } from '../contracts'; -import { IPythonPathUpdaterServiceFactory, IPythonPathUpdaterServiceManager } from './types'; +import { + IRecommendedEnvironmentService, + IPythonPathUpdaterServiceFactory, + IPythonPathUpdaterServiceManager, +} from './types'; @injectable() export class PythonPathUpdaterService implements IPythonPathUpdaterServiceManager { @@ -15,6 +19,7 @@ export class PythonPathUpdaterService implements IPythonPathUpdaterServiceManage @inject(IPythonPathUpdaterServiceFactory) private readonly pythonPathSettingsUpdaterFactory: IPythonPathUpdaterServiceFactory, @inject(IComponentAdapter) private readonly pyenvs: IComponentAdapter, + @inject(IRecommendedEnvironmentService) private readonly preferredEnvService: IRecommendedEnvironmentService, ) {} public async updatePythonPath( @@ -28,6 +33,9 @@ export class PythonPathUpdaterService implements IPythonPathUpdaterServiceManage let failed = false; try { await pythonPathUpdater.updatePythonPath(pythonPath); + if (trigger === 'ui') { + this.preferredEnvService.trackUserSelectedEnvironment(pythonPath, wkspace); + } } catch (err) { failed = true; const reason = err as Error; diff --git a/src/client/interpreter/configuration/recommededEnvironmentService.ts b/src/client/interpreter/configuration/recommededEnvironmentService.ts new file mode 100644 index 000000000000..67517b918ff9 --- /dev/null +++ b/src/client/interpreter/configuration/recommededEnvironmentService.ts @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { inject, injectable } from 'inversify'; +import { IRecommendedEnvironmentService } from './types'; +import { PythonExtension } from '../../api/types'; +import { IExtensionContext, Resource } from '../../common/types'; +import { Uri, workspace } from 'vscode'; +import { getWorkspaceStateValue, updateWorkspaceStateValue } from '../../common/persistentState'; +import { traceError } from '../../logging'; + +const MEMENTO_KEY = 'userSelectedEnvPath'; + +@injectable() +export class RecommendedEnvironmentService implements IRecommendedEnvironmentService { + private api?: PythonExtension['environments']; + constructor(@inject(IExtensionContext) private readonly extensionContext: IExtensionContext) {} + + registerEnvApi(api: PythonExtension['environments']) { + this.api = api; + } + + trackUserSelectedEnvironment(environmentPath: string | undefined, uri: Uri | undefined) { + if (workspace.workspaceFolders?.length) { + try { + void updateWorkspaceStateValue(MEMENTO_KEY, getDataToStore(environmentPath, uri)); + } catch (ex) { + traceError('Failed to update workspace state for preferred environment', ex); + } + } else { + void this.extensionContext.globalState.update(MEMENTO_KEY, environmentPath); + } + } + + getRecommededEnvironment( + resource: Resource, + ): + | { environmentPath: string; reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended' } + | undefined { + let workspaceState: string | undefined = undefined; + try { + workspaceState = getWorkspaceStateValue(MEMENTO_KEY); + } catch (ex) { + traceError('Failed to get workspace state for preferred environment', ex); + } + + if (workspace.workspaceFolders?.length && workspaceState) { + const workspaceUri = ( + (resource ? workspace.getWorkspaceFolder(resource)?.uri : undefined) || + workspace.workspaceFolders[0].uri + ).toString(); + + try { + const existingJson: Record = JSON.parse(workspaceState); + const selectedEnvPath = existingJson[workspaceUri]; + if (selectedEnvPath) { + return { environmentPath: selectedEnvPath, reason: 'workspaceUserSelected' }; + } + } catch (ex) { + traceError('Failed to parse existing workspace state value for preferred environment', ex); + } + } + + const globalSelectedEnvPath = this.extensionContext.globalState.get(MEMENTO_KEY); + if (globalSelectedEnvPath) { + return { environmentPath: globalSelectedEnvPath, reason: 'globalUserSelected' }; + } + return this.api && workspace.isTrusted + ? { + environmentPath: this.api.getActiveEnvironmentPath(resource).path, + reason: 'defaultRecommended', + } + : undefined; + } +} + +function getDataToStore(environmentPath: string | undefined, uri: Uri | undefined): string | undefined { + if (!workspace.workspaceFolders?.length) { + return environmentPath; + } + const workspaceUri = ( + (uri ? workspace.getWorkspaceFolder(uri)?.uri : undefined) || workspace.workspaceFolders[0].uri + ).toString(); + const existingData = getWorkspaceStateValue(MEMENTO_KEY); + if (!existingData) { + return JSON.stringify(environmentPath ? { [workspaceUri]: environmentPath } : {}); + } + try { + const existingJson: Record = JSON.parse(existingData); + if (environmentPath) { + existingJson[workspaceUri] = environmentPath; + } else { + delete existingJson[workspaceUri]; + } + return JSON.stringify(existingJson); + } catch (ex) { + traceError('Failed to parse existing workspace state value for preferred environment', ex); + return JSON.stringify({ + [workspaceUri]: environmentPath, + }); + } +} diff --git a/src/client/interpreter/configuration/types.ts b/src/client/interpreter/configuration/types.ts index 08518d4d12d3..8e8aecdc7f16 100644 --- a/src/client/interpreter/configuration/types.ts +++ b/src/client/interpreter/configuration/types.ts @@ -1,6 +1,7 @@ import { ConfigurationTarget, Disposable, QuickPickItem, Uri } from 'vscode'; import { Resource } from '../../common/types'; import { PythonEnvironment } from '../../pythonEnvironments/info'; +import { PythonExtension } from '../../api/types'; export interface IPythonPathUpdaterService { updatePythonPath(pythonPath: string | undefined): Promise; @@ -96,3 +97,14 @@ export interface IInterpreterQuickPick { params?: InterpreterQuickPickParams, ): Promise; } + +export const IRecommendedEnvironmentService = Symbol('IRecommendedEnvironmentService'); +export interface IRecommendedEnvironmentService { + registerEnvApi(api: PythonExtension['environments']): void; + trackUserSelectedEnvironment(environmentPath: string | undefined, uri: Uri | undefined): void; + getRecommededEnvironment( + resource: Resource, + ): + | { environmentPath: string; reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended' } + | undefined; +} diff --git a/src/client/interpreter/serviceRegistry.ts b/src/client/interpreter/serviceRegistry.ts index 688ef20cf970..1e82b9fec0df 100644 --- a/src/client/interpreter/serviceRegistry.ts +++ b/src/client/interpreter/serviceRegistry.ts @@ -16,12 +16,14 @@ import { InstallPythonViaTerminal } from './configuration/interpreterSelector/co import { ResetInterpreterCommand } from './configuration/interpreterSelector/commands/resetInterpreter'; import { SetInterpreterCommand } from './configuration/interpreterSelector/commands/setInterpreter'; import { InterpreterSelector } from './configuration/interpreterSelector/interpreterSelector'; +import { RecommendedEnvironmentService } from './configuration/recommededEnvironmentService'; import { PythonPathUpdaterService } from './configuration/pythonPathUpdaterService'; import { PythonPathUpdaterServiceFactory } from './configuration/pythonPathUpdaterServiceFactory'; import { IInterpreterComparer, IInterpreterQuickPick, IInterpreterSelector, + IRecommendedEnvironmentService, IPythonPathUpdaterServiceFactory, IPythonPathUpdaterServiceManager, } from './configuration/types'; @@ -59,6 +61,10 @@ export function registerInterpreterTypes(serviceManager: IServiceManager): void IExtensionSingleActivationService, ResetInterpreterCommand, ); + serviceManager.addSingleton( + IRecommendedEnvironmentService, + RecommendedEnvironmentService, + ); serviceManager.addSingleton(IInterpreterQuickPick, SetInterpreterCommand); serviceManager.addSingleton(IExtensionActivationService, VirtualEnvironmentPrompt); diff --git a/src/client/jupyter/jupyterIntegration.ts b/src/client/jupyter/jupyterIntegration.ts index a80c93916f3f..b81542806daf 100644 --- a/src/client/jupyter/jupyterIntegration.ts +++ b/src/client/jupyter/jupyterIntegration.ts @@ -12,7 +12,11 @@ import { IContextKeyManager, IWorkspaceService } from '../common/application/typ import { JUPYTER_EXTENSION_ID, PYLANCE_EXTENSION_ID } from '../common/constants'; import { GLOBAL_MEMENTO, IExtensions, IMemento, Resource } from '../common/types'; import { IEnvironmentActivationService } from '../interpreter/activation/types'; -import { IInterpreterQuickPickItem, IInterpreterSelector } from '../interpreter/configuration/types'; +import { + IInterpreterQuickPickItem, + IInterpreterSelector, + IRecommendedEnvironmentService, +} from '../interpreter/configuration/types'; import { ICondaService, IInterpreterDisplay, @@ -24,7 +28,6 @@ import { ExtensionContextKey } from '../common/application/contextKeys'; import { getDebugpyPath } from '../debugger/pythonDebugger'; import type { Environment, EnvironmentPath, PythonExtension } from '../api/types'; import { DisposableBase } from '../common/utils/resourceLifecycle'; -import { getLastEnvUsedByTool } from '../chat/lastUsedEnvs'; type PythonApiForJupyterExtension = { /** @@ -66,9 +69,17 @@ type PythonApiForJupyterExtension = { registerJupyterPythonPathFunction(func: (uri: Uri) => Promise): void; /** - * Returns the Environment that was last used in a Python tool. + * Returns the preferred environment for the given URI. */ - getLastUsedEnvInLmTool(uri: Uri): EnvironmentPath | undefined; + getRecommededEnvironment( + uri: Uri, + ): Promise< + | { + environment: EnvironmentPath; + reason: 'globalUserSelected' | 'workspaceUserSelected' | 'defaultRecommended'; + } + | undefined + >; }; type JupyterExtensionApi = { @@ -96,6 +107,7 @@ export class JupyterExtensionIntegration { @inject(ICondaService) private readonly condaService: ICondaService, @inject(IContextKeyManager) private readonly contextManager: IContextKeyManager, @inject(IInterpreterService) private interpreterService: IInterpreterService, + @inject(IRecommendedEnvironmentService) private preferredEnvironmentService: IRecommendedEnvironmentService, ) {} public registerEnvApi(api: PythonExtension['environments']) { this.environmentApi = api; @@ -131,11 +143,18 @@ export class JupyterExtensionIntegration { getCondaVersion: () => this.condaService.getCondaVersion(), registerJupyterPythonPathFunction: (func: (uri: Uri) => Promise) => this.registerJupyterPythonPathFunction(func), - getLastUsedEnvInLmTool: (uri) => { + getRecommededEnvironment: async (uri) => { if (!this.environmentApi) { return undefined; } - return getLastEnvUsedByTool(uri, this.environmentApi); + const preferred = this.preferredEnvironmentService.getRecommededEnvironment(uri); + if (!preferred) { + return undefined; + } + const environment = workspace.isTrusted + ? await this.environmentApi.resolveEnvironment(preferred.environmentPath) + : undefined; + return environment ? { environment, reason: preferred.reason } : undefined; }, }); return undefined; From a4d744621645fd26470ea4da750254bfbc07bf76 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 20 May 2025 10:34:47 +1000 Subject: [PATCH 2/4] Updates --- src/test/interpreters/serviceRegistry.unit.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/interpreters/serviceRegistry.unit.test.ts b/src/test/interpreters/serviceRegistry.unit.test.ts index a189696c2f82..ad8614b42d8b 100644 --- a/src/test/interpreters/serviceRegistry.unit.test.ts +++ b/src/test/interpreters/serviceRegistry.unit.test.ts @@ -27,6 +27,7 @@ import { IInterpreterSelector, IPythonPathUpdaterServiceFactory, IPythonPathUpdaterServiceManager, + IRecommendedEnvironmentService, } from '../../client/interpreter/configuration/types'; import { IActivatedEnvironmentLaunch, @@ -44,6 +45,7 @@ import { CondaInheritEnvPrompt } from '../../client/interpreter/virtualEnvs/cond import { VirtualEnvironmentPrompt } from '../../client/interpreter/virtualEnvs/virtualEnvPrompt'; import { ServiceManager } from '../../client/ioc/serviceManager'; import { InterpreterPathCommand } from '../../client/interpreter/interpreterPathCommand'; +import { RecommendedEnvironmentService } from '../../client/interpreter/configuration/recommededEnvironmentService'; suite('Interpreters - Service Registry', () => { test('Registrations', () => { @@ -64,6 +66,7 @@ suite('Interpreters - Service Registry', () => { [IPythonPathUpdaterServiceFactory, PythonPathUpdaterServiceFactory], [IPythonPathUpdaterServiceManager, PythonPathUpdaterService], + [IRecommendedEnvironmentService, RecommendedEnvironmentService], [IInterpreterSelector, InterpreterSelector], [IInterpreterHelper, InterpreterHelper], [IInterpreterComparer, EnvironmentTypeComparer], From 9a0129da207878fb7c04b52edd887a96cfb3fe4e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 20 May 2025 10:40:29 +1000 Subject: [PATCH 3/4] Updates --- src/test/debugger/envVars.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/debugger/envVars.test.ts b/src/test/debugger/envVars.test.ts index ae21c7fd5d49..8b0f55986281 100644 --- a/src/test/debugger/envVars.test.ts +++ b/src/test/debugger/envVars.test.ts @@ -16,6 +16,8 @@ import { isOs, OSType } from '../common'; import { closeActiveWindows, initialize, initializeTest, IS_MULTI_ROOT_TEST, TEST_DEBUGGER } from '../initialize'; import { UnitTestIocContainer } from '../testing/serviceRegistry'; import { normCase } from '../../client/common/platform/fs-paths'; +import { IRecommendedEnvironmentService } from '../../client/interpreter/configuration/types'; +import { RecommendedEnvironmentService } from '../../client/interpreter/configuration/recommededEnvironmentService'; use(chaiAsPromised.default); @@ -53,6 +55,10 @@ suite('Resolving Environment Variables when Debugging', () => { ioc.registerFileSystemTypes(); ioc.registerVariableTypes(); ioc.registerMockProcess(); + ioc.serviceManager.addSingleton( + IRecommendedEnvironmentService, + RecommendedEnvironmentService, + ); } async function testBasicProperties(console: ConsoleType, expectedNumberOfVariables: number) { From de358976354fc0ec854e383fa9e1b7273a3f42c2 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 20 May 2025 10:48:47 +1000 Subject: [PATCH 4/4] Fixes --- src/client/extension.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index b9db9a258ec6..af26a5657330 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -44,7 +44,6 @@ import { ProposedExtensionAPI } from './proposedApiTypes'; import { buildProposedApi } from './proposedApi'; import { GLOBAL_PERSISTENT_KEYS } from './common/persistentState'; import { registerTools } from './chat'; -import { RecommendedEnvironmentService } from './interpreter/configuration/recommededEnvironmentService'; import { IRecommendedEnvironmentService } from './interpreter/configuration/types'; durations.codeLoadingTime = stopWatch.elapsedTime; @@ -167,7 +166,7 @@ async function activateUnsafe( const proposedApi = buildProposedApi(components.pythonEnvs, ext.legacyIOC.serviceContainer); registerTools(context, components.pythonEnvs, api.environments, ext.legacyIOC.serviceContainer); ext.legacyIOC.serviceContainer - .get(RecommendedEnvironmentService) + .get(IRecommendedEnvironmentService) .registerEnvApi(api.environments); return [{ ...api, ...proposedApi }, activationPromise, ext.legacyIOC.serviceContainer]; }