Merge pull request #549 from devchat-ai/optimize_code_completion_240605

Refactor code for performance optimization and bug fixes
This commit is contained in:
boob.yang 2024-06-06 18:40:05 +08:00 committed by GitHub
commit 826806edad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 48 additions and 44 deletions

2
gui

@ -1 +1 @@
Subproject commit 750874981accb3c02d765557f0bd81fa6a2f220b Subproject commit 808bf945e263cebfdcbbd60696bae84f2a6d594d

View File

@ -277,8 +277,8 @@ export class LLMStreamComplete {
// whether lines are repeated some block before // whether lines are repeated some block before
async removeRepeatBlock(lines: string[]): Promise< string[] > { async removeRepeatBlock(lines: string[]): Promise< string[] > {
if (lines.length === 0) { if (lines.length <= 1) {
return []; return lines;
} }
// find first match line in before 50 lines // find first match line in before 50 lines

View File

@ -206,7 +206,7 @@ export async function * devchatComplete(prompt: string) : AsyncGenerator<CodeCom
model: model, model: model,
prompt: prompt, prompt: prompt,
stream: true, stream: true,
stop: ["<|endoftext|>", "<|EOT|>", "<file_sep>", "```", "/", "\n\n"], stop: ["<|endoftext|>", "<|EOT|>", "<file_sep>", "```", "//", "\n\n"],
temperature: 0.2 temperature: 0.2
}; };

View File

@ -42,16 +42,17 @@ function countTokens(
// defaults to llama2 because the tokenizer tends to produce more tokens // defaults to llama2 because the tokenizer tends to produce more tokens
modelName: string = "llama2", modelName: string = "llama2",
): number { ): number {
const encoding = encodingForModel(modelName); return content.length;
if (Array.isArray(content)) { // const encoding = encodingForModel(modelName);
return content.reduce((acc, part) => { // if (Array.isArray(content)) {
return acc + part.type === "imageUrl" // return content.reduce((acc, part) => {
? countImageTokens(part) // return acc + part.type === "imageUrl"
: encoding.encode(part.text ?? "", "all", []).length; // ? countImageTokens(part)
}, 0); // : encoding.encode(part.text ?? "", "all", []).length;
} else { // }, 0);
return encoding.encode(content, "all", []).length; // } else {
} // return encoding.encode(content, "all", []).length;
// }
} }
function flattenMessages(msgs: ChatMessage[]): ChatMessage[] { function flattenMessages(msgs: ChatMessage[]): ChatMessage[] {

View File

@ -27,7 +27,6 @@ import { findIdentifiersInAstNodeRange } from './ast/findIdentifiers';
import { DevChatConfig } from '../../util/config'; import { DevChatConfig } from '../../util/config';
const CONTEXT_LIMITED_SIZE: number = 6000;
const CONTEXT_SIMILAR_LIMITED_SIZE: number = 400; const CONTEXT_SIMILAR_LIMITED_SIZE: number = 400;
const variableCache: MemoryCacheManager = new MemoryCacheManager(4); const variableCache: MemoryCacheManager = new MemoryCacheManager(4);
@ -40,7 +39,7 @@ export async function currentFileContext(
curColumn: number curColumn: number
) : Promise< { prefix: string, suffix: string } > { ) : Promise< { prefix: string, suffix: string } > {
const contentTokens = countTokens(contents); const contentTokens = countTokens(contents);
if (contentTokens < CONTEXT_LIMITED_SIZE*0.35) { if (contentTokens < DevChatConfig.getInstance().get("complete_context_limit", 3000)*0.5) {
return curfilePrompt(filepath, contents, curRow, curColumn); return curfilePrompt(filepath, contents, curRow, curColumn);
} }
@ -51,7 +50,7 @@ export async function currentFileContext(
const functionRanges = await findFunctionRanges(filepath, ast.rootNode); const functionRanges = await findFunctionRanges(filepath, ast.rootNode);
return await collapseCodeBlock(functionRanges, filepath, contents, curRow, curColumn); return await collapseCodeBlock(functionRanges, filepath, contents, curRow, curColumn);
} }
export async function collapseCodeBlock(functions: FunctionRange[], filepath: string, contents: string, curRow: number, curColumn: number) { export async function collapseCodeBlock(functions: FunctionRange[], filepath: string, contents: string, curRow: number, curColumn: number) {
@ -186,7 +185,7 @@ async function curfilePrompt(filePath: string, fileContent: string, line: number
} }
const lineTokenCount = countTokens(lineText); const lineTokenCount = countTokens(lineText);
if (prefixTokenCount + lineTokenCount > CONTEXT_LIMITED_SIZE*0.7*0.35) { if (prefixTokenCount + lineTokenCount > DevChatConfig.getInstance().get("complete_context_limit", 3000)*0.7*0.35) {
break; break;
} }
@ -195,7 +194,7 @@ async function curfilePrompt(filePath: string, fileContent: string, line: number
} }
// 从光标所在行下一行开始,向下构建后缀 // 从光标所在行下一行开始,向下构建后缀
const suffixMaxToken = CONTEXT_LIMITED_SIZE*0.35 - prefixTokenCount; const suffixMaxToken = DevChatConfig.getInstance().get("complete_context_limit", 3000)*0.35 - prefixTokenCount;
for (let i = line; i < lines.length; i++) { for (let i = line; i < lines.length; i++) {
let lineText = lines[i] + '\n'; let lineText = lines[i] + '\n';
if (i === line) { if (i === line) {
@ -551,17 +550,10 @@ export async function createPrompt(filePath: string, fileContent: string, line:
let { prefix, suffix } = await currentFileContext(filePath, fileContent, line, column); let { prefix, suffix } = await currentFileContext(filePath, fileContent, line, column);
let tokenCount = countTokens(prefix); let tokenCount = countTokens(prefix) + countTokens(suffix);
const suffixTokenCount = countTokens(suffix);
if (tokenCount + suffixTokenCount < CONTEXT_LIMITED_SIZE) {
tokenCount += suffixTokenCount;
} else {
suffix = "";
}
let taskDescriptionContextWithCommentPrefix = ""; let taskDescriptionContextWithCommentPrefix = "";
if (tokenCount < CONTEXT_LIMITED_SIZE) { if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
const taskDescriptionContext = await createTaskDescriptionContext(); const taskDescriptionContext = await createTaskDescriptionContext();
if (taskDescriptionContext) { if (taskDescriptionContext) {
taskDescriptionContext.split("\n").forEach(line => { taskDescriptionContext.split("\n").forEach(line => {
@ -572,7 +564,7 @@ export async function createPrompt(filePath: string, fileContent: string, line:
} }
const taskDescriptionContextToken = countTokens(taskDescriptionContextWithCommentPrefix); const taskDescriptionContextToken = countTokens(taskDescriptionContextWithCommentPrefix);
if (tokenCount + taskDescriptionContextToken < CONTEXT_LIMITED_SIZE) { if (tokenCount + taskDescriptionContextToken < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
tokenCount += taskDescriptionContextToken; tokenCount += taskDescriptionContextToken;
} else { } else {
taskDescriptionContextWithCommentPrefix = ""; taskDescriptionContextWithCommentPrefix = "";
@ -580,9 +572,9 @@ export async function createPrompt(filePath: string, fileContent: string, line:
} }
// let gitDiffContext = GitDiffWatcher.getInstance().getGitDiffResult(); // let gitDiffContext = GitDiffWatcher.getInstance().getGitDiffResult();
// if (tokenCount < CONTEXT_LIMITED_SIZE && gitDiffContext.length > 0) { // if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000) && gitDiffContext.length > 0) {
// const gitDiffContextToken = countTokens(gitDiffContext); // const gitDiffContextToken = countTokens(gitDiffContext);
// if (tokenCount + gitDiffContextToken < CONTEXT_LIMITED_SIZE) { // if (tokenCount + gitDiffContextToken < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
// tokenCount += gitDiffContextToken; // tokenCount += gitDiffContextToken;
// gitDiffContext = "<git_diff_start>" + gitDiffContext + "<git_diff_end>\n\n\n\n"; // gitDiffContext = "<git_diff_start>" + gitDiffContext + "<git_diff_end>\n\n\n\n";
// } else { // } else {
@ -592,11 +584,11 @@ export async function createPrompt(filePath: string, fileContent: string, line:
let gitDiffContext = ""; let gitDiffContext = "";
let callDefContext = ""; let callDefContext = "";
if (tokenCount < CONTEXT_LIMITED_SIZE) { if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
const callCodeBlocks = await createContextCallDefine(filePath, fileContent, posoffset); const callCodeBlocks = await createContextCallDefine(filePath, fileContent, posoffset);
for (const callCodeBlock of callCodeBlocks) { for (const callCodeBlock of callCodeBlocks) {
const callBlockToken = countTokens(callCodeBlock.codeblock); const callBlockToken = countTokens(callCodeBlock.codeblock);
if (tokenCount + callBlockToken > CONTEXT_LIMITED_SIZE) { if (tokenCount + callBlockToken > DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
break; break;
} }
@ -607,18 +599,18 @@ export async function createPrompt(filePath: string, fileContent: string, line:
} }
let similarBlockContext = ""; let similarBlockContext = "";
if (tokenCount < CONTEXT_LIMITED_SIZE) { if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
let similarTokens = 0; let similarTokens = 0;
const similarContexts: {file: string, text: string}[] = await findSimilarCodeBlock(filePath, fileContent, line, column); const similarContexts: {file: string, text: string}[] = await findSimilarCodeBlock(filePath, fileContent, line, column);
for (const similarContext of similarContexts) { for (const similarContext of similarContexts) {
const blockToken = countTokens(similarContext.text); const blockToken = countTokens(similarContext.text);
if (tokenCount + blockToken > CONTEXT_LIMITED_SIZE) { if (tokenCount + blockToken > DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
continue; continue;
} }
similarTokens += blockToken; similarTokens += blockToken;
if (similarTokens > CONTEXT_SIMILAR_LIMITED_SIZE) { if (similarTokens > CONTEXT_SIMILAR_LIMITED_SIZE) {
continue; continue;
} }
tokenCount += blockToken; tokenCount += blockToken;
@ -628,11 +620,11 @@ export async function createPrompt(filePath: string, fileContent: string, line:
} }
let symbolContext = ""; let symbolContext = "";
if (tokenCount < CONTEXT_LIMITED_SIZE) { if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
const symbolDefines: { filepath: string, node: Parser.SyntaxNode, codeblock: string }[] = await symbolDefinesContext(filePath, fileContent, line, column); const symbolDefines: { filepath: string, node: Parser.SyntaxNode, codeblock: string }[] = await symbolDefinesContext(filePath, fileContent, line, column);
for (const symbolDefine of symbolDefines ) { for (const symbolDefine of symbolDefines ) {
const countSymboleToken = countTokens(symbolDefine.codeblock); const countSymboleToken = countTokens(symbolDefine.codeblock);
if (tokenCount + countSymboleToken > CONTEXT_LIMITED_SIZE) { if (tokenCount + countSymboleToken > DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
break; break;
} }
@ -644,11 +636,11 @@ export async function createPrompt(filePath: string, fileContent: string, line:
} }
let recentEditContext = ""; let recentEditContext = "";
if (tokenCount < CONTEXT_LIMITED_SIZE) { if (tokenCount < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
recentEditContext = await createRecentEditContext(recentEdits, filePath); recentEditContext = await createRecentEditContext(recentEdits, filePath);
const countRecentToken = countTokens(recentEditContext); const countRecentToken = countTokens(recentEditContext);
if (tokenCount + countRecentToken < CONTEXT_LIMITED_SIZE) { if (tokenCount + countRecentToken < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
tokenCount += countRecentToken; tokenCount += countRecentToken;
} else { } else {
recentEditContext = ""; recentEditContext = "";
@ -660,7 +652,7 @@ export async function createPrompt(filePath: string, fileContent: string, line:
const neighborFiles = await findNeighborFileContext(filePath, fileContent, line, column); const neighborFiles = await findNeighborFileContext(filePath, fileContent, line, column);
if (neighborFiles.length > 0) { if (neighborFiles.length > 0) {
const countFileToken = countTokens(neighborFiles[0].text); const countFileToken = countTokens(neighborFiles[0].text);
if (tokenCount + countFileToken < CONTEXT_LIMITED_SIZE) { if (tokenCount + countFileToken < DevChatConfig.getInstance().get("complete_context_limit", 3000)) {
tokenCount += countFileToken; tokenCount += countFileToken;
neighborFileContext += `${commentPrefix}<filename>neighbor files:\n\n ${neighborFiles[0].file}\n\n`; neighborFileContext += `${commentPrefix}<filename>neighbor files:\n\n ${neighborFiles[0].file}\n\n`;
neighborFileContext += `${neighborFiles[0].text}\n\n\n\n`; neighborFileContext += `${neighborFiles[0].text}\n\n\n\n`;

View File

@ -54,7 +54,7 @@ export class DevChatConfig {
} }
} }
public get(key: string | string[]): any { public get(key: string | string[], defaultValue: any = undefined): any {
// check if the config file has been modified // check if the config file has been modified
const currentModifyTime = fs.statSync(this.configFilePath).mtimeMs; const currentModifyTime = fs.statSync(this.configFilePath).mtimeMs;
if (currentModifyTime > this.lastModifyTime) { if (currentModifyTime > this.lastModifyTime) {
@ -68,7 +68,18 @@ export class DevChatConfig {
} else { } else {
keys = key; keys = key;
} }
return keys.reduce((prev, curr) => prev ? prev[curr] : undefined, this.data);
let value = this.data;
for (const k of keys) {
if (value && typeof value === 'object' && k in value) {
value = value[k];
} else {
// If the key is not found or value is not an object, return the default value
return defaultValue || undefined;
}
}
return value;
} }
public set(key: string | string[], value: any): void { public set(key: string | string[], value: any): void {