Skip to content

Commit 193730f

Browse files
authored
codewhisperer: telemetry for supplemental context strategy #3799
We are already able to analyze acceptance rate on either CrossFile or UTG or an aggregated number. Now we need more granular metrics for UTG `resolve source file by file name` vs. `resolve source file by file content` to help us understanding deeper the effectiveness of UTG, thus adding a new field `codewhispererSupplementalContextStrategyId` field representing the strategies being used for Crossfile and UTG. * Crossfile * OpenTabs + BM25 * UTG * Resolve by file name (regex) * Resolve by file content there are 4 values for now `Empty`, `ByName`, `ByContent`, `OpenTabs_BM25`
1 parent 902c4e8 commit 193730f

File tree

9 files changed

+72
-30
lines changed

9 files changed

+72
-30
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3583,7 +3583,7 @@
35833583
"report": "nyc report --reporter=html --reporter=json"
35843584
},
35853585
"devDependencies": {
3586-
"@aws-toolkits/telemetry": "^1.0.136",
3586+
"@aws-toolkits/telemetry": "^1.0.143",
35873587
"@cspotcode/source-map-support": "^0.8.1",
35883588
"@sinonjs/fake-timers": "^10.0.2",
35893589
"@types/adm-zip": "^0.4.34",

src/codewhisperer/util/editorContext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export async function buildListRecommendationRequest(
9696
isProcessTimeout: supplementalContexts.isProcessTimeout,
9797
contentsLength: supplementalContexts.contentsLength,
9898
latency: supplementalContexts.latency,
99+
strategy: supplementalContexts.strategy,
99100
}
100101
: undefined
101102

@@ -150,6 +151,7 @@ export async function buildGenerateRecommendationRequest(editor: vscode.TextEdit
150151
isProcessTimeout: supplementalContexts.isProcessTimeout,
151152
contentsLength: supplementalContexts.contentsLength,
152153
latency: supplementalContexts.latency,
154+
strategy: supplementalContexts.strategy,
153155
}
154156
}
155157

src/codewhisperer/util/supplementalContext/crossFileContextUtil.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import { BM25Document, BM25Okapi } from './rankBm25'
1010
import { ToolkitError } from '../../../shared/errors'
1111
import { UserGroup, crossFileContextConfig, supplemetalContextFetchingTimeoutMsg } from '../../models/constants'
1212
import { CancellationError } from '../../../shared/utilities/timeoutUtils'
13-
import { CodeWhispererSupplementalContextItem } from './supplementalContextUtil'
13+
import { CodeWhispererSupplementalContext, CodeWhispererSupplementalContextItem } from './supplementalContextUtil'
1414
import { CodeWhispererUserGroupSettings } from '../userGroupUtil'
1515
import { isTestFile } from './codeParsingUtil'
1616
import { getFileDistance } from '../../../shared/filesystemUtilities'
1717
import { getOpenFilesInWindow } from '../../../shared/utilities/editorUtilities'
1818
import { getLogger } from '../../../shared/logger/logger'
1919

