Refactor DevChatConfig instantiation to use getInstance() method

This commit is contained in:
bobo.yang 2024-04-17 00:07:09 +08:00
parent da336b61ba
commit 2b505b5536
18 changed files with 70 additions and 38 deletions

View File

@ -12,7 +12,7 @@ import { createFileBlockInfo, createIdentifierSetByQuery } from './createIdentif
let indexStore: IndexStore | undefined = undefined;
const devchatConfig = new DevChatConfig();
const devchatConfig = DevChatConfig.getInstance();
export const BLACK_LIST_DIRS = [
"node_modules",

View File

@ -61,7 +61,7 @@ export class InlineCompletionProvider implements vscode.InlineCompletionItemProv
// Read delay time from config
this.debouncer = new Debouncer(500);
this.cache = new MemoryCacheManager();
this.devchatConfig = new DevChatConfig();
this.devchatConfig = DevChatConfig.getInstance();
this.lastComplete = "";
this.recentEditors = new RecentEditsManager();
}

View File

@ -16,15 +16,23 @@ export interface CodeCompletionChunk {
}
export async function* streamComplete(prompt: string): AsyncGenerator<CodeCompletionChunk> {
for await (const chunk of nvidiaStarcoderComplete(prompt)) {
yield chunk;
const nvidiaKey = DevChatConfig.getInstance().get("complete_key");
const ollamaApiBase = DevChatConfig.getInstance().get("complete_ollama_api_base");
if (ollamaApiBase) {
for await (const chunk of ollamaDeepseekComplete(prompt)) {
yield chunk;
}
} else if (nvidiaKey) {
for await (const chunk of nvidiaStarcoderComplete(prompt)) {
yield chunk;
}
}
}
export async function * nvidiaStarcoderComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> {
const invokeUrl = 'https://api.nvcf.nvidia.com/v2/nvcf/pexec/functions/6acada03-fe2f-4e4d-9e0a-e711b9fd1b59';
const nvidiaKey = new DevChatConfig().get("complete_key");
const nvidiaKey = DevChatConfig.getInstance().get("complete_key");
if (!nvidiaKey) {
return;
}
@ -89,17 +97,24 @@ export async function * nvidiaStarcoderComplete(prompt: string) : AsyncGenerator
}
}
export async function * ollamaStarcoderComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> {
const url = 'http://192.168.1.138:11434/api/generate';
export async function * ollamaDeepseekComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> {
const ollamaApiBase = DevChatConfig.getInstance().get("complete_ollama_api_base");
if (!ollamaApiBase) {
return;
}
const urlBase = ollamaApiBase.trim().endsWith('/') ? ollamaApiBase : ollamaApiBase + '/';
const url = urlBase + 'api/generate';
const headers = {
'Content-Type': 'application/json',
};
const payload = {
model: 'starcoder:7b',
model: 'deepseek-coder:6.7b-base',
prompt: prompt,
stream: true,
options: {
stop: ["<|endoftext|>", "<file_sep>", "```", "\n\n"],
stop: ["<|endoftext|>", "<|EOT|>", "<file_sep>", "```", "/", "\n\n"],
temperature: 0.2
}
};
@ -131,8 +146,8 @@ export async function * ollamaStarcoderComplete(prompt: string) : AsyncGenerator
id: idResponse!
};
} catch (e: any) {
logger.channel()?.info("receve:", chunkText);
logger.channel()?.error("JSON Parsing Error:", e.message);
// logger.channel()?.info("receive:", chunkText);
logger.channel()?.warn("JSON Parsing fail:", e.message);
}
}
} else {

View File

@ -24,6 +24,7 @@ import MemoryCacheManager from "./cache";
import { searchSimilarBlock } from "./astIndex";
import { GitDiffWatcher } from "./gitDiffWatcher";
import { findIdentifiersInAstNodeRange } from './ast/findIdentifiers';
import { DevChatConfig } from '../../util/config';
const CONTEXT_LIMITED_SIZE: number = 6000;
@ -674,7 +675,14 @@ export async function createPrompt(filePath: string, fileContent: string, line:
logger.channel()?.info("Complete token:", tokenCount);
const prompt = "<fim_prefix>" + taskDescriptionContextWithCommentPrefix + neighborFileContext + recentEditContext + symbolContext + callDefContext + similarBlockContext + gitDiffContext + `${commentPrefix}<filename>${filePath}\n\n` + prefix + "<fim_suffix>" + suffix + "<fim_middle>";
let prompt = "";
const ollamaApiBase = DevChatConfig.getInstance().get("complete_ollama_api_base");
if (ollamaApiBase) {
prompt = "<fim▁begin>" + taskDescriptionContextWithCommentPrefix + neighborFileContext + recentEditContext + symbolContext + callDefContext + similarBlockContext + gitDiffContext + `${commentPrefix}<filename>${filePath}\n\n` + prefix + "<fim▁hole>" + suffix + "<fim▁end>";
} else {
prompt = "<fim_prefix>" + taskDescriptionContextWithCommentPrefix + neighborFileContext + recentEditContext + symbolContext + callDefContext + similarBlockContext + gitDiffContext + `${commentPrefix}<filename>${filePath}\n\n` + prefix + "<fim_suffix>" + suffix + "<fim_middle>";
}
return prompt;
}

View File

@ -25,7 +25,7 @@ import { DevChatConfig } from '../../../util/config';
// 记录文件的最后修改时间
const lastModified = new Map<string, number>();
const devchatConfig = new DevChatConfig();
const devchatConfig = DevChatConfig.getInstance();
const BLACK_LIST_DIRS = [
"node_modules",

View File

@ -145,7 +145,7 @@ export function regPythonPathCommand(context: vscode.ExtensionContext) {
})) ?? "";
if (pythonPath) {
new DevChatConfig().set("python_for_chat", pythonPath);
DevChatConfig.getInstance().set("python_for_chat", pythonPath);
}
})
);
@ -290,7 +290,7 @@ export function registerInstallCommandsPython(context: vscode.ExtensionContext)
return '';
}
new DevChatConfig().set("python_for_commands", pythonCommand.trim());
DevChatConfig.getInstance().set("python_for_commands", pythonCommand.trim());
// vscode.window.showInformationMessage(`All slash Commands are ready to use! Please input / to try workflow commands!`);
});
@ -352,7 +352,7 @@ export function registerHandleUri(context: vscode.ExtensionContext) {
// 解析 URI 并执行相应的操作
if (uri.path.includes("accesskey")) {
const accessKey = uri.path.split("/")[2];
new DevChatConfig().set("provides.devchat.api_key", accessKey);
DevChatConfig.getInstance().set("provides.devchat.api_key", accessKey);
ensureChatPanel(context);
await new Promise((resolve, reject) => {
setTimeout(() => {

View File

@ -43,7 +43,7 @@ function getDefaultPythonCommand(): string | undefined {
export function getValidPythonCommand(): string | undefined {
try {
const devchatConfig = new DevChatConfig();
const devchatConfig = DevChatConfig.getInstance();
const pythonCommand = devchatConfig.get('python_for_chat');
if (pythonCommand) {
return pythonCommand;

View File

@ -36,7 +36,7 @@ import { indexDir } from "./contributes/codecomplete/astIndex";
async function migrateConfig() {
const devchatConfig = new DevChatConfig();
const devchatConfig = DevChatConfig.getInstance();
const devchatProvider = "providers.devchat";
const devchatProviderConfig: any = devchatConfig.get(devchatProvider);
if (devchatProviderConfig) {

View File

@ -11,10 +11,10 @@ regInMessage({command: 'readConfig', key: ['A','B']}); // when key is "", it wil
regOutMessage({command: 'readConfig', key: ['A', 'B'], value: 'any'});
export async function readConfig(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
if (message.key === '' || message.key === '*' || message.key.length === 0 || message.key[1] === '*') {
const config = new DevChatConfig().getAll();
const config = DevChatConfig.getInstance().getAll();
MessageHandler.sendMessage(panel, {command: 'readConfig', key: message.key, value: config});
} else {
const config = new DevChatConfig().get(message.key);
const config = DevChatConfig.getInstance().get(message.key);
MessageHandler.sendMessage(panel, {command: 'readConfig', key: message.key, value: config});
}
}
@ -22,8 +22,8 @@ export async function readConfig(message: any, panel: vscode.WebviewPanel|vscode
regInMessage({command: 'writeConfig', key: ['A', 'B'], value: 'any'}); // when key is "", it will rewrite all config values
export async function writeConfig(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
if (message.key === '' || message.key === '*' || message.key.length === 0 || message.key[1] === '*') {
new DevChatConfig().setAll(message.value);
DevChatConfig.getInstance().setAll(message.value);
} else {
new DevChatConfig().set(message.key, message.value);
DevChatConfig.getInstance().set(message.key, message.value);
}
}

View File

@ -11,7 +11,7 @@ regInMessage({command: 'historyMessages', topicId: '', page: 0});
regOutMessage({command: 'loadHistoryMessages', entries: [{hash: '',user: '',date: '',request: '',response: '',context: [{content: '',role: ''}]}]});
export async function getHistoryMessages(message: {command: string, topicId: string, page: number}, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> {
// if history message has load, send it to webview
const maxCount = Number(new DevChatConfig().get('max_log_count'));
const maxCount = Number(DevChatConfig.getInstance().get('max_log_count'));
const skip = maxCount * (message.page ? message.page : 0);
const topicId = message.topicId;

View File

@ -1,7 +1,7 @@
import { DevChatConfig } from "../../util/config";
export async function ideLanguage() {
const language = new DevChatConfig().get('language');
const language = DevChatConfig.getInstance().get('language');
// 'en' stands for English, 'zh' stands for Simplified Chinese
return language;
}

View File

@ -25,7 +25,7 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St
function checkDevChatCommandsStatus() {
const timerDevchatCommands = setInterval(async () => {
try {
const pythonCommand = new DevChatConfig().get('python_for_commands');
const pythonCommand = DevChatConfig.getInstance().get('python_for_commands');
if (!pythonCommand) {
statusBarItem.text = `$(pass)DevChat$(warning)`;
statusBarItem.tooltip = `ready to chat, command functionality limited`;

View File

@ -123,7 +123,7 @@ class DevChat {
assertValue(!llmModelData || !llmModelData.model, 'You must select a LLM model to use for conversations');
args.push("-m", llmModelData.model);
const functionCalling = new DevChatConfig().get('enable_function_calling');
const functionCalling = DevChatConfig.getInstance().get('enable_function_calling');
if (functionCalling) {
args.push("-a");
}
@ -140,7 +140,7 @@ class DevChat {
if (options.maxCount) {
args.push('--max-count', `${options.maxCount}`);
} else {
const maxLogCount = new DevChatConfig().get('max_log_count');
const maxLogCount = DevChatConfig.getInstance().get('max_log_count');
args.push('--max-count', `${maxLogCount}`);
}
@ -211,7 +211,7 @@ class DevChat {
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages"
};
const pythonApp = new DevChatConfig().get('python_for_chat') || "python3";
const pythonApp = DevChatConfig.getInstance().get('python_for_chat') || "python3";
// run command
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(
@ -251,7 +251,7 @@ class DevChat {
// eslint-disable-next-line @typescript-eslint/naming-convention
"PYTHONUTF8": 1,
// eslint-disable-next-line @typescript-eslint/naming-convention
"command_python": new DevChatConfig().get('python_for_commands') || "",
"command_python": DevChatConfig.getInstance().get('python_for_commands') || "",
// eslint-disable-next-line @typescript-eslint/naming-convention
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
// eslint-disable-next-line @typescript-eslint/naming-convention
@ -279,7 +279,7 @@ class DevChat {
onData(data);
};
// run command
const pythonApp = new DevChatConfig().get('python_for_chat') || "python3";
const pythonApp = DevChatConfig.getInstance().get('python_for_chat') || "python3";
logger.channel()?.info(`Running devchat:${pythonApp} ${args.join(" ")}`);
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, args, spawnAsyncOptions, onStdoutPartial, undefined, undefined, undefined);
// handle result

View File

@ -7,7 +7,7 @@ import { logger } from './logger';
export class ApiKeyManager {
static async llmModel() {
const devchatConfig = new DevChatConfig();
const devchatConfig = DevChatConfig.getInstance();
const defaultModel = devchatConfig.get('default_model');
if (!defaultModel) {
return undefined;

View File

@ -5,18 +5,27 @@ import { logger } from './logger';
export class DevChatConfig {
private static instance: DevChatConfig;
// 配置文件路径,根据操作系统的差异,可能需要调整
private configFilePath: string;
private data: any;
// last modify timestamp of the config file
private lastModifyTime: number;
constructor() {
private constructor() {
// 视操作系统的差异,可能需要调整路径 ~/.chat/config.yml
this.configFilePath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.chat', 'config.yml');
this.lastModifyTime = 0;
this.readConfigFile();
}
public static getInstance(): DevChatConfig {
if (!DevChatConfig.instance) {
DevChatConfig.instance = new DevChatConfig();
}
return DevChatConfig.instance;
}
private readConfigFile() {
try {
const fileContents = fs.readFileSync(this.configFilePath, 'utf8');

View File

@ -14,7 +14,7 @@ const featureToggles = JSON.parse(featureTogglesJson);
// eslint-disable-next-line @typescript-eslint/naming-convention
export function FT(feature: string): boolean {
const betaInvitationCode = new DevChatConfig().get('beta_invitation_code');
const betaInvitationCode = DevChatConfig.getInstance().get('beta_invitation_code');
const expectedInvitationCode = 'WELCOMEADDTODEVCHAT';
return betaInvitationCode === expectedInvitationCode || featureToggles[feature] === true;

View File

@ -45,7 +45,7 @@ export async function installDevchat(): Promise<string> {
fs.writeFileSync(pythonPathFile, content);
// update DevChat.PythonForChat configration
await new DevChatConfig().set("python_for_chat", pythonApp);
await DevChatConfig.getInstance().set("python_for_chat", pythonApp);
return pythonApp;
} else {
// if current os is not windows, we need to get default python path
@ -70,7 +70,7 @@ export async function installDevchat(): Promise<string> {
}
logger.channel()?.info(`Create env success: ${pythonCommand}`);
await new DevChatConfig().set("python_for_chat", pythonCommand);
await DevChatConfig.getInstance().set("python_for_chat", pythonCommand);
return pythonCommand;
}
} catch (error) {

View File

@ -41,12 +41,12 @@ describe('DevChatConfig', () => {
});
it('should read config file and get the correct value for a given key', () => {
const config = new DevChatConfig();
const config = DevChatConfig.getInstance();
expect(config.get('username')).to.equal('DevUser');
});
it('should set a new key-value pair and write to the config file', () => {
const config = new DevChatConfig();
const config = DevChatConfig.getInstance();
const newKey = 'notifications.enabled';
const newValue = true;
@ -61,7 +61,7 @@ describe('DevChatConfig', () => {
readFileStub.throws(new Error('Failed to read file'));
// Constructing the config will attempt to read the file and log an error
const config = new DevChatConfig();
const config = DevChatConfig.getInstance();
// Check if the error was logged
sinon.assert.called(loggerStub);