@@ -25,7 +25,6 @@ import software.aws.toolkits.core.utils.debug
25
25
import software.aws.toolkits.core.utils.getLogger
26
26
import software.aws.toolkits.core.utils.info
27
27
import software.aws.toolkits.core.utils.warn
28
- import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
29
28
import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextController
30
29
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil
31
30
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
@@ -44,6 +43,7 @@ import java.io.DataInput
44
43
import java.io.DataOutput
45
44
import java.util.Collections
46
45
import kotlin.coroutines.coroutineContext
46
+ import kotlin.time.measureTimedValue
47
47
48
48
private val contentRootPathProvider = CopyContentRootPathProvider ()
49
49
@@ -147,12 +147,20 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
147
147
val latency = System .currentTimeMillis() - startFetchingTimestamp
148
148
if (it.contents.isNotEmpty()) {
149
149
val logStr = buildString {
150
- append(" Successfully fetched supplemental context with strategy ${it.strategy} with $latency ms" )
150
+ append(
151
+ """ Q inline completion supplemental context:
152
+ | Strategy: ${it.strategy} ,
153
+ | Latency: $latency ms,
154
+ | Contents: ${it.contents.size} chunks,
155
+ | ContentLength: ${it.contentLength} chars,
156
+ | TargetFile: ${it.targetFileName} ,
157
+ """ .trimMargin()
158
+ )
151
159
it.contents.forEachIndexed { index, chunk ->
152
160
append(
153
161
"""
154
162
|
155
- | Chunk ${ index + 1 } :
163
+ | Chunk $index :
156
164
| path = ${chunk.path} ,
157
165
| score = ${chunk.score} ,
158
166
| contentLength = ${chunk.content.length}
@@ -219,55 +227,113 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
219
227
val query = generateQuery(targetContext)
220
228
221
229
val contexts = withContext(coroutineContext) {
222
- val projectContextDeferred1 = if (CodeWhispererFeatureConfigService .getInstance().getInlineCompletion()) {
223
- async {
224
- val t0 = System .currentTimeMillis()
225
- val r = fetchProjectContext(query, psiFile, targetContext)
226
- val t1 = System .currentTimeMillis()
227
- LOG .debug {
228
- buildString {
229
- append(" time elapse for fetching project context=${t1 - t0} ms; " )
230
- append(" numberOfChunks=${r.contents.size} ; " )
231
- append(" totalLength=${r.contentLength} " )
232
- }
230
+ val projectContextDeferred1 = async {
231
+ val timedCodemapContext = measureTimedValue { fetchProjectContext(query, psiFile, targetContext) }
232
+ val codemapContext = timedCodemapContext.value
233
+ LOG .debug {
234
+ buildString {
235
+ append(" time elapse for fetching project context=${timedCodemapContext.duration.inWholeMilliseconds} ms; " )
236
+ append(" numberOfChunks=${codemapContext.contents.size} ; " )
237
+ append(" totalLength=${codemapContext.contentLength} " )
233
238
}
234
-
235
- r
236
239
}
237
- } else {
238
- null
240
+
241
+ codemapContext
239
242
}
240
243
241
244
val openTabsContextDeferred1 = async {
242
- val t0 = System .currentTimeMillis()
243
- val r = fetchOpenTabsContext(query, psiFile, targetContext)
244
- val t1 = System .currentTimeMillis()
245
+ val timedOpentabContext = measureTimedValue { fetchOpenTabsContext(query, psiFile, targetContext) }
246
+ val opentabContext = timedOpentabContext.value
245
247
LOG .debug {
246
248
buildString {
247
- append(" time elapse for open tabs context=${t1 - t0 } ms; " )
248
- append(" numberOfChunks=${r .contents.size} ; " )
249
- append(" totalLength=${r .contentLength} " )
249
+ append(" time elapse for open tabs context=${timedOpentabContext.duration.inWholeMilliseconds } ms; " )
250
+ append(" numberOfChunks=${opentabContext .contents.size} ; " )
251
+ append(" totalLength=${opentabContext .contentLength} " )
250
252
}
251
253
}
252
254
253
- r
255
+ opentabContext
254
256
}
255
257
256
- if (projectContextDeferred1 != null ) {
257
- awaitAll(projectContextDeferred1, openTabsContextDeferred1)
258
- } else {
259
- awaitAll(openTabsContextDeferred1)
260
- }
258
+ awaitAll(projectContextDeferred1, openTabsContextDeferred1)
261
259
}
262
260
263
- val projectContext = contexts.find { it.strategy == CrossFileStrategy .ProjectContext }
261
+ val projectContext = contexts.find { it.strategy == CrossFileStrategy .Codemap }
264
262
val openTabsContext = contexts.find { it.strategy == CrossFileStrategy .OpenTabsBM25 }
265
263
266
- return if (projectContext != null && projectContext.contents.isNotEmpty()) {
267
- projectContext
268
- } else {
269
- openTabsContext ? : SupplementalContextInfo .emptyCrossFileContextInfo(targetContext.filename)
264
+ /* *
265
+ * We're using both codemap and opentabs context
266
+ * 1. If both are present, codemap should live in the first of supplemental context list, i.e [codemap, opentabs_0, opentabs_1...] with strategy name codemap
267
+ * 2. If only one is present, return the one present with corresponding strategy name, either codemap or opentabs
268
+ * 3. If none is present, return empty list with strategy name empty
269
+ *
270
+ * Service will throw 400 error when context length is greater than 20480, drop the last chunk until the total length fits in the cap
271
+ */
272
+ val contextBeforeTruncation = when {
273
+ projectContext == null && openTabsContext == null -> SupplementalContextInfo .emptyCrossFileContextInfo(targetContext.filename)
274
+
275
+ projectContext != null && openTabsContext != null -> {
276
+ val context1 = projectContext.contents
277
+ val context2 = openTabsContext.contents
278
+ val mergedContext = (context1 + context2).filter { it.content.isNotEmpty() }
279
+
280
+ val strategy = if (projectContext.contentLength != 0 && openTabsContext.contentLength != 0 ) {
281
+ CrossFileStrategy .Codemap
282
+ } else if (projectContext.contentLength != 0 ) {
283
+ CrossFileStrategy .Codemap
284
+ } else if (openTabsContext.contentLength != 0 ) {
285
+ CrossFileStrategy .OpenTabsBM25
286
+ } else {
287
+ CrossFileStrategy .Empty
288
+ }
289
+
290
+ SupplementalContextInfo (
291
+ isUtg = false ,
292
+ contents = mergedContext,
293
+ targetFileName = targetContext.filename,
294
+ strategy = strategy
295
+ )
296
+ }
297
+
298
+ projectContext != null -> {
299
+ return if (projectContext.contentLength == 0 ) {
300
+ SupplementalContextInfo .emptyCrossFileContextInfo(targetContext.filename)
301
+ } else {
302
+ SupplementalContextInfo (
303
+ isUtg = false ,
304
+ contents = projectContext.contents,
305
+ targetFileName = targetContext.filename,
306
+ strategy = CrossFileStrategy .Codemap
307
+ )
308
+ }
309
+ }
310
+
311
+ openTabsContext != null -> {
312
+ return if (openTabsContext.contentLength == 0 ) {
313
+ SupplementalContextInfo .emptyCrossFileContextInfo(targetContext.filename)
314
+ } else {
315
+ SupplementalContextInfo (
316
+ isUtg = false ,
317
+ contents = openTabsContext.contents,
318
+ targetFileName = targetContext.filename,
319
+ strategy = CrossFileStrategy .OpenTabsBM25
320
+ )
321
+ }
322
+ }
323
+
324
+ else -> SupplementalContextInfo .emptyCrossFileContextInfo(targetContext.filename)
325
+ }
326
+
327
+ return truncateContext(contextBeforeTruncation)
328
+ }
329
+
330
+ fun truncateContext (context : SupplementalContextInfo ): SupplementalContextInfo {
331
+ var c = context.contents
332
+ while (c.sumOf { it.content.length } >= CodeWhispererConstants .CrossFile .MAX_TOTAL_LENGTH ) {
333
+ c = c.dropLast(1 )
270
334
}
335
+
336
+ return context.copy(contents = c)
271
337
}
272
338
273
339
@VisibleForTesting
@@ -285,7 +351,7 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
285
351
)
286
352
},
287
353
targetFileName = targetContext.filename,
288
- strategy = CrossFileStrategy .ProjectContext
354
+ strategy = CrossFileStrategy .Codemap
289
355
)
290
356
}
291
357
0 commit comments