Skip to content

Commit f5edf60

Browse files
committed
Port Python tools, and create a tool to quickly create a virtual env
1 parent 1a96c22 commit f5edf60

15 files changed

Lines changed: 631 additions & 734 deletions

package.json

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
}
2626
},
2727
"activationEvents": [
28-
"onLanguage:python"
28+
"onLanguage:python",
29+
"onLanguageModelTool:get_python_environment_info",
30+
"onLanguageModelTool:get_python_executable_info",
31+
"onLanguageModelTool:install_python_package",
32+
"onLanguageModelTool:create_quick_virtual_environment"
2933
],
3034
"homepage": "https://github.com/microsoft/vscode-python-environments",
3135
"repository": {
@@ -509,15 +513,15 @@
509513
],
510514
"languageModelTools": [
511515
{
512-
"name": "python_environment",
513-
"userDescription": "%python.languageModelTools.python_environment.userDescription%",
514-
"displayName": "Get Python Environment Information",
515-
"modelDescription": "Provides details about the Python environment for a specified file or workspace, including environment type, Python version, run command, and installed packages with their versions. Use this tool to determine the correct command for executing Python code in this workspace.",
516-
"toolReferenceName": "pythonGetEnvironmentInfo",
516+
"name": "get_python_environment_info",
517+
"displayName": "%python.languageModelTools.get_python_environment_info.displayName%",
518+
"userDescription": "%python.languageModelTools.get_python_environment_info.userDescription%",
519+
"modelDescription": "This tool will retrieve the details of the Python Environment for the specified file or workspace. The details returned include the 1. Type of Environment (conda, venv, etec), 2. Version of Python, 3. List of all installed packages with their versions. ",
520+
"toolReferenceName": "pythonEnvironmentDetails",
517521
"tags": [
518-
"ms-python.python"
522+
"extension_installed_by_tool"
519523
],
520-
"icon": "$(files)",
524+
"icon": "$(snake)",
521525
"canBeReferencedInPrompt": true,
522526
"inputSchema": {
523527
"type": "object",
@@ -527,19 +531,40 @@
527531
}
528532
},
529533
"description": "The path to the Python file or workspace to get the environment information for.",
530-
"required": [
531-
"resourcePath"
532-
]
534+
"required": []
533535
}
534536
},
535537
{
536-
"name": "python_install_package",
537-
"displayName": "Install Python Package",
538-
"userDescription": "%python.languageModelTools.python_install_package.userDescription%",
538+
"name": "get_python_executable_info",
539+
"displayName": "%python.languageModelTools.get_python_executable.displayName%",
540+
"userDescription": "%python.languageModelTools.get_python_executable.userDescription%",
541+
"modelDescription": "This tool will retrieve the details of the Python Environment for the specified file or workspace. ALWAYS use this tool before executing any Python command in the terminal. This tool returns the details of how to construct the fully qualified path and or command including details such as arguments required to run Python in a terminal. Note: Instead of executing `python --version` or `python -c 'import sys; print(sys.executable)'`, use this tool to get the Python executable path to replace the `python` command. E.g. instead of using `python -c 'import sys; print(sys.executable)'`, use this tool to build the command `conda run -n <env_name> -c 'import sys; print(sys.executable)'`.",
542+
"toolReferenceName": "pythonExecutableDetails",
543+
"tags": [
544+
"extension_installed_by_tool"
545+
],
546+
"icon": "$(files)",
547+
"canBeReferencedInPrompt": true,
548+
"inputSchema": {
549+
"type": "object",
550+
"properties": {
551+
"resourcePath": {
552+
"type": "string"
553+
}
554+
},
555+
"description": "The path to the Python file or workspace to get the executable information for. If not provided, the current workspace will be used. Where possible pass the path to the file or workspace.",
556+
"required": []
557+
}
558+
},
559+
{
560+
"name": "install_python_package",
561+
"displayName": "%python.languageModelTools.install_python_package.displayName%",
562+
"userDescription": "%python.languageModelTools.install_python_package.userDescription%",
539563
"modelDescription": "Installs Python packages in the given workspace. Use this tool to install packages in the user's chosen environment.",
540-
"toolReferenceName": "pythonInstallPackage",
564+
"toolReferenceName": "installPythonPackage",
541565
"tags": [
542-
"ms-python.python"
566+
"install python package",
567+
"extension_installed_by_tool"
543568
],
544569
"icon": "$(package)",
545570
"canBeReferencedInPrompt": true,
@@ -555,14 +580,31 @@
555580
},
556581
"resourcePath": {
557582
"type": "string",
558-
"description": "The path to the Python file or workspace to get the environment information for."
583+
"description": "The path to the Python file or workspace into which the packages are installed. If not provided, the current workspace will be used. Where possible pass the path to the file or workspace."
559584
}
560585
},
561586
"required": [
562-
"packageList",
563-
"resourcePath"
587+
"packageList"
564588
]
565589
}
590+
},
591+
{
592+
"name": "create_quick_virtual_environment",
593+
"displayName": "Create a Virtual Environment",
594+
"modelDescription": "This tool will create a Virual Environment",
595+
"tags": [],
596+
"canBeReferencedInPrompt": false,
597+
"inputSchema": {
598+
"type": "object",
599+
"properties": {
600+
"resourcePath": {
601+
"type": "string",
602+
"description": "The path to the Python file or workspace for which a Python Environment needs to be configured."
603+
}
604+
},
605+
"required": []
606+
},
607+
"when": "false"
566608
}
567609
]
568610
},

