Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 140 additions & 56 deletions src/commands/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ import Anthropic from "@anthropic-ai/sdk";
import { GenerativeModel, GoogleGenerativeAI } from "@google/generative-ai";
import Groq from "groq-sdk";
import * as vscode from "vscode";
import {
APP_CONFIG,
COMMON,
generativeAiModels,
} from "../application/constant";
import { APP_CONFIG, COMMON, generativeAiModels } from "../application/constant";
import { AnthropicWebViewProvider } from "../webview-providers/anthropic";
import { DeepseekWebViewProvider } from "../webview-providers/deepseek";
import { GeminiWebViewProvider } from "../webview-providers/gemini";
import { GroqWebViewProvider } from "../webview-providers/groq";
import {
Expand Down Expand Up @@ -44,7 +41,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
constructor(
private readonly action: string,
_context: vscode.ExtensionContext,
errorMessage?: string,
errorMessage?: string
) {
this.context = _context;
this.error = errorMessage;
Expand Down Expand Up @@ -78,23 +75,132 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
return getConfigValue(configKey);
}

protected createModel():
| { generativeAi: string; model: any; modelName: string }
| undefined {
/**
* Get command feedback with action name and description
*/
private getCommandFeedback(action: string): {
action: string;
description: string;
} {
const { CODEBUDDY_ACTIONS } = require("../application/constant");

const commandDescriptions: Record<string, { action: string; description: string }> = {
[CODEBUDDY_ACTIONS.comment]: {
action: "Adding Code Comments",
description:
"CodeBuddy is analyzing your code and adding meaningful comments that explain the intent and logic...",
},
[CODEBUDDY_ACTIONS.review]: {
action: "Reviewing Code Quality",
description:
"CodeBuddy is conducting a comprehensive code review covering security, performance, and best practices...",
},
[CODEBUDDY_ACTIONS.refactor]: {
action: "Refactoring Code",
description: "CodeBuddy is applying SOLID principles and design patterns to improve code maintainability...",
},
[CODEBUDDY_ACTIONS.optimize]: {
action: "Optimizing Performance",
description:
"CodeBuddy is analyzing algorithms and data structures to enhance code performance and efficiency...",
},
[CODEBUDDY_ACTIONS.fix]: {
action: "Fixing Code Issues",
description: "CodeBuddy is diagnosing the error and implementing defensive programming solutions...",
},
[CODEBUDDY_ACTIONS.explain]: {
action: "Explaining Code Logic",
description:
"CodeBuddy is breaking down complex code into clear, educational explanations with real-world context...",
},
[CODEBUDDY_ACTIONS.commitMessage]: {
action: "Generating Commit Message",
description: "CodeBuddy is analyzing your staged changes and crafting a professional commit message...",
},
[CODEBUDDY_ACTIONS.interviewMe]: {
action: "Preparing Interview Questions",
description: "CodeBuddy is creating comprehensive technical interview questions based on your code...",
},
[CODEBUDDY_ACTIONS.generateUnitTest]: {
action: "Generating Unit Tests",
description: "CodeBuddy is creating comprehensive test suites with edge cases and mocking strategies...",
},
[CODEBUDDY_ACTIONS.generateDiagram]: {
action: "Creating System Diagram",
description: "CodeBuddy is visualizing your code architecture with professional Mermaid diagrams...",
},
[CODEBUDDY_ACTIONS.reviewPR]: {
action: "Reviewing Pull Request",
description:
"CodeBuddy is conducting a thorough PR review with security, performance, and architecture analysis...",
},
[CODEBUDDY_ACTIONS.inlineChat]: {
action: "Processing Inline Request",
description: "CodeBuddy is analyzing your inline query and generating a contextual response...",
},
};

return (
commandDescriptions[action] || {
action: "Processing Request",
description: "CodeBuddy is analyzing your code and generating a response...",
}
);
}

/**
* Send command feedback to the webview
*/
private async sendCommandFeedback(action: string): Promise<void> {
const feedback = this.getCommandFeedback(action);

switch (this.generativeAi) {
case generativeAiModels.GROQ:
await GroqWebViewProvider.webView?.webview.postMessage({
type: "codebuddy-commands",
message: feedback,
});
break;
case generativeAiModels.GEMINI:
await GeminiWebViewProvider.webView?.webview.postMessage({
type: "codebuddy-commands",
message: feedback,
});
break;
case generativeAiModels.DEEPSEEK:
await DeepseekWebViewProvider.webView?.webview.postMessage({
type: "codebuddy-commands",
message: feedback,
});
break;
case generativeAiModels.ANTHROPIC:
case generativeAiModels.GROK:
await AnthropicWebViewProvider.webView?.webview.postMessage({
type: "codebuddy-commands",
message: feedback,
});
break;
default:
this.logger.error("Unknown generative AI", "");
break;
}
}

protected createModel(): { generativeAi: string; model: any; modelName: string } | undefined {
try {
let model;
let modelName = "";
if (!this.generativeAi) {
vscodeErrorMessage(
"Configuration not found. Go to settings, search for Your coding buddy. Fill up the model and model name",
"Configuration not found. Go to settings, search for Your coding buddy. Fill up the model and model name"
);
}
if (this.generativeAi === generativeAiModels.GROQ) {
const apiKey = this.groqApiKey;
modelName = this.groqModel;
if (!apiKey || !modelName) {
vscodeErrorMessage(
"Configuration not found. Go to settings, search for Your coding buddy. Fill up the model and model name",
"Configuration not found. Go to settings, search for Your coding buddy. Fill up the model and model name"
);
}
model = this.createGroqModel(apiKey);
Expand All @@ -120,9 +226,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
return { generativeAi: this.generativeAi, model, modelName };
} catch (error) {
console.error("Error creating model:", error);
vscode.window.showErrorMessage(
"An error occurred while creating the model. Please try again.",
);
vscode.window.showErrorMessage("An error occurred while creating the model. Please try again.");
}
}

Expand Down Expand Up @@ -155,9 +259,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
return new Groq({ apiKey });
}

protected async generateModelResponse(
text: string,
): Promise<string | Anthropic.Messages.Message | undefined> {
protected async generateModelResponse(text: string): Promise<string | Anthropic.Messages.Message | undefined> {
try {
if (text?.length > 0) {
this.orchestrator.publish("onUserPrompt", text);
Expand Down Expand Up @@ -197,7 +299,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {

if (!response) {
throw new Error(
"Could not generate response. Check your settings, ensure the API keys and Model Name is added properly.",
"Could not generate response. Check your settings, ensure the API keys and Model Name is added properly."
);
}
if (this.action.includes("chart")) {
Expand All @@ -208,9 +310,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
return response;
} catch (error) {
this.logger.error("Error generating response:", error);
vscode.window.showErrorMessage(
"An error occurred while generating the response. Please try again.",
);
vscode.window.showErrorMessage("An error occurred while generating the response. Please try again.");
}
}

Expand All @@ -221,18 +321,15 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
return inputString;
}

async generateGeminiResponse(
model: any,
text: string,
): Promise<string | undefined> {
async generateGeminiResponse(model: any, text: string): Promise<string | undefined> {
const result = await model.generateContent(text);
return result ? await result.response.text() : undefined;
}

private async anthropicResponse(
model: Anthropic,
generativeAiModel: string,
userPrompt: string,
userPrompt: string
): Promise<string | undefined> {
try {
const response = await model.messages.create({
Expand All @@ -256,15 +353,9 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
}
}

private async groqResponse(
model: Groq,
prompt: string,
generativeAiModel: string,
): Promise<string | undefined> {
private async groqResponse(model: Groq, prompt: string, generativeAiModel: string): Promise<string | undefined> {
try {
const chatHistory = Memory.has(COMMON.ANTHROPIC_CHAT_HISTORY)
? Memory.get(COMMON.GROQ_CHAT_HISTORY)
: [];
const chatHistory = Memory.has(COMMON.ANTHROPIC_CHAT_HISTORY) ? Memory.get(COMMON.GROQ_CHAT_HISTORY) : [];
const params = {
messages: [
...chatHistory,
Expand All @@ -276,8 +367,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
model: generativeAiModel,
};

const completion: Groq.Chat.ChatCompletion =
await model.chat.completions.create(params);
const completion: Groq.Chat.ChatCompletion = await model.chat.completions.create(params);
return completion.choices[0]?.message?.content ?? undefined;
} catch (error) {
this.logger.error("Error generating response:", error);
Expand All @@ -288,9 +378,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {

abstract createPrompt(text?: string): any;

async generateResponse(
message?: string,
): Promise<string | Anthropic.Messages.Message | undefined> {
async generateResponse(message?: string): Promise<string | Anthropic.Messages.Message | undefined> {
this.logger.info(this.action);
let prompt;
const selectedCode = this.getSelectedWindowArea();
Expand All @@ -302,9 +390,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
if (message && selectedCode) {
prompt = await this.createPrompt(`${message} \n ${selectedCode}`);
} else {
message
? (prompt = await this.createPrompt(message))
: (prompt = await this.createPrompt(selectedCode));
message ? (prompt = await this.createPrompt(message)) : (prompt = await this.createPrompt(selectedCode));
}

if (!prompt) {
Expand Down Expand Up @@ -387,9 +473,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
placeHolder: "Enter instructions for CodeBuddy",
ignoreFocusOut: true,
validateInput: (text) => {
return text === ""
? "Enter instructions for CodeBuddy or press Escape to close chat box"
: null;
return text === "" ? "Enter instructions for CodeBuddy or press Escape to close chat box" : null;
},
});
return userPrompt;
Expand All @@ -400,10 +484,11 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {

async execute(action?: string, message?: string): Promise<void> {
try {
// Send command feedback immediately at the start
await this.sendCommandFeedback(action || this.action);

let prompt: string | undefined;
const response = (await this.generateResponse(
prompt ?? message,
)) as string;
const response = (await this.generateResponse(prompt ?? message)) as string;
if (!response) {
vscode.window.showErrorMessage("model not reponding, try again later");
return;
Expand All @@ -426,12 +511,14 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
message: formattedResponse,
});
break;
case generativeAiModels.DEEPSEEK:
await DeepseekWebViewProvider.webView?.webview.postMessage({
type: "bot-response",
message: formattedResponse,
});
break;
case generativeAiModels.ANTHROPIC:
case generativeAiModels.GROK:
await AnthropicWebViewProvider.webView?.webview.postMessage({
type: "codebuddy-commands",
message: action,
});
await AnthropicWebViewProvider.webView?.webview.postMessage({
type: "bot-response",
message: formattedResponse,
Expand All @@ -442,10 +529,7 @@ export abstract class CodeCommandHandler implements ICodeCommandHandler {
break;
}
} catch (error) {
this.logger.error(
"Error while passing model response to the webview",
error,
);
this.logger.error("Error while passing model response to the webview", error);
}
}
}
Loading