tabby/clients/vscode/src/CompletionCache.ts

74 lines
1.9 KiB
TypeScript
Raw Normal View History

import { LinkedList } from "linked-list-typescript";
import { CompletionResponse, Choice } from "tabby-agent";
type Range = {
start: number;
end: number;
};
export type CompletionCacheEntry = {
documentId: any;
promptRange: Range;
prompt: string;
completion: CompletionResponse;
};
export class CompletionCache {
2023-05-12 15:10:57 +00:00
public static capacity = 10;
private cache = new LinkedList<CompletionCacheEntry>();
constructor() {}
2023-05-12 15:10:57 +00:00
private refresh(entry: CompletionCacheEntry) {
this.cache.remove(entry);
this.cache.prepend(entry);
}
public add(entry: CompletionCacheEntry) {
this.cache.prepend(entry);
2023-05-12 15:10:57 +00:00
while (this.cache.length > CompletionCache.capacity) {
this.cache.removeTail();
}
}
public findCompatible(documentId: any, text: string, cursor: number): CompletionResponse | null {
let hit: { entry: CompletionCacheEntry; compatibleChoices: Choice[] } | null = null;
for (const entry of this.cache) {
if (entry.documentId !== documentId) {
continue;
}
// Check if text in prompt range has not changed
if (text.slice(entry.promptRange.start, entry.promptRange.end) !== entry.prompt) {
continue;
}
// Filter choices that start with inputted text after prompt
const compatibleChoices = entry.completion.choices
.filter((choice) => choice.text.startsWith(text.slice(entry.promptRange.end, cursor)))
.map((choice) => {
return {
index: choice.index,
text: choice.text.substring(cursor - entry.promptRange.end),
};
});
if (compatibleChoices.length > 0) {
hit = {
entry,
compatibleChoices,
};
break;
}
}
if (hit) {
2023-05-12 15:10:57 +00:00
this.refresh(hit.entry);
return {
id: hit.entry.completion.id,
created: hit.entry.completion.created,
choices: hit.compatibleChoices,
};
}
return null;
}
}