Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(audoedit): add isRead field and postProcessed state to the analytics logger #6534

Merged
merged 1 commit into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 75 additions & 40 deletions vscode/src/autoedits/analytics-logger/analytics-logger.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import omit from 'lodash/omit'
import * as uuid from 'uuid'
import { type MockInstance, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import * as vscode from 'vscode'

import { mockAuthStatus, ps, telemetryRecorder } from '@sourcegraph/cody-shared'
import { type MockInstance, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'

import {
isWindows,
mockAuthStatus,
ps,
setDisplayPathEnvInfo,
telemetryRecorder,
} from '@sourcegraph/cody-shared'

import { getCurrentDocContext } from '../../completions/get-current-doc-context'
import { documentAndPosition } from '../../completions/test-helpers'
import * as sentryModule from '../../services/sentry/sentry'
import type { AutoeditModelOptions } from '../adapters/base'
import { getCurrentFilePromptComponents } from '../prompt/prompt-utils'
import { getDecorationInfo } from '../renderer/diff-utils'

import {
AutoeditAnalyticsLogger,
Expand All @@ -21,7 +29,30 @@ describe('AutoeditAnalyticsLogger', () => {
let recordSpy: MockInstance
let stableIdCounter = 0

setDisplayPathEnvInfo({
isWindows: isWindows(),
workspaceFolders: [],
})

const { document, position } = documentAndPosition('█', 'typescript', 'file:///fake-file.ts')
const docContext = getCurrentDocContext({
document,
position,
maxPrefixLength: 1000,
maxSuffixLength: 1000,
})

const { codeToReplaceData } = getCurrentFilePromptComponents({
docContext,
position,
document,
tokenBudget: {
maxPrefixLinesInArea: 2,
maxSuffixLinesInArea: 2,
codeToRewritePrefixLines: 1,
codeToRewriteSuffixLines: 1,
},
})

const modelOptions: AutoeditModelOptions = {
url: 'https://test-url.com/',
Expand All @@ -35,19 +66,27 @@ describe('AutoeditAnalyticsLogger', () => {
isChatModel: false,
}

const requestStartMetadata: Parameters<AutoeditAnalyticsLogger['createRequest']>[0] = {
languageId: 'typescript',
model: 'autoedit-model',
traceId: 'trace-id',
triggerKind: autoeditTriggerKind.automatic,
codeToRewrite: 'Code to rewrite',
function getRequestStartMetadata(): Parameters<AutoeditAnalyticsLogger['createRequest']>[0] {
return {
startedAt: performance.now(),
docContext,
document,
position,
codeToReplaceData,
payload: {
languageId: 'typescript',
model: 'autoedit-model',
triggerKind: autoeditTriggerKind.automatic,
codeToRewrite: 'Code to rewrite',
},
}
}

function createAndAdvanceRequest({
finalPhase,
prediction,
}: { finalPhase: 'suggested' | 'accepted' | 'rejected'; prediction: string }): AutoeditRequestID {
const requestId = autoeditLogger.createRequest(requestStartMetadata)
const requestId = autoeditLogger.createRequest(getRequestStartMetadata())

autoeditLogger.markAsContextLoaded({
requestId,
Expand All @@ -68,7 +107,7 @@ describe('AutoeditAnalyticsLogger', () => {

autoeditLogger.markAsLoaded({
requestId,
modelOptions: modelOptions,
prompt: modelOptions.prompt,
payload: {
prediction,
source: autoeditSource.network,
Expand All @@ -77,16 +116,16 @@ describe('AutoeditAnalyticsLogger', () => {
},
})

autoeditLogger.markAsPostProcessed({
requestId,
prediction,
inlineCompletionItems: [],
decorationInfo: getDecorationInfo(prediction, prediction),
})
autoeditLogger.markAsSuggested(requestId)

if (finalPhase === 'accepted') {
autoeditLogger.markAsAccepted({
requestId,
trackedRange: new vscode.Range(position, position),
position,
document,
prediction,
})
autoeditLogger.markAsAccepted(requestId)
}

if (finalPhase === 'rejected') {
Expand All @@ -96,15 +135,17 @@ describe('AutoeditAnalyticsLogger', () => {
return requestId
}

beforeAll(() => {
vi.useFakeTimers()
mockAuthStatus()
})

beforeEach(() => {
autoeditLogger = new AutoeditAnalyticsLogger()
recordSpy = vi.spyOn(telemetryRecorder, 'recordEvent')
mockAuthStatus()

stableIdCounter = 0
vi.spyOn(uuid, 'v4').mockImplementation(() => `stable-id-for-tests-${++stableIdCounter}`)

vi.useFakeTimers()
})

afterEach(() => {
Expand All @@ -113,20 +154,14 @@ describe('AutoeditAnalyticsLogger', () => {
})

it('logs a suggestion lifecycle (started -> contextLoaded -> loaded -> suggested -> accepted)', () => {
const prediction = 'console.log("Hello from autoedit!")'
const prediction = 'say("Hello from autoedit!")'
const requestId = createAndAdvanceRequest({
finalPhase: 'accepted',
prediction,
})

// Invalid transition attempt
autoeditLogger.markAsAccepted({
requestId,
trackedRange: new vscode.Range(position, position),
position,
document,
prediction,
})
autoeditLogger.markAsAccepted(requestId)

expect(recordSpy).toHaveBeenCalledTimes(3)
expect(recordSpy).toHaveBeenNthCalledWith(1, 'cody.autoedit', 'suggested', expect.any(Object))
Expand All @@ -147,17 +182,17 @@ describe('AutoeditAnalyticsLogger', () => {
},
"interactionID": "stable-id-for-tests-2",
"metadata": {
"charCount": 35,
"charCount": 27,
"contextSummary.duration": 1.234,
"contextSummary.prefixChars": 5,
"contextSummary.suffixChars": 5,
"contextSummary.totalChars": 10,
"displayDuration": 0,
"isAccepted": 1,
"isDisjoint": 0,
"isFullyOutsideOfVisibleRanges": 1,
"isFuzzyMatch": 0,
"isPartiallyOutsideOfVisibleRanges": 1,
"isRead": 1,
"isSelectionStale": 1,
"latency": 300,
"lineCount": 1,
Expand All @@ -166,7 +201,8 @@ describe('AutoeditAnalyticsLogger', () => {
"outsideOfActiveEditor": 1,
"recordsPrivateMetadataTranscript": 1,
"source": 1,
"suggestionsStartedSinceLastSuggestion": 0,
"suggestionsStartedSinceLastSuggestion": 1,
"timeFromSuggestedAt": 0,
"triggerKind": 1,
"windowNotFocused": 1,
},
Expand All @@ -185,9 +221,8 @@ describe('AutoeditAnalyticsLogger', () => {
"languageId": "typescript",
"model": "autoedit-model",
"otherCompletionProviders": [],
"prediction": "console.log("Hello from autoedit!")",
"prediction": "say("Hello from autoedit!")",
"responseHeaders": {},
"traceId": "trace-id",
"upstreamLatency": undefined,
},
"version": 0,
Expand Down Expand Up @@ -241,15 +276,15 @@ describe('AutoeditAnalyticsLogger', () => {
const suggestedEvent3 = recordSpy.mock.calls[3].at(2)

// First two suggested calls should reuse the same stable ID
expect(suggestedEvent1.privateMetadata.id).toEqual('stable-id-for-tests-2')
expect(suggestedEvent2.privateMetadata.id).toEqual('stable-id-for-tests-2')
expect(suggestedEvent1.privateMetadata.id).toEqual(suggestedEvent2.privateMetadata.id)
// The third one should be different because we just accepted a completion
// which removes the stable ID from the cache.
expect(suggestedEvent3.privateMetadata.id).toEqual('stable-id-for-tests-5')
expect(suggestedEvent3.privateMetadata.id).not.toBe(suggestedEvent1.privateMetadata.id)
expect(suggestedEvent3.privateMetadata.id).not.toBe(suggestedEvent2.privateMetadata.id)
})

it('logs `discarded` if the suggestion was not suggested for any reason', () => {
const requestId = autoeditLogger.createRequest(requestStartMetadata)
const requestId = autoeditLogger.createRequest(getRequestStartMetadata())
autoeditLogger.markAsContextLoaded({ requestId, payload: { contextSummary: undefined } })
autoeditLogger.markAsDiscarded(requestId)

Expand All @@ -258,7 +293,7 @@ describe('AutoeditAnalyticsLogger', () => {
})

it('handles invalid transitions by logging debug events (invalidTransitionToXYZ)', () => {
const requestId = autoeditLogger.createRequest(requestStartMetadata)
const requestId = autoeditLogger.createRequest(getRequestStartMetadata())

// Both calls below are invalid transitions, so the logger logs debug events
autoeditLogger.markAsSuggested(requestId)
Expand Down
Loading
Loading