3
3
* SPDX-License-Identifier: Apache-2.0
4
4
*/
5
5
6
- import { featureName , Mode } from '../constants'
6
+ import { docScheme , featureName , Mode } from '../constants'
7
7
import { DeletedFileInfo , Interaction , NewFileInfo , SessionState , SessionStateConfig } from '../types'
8
8
import { PrepareCodeGenState } from './sessionState'
9
9
import { telemetry } from '../../shared/telemetry/telemetry'
@@ -19,13 +19,14 @@ import { logWithConversationId } from '../../amazonqFeatureDev/userFacingText'
19
19
import { ConversationIdNotFoundError } from '../../amazonqFeatureDev/errors'
20
20
import { referenceLogText } from '../../amazonqFeatureDev/constants'
21
21
import {
22
- DocGenerationEvent ,
23
- DocGenerationInteractionType ,
22
+ DocInteractionType ,
23
+ DocV2AcceptanceEvent ,
24
+ DocV2GenerationEvent ,
24
25
SendTelemetryEventRequest ,
25
26
} from '../../codewhisperer/client/codewhispereruserclient'
26
- import { getDiffCharsAndLines } from '../../shared/utilities/diffUtils'
27
27
import { getClientId , getOperatingSystem , getOptOutPreference } from '../../shared/telemetry/util'
28
28
import { DocMessenger } from '../messenger'
29
+ import { computeDiff } from '../../amazonq/commons/diff'
29
30
30
31
export class Session {
31
32
private _state ?: SessionState | Omit < SessionState , 'uploadId' >
@@ -38,6 +39,7 @@ export class Session {
38
39
39
40
// Used to keep track of whether or not the current session is currently authenticating/needs authenticating
40
41
public isAuthenticating : boolean
42
+ private _reportedDocChanges : { [ key : string ] : string } = { }
41
43
42
44
constructor (
43
45
public readonly config : SessionConfig ,
@@ -177,41 +179,92 @@ export class Session {
177
179
}
178
180
}
179
181
180
- public async countAddedContent ( interactionType ?: DocGenerationInteractionType ) {
181
- let totalAddedChars = 0
182
- let totalAddedLines = 0
183
- let totalAddedFiles = 0
182
+ private getFromReportedChanges ( filepath : NewFileInfo ) {
183
+ const key = ` ${ filepath . workspaceFolder . uri . fsPath } / ${ filepath . relativePath } `
184
+ return this . _reportedDocChanges [ key ]
185
+ }
184
186
185
- for ( const filePath of this . state . filePaths ?. filter ( ( i ) => ! i . rejected ) ?? [ ] ) {
186
- const absolutePath = path . join ( filePath . workspaceFolder . uri . fsPath , filePath . relativePath )
187
- const uri = filePath . virtualMemoryUri
188
- const content = await this . config . fs . readFile ( uri )
189
- const decodedContent = new TextDecoder ( ) . decode ( content )
190
- totalAddedFiles += 1
187
+ private addToReportedChanges ( filepath : NewFileInfo ) {
188
+ const key = `${ filepath . workspaceFolder . uri . fsPath } /${ filepath . relativePath } `
189
+ this . _reportedDocChanges [ key ] = filepath . fileContent
190
+ }
191
191
192
- if ( ( await fs . exists ( absolutePath ) ) && interactionType === 'UPDATE_README' ) {
193
- const existingContent = await fs . readFileText ( absolutePath )
194
- const { addedChars, addedLines } = getDiffCharsAndLines ( existingContent , decodedContent )
195
- totalAddedChars += addedChars
196
- totalAddedLines += addedLines
192
+ public async countGeneratedContent ( interactionType ?: DocInteractionType ) {
193
+ let totalGeneratedChars = 0
194
+ let totalGeneratedLines = 0
195
+ let totalGeneratedFiles = 0
196
+ const filePaths = this . state . filePaths ?? [ ]
197
+
198
+ for ( const filePath of filePaths ) {
199
+ const reportedDocChange = this . getFromReportedChanges ( filePath )
200
+ if ( interactionType === 'GENERATE_README' ) {
201
+ if ( reportedDocChange ) {
202
+ const { charsAdded, linesAdded } = await this . computeFilePathDiff ( filePath , reportedDocChange )
203
+ totalGeneratedChars += charsAdded
204
+ totalGeneratedLines += linesAdded
205
+ } else {
206
+ // If no changes are reported, this is the initial README generation and no comparison with existing files is needed
207
+ const fileContent = filePath . fileContent
208
+ totalGeneratedChars += fileContent . length
209
+ totalGeneratedLines += fileContent . split ( '\n' ) . length
210
+ }
197
211
} else {
198
- totalAddedChars += decodedContent . length
199
- totalAddedLines += decodedContent . split ( '\n' ) . length
212
+ const { charsAdded, linesAdded } = await this . computeFilePathDiff ( filePath , reportedDocChange )
213
+ totalGeneratedChars += charsAdded
214
+ totalGeneratedLines += linesAdded
200
215
}
216
+ this . addToReportedChanges ( filePath )
217
+ totalGeneratedFiles += 1
218
+ }
219
+ return {
220
+ totalGeneratedChars,
221
+ totalGeneratedLines,
222
+ totalGeneratedFiles,
201
223
}
224
+ }
202
225
226
+ public async countAddedContent ( interactionType ?: DocInteractionType ) {
227
+ let totalAddedChars = 0
228
+ let totalAddedLines = 0
229
+ let totalAddedFiles = 0
230
+ const newFilePaths =
231
+ this . state . filePaths ?. filter ( ( filePath ) => ! filePath . rejected && ! filePath . changeApplied ) ?? [ ]
232
+
233
+ for ( const filePath of newFilePaths ) {
234
+ if ( interactionType === 'GENERATE_README' ) {
235
+ const fileContent = filePath . fileContent
236
+ totalAddedChars += fileContent . length
237
+ totalAddedLines += fileContent . split ( '\n' ) . length
238
+ } else {
239
+ const { charsAdded, linesAdded } = await this . computeFilePathDiff ( filePath )
240
+ totalAddedChars += charsAdded
241
+ totalAddedLines += linesAdded
242
+ }
243
+ totalAddedFiles += 1
244
+ }
203
245
return {
204
246
totalAddedChars,
205
247
totalAddedLines,
206
248
totalAddedFiles,
207
249
}
208
250
}
209
- public async sendDocGenerationTelemetryEvent ( docGenerationEvent : DocGenerationEvent ) {
251
+
252
+ public async computeFilePathDiff ( filePath : NewFileInfo , reportedChanges ?: string ) {
253
+ const leftPath = `${ filePath . workspaceFolder . uri . fsPath } /${ filePath . relativePath } `
254
+ const rightPath = filePath . virtualMemoryUri . path
255
+ const diff = await computeDiff ( leftPath , rightPath , this . tabID , docScheme , reportedChanges )
256
+ return { leftPath, rightPath, ...diff }
257
+ }
258
+
259
+ public async sendDocTelemetryEvent (
260
+ telemetryEvent : DocV2GenerationEvent | DocV2AcceptanceEvent ,
261
+ eventType : 'generation' | 'acceptance'
262
+ ) {
210
263
const client = await this . proxyClient . getClient ( )
211
264
try {
212
265
const params : SendTelemetryEventRequest = {
213
266
telemetryEvent : {
214
- docGenerationEvent ,
267
+ [ eventType === 'generation' ? 'docV2GenerationEvent' : 'docV2AcceptanceEvent' ] : telemetryEvent ,
215
268
} ,
216
269
optOutPreference : getOptOutPreference ( ) ,
217
270
userContext : {
@@ -222,13 +275,14 @@ export class Session {
222
275
ideVersion : extensionVersion ,
223
276
} ,
224
277
}
278
+
225
279
const response = await client . sendTelemetryEvent ( params ) . promise ( )
226
280
getLogger ( ) . debug (
227
- `${ featureName } : successfully sent docGenerationEvent: ConversationId: ${ docGenerationEvent . conversationId } RequestId: ${ response . $response . requestId } `
281
+ `${ featureName } : successfully sent docV2 ${ eventType === 'generation' ? 'GenerationEvent' : 'AcceptanceEvent' } : ConversationId: ${ telemetryEvent . conversationId } RequestId: ${ response . $response . requestId } `
228
282
)
229
283
} catch ( e ) {
230
284
getLogger ( ) . error (
231
- `${ featureName } : failed to send doc generation telemetry: ${ ( e as Error ) . name } : ${
285
+ `${ featureName } : failed to send doc ${ eventType } telemetry: ${ ( e as Error ) . name } : ${
232
286
( e as Error ) . message
233
287
} RequestId: ${ ( e as any ) . requestId } `
234
288
)
0 commit comments