From bba8c6c2dea3e68187cc86634a3973909c42dbfd Mon Sep 17 00:00:00 2001 From: Olasunkanmi Oyinlola Date: Wed, 18 Dec 2024 15:11:05 +0800 Subject: [PATCH 1/2] feat(api, ui): Add in-line chat functionality Introduce new InLineChat class for handling in-line chat interactions Update EventGenerator to support in-line chat functionality Add new command for in-line chat in package.json and extension.ts Modify existing code to accommodate in-line chat changes --- package.json | 8 +++++ src/constant.ts | 1 + src/events/event-generator.ts | 47 +++++++++++++++++++++---- src/events/inline-chat.ts | 25 +++++++++++++ src/extension.ts | 7 ++++ src/providers/code-actions-provider.ts | 10 +++--- src/providers/groq-web-view-provider.ts | 8 ++--- src/services/chat-manager.ts | 44 +++++++++++------------ 8 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 src/events/inline-chat.ts diff --git a/package.json b/package.json index 125b584..358190b 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,10 @@ "command": "CodeBuddy.generateCommitMessage", "group": "CodeBuddy" }, + { + "command": "CodeBuddy.inLineChat", + "group":"CodeBuddy" + }, { "when": "editorHasSelection", "command": "CodeBuddy.interviewMe", @@ -136,6 +140,10 @@ "command": "CodeBuddy.generateCommitMessage", "title": "CodeBuddy. Generate commit message" }, + { + "command": "CodeBuddy.inLineChat", + "title": "CodeBuddy. In line chat" + }, { "command": "CodeBuddy.interviewMe", "title": "CodeBuddy. Interview Me." diff --git a/src/constant.ts b/src/constant.ts index a02543b..ea3b687 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -12,6 +12,7 @@ export enum OLA_ACTIONS { interviewMe = "CodeBuddy.interviewMe", generateUnitTest = "CodeBuddy.generateUnitTest", generateCodeChart = "CodeBuddy.generateCodeChart", + inlineChat = "CodeBuddy.inLineChat", } export enum COMMON { diff --git a/src/events/event-generator.ts b/src/events/event-generator.ts index 047ceb0..555a361 100644 --- a/src/events/event-generator.ts +++ b/src/events/event-generator.ts @@ -278,19 +278,23 @@ export abstract class EventGenerator implements IEventGenerator { abstract createPrompt(text?: string): any; async generateResponse( - errorMessage?: string, + message?: string, ): Promise { this.showInformationMessage(); let prompt; const selectedCode = this.getSelectedWindowArea(); - if (!errorMessage && !selectedCode) { + if (!message && !selectedCode) { vscode.window.showErrorMessage("select a piece of code."); return; } - errorMessage - ? (prompt = await this.createPrompt(errorMessage)) - : (prompt = await this.createPrompt(selectedCode)); + if (message && selectedCode) { + prompt = await this.createPrompt(`${message} \n ${selectedCode}`); + } else { + message + ? (prompt = await this.createPrompt(message)) + : (prompt = await this.createPrompt(selectedCode)); + } if (!prompt) { vscode.window.showErrorMessage("model not reponding, try again later"); @@ -366,8 +370,37 @@ export abstract class EventGenerator implements IEventGenerator { return response; } - async execute(errorMessage?: string): Promise { - const response = (await this.generateResponse(errorMessage)) as string; + async getUserInLineChat(): Promise { + try { + const userPrompt = await vscode.window.showInputBox({ + placeHolder: "Enter instructions for CodeBuddy", + ignoreFocusOut: true, + validateInput: (text) => { + return text === "" + ? "Enter instructions for CodeBuddy or press Escape to close chat box" + : null; + }, + }); + if (userPrompt) { + Brain.set("inLineChat", true); + } + return userPrompt; + } catch (error) { + vscode.window.showInformationMessage( + `Error occured while getting user prompt`, + ); + console.log(error); + } + } + + async execute(message?: string): Promise { + const prompt: string | undefined = await this.getUserInLineChat(); + if (prompt && Brain.has("inLineChat")) { + Brain.delete("inLineChat"); + } + const response = (await this.generateResponse( + prompt ? prompt : message, + )) as string; if (!response) { vscode.window.showErrorMessage("model not reponding, try again later"); return; diff --git a/src/events/inline-chat.ts b/src/events/inline-chat.ts new file mode 100644 index 0000000..b5ec1e9 --- /dev/null +++ b/src/events/inline-chat.ts @@ -0,0 +1,25 @@ +import { formatText } from "../utils"; +import { EventGenerator } from "./event-generator"; +import * as vscode from "vscode"; + +export class InLineChat extends EventGenerator { + selectedCode: string | undefined; + constructor(action: string, context: vscode.ExtensionContext) { + super(action, context); + } + + getCurrentActiveEditorCode(): string | undefined { + const editor = vscode.window.activeTextEditor; + return editor ? editor?.document?.getText() : undefined; + } + + formatResponse(comment: string): string { + return formatText(comment); + } + + createPrompt(selectedCode: string): string { + let context = this.getCurrentActiveEditorCode() ?? ""; + const fullPrompt = `${selectedCode} \n Here is the code context ${context}`; + return fullPrompt; + } +} diff --git a/src/extension.ts b/src/extension.ts index 0e7c95d..4c1ca10 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -25,6 +25,7 @@ import { setUpGenerativeAiModel } from "./services/generative-ai-model-manager"; import { getConfigValue } from "./utils"; import { AnthropicWebViewProvider } from "./providers/anthropic-web-view-provider"; import { Brain } from "./services/memory"; +import { InLineChat } from "./events/inline-chat"; const { geminiKey, @@ -53,11 +54,16 @@ export async function activate(context: vscode.ExtensionContext) { interviewMe, generateUnitTest, generateCodeChart, + inlineChat, } = OLA_ACTIONS; const getComment = new Comments( `${USER_MESSAGE} generates the code comments...`, context, ); + const getInLineChat = new InLineChat( + `${USER_MESSAGE} generates a response...`, + context, + ); const generateOptimizeCode = new OptimizeCode( `${USER_MESSAGE} optimizes the code...`, context, @@ -115,6 +121,7 @@ export async function activate(context: vscode.ExtensionContext) { [knowledge]: () => knowledgeBase.execute(), [commitMessage]: () => generateCommitMessage.execute("hello"), [generateCodeChart]: () => codeChartGenerator.execute(), + [inlineChat]: () => getInLineChat.execute(), }; const subscriptions: vscode.Disposable[] = Object.entries(actionMap).map( diff --git a/src/providers/code-actions-provider.ts b/src/providers/code-actions-provider.ts index 8af90d3..7e141ae 100644 --- a/src/providers/code-actions-provider.ts +++ b/src/providers/code-actions-provider.ts @@ -5,7 +5,7 @@ export class CodeActionsProvider implements vscode.CodeActionProvider { document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, - token: vscode.CancellationToken + token: vscode.CancellationToken, ): vscode.ProviderResult<(vscode.Command | vscode.CodeAction)[]> { return this.quickFix(context, document); } @@ -18,11 +18,11 @@ export class CodeActionsProvider implements vscode.CodeActionProvider { // This returns the whole line where the error occured const erroredLineOfCode = this.getErroredFunction( document, - diagnostic.range + diagnostic.range, ); const action = new vscode.CodeAction( "CodeBuddy fix this error", - vscode.CodeActionKind.QuickFix + vscode.CodeActionKind.QuickFix, ); const windowContent = document.getText(); // Seems the text is too much `${errorMessage} \n ${windowContent}` @@ -39,13 +39,13 @@ export class CodeActionsProvider implements vscode.CodeActionProvider { private getErroredFunction( document: vscode.TextDocument, - range: vscode.Range + range: vscode.Range, ): string { const startLine = range.start.line; const endLine = range.end.line; const erroredFunctionRange: vscode.Range = new vscode.Range( new vscode.Position(startLine, 0), - new vscode.Position(endLine + 1, 0) + new vscode.Position(endLine + 1, 0), ); return document.getText(erroredFunctionRange); } diff --git a/src/providers/groq-web-view-provider.ts b/src/providers/groq-web-view-provider.ts index 4c57921..97f6896 100644 --- a/src/providers/groq-web-view-provider.ts +++ b/src/providers/groq-web-view-provider.ts @@ -16,14 +16,14 @@ export class GroqWebViewProvider extends BaseWebViewProvider { extensionUri: vscode.Uri, apiKey: string, generativeAiModel: string, - context: vscode.ExtensionContext + context: vscode.ExtensionContext, ) { super(extensionUri, apiKey, generativeAiModel, context); } public async sendResponse( response: string, - currentChat: string + currentChat: string, ): Promise { try { const type = currentChat === "bot" ? "bot-response" : "user-input"; @@ -60,7 +60,7 @@ export class GroqWebViewProvider extends BaseWebViewProvider { async generateResponse( apiKey = undefined, name = undefined, - message: string + message: string, ): Promise { try { const { temperature, max_tokens, top_p, stop } = GROQ_CONFIG; @@ -99,7 +99,7 @@ export class GroqWebViewProvider extends BaseWebViewProvider { console.error(error); Brain.set(COMMON.GROQ_CHAT_HISTORY, []); vscode.window.showErrorMessage( - "Model not responding, please resend your question" + "Model not responding, please resend your question", ); return; } diff --git a/src/services/chat-manager.ts b/src/services/chat-manager.ts index ee9d135..1e54a46 100644 --- a/src/services/chat-manager.ts +++ b/src/services/chat-manager.ts @@ -73,10 +73,10 @@ export class ChatManager { } catch (error) { console.error(error); vscodeErrorMessage( - "Failed to generate content. Please try again later." + "Failed to generate content. Please try again later.", ); } - } + }, ); } @@ -132,60 +132,60 @@ export class ChatManager { const generativeAi: string | undefined = getGenerativeAiModel(); if (generativeAi === generativeAiModels.GROQ) { const groqAiConfigurations = this.handleAiProvider( - generativeAiModels.GROQ + generativeAiModels.GROQ, ); const chatViewProvider = new GroqWebViewProvider( this._context.extensionUri, groqAiConfigurations.apiKey, groqAiConfigurations.model, - this._context + this._context, ); return await chatViewProvider.generateResponse( undefined, undefined, - message + message, ); } if (generativeAi === generativeAiModels.GEMINI) { const geminiConfigurations = this.handleAiProvider( - generativeAiModels.GEMINI + generativeAiModels.GEMINI, ); const geminiWebViewProvider = new GeminiWebViewProvider( this._context.extensionUri, geminiConfigurations.apiKey, geminiConfigurations.model, - this._context + this._context, ); return await geminiWebViewProvider.generateResponse( geminiConfigurations.apiKey, geminiConfigurations.model, - message + message, ); } if (generativeAi === "Anthropic") { const anthropicConfigurations: aIProviderConfig = this.handleAiProvider( - generativeAiModels.ANTHROPIC + generativeAiModels.ANTHROPIC, ); const anthropicWebViewProvider = this.getAnthropicWebViewProvider( - anthropicConfigurations + anthropicConfigurations, ); return await anthropicWebViewProvider.generateResponse( undefined, undefined, - message + message, ); } if (generativeAi === generativeAiModels.GROK) { const grokConfigurations: aIProviderConfig = this.handleAiProvider( - generativeAiModels.GROK + generativeAiModels.GROK, ); const anthropicWebViewProvider = this.getAnthropicWebViewProvider(grokConfigurations); return await anthropicWebViewProvider.generateResponse( undefined, undefined, - message + message, ); } } catch (error) { @@ -202,7 +202,7 @@ export class ChatManager { this._context.extensionUri, this.groqApiKey, this.groqModel, - this._context + this._context, ); chatViewProvider.sendResponse(formatText(userInput), COMMON.USER_INPUT); chatViewProvider.sendResponse(formatText(response), COMMON.BOT); @@ -212,11 +212,11 @@ export class ChatManager { this._context.extensionUri, this.geminiApiKey, this.geminiModel, - this._context + this._context, ); geminiWebViewProvider.sendResponse( formatText(userInput), - COMMON.USER_INPUT + COMMON.USER_INPUT, ); geminiWebViewProvider.sendResponse(formatText(response), COMMON.BOT); } @@ -227,12 +227,12 @@ export class ChatManager { let anthropicConfigurations: aIProviderConfig | undefined; if (this.generativeAi === generativeAiModels.ANTHROPIC) { anthropicConfigurations = this.handleAiProvider( - generativeAiModels.ANTHROPIC + generativeAiModels.ANTHROPIC, ); } if (this.generativeAi === generativeAiModels.GROK) { anthropicConfigurations = this.handleAiProvider( - generativeAiModels.GROK + generativeAiModels.GROK, ); } if (!anthropicConfigurations) { @@ -240,11 +240,11 @@ export class ChatManager { } const anthropicWebViewProvider = this.getAnthropicWebViewProvider( - anthropicConfigurations + anthropicConfigurations, ); anthropicWebViewProvider.sendResponse( formatText(userInput), - COMMON.USER_INPUT + COMMON.USER_INPUT, ); anthropicWebViewProvider.sendResponse(formatText(response), COMMON.BOT); } @@ -256,7 +256,7 @@ export class ChatManager { } private getAnthropicWebViewProvider( - config: aIProviderConfig + config: aIProviderConfig, ): AnthropicWebViewProvider { let xGrokBaseURL; if (getConfigValue(APP_CONFIG.generativeAi) === generativeAiModels.GROK) { @@ -267,7 +267,7 @@ export class ChatManager { config.apiKey, config.model, this._context, - xGrokBaseURL + xGrokBaseURL, ); } } From d6423c15b9dc907f68aabf52a2c808fe4b1812bd Mon Sep 17 00:00:00 2001 From: Olasunkanmi Oyinlola Date: Wed, 18 Dec 2024 15:14:19 +0800 Subject: [PATCH 2/2] update package json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 358190b..207a68e 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ }, { "command": "CodeBuddy.inLineChat", - "title": "CodeBuddy. In line chat" + "title": "CodeBuddy. Inline chat" }, { "command": "CodeBuddy.interviewMe",