Skip to content

Commit

Permalink
Merge pull request #4478 from leigaol/percent_copy
Browse files Browse the repository at this point in the history
feat(cwspr): Improve percentage code written metrics to include some user multi character input
  • Loading branch information
leigaol committed Feb 28, 2024
2 parents 69a7754 + 38c89c6 commit 82b6a9b
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Feature",
"description": "CodeWhisperer: Improve percentage code written metrics to include user multi character input"
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export class CWInlineCompletionItemProvider implements vscode.InlineCompletionIt
}
return undefined
}
TelemetryHelper.instance.lastSuggestionInDisplay = truncatedSuggestion
return {
insertText: truncatedSuggestion,
range: new vscode.Range(start, end),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ interface CodeWhispererToken {
accepted: number
}

interface UserInputDetail {
count: number
total: number
}

interface UserInputDetails {
lt1: UserInputDetail
lt50: UserInputDetail
lt100: UserInputDetail
lt200: UserInputDetail
lt300: UserInputDetail
lt400: UserInputDetail
lt500: UserInputDetail
lt1000: UserInputDetail
gt1000: UserInputDetail
}

const autoClosingKeystrokeInputs = ['[]', '{}', '()', '""', "''"]

/**
Expand All @@ -37,12 +54,25 @@ export class CodeWhispererCodeCoverageTracker {
private _language: CodewhispererLanguage
private _serviceInvocationCount: number

private _userInputDetails: UserInputDetails

private constructor(language: CodewhispererLanguage) {
this._acceptedTokens = {}
this._totalTokens = {}
this._startTime = 0
this._language = language
this._serviceInvocationCount = 0
this._userInputDetails = {
lt1: { count: 0, total: 0 },
lt50: { count: 0, total: 0 },
lt100: { count: 0, total: 0 },
lt200: { count: 0, total: 0 },
lt300: { count: 0, total: 0 },
lt400: { count: 0, total: 0 },
lt500: { count: 0, total: 0 },
lt1000: { count: 0, total: 0 },
gt1000: { count: 0, total: 0 },
}
}

public get serviceInvocationCount(): number {
Expand Down Expand Up @@ -110,7 +140,7 @@ export class CodeWhispererCodeCoverageTracker {
}
// the accepted characters without counting user modification
let acceptedTokens = 0
// the accepted characters after calculating user modificaiton
// the accepted characters after calculating user modification
let unmodifiedAcceptedTokens = 0
for (const filename in this._acceptedTokens) {
this._acceptedTokens[filename].forEach(v => {
Expand All @@ -136,6 +166,7 @@ export class CodeWhispererCodeCoverageTracker {
codewhispererUserGroup: CodeWhispererUserGroupSettings.getUserGroup().toString(),
codewhispererCustomizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn,
credentialStartUrl: AuthUtil.instance.startUrl,
codewhispererUserInputDetails: JSON.stringify(this._userInputDetails),
})

client
Expand Down Expand Up @@ -206,6 +237,17 @@ export class CodeWhispererCodeCoverageTracker {
this._acceptedTokens = {}
this._startTime = 0
this._serviceInvocationCount = 0
this._userInputDetails = {
lt1: { count: 0, total: 0 },
lt50: { count: 0, total: 0 },
lt100: { count: 0, total: 0 },
lt200: { count: 0, total: 0 },
lt300: { count: 0, total: 0 },
lt400: { count: 0, total: 0 },
lt500: { count: 0, total: 0 },
lt1000: { count: 0, total: 0 },
gt1000: { count: 0, total: 0 },
}
}

private closeTimer() {
Expand Down Expand Up @@ -287,9 +329,54 @@ export class CodeWhispererCodeCoverageTracker {
if (this.isFromUserKeystroke(e)) {
this.tryStartTimer()
this.addTotalTokens(e.document.fileName, 1)
this._userInputDetails.lt1.count += 1
this._userInputDetails.lt1.total += 1
} else if (this.getCharacterCountFromComplexEvent(e) !== 0) {
this.tryStartTimer()
this.addTotalTokens(e.document.fileName, this.getCharacterCountFromComplexEvent(e))
const characterIncrease = this.getCharacterCountFromComplexEvent(e)
this.addTotalTokens(e.document.fileName, characterIncrease)
this._userInputDetails.lt1.count += 1
this._userInputDetails.lt1.total += characterIncrease
}
// also include multi character input within 500 characters (not from CWSPR)
else if (
e.contentChanges.length === 1 &&
e.contentChanges[0].text.length > 1 &&
TelemetryHelper.instance.lastSuggestionInDisplay !== e.contentChanges[0].text
) {
const multiCharInputSize = e.contentChanges[0].text.length

// select 500 as the cut-off threshold for counting user input.
if (multiCharInputSize < 500) {
this.addTotalTokens(e.document.fileName, multiCharInputSize)
}

// report multiple user input patterns for adjusting the threshold
if (multiCharInputSize < 50) {
this._userInputDetails.lt50.total += multiCharInputSize
this._userInputDetails.lt50.count += 1
} else if (multiCharInputSize < 100) {
this._userInputDetails.lt100.total += multiCharInputSize
this._userInputDetails.lt100.count += 1
} else if (multiCharInputSize < 200) {
this._userInputDetails.lt200.total += multiCharInputSize
this._userInputDetails.lt200.count += 1
} else if (multiCharInputSize < 300) {
this._userInputDetails.lt300.total += multiCharInputSize
this._userInputDetails.lt300.count += 1
} else if (multiCharInputSize < 400) {
this._userInputDetails.lt400.total += multiCharInputSize
this._userInputDetails.lt400.count += 1
} else if (multiCharInputSize < 500) {
this._userInputDetails.lt500.total += multiCharInputSize
this._userInputDetails.lt500.count += 1
} else if (multiCharInputSize < 1000) {
this._userInputDetails.lt1000.total += multiCharInputSize
this._userInputDetails.lt1000.count += 1
} else {
this._userInputDetails.gt1000.total += multiCharInputSize
this._userInputDetails.gt1000.count += 1
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/toolkit/src/codewhisperer/util/telemetryHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export class TelemetryHelper {
private classifierResult?: number = undefined
private classifierThreshold?: number = undefined

// use this to distinguish DocumentChangeEvent from CWSPR or from other sources
public lastSuggestionInDisplay = ''

constructor() {}

static #instance: TelemetryHelper
Expand Down
46 changes: 46 additions & 0 deletions packages/toolkit/src/shared/telemetry/vscodeTelemetry.json
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@
"name": "cwsprChatCommandName",
"type": "string",
"description": "Type of chat command name executed"
},
{
"name": "codewhispererUserInputDetails",
"type": "string",
"description": "A JSON serialized string of user input details."
}
],
"metrics": [
Expand Down Expand Up @@ -989,6 +994,47 @@
"required": false
}
]
},
{
"name": "codewhisperer_codePercentage",
"description": "Percentage of user tokens against suggestions until 5 mins of time",
"metadata": [
{
"type": "codewhispererAcceptedTokens"
},
{
"type": "codewhispererLanguage"
},
{
"type": "codewhispererPercentage"
},
{
"type": "codewhispererTotalTokens"
},
{
"type": "codewhispererUserInputDetails",
"required": false
},
{
"type": "codewhispererUserGroup"
},
{
"type": "reason",
"required": false
},
{
"type": "successCount"
},
{
"type": "codewhispererCustomizationArn",
"required": false
},
{
"type": "credentialStartUrl",
"required": false
}
],
"passive": true
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ describe('codewhispererCodecoverageTracker', function () {
CodeWhispererCodeCoverageTracker.instances.clear()
})

it('Should skip when content change size is not 1', function () {
it('Should skip when content change size is more than 500', function () {
if (!tracker) {
assert.fail()
}
Expand All @@ -241,16 +241,34 @@ describe('codewhispererCodecoverageTracker', function () {
document: createMockDocument(),
contentChanges: [
{
range: new vscode.Range(0, 0, 0, 30),
range: new vscode.Range(0, 0, 0, 600),
rangeOffset: 0,
rangeLength: 30,
text: 'def twoSum(nums, target):\nfor',
rangeLength: 600,
text: 'def twoSum(nums, target):\nfor '.repeat(20),
},
],
})
assert.strictEqual(Object.keys(tracker.totalTokens).length, 0)
})

const startedSpy = sinon.spy(CodeWhispererCodeCoverageTracker.prototype, 'addTotalTokens')
assert.ok(!startedSpy.called)
it('Should not skip when content change size is less than 500', function () {
if (!tracker) {
assert.fail()
}
tracker.countTotalTokens({
reason: undefined,
document: createMockDocument(),
contentChanges: [
{
range: new vscode.Range(0, 0, 0, 300),
rangeOffset: 0,
rangeLength: 300,
text: 'def twoSum(nums, target): for '.repeat(10),
},
],
})
assert.strictEqual(Object.keys(tracker.totalTokens).length, 1)
assert.strictEqual(Object.values(tracker.totalTokens)[0], 300)
})

it('Should skip when CodeWhisperer is editing', function () {
Expand Down

0 comments on commit 82b6a9b

Please sign in to comment.