@@ -25,7 +25,6 @@ import software.aws.toolkits.core.utils.debug
2525import software.aws.toolkits.core.utils.getLogger
2626import software.aws.toolkits.core.utils.info
2727import software.aws.toolkits.core.utils.warn
28- import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
2928import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextController
3029import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil
3130import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
@@ -44,6 +43,7 @@ import java.io.DataInput
4443import java.io.DataOutput
4544import java.util.Collections
4645import kotlin.coroutines.coroutineContext
46+ import kotlin.time.measureTimedValue
4747
4848private val contentRootPathProvider = CopyContentRootPathProvider ()
4949
@@ -147,12 +147,20 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
147147 val latency = System .currentTimeMillis() - startFetchingTimestamp
148148 if (it.contents.isNotEmpty()) {
149149 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+ )
151159 it.contents.forEachIndexed { index, chunk ->
152160 append(
153161 """
154162 |
155- | Chunk ${ index + 1 } :
163+ | Chunk $index :
156164 | path = ${chunk.path} ,
157165 | score = ${chunk.score} ,
158166 | contentLength = ${chunk.content.length}
@@ -219,55 +227,113 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
219227 val query = generateQuery(targetContext)
220228
221229 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} " )
233238 }
234-
235- r
236239 }
237- } else {
238- null
240+
241+ codemapContext
239242 }
240243
241244 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
245247 LOG .debug {
246248 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} " )
250252 }
251253 }
252254
253- r
255+ opentabContext
254256 }
255257
256- if (projectContextDeferred1 != null ) {
257- awaitAll(projectContextDeferred1, openTabsContextDeferred1)
258- } else {
259- awaitAll(openTabsContextDeferred1)
260- }
258+ awaitAll(projectContextDeferred1, openTabsContextDeferred1)
261259 }
262260
263- val projectContext = contexts.find { it.strategy == CrossFileStrategy .ProjectContext }
261+ val projectContext = contexts.find { it.strategy == CrossFileStrategy .Codemap }
264262 val openTabsContext = contexts.find { it.strategy == CrossFileStrategy .OpenTabsBM25 }
265263
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 )
270334 }
335+
336+ return context.copy(contents = c)
271337 }
272338
273339 @VisibleForTesting
@@ -285,7 +351,7 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi
285351 )
286352 },
287353 targetFileName = targetContext.filename,
288- strategy = CrossFileStrategy .ProjectContext
354+ strategy = CrossFileStrategy .Codemap
289355 )
290356 }
291357
0 commit comments