2023-05-11 14:55:34 +00:00
|
|
|
import { LinkedList } from "linked-list-typescript";
|
2023-05-24 16:21:38 +00:00
|
|
|
import { CompletionResponse, Choice } from "tabby-agent";
|
2023-05-11 14:55:34 +00:00
|
|
|
|
|
|
|
|
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;
|
2023-05-11 14:55:34 +00:00
|
|
|
private cache = new LinkedList<CompletionCacheEntry>();
|
|
|
|
|
|
|
|
|
|
constructor() {}
|
|
|
|
|
|
2023-05-12 15:10:57 +00:00
|
|
|
private refresh(entry: CompletionCacheEntry) {
|
2023-05-11 14:55:34 +00:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-11 14:55:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2023-05-24 16:21:38 +00:00
|
|
|
// Filter choices that start with inputted text after prompt
|
2023-05-11 14:55:34 +00:00
|
|
|
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);
|
2023-05-11 14:55:34 +00:00
|
|
|
return {
|
|
|
|
|
id: hit.entry.completion.id,
|
|
|
|
|
created: hit.entry.completion.created,
|
|
|
|
|
choices: hit.compatibleChoices,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|