Skip to content

Diagnose-w-ai #629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@
"main": "./dist/extension.js",
"icon": "icon.png",
"contributes": {
"chatParticipants": [
{
"name": "pythonHelper",
"id": "python-helper",
"description": "%chatParticipants.pythonHelper.description%",
"isSticky": true,
"when": "config.python.useEnvironmentsExtension != false"
}
],
"configuration": {
"properties": {
"python-envs.defaultEnvManager": {
Expand Down Expand Up @@ -586,6 +595,8 @@
"fs-extra": "^11.2.0",
"stack-trace": "0.0.10",
"vscode-jsonrpc": "^9.0.0-next.5",
"which": "^4.0.0"
"which": "^4.0.0",
"@vscode/chat-extension-utils": "^0.0.0-alpha.1",
"@vscode/prompt-tsx": "^0.3.0-alpha.12"
}
}
3 changes: 2 additions & 1 deletion package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@
"python-envs.terminal.deactivate.title": "Deactivate Environment in Current Terminal",
"python-envs.uninstallPackage.title": "Uninstall Package",
"python-envs.revealProjectInExplorer.title": "Reveal Project in Explorer",
"python-envs.runPetInTerminal.title": "Run Python Environment Tool (PET) in Terminal"
"python-envs.runPetInTerminal.title": "Run Python Environment Tool (PET) in Terminal",
"chatParticipants.pythonHelper.description": "EXPERIMENTAL: Use natural language to learn about Python environments and diagnose issues."
}
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
onDidChangeTerminalShellIntegration,
} from './common/window.apis';
import { getConfiguration } from './common/workspace.apis';
import { registerChatParticipant } from './features/chat/chatParticipant';
import { createManagerReady } from './features/common/managerReady';
import { AutoFindProjects } from './features/creators/autoFindProjects';
import { ExistingProjects } from './features/creators/existingProjects';
Expand Down Expand Up @@ -233,6 +234,7 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
shellEnvsProviders,
api,
);
registerChatParticipant(context);

