Skip to content

Commit 43ce3b8

Browse files
authored
nes: ghost: support showing ghost-text requests in the debug log tree (#2590)
* nes: formatting * nes: ghost: support showing ghost-text requests in the debug log tree
1 parent 83e7994 commit 43ce3b8

File tree

6 files changed

+132
-6
lines changed

6 files changed

+132
-6
lines changed

package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,14 @@
21752175
"command": "github.copilot.chat.debug.hideNesRequests",
21762176
"title": "Hide NES Requests"
21772177
},
2178+
{
2179+
"command": "github.copilot.chat.debug.showGhostRequests",
2180+
"title": "Show Ghost Requests"
2181+
},
2182+
{
2183+
"command": "github.copilot.chat.debug.hideGhostRequests",
2184+
"title": "Hide Ghost Requests"
2185+
},
21782186
{
21792187
"command": "github.copilot.chat.debug.showRawRequestBody",
21802188
"title": "Show Raw Request Body"
@@ -4123,6 +4131,14 @@
41234131
"command": "github.copilot.chat.debug.hideNesRequests",
41244132
"when": "false"
41254133
},
4134+
{
4135+
"command": "github.copilot.chat.debug.showGhostRequests",
4136+
"when": "false"
4137+
},
4138+
{
4139+
"command": "github.copilot.chat.debug.hideGhostRequests",
4140+
"when": "false"
4141+
},
41264142
{
41274143
"command": "github.copilot.chat.debug.exportLogItem",
41284144
"when": "false"
@@ -4447,6 +4463,16 @@
44474463
"command": "github.copilot.chat.debug.hideNesRequests",
44484464
"when": "!github.copilot.chat.debug.nesRequestsHidden",
44494465
"group": "commands@2"
4466+
},
4467+
{
4468+
"command": "github.copilot.chat.debug.showGhostRequests",
4469+
"when": "github.copilot.chat.debug.ghostRequestsHidden",
4470+
"group": "commands@3"
4471+
},
4472+
{
4473+
"command": "github.copilot.chat.debug.hideGhostRequests",
4474+
"when": "!github.copilot.chat.debug.ghostRequestsHidden",
4475+
"group": "commands@3"
44504476
}
44514477
],
44524478
"notebook/toolbar": [
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { InlineEditRequestLogContext } from '../../../platform/inlineEdits/common/inlineEditLogContext';
7+
import { basename } from '../../../util/vs/base/common/path';
8+
9+
export class GhostTextContext extends InlineEditRequestLogContext {
10+
override get includeInLogTree(): boolean {
11+
return true;
12+
}
13+
override getDebugName(): string {
14+
return `Ghost | ${basename(this.filePath)} (v${this.version})`;
15+
}
16+
}

src/extension/completions-core/vscode-node/extension/src/inlineCompletion.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ import {
1717
workspace,
1818
} from 'vscode';
1919
import { Disposable } from '../../../../../util/vs/base/common/lifecycle';
20+
import { LineEdit } from '../../../../../util/vs/editor/common/core/edits/lineEdit';
21+
import { TextEdit, TextReplacement } from '../../../../../util/vs/editor/common/core/edits/textEdit';
22+
import { Range } from '../../../../../util/vs/editor/common/core/range';
23+
import { LineBasedText } from '../../../../../util/vs/editor/common/core/text/abstractText';
2024
import { IInstantiationService, ServicesAccessor } from '../../../../../util/vs/platform/instantiation/common/instantiation';
25+
import { InlineEditLogger } from '../../../../inlineEdits/vscode-node/parts/inlineEditLogger';
26+
import { GhostTextContext } from '../../../common/ghostTextContext';
2127
import { ICompletionsTelemetryService } from '../../bridge/src/completionsTelemetryServiceBridge';
2228
import { BuildInfo } from '../../lib/src/config';
2329
import { CopilotConfigPrefix } from '../../lib/src/constants';
@@ -53,6 +59,7 @@ export function exception(accessor: ServicesAccessor, error: unknown, origin: st
5359
export class CopilotInlineCompletionItemProvider extends Disposable implements InlineCompletionItemProvider {
5460
private readonly copilotCompletionFeedbackTracker: CopilotCompletionFeedbackTracker;
5561
private readonly ghostTextProvider: InlineCompletionItemProvider;
62+
private readonly inlineEditLogger: InlineEditLogger;
5663

5764
public onDidChange = undefined;
5865
public handleListEndOfLifetime: InlineCompletionItemProvider['handleListEndOfLifetime'] = undefined;
@@ -65,6 +72,7 @@ export class CopilotInlineCompletionItemProvider extends Disposable implements I
6572
super();
6673
this.copilotCompletionFeedbackTracker = this._register(this.instantiationService.createInstance(CopilotCompletionFeedbackTracker));
6774
this.ghostTextProvider = this.instantiationService.createInstance(GhostTextProvider);
75+
this.inlineEditLogger = this.instantiationService.createInstance(InlineEditLogger);
6876
}
6977

7078
async provideInlineCompletionItems(
@@ -73,17 +81,22 @@ export class CopilotInlineCompletionItemProvider extends Disposable implements I
7381
context: InlineCompletionContext,
7482
token: CancellationToken
7583
): Promise<InlineCompletionList | undefined> {
84+
const logContext = new GhostTextContext(doc.uri.toString(), doc.version, context);
7685
try {
77-
return await this._provideInlineCompletionItems(doc, position, context, token);
86+
return await this._provideInlineCompletionItems(doc, position, context, logContext, token);
7887
} catch (e) {
88+
logContext.setError(e);
7989
this.telemetryService.sendGHTelemetryException(e, 'codeUnification.completions.exception');
90+
} finally {
91+
this.inlineEditLogger.add(logContext);
8092
}
8193
}
8294

8395
private async _provideInlineCompletionItems(
8496
doc: TextDocument,
8597
position: Position,
8698
context: InlineCompletionContext,
99+
logContext: GhostTextContext,
87100
token: CancellationToken
88101
): Promise<InlineCompletionList | undefined> {
89102
if (context.triggerKind === InlineCompletionTriggerKind.Automatic) {
@@ -103,23 +116,31 @@ export class CopilotInlineCompletionItemProvider extends Disposable implements I
103116
if (!copilotConfig.get('respectSelectedCompletionInfo', quickSuggestionsDisabled() || BuildInfo.isPreRelease())) {
104117
context = { ...context, selectedCompletionInfo: undefined };
105118
}
119+
106120
try {
107121
let items = await this.ghostTextProvider.provideInlineCompletionItems(doc, position, context, token);
108122

109123
if (!items) {
124+
if (token.isCancellationRequested) {
125+
logContext.setIsSkipped();
126+
}
110127
return undefined;
111128
}
112129

113130
// If the language client provides a list of items, we want to add the send feedback command to it.
114131
if (Array.isArray(items)) {
115132
items = { items };
116133
}
134+
135+
this.logSuggestion(logContext, doc, items);
136+
117137
return {
118138
...items,
119139
commands: [sendCompletionFeedbackCommand],
120140
};
121141
} catch (e) {
122142
this.instantiationService.invokeFunction(exception, e, '.provideInlineCompletionItems', logger);
143+
logContext.setError(e);
123144
}
124145
}
125146

@@ -150,4 +171,43 @@ export class CopilotInlineCompletionItemProvider extends Disposable implements I
150171
this.instantiationService.invokeFunction(exception, e, '.handleEndOfLifetime', logger);
151172
}
152173
}
174+
175+
private logSuggestion(
176+
logContext: GhostTextContext,
177+
doc: TextDocument,
178+
items: InlineCompletionList
179+
) {
180+
if (items.items.length === 0) {
181+
logContext.markAsNoSuggestions();
182+
logContext.addLog('No inline completion items provided');
183+
return;
184+
}
185+
const firstItem = items.items[0];
186+
if (!firstItem.range) {
187+
logContext.addLog('Inline completion item has no range');
188+
return;
189+
}
190+
if (typeof firstItem.insertText !== 'string') {
191+
logContext.addLog('Inline completion item has non-string insertText');
192+
return;
193+
}
194+
195+
const text = new LineBasedText(lineNumber => doc.lineAt(lineNumber - 1).text, doc.lineCount);
196+
197+
const lineEdit = LineEdit.fromTextEdit(
198+
new TextEdit(
199+
[new TextReplacement(
200+
new Range(firstItem.range.start.line + 1, firstItem.range.start.character + 1, firstItem.range.end.line + 1, firstItem.range.end.character + 1),
201+
firstItem.insertText,
202+
)],
203+
),
204+
text
205+
);
206+
207+
const patch = lineEdit.humanReadablePatch(text.getLines());
208+
209+
logContext.addLog(`Provided inline completion item:`);
210+
logContext.addCodeblockToLog(patch);
211+
logContext.setResult(patch);
212+
}
153213
}

