33 * SPDX-License-Identifier: Apache-2.0
44 */
55
6- import { featureName , Mode } from '../constants'
6+ import { docScheme , featureName , Mode } from '../constants'
77import { DeletedFileInfo , Interaction , NewFileInfo , SessionState , SessionStateConfig } from '../types'
88import { PrepareCodeGenState } from './sessionState'
99import { telemetry } from '../../shared/telemetry/telemetry'
@@ -19,13 +19,14 @@ import { logWithConversationId } from '../../amazonqFeatureDev/userFacingText'
1919import { ConversationIdNotFoundError } from '../../amazonqFeatureDev/errors'
2020import { referenceLogText } from '../../amazonqFeatureDev/constants'
2121import {
22- DocGenerationEvent ,
23- DocGenerationInteractionType ,
22+ DocInteractionType ,
23+ DocV2AcceptanceEvent ,
24+ DocV2GenerationEvent ,
2425 SendTelemetryEventRequest ,
2526} from '../../codewhisperer/client/codewhispereruserclient'
26- import { getDiffCharsAndLines } from '../../shared/utilities/diffUtils'
2727import { getClientId , getOperatingSystem , getOptOutPreference } from '../../shared/telemetry/util'
2828import { DocMessenger } from '../messenger'
29+ import { computeDiff } from '../../amazonq/commons/diff'
2930
3031export class Session {
3132 private _state ?: SessionState | Omit < SessionState , 'uploadId' >
@@ -38,6 +39,7 @@ export class Session {
3839
3940 // Used to keep track of whether or not the current session is currently authenticating/needs authenticating
4041 public isAuthenticating : boolean
42+ private _reportedDocChanges : { [ key : string ] : string } = { }
4143
4244 constructor (
4345 public readonly config : SessionConfig ,
@@ -177,41 +179,92 @@ export class Session {
177179 }
178180 }
179181
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+ }
184186
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+ }
191191
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+ }
197211 } 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
200215 }
216+ this . addToReportedChanges ( filePath )
217+ totalGeneratedFiles += 1
218+ }
219+ return {
220+ totalGeneratedChars,
221+ totalGeneratedLines,
222+ totalGeneratedFiles,
201223 }
224+ }
202225
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+ }
203245 return {
204246 totalAddedChars,
205247 totalAddedLines,
206248 totalAddedFiles,
207249 }
208250 }
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+ ) {
210263 const client = await this . proxyClient . getClient ( )
211264 try {
212265 const params : SendTelemetryEventRequest = {
213266 telemetryEvent : {
214- docGenerationEvent ,
267+ [ eventType === 'generation' ? 'docV2GenerationEvent' : 'docV2AcceptanceEvent' ] : telemetryEvent ,
215268 } ,
216269 optOutPreference : getOptOutPreference ( ) ,
217270 userContext : {
@@ -222,13 +275,14 @@ export class Session {
222275 ideVersion : extensionVersion ,
223276 } ,
224277 }
278+
225279 const response = await client . sendTelemetryEvent ( params ) . promise ( )
226280 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 } `
228282 )
229283 } catch ( e ) {
230284 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 } : ${
232286 ( e as Error ) . message
233287 } RequestId: ${ ( e as any ) . requestId } `
234288 )
0 commit comments