Skip to content

Commit 963b6e9

Browse files
authored
perf: remove edit completion retry mechanism on document change (aws#2124)
1 parent 0e23e2d commit 963b6e9

File tree

4 files changed

+48
-54
lines changed

4 files changed

+48
-54
lines changed

server/aws-lsp-codewhisperer/src/language-server/inline-completion/codeWhispererServer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,9 @@ export const CodewhispererServerFactory =
633633
}
634634

635635
if (session.state !== 'ACTIVE') {
636-
logging.log(`ERROR: Trying to record trigger decision for not-active session ${sessionId}`)
636+
logging.log(
637+
`ERROR: Trying to record trigger decision for not-active session ${sessionId} with wrong state ${session.state}`
638+
)
637639
return
638640
}
639641

server/aws-lsp-codewhisperer/src/language-server/inline-completion/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export const FILENAME_CHARS_LIMIT = 1024
33
export const CONTEXT_CHARACTERS_LIMIT = 10240
44
export const EMPTY_RESULT = { sessionId: '', items: [] }
55
export const EDIT_DEBOUNCE_INTERVAL_MS = 500
6-
export const EDIT_STALE_RETRY_COUNT = 3
76
// ABAP ADT extensions commonly used with Eclipse
87
export const ABAP_EXTENSIONS = new Set([
98
'asprog',

server/aws-lsp-codewhisperer/src/language-server/inline-completion/editCompletionHandler.ts

Lines changed: 36 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
GenerateSuggestionsRequest,
1717
GenerateSuggestionsResponse,
1818
getFileContext,
19-
SuggestionType,
2019
} from '../../shared/codeWhispererService'
2120
import { CodeWhispererSession, SessionManager } from './session/sessionManager'
2221
import { CursorTracker } from './tracker/cursorTracker'
@@ -36,7 +35,7 @@ import { RejectedEditTracker } from './tracker/rejectedEditTracker'
3635
import { getErrorMessage, hasConnectionExpired } from '../../shared/utils'
3736
import { AmazonQError, AmazonQServiceConnectionExpiredError } from '../../shared/amazonQServiceManager/errors'
3837
import { DocumentChangedListener } from './documentChangedListener'
39-
import { EMPTY_RESULT, EDIT_DEBOUNCE_INTERVAL_MS, EDIT_STALE_RETRY_COUNT } from './constants'
38+
import { EMPTY_RESULT, EDIT_DEBOUNCE_INTERVAL_MS } from './constants'
4039

4140
export class EditCompletionHandler {
4241
private readonly editsEnabled: boolean
@@ -74,22 +73,19 @@ export class EditCompletionHandler {
7473
*/
7574
documentChanged() {
7675
if (this.debounceTimeout) {
77-
this.logging.info('[NEP] refresh timeout')
78-
this.debounceTimeout.refresh()
79-
}
80-
81-
if (this.isWaiting) {
82-
this.hasDocumentChangedSinceInvocation = true
76+
if (this.isWaiting) {
77+
this.hasDocumentChangedSinceInvocation = true
78+
} else {
79+
this.logging.info(`refresh and debounce edits suggestion for another ${EDIT_DEBOUNCE_INTERVAL_MS}`)
80+
this.debounceTimeout.refresh()
81+
}
8382
}
8483
}
8584

8685
async onEditCompletion(
8786
params: InlineCompletionWithReferencesParams,
8887
token: CancellationToken
8988
): Promise<InlineCompletionListWithReferences> {
90-
this.hasDocumentChangedSinceInvocation = false
91-
this.debounceTimeout = undefined
92-
9389
// On every new completion request close current inflight session.
9490
const currentSession = this.sessionManager.getCurrentSession()
9591
if (currentSession && currentSession.state == 'REQUESTING' && !params.partialResultToken) {
@@ -156,46 +152,37 @@ export class EditCompletionHandler {
156152
}
157153
}
158154

159-
// TODO: telemetry, discarded suggestions
160-
// The other easy way to do this is simply not return any suggestion (which is used when retry > 3)
161-
const invokeWithRetry = async (attempt: number = 0): Promise<InlineCompletionListWithReferences> => {
162-
return new Promise(async resolve => {
163-
this.debounceTimeout = setTimeout(async () => {
164-
try {
165-
this.isWaiting = true
166-
const result = await this._invoke(
167-
params,
168-
token,
169-
textDocument,
170-
inferredLanguageId,
171-
currentSession
172-
).finally(() => {
173-
this.isWaiting = false
155+
return new Promise(async resolve => {
156+
this.debounceTimeout = setTimeout(async () => {
157+
try {
158+
this.isWaiting = true
159+
const result = await this._invoke(
160+
params,
161+
token,
162+
textDocument,
163+
inferredLanguageId,
164+
currentSession
165+
).finally(() => {
166+
this.isWaiting = false
167+
})
168+
if (this.hasDocumentChangedSinceInvocation) {
169+
this.logging.info(
170+
'EditCompletionHandler - Document changed during execution, resolving empty result'
171+
)
172+
resolve({
173+
sessionId: SessionManager.getInstance('EDITS').getActiveSession()?.id ?? '',
174+
items: [],
174175
})
175-
if (this.hasDocumentChangedSinceInvocation) {
176-
if (attempt < EDIT_STALE_RETRY_COUNT) {
177-
this.logging.info(
178-
`EditCompletionHandler - Document changed during execution, retrying (attempt ${attempt + 1})`
179-
)
180-
this.hasDocumentChangedSinceInvocation = false
181-
const retryResult = await invokeWithRetry(attempt + 1)
182-
resolve(retryResult)
183-
} else {
184-
this.logging.info('EditCompletionHandler - Max retries reached, returning empty result')
185-
resolve(EMPTY_RESULT)
186-
}
187-
} else {
188-
this.logging.info('EditCompletionHandler - No document changes, resolving result')
189-
resolve(result)
190-
}
191-
} finally {
192-
this.debounceTimeout = undefined
176+
} else {
177+
this.logging.info('EditCompletionHandler - No document changes, resolving result')
178+
resolve(result)
193179
}
194-
}, EDIT_DEBOUNCE_INTERVAL_MS)
195-
})
196-
}
197-
198-
return invokeWithRetry()
180+
} finally {
181+
this.debounceTimeout = undefined
182+
this.hasDocumentChangedSinceInvocation = false
183+
}
184+
}, EDIT_DEBOUNCE_INTERVAL_MS)
185+
})
199186
}
200187

201188
async _invoke(

server/aws-lsp-codewhisperer/src/language-server/inline-completion/session/sessionManager.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
} from '../../../shared/codeWhispererService'
1515
import { CodewhispererLanguage } from '../../../shared/languageDetection'
1616
import { CodeWhispererSupplementalContext } from '../../../shared/models/model'
17-
import { Logging } from '@aws/language-server-runtimes/server-interface'
1817

1918
type SessionState = 'REQUESTING' | 'ACTIVE' | 'CLOSED' | 'ERROR' | 'DISCARD'
2019
export type UserDecision = 'Empty' | 'Filter' | 'Discard' | 'Accept' | 'Ignore' | 'Reject' | 'Unseen'
@@ -45,7 +44,13 @@ export class CodeWhispererSession {
4544
startTime: number
4645
// Time when Session was closed and final state of user decisions is recorded in suggestionsStates
4746
closeTime?: number = 0
48-
state: SessionState
47+
private _state: SessionState
48+
get state(): SessionState {
49+
return this._state
50+
}
51+
private set state(newState: SessionState) {
52+
this._state = newState
53+
}
4954
codewhispererSessionId?: string
5055
startPosition: Position = {
5156
line: 0,
@@ -96,7 +101,8 @@ export class CodeWhispererSession {
96101
this.classifierThreshold = data.classifierThreshold
97102
this.customizationArn = data.customizationArn
98103
this.supplementalMetadata = data.supplementalMetadata
99-
this.state = 'REQUESTING'
104+
this._state = 'REQUESTING'
105+
100106
this.startTime = new Date().getTime()
101107
}
102108

0 commit comments

Comments
 (0)