-
Notifications
You must be signed in to change notification settings - Fork 274
feat(inline completion): enhance inline completion supplemental context fetching #5256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
05eb0a7
1cde7de
c4bc45d
0a72109
9f20bcc
5479787
e4af399
04d3a58
bf1553c
3e5da46
ae2dce9
d816c0f
e6e5fbe
20d5180
ccf5f0d
e29a079
3f2542d
39bc048
fbf9b05
0021456
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "type" : "feature", | ||
| "description" : "Enhance Q inline completion context fetching for better suggestion quality" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,6 @@ import software.aws.toolkits.core.utils.debug | |
| import software.aws.toolkits.core.utils.getLogger | ||
| import software.aws.toolkits.core.utils.info | ||
| import software.aws.toolkits.core.utils.warn | ||
| import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService | ||
| import software.aws.toolkits.jetbrains.services.amazonq.project.ProjectContextController | ||
| import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorUtil | ||
| import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage | ||
|
|
@@ -147,12 +146,20 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi | |
| val latency = System.currentTimeMillis() - startFetchingTimestamp | ||
| if (it.contents.isNotEmpty()) { | ||
| val logStr = buildString { | ||
| append("Successfully fetched supplemental context with strategy ${it.strategy} with $latency ms") | ||
| append( | ||
| """Q inline completion supplemental context: | ||
| | Strategy: ${it.strategy}, | ||
| | Latency: $latency ms, | ||
| | Contents: ${it.contents.size} chunks, | ||
| | ContentLength: ${it.contentLength} chars, | ||
| | TargetFile: ${it.targetFileName}, | ||
| """.trimMargin() | ||
| ) | ||
| it.contents.forEachIndexed { index, chunk -> | ||
| append( | ||
| """ | ||
| | | ||
| | Chunk ${index + 1}: | ||
| | Chunk $index: | ||
| | path = ${chunk.path}, | ||
| | score = ${chunk.score}, | ||
| | contentLength = ${chunk.content.length} | ||
|
|
@@ -219,55 +226,115 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi | |
| val query = generateQuery(targetContext) | ||
|
|
||
| val contexts = withContext(coroutineContext) { | ||
| val projectContextDeferred1 = if (CodeWhispererFeatureConfigService.getInstance().getInlineCompletion()) { | ||
| async { | ||
| val t0 = System.currentTimeMillis() | ||
| val r = fetchProjectContext(query, psiFile, targetContext) | ||
| val t1 = System.currentTimeMillis() | ||
| LOG.debug { | ||
| buildString { | ||
| append("time elapse for fetching project context=${t1 - t0}ms; ") | ||
| append("numberOfChunks=${r.contents.size}; ") | ||
| append("totalLength=${r.contentLength}") | ||
| } | ||
| val projectContextDeferred1 = async { | ||
| val t0 = System.currentTimeMillis() | ||
| val codemapContext = fetchProjectContext(query, psiFile, targetContext) | ||
| val t1 = System.currentTimeMillis() | ||
| LOG.debug { | ||
| buildString { | ||
| append("time elapse for fetching project context=${t1 - t0}ms; ") | ||
| append("numberOfChunks=${codemapContext.contents.size}; ") | ||
| append("totalLength=${codemapContext.contentLength}") | ||
| } | ||
|
|
||
| r | ||
| } | ||
| } else { | ||
| null | ||
|
|
||
| codemapContext | ||
| } | ||
|
|
||
| val openTabsContextDeferred1 = async { | ||
| val t0 = System.currentTimeMillis() | ||
| val r = fetchOpenTabsContext(query, psiFile, targetContext) | ||
| val opentabContext = fetchOpenTabsContext(query, psiFile, targetContext) | ||
| val t1 = System.currentTimeMillis() | ||
| LOG.debug { | ||
| buildString { | ||
| append("time elapse for open tabs context=${t1 - t0}ms; ") | ||
| append("numberOfChunks=${r.contents.size}; ") | ||
| append("totalLength=${r.contentLength}") | ||
| append("numberOfChunks=${opentabContext.contents.size}; ") | ||
| append("totalLength=${opentabContext.contentLength}") | ||
| } | ||
| } | ||
|
|
||
| r | ||
| opentabContext | ||
| } | ||
|
|
||
| if (projectContextDeferred1 != null) { | ||
| awaitAll(projectContextDeferred1, openTabsContextDeferred1) | ||
| } else { | ||
| awaitAll(openTabsContextDeferred1) | ||
| } | ||
| awaitAll(projectContextDeferred1, openTabsContextDeferred1) | ||
| } | ||
|
|
||
| val projectContext = contexts.find { it.strategy == CrossFileStrategy.ProjectContext } | ||
| val projectContext = contexts.find { it.strategy == CrossFileStrategy.Codemap } | ||
| val openTabsContext = contexts.find { it.strategy == CrossFileStrategy.OpenTabsBM25 } | ||
|
|
||
| return if (projectContext != null && projectContext.contents.isNotEmpty()) { | ||
| projectContext | ||
| } else { | ||
| openTabsContext ?: SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename) | ||
| /** | ||
| * We're using both codemap and opentabs context | ||
| * 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 | ||
| * 2. If only one is present, return the one present with corresponding strategy name, either codemap or opentabs | ||
| * 3. If none is present, return empty list with strategy name empty | ||
| * | ||
| * Service will throw 400 error when context length is greater than 20480, drop the last chunk until the total length fits in the cap | ||
| */ | ||
| val contextBeforeTruncation = when { | ||
| projectContext == null && openTabsContext == null -> SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename) | ||
|
|
||
| projectContext != null && openTabsContext != null -> { | ||
| val context1 = projectContext.contents | ||
| val context2 = openTabsContext.contents | ||
| val mergedContext = (context1 + context2).filter { it.content.isNotEmpty() } | ||
|
|
||
| val strategy = if (projectContext.contentLength != 0 && openTabsContext.contentLength != 0) { | ||
| CrossFileStrategy.Codemap | ||
| } else if (projectContext.contentLength != 0) { | ||
| CrossFileStrategy.Codemap | ||
| } else if (openTabsContext.contentLength != 0) { | ||
| CrossFileStrategy.OpenTabsBM25 | ||
| } else { | ||
| CrossFileStrategy.Empty | ||
| } | ||
|
|
||
| SupplementalContextInfo( | ||
| isUtg = false, | ||
| contents = mergedContext, | ||
| targetFileName = targetContext.filename, | ||
| strategy = strategy | ||
| ) | ||
| } | ||
|
|
||
| projectContext != null -> { | ||
| return if (projectContext.contentLength == 0) { | ||
| SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename) | ||
| } else { | ||
| SupplementalContextInfo( | ||
| isUtg = false, | ||
| contents = projectContext.contents, | ||
| targetFileName = targetContext.filename, | ||
| strategy = CrossFileStrategy.Codemap | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| openTabsContext != null -> { | ||
| return if (openTabsContext.contentLength == 0) { | ||
| SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename) | ||
| } else { | ||
| SupplementalContextInfo( | ||
| isUtg = false, | ||
| contents = openTabsContext.contents, | ||
| targetFileName = targetContext.filename, | ||
| strategy = CrossFileStrategy.OpenTabsBM25 | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| else -> SupplementalContextInfo.emptyCrossFileContextInfo(targetContext.filename) | ||
| } | ||
|
|
||
| return truncateContext(contextBeforeTruncation) | ||
| } | ||
|
|
||
| fun truncateContext(context: SupplementalContextInfo): SupplementalContextInfo { | ||
| var c = context.contents | ||
| while (c.sumOf { it.content.length } >= CodeWhispererConstants.CrossFile.MAX_TOTAL_LENGTH) { | ||
| c = c.dropLast(1) | ||
|
Comment on lines
+332
to
+333
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks nice, but maybe optimize
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea agreeeeeeeee
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let me follow up on it |
||
| } | ||
|
|
||
| return context.copy(contents = c) | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
|
|
@@ -285,7 +352,7 @@ class DefaultCodeWhispererFileContextProvider(private val project: Project) : Fi | |
| ) | ||
| }, | ||
| targetFileName = targetContext.filename, | ||
| strategy = CrossFileStrategy.ProjectContext | ||
| strategy = CrossFileStrategy.Codemap | ||
| ) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.