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; let indexStore: IndexStore | undefined = undefined;
const devchatConfig = new DevChatConfig(); const devchatConfig = DevChatConfig.getInstance();
export const BLACK_LIST_DIRS = [ export const BLACK_LIST_DIRS = [
"node_modules", "node_modules",

View File

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

View File

@ -16,15 +16,23 @@ export interface CodeCompletionChunk {
} }
export async function* streamComplete(prompt: string): AsyncGenerator<CodeCompletionChunk> { export async function* streamComplete(prompt: string): AsyncGenerator<CodeCompletionChunk> {
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)) { for await (const chunk of nvidiaStarcoderComplete(prompt)) {
yield chunk; yield chunk;
} }
} }
}
export async function * nvidiaStarcoderComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> { 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 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) { if (!nvidiaKey) {
return; return;
} }
@ -89,17 +97,24 @@ export async function * nvidiaStarcoderComplete(prompt: string) : AsyncGenerator
} }
} }
export async function * ollamaStarcoderComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> { export async function * ollamaDeepseekComplete(prompt: string) : AsyncGenerator<CodeCompletionChunk> {
const url = 'http://192.168.1.138:11434/api/generate'; 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 = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}; };
const payload = { const payload = {
model: 'starcoder:7b', model: 'deepseek-coder:6.7b-base',
prompt: prompt, prompt: prompt,
stream: true, stream: true,
options: { options: {
stop: ["<|endoftext|>", "<file_sep>", "```", "\n\n"], stop: ["<|endoftext|>", "<|EOT|>", "<file_sep>", "```", "/", "\n\n"],
temperature: 0.2 temperature: 0.2
} }
}; };
@ -131,8 +146,8 @@ export async function * ollamaStarcoderComplete(prompt: string) : AsyncGenerator
id: idResponse! id: idResponse!
}; };
} catch (e: any) { } catch (e: any) {
logger.channel()?.info("receve:", chunkText); // logger.channel()?.info("receive:", chunkText);
logger.channel()?.error("JSON Parsing Error:", e.message); logger.channel()?.warn("JSON Parsing fail:", e.message);
} }
} }
} else { } else {

View File

@ -24,6 +24,7 @@ import MemoryCacheManager from "./cache";
import { searchSimilarBlock } from "./astIndex"; import { searchSimilarBlock } from "./astIndex";
import { GitDiffWatcher } from "./gitDiffWatcher"; import { GitDiffWatcher } from "./gitDiffWatcher";
import { findIdentifiersInAstNodeRange } from './ast/findIdentifiers'; import { findIdentifiersInAstNodeRange } from './ast/findIdentifiers';
import { DevChatConfig } from '../../util/config';
const CONTEXT_LIMITED_SIZE: number = 6000; 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); 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; return prompt;
} }

View File

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

View File