src/extension/inlineEdits/vscode-node/parts/inlineEditLogger.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ export class InlineEditLogger extends Disposable {
1717
}
1818

1919
add(request: InlineEditRequestLogContext): void {
20-
if (!request.includeInLogTree) { return; }
20+
if (!request.includeInLogTree) {
21+
return;
22+
}
2123

2224
this._requestLogger.addEntry({
2325
type: LoggedRequestKind.MarkdownContentRequest,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ class LogTreeFilters extends Disposable {
817817
private _elementsShown = true;
818818
private _toolsShown = true;
819819
private _nesRequestsShown = true;
820+
private _ghostRequestsShown = true;
820821

821822
private readonly _onDidChangeFilters = new vscode.EventEmitter<void>();
822823
readonly onDidChangeFilters = this._onDidChangeFilters.event;
@@ -829,6 +830,7 @@ class LogTreeFilters extends Disposable {
829830
this.setElementsShown(!vscodeExtensionContext.workspaceState.get(this.getStorageKey('elements')));
830831
this.setToolsShown(!vscodeExtensionContext.workspaceState.get(this.getStorageKey('tools')));
831832
this.setNesRequestsShown(!vscodeExtensionContext.workspaceState.get(this.getStorageKey('nesRequests')));
833+
this.setGhostRequestsShown(!vscodeExtensionContext.workspaceState.get(this.getStorageKey('ghostRequests')));
832834
}
833835

834836
private getStorageKey(name: string): string {
@@ -850,6 +852,11 @@ class LogTreeFilters extends Disposable {
850852
this.setShown('nesRequests', this._nesRequestsShown);
851853
}
852854

855+
setGhostRequestsShown(value: boolean) {
856+
this._ghostRequestsShown = value;
857+
this.setShown('ghostRequests', this._ghostRequestsShown);
858+
}
859+
853860
itemIncluded(item: TreeItem): boolean {
854861
if (item instanceof ChatPromptItem) {
855862
if (this.isNesRequest(item)) {
@@ -865,11 +872,20 @@ class LogTreeFilters extends Disposable {
865872
if (this.isNesRequest(item)) {
866873
return this._nesRequestsShown;
867874
}
875+
// Check if this is a Ghost request
876+
if (this.isGhostRequest(item)) {
877+
return this._ghostRequestsShown;
878+
}
868879
}
869880

870881
return true;
871882
}
872883

884+
private isGhostRequest(item: ChatRequestItem): boolean {
885+
const debugName = item.info.entry.debugName.toLowerCase();
886+
return debugName === 'ghost' || debugName.startsWith('ghost |');
887+
}
888+
873889
private isNesRequest(item: ChatPromptItem | ChatRequestItem): boolean {
874890
let debugName: string;
875891
if (item instanceof ChatPromptItem) {
@@ -898,5 +914,7 @@ class LogTreeFilterCommands extends Disposable {
898914
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.hideTools', () => filters.setToolsShown(false)));
899915
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.showNesRequests', () => filters.setNesRequestsShown(true)));
900916
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.hideNesRequests', () => filters.setNesRequestsShown(false)));
917+
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.showGhostRequests', () => filters.setGhostRequestsShown(true)));
918+
this._register(vscode.commands.registerCommand('github.copilot.chat.debug.hideGhostRequests', () => filters.setGhostRequestsShown(false)));
901919
}
902920
}

src/platform/inlineEdits/common/inlineEditLogContext.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ export class InlineEditRequestLogContext {
164164
} else if (this._nesTypePicked === 'llm' && this._resultEdit) {
165165
lines.push(`## Result:`);
166166
lines.push('``` patch');
167-
lines.push(this._resultEdit.toString());
167+
if (typeof this._resultEdit === 'string') {
168+
lines.push(this._resultEdit);
169+
} else {
170+
lines.push(this._resultEdit.toString());
171+
}
168172
lines.push('```');
169173
} else {
170174
lines.push(`## Result: <NOT-SET>`);
@@ -200,11 +204,11 @@ export class InlineEditRequestLogContext {
200204
this._nextEditRequest = nextEditRequest;
201205
}
202206

203-
private _resultEdit: RootedLineEdit | undefined = undefined;
207+
private _resultEdit: RootedLineEdit | string | undefined = undefined;
204208

205-
setResult(resultEdit: RootedLineEdit) {
209+
setResult(resultEditOrPatchString: RootedLineEdit | string) {
206210
this._isVisible = true;
207-
this._resultEdit = resultEdit;
211+
this._resultEdit = resultEditOrPatchString;
208212
}
209213

210214
protected _diagnosticsResultEdit: RootedLineEdit | undefined = undefined;

0 commit comments

Comments
 (0)