From 0ab40774810f4d3525a76ddd14f9fc744934d2cd Mon Sep 17 00:00:00 2001 From: Tim Kmecl Date: Thu, 8 Dec 2022 22:20:31 +0100 Subject: [PATCH] Implemented conversation (followup questions) - v2.0.0 of chatgpt-api - response timeout setting - updated README --- README.md | 7 +- package-lock.json | 166 +++++++++++++++++++++++++--------------------- package.json | 68 +++++++++++++++++-- src/extension.ts | 136 ++++++++++++++++++++++++++++--------- yarn.lock | 66 +++++++++--------- 5 files changed, 292 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index de36ca2..fea6010 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ This Visual Studio Code extension allows you to use the [unofficial ChatGPT API] ## Features - **Ask general questions** or use code snippets from the editor to query ChatGPT via an input box in the sidebar -- Right click on selection and run one of the context menu **shortcuts** +- Right click on a code selection and run one of the context menu **shortcuts** - View ChatGPT's responses in a panel next to the editor +- Ask **follow-up questions** to the response (conversation context is maintained) - **Insert code snippets** from the AI's response into the active editor by clicking on them @@ -62,6 +63,10 @@ You can select some code in the editor, right click on it and choose one of the `Ask ChatGPT` is also available when nothing is selected. For the other four commands, you can customize the exact prompt that will be sent to the AI by editing the extension settings in VSCode Preferences. + +Because ChatGPT is a conversational AI, you can ask follow-up questions to the response. The conversation context is maintained between queries, so you can ask multiple questions in a row. +To **reset the conversation context**, click `ctrl+shift+p` and select `ChatGPT: Reset Conversation`. + --- Please note that this extension is currently a proof of concept and may have some limitations or bugs. We welcome feedback and contributions to improve the extension. diff --git a/package-lock.json b/package-lock.json index 4d731b9..09a2364 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "chatgpt", - "version": "0.3.0", + "version": "0.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "chatgpt", - "version": "0.3.0", + "version": "0.4.0", "license": "MIT", "dependencies": { - "chatgpt": "^1.4.0" + "chatgpt": "^2.0.5" }, "devDependencies": { "@types/glob": "^8.0.0", @@ -960,6 +960,18 @@ "node": ">=0.2.0" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "optional": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1035,19 +1047,22 @@ } }, "node_modules/chatgpt": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-1.4.0.tgz", - "integrity": "sha512-954YN2XTaICfi6ELTEJKC/ueVSiFiqoK1bRxbzzp7aJZbzBM6eGOY8WsL3REzFuTiGkDZurbxDzHEslmMLKk7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz", + "integrity": "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==", "dependencies": { "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", - "node-fetch": "2", + "p-timeout": "^6.0.0", "remark": "^14.0.2", "strip-markdown": "^5.0.0", "uuid": "^9.0.0" }, "engines": { - "node": ">=14" + "node": ">=16.8" + }, + "optionalDependencies": { + "undici": "^5.13.0" } }, "node_modules/chokidar": { @@ -3073,25 +3088,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -3171,6 +3167,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-timeout": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz", + "integrity": "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -3733,6 +3740,15 @@ "source-map": "^0.6.0" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3903,11 +3919,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -4003,6 +4014,18 @@ "node": ">=4.2.0" } }, + "node_modules/undici": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", + "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", + "optional": true, + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=12.18" + } + }, "node_modules/unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -4194,11 +4217,6 @@ "node": ">=10.13.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, "node_modules/webpack": { "version": "5.75.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", @@ -4322,15 +4340,6 @@ "node": ">=10.13.0" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5178,6 +5187,15 @@ "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", "dev": true }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "optional": true, + "requires": { + "streamsearch": "^1.1.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -5221,15 +5239,16 @@ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" }, "chatgpt": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-1.4.0.tgz", - "integrity": "sha512-954YN2XTaICfi6ELTEJKC/ueVSiFiqoK1bRxbzzp7aJZbzBM6eGOY8WsL3REzFuTiGkDZurbxDzHEslmMLKk7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz", + "integrity": "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==", "requires": { "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", - "node-fetch": "2", + "p-timeout": "^6.0.0", "remark": "^14.0.2", "strip-markdown": "^5.0.0", + "undici": "^5.13.0", "uuid": "^9.0.0" } }, @@ -6641,14 +6660,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, "node-releases": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", @@ -6707,6 +6718,11 @@ "p-limit": "^3.0.2" } }, + "p-timeout": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz", + "integrity": "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==" + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -7098,6 +7114,12 @@ "source-map": "^0.6.0" } }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "optional": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7212,11 +7234,6 @@ "is-number": "^7.0.0" } }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", @@ -7276,6 +7293,15 @@ "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true }, + "undici": { + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz", + "integrity": "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==", + "optional": true, + "requires": { + "busboy": "^1.6.0" + } + }, "unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -7411,11 +7437,6 @@ "graceful-fs": "^4.1.2" } }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, "webpack": { "version": "5.75.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", @@ -7493,15 +7514,6 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 30133d9..1d1c950 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "chatgpt", "displayName": "ChatGPT: write and improve code using AI", - "description": "A VSCode extension that allows you to use ChatGPT in IDE (unofficial)", - "version": "0.3.0", + "description": "Extension that allows you to use OpeanAI's ChatGPT inside the IDE (unofficial)", + "version": "0.4.0", "publisher": "timkmecl", "icon": "resources/extensionIcon.png", "license": "MIT", @@ -15,6 +15,16 @@ "categories": [ "Other" ], + "keywords": [ + "copilot", + "openai", + "chatgpt", + "ai", + "explain", + "find bugs", + "explain ", + "refactor" + ], "activationEvents": [ "onView:chatgpt.chatView", "onCommand:chatgpt.ask", @@ -45,6 +55,14 @@ { "command": "chatgpt.optimize", "title": "ChatGPT: Optimize selection" + }, + { + "command": "chatgpt.conversationId", + "title": "Set ChatGPT conversation ID" + }, + { + "command": "chatgpt.resetConversation", + "title": "Reset ChatGPT conversation" } ], "menus": { @@ -74,6 +92,33 @@ "when": "editorHasSelection", "group": "chatgpt-menu-group@5" } + ], + "commandPalette": [ + { + "command": "chatgpt.ask" + }, + { + "command": "chatgpt.explain", + "when": "editorHasSelection" + }, + { + "command": "chatgpt.refactor", + "when": "editorHasSelection" + }, + { + "command": "chatgpt.findProblems", + "when": "editorHasSelection" + }, + { + "command": "chatgpt.optimize", + "when": "editorHasSelection" + }, + { + "command": "chatgpt.conversationId" + }, + { + "command": "chatgpt.resetConversation" + } ] }, "viewsContainers": { @@ -106,7 +151,7 @@ "chatgpt.pasteOnClick": { "type": "boolean", "default": true, - "description": "Paste the code from a codeblock inside the response into the editor when you click on it?", + "description": "Paste the code from a codeblock inside the response into the editor when you click on it", "order": 2 }, "chatgpt.promptPrefix.explain": { @@ -133,11 +178,22 @@ "description": "The prompt prefix used for optimizing the selected code", "order": 6 }, - "chatgpt.selectedInsideCodeblock": { + "chatgpt.keepConversation": { "type": "boolean", "default": true, - "description": "Append selected code as a codeblock (```...code...```) instead of plain text?", + "description": "Keep the conversation going by using the same conversation ID for all requests (allows follow-up questions)", "order": 7 + },"chatgpt.timeoutLength": { + "type": "number", + "default": "60", + "description": "How long should the request wait for a response before timing out (in seconds)", + "order": 8 + }, + "chatgpt.selectedInsideCodeblock": { + "type": "boolean", + "default": true, + "description": "Append selected code as a codeblock (```...code...```) instead of plain text", + "order": 9 } } } @@ -170,6 +226,6 @@ "webpack-cli": "^5.0.0" }, "dependencies": { - "chatgpt": "^1.4.0" + "chatgpt": "^2.0.5" } } diff --git a/src/extension.ts b/src/extension.ts index f7782dd..0449cd8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { ChatGPTAPI } from 'chatgpt'; +import { ChatGPTAPI, ChatGPTConversation } from 'chatgpt'; export function activate(context: vscode.ExtensionContext) { @@ -10,7 +10,13 @@ export function activate(context: vscode.ExtensionContext) { // Create a new ChatGPTViewProvider instance and register it with the extension's context const provider = new ChatGPTViewProvider(context.extensionUri); provider.setSessionToken(sessionToken); + + // Put configuration settings into the provider provider.selectedInsideCodeblock = config.get('selectedInsideCodeblock') || false; + provider.pasteOnClick = config.get('pasteOnClick') || false; + provider.keepConversation = config.get('keepConversation') || false; + provider.timeoutLength = config.get('timeoutLength') || 60; + context.subscriptions.push( vscode.window.registerWebviewViewProvider(ChatGPTViewProvider.viewType, provider, { webviewOptions: { retainContextWhenHidden: true } @@ -18,10 +24,12 @@ export function activate(context: vscode.ExtensionContext) { ); + + // Register the commands that can be called from the extension's package.json const commandHandler = (command:string) => { const config = vscode.workspace.getConfiguration('chatgpt'); const prompt = config.get(command) as string; - provider.search(prompt, true); + provider.search(prompt); }; const commandAsk = vscode.commands.registerCommand('chatgpt.ask', () => { @@ -29,6 +37,25 @@ export function activate(context: vscode.ExtensionContext) { provider.search(value); }); }); + const commandConversationId = vscode.commands.registerCommand('chatgpt.conversationId', () => { + vscode.window.showInputBox({ + prompt: 'Set Conversation ID or delete it to reset the conversation', + placeHolder: 'conversationId (leave empty to reset)', + value: provider.getConversationId() + }).then((conversationId) => { + if (!conversationId) { + provider.setConversationId(); + } else { + vscode.window.showInputBox({ + prompt: 'Set Parent Message ID', + placeHolder: 'messageId (leave empty to reset)', + value: provider.getParentMessageId() + }).then((messageId) => { + provider.setConversationId(conversationId, messageId); + }); + } + }); + }); const commandExplain = vscode.commands.registerCommand('chatgpt.explain', () => { commandHandler('promptPrefix.explain'); }); @@ -41,26 +68,40 @@ export function activate(context: vscode.ExtensionContext) { const commandProblems = vscode.commands.registerCommand('chatgpt.findProblems', () => { commandHandler('promptPrefix.findProblems'); }); + + let commandResetConversation = vscode.commands.registerCommand('chatgpt.resetConversation', () => { + provider.setConversationId(); + }); - context.subscriptions.push(commandAsk, commandExplain, commandRefactor, commandOptimize, commandProblems); + context.subscriptions.push(commandAsk, commandConversationId, commandExplain, commandRefactor, commandOptimize, commandProblems, commandResetConversation); // Change the extension's session token when configuration is changed vscode.workspace.onDidChangeConfiguration((event: vscode.ConfigurationChangeEvent) => { if (event.affectsConfiguration('chatgpt.sessionToken')) { - // Get the extension's configuration - const config = vscode.workspace.getConfiguration('chatgpt'); - const sessionToken = config.get('sessionToken') as string|undefined; - // add the new token to the provider - provider.setSessionToken(sessionToken); + // Get the extension's configuration + const config = vscode.workspace.getConfiguration('chatgpt'); + const sessionToken = config.get('sessionToken') as string|undefined; + // add the new token to the provider + provider.setSessionToken(sessionToken); + } else if (event.affectsConfiguration('chatgpt.selectedInsideCodeblock')) { const config = vscode.workspace.getConfiguration('chatgpt'); provider.selectedInsideCodeblock = config.get('selectedInsideCodeblock') || false; + } else if (event.affectsConfiguration('chatgpt.pasteOnClick')) { const config = vscode.workspace.getConfiguration('chatgpt'); provider.pasteOnClick = config.get('pasteOnClick') || false; + + } else if (event.affectsConfiguration('chatgpt.keepConversation')) { + const config = vscode.workspace.getConfiguration('chatgpt'); + provider.keepConversation = config.get('keepConversation') || false; + + }else if (event.affectsConfiguration('chatgpt.timeoutLength')) { + const config = vscode.workspace.getConfiguration('chatgpt'); + provider.timeoutLength = config.get('timeoutLength') || 60; } }); } @@ -76,13 +117,17 @@ class ChatGPTViewProvider implements vscode.WebviewViewProvider { // This variable holds a reference to the ChatGPTAPI instance private _chatGPTAPI?: ChatGPTAPI; + private _conversation?: ChatGPTConversation; private _response?: string; private _prompt?: string; + private _fullPrompt?: string; public selectedInsideCodeblock = false; public pasteOnClick = true; + public keepConversation = true; + public timeoutLength = 60; private _sessionToken?: string; // In the constructor, we store the URI of the extension @@ -96,6 +141,21 @@ class ChatGPTViewProvider implements vscode.WebviewViewProvider { this._newAPI(); } + public setConversationId(conversationId?: string, parentMessageId?: string) { + if (!conversationId || !parentMessageId) { + this._conversation = this._chatGPTAPI?.getConversation(); + } else if (conversationId && parentMessageId) { + this._conversation = this._chatGPTAPI?.getConversation({conversationId: conversationId, parentMessageId: parentMessageId}); + } + } + + public getConversationId() { + return this._conversation?.conversationId; + } + public getParentMessageId() { + return this._conversation?.parentMessageId; + } + // This private method initializes a new ChatGPTAPI instance, using the session token if it is set private _newAPI() { if (!this._sessionToken) { @@ -104,6 +164,7 @@ class ChatGPTViewProvider implements vscode.WebviewViewProvider { this._chatGPTAPI = new ChatGPTAPI({ sessionToken: this._sessionToken }); + this._conversation = this._chatGPTAPI.getConversation(); } } @@ -153,7 +214,7 @@ class ChatGPTViewProvider implements vscode.WebviewViewProvider { - public async search(prompt:string|undefined, fromCommand:boolean = false) { + public async search(prompt?:string) { this._prompt = prompt; if (!prompt) { prompt = ''; @@ -170,47 +231,58 @@ class ChatGPTViewProvider implements vscode.WebviewViewProvider { } else { this._view?.show?.(true); } - + let response = ''; - if (!this._chatGPTAPI) { + // Get the selected text of the active editor + const selection = vscode.window.activeTextEditor?.selection; + const selectedText = vscode.window.activeTextEditor?.document.getText(selection); + let searchPrompt = ''; + + if (selection && selectedText) { + // If there is a selection, add the prompt and the selected text to the search prompt + if (this.selectedInsideCodeblock) { + searchPrompt = `${prompt}\n\`\`\`\n${selectedText}\n\`\`\``; + } else { + searchPrompt = `${prompt}\n${selectedText}\n`; + } + } else { + // Otherwise, just use the prompt if user typed it + searchPrompt = prompt; + } + + this._fullPrompt = searchPrompt; + + + if (!this._chatGPTAPI || !this._conversation) { response = '[ERROR] Please enter an API key in the extension settings'; } else { // If successfully signed in + console.log("sendMessage"); + + // Make sure the prompt is shown + this._view?.webview.postMessage({ type: 'setPrompt', value: this._prompt }); + if (this._view) { this._view.webview.postMessage({ type: 'addResponse', value: '...' }); } - // Get the selected text of the active editor - const selection = vscode.window.activeTextEditor?.selection; - const selectedText = vscode.window.activeTextEditor?.document.getText(selection); - let searchPrompt = ''; - - if (selection && selectedText) { - // If there is a selection, add the prompt and the selected text to the search prompt - if (this.selectedInsideCodeblock) { - searchPrompt = `${prompt}\n\`\`\`\n${selectedText}\n\`\`\``; - } else { - searchPrompt = `${prompt}\n${selectedText}\n`; - } + let agent; + if (this.keepConversation) { + agent = this._conversation; } else { - // Otherwise, just use the prompt if user typed it - searchPrompt = prompt; + agent = this._chatGPTAPI; } - console.log("sendMessage"); - - // Make sure the prompt is shown - this._view?.webview.postMessage({ type: 'setPrompt', value: this._prompt }); - try { // Send the search prompt to the ChatGPTAPI instance and store the response - response = await this._chatGPTAPI.sendMessage(searchPrompt, { + response = await agent.sendMessage(searchPrompt, { onProgress: (partialResponse) => { if (this._view && this._view.visible) { this._view.webview.postMessage({ type: 'addResponse', value: partialResponse }); } - } + }, + timeoutMs: this.timeoutLength * 1000 }); } catch (e) { console.error(e); diff --git a/yarn.lock b/yarn.lock index cdff110..2545473 100644 --- a/yarn.lock +++ b/yarn.lock @@ -586,6 +586,13 @@ "resolved" "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz" "version" "0.1.1" +"busboy@^1.6.0": + "integrity" "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==" + "resolved" "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" + "version" "1.6.0" + dependencies: + "streamsearch" "^1.1.0" + "callsites@^3.0.0": "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" "resolved" "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -621,17 +628,19 @@ "resolved" "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz" "version" "2.0.2" -"chatgpt@^1.4.0": - "integrity" "sha512-954YN2XTaICfi6ELTEJKC/ueVSiFiqoK1bRxbzzp7aJZbzBM6eGOY8WsL3REzFuTiGkDZurbxDzHEslmMLKk7A==" - "resolved" "https://registry.npmjs.org/chatgpt/-/chatgpt-1.4.0.tgz" - "version" "1.4.0" +"chatgpt@^2.0.5": + "integrity" "sha512-eVswcmvAfYcYh3TK8ryP7305lnvUJXM4CTLayEtFoS25Ovd9mNsOYJPWYLa/B10+ClEOWpeRe1cLR9VssY6STQ==" + "resolved" "https://registry.npmjs.org/chatgpt/-/chatgpt-2.0.5.tgz" + "version" "2.0.5" dependencies: "eventsource-parser" "^0.0.5" "expiry-map" "^2.0.0" - "node-fetch" "2" + "p-timeout" "^6.0.0" "remark" "^14.0.2" "strip-markdown" "^5.0.0" "uuid" "^9.0.0" + optionalDependencies: + "undici" "^5.13.0" "chokidar@3.5.3": "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" @@ -1044,11 +1053,6 @@ "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" "version" "1.0.0" -"fsevents@~2.3.2": - "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" - "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - "version" "2.3.2" - "fstream@^1.0.12": "integrity" "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==" "resolved" "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz" @@ -1793,13 +1797,6 @@ "resolved" "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" "version" "2.6.2" -"node-fetch@2": - "integrity" "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==" - "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" - "version" "2.6.7" - dependencies: - "whatwg-url" "^5.0.0" - "node-releases@^2.0.6": "integrity" "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" "resolved" "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" @@ -1862,6 +1859,11 @@ dependencies: "p-limit" "^3.0.2" +"p-timeout@^6.0.0": + "integrity" "sha512-5iS61MOdUMemWH9CORQRxVXTp9g5K8rPnI9uQpo97aWgsH3vVXKjkIhDi+OgIDmN3Ly9+AZ2fZV01Wut1yzfKA==" + "resolved" "https://registry.npmjs.org/p-timeout/-/p-timeout-6.0.0.tgz" + "version" "6.0.0" + "p-try@^2.0.0": "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" "resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" @@ -2147,6 +2149,11 @@ "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" "version" "0.6.1" +"streamsearch@^1.1.0": + "integrity" "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + "resolved" "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" + "version" "1.1.0" + "string_decoder@~1.1.1": "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==" "resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" @@ -2248,11 +2255,6 @@ dependencies: "is-number" "^7.0.0" -"tr46@~0.0.3": - "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" - "version" "0.0.3" - "traverse@>=0.3.0 <0.4": "integrity" "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==" "resolved" "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz" @@ -2302,6 +2304,13 @@ "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz" "version" "4.9.3" +"undici@^5.13.0": + "integrity" "sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==" + "resolved" "https://registry.npmjs.org/undici/-/undici-5.14.0.tgz" + "version" "5.14.0" + dependencies: + "busboy" "^1.6.0" + "unified@^10.0.0": "integrity" "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==" "resolved" "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz" @@ -2421,11 +2430,6 @@ "glob-to-regexp" "^0.4.1" "graceful-fs" "^4.1.2" -"webidl-conversions@^3.0.0": - "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" - "version" "3.0.1" - "webpack-cli@^5.0.0", "webpack-cli@5.x.x": "integrity" "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==" "resolved" "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz" @@ -2488,14 +2492,6 @@ "watchpack" "^2.4.0" "webpack-sources" "^3.2.3" -"whatwg-url@^5.0.0": - "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" - "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" - "version" "5.0.0" - dependencies: - "tr46" "~0.0.3" - "webidl-conversions" "^3.0.0" - "which@^2.0.1": "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz"