Skip to content

Commit b198148

Browse files
SibidharanSibidharan
authored andcommitted
Improve request log tree performance
1 parent 5dc4178 commit b198148

File tree

1 file changed

+73
-36
lines changed

1 file changed

+73
-36
lines changed

src/extension/log/vscode-node/requestLogTree.ts

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,10 @@ type TreeItem = ChatPromptItem | ChatRequestItem | ChatElementItem | ToolCallIte
559559

560560
class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<TreeItem> {
561561
private readonly filters: LogTreeFilters;
562+
private rootItems: (ChatPromptItem | TreeChildItem)[] = [];
563+
private seenChatRequests = new WeakSet<ChatRequest>();
564+
private processedCount = 0;
565+
private currentPrompt: ChatPromptItem | undefined;
562566

563567
constructor(
564568
@IRequestLogger private readonly requestLogger: IRequestLogger,
@@ -567,8 +571,9 @@ class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<
567571
super();
568572
this.filters = this._register(instantiationService.createInstance(LogTreeFilters));
569573
this._register(new LogTreeFilterCommands(this.filters));
570-
this._register(this.requestLogger.onDidChangeRequests(() => this._onDidChangeTreeData.fire()));
574+
this._register(this.requestLogger.onDidChangeRequests(() => this.handleRequestChange()));
571575
this._register(this.filters.onDidChangeFilters(() => this._onDidChangeTreeData.fire()));
576+
this.rebuildFromScratch();
572577
}
573578

574579
private readonly _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined | void>();
@@ -580,48 +585,80 @@ class ChatRequestProvider extends Disposable implements vscode.TreeDataProvider<
580585

581586
getChildren(element?: TreeItem | undefined): vscode.ProviderResult<TreeItem[]> {
582587
if (element instanceof ChatPromptItem) {
583-
return element.children;
584-
} else if (element) {
588+
return element.children.filter(child => this.filters.itemIncluded(child));
589+
}
590+
591+
if (element) {
585592
return [];
586-
} else {
587-
let lastPrompt: ChatPromptItem | undefined;
588-
const result: (ChatPromptItem | TreeChildItem)[] = [];
589-
const seen = new Set<ChatRequest | undefined>();
590-
591-
for (const r of this.requestLogger.getRequests()) {
592-
const item = this.logToTreeItem(r);
593-
if (r.chatRequest !== lastPrompt?.request) {
594-
if (lastPrompt) {
595-
result.push(lastPrompt);
596-
}
597-
lastPrompt = r.chatRequest ? ChatPromptItem.create(r, r.chatRequest, seen.has(r.chatRequest)) : undefined;
598-
seen.add(r.chatRequest);
593+
}
594+
595+
return this.rootItems.filter(item => this.filters.itemIncluded(item));
596+
}
597+
598+
private rebuildFromScratch(): void {
599+
this.rootItems = [];
600+
this.seenChatRequests = new WeakSet<ChatRequest>();
601+
this.processedCount = 0;
602+
this.currentPrompt = undefined;
603+
this.appendNewEntries();
604+
}
605+
606+
private handleRequestChange(): void {
607+
this.appendNewEntries();
608+
}
609+
610+
private appendNewEntries(): void {
611+
const requests = this.requestLogger.getRequests();
612+
let rootChanged = false;
613+
const promptsToRefresh = new Set<ChatPromptItem>();
614+
const newPrompts = new Set<ChatPromptItem>();
615+
616+
if (requests.length < this.processedCount) {
617+
this.rootItems = [];
618+
this.seenChatRequests = new WeakSet<ChatRequest>();
619+
this.processedCount = 0;
620+
this.currentPrompt = undefined;
621+
rootChanged = true;
622+
}
623+
624+
for (let i = this.processedCount; i < requests.length; i++) {
625+
const info = requests[i];
626+
const child = this.logToTreeItem(info);
627+
const request = info.chatRequest;
628+
629+
if (request) {
630+
const hasSeen = this.seenChatRequests.has(request);
631+
if (!this.currentPrompt || this.currentPrompt.request !== request) {
632+
this.currentPrompt = ChatPromptItem.create(info, request, hasSeen);
633+
this.currentPrompt.children = [];
634+
this.rootItems.push(this.currentPrompt);
635+
this.seenChatRequests.add(request);
636+
rootChanged = true;
637+
newPrompts.add(this.currentPrompt);
599638
}
600639

601-
if (lastPrompt) {
602-
if (!lastPrompt.children.find(c => c.id === item.id)) {
603-
lastPrompt.children.push(item);
604-
}
605-
if (!lastPrompt.children.find(c => c.id === item.id)) {
606-
lastPrompt.children.push(item);
607-
}
608-
} else {
609-
result.push(item);
640+
if (!this.currentPrompt.children.some(c => c.id === child.id)) {
641+
this.currentPrompt.children.push(child);
642+
promptsToRefresh.add(this.currentPrompt);
610643
}
644+
} else {
645+
this.currentPrompt = undefined;
646+
this.rootItems.push(child);
647+
rootChanged = true;
611648
}
649+
}
612650

613-
if (lastPrompt) {
614-
result.push(lastPrompt);
615-
}
651+
this.processedCount = requests.length;
616652

617-
return result.map(r => {
618-
if (r instanceof ChatPromptItem) {
619-
return r.withFilteredChildren(child => this.filters.itemIncluded(child));
620-
}
653+
if (rootChanged) {
654+
this._onDidChangeTreeData.fire(undefined);
655+
}
621656

622-
return r;
623-
})
624-
.filter(r => this.filters.itemIncluded(r));
657+
for (const prompt of promptsToRefresh) {
658+
if (rootChanged && newPrompts.has(prompt)) {
659+
continue;
660+
}
661+
this._onDidChangeTreeData.fire(prompt);
625662
}
626663
}
627664

@@ -824,4 +861,4 @@ class LogTreeFilterCommands extends Disposable {
824861
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.showNesRequests', () => filters.setNesRequestsShown(true)));
825862
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.hideNesRequests', () => filters.setNesRequestsShown(false)));
826863
}
827-
}
864+
}

0 commit comments

Comments
 (0)