mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
JBAI-5408 Release ML Imports Ranking in 2024.3
[ml tools] Fix build issue [ml tools] Fix build issue [ml tools] Fix build issue [ml tools] Replace task object with service [ml tools] Fix rebase issues Add session duration metric [ml imports ranking] Log fully only during EAP [ml imports ranking] Set up FUS logs [ml tools] Add dependency on ml api library Merge-request: IJ-MR-142857 Merged-by: Gleb Marin <Gleb.Marin@jetbrains.com> GitOrigin-RevId: 69be00b4b8f38ec71208c3b17cd6566a34508859
This commit is contained in:
committed by
intellij-monorepo-bot
parent
786deec129
commit
dc3c5730ea
+6
-6
@@ -1,20 +1,20 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.mlapi.extension" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="com.jetbrains.mlapi:extension:32">
|
||||
<properties include-transitive-deps="false" maven-id="com.jetbrains.mlapi:extension:34">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/32/extension-32.jar">
|
||||
<sha256sum>39e57d44df6db53d7336ce8b5ab3e6486b3f0beef16e7c792cbe7a0cf3e4d50b</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/34/extension-34.jar">
|
||||
<sha256sum>1d18aa7a0d891cab230ddb67e3d1174830e1472c7373c37296c23a3905040a7d</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/32/extension-32.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/34/extension-34.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/32/extension-32-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/34/extension-34-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/32/extension-32-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/extension/34/extension-34-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
+6
-6
@@ -1,20 +1,20 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jetbrains.mlapi.usage" type="repository">
|
||||
<properties include-transitive-deps="false" maven-id="com.jetbrains.mlapi:usage:32">
|
||||
<properties include-transitive-deps="false" maven-id="com.jetbrains.mlapi:usage:34">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/32/usage-32.jar">
|
||||
<sha256sum>714206436b4e8d619d322cba1db73c8e0908ccaf292fa3278a2c85b64512fe8a</sha256sum>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/34/usage-34.jar">
|
||||
<sha256sum>b28271a408b89f4a9e00e4a849543b5e9206333472ee379027d969e5d1b42e6f</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/32/usage-32.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/34/usage-34.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/32/usage-32-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/34/usage-34-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/32/usage-32-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/jetbrains/mlapi/usage/34/usage-34-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -19,7 +19,7 @@ class IJPlatform : com.jetbrains.ml.platform.MLApiPlatform(
|
||||
override val taskListeners: Map<String, List<com.jetbrains.ml.monitoring.MLTaskListenerTyped<*, *>>>
|
||||
get() = KeyedMessagingProvider.collect(MLTaskListenerTyped.TOPIC)
|
||||
|
||||
override fun addTaskListener(taskId: String, taskListener: com.jetbrains.ml.monitoring.MLTaskListenerTyped<*, *>): ExtensionController {
|
||||
override fun addTaskListener(taskId: String, taskListener: com.jetbrains.ml.monitoring.MLTaskListenerTyped<*, *>): com.jetbrains.ml.platform.MLApiPlatform.ExtensionController {
|
||||
val connection = application.messageBus.connect()
|
||||
|
||||
fun <M : com.jetbrains.ml.model.MLModel<P>, P : Any> capturingType(taskListenerTyped: com.jetbrains.ml.monitoring.MLTaskListenerTyped<M, P>) {
|
||||
|
||||
+15
-3
@@ -8,7 +8,6 @@ import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.platform.ml.feature.Feature
|
||||
import com.intellij.platform.ml.feature.FeatureDeclaration
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.awt.event.KeyAdapter
|
||||
@@ -18,6 +17,9 @@ import kotlin.math.pow
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.DurationUnit
|
||||
import com.intellij.platform.ml.feature.FeatureDeclaration as OldFeatureDeclaration
|
||||
import com.jetbrains.ml.Feature as NewFeature
|
||||
import com.jetbrains.ml.FeatureDeclaration as NewFeatureDeclaration
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Service
|
||||
@@ -49,6 +51,12 @@ class TypingSpeedTracker {
|
||||
}
|
||||
}
|
||||
|
||||
fun getTypingSpeedNewEventPairs(): Collection<Pair<EventPair<*>, NewFeature>> = DECAY_DURATIONS_NEW.mapNotNull { (decayDuration, eventFieldAndFeature) ->
|
||||
typingSpeeds[decayDuration]?.let {
|
||||
(eventFieldAndFeature.first with it) to (eventFieldAndFeature.second with it)
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun getTypingSpeed(decayDuration: Duration): Float? = typingSpeeds[decayDuration]
|
||||
|
||||
@@ -73,10 +81,14 @@ class TypingSpeedTracker {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DECAY_DURATIONS = listOf(1, 2, 5, 30).associate { it.seconds to Pair(EventFields.Float("typing_speed_${it}s"), FeatureDeclaration.float("typing_speed_${it}s").nullable()) }
|
||||
private val DECAY_DURATIONS = listOf(1, 2, 5, 30)
|
||||
.associate { it.seconds to Pair(EventFields.Float("typing_speed_${it}s"), OldFeatureDeclaration.float("typing_speed_${it}s").nullable()) }
|
||||
private val DECAY_DURATIONS_NEW = listOf(1, 2, 5, 30)
|
||||
.associate { it.seconds to Pair(EventFields.Float("typing_speed_${it}s"), NewFeatureDeclaration.float("typing_speed_${it}s").nullable()) }
|
||||
|
||||
fun getInstance(): TypingSpeedTracker = service()
|
||||
fun getEventFields(): Array<EventField<*>> = DECAY_DURATIONS.values.map { it.first }.toTypedArray()
|
||||
fun getFeatures(): Set<FeatureDeclaration<*>> = DECAY_DURATIONS.values.map { it.second }.toSet()
|
||||
fun getFeatures(): Set<OldFeatureDeclaration<*>> = DECAY_DURATIONS.values.map { it.second }.toSet()
|
||||
fun getFeaturesNew(): List<NewFeatureDeclaration<*>> = DECAY_DURATIONS_NEW.values.map { it.second }
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.inline.completion.ml
|
||||
|
||||
import com.intellij.codeInsight.inline.completion.logs.TypingSpeedTracker
|
||||
import com.jetbrains.ml.*
|
||||
|
||||
internal class TypingSpeedFeatureProvider : FeatureProvider(MLUnitTyping) {
|
||||
object Features {
|
||||
val TIME_SINCE_LAST_TYPING = FeatureDeclaration.long("time_since_last_typing").nullable()
|
||||
}
|
||||
|
||||
override val featureComputationPolicy = FeatureComputationPolicy(true, true)
|
||||
|
||||
override suspend fun computeFeatures(units: MLUnitsMap, usefulFeaturesFilter: FeatureFilter): List<Feature> = buildList {
|
||||
with(units[MLUnitTyping]) {
|
||||
val timeSinceLastTyping = getTimeSinceLastTyping()
|
||||
if (timeSinceLastTyping != null) {
|
||||
add(Features.TIME_SINCE_LAST_TYPING.with(timeSinceLastTyping))
|
||||
addAll(getTypingSpeedNewEventPairs().map { it.second })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override val featureDeclarations = TypingSpeedTracker.getFeaturesNew() + Features.TIME_SINCE_LAST_TYPING
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.intellij.lang.Language
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.platform.ml.Tier
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.jetbrains.ml.MLUnit
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -14,6 +15,12 @@ object TierTyping : Tier<TypingSpeedTracker>()
|
||||
@ApiStatus.Internal
|
||||
object TierCaretLocation : Tier<CaretLocation>()
|
||||
|
||||
@ApiStatus.Internal
|
||||
object MLUnitTyping : MLUnit<TypingSpeedTracker>("typing_speed_tracker")
|
||||
|
||||
@ApiStatus.Internal
|
||||
object MLUnitCaretLocation : MLUnit<CaretLocation>("caret_location")
|
||||
|
||||
@ApiStatus.Internal
|
||||
data class CaretLocation(
|
||||
val psiFile: PsiFile,
|
||||
|
||||
@@ -1692,6 +1692,7 @@
|
||||
<!-- ML API -->
|
||||
<platform.ml.descriptor implementation="com.intellij.codeInsight.inline.completion.ml.TypingFeatures"/>
|
||||
<platform.ml.environmentExtender implementation="com.intellij.codeInsight.inline.completion.ml.TypingSpeedProvider"/>
|
||||
<platform.ml.featureProvider implementation="com.intellij.codeInsight.inline.completion.ml.TypingSpeedFeatureProvider"/>
|
||||
|
||||
<!-- Presentation Assistant -->
|
||||
<applicationService serviceImplementation="com.intellij.platform.ide.impl.presentationAssistant.PresentationAssistant"/>
|
||||
|
||||
@@ -508,6 +508,7 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.run.runAnything.PyRunAnythingCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.debugger.statistics.PyDataViewerCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.sdk.installer.BinaryInstallerUsagesCollector"/>
|
||||
<statistics.counterUsagesCollector implementationClass="com.jetbrains.python.codeInsight.imports.mlapi.PyCharmImportsRankingLogs"/>
|
||||
|
||||
<!-- Code-insight IDE bridge -->
|
||||
<applicationService serviceInterface="com.jetbrains.python.PythonRuntimeService"
|
||||
@@ -630,6 +631,12 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
description="Show type hints for all parameters in parameter info window"/>
|
||||
<registryKey key="python.unified.interpreter.configuration" defaultValue="true"
|
||||
description="Use the same UI to configure interpreters in IDE widget and New Project Wizard"/>
|
||||
|
||||
<!-- Machine Learning -->
|
||||
<registryKey
|
||||
key="quickfix.ranking.ml"
|
||||
defaultValue="[IN_EXPERIMENT*|ENABLED|DISABLED]"
|
||||
description="Enable ML ranking in quick fix for missing imports"/>
|
||||
</extensions>
|
||||
|
||||
<extensionPoints>
|
||||
|
||||
@@ -38,5 +38,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.backend.workspace" />
|
||||
<orderEntry type="module" module-name="intellij.python.parser" />
|
||||
<orderEntry type="module" module-name="intellij.python.syntax.core" exported="" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ml" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -30,6 +30,12 @@
|
||||
<projectService serviceInterface="com.jetbrains.python.debugger.PySignatureCacheManager"
|
||||
serviceImplementation="com.jetbrains.python.debugger.PySignatureCacheManagerImpl"/>
|
||||
|
||||
<!-- ML API -->
|
||||
<platform.ml.featureProvider implementation="com.jetbrains.python.codeInsight.imports.mlapi.features.CandidatesListFeatures"/>
|
||||
<platform.ml.featureProvider implementation="com.jetbrains.python.codeInsight.imports.mlapi.features.ImportCandidateRelativeFeatures"/>
|
||||
<platform.ml.featureProvider implementation="com.jetbrains.python.codeInsight.imports.mlapi.features.PrimitiveImportFeatures"/>
|
||||
<platform.ml.featureProvider implementation="com.jetbrains.python.codeInsight.imports.mlapi.features.PsiStructureFeatures"/>
|
||||
|
||||
<stubIndex implementation="com.jetbrains.python.psi.stubs.PyClassNameIndex"/>
|
||||
<stubIndex implementation="com.jetbrains.python.psi.stubs.PyClassNameIndexInsensitive"/>
|
||||
<stubIndex implementation="com.jetbrains.python.psi.stubs.PyFunctionNameIndex"/>
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi.features
|
||||
|
||||
import com.jetbrains.ml.*
|
||||
import com.jetbrains.python.codeInsight.imports.mlapi.MLUnitImportCandidatesList
|
||||
|
||||
class CandidatesListFeatures : FeatureProvider(MLUnitImportCandidatesList) {
|
||||
object Features {
|
||||
val LENGTH = FeatureDeclaration.int("n_candidates") {
|
||||
"The amount of import candidates"
|
||||
}
|
||||
val HIGHEST_OLD_RELEVANCE = FeatureDeclaration.int("highest_old_relevance") {
|
||||
"The highest heuristic-based relevance com.jetbrains.python.codeInsight.completion.PyCompletionUtilsKt.computeCompletionWeight"
|
||||
}.nullable()
|
||||
}
|
||||
|
||||
override val featureDeclarations = extractFieldsAsFeatureDeclarations(Features)
|
||||
|
||||
override suspend fun computeFeatures(units: MLUnitsMap, usefulFeaturesFilter: FeatureFilter) = buildList<Feature> {
|
||||
val candidates = units[MLUnitImportCandidatesList]
|
||||
add(Features.LENGTH with candidates.size)
|
||||
add(Features.HIGHEST_OLD_RELEVANCE with candidates.maxOfOrNull { it.relevance })
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi.features
|
||||
|
||||
import com.jetbrains.ml.*
|
||||
import com.jetbrains.python.codeInsight.imports.mlapi.MLUnitImportCandidate
|
||||
import com.jetbrains.python.codeInsight.imports.mlapi.MLUnitImportCandidatesList
|
||||
|
||||
class ImportCandidateRelativeFeatures : FeatureProvider(MLUnitImportCandidate) {
|
||||
object Features {
|
||||
val RELATIVE_POSITION = FeatureDeclaration.int("original_position") {
|
||||
"The import's position without ML ranking"
|
||||
}
|
||||
}
|
||||
|
||||
override val unitSight = setOf(
|
||||
MLUnitImportCandidatesList
|
||||
)
|
||||
|
||||
override val featureDeclarations = extractFieldsAsFeatureDeclarations(Features)
|
||||
|
||||
override suspend fun computeFeatures(units: MLUnitsMap, usefulFeaturesFilter: FeatureFilter) = buildList<Feature> {
|
||||
val candidate = units[MLUnitImportCandidate]
|
||||
val allCandidates = units[MLUnitImportCandidatesList]
|
||||
|
||||
add(Features.RELATIVE_POSITION with allCandidates.indexOf(candidate))
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi.features
|
||||
|
||||
import com.jetbrains.ml.*
|
||||
import com.jetbrains.python.codeInsight.imports.mlapi.MLUnitImportCandidate
|
||||
|
||||
|
||||
class PrimitiveImportFeatures : FeatureProvider(MLUnitImportCandidate) {
|
||||
object Features {
|
||||
val RELEVANCE = FeatureDeclaration.int("old_relevance") {
|
||||
"""
|
||||
Heuristic-based relevance computed in com.jetbrains.python.codeInsight.completion.PyCompletionUtilsKt.computeCompletionWeight
|
||||
""".trimIndent()
|
||||
}
|
||||
val COMPONENT_COUNT = FeatureDeclaration.int("number_of_dots") {
|
||||
"The amount of components in the import statement"
|
||||
}.nullable()
|
||||
}
|
||||
|
||||
override val featureDeclarations = extractFieldsAsFeatureDeclarations(Features)
|
||||
|
||||
override suspend fun computeFeatures(units: MLUnitsMap, usefulFeaturesFilter: FeatureFilter) = buildList {
|
||||
|
||||
val importCandidate = units[MLUnitImportCandidate]
|
||||
|
||||
add(Features.RELEVANCE with importCandidate.relevance)
|
||||
add(Features.COMPONENT_COUNT with importCandidate.path?.componentCount)
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi.features
|
||||
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.jetbrains.ml.*
|
||||
import com.jetbrains.python.codeInsight.imports.ImportCandidateHolder
|
||||
import com.jetbrains.python.codeInsight.imports.mlapi.MLUnitImportCandidate
|
||||
|
||||
|
||||
class PsiStructureFeatures : FeatureProvider(MLUnitImportCandidate) {
|
||||
object Features {
|
||||
val PSI_CLASS = FeatureDeclaration.aClass("importable_class") { "PSI class of the imported element" }.nullable()
|
||||
val PSI_PARENT = (1..4).map { i -> FeatureDeclaration.aClass("psi_parent_$i") { "PSI parent #$i" }.nullable() }
|
||||
}
|
||||
|
||||
override val featureDeclarations = extractFieldsAsFeatureDeclarations(Features)
|
||||
|
||||
override suspend fun computeFeatures(units: MLUnitsMap, usefulFeaturesFilter: FeatureFilter) = buildList {
|
||||
val importCandidate: ImportCandidateHolder = units[MLUnitImportCandidate]
|
||||
|
||||
readAction {
|
||||
add(Features.PSI_CLASS with (importCandidate.importable?.javaClass))
|
||||
Features.PSI_PARENT.withIndex().forEach { (i, featureDeclaration) ->
|
||||
var parent: PsiElement? = importCandidate.importable
|
||||
repeat(i) {
|
||||
parent = parent?.parent
|
||||
}
|
||||
add(featureDeclaration with parent?.javaClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.jetbrains.ml.MLUnit
|
||||
import com.jetbrains.python.codeInsight.imports.ImportCandidateHolder
|
||||
|
||||
val MLUnitImportCandidate = MLUnit<ImportCandidateHolder>("import_candidate")
|
||||
|
||||
val MLUnitImportCandidatesList = MLUnit<List<ImportCandidateHolder>>("import_candidates_list")
|
||||
@@ -13,6 +13,7 @@ import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.jetbrains.python.PyPsiBundle;
|
||||
import kotlin.Unit;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
@@ -21,6 +22,9 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.codeInsight.imports.mlapi.MlImplementationKt.launchMLRanking;
|
||||
|
||||
|
||||
public final class PyImportChooser implements ImportChooser {
|
||||
|
||||
@Override
|
||||
@@ -28,20 +32,27 @@ public final class PyImportChooser implements ImportChooser {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return Promises.resolvedPromise(sources.get(0));
|
||||
}
|
||||
|
||||
AsyncPromise<ImportCandidateHolder> result = new AsyncPromise<>();
|
||||
|
||||
// GUI part
|
||||
DataManager.getInstance().getDataContextFromFocus().doWhenDone((Consumer<DataContext>)dataContext -> JBPopupFactory.getInstance()
|
||||
.createPopupChooserBuilder(sources)
|
||||
.setRenderer(new CellRenderer())
|
||||
.setTitle(useQualifiedImport ? PyPsiBundle.message("ACT.qualify.with.module") : PyPsiBundle.message("ACT.from.some.module.import"))
|
||||
.setItemChosenCallback(item -> {
|
||||
result.setResult(item);
|
||||
})
|
||||
.setNamerForFiltering(o -> o.getPresentableText())
|
||||
.createPopup()
|
||||
.showInBestPositionFor(dataContext));
|
||||
launchMLRanking(sources, (mlRanking) -> {
|
||||
// GUI part
|
||||
DataManager.getInstance().getDataContextFromFocus().doWhenDone((Consumer<DataContext>)dataContext -> JBPopupFactory.getInstance()
|
||||
.createPopupChooserBuilder(mlRanking.getOrder())
|
||||
.setRenderer(new CellRenderer())
|
||||
.setTitle(useQualifiedImport ? PyPsiBundle.message("ACT.qualify.with.module") : PyPsiBundle.message("ACT.from.some.module.import"))
|
||||
.setItemChosenCallback(item -> {
|
||||
result.setResult(item);
|
||||
mlRanking.submitSelectedItem(item);
|
||||
})
|
||||
.setCancelCallback(() -> {
|
||||
mlRanking.submitPopUpClosed();
|
||||
return true;
|
||||
})
|
||||
.setNamerForFiltering(o -> o.getPresentableText())
|
||||
.createPopup()
|
||||
.showInBestPositionFor(dataContext));
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.intellij.internal.statistic.eventLog.EventLogConfiguration
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.ml.impl.logs.MLEventLoggerProvider.Companion.ML_RECORDER_ID
|
||||
|
||||
@Service
|
||||
internal class FinalImportRankingStatusService {
|
||||
private val REGISTRY_KEY = "quickfix.ranking.ml"
|
||||
|
||||
enum class RegistryOption {
|
||||
IN_EXPERIMENT,
|
||||
ENABLED,
|
||||
DISABLED
|
||||
}
|
||||
|
||||
private val bucket: Int
|
||||
by lazy { service<EventLogConfiguration>().getOrCreate(ML_RECORDER_ID).bucket }
|
||||
|
||||
private val mlEnabledOnBucket: Boolean
|
||||
by lazy { bucket % 2 == 0 }
|
||||
|
||||
private fun getRegistryOption(): RegistryOption = RegistryOption.valueOf(requireNotNull(Registry.get(REGISTRY_KEY).selectedOption) { "Registry key $REGISTRY_KEY can't be empty" })
|
||||
|
||||
val status: FinalImportRankingStatus
|
||||
get() {
|
||||
return when (getRegistryOption()) {
|
||||
RegistryOption.IN_EXPERIMENT -> FinalImportRankingStatus(mlEnabledOnBucket, true)
|
||||
RegistryOption.ENABLED -> FinalImportRankingStatus(true, false)
|
||||
RegistryOption.DISABLED -> FinalImportRankingStatus(false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class FinalImportRankingStatus(
|
||||
val mlEnabled: Boolean,
|
||||
val mlStatusCorrespondsToBucket: Boolean,
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.jetbrains.ml.analysis.MLTaskFeedback
|
||||
import com.jetbrains.ml.analysis.MLTaskFeedbackField
|
||||
import com.jetbrains.ml.analysis.MLUnitFeedbackField
|
||||
import com.jetbrains.ml.logs.schema.*
|
||||
|
||||
// suspendable analysis -- written after the imports were ranked
|
||||
|
||||
internal val MLFieldCorrectElement = MLUnitFeedbackField(
|
||||
unit = MLUnitImportCandidate,
|
||||
field = BooleanEventField("is_correct") { "The candidate was chosen" },
|
||||
throwOnTimeout = false
|
||||
)
|
||||
|
||||
|
||||
internal object MLFeedbackCorrectElementPosition : MLTaskFeedback() {
|
||||
val CANCELLED = BooleanEventField("selection_cancelled") { "No item has been selected" }
|
||||
val SELECTED_POSITION = IntEventField("selected_position") { "The position of the selected import statement" }
|
||||
val INVALID_SELECTED_ITEM = BooleanEventField("invalid_selected_item") { "The selected item hasn't been found in the initial list" }
|
||||
|
||||
override val feedbackDeclaration = listOf(CANCELLED, SELECTED_POSITION, INVALID_SELECTED_ITEM)
|
||||
}
|
||||
|
||||
internal val ML_FEEDBACK_TIME_MS_TO_DISPLAY = MLTaskFeedbackField(
|
||||
field = LongEventField("time_ms_before_displayed") { "Duration from the quickfix start until when the imports were displayed" },
|
||||
)
|
||||
|
||||
internal val ML_FEEDBACK_TIME_MS_BEFORE_CLOSED = MLTaskFeedbackField(
|
||||
field = LongEventField("time_ms_before_closed") { "Duration from the quickfix start until the pop-up was closed" },
|
||||
)
|
||||
|
||||
// runtime analysis -- written during the imports are ranked
|
||||
|
||||
internal val ML_LOGGING_STATE: EnumEventField<LoggingOption> = EnumEventField.of<LoggingOption>("ml_logging_state", { "State of the ML session logging" })
|
||||
internal val ML_ENABLED = BooleanEventField("ml_enabled") { "Machine Learning ranking is enabled" }
|
||||
internal val ML_STATUS_CORRESPONDS_TO_BUCKET = BooleanEventField("ml_status_corresponds_to_bucket") { "Field 'ml_enabled' corresponds to the ML bucket % 2" }
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.intellij.codeInsight.inline.completion.logs.TypingSpeedTracker
|
||||
import com.intellij.codeInsight.inline.completion.ml.MLUnitTyping
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.util.application
|
||||
import com.jetbrains.ml.MockMLModel
|
||||
import com.jetbrains.ml.platform.MLApiTaskExecutor
|
||||
import com.jetbrains.ml.session.MLSession
|
||||
import com.jetbrains.python.codeInsight.imports.ImportCandidateHolder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
private class MLApiComputations(
|
||||
val coroutineScope: CoroutineScope,
|
||||
)
|
||||
|
||||
internal sealed class FinalCandidatesRanker(
|
||||
protected val mlSession: MLSession<MockMLModel, Unit>,
|
||||
protected val timestampStarted: Long,
|
||||
) {
|
||||
|
||||
abstract fun launchMLRanking(initialCandidatesOrder: MutableList<out ImportCandidateHolder>, displayResult: (RateableRankingResult) -> Unit)
|
||||
|
||||
abstract val mlEnabled: Boolean
|
||||
}
|
||||
|
||||
private class ExperimentalMLRanker(
|
||||
private val coroutineScope: CoroutineScope,
|
||||
mlSession: MLSession<MockMLModel, Unit>, timestampStarted: Long,
|
||||
) : FinalCandidatesRanker(mlSession, timestampStarted) {
|
||||
|
||||
override val mlEnabled = true
|
||||
|
||||
override fun launchMLRanking(initialCandidatesOrder: MutableList<out ImportCandidateHolder>, displayResult: (RateableRankingResult) -> Unit) {
|
||||
coroutineScope.launch(Dispatchers.Default) {
|
||||
val mlTreeRoot = mlSession.buildRoot(
|
||||
MLUnitImportCandidatesList with initialCandidatesOrder,
|
||||
MLUnitTyping with TypingSpeedTracker.getInstance(),
|
||||
).computingFeatures { computeAllFeaturesNow() }
|
||||
|
||||
for (candidate in initialCandidatesOrder) {
|
||||
mlTreeRoot
|
||||
.onlyLog(MLUnitImportCandidate with candidate)
|
||||
.computingFeatures { computeAllFeaturesNow() }
|
||||
}
|
||||
|
||||
mlSession.finish()
|
||||
|
||||
writeAction {
|
||||
displayResult(RateableRankingResult(mlSession, initialCandidatesOrder, timestampStarted))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InitialOrderKeepingRanker(
|
||||
mlSession: MLSession<MockMLModel, Unit>, timestampStarted: Long,
|
||||
) : FinalCandidatesRanker(mlSession, timestampStarted) {
|
||||
override val mlEnabled = false
|
||||
|
||||
override fun launchMLRanking(initialCandidatesOrder: MutableList<out ImportCandidateHolder>, displayResult: (RateableRankingResult) -> Unit) {
|
||||
mlSession.finish()
|
||||
|
||||
application.runWriteAction {
|
||||
displayResult(RateableRankingResult(mlSession, initialCandidatesOrder, timestampStarted))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun launchMLRanking(initialCandidatesOrder: MutableList<out ImportCandidateHolder>, displayResult: (RateableRankingResult) -> Unit) {
|
||||
// In EDT
|
||||
|
||||
val timestampStarted = System.currentTimeMillis()
|
||||
val mlCoroutineScope = service<MLApiComputations>().coroutineScope
|
||||
val mlSession = service<MLTaskPyCharmImportStatementsRanking>().task
|
||||
.startMLSession(taskExecutor = MLApiTaskExecutor.configure(mlCoroutineScope))
|
||||
val rankingStatus = service<FinalImportRankingStatusService>().status
|
||||
val ranker = if (rankingStatus.mlEnabled)
|
||||
ExperimentalMLRanker(mlCoroutineScope, mlSession, timestampStarted)
|
||||
else
|
||||
InitialOrderKeepingRanker(mlSession, timestampStarted)
|
||||
|
||||
mlSession.writeRuntimeSessionAnalysis(ML_LOGGING_STATE with getLoggingOption(mlSession))
|
||||
mlSession.writeRuntimeSessionAnalysis(ML_ENABLED with rankingStatus.mlEnabled)
|
||||
mlSession.writeRuntimeSessionAnalysis(ML_STATUS_CORRESPONDS_TO_BUCKET with rankingStatus.mlStatusCorrespondsToBucket)
|
||||
|
||||
ranker.launchMLRanking(initialCandidatesOrder) { result ->
|
||||
val timestampDisplayed = System.currentTimeMillis()
|
||||
displayResult(result)
|
||||
ML_FEEDBACK_TIME_MS_TO_DISPLAY.feedback(mlSession, timestampDisplayed - timestampStarted)
|
||||
}
|
||||
}
|
||||
|
||||
internal class RateableRankingResult(
|
||||
private val mlSession: MLSession<*, *>,
|
||||
val order: List<ImportCandidateHolder>,
|
||||
private val timestampStarted: Long,
|
||||
) {
|
||||
private var submitted = false
|
||||
|
||||
fun submitPopUpClosed() {
|
||||
val timestampClosed = System.currentTimeMillis()
|
||||
ML_FEEDBACK_TIME_MS_BEFORE_CLOSED.feedback(mlSession, timestampClosed - timestampStarted)
|
||||
service<MLApiComputations>().coroutineScope.launch {
|
||||
delay(100.milliseconds)
|
||||
synchronized(this@RateableRankingResult) {
|
||||
if (submitted) return@launch
|
||||
submitted = true
|
||||
MLFeedbackCorrectElementPosition.feedbackEventPairs(mlSession, MLFeedbackCorrectElementPosition.CANCELLED with true)
|
||||
MLFieldCorrectElement.cancelFeedback(mlSession)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun submitSelectedItem(selected: ImportCandidateHolder) = synchronized(this) {
|
||||
// In EDT
|
||||
require(!submitted) { "Some feedback to the ml ranker was already submitted" }
|
||||
submitted = true
|
||||
MLFieldCorrectElement.feedback(mlSession, selected, true)
|
||||
val selectedIndex = order.indexOf(selected)
|
||||
if (selectedIndex == -1)
|
||||
MLFeedbackCorrectElementPosition.feedbackEventPairs(mlSession, MLFeedbackCorrectElementPosition.INVALID_SELECTED_ITEM with true)
|
||||
else
|
||||
MLFeedbackCorrectElementPosition.feedbackEventPairs(mlSession, MLFeedbackCorrectElementPosition.SELECTED_POSITION with selectedIndex)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.ml.impl.logs.MLEventLoggerProvider.Companion.ML_RECORDER_ID
|
||||
import com.intellij.platform.ml.impl.tools.registerMLTaskLogging
|
||||
import com.intellij.util.application
|
||||
import com.jetbrains.ml.MLUnit
|
||||
import com.jetbrains.ml.logs.*
|
||||
import com.jetbrains.ml.logs.schema.BooleanEventField
|
||||
import com.jetbrains.ml.logs.schema.EventField
|
||||
import com.jetbrains.ml.logs.schema.EventPair
|
||||
import com.jetbrains.ml.model.MLModel
|
||||
import com.jetbrains.ml.session.MLSession
|
||||
import com.jetbrains.ml.session.MLSessionInfo
|
||||
import com.jetbrains.ml.tree.LevelFeaturesSchema
|
||||
import com.jetbrains.ml.tree.MLTree
|
||||
|
||||
class PyCharmImportsRankingLogs : CounterUsagesCollector() {
|
||||
private val GROUP = EventLogGroup("pycharm.quickfix.imports", 1, ML_RECORDER_ID).also {
|
||||
it.registerMLTaskLogging(service<MLTaskPyCharmImportStatementsRanking>().task,
|
||||
parentDisposable = Disposer.newDisposable())
|
||||
}
|
||||
|
||||
override fun getGroup() = GROUP
|
||||
}
|
||||
|
||||
internal enum class LoggingOption {
|
||||
FULL,
|
||||
NO_TREE,
|
||||
SKIP
|
||||
}
|
||||
|
||||
internal const val RELEASE_LOGGING_PERCENT = 5
|
||||
|
||||
internal fun getLoggingOption(mlSession: MLSession<*, *>): LoggingOption {
|
||||
if (application.isEAP || application.isUnitTestMode) {
|
||||
return LoggingOption.FULL
|
||||
}
|
||||
if (mlSession.id % 100 > RELEASE_LOGGING_PERCENT) return LoggingOption.SKIP
|
||||
return LoggingOption.NO_TREE
|
||||
}
|
||||
|
||||
object PyCharmImportsRankingLogger : MLSessionLoggerProvider<Any> {
|
||||
private val baseLoggerProvider = EntireSessionLoggerProvider<Any, Boolean>(BooleanEventField("prediction") { "ML model prediction" }) { null }
|
||||
|
||||
override fun createMLSessionLogger(
|
||||
eventPrefix: String,
|
||||
taskId: String,
|
||||
treeAnalysisDeclaration: Map<MLUnit<*>, List<EventField<*>>>,
|
||||
sessionAnalysisDeclaration: List<EventField<*>>,
|
||||
featuresDeclaration: List<LevelFeaturesSchema>,
|
||||
fusEventRegister: FusEventRegister,
|
||||
): MLSessionLogger<Any> {
|
||||
val baseSessionLogger = baseLoggerProvider.createMLSessionLogger(eventPrefix, taskId, treeAnalysisDeclaration, sessionAnalysisDeclaration, featuresDeclaration, fusEventRegister)
|
||||
return object : MLSessionLogger<Any> {
|
||||
override suspend fun logMLSession(sessionInfo: MLSessionInfo<out MLModel<Any>, Any>, analysisException: Throwable?, sessionAnalysis: List<EventPair<*>>, structure: MLTree.ATopNode<out MLModel<Any>, Any>?): MLSessionLoggingOutcome {
|
||||
val loggingOption = checkNotNull(sessionAnalysis.find { it.field == ML_LOGGING_STATE }).data as LoggingOption
|
||||
return when (loggingOption) {
|
||||
LoggingOption.FULL -> baseSessionLogger.logMLSession(sessionInfo, analysisException, sessionAnalysis, structure)
|
||||
LoggingOption.NO_TREE -> {
|
||||
baseSessionLogger.logMLSession(sessionInfo, analysisException, sessionAnalysis, null)
|
||||
MLSessionLoggingOutcome.Custom("no tree logged")
|
||||
}
|
||||
LoggingOption.SKIP -> MLSessionLoggingOutcome.FilteredOut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.codeInsight.imports.mlapi
|
||||
|
||||
import com.intellij.codeInsight.inline.completion.ml.MLUnitTyping
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.platform.ml.impl.tools.IJPlatform
|
||||
import com.jetbrains.ml.MLTaskBuilder
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
|
||||
internal val LONGEST_POPUP_LIFE_DURATION = 365.days
|
||||
|
||||
@Service
|
||||
class MLTaskPyCharmImportStatementsRanking {
|
||||
val task by lazy {
|
||||
MLTaskBuilder(
|
||||
taskId = "pycharm_import_statements_ranking",
|
||||
taskLevels = listOf(
|
||||
setOf(MLUnitImportCandidatesList, MLUnitTyping),
|
||||
setOf(MLUnitImportCandidate)
|
||||
),
|
||||
platform = service<IJPlatform>()
|
||||
).buildWithoutMLModel {
|
||||
|
||||
suspendableTreeAnalysis(MLFieldCorrectElement)
|
||||
suspendableSessionAnalysis(MLFeedbackCorrectElementPosition)
|
||||
suspendableSessionAnalysis(ML_FEEDBACK_TIME_MS_TO_DISPLAY)
|
||||
suspendableSessionAnalysis(ML_FEEDBACK_TIME_MS_BEFORE_CLOSED)
|
||||
runtimeSessionAnalysis(ML_LOGGING_STATE, ML_ENABLED, ML_STATUS_CORRESPONDS_TO_BUCKET)
|
||||
|
||||
logger = PyCharmImportsRankingLogger
|
||||
|
||||
logFilter
|
||||
maxAnalysisDuration = LONGEST_POPUP_LIFE_DURATION
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user