Skip to content

Commit 11b22ed

Browse files
committed
feat(chat): add model selector to replace static model label
Replace the static model label with an interactive dropdown that allows users to select between GitHub Copilot and custom LLM models. Add supporting methods in LLMModelManager for model discovery and provider name conversion. - Add getAllAvailableModels() to fetch GitHub Copilot and custom models - Add model ID/provider name conversion utilities - Replace JBLabel with JComboBox for dynamic model selection - Fix EDT threading issue in DiffRepair editor updates
1 parent fd8437f commit 11b22ed

File tree

3 files changed

+118
-10
lines changed

3 files changed

+118
-10
lines changed

core/src/main/kotlin/cc/unitmesh/devti/gui/chat/ui/AutoDevInputSection.kt

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import cc.unitmesh.devti.llms.tokenizer.TokenizerFactory
1818
import cc.unitmesh.devti.provider.RelatedClassesProvider
1919
import cc.unitmesh.devti.settings.AutoDevSettingsState
2020
import cc.unitmesh.devti.settings.customize.customizeSetting
21+
import cc.unitmesh.devti.settings.model.LLMModelManager
22+
import cc.unitmesh.devti.settings.ui.ModelItem
2123
import cc.unitmesh.devti.util.AutoDevCoroutineScope
2224
import cc.unitmesh.devti.util.parser.CodeFence
2325
import com.intellij.codeInsight.lookup.LookupManagerListener
@@ -67,15 +69,18 @@ import javax.swing.*
6769
import kotlin.math.max
6870
import kotlin.math.min
6971

