Skip to content

Commit

Permalink
Merge pull request #115 from olasunkanmi-SE/inline-chat
Browse files Browse the repository at this point in the history
feat(api, ui): Add in-line chat functionality
  • Loading branch information
olasunkanmi-SE authored Dec 18, 2024
2 parents c24b9cc + d6423c1 commit 1af6295
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 38 deletions.
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@
"command": "CodeBuddy.generateCommitMessage",
"group": "CodeBuddy"
},
{
"command": "CodeBuddy.inLineChat",
"group":"CodeBuddy"
},
{
"when": "editorHasSelection",
"command": "CodeBuddy.interviewMe",
Expand Down Expand Up @@ -136,6 +140,10 @@
"command": "CodeBuddy.generateCommitMessage",
"title": "CodeBuddy. Generate commit message"
},
{
"command": "CodeBuddy.inLineChat",
"title": "CodeBuddy. Inline chat"
},
{
"command": "CodeBuddy.interviewMe",
"title": "CodeBuddy. Interview Me."
Expand Down
1 change: 1 addition & 0 deletions src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum OLA_ACTIONS {
interviewMe = "CodeBuddy.interviewMe",
generateUnitTest = "CodeBuddy.generateUnitTest",
generateCodeChart = "CodeBuddy.generateCodeChart",
inlineChat = "CodeBuddy.inLineChat",
}

export enum COMMON {
Expand Down
47 changes: 40 additions & 7 deletions src/events/event-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,19 +278,23 @@ export abstract class EventGenerator implements IEventGenerator {
abstract createPrompt(text?: string): any;

async generateResponse(
errorMessage?: string,
message?: string,
): Promise<string | Anthropic.Messages.Message | undefined> {
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");
Expand Down Expand Up @@ -366,8 +370,37 @@ export abstract class EventGenerator implements IEventGenerator {
return response;
}

async execute(errorMessage?: string): Promise<void> {
const response = (await this.generateResponse(errorMessage)) as string;
async getUserInLineChat(): Promise<string | undefined> {
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<void> {
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;
Expand Down
25 changes: 25 additions & 0 deletions src/events/inline-chat.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
7 changes: 7 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
10 changes: 5 additions & 5 deletions src/providers/code-actions-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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}`
Expand All @@ -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);
}
Expand Down
8 changes: 4 additions & 4 deletions src/providers/groq-web-view-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean | undefined> {
try {
const type = currentChat === "bot" ? "bot-response" : "user-input";
Expand Down Expand Up @@ -60,7 +60,7 @@ export class GroqWebViewProvider extends BaseWebViewProvider {
async generateResponse(
apiKey = undefined,
name = undefined,
message: string
message: string,
): Promise<string | undefined> {
try {
const { temperature, max_tokens, top_p, stop } = GROQ_CONFIG;
Expand Down Expand Up @@ -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;
}
Expand Down
44 changes: 22 additions & 22 deletions src/services/chat-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
);
}
}
},
);
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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);
}
Expand All @@ -227,24 +227,24 @@ 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) {
throw new Error(`Configuration not found for ${this.generativeAi}`);
}

const anthropicWebViewProvider = this.getAnthropicWebViewProvider(
anthropicConfigurations
anthropicConfigurations,
);
anthropicWebViewProvider.sendResponse(
formatText(userInput),
COMMON.USER_INPUT
COMMON.USER_INPUT,
);
anthropicWebViewProvider.sendResponse(formatText(response), COMMON.BOT);
}
Expand All @@ -256,7 +256,7 @@ export class ChatManager {
}

private getAnthropicWebViewProvider(
config: aIProviderConfig
config: aIProviderConfig,
): AnthropicWebViewProvider {
let xGrokBaseURL;
if (getConfigValue(APP_CONFIG.generativeAi) === generativeAiModels.GROK) {
Expand All @@ -267,7 +267,7 @@ export class ChatManager {
config.apiKey,
config.model,
this._context,
xGrokBaseURL
xGrokBaseURL,
);
}
}

0 comments on commit 1af6295

Please sign in to comment.