Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererConnection
import software.aws.toolkits.jetbrains.services.amazonq.SUPPLEMENTAL_CONTEXT_TIMEOUT
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.GetConfigurationFromServerParams
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LspServerConfigurations
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorManager
Expand Down Expand Up @@ -91,6 +94,9 @@ import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodewhispererCompletionType
import software.aws.toolkits.telemetry.CodewhispererSuggestionState
import software.aws.toolkits.telemetry.CodewhispererTriggerType
import java.net.URI
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit

@Service
Expand Down Expand Up @@ -231,7 +237,8 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
buildCodeWhispererRequest(
requestContext.fileContextInfo,
requestContext.awaitSupplementalContext(),
requestContext.customizationArn
requestContext.customizationArn,
requestContext.workspaceId
)
)

Expand Down Expand Up @@ -666,7 +673,33 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
// 5. customization
val customizationArn = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn

return RequestContext(project, editor, triggerTypeInfo, caretPosition, fileContext, supplementalContext, connection, latencyContext, customizationArn)
var workspaceId: String? = null
try {
val workspacesInfos = getWorkspaceIds(project).get().workspaces
for (workspaceInfo in workspacesInfos) {
val workspaceRootPath = Paths.get(URI(workspaceInfo.workspaceRoot)).toString()
if (psiFile.virtualFile.path.startsWith(workspaceRootPath)) {
workspaceId = workspaceInfo.workspaceId
LOG.info { "Found workspaceId from LSP '$workspaceId'" }
break
}
}
} catch (e: Exception) {
LOG.warn { "Cannot get workspaceId from LSP'$e'" }
}
return RequestContext(
project, editor, triggerTypeInfo, caretPosition,
fileContext, supplementalContext, connection, latencyContext, customizationArn, workspaceId
)
}

private fun getWorkspaceIds(project: Project): CompletableFuture<LspServerConfigurations> {
val payload = GetConfigurationFromServerParams(
section = "aws.q.workspaceContext"
)
return AmazonQLspService.executeIfRunning(project) { server ->
server.getConfigurationFromServer(payload)
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))
}

fun validateResponse(response: GenerateCompletionsResponse): GenerateCompletionsResponse {
Expand Down Expand Up @@ -800,6 +833,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
fileContextInfo: FileContextInfo,
supplementalContext: SupplementalContextInfo?,
customizationArn: String?,
workspaceId: String?,
): GenerateCompletionsRequest {
val programmingLanguage = ProgrammingLanguage.builder()
.languageName(fileContextInfo.programmingLanguage.toCodeWhispererRuntimeLanguage().languageId)
Expand Down Expand Up @@ -828,6 +862,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
.referenceTrackerConfiguration { it.recommendationsWithReferences(includeCodeWithReference) }
.customizationArn(customizationArn)
.optOutPreference(getTelemetryOptOutPreference())
.workspaceId(workspaceId)
.build()
}
}
Expand All @@ -843,6 +878,7 @@ data class RequestContext(
val connection: ToolkitConnection?,
val latencyContext: LatencyContext,
val customizationArn: String?,
val workspaceId: String?,
) {
// TODO: should make the entire getRequestContext() suspend function instead of making supplemental context only
var supplementalContext: SupplementalContextInfo? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ class CodeWhispererConfigurable(private val project: Project) :
}

