Skip to content

Commit a737348

Browse files
authored
ghost: support isShown and isFromCache for provideInlineEdit (#3250)
* ghost: support isFromCache for provideInlineEdit * ghost: support isShown for provideInlineEdit * formatting * easier way to mark a suggestion as from cache
1 parent 2e57ddd commit a737348

File tree

8 files changed

+36
-11
lines changed

8 files changed

+36
-11
lines changed

src/extension/completions-core/vscode-node/extension/src/ghostText/ghostTextProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export class GhostTextProvider {
8383
opportunityId,
8484
},
8585
logContext,
86+
telemetryBuilder.nesBuilder,
8687
);
8788

8889
if (!rawCompletions) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ export class CopilotInlineCompletionItemProvider extends Disposable implements I
173173

174174
handleDidShowCompletionItem(item: GhostTextCompletionItem) {
175175
try {
176+
item.telemetryBuilder.setAsShown();
176177
this.copilotCompletionFeedbackTracker.trackItem(item);
177178
return this.ghostTextProvider.handleDidShowCompletionItem(item);
178179
} catch (e) {

src/extension/completions-core/vscode-node/lib/src/ghostText/ghostText.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ITelemetryService } from '../../../../../../platform/telemetry/common/t
88
import { createSha256Hash } from '../../../../../../util/common/crypto';
99
import { generateUuid } from '../../../../../../util/vs/base/common/uuid';
1010
import { IInstantiationService, ServicesAccessor } from '../../../../../../util/vs/platform/instantiation/common/instantiation';
11+
import { LlmNESTelemetryBuilder } from '../../../../../inlineEdits/node/nextEditProviderTelemetry';
1112
import { GhostTextLogContext } from '../../../../common/ghostTextContext';
1213
import { initializeTokenizers } from '../../../prompt/src/tokenization';
1314
import { CancellationTokenSource, CancellationToken as ICancellationToken } from '../../../types/src';
@@ -145,6 +146,7 @@ export class GhostTextComputer {
145146
token: ICancellationToken | undefined,
146147
options: Partial<GetGhostTextOptions>,
147148
logContext: GhostTextLogContext,
149+
telemetryBuilder: LlmNESTelemetryBuilder,
148150
): Promise<GhostTextResultWithTelemetry<[CompletionResult[], ResultType]>> {
149151
const id = generateUuid();
150152
this.currentGhostText.currentRequestId = id;
@@ -164,7 +166,7 @@ export class GhostTextComputer {
164166
options
165167
);
166168
this.notifierService.notifyRequest(completionState, id, telemetryData, token, options);
167-
const result = await this.getGhostTextWithoutAbortHandling(completionState, id, telemetryData, token, options, logContext);
169+
const result = await this.getGhostTextWithoutAbortHandling(completionState, id, telemetryData, token, options, logContext, telemetryBuilder);
168170
const statistics = this.contextproviderStatistics.getStatisticsForCompletion(id);
169171
const opportunityId = options?.opportunityId ?? 'unknown';
170172
for (const [providerId, statistic] of statistics.getAllUsageStatistics()) {
@@ -219,6 +221,7 @@ export class GhostTextComputer {
219221
cancellationToken: ICancellationToken | undefined,
220222
options: Partial<GetGhostTextOptions>,
221223
logContext: GhostTextLogContext,
224+
telemetryBuilder: LlmNESTelemetryBuilder,
222225
): Promise<GhostTextResultWithTelemetry<[CompletionResult[], ResultType]>> {
223226
let start = preIssuedTelemetryDataWithExp.issuedTime; // Start before getting exp assignments
224227
const performanceMetrics: [string, number][] = [];
@@ -436,6 +439,10 @@ export class GhostTextComputer {
436439
.filter(c => c !== undefined);
437440
}
438441

442+
if (choices && choices[1] === ResultType.Cache) {
443+
telemetryBuilder.setIsFromCache();
444+
}
445+
439446
if (choices !== undefined && choices[0].length === 0) {
440447
this.logger.debug(`Found empty inline suggestions locally via ${resultTypeToString(choices[1])}`);
441448
return {
@@ -632,10 +639,11 @@ export async function getGhostText(
632639
token: ICancellationToken | undefined,
633640
options: Partial<GetGhostTextOptions>,
634641
logContext: GhostTextLogContext,
642+
telemetryBuilder: LlmNESTelemetryBuilder,
635643
): Promise<GhostTextResultWithTelemetry<[CompletionResult[], ResultType]>> {
636644
const instaService = accessor.get(IInstantiationService);
637645
const ghostTextComputer = instaService.createInstance(GhostTextComputer);
638-
return ghostTextComputer.getGhostText(completionState, token, options, logContext);
646+
return ghostTextComputer.getGhostText(completionState, token, options, logContext, telemetryBuilder);
639647
}
640648

641649
/**

src/extension/completions-core/vscode-node/lib/src/ghostText/test/ghostText.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { ICompletionsCurrentGhostText } from '../current';
3434
import { getGhostText, GhostCompletion } from '../ghostText';
3535
import { ResultType } from '../resultType';
3636
import { mkBasicResultTelemetry } from '../telemetry';
37+
import { LlmNESTelemetryBuilder } from '../../../../../../inlineEdits/node/nextEditProviderTelemetry';
3738

3839
// Unit tests for ghostText that do not require network connectivity. For other
3940
// tests, see lib/e2e/src/ghostText.test.ts.
@@ -66,7 +67,8 @@ suite('Isolated GhostText tests', function () {
6667

6768
// Setup closures with the state as default
6869
function requestGhostText(completionState = state) {
69-
return getGhostText(accessor, completionState, token, {}, new GhostTextLogContext(filePath, doc.version, undefined));
70+
const telemetryBuilder = new LlmNESTelemetryBuilder(undefined, undefined, undefined, 'ghostText', undefined);
71+
return getGhostText(accessor, completionState, token, {}, new GhostTextLogContext(filePath, doc.version, undefined), telemetryBuilder);
7072
}
7173
async function requestPrompt(completionState = state) {
7274
const telemExp = TelemetryWithExp.createEmptyConfigForTesting();
@@ -653,7 +655,8 @@ suite('Isolated GhostText tests', function () {
653655
configProvider.setConfig(ConfigKey.AlwaysRequestMultiline, true);
654656
currentGhostText.hasAcceptedCurrentCompletion = () => true;
655657

656-
const response = await getGhostText(accessor, state, undefined, { isSpeculative: true }, new GhostTextLogContext('file:///fizzbuzz.go', doc.version, undefined));
658+
const telemetryBuilder = new LlmNESTelemetryBuilder(undefined, undefined, undefined, 'ghostText', undefined);
659+
const response = await getGhostText(accessor, state, undefined, { isSpeculative: true }, new GhostTextLogContext('file:///fizzbuzz.go', doc.version, undefined), telemetryBuilder);
657660

658661
assert.strictEqual(response.type, 'success');
659662
assert.strictEqual(response.value[0].length, 1);

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ICompletionsSpeculativeRequestCache } from './ghostText/speculativeRequ
1515
import { GhostTextResultWithTelemetry, handleGhostTextResultTelemetry, logger } from './ghostText/telemetry';
1616
import { ICompletionsLogTargetService } from './logger';
1717
import { ITextDocument, TextDocumentContents } from './textDocument';
18+
import { LlmNESTelemetryBuilder } from '../../../../inlineEdits/node/nextEditProviderTelemetry';
1819

1920
type GetInlineCompletionsOptions = Partial<GetGhostTextOptions> & {
2021
formattingOptions?: ITextEditorOptions;
@@ -34,10 +35,11 @@ export class GhostText {
3435
token: CancellationToken,
3536
options: Exclude<Partial<GetInlineCompletionsOptions>, 'promptOnly'> = {},
3637
logContext: GhostTextLogContext,
38+
telemetryBuilder: LlmNESTelemetryBuilder,
3739
): Promise<CopilotCompletion[] | undefined> {
3840
logCompletionLocation(this.logTargetService, textDocument, position);
3941

40-
const result = await this.getInlineCompletionsResult(createCompletionState(textDocument, position), token, options, logContext);
42+
const result = await this.getInlineCompletionsResult(createCompletionState(textDocument, position), token, options, logContext, telemetryBuilder);
4143
return this.instantiationService.invokeFunction(handleGhostTextResultTelemetry, result);
4244
}
4345

@@ -46,6 +48,7 @@ export class GhostText {
4648
token: CancellationToken,
4749
options: GetInlineCompletionsOptions = {},
4850
logContext: GhostTextLogContext,
51+
telemetryBuilder: LlmNESTelemetryBuilder,
4952
): Promise<GhostTextResultWithTelemetry<CopilotCompletion[]>> {
5053
let lineLengthIncrease = 0;
5154
// The golang.go extension (and quite possibly others) uses snippets for function completions, which collapse down
@@ -56,8 +59,12 @@ export class GhostText {
5659
lineLengthIncrease = completionState.position.character - options.selectedCompletionInfo.range.end.character;
5760
}
5861

59-
const result = await this.instantiationService.invokeFunction(getGhostText, completionState, token, options, logContext);
60-
if (result.type !== 'success') { return result; }
62+
const result = await this.instantiationService.invokeFunction(getGhostText, completionState, token, options, logContext, telemetryBuilder);
63+
64+
if (result.type !== 'success') {
65+
return result;
66+
}
67+
6168
const [resultArray, resultType] = result.value;
6269

6370
if (token.isCancellationRequested) {
@@ -95,7 +102,7 @@ export class GhostText {
95102

96103
// Cache speculative request to be triggered when telemetryShown is called
97104
const specOpts = { isSpeculative: true, opportunityId: options.opportunityId };
98-
const fn = () => this.instantiationService.invokeFunction(getGhostText, completionState, undefined, specOpts, logContext);
105+
const fn = () => this.instantiationService.invokeFunction(getGhostText, completionState, undefined, specOpts, logContext, telemetryBuilder);
99106
this.speculativeRequestCache.set(completions[0].clientCompletionId, fn);
100107
}
101108

src/extension/completions-core/vscode-node/lib/src/prompt/test/prompt.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { IPosition, ITextDocument } from '../../textDocument';
1212
import { ICompletionsContextProviderBridgeService } from '../components/contextProviderBridge';
1313
import { extractPrompt, ExtractPromptOptions } from '../prompt';
1414
import { GhostTextLogContext } from '../../../../../common/ghostTextContext';
15+
import { LlmNESTelemetryBuilder } from '../../../../../../inlineEdits/node/nextEditProviderTelemetry';
1516

1617
export async function extractPromptInternal(
1718
accessor: ServicesAccessor,
@@ -33,5 +34,6 @@ export async function getGhostTextInternal(
3334
position: IPosition,
3435
token?: CancellationToken
3536
) {
36-
return getGhostText(accessor, createCompletionState(textDocument, position), token, { opportunityId: 'opId' }, new GhostTextLogContext(textDocument.uri, textDocument.version, undefined));
37+
const telemetryBuilder = new LlmNESTelemetryBuilder(undefined, undefined, undefined, 'ghostText', undefined);
38+
return getGhostText(accessor, createCompletionState(textDocument, position), token, { opportunityId: 'opId' }, new GhostTextLogContext(textDocument.uri, textDocument.version, undefined), telemetryBuilder);
3739
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { createLibTestingContext } from './context';
2020
import { createFakeCompletionResponse, StaticFetcher } from './fetcher';
2121
import { withInMemoryTelemetry } from './telemetry';
2222
import { createTextDocument } from './textDocument';
23+
import { LlmNESTelemetryBuilder } from '../../../../../inlineEdits/node/nextEditProviderTelemetry';
2324

2425
suite('getInlineCompletions()', function () {
2526
function setupCompletion(
@@ -38,7 +39,8 @@ suite('getInlineCompletions()', function () {
3839
function requestInlineCompletions(textDoc = doc, pos = position) {
3940
const instaService = accessor.get(IInstantiationService);
4041
const ghostText = instaService.createInstance(GhostText);
41-
return ghostText.getInlineCompletions(textDoc, pos, CancellationToken.None, undefined, new GhostTextLogContext(textDoc.uri, textDoc.version, undefined));
42+
const telemetryBuilder = new LlmNESTelemetryBuilder(undefined, undefined, undefined, 'ghostText', undefined);
43+
return ghostText.getInlineCompletions(textDoc, pos, CancellationToken.None, undefined, new GhostTextLogContext(textDoc.uri, textDoc.version, undefined), telemetryBuilder);
4244
}
4345

4446
return {

src/lib/node/chatLibMain.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,8 @@ class InlineCompletionsProvider extends Disposable implements IInlineCompletions
662662
}
663663

664664
async getInlineCompletions(textDocument: ITextDocument, position: Position, token?: CancellationToken, options?: IGetInlineCompletionsOptions): Promise<CopilotCompletion[] | undefined> {
665-
return await this.ghostText.getInlineCompletions(textDocument, position, token ?? CancellationToken.None, options, new GhostTextLogContext(textDocument.uri, textDocument.version, undefined));
665+
const telemetryBuilder = new LlmNESTelemetryBuilder(undefined, undefined, undefined, 'ghostText', undefined);
666+
return await this.ghostText.getInlineCompletions(textDocument, position, token ?? CancellationToken.None, options, new GhostTextLogContext(textDocument.uri, textDocument.version, undefined), telemetryBuilder);
666667
}
667668

668669
async inlineCompletionShown(completionId: string): Promise<void> {

0 commit comments

Comments
 (0)