70-
class AutoDevInputSection(private val project: Project, val disposable: Disposable?, showAgent: Boolean = true) :
71-
BorderLayoutPanel() {
72+
class AutoDevInputSection(
73+
private val project: Project,
74+
val disposable: Disposable?,
75+
showAgent: Boolean = true
76+
) : BorderLayoutPanel() {
7277
private val input: AutoDevInput
7378
private val documentListener: DocumentListener
7479
private val sendButtonPresentation: Presentation
7580
private val stopButtonPresentation: Presentation
7681
private val enhanceButtonPresentation: Presentation
7782
private val sendButton: ActionButton
78-
private val modelLabel: JBLabel
83+
private val modelSelector: JComboBox<ModelItem>
7984
private val stopButton: ActionButton
8085
private val enhanceButton: ActionButton
8186
private var buttonPanel: JPanel = JPanel(CardLayout())
@@ -121,9 +126,25 @@ class AutoDevInputSection(private val project: Project, val disposable: Disposab
121126
input = AutoDevInput(project, listOf(), disposable, this)
122127
workspaceFilePanel = WorkspaceFilePanel(project)
123128

124-
// Create a label to display the current model ID
125-
val modelId = AutoDevSettingsState.getInstance().defaultModelId.ifEmpty { "Default" }
126-
modelLabel = JBLabel("Model: $modelId").apply { foreground = JBUI.CurrentTheme.Label.disabledForeground() }
129+
// Create model selector
130+
val modelItems = LLMModelManager.getInstance().getAllAvailableModels()
131+
modelSelector = JComboBox(modelItems.toTypedArray())
132+
133+
val currentModel = AutoDevSettingsState.getInstance().defaultModelId.ifEmpty { "Default" }
134+
val currentModelId = LLMModelManager.getInstance().getModelIdFromProvider(currentModel)
135+
for (i in 0 until modelSelector.itemCount) {
136+
val item = modelSelector.getItemAt(i)
137+
if (item.modelId == currentModelId) {
138+
modelSelector.selectedIndex = i
139+
break
140+
}
141+
}
142+
143+
modelSelector.addActionListener {
144+
val selected = modelSelector.selectedItem as? ModelItem ?: return@addActionListener
145+
val newProvider = LLMModelManager.getInstance().getProviderFromModelId(selected.modelId)
146+
AutoDevSettingsState.getInstance().defaultModelId = newProvider
147+
}
127148

128149
setupElementsList()
129150
val sendButtonPresentation = Presentation(AutoDevBundle.message("chat.panel.send"))
@@ -202,13 +223,17 @@ class AutoDevInputSection(private val project: Project, val disposable: Disposab
202223

203224
input.minimumSize = Dimension(input.minimumSize.width, 64)
204225
layoutPanel.addToLeft(customAgent)
205-
// Add model label next to the custom agent dropdown
226+
// Add model selector next to the custom agent dropdown
206227
layoutPanel.addToLeft(Box.createHorizontalStrut(JBUI.scale(8)))
207-
layoutPanel.addToLeft(modelLabel)
228+
layoutPanel.addToLeft(modelSelector)
208229
} else {
209-
layoutPanel.addToLeft(modelLabel)
230+
layoutPanel.addToLeft(modelSelector)
210231
}
211232

233+
// Set style for model selector
234+
modelSelector.border = JBUI.Borders.empty(0, 4)
235+
modelSelector.preferredSize = Dimension(200, modelSelector.preferredSize.height)
236+
212237
buttonPanel = createButtonPanel()
213238

214239
layoutPanel.addToCenter(horizontalGlue)

core/src/main/kotlin/cc/unitmesh/devti/settings/model/LLMModelManager.kt

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import cc.unitmesh.devti.settings.ui.ModelItem
1010
import cc.unitmesh.devti.util.AutoDevAppScope
1111
import com.intellij.openapi.components.service
1212
import com.intellij.openapi.project.Project
13+
import com.intellij.openapi.project.ProjectManager
1314
import com.intellij.openapi.ui.ComboBox
1415
import com.intellij.openapi.ui.Messages
1516
import kotlinx.coroutines.CoroutineName
@@ -43,6 +44,77 @@ class LLMModelManager(
4344
return userModels.any { it.name == modelName }
4445
}
4546

47+
/**
48+
* Get all available models (GitHub Copilot + Custom LLMs)
49+
* Used by AutoDevInputSection to populate model selector
50+
*/
51+
fun getAllAvailableModels(): List<ModelItem> {
52+
val models = mutableListOf<ModelItem>()
53+
val manager = service<GithubCopilotManager>()
54+
if (manager.isInitialized()) {
55+
val githubModels = manager.getSupportedModels(forceRefresh = false)
56+
githubModels?.forEach { model ->
57+
models.add(ModelItem(
58+
displayName = "Github: ${model.id}",
59+
id = model.id,
60+
isCustom = false
61+
))
62+
}
63+
}
64+
65+
val userModels = LlmConfig.load()
66+
userModels.forEach { llm ->
67+
models.add(ModelItem(
68+
displayName = llm.name,
69+
id = llm.name,
70+
isCustom = true
71+
))
72+
}
73+
74+
return models
75+
}
76+
77+
/**
78+
* Get model ID from provider name
79+
* Used by AutoDevInputSection to find current selected model
80+
*/
81+
fun getModelIdFromProvider(providerName: String): String {
82+
if (providerName.isEmpty() || providerName == "Default") {
83+
return "Default"
84+
}
85+
86+
// For GitHub models, extract model ID from "Github: model-id" format
87+
if (providerName.startsWith("Github: ")) {
88+
return providerName.removePrefix("Github: ")
89+
}
90+
91+
// For custom models, the provider name is the model name
92+
return providerName
93+
}
94+
95+
/**
96+
* Get provider name from model ID
97+
* Used by AutoDevInputSection when model selection changes
98+
*/
99+
fun getProviderFromModelId(modelId: String): String {
100+
if (modelId.isEmpty() || modelId == "Default") {
101+
return "Default"
102+
}
103+
104+
// Check if it's a GitHub Copilot model
105+
val manager = service<GithubCopilotManager>()
106+
if (manager.isInitialized()) {
107+
val githubModels = manager.getSupportedModels(forceRefresh = false)
108+
val isGithubModel = githubModels?.any { it.id == modelId } == true
109+
if (isGithubModel) {
110+
return "Github: $modelId"
111+
}
112+
}
113+
114+
// For custom models, return the model name directly
115+
return modelId
116+
}
117+
46118
/**
47119
* Create a new LLM configuration dialog
48120
*/
@@ -378,4 +450,12 @@ class LLMModelManager(
378450
}
379451
}
380452
}
453+
454+
companion object {
455+
fun getInstance(): LLMModelManager {
456+
val project = ProjectManager.getInstance().openProjects.firstOrNull()
457+
val settings = AutoDevSettingsState.getInstance()
458+
return LLMModelManager(project, settings, {})
459+
}
460+
}
381461
}

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/patch/DiffRepair.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cc.unitmesh.devti.template.GENIUS_CODE
77
import cc.unitmesh.devti.template.TemplateRender
88
import cc.unitmesh.devti.util.AutoDevCoroutineScope
99
import cc.unitmesh.devti.util.parser.CodeFence
10+
import com.intellij.openapi.application.runInEdt
1011
import com.intellij.openapi.application.runWriteAction
1112
import com.intellij.openapi.editor.Editor
1213
import com.intellij.openapi.project.Project
@@ -31,7 +32,9 @@ object DiffRepair {
3132
processStreamRealtime(project, flow) { code ->
3233
callback?.invoke(code)
3334
runWriteAction {
34-
editor.document.setText(code)
35+
runInEdt {
36+
editor.document.setText(code)
37+
}
3538
}
3639
}
3740
}

0 commit comments

Comments
 (0)