@ -145,7 +145,7 @@ export function regPythonPathCommand(context: vscode.ExtensionContext) {
})) ?? ""; })) ?? "";
if (pythonPath) { 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 ''; 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!`); // 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 并执行相应的操作 // 解析 URI 并执行相应的操作
if (uri.path.includes("accesskey")) { if (uri.path.includes("accesskey")) {
const accessKey = uri.path.split("/")[2]; const accessKey = uri.path.split("/")[2];
new DevChatConfig().set("provides.devchat.api_key", accessKey); DevChatConfig.getInstance().set("provides.devchat.api_key", accessKey);
ensureChatPanel(context); ensureChatPanel(context);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {

View File

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

View File

@ -36,7 +36,7 @@ import { indexDir } from "./contributes/codecomplete/astIndex";
async function migrateConfig() { async function migrateConfig() {
const devchatConfig = new DevChatConfig(); const devchatConfig = DevChatConfig.getInstance();
const devchatProvider = "providers.devchat"; const devchatProvider = "providers.devchat";
const devchatProviderConfig: any = devchatConfig.get(devchatProvider); const devchatProviderConfig: any = devchatConfig.get(devchatProvider);
if (devchatProviderConfig) { 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'}); regOutMessage({command: 'readConfig', key: ['A', 'B'], value: 'any'});
export async function readConfig(message: any, panel: vscode.WebviewPanel|vscode.WebviewView): Promise<void> { 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] === '*') { 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}); MessageHandler.sendMessage(panel, {command: 'readConfig', key: message.key, value: config});
} else { } 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}); 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 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> { 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] === '*') { if (message.key === '' || message.key === '*' || message.key.length === 0 || message.key[1] === '*') {
new DevChatConfig().setAll(message.value); DevChatConfig.getInstance().setAll(message.value);
} else { } 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: ''}]}]}); 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> { 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 // 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 skip = maxCount * (message.page ? message.page : 0);
const topicId = message.topicId; const topicId = message.topicId;

View File

@ -1,7 +1,7 @@
import { DevChatConfig } from "../../util/config"; import { DevChatConfig } from "../../util/config";
export async function ideLanguage() { 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 // 'en' stands for English, 'zh' stands for Simplified Chinese
return language; return language;
} }

View File

@ -25,7 +25,7 @@ export function createStatusBarItem(context: vscode.ExtensionContext): vscode.St
function checkDevChatCommandsStatus() { function checkDevChatCommandsStatus() {
const timerDevchatCommands = setInterval(async () => { const timerDevchatCommands = setInterval(async () => {
try { try {
const pythonCommand = new DevChatConfig().get('python_for_commands'); const pythonCommand = DevChatConfig.getInstance().get('python_for_commands');
if (!pythonCommand) { if (!pythonCommand) {
statusBarItem.text = `$(pass)DevChat$(warning)`; statusBarItem.text = `$(pass)DevChat$(warning)`;
statusBarItem.tooltip = `ready to chat, command functionality limited`; 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'); assertValue(!llmModelData || !llmModelData.model, 'You must select a LLM model to use for conversations');
args.push("-m", llmModelData.model); args.push("-m", llmModelData.model);
const functionCalling = new DevChatConfig().get('enable_function_calling'); const functionCalling = DevChatConfig.getInstance().get('enable_function_calling');
if (functionCalling) { if (functionCalling) {
args.push("-a"); args.push("-a");
} }
@ -140,7 +140,7 @@ class DevChat {
if (options.maxCount) { if (options.maxCount) {
args.push('--max-count', `${options.maxCount}`); args.push('--max-count', `${options.maxCount}`);
} else { } else {
const maxLogCount = new DevChatConfig().get('max_log_count'); const maxLogCount = DevChatConfig.getInstance().get('max_log_count');
args.push('--max-count', `${maxLogCount}`); args.push('--max-count', `${maxLogCount}`);
} }
@ -211,7 +211,7 @@ class DevChat {
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages" "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 // run command
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync( const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(
@ -251,7 +251,7 @@ class DevChat {
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
"PYTHONUTF8": 1, "PYTHONUTF8": 1,
// eslint-disable-next-line @typescript-eslint/naming-convention // 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 // eslint-disable-next-line @typescript-eslint/naming-convention
"PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages", "PYTHONPATH": UiUtilWrapper.extensionPath() + "/tools/site-packages",
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
@ -279,7 +279,7 @@ class DevChat {
onData(data); onData(data);
}; };
// run command // 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(" ")}`); logger.channel()?.info(`Running devchat:${pythonApp} ${args.join(" ")}`);
const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, args, spawnAsyncOptions, onStdoutPartial, undefined, undefined, undefined); const { exitCode: code, stdout, stderr } = await this.commandRun.spawnAsync(pythonApp, args, spawnAsyncOptions, onStdoutPartial, undefined, undefined, undefined);
// handle result // handle result

View File

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

View File

@ -5,18 +5,27 @@ import { logger } from './logger';
export class DevChatConfig { export class DevChatConfig {
private static instance: DevChatConfig;
// 配置文件路径,根据操作系统的差异,可能需要调整
private configFilePath: string; private configFilePath: string;
private data: any; private data: any;
// last modify timestamp of the config file // last modify timestamp of the config file
private lastModifyTime: number; private lastModifyTime: number;
constructor() { private constructor() {
// 视操作系统的差异,可能需要调整路径 ~/.chat/config.yml // 视操作系统的差异,可能需要调整路径 ~/.chat/config.yml
this.configFilePath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.chat', 'config.yml'); this.configFilePath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.chat', 'config.yml');
this.lastModifyTime = 0; this.lastModifyTime = 0;
this.readConfigFile(); this.readConfigFile();
} }
public static getInstance(): DevChatConfig {
if (!DevChatConfig.instance) {
DevChatConfig.instance = new DevChatConfig();
}
return DevChatConfig.instance;
}
private readConfigFile() { private readConfigFile() {
try { try {
const fileContents = fs.readFileSync(this.configFilePath, 'utf8'); 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 // eslint-disable-next-line @typescript-eslint/naming-convention
export function FT(feature: string): boolean { 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'; const expectedInvitationCode = 'WELCOMEADDTODEVCHAT';
return betaInvitationCode === expectedInvitationCode || featureToggles[feature] === true; return betaInvitationCode === expectedInvitationCode || featureToggles[feature] === true;

View File

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

View File

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