package.nls.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
"python-envs.terminal.activate.title": "Activate Environment in Current Terminal",
3535
"python-envs.terminal.deactivate.title": "Deactivate Environment in Current Terminal",
3636
"python-envs.uninstallPackage.title": "Uninstall Package",
37-
"python.languageModelTools.python_environment.userDescription": "Get Python environment info for a file or path, including version, packages, and the command to run it.",
38-
"python.languageModelTools.python_install_package.userDescription": "Installs Python packages in the given workspace."
37+
"python.languageModelTools.get_python_environment_info.displayName": "Get Python Environment Info",
38+
"python.languageModelTools.get_python_environment_info.userDescription": "Get information for a Python Environment, such as Type, Version, Packages, and more.",
39+
"python.languageModelTools.install_python_package.displayName": "Install Python Package",
40+
"python.languageModelTools.install_python_package.userDescription": "Installs Python packages in a Python Environment.",
41+
"python.languageModelTools.get_python_executable.displayName": "Get Python Executable Info",
42+
"python.languageModelTools.get_python_executable.userDescription": "Get executable info for a Python Environment"
3943
}

src/common/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as path from 'path';
22

33
export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs';
44
export const PYTHON_EXTENSION_ID = 'ms-python.python';
5+
export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter';
56
export const EXTENSION_ROOT_DIR = path.dirname(__dirname);
67

78
export const DEFAULT_PACKAGE_MANAGER_ID = 'ms-python.python:pip';
@@ -25,3 +26,5 @@ export const KNOWN_FILES = [
2526
];
2627

2728
export const KNOWN_TEMPLATE_ENDINGS = ['.j2', '.jinja2'];
29+
30+
export const NotebookCellScheme = 'vscode-notebook-cell';

src/extension.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
onDidChangeTerminalShellIntegration,
1818
} from './common/window.apis';
1919
import { createManagerReady } from './features/common/managerReady';
20-
import { GetEnvironmentInfoTool, InstallPackageTool } from './features/copilotTools';
2120
import { AutoFindProjects } from './features/creators/autoFindProjects';
2221
import { ExistingProjects } from './features/creators/existingProjects';
2322
import { ProjectCreatorsImpl } from './features/creators/projectCreators';
@@ -67,6 +66,12 @@ import { registerSystemPythonFeatures } from './managers/builtin/main';
6766
import { createNativePythonFinder, NativePythonFinder } from './managers/common/nativePythonFinder';
6867
import { registerCondaFeatures } from './managers/conda/main';
6968
import { registerPyenvFeatures } from './managers/pyenv/main';
69+
import { GetEnvironmentInfoTool } from './features/chat/getEnvInfoTool';
70+
import { GetExecutableTool } from './features/chat/getExecutableTool';
71+
import { InstallPackageTool } from './features/chat/installPackagesTool';
72+
import { CreateQuickVirtualEnvironmentTool } from './features/chat/createQuickVenvTool';
73+
import { createDeferred } from './common/utils/deferred';
74+
import { SysPythonManager } from './managers/builtin/sysPythonManager';
7075