group(message("aws.settings.codewhisperer.group.q_chat")) {
row {
checkBox(message("aws.settings.codewhisperer.workspace_context")).apply {
connect.subscribe(
ToolkitConnectionManagerListener.TOPIC,
object : ToolkitConnectionManagerListener {
override fun activeConnectionChanged(newConnection: ToolkitConnection?) {
enabled(isCodeWhispererEnabled(project))
}
}
)
enabled(invoke)
bindSelected(codeWhispererSettings::isWorkspaceContextEnabled, codeWhispererSettings::toggleWorkspaceContextEnabled)
}.comment(message("aws.settings.codewhisperer.workspace_context.tooltip"))
}
row {
checkBox(message("aws.settings.codewhisperer.project_context")).apply {
connect.subscribe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@ fun aRequestContext(
Random.nextLong(),
aString()
),
customizationArn = null
customizationArn = null,
workspaceId = null
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
CodeWhispererLspConfiguration(
shouldShareData = CodeWhispererSettings.getInstance().isMetricOptIn(),
shouldShareCodeReferences = CodeWhispererSettings.getInstance().isIncludeCodeWithReference(),
shouldEnableWorkspaceContext = CodeWhispererSettings.getInstance().isWorkspaceContextEnabled()
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest
import org.eclipse.lsp4j.services.LanguageServer
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.GetConfigurationFromServerParams
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LspServerConfigurations
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.credentials.UpdateCredentialsPayload
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.dependencies.DidChangeDependencyPathsParams
import java.util.concurrent.CompletableFuture
Expand All @@ -24,4 +26,7 @@ interface AmazonQLanguageServer : LanguageServer {

@JsonNotification("aws/credentials/token/delete")
fun deleteTokenCredentials(): CompletableFuture<Unit>

@JsonRequest("aws/getConfigurationFromServer")
fun getConfigurationFromServer(params: GetConfigurationFromServerParams): CompletableFuture<LspServerConfigurations>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ object AmazonQLspConstants {
const val LSP_CW_CONFIGURATION_KEY = "aws.codeWhisperer"
const val LSP_CW_OPT_OUT_KEY = "shareCodeWhispererContentWithAWS"
const val LSP_CODE_REFERENCES_OPT_OUT_KEY = "includeSuggestionsWithCodeReferences"
const val LSP_WORKSPACE_CONTEXT_ENABLED_KEY = "workspaceContext"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ data class CodeWhispererLspConfiguration(
@SerializedName(AmazonQLspConstants.LSP_CW_OPT_OUT_KEY)
val shouldShareData: Boolean? = null,

@SerializedName(AmazonQLspConstants.LSP_WORKSPACE_CONTEXT_ENABLED_KEY)
val shouldEnableWorkspaceContext: Boolean? = null,

@SerializedName(AmazonQLspConstants.LSP_CODE_REFERENCES_OPT_OUT_KEY)
val shouldShareCodeReferences: Boolean? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws

data class GetConfigurationFromServerParams(
val section: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws

// This represents each item in the array
data class WorkspaceInfo(val workspaceRoot: String, val workspaceId: String)

// This represents the entire array
data class LspServerConfigurations(val workspaces: List<WorkspaceInfo>)
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ class CodeWhispererSettings : PersistentStateComponent<CodeWhispererConfiguratio
}
}

fun toggleWorkspaceContextEnabled(value: Boolean) {
state.value[CodeWhispererConfigurationType.IsWorkspaceContextEnabled] = value
ProjectManager.getInstance().openProjects.forEach {
if (it.isDisposed) {
return@forEach
}
AmazonQLspService.didChangeConfiguration(it)
}
}

fun isWorkspaceContextEnabled() = state.value.getOrDefault(CodeWhispererConfigurationType.IsWorkspaceContextEnabled, true)
fun isProjectContextEnabled() = state.value.getOrDefault(CodeWhispererConfigurationType.IsProjectContextEnabled, false)

private fun hasEnabledProjectContextOnce() = state.value.getOrDefault(CodeWhispererConfigurationType.HasEnabledProjectContextOnce, false)
Expand Down Expand Up @@ -199,6 +210,7 @@ enum class CodeWhispererConfigurationType {
HasEnabledProjectContextOnce,
IsQPrioritizedForTabAccept,
IsTabAcceptPriorityNotificationShownOnce,
IsWorkspaceContextEnabled,
}

enum class CodeWhispererStringConfigurationType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,43 @@ class AmazonQLanguageClientImplTest {
@Test
fun `configuration for codeWhisperer respects opt-out`() {
CodeWhispererSettings.getInstance().toggleMetricOptIn(false)
CodeWhispererSettings.getInstance().toggleWorkspaceContextEnabled(true)
assertThat(sut.configuration(configurationParams("aws.codeWhisperer")).get())
.singleElement()
.isEqualTo(
CodeWhispererLspConfiguration(
shouldShareData = false,
shouldShareCodeReferences = false
shouldShareCodeReferences = false,
shouldEnableWorkspaceContext = true
)
)
}

@Test
fun `configuration for codeWhisperer respects opt-in`() {
CodeWhispererSettings.getInstance().toggleMetricOptIn(true)
CodeWhispererSettings.getInstance().toggleWorkspaceContextEnabled(true)
assertThat(sut.configuration(configurationParams("aws.codeWhisperer")).get())
.singleElement()
.isEqualTo(
CodeWhispererLspConfiguration(
shouldShareData = true,
shouldShareCodeReferences = false
shouldShareCodeReferences = false,
shouldEnableWorkspaceContext = true
)
)
}

@Test
fun `configuration for workspace context respects opt-in`() {
CodeWhispererSettings.getInstance().toggleWorkspaceContextEnabled(false)
assertThat(sut.configuration(configurationParams("aws.codeWhisperer")).get())
.singleElement()
.isEqualTo(
CodeWhispererLspConfiguration(
shouldShareData = true,
shouldShareCodeReferences = false,
shouldEnableWorkspaceContext = false
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ aws.settings.codewhisperer.project_context_index_max_size.tooltip=The maximum si
aws.settings.codewhisperer.project_context_index_thread=Workspace index worker threads
aws.settings.codewhisperer.project_context_index_thread.tooltip=Number of worker threads of Amazon Q local index process. Set to 0 to use system default worker threads for balanced performance. Please restart or reload IntelliJ after changing worker threads.
aws.settings.codewhisperer.warning=To use Amazon Q, login with AWS Builder ID or AWS IAM Identity Center.
aws.settings.codewhisperer.workspace_context=Workspace context
aws.settings.codewhisperer.workspace_context.tooltip=When checked, Amazon Q will enable server side project context.
aws.settings.dynamic_resources_configurable.clear_all=Clear All
aws.settings.dynamic_resources_configurable.select_all=Select All
aws.settings.dynamic_resources_configurable.suggest_types.dialog.message=Please suggest additional AWS resource types (e.g. AWS::S3::Bucket)\nyou would like to see supported in future releases.\n\n(max length: 2000 chars)
Expand Down
Loading