context.subscriptions.push(
shellStartupVarsMgr,
Expand Down
153 changes: 153 additions & 0 deletions src/features/chat/chatParticipant.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { traceInfo, traceWarn } from '../../common/logging';
import * as vscode from 'vscode';
import { PythonEnvsPrompt, PythonEnvsPromptProps } from './prompts';
import { PythonHelperChatParticipant } from './pythonHelperChatParticipant';
import { PromptElement, renderPrompt, UserMessage } from '@vscode/prompt-tsx';

Check failure on line 5 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (ubuntu-latest)

'UserMessage' is declared but its value is never read.

Check failure on line 5 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (ubuntu-latest)

'PromptElement' is declared but its value is never read.

Check failure on line 5 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (windows-latest)

'UserMessage' is declared but its value is never read.

Check failure on line 5 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (windows-latest)

'PromptElement' is declared but its value is never read.
import { LanguageModelTextPart, LanguageModelToolCallPart } from 'vscode';
import { ToolCallRound, ToolResultMetadata } from '@vscode/chat-extension-utils/dist/toolsPrompt';

export const CHAT_PARTICIPANT_ID = 'python-helper';
export const CHAT_PARTICIPANT_AT_MENTION = `@${CHAT_PARTICIPANT_ID}`;

export function registerChatParticipant(_context: vscode.ExtensionContext) {
traceInfo('Registering python helper chat participant'); // Log registration start

// Define the request handler for the chat participant
const requestHandler: vscode.ChatRequestHandler = async (
request: vscode.ChatRequest, // prompt made in pip file
chatContext: vscode.ChatContext, // history

Check failure on line 18 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (ubuntu-latest)

'chatContext' is declared but its value is never read.

Check failure on line 18 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (windows-latest)

'chatContext' is declared but its value is never read.
stream: vscode.ChatResponseStream,
token: vscode.CancellationToken,
) => {
traceWarn('Python helper chat participant invoked with request:', request); // Log request details

// gather the available tools
const first100tools = vscode.lm.tools.slice(0, 100);
const tools: vscode.LanguageModelChatTool[] = first100tools.map((tool): vscode.LanguageModelChatTool => {
return {
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema ?? {},
};
});
traceInfo('Tools prepared:', tools); // Log tools

const userPrompt = request.prompt;
traceInfo('User prompt received:', userPrompt); // Log user prompt

const refTools = request.toolReferences;
const refToolInvToken = request.toolInvocationToken;
const refRef = request.references;
const refCom = request.command;
const refPrompt = request.prompt;
let model = request.model;
traceInfo('References received:', refTools, refToolInvToken, refRef, refCom, refPrompt, model); // Log references

// takes the info and creates a prompt using the PythonEnvsPrompt
const result = await renderPrompt<PythonEnvsPromptProps>(
PythonEnvsPrompt, // Extract the constructor PythonEnvsPromptProps
{
title: 'Python Environment',
description: 'Provide information about the Python environment.',
request: {
prompt: '' + userPrompt + ' What is the current Python environment? What packages are installed?',
},
},
{ modelMaxPromptTokens: model.maxInputTokens },
model,
);

// result of building the prompt
let messages = result.messages;

const options: vscode.LanguageModelChatRequestOptions = {
justification: 'To make a request to @toolsTSX',
tools: tools,
};

const toolReferences = [...request.toolReferences];
const accumulatedToolResults: Record<string, vscode.LanguageModelToolResult> = {};
const toolCallRounds: ToolCallRound[] = [];
const runWithTools = async (): Promise<void> => {
const requestedTool = toolReferences.shift();

if (requestedTool) {
// NOT WORKING::: If a toolReference is present, force the model to call that tool
options.toolMode = vscode.LanguageModelChatToolMode.Required;
options.tools = vscode.lm.tools.filter((tool) => tool.name === requestedTool.name);
} else {
options.toolMode = undefined;
options.tools = [...tools];
}
console.log('Requested tool:', requestedTool); // Log requested tool

// Send the request to the model
const response = await model.sendRequest(messages, options, token);
traceInfo('Chat participant response sent:', response); // Log response

// Stream the response back to VS Code
let responseStr = '';
const toolCalls: vscode.LanguageModelToolCallPart[] = [];

for await (const chunk of response.stream) {
if (chunk instanceof LanguageModelTextPart) {
stream.markdown(chunk.value);
responseStr += chunk.value; // Accumulate the response string
} else if (chunk instanceof LanguageModelToolCallPart) {
// If the response contains vscode.LanguageModelToolCallPart, then you should re-send the prompt with a ToolCall element for each of those.
console.log('TOOL CALL', chunk);
toolCalls.push(chunk);
}
}

if (toolCalls.length) {
traceInfo('Tool calls detected:', toolCalls); // Log tool calls

// If the model called any tools, then we do another round- render the prompt with those tool calls (rendering the PromptElements will invoke the tools)
// and include the tool results in the prompt for the next request.
toolCallRounds.push({
response: responseStr,
toolCalls,
});

const result = await renderPrompt<PythonEnvsPromptProps>(
PythonEnvsPrompt, // Extract the constructor PythonEnvsPromptProps
{
title: 'Python Environment',
description: 'Provide information about the Python environment.',
request: {
prompt:
'' +
userPrompt +
' What is the current Python environment? What packages are installed?',
},
},
{ modelMaxPromptTokens: model.maxInputTokens },
model,
);

// result of building the prompt
let messages = result.messages;

Check failure on line 130 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (ubuntu-latest)

'messages' is declared but its value is never read.

Check failure on line 130 in src/features/chat/chatParticipant.tsx

View workflow job for this annotation

GitHub Actions / TypeScript Unit Tests (windows-latest)

'messages' is declared but its value is never read.
const toolResultMetadata = result.metadatas.getAll(ToolResultMetadata);
if (toolResultMetadata?.length) {
// Cache tool results for later, so they can be incorporated into later prompts without calling the tool again
toolResultMetadata.forEach((meta) => (accumulatedToolResults[meta.toolCallId] = meta.result));
}

// This loops until the model doesn't want to call any more tools, then the request is done.
return runWithTools();
}
};
await runWithTools(); // Ensure tools are run before proceeding

// end of request handler
};

const participant = vscode.chat.createChatParticipant(CHAT_PARTICIPANT_ID, requestHandler);
participant.iconPath = new vscode.ThemeIcon('python');

traceInfo('Chat participant created and registered'); // Log participant creation

// Register using our singleton manager
return PythonHelperChatParticipant.register(participant, _context);
}
109 changes: 109 additions & 0 deletions src/features/chat/prompts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { BasePromptElementProps, PromptElement, PromptSizing, UserMessage } from '@vscode/prompt-tsx';
import { ChatEnvironmentErrorInfo, ERROR_CHAT_CONTEXT_QUEUE } from './send_prompt';
import { traceWarn } from '../../common/logging';

export interface PythonEnvsPromptProps extends BasePromptElementProps {
title: string;
description?: string;
request: {
prompt: string;
};
response?: {
content: string;
error?: string;
};

}

export class PythonEnvsPrompt extends PromptElement<PythonEnvsPromptProps, void> {
render(_state: void, _sizing: PromptSizing) {
traceWarn('Render function called'); // Log when render is invoked

// this.props is PythonEnvsPromptProps
const isContext = !ERROR_CHAT_CONTEXT_QUEUE.isEmpty();
traceWarn('Context queue is empty:', !isContext); // Log the state of the context queue

if (!isContext) {
traceWarn('No context found for python helper chat participant');
return;
}

const contextErr: ChatEnvironmentErrorInfo | undefined = ERROR_CHAT_CONTEXT_QUEUE.pop();
traceWarn('Popped context error:', contextErr); // Log the popped context error

if (!contextErr) {
traceWarn('No context error found for python helper chat participant');
return;
}

const attemptedPackages = contextErr.attemptedPackages.join(', ');
const packagesBeforeInstall = contextErr.packagesBeforeInstall.join(', ');
traceWarn('Attempted packages:', attemptedPackages); // Log attempted packages
traceWarn('Packages before install:', packagesBeforeInstall); // Log packages before install

const envString = contextErr.environment.displayName + ' (' + contextErr.environment.environmentPath + ') ' + contextErr.environment.version;
traceWarn('Environment string:', envString); // Log environment string

const rawPrompt = this.props.request.prompt .replace(/^@pythonHelper\s*/, '');
traceWarn('Raw prompt:', rawPrompt); // Log the raw prompt

// let errorInfo: ChatEnvironmentErrorInfo | undefined;
// try {
// errorInfo = JSON.stringify(rawPrompt);
// traceWarn('Parsed error info:', errorInfo); // Log parsed error info
// } catch (e) {
// console.error('Error parsing raw prompt the prompt:', rawPrompt);
// traceWarn('Error parsing raw prompt:', e); // Log parsing error
// }
const msg = <>
<UserMessage priority={100}>🚨 **Package Management Error**##<br /> ❗ Error Details<br />``` <br /> {contextErr.errorMessage}<br />```<br />Stack Trace<br />```<br />{contextErr.stackTrace}<br />```<br />## 📝 **Context**<br />**Attempted Packages:**{attemptedPackages}<br /><br /> **Package Manager:** {contextErr.packageManager}<br /> **Environment:** {envString}<br /> **Packages Before Install:** {packagesBeforeInstall}<br />## 🛠️ **How to Diagnose & Fix**<br />1. **Diagnose the error above.**<br />2. **Suggest a fix.**<br />3. Use the following tools where applicable:<br />- `installPythonPackage`: Install a package with a version specification, etc.<br /> -`configurePythonEnvironment`: Create an environment with the given attributes.<br /> If you see one best path forward, start doing that WITHOUT asking the user again. If you see 2 to 3 paths to solve this problem, reply with the two paths like so: `button1: prompt solution` `button2: prompt solution</UserMessage>
</>

traceWarn('Returning message final:', msg); // Log the final message to be returned
return msg;

// <UserMessage priority={69}><br /> **Attempted Packages:**{attemptedPackages}<br /> **Package Manager:** {contextErr.packageManager}<br /> **Environment:** {contextErr.environment}<br /> **Packages Before Install:** {contextErr.packagesBeforeInstall}</UserMessage>
// <UserMessage priority={67}><br /> **Environment:** {envString}<br /></UserMessage>

// <UserMessage priority={69}><br /> **Attempted Packages:**{attemptedPackages}<br /> **Package Manager:** {contextErr.packageManager}<br /> **Environment:** {contextErr.environment}<br /> **Packages Before Install:** {contextErr.packagesBeforeInstall}</UserMessage>
// <UserMessage priority={60}>## 🛠️ **How to Diagnose & Fix**</UserMessage>
// <UserMessage priority={59}><br />1. **Diagnose the error above.**<br />2. **Suggest a fix.**<br />3. Use the following tools where applicable:<br /> - `installPythonPackage`: Install a package with a version specification, etc.<br /> -`configurePythonEnvironment`: Create an environment with the given attributes.<br /> - `getPythonEnvironmentInfo`: Get more information on the user setup if needed.</UserMessage>
// return (
// <>
// <UserMessage priority={100}>🚨 **Package Management Error**</UserMessage>

// <UserMessage priority={90}>## ❗ Error Details</UserMessage>
// <UserMessage priority={89}>```
// {contextErr.errorMessage}
// ```</UserMessage>

// <UserMessage priority={80}>
// Stack Trace
// ```
// {contextErr.stackTrace}
// ```
// </UserMessage>

// <UserMessage priority={70}>## 📝 **Context**</UserMessage>
// <UserMessage priority={69}>
// - **Attempted Packages:** {contextErr.attemptedPackages.join(', ')}
// - **Package Manager:** {contextErr.packageManager}
// - **Environment:** {contextErr.environment}
// - **Packages Before Install:** {contextErr.packagesBeforeInstall}
// </UserMessage>

// <UserMessage priority={60}>## 🛠️ **How to Diagnose & Fix**</UserMessage>
// <UserMessage priority={59}>
// 1. **Diagnose the error above.**
// 2. **Suggest a fix.**
// 3. Use the following tools where applicable:
// - `installPythonPackage`: Install a package with a version specification, etc.
// - `configurePythonEnvironment`: Create an environment with the given attributes.
// - `getPythonEnvironmentInfo`: Get more information on the user setup if needed.

// If you see multiple equally valid paths forward, offer the user a choice in the format:
// </UserMessage>
// </>
// );
}
}
Loading
Loading