Skip to content

Commit 153e8e7

Browse files
committed
Handle cases when failing to resolve env
1 parent 4a602e8 commit 153e8e7

File tree

5 files changed

+110
-50
lines changed

5 files changed

+110
-50
lines changed

src/client/chat/configurePythonEnvTool.ts

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { PythonExtension, ResolvedEnvironment } from '../api/types';
1919
import { IServiceContainer } from '../ioc/types';
2020
import { ICodeExecutionService } from '../terminals/types';
2121
import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/terminalCodeExecution';
22-
import { getEnvironmentDetails, raceCancellationError } from './utils';
22+
import { getEnvironmentDetails, NoEnvironmentError, raceCancellationError } from './utils';
2323
import { resolveFilePath } from './utils';
2424
import { IRecommendedEnvironmentService } from '../interpreter/configuration/types';
2525
import { ITerminalHelper } from '../common/terminal/types';
@@ -67,33 +67,51 @@ export class ConfigurePythonEnvTool implements LanguageModelTool<IResourceRefere
6767
options: LanguageModelToolInvocationOptions<IResourceReference>,
6868
token: CancellationToken,
6969
): Promise<LanguageModelToolResult> {
70-
const resource = resolveFilePath(options.input.resourcePath);
71-
const recommededEnv = await this.recommendedEnvService.getRecommededEnvironment(resource);
72-
// Already selected workspace env, hence nothing to do.
73-
if (recommededEnv?.reason === 'workspaceUserSelected' && workspace.workspaceFolders?.length) {
74-
return await getEnvDetailsForResponse(
75-
recommededEnv.environment,
76-
this.api,
77-
this.terminalExecutionService,
78-
this.terminalHelper,
79-
resource,
80-
token,
81-
);
82-
}
83-
// No workspace folders, and the user selected a global environment.
84-
if (recommededEnv?.reason === 'globalUserSelected' && !workspace.workspaceFolders?.length) {
85-
return await getEnvDetailsForResponse(
86-
recommededEnv.environment,
87-
this.api,
88-
this.terminalExecutionService,
89-
this.terminalHelper,
90-
resource,
91-
token,
92-
);
93-
}
70+
try {
71+
const resource = resolveFilePath(options.input.resourcePath);
72+
const recommededEnv = await this.recommendedEnvService.getRecommededEnvironment(resource);
73+
// Already selected workspace env, hence nothing to do.
74+
if (recommededEnv?.reason === 'workspaceUserSelected' && workspace.workspaceFolders?.length) {
75+
return await getEnvDetailsForResponse(
76+
recommededEnv.environment,
77+
this.api,
78+
this.terminalExecutionService,
79+
this.terminalHelper,
80+
resource,
81+
token,
82+
);
83+
}
84+
// No workspace folders, and the user selected a global environment.
85+
if (recommededEnv?.reason === 'globalUserSelected' && !workspace.workspaceFolders?.length) {
86+
return await getEnvDetailsForResponse(
87+
recommededEnv.environment,
88+
this.api,
89+
this.terminalExecutionService,
90+
this.terminalHelper,
91+
resource,
92+
token,
93+
);
94+
}
9495

95-
if (!workspace.workspaceFolders?.length) {
96-
const selected = await Promise.resolve(commands.executeCommand(Commands.Set_Interpreter));
96+
if (!workspace.workspaceFolders?.length) {
97+
const selected = await Promise.resolve(commands.executeCommand(Commands.Set_Interpreter));
98+
const env = await this.api.resolveEnvironment(this.api.getActiveEnvironmentPath(resource));
99+
if (selected && env) {
100+
return await getEnvDetailsForResponse(
101+
env,
102+
this.api,
103+
this.terminalExecutionService,
104+
this.terminalHelper,
105+
resource,
106+
token,
107+
);
108+
}
109+
return new LanguageModelToolResult([
110+
new LanguageModelTextPart('User did not select a Python environment.'),
111+
]);
112+
}
113+
114+
const selected = await showCreateAndSelectEnvironmentQuickPick(resource, this.serviceContainer);
97115
const env = await this.api.resolveEnvironment(this.api.getActiveEnvironmentPath(resource));
98116
if (selected && env) {
99117
return await getEnvDetailsForResponse(
@@ -106,25 +124,18 @@ export class ConfigurePythonEnvTool implements LanguageModelTool<IResourceRefere
106124
);
107125
}
108126
return new LanguageModelToolResult([
109-
new LanguageModelTextPart('User did not select a Python environment.'),
127+
new LanguageModelTextPart('User did not create nor select a Python environment.'),
110128
]);
129+
} catch (ex) {
130+
if (ex instanceof NoEnvironmentError) {
131+
return new LanguageModelToolResult([
132+
new LanguageModelTextPart(
133+
'Failed to configure a Python Environment, as the environment could not be found.',
134+
),
135+
]);
136+
}
137+
throw ex;
111138
}
112-
113-
const selected = await showCreateAndSelectEnvironmentQuickPick(resource, this.serviceContainer);
114-
const env = await this.api.resolveEnvironment(this.api.getActiveEnvironmentPath(resource));
115-
if (selected && env) {
116-
return await getEnvDetailsForResponse(
117-
env,
118-
this.api,
119-
this.terminalExecutionService,
120-
this.terminalHelper,
121-
resource,
122-
token,
123-
);
124-
}
125-
return new LanguageModelToolResult([
126-
new LanguageModelTextPart('User did not create nor select a Python environment.'),
127-
]);
128139
}
129140

130141
async prepareInvocation?(

src/client/chat/getExecutableTool.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { PythonExtension } from '../api/types';
1616
import { IServiceContainer } from '../ioc/types';
1717
import { ICodeExecutionService } from '../terminals/types';
1818
import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/terminalCodeExecution';
19-
import { getEnvDisplayName, getEnvironmentDetails, raceCancellationError } from './utils';
19+
import { getEnvDisplayName, getEnvironmentDetails, NoEnvironmentError, raceCancellationError } from './utils';
2020
import { resolveFilePath } from './utils';
2121
import { traceError } from '../logging';
2222
import { ITerminalHelper } from '../common/terminal/types';
@@ -58,6 +58,14 @@ export class GetExecutableTool implements LanguageModelTool<IResourceReference>
5858
);
5959
return new LanguageModelToolResult([new LanguageModelTextPart(message)]);
6060
} catch (error) {
61+
if (error instanceof NoEnvironmentError) {
62+
return new LanguageModelToolResult([
63+
new LanguageModelTextPart(
64+
'Failed to configure a Python Environment, as the environment could not be found.',
65+
),
66+
]);
67+
}
68+
6169
if (error instanceof CancellationError) {
6270
throw error;
6371
}

src/client/chat/getPythonEnvTool.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { IServiceContainer } from '../ioc/types';
1717
import { ICodeExecutionService } from '../terminals/types';
1818
import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/terminalCodeExecution';
1919
import { IProcessServiceFactory, IPythonExecutionFactory } from '../common/process/types';
20-
import { getEnvironmentDetails, raceCancellationError } from './utils';
20+
import { getEnvironmentDetails, NoEnvironmentError, raceCancellationError } from './utils';
2121
import { resolveFilePath } from './utils';
2222
import { getPythonPackagesResponse } from './listPackagesTool';
2323
import { ITerminalHelper } from '../common/terminal/types';
@@ -82,6 +82,13 @@ export class GetEnvironmentInfoTool implements LanguageModelTool<IResourceRefere
8282

8383
return new LanguageModelToolResult([new LanguageModelTextPart(message)]);
8484
} catch (error) {
85+
if (error instanceof NoEnvironmentError) {
86+
return new LanguageModelToolResult([
87+
new LanguageModelTextPart(
88+
'Failed to configure a Python Environment, as the environment could not be found.',
89+
),
90+
]);
91+
}
8592
if (error instanceof CancellationError) {
8693
throw error;
8794
}

src/client/chat/installPackagesTool.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from 'vscode';
1515
import { PythonExtension } from '../api/types';
1616
import { IServiceContainer } from '../ioc/types';
17-
import { getEnvDisplayName, raceCancellationError } from './utils';
17+
import { getEnvDisplayName, NoEnvironmentError, raceCancellationError } from './utils';
1818
import { resolveFilePath } from './utils';
1919
import { IModuleInstaller } from '../common/installer/types';
2020
import { ModuleInstallerType } from '../pythonEnvironments/info';
@@ -51,7 +51,7 @@ export class InstallPackagesTool implements LanguageModelTool<IInstallPackageArg
5151
const envPath = this.api.getActiveEnvironmentPath(resourcePath);
5252
const environment = await raceCancellationError(this.api.resolveEnvironment(envPath), token);
5353
if (!environment || !environment.version) {
54-
throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath);
54+
throw new NoEnvironmentError('No environment found');
5555
}
5656
const isConda = (environment.environment?.type || '').toLowerCase() === 'conda';
5757
const installers = this.serviceContainer.getAll<IModuleInstaller>(IModuleInstaller);
@@ -70,6 +70,14 @@ export class InstallPackagesTool implements LanguageModelTool<IInstallPackageArg
7070
const resultMessage = `Successfully installed ${packagePlurality}: ${options.input.packageList.join(', ')}`;
7171
return new LanguageModelToolResult([new LanguageModelTextPart(resultMessage)]);
7272
} catch (error) {
73+
if (error instanceof NoEnvironmentError) {
74+
return new LanguageModelToolResult([
75+
new LanguageModelTextPart(
76+
'Failed to configure a Python Environment, as the environment could not be found.',
77+
),
78+
]);
79+
}
80+
7381
if (error instanceof CancellationError) {
7482
throw error;
7583
}

src/client/chat/utils.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
import { CancellationError, CancellationToken, Uri, workspace } from 'vscode';
4+
import {
5+
CancellationError,
6+
CancellationToken,
7+
LanguageModelTextPart,
8+
LanguageModelToolResult,
9+
Uri,
10+
workspace,
11+
} from 'vscode';
512
import { IDiscoveryAPI } from '../pythonEnvironments/base/locator';
613
import { PythonExtension, ResolvedEnvironment } from '../api/types';
714
import { ITerminalHelper, TerminalShellType } from '../common/terminal/types';
@@ -52,6 +59,12 @@ export function isCondaEnv(env: ResolvedEnvironment) {
5259
return (env.environment?.type || '').toLowerCase() === 'conda';
5360
}
5461

62+
export class NoEnvironmentError extends Error {
63+
constructor(message: string) {
64+
super(message);
65+
}
66+
}
67+
5568
export async function getEnvironmentDetails(
5669
resourcePath: Uri | undefined,
5770
api: PythonExtension['environments'],
@@ -64,7 +77,7 @@ export async function getEnvironmentDetails(
6477
const envPath = api.getActiveEnvironmentPath(resourcePath);
6578
const environment = await raceCancellationError(api.resolveEnvironment(envPath), token);
6679
if (!environment || !environment.version) {
67-
throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath);
80+
throw new NoEnvironmentError('No environment found for the provided resource path: ' + resourcePath?.fsPath);
6881
}
6982
const runCommand = await raceCancellationError(
7083
getTerminalCommand(environment, resourcePath, terminalExecutionService, terminalHelper),
@@ -115,3 +128,16 @@ async function getCondaRunCommand(environment: ResolvedEnvironment) {
115128
}
116129
return { command: cmd[0], args: cmd.slice(1) };
117130
}
131+
132+
function throwErrorIfUriIsNotebook(uri: Uri | undefined) {
133+
if (!uri) {
134+
return;
135+
}
136+
if (
137+
uri.scheme === 'vscode-notebook-cell' ||
138+
uri.fsPath.endsWith('.ipynb') ||
139+
workspace.notebookDocuments.some((doc) => doc.uri.fsPath === uri.fsPath)
140+
) {
141+
throw new Error('This tool does not support Notebooks, use a relevant tool instead.');
142+
}
143+
}

0 commit comments

Comments
 (0)