7176
export async function activate(context: ExtensionContext): Promise<PythonEnvironmentApi> {
7277
const start = new StopWatch();
@@ -113,7 +118,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
113118

114119
setPythonApi(envManagers, projectManager, projectCreators, terminalManager, envVarManager);
115120
const api = await getPythonApi();
116-
121+
const sysPythonManager = createDeferred<SysPythonManager>();
117122
const managerView = new EnvManagerView(envManagers);
118123
context.subscriptions.push(managerView);
119124

@@ -131,8 +136,19 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
131136
context.subscriptions.push(
132137
shellStartupVarsMgr,
133138
registerCompletionProvider(envManagers),
134-
registerTools('python_environment', new GetEnvironmentInfoTool(api, envManagers)),
135-
registerTools('python_install_package', new InstallPackageTool(api)),
139+
registerTools(
140+
CreateQuickVirtualEnvironmentTool.toolName,
141+
new CreateQuickVirtualEnvironmentTool(
142+
api,
143+
envManagers,
144+
projectManager,
145+
sysPythonManager.promise,
146+
outputChannel,
147+
),
148+
),
149+
registerTools(GetEnvironmentInfoTool.toolName, new GetEnvironmentInfoTool(api, envManagers)),
150+
registerTools(GetExecutableTool.toolName, new GetExecutableTool(api, envManagers)),
151+
registerTools(InstallPackageTool.toolName, new InstallPackageTool(api)),
136152
commands.registerCommand('python-envs.terminal.revertStartupScriptChanges', async () => {
137153
await cleanupStartupScripts(shellStartupProviders);
138154
}),
@@ -292,8 +308,10 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
292308
// This is the finder that is used by all the built in environment managers
293309
const nativeFinder: NativePythonFinder = await createNativePythonFinder(outputChannel, api, context);
294310
context.subscriptions.push(nativeFinder);
311+
const sysMgr = new SysPythonManager(nativeFinder, api, outputChannel);
312+
sysPythonManager.resolve(sysMgr);
295313
await Promise.all([
296-
registerSystemPythonFeatures(nativeFinder, context.subscriptions, outputChannel),
314+
registerSystemPythonFeatures(nativeFinder, context.subscriptions, outputChannel, sysMgr),
297315
registerCondaFeatures(nativeFinder, context.subscriptions, outputChannel),
298316
registerPyenvFeatures(nativeFinder, context.subscriptions),
299317
shellStartupVarsMgr.initialize(),
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import {
5+
CancellationToken,
6+
l10n,
7+
LanguageModelTextPart,
8+
LanguageModelTool,
9+
LanguageModelToolInvocationOptions,
10+
LanguageModelToolInvocationPrepareOptions,
11+
LanguageModelToolResult,
12+
LogOutputChannel,
13+
PreparedToolInvocation,
14+
} from 'vscode';
15+
import { PythonEnvironmentApi } from '../../api';
16+
import { EnvironmentManagers, PythonProjectManager } from '../../internal.api';
17+
import { createAnyEnvironmentCommand } from '../envCommands';
18+
import { getEnvironmentDetails, resolveFilePath } from './utils';
19+
import { SysPythonManager } from '../../managers/builtin/sysPythonManager';
20+
import { ensureGlobalEnv } from '../../managers/builtin/venvUtils';
21+
22+
export interface IResourceReference {
23+
resourcePath?: string;
24+
}
25+
26+
export class CreateQuickVirtualEnvironmentTool implements LanguageModelTool<IResourceReference> {
27+
public static readonly toolName = 'create_quick_virtual_environment';
28+
constructor(
29+
private readonly api: PythonEnvironmentApi,
30+
private readonly envManagers: EnvironmentManagers,
31+
private readonly projectManager: PythonProjectManager,
32+
private readonly sysManager: Promise<SysPythonManager>,
33+
private readonly log: LogOutputChannel,
34+
) {}
35+
async invoke(
36+
options: LanguageModelToolInvocationOptions<IResourceReference>,
37+
token: CancellationToken,
38+
): Promise<LanguageModelToolResult | undefined> {
39+
const resourcePath = resolveFilePath(options.input.resourcePath);
40+
const env = await createAnyEnvironmentCommand(this.envManagers, this.projectManager, {
41+
selectEnvironment: true,
42+
quickCreate: true,
43+
uri: resourcePath,
44+
});
45+
if (env) {
46+
const message = await getEnvironmentDetails(
47+
resourcePath,
48+
env,
49+
this.api,
50+
this.envManagers,
51+
undefined,
52+
token,
53+
);
54+
return new LanguageModelToolResult([new LanguageModelTextPart(message)]);
55+
}
56+
}
57+
58+
async prepareInvocation?(
59+
_options: LanguageModelToolInvocationPrepareOptions<IResourceReference>,
60+
_token: CancellationToken,
61+
): Promise<PreparedToolInvocation> {
62+
let version = '';
63+
try {
64+
const sysMgr = await this.sysManager;
65+
const globals = await sysMgr.getEnvironments('global');
66+
const sortedEnvs = ensureGlobalEnv(globals, this.log);
67+
version = getDisplayVersion(sortedEnvs[0].version);
68+
} catch (ex) {
69+
this.log.error('Failed to get Python version for quick virtual environment creation', ex);
70+
}
71+
72+
return {
73+
confirmationMessages: {
74+
title: l10n.t('Create a Virtual Environment{0}?', version ? ` (${version})` : ''),
75+
message: l10n.t(`Virtual Environments provide the benefit of package isolation and more.`),
76+
},
77+
invocationMessage: l10n.t('Creating a Virtual Environment'),
78+
};
79+
}
80+
}
81+
82+
function getDisplayVersion(version: string): string {
83+
if (!version) {
84+
return '';
85+
}
86+
const parts = version.split('.');
87+
if (parts.length < 3) {
88+
return version;
89+
}
90+
return `${parts[0]}.${parts[1]}.${parts[2]}`;
91+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import {
5+
CancellationToken,
6+
l10n,
7+
LanguageModelTextPart,
8+
LanguageModelTool,
9+
LanguageModelToolInvocationOptions,
10+
LanguageModelToolInvocationPrepareOptions,
11+
LanguageModelToolResult,
12+
PreparedToolInvocation,
13+
} from 'vscode';
14+
import { PythonEnvironmentApi } from '../../api';
15+
import { EnvironmentManagers } from '../../internal.api';
16+
import { getPythonPackagesResponse } from './listPackagesTool';
17+
import { getEnvironmentDetails, getToolResponseIfNotebook, raceCancellationError, resolveFilePath } from './utils';
18+
19+
export interface IResourceReference {
20+
resourcePath?: string;
21+
}
22+
23+
export class GetEnvironmentInfoTool implements LanguageModelTool<IResourceReference> {
24+
public static readonly toolName = 'get_python_environment_info';
25+
constructor(private readonly api: PythonEnvironmentApi, private readonly envManagers: EnvironmentManagers) {}
26+
async invoke(
27+
options: LanguageModelToolInvocationOptions<IResourceReference>,
28+
token: CancellationToken,
29+
): Promise<LanguageModelToolResult> {
30+
const resourcePath = resolveFilePath(options.input.resourcePath);
31+
const notebookResponse = getToolResponseIfNotebook(resourcePath);
32+
if (notebookResponse) {
33+
return notebookResponse;
34+
}
35+
const environment = await raceCancellationError(this.api.getEnvironment(resourcePath), token);
36+
if (!environment) {
37+
throw new Error(`No environment found for the provided resource path ${resourcePath?.fsPath}`);
38+
}
39+
40+
const packages = await getPythonPackagesResponse(environment, this.api, token);
41+
const message = await getEnvironmentDetails(
42+
resourcePath,
43+
undefined,
44+
this.api,
45+
this.envManagers,
46+
packages,
47+
token,
48+
);
49+
50+
return new LanguageModelToolResult([new LanguageModelTextPart(message)]);
51+
}
52+
53+
async prepareInvocation?(
54+
_options: LanguageModelToolInvocationPrepareOptions<IResourceReference>,
55+
_token: CancellationToken,
56+
): Promise<PreparedToolInvocation> {
57+
return {
58+
invocationMessage: l10n.t('Fetching Python environment information'),
59+
};
60+
}
61+
}

0 commit comments

Comments
 (0)