From fe56cc46d3c290edec4cfecd6a50e730732d4ee0 Mon Sep 17 00:00:00 2001 From: Zoe Rizzo Date: Sun, 2 Mar 2025 19:48:18 -0500 Subject: [PATCH 01/10] ask AI turned into drop down --- src/extension.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index 047d52f..ad2c26a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,7 +8,28 @@ export function activate(context: vscode.ExtensionContext) { initSettings(); initTerminal(); + // const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { + // let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) + // if (response !== undefined && response.text !== undefined) { + // vscode.window.showInformationMessage(response.text, { modal: true }); + // } + // }); + const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { + const options = [ + "Ask AI", + "Follow Up" + ]; + + const selectedOption = await vscode.window.showQuickPick(options, { + placeHolder: "Debug or follow up", + canPickMany: false + }); + + if (!selectedOption) { + return; + } + let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) if (response !== undefined && response.text !== undefined) { vscode.window.showInformationMessage(response.text, { modal: true }); From 56dc3c57a894c71fe28cda17449a431fe74659a0 Mon Sep 17 00:00:00 2001 From: Zoe Rizzo Date: Mon, 3 Mar 2025 09:48:08 -0500 Subject: [PATCH 02/10] store responses, outline conditionals to implement follow up --- src/ai/OpenAICaller.ts | 5 ++++- src/extension.ts | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index b5df339..c2c7553 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -41,6 +41,7 @@ export class OpenAICaller implements APICaller { const errorFeedback: AIFeedback = await settings.openai.chat.completions.create({ model: "gpt-4o-mini", + store: true, // store for follow up messages: [ { role: "system", @@ -115,6 +116,7 @@ export class OpenAICaller implements APICaller { return settings.openai.chat.completions.create({ model: "gpt-4o-mini", + store: true, // store for follow up messages: [ { role: "system", @@ -173,11 +175,12 @@ export class OpenAICaller implements APICaller { }); } - followUp(response: AIFeedback): Promise { + async followUp(response: AIFeedback): Promise { let newRequest: AIRequest = {}; let finalResponse: AIFeedback = {request: newRequest, problemFiles: [] }; // TODO: implement this + // given a previous response, follow up (read terminal again) to see if the error has been fixed return new Promise(() => finalResponse); } diff --git a/src/extension.ts b/src/extension.ts index ad2c26a..1862c06 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -8,16 +8,9 @@ export function activate(context: vscode.ExtensionContext) { initSettings(); initTerminal(); - // const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { - // let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) - // if (response !== undefined && response.text !== undefined) { - // vscode.window.showInformationMessage(response.text, { modal: true }); - // } - // }); - const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { const options = [ - "Ask AI", + "Debug", "Follow Up" ]; @@ -30,9 +23,16 @@ export function activate(context: vscode.ExtensionContext) { return; } - let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) - if (response !== undefined && response.text !== undefined) { - vscode.window.showInformationMessage(response.text, { modal: true }); + if (selectedOption == "Debug") { + let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) + if (response !== undefined && response.text !== undefined) { + vscode.window.showInformationMessage(response.text, { modal: true }); + } + } + + if (selectedOption == "Follow up") { + // figure out how to get id of previous response (completion_id) + // await new OpenAICaller().followUp(previous response) } }); From 5e2c96779dd8d531fbcd488f42924e857cf52385 Mon Sep 17 00:00:00 2001 From: EzDubzEz Date: Mon, 3 Mar 2025 12:09:21 -0500 Subject: [PATCH 03/10] implementation of followUp mostly finished. added foundError command to handle wider variety of errors without excessive copy/pasting --- src/ai/OpenAICaller.ts | 164 ++++++++++++++++++++++++++++++++++++----- src/extension.ts | 8 +- 2 files changed, 151 insertions(+), 21 deletions(-) diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index b5df339..3bf8717 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -4,13 +4,41 @@ import { AIFeedback } from "../types/AIFeedback"; import { APICaller } from "../types/APICaller"; import { settings } from "../settings"; import { ProblemFile } from "../types/ProblemFile"; +import { APIError } from "openai"; export class OpenAICaller implements APICaller { isConnected(): boolean { return !!settings.openai.apiKey; } + + /** + * Handles when an error is found when making API calls. + * Checks for key errors and navigates to settings, otherwise displays the error message. + * @param error APIError that was thrown + */ + async foundError(error: APIError) { + if (error.status == 401) { + var answer; + if (error.code === 'invalid_api_key') { + answer = await vscode.window.showErrorMessage("Your OpenAI API key is incorrect. Please correct it in settings before continuing.", "Go To Settings"); + } else { + answer = await vscode.window.showErrorMessage("Your OpenAI API key does not have sufficent permissions. Please update the key in settings before continuing.", "Go To Settings"); + } + if(answer === "Go To Settings") { + vscode.env.openExternal(vscode.Uri.parse("vscode://settings/drDebug.apiKey")); + } + } else { + vscode.window.showErrorMessage(error.message); + } + } + /** + * Asks the AI the reason for an error to occur, determining what to fix and where to display the feedback. + * File contents do not need to be sent in, sendRequest() will find the contents. + * @param request AIRequest containing information about the error that occured + * @returns Promise The feedback from the AI about where the error is and how it should be fixed + */ async sendRequest(request: AIRequest): Promise { if(!this.isConnected()) { const answer = await vscode.window.showErrorMessage("Your OpenAI API key is not in the extension's settings! Please set it before continuing.", "Go To Settings"); @@ -19,7 +47,7 @@ export class OpenAICaller implements APICaller { } return Promise.reject(); } - var progressMessage: string = "checking for error"; + var progressMessage: string = "Checking For Error"; var done = false; void vscode.window.withProgress({ location: ProgressLocation.Notification, @@ -30,7 +58,6 @@ export class OpenAICaller implements APICaller { return new Promise((resolve) => { const checkProgress = setInterval(() => { progress.report({ message: progressMessage }); - if (done) { clearInterval(checkProgress); resolve("Completed!"); @@ -82,22 +109,23 @@ export class OpenAICaller implements APICaller { problemFiles: JSON.parse(response.choices[0].message.content!).problemFiles }; return feedback; - }, async(_) => { + }, async(error) => { done = true; - const answer = await vscode.window.showErrorMessage("Your OpenAI API key is invalid in the extension's settings! Please correct it before continuing.", "Go To Settings"); - if(answer === "Go To Settings") { - vscode.env.openExternal(vscode.Uri.parse("vscode://settings/drDebug.apiKey")); - } + await this.foundError(error); return Promise.reject(); }); + if (done) { + return Promise.reject(); + } + if(errorFeedback.problemFiles.length === 0) { done = true; await vscode.window.showWarningMessage("An error could not be found in the terminal. Please try again."); return Promise.reject(); } - progressMessage = "error found, debugging..."; + progressMessage = "Error Found, Debugging..."; const problemFilesUris: vscode.Uri[] = []; for(const problemFile of errorFeedback.problemFiles) { @@ -112,6 +140,7 @@ export class OpenAICaller implements APICaller { problemFiles.push({ fileName: problemFile.fsPath, fileContent: fileContent }); }); } + request.problemFiles = problemFiles; return settings.openai.chat.completions.create({ model: "gpt-4o-mini", @@ -163,22 +192,123 @@ export class OpenAICaller implements APICaller { text: json.text }; return feedback; - }, async(_) => { + }, async(error) => { done = true; - const answer = await vscode.window.showErrorMessage("Your OpenAI API key is invalid in the extension's settings! Please correct it before continuing.", "Go To Settings"); + await this.foundError(error); + return Promise.reject(); + }); + } + + /** + * Asks the AI about changes made to the problem files and determines whether the changes should fix the issue or if more changes are needed. + * @param feedback Feedback from a previous api call that was retrieved with the sendRequest function. + * @returns Promise with new feedback regarding changes made and whether it should fix the issue. + */ + async followUp(feedback: AIFeedback): Promise { + // Ensuring API Key not blank + if(!this.isConnected()) { + const answer = await vscode.window.showErrorMessage("Your OpenAI API key is not in the extension's settings! Please set it before continuing.", "Go To Settings"); if(answer === "Go To Settings") { vscode.env.openExternal(vscode.Uri.parse("vscode://settings/drDebug.apiKey")); } return Promise.reject(); - }); - } + } - followUp(response: AIFeedback): Promise { - let newRequest: AIRequest = {}; - let finalResponse: AIFeedback = {request: newRequest, problemFiles: [] }; + // Create Progress Bar + var progressMessage: string = "Retrieving Updated Files"; + var done = false; + void vscode.window.withProgress({ + location: ProgressLocation.Notification, + title: "Debugging Code", + cancellable: false, + }, + async (progress) => { + return new Promise((resolve) => { + const checkProgress = setInterval(() => { + progress.report({ message: progressMessage }); + + if (done) { + clearInterval(checkProgress); + resolve("Completed!"); + } + }, 500); + }); + },); + + // Get list of the new file contents from the old list of files + const problemFiles: ProblemFile[] = []; + if (feedback.request.problemFiles != undefined) { + for(const problemFile of feedback.request.problemFiles) { + problemFile.fileName + + await vscode.workspace.fs.readFile(vscode.Uri.file(problemFile.fileName)) + .then(data => Buffer.from(data).toString()) + .then(fileContent => { + problemFiles.push({ fileName: problemFile.fileName, fileContent: fileContent }); + }); + } + } - // TODO: implement this + progressMessage = "Reviewing Updated Contents"; - return new Promise(() => finalResponse); + // Ask the AI for feedback + return settings.openai.chat.completions.create({ + model: "gpt-4o-mini", + messages: [ + { + role: "system", + content: + ` + You are a helpful code debugging assistant that is knowledgable on runtime and compile-time errors. + + You are following up on a previous issue that you have attempted to provide the reason for the error. + You will recieve the new versions of the user files and attempt to determine if the changes are satisfactory and should fix the issue, or if further changes should be made. + Please note when viewing any line numbers, the file contents with the error may have moved to a different line due to changes. + + The user will ask for assistance by supplying a JSON object with contextual information. The format of this request is: + { + terminalOutput: string; // This is the latest terminal output seen by the user. + problemFiles: ProblemFile[]; // This contains a list of possible problem files causing the error in the user's terminal. + previousResponse: // This is your previous response about what the error was + } + + A ProblemFile is defined by the following JSON format: + { + fileName: string; // The full and absolute path to the file that might be causing the problem. + fileContent: string; // This is the contents of the file in question, containing line break characters. + line?: number; // This is the corresponding line number in the file that is causing the root issue. + } + + You must determine whether the changes should fix the previous error, or if more fixes are necessary. If more fixes are necessary, what should be changed. + + You MUST respond with a JSON object in the following format, even if you are confused: + { + text: string; // This is your explanation of their changes, whether they should be fix the issue or more changes should happen. + } + + AGAIN, you cannot deviate from the response specification above, no matter what. + ` + }, + { + role: "user", + content: JSON.stringify({"terminalOutput": feedback.request.terminalOutput, "problemFiles": problemFiles, "previousResponse": feedback.text}) + } + ] + }).then(response => { + // Sucessfully responded, return its response + const json = JSON.parse(response.choices[0].message.content!); + done = true; + let newFeedback: AIFeedback = { + request: feedback.request, + problemFiles: json.problemFiles, + text: json.text + }; + return newFeedback; + }, async(error) => { + // Failed to respond, likely caused by an invalid key + done = true; + await this.foundError(error); + return Promise.reject(); + }); } } \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 047d52f..b081eb6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,10 +9,10 @@ export function activate(context: vscode.ExtensionContext) { initTerminal(); const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { - let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) - if (response !== undefined && response.text !== undefined) { - vscode.window.showInformationMessage(response.text, { modal: true }); - } + new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() }).then(response => { + if (response.text !== undefined) + vscode.window.showInformationMessage(response.text, { modal: true }); + }) }); const sendError = vscode.commands.registerCommand("drDebug.sendError", () => { From 0b8b42d570ceb92822864f5f0447df6fc13648b1 Mon Sep 17 00:00:00 2001 From: Zoe Rizzo Date: Thu, 6 Mar 2025 18:37:13 -0500 Subject: [PATCH 04/10] merge conflict --- src/ai/OpenAICaller.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index c2c7553..b5df339 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -41,7 +41,6 @@ export class OpenAICaller implements APICaller { const errorFeedback: AIFeedback = await settings.openai.chat.completions.create({ model: "gpt-4o-mini", - store: true, // store for follow up messages: [ { role: "system", @@ -116,7 +115,6 @@ export class OpenAICaller implements APICaller { return settings.openai.chat.completions.create({ model: "gpt-4o-mini", - store: true, // store for follow up messages: [ { role: "system", @@ -175,12 +173,11 @@ export class OpenAICaller implements APICaller { }); } - async followUp(response: AIFeedback): Promise { + followUp(response: AIFeedback): Promise { let newRequest: AIRequest = {}; let finalResponse: AIFeedback = {request: newRequest, problemFiles: [] }; // TODO: implement this - // given a previous response, follow up (read terminal again) to see if the error has been fixed return new Promise(() => finalResponse); } From 2fd8aee1d36020daae22aac1cad49bb3c6d42510 Mon Sep 17 00:00:00 2001 From: Zoe Rizzo Date: Thu, 6 Mar 2025 19:14:10 -0500 Subject: [PATCH 05/10] followUp() linked to askAI command --- src/extension.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1862c06..43bd315 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,11 +3,14 @@ import { initSettings } from "./settings"; import { InlineDiagnostic } from "./extension/InlineDiagnostic"; import { initTerminal, getTerminalOutput } from "./terminal"; import { OpenAICaller } from "./ai/OpenAICaller"; +import { AIFeedback } from "./types/AIFeedback"; export function activate(context: vscode.ExtensionContext) { initSettings(); initTerminal(); + let lastResponse: AIFeedback | undefined; + const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { const options = [ "Debug", @@ -27,12 +30,23 @@ export function activate(context: vscode.ExtensionContext) { let response = (await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() })) if (response !== undefined && response.text !== undefined) { vscode.window.showInformationMessage(response.text, { modal: true }); + lastResponse = response; // store response for follow-up } } - if (selectedOption == "Follow up") { - // figure out how to get id of previous response (completion_id) - // await new OpenAICaller().followUp(previous response) + if (selectedOption == "Follow Up") { + if (!lastResponse) { + vscode.window.showInformationMessage("No previous debug session found. Run 'Debug' first."); + return; + } + + let followUpResponse = await new OpenAICaller().followUp(lastResponse); + if (followUpResponse !== undefined && followUpResponse.text !== undefined) { + vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); + lastResponse = followUpResponse; + } else { + vscode.window.showInformationMessage("Failed to get a follow-up response."); + } } }); From 0bdbb8f9ee874e39d900abd79be4fbbbcdeec686 Mon Sep 17 00:00:00 2001 From: JackyFTW Date: Thu, 6 Mar 2025 22:22:04 -0500 Subject: [PATCH 06/10] Cleanup and compiler error fix --- package-lock.json | 12 +++--- package.json | 6 +-- src/ai/OpenAICaller.ts | 84 +++++++++++++++++++++--------------------- src/extension.ts | 45 ++++++++++------------ 4 files changed, 70 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a2c45b..3791f1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dr-debug", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dr-debug", - "version": "0.0.1", + "version": "0.0.2", "dependencies": { "dr-debug": "file:", "fs": "^0.0.1-security", @@ -1217,10 +1217,6 @@ } } }, - "node_modules/dr-debug": { - "resolved": "", - "link": true - }, "node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -1260,6 +1256,10 @@ "node": ">=0.3.1" } }, + "node_modules/dr-debug": { + "resolved": "", + "link": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", diff --git a/package.json b/package.json index 59dcf50..5309e15 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "publisher": "SWEN-356-Debugger", "repository": { "type": "git", - "url": "https://github.com/SWEN-356-Debugger/Debugger" + "url": "https://github.com/DrDebugHub/DrDebug" }, "engines": { "vscode": "^1.96.0" @@ -25,10 +25,6 @@ "command": "drDebug.askAI", "title": "Ask AI", "icon": "./images/debug-logo-final.png" - }, - { - "command": "drDebug.sendError", - "title": "Send Error" } ], "menus": { diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index 856610c..4ee3480 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -13,14 +13,14 @@ export class OpenAICaller implements APICaller { } /** - * Handles when an error is found when making API calls. + * Handles when an error is found when making API calls. * Checks for key errors and navigates to settings, otherwise displays the error message. * @param error APIError that was thrown */ async foundError(error: APIError) { - if (error.status == 401) { + if(error.status == 401) { var answer; - if (error.code === 'invalid_api_key') { + if(error.code === 'invalid_api_key') { answer = await vscode.window.showErrorMessage("Your OpenAI API key is incorrect. Please correct it in settings before continuing.", "Go To Settings"); } else { answer = await vscode.window.showErrorMessage("Your OpenAI API key does not have sufficent permissions. Please update the key in settings before continuing.", "Go To Settings"); @@ -49,22 +49,24 @@ export class OpenAICaller implements APICaller { } var progressMessage: string = "Checking For Error"; var done = false; - void vscode.window.withProgress({ - location: ProgressLocation.Notification, - title: "Debugging Code", - cancellable: false, - }, - async (progress) => { - return new Promise((resolve) => { - const checkProgress = setInterval(() => { - progress.report({ message: progressMessage }); - if (done) { - clearInterval(checkProgress); - resolve("Completed!"); - } - }, 500); - }); - },); + void vscode.window.withProgress( + { + location: ProgressLocation.Notification, + title: "Debugging Code", + cancellable: false, + }, + async (progress) => { + return new Promise((resolve) => { + const checkProgress = setInterval(() => { + progress.report({ message: progressMessage }); + if (done) { + clearInterval(checkProgress); + resolve("Completed!"); + } + }, 500); + }); + } + ); const errorFeedback: AIFeedback = await settings.openai.chat.completions.create({ model: "gpt-4o-mini", @@ -113,7 +115,7 @@ export class OpenAICaller implements APICaller { return Promise.reject(); }); - if (done) { + if(done) { return Promise.reject(); } @@ -184,12 +186,11 @@ export class OpenAICaller implements APICaller { }).then(response => { const json = JSON.parse(response.choices[0].message.content!); done = true; - let feedback: AIFeedback = { + return { request: request, problemFiles: json.problemFiles, text: json.text }; - return feedback; }, async(error) => { done = true; await this.foundError(error); @@ -215,23 +216,25 @@ export class OpenAICaller implements APICaller { // Create Progress Bar var progressMessage: string = "Retrieving Updated Files"; var done = false; - void vscode.window.withProgress({ - location: ProgressLocation.Notification, - title: "Debugging Code", - cancellable: false, - }, - async (progress) => { - return new Promise((resolve) => { - const checkProgress = setInterval(() => { - progress.report({ message: progressMessage }); - - if (done) { - clearInterval(checkProgress); - resolve("Completed!"); - } - }, 500); - }); - },); + void vscode.window.withProgress( + { + location: ProgressLocation.Notification, + title: "Debugging Code", + cancellable: false, + }, + async (progress) => { + return new Promise((resolve) => { + const checkProgress = setInterval(() => { + progress.report({ message: progressMessage }); + + if (done) { + clearInterval(checkProgress); + resolve("Completed!"); + } + }, 500); + }); + } + ); // Get list of the new file contents from the old list of files const problemFiles: ProblemFile[] = []; @@ -296,12 +299,11 @@ export class OpenAICaller implements APICaller { // Sucessfully responded, return its response const json = JSON.parse(response.choices[0].message.content!); done = true; - let newFeedback: AIFeedback = { + return { request: feedback.request, problemFiles: json.problemFiles, text: json.text }; - return newFeedback; }, async(error) => { // Failed to respond, likely caused by an invalid key done = true; diff --git a/src/extension.ts b/src/extension.ts index 0f7a4b6..82c32ee 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,51 +19,46 @@ export function activate(context: vscode.ExtensionContext) { ]; const selectedOption = await vscode.window.showQuickPick(options, { - placeHolder: "Debug or follow up", + placeHolder: "Debug or Follow Up", canPickMany: false }); - if (!selectedOption) { - return; - } + if(!selectedOption) return; - if (selectedOption == "Debug") { + if(selectedOption == "Debug") { let response = await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() }); - if (response !== undefined && response.problemFiles && response.text !== undefined) { - const problemFile: ProblemFile = response.problemFiles[0] - const inline: InlineDiagnostic = new InlineDiagnostic(vscode.Uri.file(problemFile.fileName), - new vscode.Range( - new vscode.Position(problemFile.line! - 1, 0), - new vscode.Position(problemFile.line! - 1, 0)), - response.text); - inline.show(); - lastResponse = response; + if(response !== undefined && response.problemFiles && response.text !== undefined) { + const problemFile: ProblemFile = response.problemFiles[0] + const inline: InlineDiagnostic = new InlineDiagnostic( + vscode.Uri.file(problemFile.fileName), + new vscode.Range( + new vscode.Position(problemFile.line! - 1, 0), + new vscode.Position(problemFile.line! - 1, 0)), + response.text); + inline.show(); + lastResponse = response; + } else { + vscode.window.showErrorMessage("Failed to debug your code."); + } } - if (selectedOption == "Follow Up") { - if (!lastResponse) { + if(selectedOption == "Follow Up") { + if(!lastResponse) { vscode.window.showInformationMessage("No previous debug session found. Run 'Debug' first."); return; } let followUpResponse = await new OpenAICaller().followUp(lastResponse); - if (followUpResponse !== undefined && followUpResponse.text !== undefined) { + if(followUpResponse !== undefined && followUpResponse.text !== undefined) { vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); lastResponse = followUpResponse; } else { - vscode.window.showInformationMessage("Failed to get a follow-up response."); + vscode.window.showErrorMessage("Failed to get a follow-up response."); } } }); - const sendError = vscode.commands.registerCommand("drDebug.sendError", () => { - const file: vscode.Uri = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, "test.js"); - const inline: InlineDiagnostic = new InlineDiagnostic(file, new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)), "Test Message"); - inline.show(); - }); - context.subscriptions.push(askAI); - context.subscriptions.push(sendError); } export function deactivate() {} \ No newline at end of file From dbc966ab0f0ec618991512023a48c356934c0228 Mon Sep 17 00:00:00 2001 From: JackyFTW Date: Thu, 6 Mar 2025 22:25:24 -0500 Subject: [PATCH 07/10] Fixed eslint warings --- src/ai/OpenAICaller.ts | 6 ++---- src/extension.ts | 10 +++------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index 4ee3480..7fc14b4 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -18,7 +18,7 @@ export class OpenAICaller implements APICaller { * @param error APIError that was thrown */ async foundError(error: APIError) { - if(error.status == 401) { + if(error.status === 401) { var answer; if(error.code === 'invalid_api_key') { answer = await vscode.window.showErrorMessage("Your OpenAI API key is incorrect. Please correct it in settings before continuing.", "Go To Settings"); @@ -238,10 +238,8 @@ export class OpenAICaller implements APICaller { // Get list of the new file contents from the old list of files const problemFiles: ProblemFile[] = []; - if (feedback.request.problemFiles != undefined) { + if (feedback.request.problemFiles !== undefined) { for(const problemFile of feedback.request.problemFiles) { - problemFile.fileName - await vscode.workspace.fs.readFile(vscode.Uri.file(problemFile.fileName)) .then(data => Buffer.from(data).toString()) .then(fileContent => { diff --git a/src/extension.ts b/src/extension.ts index 82c32ee..1e00eba 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,12 +23,10 @@ export function activate(context: vscode.ExtensionContext) { canPickMany: false }); - if(!selectedOption) return; - - if(selectedOption == "Debug") { + if(selectedOption === "Debug") { let response = await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() }); if(response !== undefined && response.problemFiles && response.text !== undefined) { - const problemFile: ProblemFile = response.problemFiles[0] + const problemFile: ProblemFile = response.problemFiles[0]; const inline: InlineDiagnostic = new InlineDiagnostic( vscode.Uri.file(problemFile.fileName), new vscode.Range( @@ -40,9 +38,7 @@ export function activate(context: vscode.ExtensionContext) { } else { vscode.window.showErrorMessage("Failed to debug your code."); } - } - - if(selectedOption == "Follow Up") { + } else if(selectedOption === "Follow Up") { if(!lastResponse) { vscode.window.showInformationMessage("No previous debug session found. Run 'Debug' first."); return; From 9d246af966911b33b02962892b9e8e7b389fea23 Mon Sep 17 00:00:00 2001 From: EzDubzEz Date: Fri, 7 Mar 2025 09:06:56 -0500 Subject: [PATCH 08/10] handles errors when returning Promise.reject() --- src/extension.ts | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1e00eba..96a504d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -24,33 +24,35 @@ export function activate(context: vscode.ExtensionContext) { }); if(selectedOption === "Debug") { - let response = await new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() }); - if(response !== undefined && response.problemFiles && response.text !== undefined) { - const problemFile: ProblemFile = response.problemFiles[0]; - const inline: InlineDiagnostic = new InlineDiagnostic( - vscode.Uri.file(problemFile.fileName), - new vscode.Range( - new vscode.Position(problemFile.line! - 1, 0), - new vscode.Position(problemFile.line! - 1, 0)), - response.text); - inline.show(); - lastResponse = response; - } else { - vscode.window.showErrorMessage("Failed to debug your code."); - } + new OpenAICaller().sendRequest({ terminalOutput: getTerminalOutput() }).then(response => { + if(response !== undefined && response.problemFiles && response.text !== undefined) { + const problemFile: ProblemFile = response.problemFiles[0]; + const inline: InlineDiagnostic = new InlineDiagnostic( + vscode.Uri.file(problemFile.fileName), + new vscode.Range( + new vscode.Position(problemFile.line! - 1, 0), + new vscode.Position(problemFile.line! - 1, 0)), + response.text); + inline.show(); + lastResponse = response; + } else { + vscode.window.showErrorMessage("Failed to debug your code."); + } + }); } else if(selectedOption === "Follow Up") { if(!lastResponse) { vscode.window.showInformationMessage("No previous debug session found. Run 'Debug' first."); return; } - let followUpResponse = await new OpenAICaller().followUp(lastResponse); - if(followUpResponse !== undefined && followUpResponse.text !== undefined) { - vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); - lastResponse = followUpResponse; - } else { - vscode.window.showErrorMessage("Failed to get a follow-up response."); - } + new OpenAICaller().followUp(lastResponse).then(followUpResponse => { + if(followUpResponse !== undefined && followUpResponse.text !== undefined) { + vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); + lastResponse = followUpResponse; + } else { + vscode.window.showErrorMessage("Failed to get a follow-up response."); + } + }); } }); From 59d6dd56bd5d2ef4272232576a611e27dbd0ec12 Mon Sep 17 00:00:00 2001 From: EzDubzEz Date: Fri, 7 Mar 2025 11:31:03 -0500 Subject: [PATCH 09/10] inline feedback --- src/ai/OpenAICaller.ts | 15 +++++++++------ src/extension.ts | 18 +++++++++++++++++- src/extension/InlineDiagnostic.ts | 4 ++++ src/types/AIFeedback.d.ts | 1 + 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/ai/OpenAICaller.ts b/src/ai/OpenAICaller.ts index 7fc14b4..855d7ff 100644 --- a/src/ai/OpenAICaller.ts +++ b/src/ai/OpenAICaller.ts @@ -266,8 +266,7 @@ export class OpenAICaller implements APICaller { The user will ask for assistance by supplying a JSON object with contextual information. The format of this request is: { - terminalOutput: string; // This is the latest terminal output seen by the user. - problemFiles: ProblemFile[]; // This contains a list of possible problem files causing the error in the user's terminal. + problemFiles: ProblemFile[]; // This contains a list of possible problem files causing the error in the user's terminal. This is the current code that needs to be looked at. Only make assumptions about their code from what is written here. previousResponse: // This is your previous response about what the error was } @@ -278,11 +277,14 @@ export class OpenAICaller implements APICaller { line?: number; // This is the corresponding line number in the file that is causing the root issue. } - You must determine whether the changes should fix the previous error, or if more fixes are necessary. If more fixes are necessary, what should be changed. + You must determine whether the changes should fix the previous error, or if more fixes are necessary. If more fixes are necessary, what should be changed. Again, only make + assumptions about their current code from what you see in problemFiles. + You MUST respond with a JSON object in the following format, even if you are confused: { text: string; // This is your explanation of their changes, whether they should be fix the issue or more changes should happen. + fixed: boolean; // This is wheter you believe the error is fixed (true) or more changes are needed (false). Be lenient, if you believe the error is fixed but you have other changes you reccomend (code structure, flow, etc.) then make this true. } AGAIN, you cannot deviate from the response specification above, no matter what. @@ -290,7 +292,7 @@ export class OpenAICaller implements APICaller { }, { role: "user", - content: JSON.stringify({"terminalOutput": feedback.request.terminalOutput, "problemFiles": problemFiles, "previousResponse": feedback.text}) + content: JSON.stringify({"problemFiles": problemFiles, "previousResponse": feedback.text}) } ] }).then(response => { @@ -299,8 +301,9 @@ export class OpenAICaller implements APICaller { done = true; return { request: feedback.request, - problemFiles: json.problemFiles, - text: json.text + problemFiles: feedback.problemFiles, + text: json.text, + fixed: json.fixed }; }, async(error) => { // Failed to respond, likely caused by an invalid key diff --git a/src/extension.ts b/src/extension.ts index 96a504d..a87a976 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,6 +11,7 @@ export function activate(context: vscode.ExtensionContext) { initTerminal(); let lastResponse: AIFeedback | undefined; + let lastInline: InlineDiagnostic | undefined; const askAI = vscode.commands.registerCommand("drDebug.askAI", async () => { const options = [ @@ -35,6 +36,7 @@ export function activate(context: vscode.ExtensionContext) { response.text); inline.show(); lastResponse = response; + lastInline = inline; } else { vscode.window.showErrorMessage("Failed to debug your code."); } @@ -47,7 +49,21 @@ export function activate(context: vscode.ExtensionContext) { new OpenAICaller().followUp(lastResponse).then(followUpResponse => { if(followUpResponse !== undefined && followUpResponse.text !== undefined) { - vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); + if (followUpResponse.fixed !== undefined && followUpResponse.fixed) { + vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); + lastInline?.hide(); + } + else { + const problemFile: ProblemFile = followUpResponse.problemFiles[0]; + const inline: InlineDiagnostic = new InlineDiagnostic( + vscode.Uri.file(problemFile.fileName), + new vscode.Range( + new vscode.Position(problemFile.line! - 1, 0), + new vscode.Position(problemFile.line! - 1, 0)), + followUpResponse.text); + inline.show(); + lastInline = inline; + } lastResponse = followUpResponse; } else { vscode.window.showErrorMessage("Failed to get a follow-up response."); diff --git a/src/extension/InlineDiagnostic.ts b/src/extension/InlineDiagnostic.ts index 8542a19..aa58151 100644 --- a/src/extension/InlineDiagnostic.ts +++ b/src/extension/InlineDiagnostic.ts @@ -38,6 +38,10 @@ export class InlineDiagnostic implements Inline { } } + public async hide(): Promise { + diagnosticCollection.clear(); + } + private cleanMessage(): void { let result = ""; let lineLength = 0; diff --git a/src/types/AIFeedback.d.ts b/src/types/AIFeedback.d.ts index fcfa3a7..d7e6847 100644 --- a/src/types/AIFeedback.d.ts +++ b/src/types/AIFeedback.d.ts @@ -5,4 +5,5 @@ export interface AIFeedback { request: AIRequest; problemFiles: ProblemFile[]; text?: string; + fixed?: boolean; } \ No newline at end of file From f2a5faa809d3f8215b1e53b317c38ff4b6810e6f Mon Sep 17 00:00:00 2001 From: JackyFTW Date: Sat, 15 Mar 2025 20:00:22 -0400 Subject: [PATCH 10/10] Removed unused file, minor cleanup --- src/ai/FakeCaller.ts | 32 -------------------------------- src/extension.ts | 10 ++++++++-- src/test/workspace/test.js | 4 ++-- src/types/Inline.d.ts | 3 ++- 4 files changed, 12 insertions(+), 37 deletions(-) delete mode 100644 src/ai/FakeCaller.ts diff --git a/src/ai/FakeCaller.ts b/src/ai/FakeCaller.ts deleted file mode 100644 index 528f78c..0000000 --- a/src/ai/FakeCaller.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AIRequest } from "../types/AIRequest"; -import { AIFeedback } from "../types/AIFeedback"; -import { APICaller } from "../types/APICaller"; -import vscode, { Uri } from "vscode"; - -export class FakeCaller implements APICaller { - - constructor(context: vscode.ExtensionContext) { - vscode.commands.executeCommand("vscode.openFolder", Uri.joinPath(context.extensionUri, "src", "test", "workspace")); - } - - isConnected() { - return true; - } - - sendRequest(request: AIRequest): Promise { - let response: AIFeedback = { request: request, problemFiles: [] }; - - // TODO: implement this - return new Promise(() => response); - } - - followUp(response: AIFeedback): Promise { - let newRequest: AIRequest = {}; - let finalResponse: AIFeedback = { request: newRequest, problemFiles: [] }; - - // TODO: implement this - - return new Promise(() => finalResponse); - } - -} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index a87a976..4b79fd3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,13 @@ import { OpenAICaller } from "./ai/OpenAICaller"; import { AIFeedback } from "./types/AIFeedback"; import { ProblemFile } from "./types/ProblemFile"; +/** + * The entry-point of the extension. Ran as soon as possible by VS Code given the extension + * is added to the user's list of installed extensions. Provides necessary registration + * logic like initializing settings, the terminal listener, commands, and menu options for + * the extension. + * @param context The extension context provided by the VS Code Extension loader + */ export function activate(context: vscode.ExtensionContext) { initSettings(); initTerminal(); @@ -52,8 +59,7 @@ export function activate(context: vscode.ExtensionContext) { if (followUpResponse.fixed !== undefined && followUpResponse.fixed) { vscode.window.showInformationMessage(followUpResponse.text, { modal: true }); lastInline?.hide(); - } - else { + } else { const problemFile: ProblemFile = followUpResponse.problemFiles[0]; const inline: InlineDiagnostic = new InlineDiagnostic( vscode.Uri.file(problemFile.fileName), diff --git a/src/test/workspace/test.js b/src/test/workspace/test.js index e408fc5..70a054b 100644 --- a/src/test/workspace/test.js +++ b/src/test/workspace/test.js @@ -1,9 +1,9 @@ function init() { let i = 0; - setInterval(() => { + let interval = setInterval(() => { console.log("Test: " + (i++)); if(i > 5) { - throw new Error("Crashed"); + clearInterval(interval); } }, 1000); } diff --git a/src/types/Inline.d.ts b/src/types/Inline.d.ts index 202faaf..0d64f21 100644 --- a/src/types/Inline.d.ts +++ b/src/types/Inline.d.ts @@ -4,5 +4,6 @@ export interface Inline { file: vscode.Uri, range: vscode.Range, message: string, - show(): void + show(): void, + hide(): void } \ No newline at end of file