20+
export type CrossFileStrategy = 'OpenTabs_BM25'
21+
2022
type CrossFileSupportedLanguage =
2123
| 'java'
2224
| 'python'
@@ -51,14 +53,19 @@ interface Chunk {
5153
export async function fetchSupplementalContextForSrc(
5254
editor: vscode.TextEditor,
5355
cancellationToken: vscode.CancellationToken
54-
): Promise<CodeWhispererSupplementalContextItem[] | undefined> {
56+
): Promise<Pick<CodeWhispererSupplementalContext, 'supplementalContextItems' | 'strategy'> | undefined> {
5557
const shouldProceed = shouldFetchCrossFileContext(
5658
editor.document.languageId,
5759
CodeWhispererUserGroupSettings.instance.userGroup
5860
)
5961

6062
if (!shouldProceed) {
61-
return shouldProceed === undefined ? undefined : []
63+
return shouldProceed === undefined
64+
? undefined
65+
: {
66+
supplementalContextItems: [],
67+
strategy: 'Empty',
68+
}
6269
}
6370

6471
const codeChunksCalculated = crossFileContextConfig.numberOfChunkToFetch
@@ -104,7 +111,10 @@ export async function fetchSupplementalContextForSrc(
104111

105112
// DO NOT send code chunk with empty content
106113
getLogger().debug(`CodeWhisperer finished fetching crossfile context out of ${relevantCrossFilePaths.length} files`)
107-
return supplementalContexts.filter(item => item.content.trim().length !== 0)
114+
return {
115+
supplementalContextItems: supplementalContexts.filter(item => item.content.trim().length !== 0),
116+
strategy: 'OpenTabs_BM25',
117+
}
108118
}
109119

110120
function findBestKChunkMatches(chunkInput: Chunk, chunkReferences: Chunk[], k: number): Chunk[] {

src/codewhisperer/util/supplementalContext/supplementalContextUtil.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { fetchSupplementalContextForTest } from './utgUtils'
7-
import { fetchSupplementalContextForSrc } from './crossFileContextUtil'
6+
import { UtgStrategy, fetchSupplementalContextForTest } from './utgUtils'
7+
import { CrossFileStrategy, fetchSupplementalContextForSrc } from './crossFileContextUtil'
88
import { isTestFile } from './codeParsingUtil'
99
import { DependencyGraphFactory } from '../dependencyGraph/dependencyGraphFactory'
1010
import * as vscode from 'vscode'
@@ -14,12 +14,15 @@ import { getLogger } from '../../../shared/logger/logger'
1414

1515
const performance = globalThis.performance ?? require('perf_hooks').performance
1616

17+
export type SupplementalContextStrategy = CrossFileStrategy | UtgStrategy | 'Empty'
18+
1719
export interface CodeWhispererSupplementalContext {
1820
isUtg: boolean
1921
isProcessTimeout: boolean
2022
supplementalContextItems: CodeWhispererSupplementalContextItem[]
2123
contentsLength: number
2224
latency: number
25+
strategy: SupplementalContextStrategy
2326
}
2427

2528
export interface CodeWhispererSupplementalContextItem {
@@ -41,7 +44,9 @@ export async function fetchSupplementalContext(
4144
fileContent: editor.document.getText(),
4245
})
4346

44-
let supplementalContextPromise: Promise<CodeWhispererSupplementalContextItem[] | undefined>
47+
let supplementalContextPromise: Promise<
48+
Pick<CodeWhispererSupplementalContext, 'supplementalContextItems' | 'strategy'> | undefined
49+
>
4550

4651
if (isUtg) {
4752
supplementalContextPromise = fetchSupplementalContextForTest(editor, cancellationToken)
@@ -55,9 +60,10 @@ export async function fetchSupplementalContext(
5560
return {
5661
isUtg: isUtg,
5762
isProcessTimeout: false,
58-
supplementalContextItems: value,
59-
contentsLength: value.reduce((acc, curr) => acc + curr.content.length, 0),
63+
supplementalContextItems: value.supplementalContextItems,
64+
contentsLength: value.supplementalContextItems.reduce((acc, curr) => acc + curr.content.length, 0),
6065
latency: performance.now() - timesBeforeFetching,
66+
strategy: value.strategy,
6167
}
6268
} else {
6369
return undefined
@@ -71,6 +77,7 @@ export async function fetchSupplementalContext(
7177
supplementalContextItems: [],
7278
contentsLength: 0,
7379
latency: performance.now() - timesBeforeFetching,
80+
strategy: 'Empty',
7481
}
7582
} else {
7683
getLogger().error(

src/codewhisperer/util/supplementalContext/utgUtils.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { ToolkitError } from '../../../shared/errors'
1919
import { supplemetalContextFetchingTimeoutMsg } from '../../models/constants'
2020
import { CancellationError } from '../../../shared/utilities/timeoutUtils'
21-
import { CodeWhispererSupplementalContextItem } from './supplementalContextUtil'
21+
import { CodeWhispererSupplementalContext, CodeWhispererSupplementalContextItem } from './supplementalContextUtil'
2222
import { utgConfig } from '../../models/constants'
2323
import { CodeWhispererUserGroupSettings } from '../userGroupUtil'
2424
import { UserGroup } from '../../models/constants'
@@ -27,6 +27,8 @@ import { getLogger } from '../../../shared/logger/logger'
2727

2828
type UtgSupportedLanguage = keyof typeof utgLanguageConfigs
2929

30+
export type UtgStrategy = 'ByName' | 'ByContent'
31+
3032
function isUtgSupportedLanguage(languageId: vscode.TextDocument['languageId']): languageId is UtgSupportedLanguage {
3133
return languageId in utgLanguageConfigs
3234
}
@@ -58,14 +60,14 @@ export function shouldFetchUtgContext(
5860
export async function fetchSupplementalContextForTest(
5961
editor: vscode.TextEditor,
6062
cancellationToken: vscode.CancellationToken
61-
): Promise<CodeWhispererSupplementalContextItem[] | undefined> {
63+
): Promise<Pick<CodeWhispererSupplementalContext, 'supplementalContextItems' | 'strategy'> | undefined> {
6264
const shouldProceed = shouldFetchUtgContext(
6365
editor.document.languageId,
6466
CodeWhispererUserGroupSettings.instance.userGroup
6567
)
6668

6769
if (!shouldProceed) {
68-
return shouldProceed === undefined ? undefined : []
70+
return shouldProceed === undefined ? undefined : { supplementalContextItems: [], strategy: 'Empty' }
6971
}
7072

7173
const languageConfig = utgLanguageConfigs[editor.document.languageId]
@@ -77,24 +79,42 @@ export async function fetchSupplementalContextForTest(
7779
if (crossSourceFile) {
7880
// TODO (Metrics): 2. Success count for fetchSourceFileByName (find source file by name)
7981
getLogger().debug(`CodeWhisperer finished fetching utg context by file name`)
80-
return generateSupplementalContextFromFocalFile(crossSourceFile, cancellationToken)
82+
return {
83+
supplementalContextItems: generateSupplementalContextFromFocalFile(
84+
crossSourceFile,
85+
'ByName',
86+
cancellationToken
87+
),
88+
strategy: 'ByName',
89+
}
8190
}
8291
throwIfCancelled(cancellationToken)
8392

8493
crossSourceFile = await findSourceFileByContent(editor, languageConfig, cancellationToken)
8594
if (crossSourceFile) {
8695
// TODO (Metrics): 3. Success count for fetchSourceFileByContent (find source file by content)
8796
getLogger().debug(`CodeWhisperer finished fetching utg context by file content`)
88-
return generateSupplementalContextFromFocalFile(crossSourceFile, cancellationToken)
97+
return {
98+
supplementalContextItems: generateSupplementalContextFromFocalFile(
99+
crossSourceFile,
100+
'ByContent',
101+
cancellationToken
102+
),
103+
strategy: 'ByContent',
104+
}
89105
}
90106

91107
// TODO (Metrics): 4. Failure count - when unable to find focal file (supplemental context empty)
92108
getLogger().debug(`CodeWhisperer failed to fetch utg context`)
93-
return []
109+
return {
110+
supplementalContextItems: [],
111+
strategy: 'Empty',
112+
}
94113
}
95114

96115
function generateSupplementalContextFromFocalFile(
97116
filePath: string,
117+
strategy: UtgStrategy,
98118
cancellationToken: vscode.CancellationToken
99119
): CodeWhispererSupplementalContextItem[] {
100120
const fileContent = fs.readFileSync(vscode.Uri.file(filePath!).fsPath, 'utf-8')

src/codewhisperer/util/telemetryHelper.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,8 @@ export class TelemetryHelper {
299299
codewhispererSupplementalContextTimeout: supplementalContextMetadata?.isProcessTimeout,
300300
codewhispererSupplementalContextIsUtg: supplementalContextMetadata?.isUtg,
301301
codewhispererSupplementalContextLength: supplementalContextMetadata?.contentsLength,
302+
// eslint-disable-next-line id-length
303+
codewhispererSupplementalContextStrategyId: supplementalContextMetadata?.strategy,
302304
}
303305
telemetry.codewhisperer_userTriggerDecision.emit(aggregated)
304306
this.prevTriggerDecision = this.getAggregatedUserDecision(this.sessionDecisions)

src/test/codewhisperer/service/recommendationHandler.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ describe('recommendationHandler', function () {
133133
supplementalContextItems: [],
134134
contentsLength: 100,
135135
latency: 0,
136+
strategy: 'Empty',
136137
})
137138
sinon.stub(performance, 'now').returns(0.0)
138139
handler.startPos = new vscode.Position(1, 0)

src/test/codewhisperer/util/crossFileContextUtil.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ describe('crossFileContextUtil', function () {
4646
})
4747
const actual = await crossFile.fetchSupplementalContextForSrc(myCurrentEditor, fakeCancellationToken)
4848
assert.ok(actual)
49-
assert.ok(actual.length === 3)
49+
assert.ok(actual.supplementalContextItems.length === 3)
5050

51-
assert.strictEqual(actual[0].content.split('\n').length, 10)
52-
assert.strictEqual(actual[1].content.split('\n').length, 10)
53-
assert.strictEqual(actual[2].content.split('\n').length, 10)
51+
assert.strictEqual(actual.supplementalContextItems[0].content.split('\n').length, 10)
52+
assert.strictEqual(actual.supplementalContextItems[1].content.split('\n').length, 10)
53+
assert.strictEqual(actual.supplementalContextItems[2].content.split('\n').length, 10)
5454
}
5555

5656
it('control group', async function () {
@@ -169,7 +169,7 @@ describe('crossFileContextUtil', function () {
169169

170170
const actual = await crossFile.fetchSupplementalContextForSrc(editor, fakeCancellationToken)
171171

172-
assert.ok(actual?.length !== undefined && actual.length === 0)
172+
assert.ok(actual && actual.supplementalContextItems.length === 0)
173173
})
174174
})
175175
})
@@ -201,7 +201,7 @@ describe('crossFileContextUtil', function () {
201201

202202
const actual = await crossFile.fetchSupplementalContextForSrc(editor, fakeCancellationToken)
203203

204-
assert.ok(actual?.length !== undefined && actual.length !== 0)
204+
assert.ok(actual && actual.supplementalContextItems.length !== 0)
205205
})
206206
})
207207
})
@@ -233,7 +233,7 @@ describe('crossFileContextUtil', function () {
233233

234234
const actual = await crossFile.fetchSupplementalContextForSrc(editor, fakeCancellationToken)
235235

236-
assert.ok(actual?.length !== undefined && actual.length !== 0)
236+
assert.ok(actual && actual.supplementalContextItems.length !== 0)
237237
})
238238
})
239239
})

0 commit comments

Comments
 (0)