Skip to content

Commit 324946d

Browse files
committed
feat(settings): panel for OpenAI client
1 parent 3916667 commit 324946d

File tree

8 files changed

+183
-183
lines changed

8 files changed

+183
-183
lines changed

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/AppSettings2.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
8888
private fun migrateOpenAiClientFromVersion1(openAiLlmClient: LLMClient?, appSettings: AppSettings) {
8989
openAiLlmClient?.apply {
9090
host = appSettings.openAIHost
91-
hosts = appSettings.openAIHosts
9291
appSettings.openAISocketTimeout.toIntOrNull()?.let { timeout = it }
9392
proxyUrl = appSettings.proxyUrl
9493
modelId = appSettings.openAIModelId
95-
modelIds = appSettings.openAIModelIds
9694
temperature = appSettings.openAITemperature
9795
AICommitsUtils.retrieveToken(appSettings.openAITokenTitle)?.let { token = it }
9896
}
97+
OpenAIClient.hosts.addAll(appSettings.openAIHosts)
98+
OpenAIClient.modelIds.addAll(appSettings.openAIModelIds)
9999
}
100100

101101
fun recordHit() {
Lines changed: 3 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,29 @@
11
package com.github.blarc.ai.commits.intellij.plugin.settings
22

3-
import com.aallam.openai.api.exception.OpenAIAPIException
4-
import com.github.blarc.ai.commits.intellij.plugin.*
3+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
54
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
65
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientTable
76
import com.github.blarc.ai.commits.intellij.plugin.settings.prompts.Prompt
87
import com.github.blarc.ai.commits.intellij.plugin.settings.prompts.PromptTable
9-
import com.intellij.icons.AllIcons
108
import com.intellij.openapi.options.BoundConfigurable
11-
import com.intellij.openapi.progress.runBackgroundableTask
129
import com.intellij.openapi.ui.ComboBox
13-
import com.intellij.openapi.ui.naturalSorted
1410
import com.intellij.ui.CommonActionsPanel
1511
import com.intellij.ui.ToolbarDecorator
1612
import com.intellij.ui.components.JBLabel
17-
import com.intellij.ui.components.JBPasswordField
18-
import com.intellij.ui.components.JBTextField
1913
import com.intellij.ui.dsl.builder.*
20-
import com.intellij.ui.util.minimumWidth
21-
import kotlinx.coroutines.*
2214
import java.util.*
23-
import javax.swing.DefaultComboBoxModel
2415

2516
class AppSettingsConfigurable : BoundConfigurable(message("settings.general.group.title")) {
2617

27-
private val hostComboBox = ComboBox<String>()
28-
private val tokenPasswordField = JBPasswordField()
29-
private val verifyLabel = JBLabel()
30-
private val proxyTextField = JBTextField()
31-
private val socketTimeoutTextField = JBTextField()
32-
private var modelComboBox = ComboBox<String>()
3318
private val llmClientTable = LLMClientTable()
3419
private val promptTable = PromptTable()
3520
private lateinit var llmClientToolbarDecorator: ToolbarDecorator
3621
private lateinit var toolbarDecorator: ToolbarDecorator
3722
private lateinit var promptComboBox: Cell<ComboBox<Prompt>>
3823

39-
init {
40-
hostComboBox.isEditable = true
41-
hostComboBox.model = DefaultComboBoxModel(Vector(AppSettings2.instance.getActiveLLMClient().hosts.naturalSorted()))
42-
modelComboBox.model = DefaultComboBoxModel(Vector(AppSettings2.instance.getActiveLLMClient().modelIds.naturalSorted()))
43-
modelComboBox.renderer = AppSettingsListCellRenderer()
44-
}
45-
4624
override fun createPanel() = panel {
4725

4826
group(JBLabel("OpenAI")) {
49-
row {
50-
label(message("settings.openAIHost"))
51-
.widthGroup("label")
52-
53-
cell(hostComboBox)
54-
.bindItem(AppSettings2.instance.getActiveLLMClient()::host.toNullableProperty())
55-
.widthGroup("input")
56-
}
57-
row {
58-
label(message("settings.openAIProxy")).widthGroup("label")
59-
cell(proxyTextField)
60-
.bindText(AppSettings2.instance.getActiveLLMClient()::proxyUrl.toNonNullableProperty(""))
61-
.applyToComponent { minimumWidth = 400 }
62-
.resizableColumn()
63-
.widthGroup("input")
64-
}
65-
row {
66-
comment(message("settings.openAIProxyComment"))
67-
}
68-
row {
69-
label(message("settings.openAISocketTimeout")).widthGroup("label")
70-
cell(socketTimeoutTextField)
71-
.bindIntText(AppSettings2.instance.getActiveLLMClient()::timeout)
72-
.applyToComponent { minimumWidth = 400 }
73-
.resizableColumn()
74-
.widthGroup("input")
75-
.validationOnInput { isInt(it.text) }
76-
}
77-
row {
78-
label(message("settings.openAIToken"))
79-
.widthGroup("label")
80-
cell(tokenPasswordField)
81-
.bindText(
82-
{ AppSettings2.instance.getActiveLLMClient().token.orEmpty() },
83-
{ AppSettings2.instance.getActiveLLMClient().token = it }
84-
)
85-
.emptyText(message("settings.openAITokenExample"))
86-
.align(Align.FILL)
87-
.resizableColumn()
88-
.focused()
89-
button(message("settings.verifyToken")) { verifyToken() }
90-
.align(AlignX.RIGHT)
91-
.widthGroup("button")
92-
}
93-
row {
94-
comment(message("settings.openAITokenComment"))
95-
.align(AlignX.LEFT)
96-
cell(verifyLabel)
97-
.align(AlignX.RIGHT)
98-
}
99-
row {
100-
label(message("settings.openAIModel")).widthGroup("label")
101-
102-
cell(modelComboBox)
103-
.bindItem({ AppSettings2.instance.getActiveLLMClient().modelId }, {
104-
if (it != null) {
105-
AppSettings2.instance.getActiveLLMClient().modelId = it
106-
}
107-
})
108-
.resizableColumn()
109-
.align(Align.FILL)
110-
button(message("settings.refreshModels")) {
111-
runBackgroundableTask(message("settings.loadingModels")) {
112-
runBlocking(Dispatchers.IO) {
113-
OpenAIService.instance.refreshOpenAIModelIds()
114-
modelComboBox.model = DefaultComboBoxModel(Vector(AppSettings2.instance.getActiveLLMClient().modelIds.naturalSorted()))
115-
modelComboBox.item = AppSettings2.instance.getActiveLLMClient().modelId
116-
}
117-
}
118-
}
119-
.align(AlignX.RIGHT)
120-
.widthGroup("button")
121-
}
122-
123-
row {
124-
label(message("settings.openAITemperature"))
125-
.widthGroup("label")
126-
127-
textField()
128-
.bindText(AppSettings2.instance.getActiveLLMClient()::temperature)
129-
.applyToComponent { minimumWidth = 400 }
130-
.resizableColumn()
131-
.widthGroup("input")
132-
.validationOnInput { temperatureValid(it.text) }
133-
134-
contextHelp(message("settings.openAITemperatureComment"))
135-
}
136-
137-
row {
138-
cell(verifyLabel)
139-
.align(AlignX.RIGHT)
140-
}
141-
14227
row {
14328
llmClientToolbarDecorator = ToolbarDecorator.createDecorator(llmClientTable.table)
14429
.setAddAction {
@@ -151,8 +36,6 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
15136
}
15237
}
15338

154-
155-
15639
group(JBLabel("Prompt")) {
15740
row {
15841
label(message("settings.locale")).widthGroup("labelPrompt")
@@ -224,7 +107,8 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
224107
}
225108

226109
override fun apply() {
227-
AppSettings2.instance.getActiveLLMClient().hosts.add(hostComboBox.item)
110+
// TODO @Blarc
111+
// AppSettings2.instance.getActiveLLMClient().hosts.add(hostComboBox.item)
228112
promptTable.apply()
229113
super.apply()
230114
}
@@ -234,34 +118,4 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
234118
super.reset()
235119
}
236120

237-
@OptIn(DelicateCoroutinesApi::class)
238-
private fun verifyToken() {
239-
runBackgroundableTask(message("settings.verify.running")) {
240-
if (tokenPasswordField.password.isEmpty()) {
241-
verifyLabel.icon = AllIcons.General.InspectionsError
242-
verifyLabel.text = message("settings.verify.token-is-empty")
243-
} else {
244-
verifyLabel.icon = AllIcons.General.InlineRefreshHover
245-
verifyLabel.text = message("settings.verify.running")
246-
247-
GlobalScope.launch(Dispatchers.IO) {
248-
try {
249-
OpenAIService.instance.verifyOpenAIConfiguration(hostComboBox.item, String(tokenPasswordField.password), proxyTextField.text, socketTimeoutTextField.text)
250-
verifyLabel.text = message("settings.verify.valid")
251-
verifyLabel.icon = AllIcons.General.InspectionsOK
252-
} catch (e: OpenAIAPIException) {
253-
verifyLabel.text = message("settings.verify.invalid", e.statusCode)
254-
verifyLabel.icon = AllIcons.General.InspectionsError
255-
} catch (e: NumberFormatException) {
256-
verifyLabel.text = message("settings.verify.invalid", e.localizedMessage)
257-
verifyLabel.icon = AllIcons.General.InspectionsError
258-
} catch (e: Exception) {
259-
verifyLabel.text = message("settings.verify.invalid", "Unknown")
260-
verifyLabel.icon = AllIcons.General.InspectionsError
261-
}
262-
}
263-
}
264-
}
265-
266-
}
267121
}

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClient.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@ import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotificatio
77
import com.intellij.ide.passwordSafe.PasswordSafe
88
import com.intellij.util.xmlb.annotations.Attribute
99
import com.intellij.util.xmlb.annotations.Transient
10-
import com.intellij.util.xmlb.annotations.XCollection
1110

1211
abstract class LLMClient(
1312
@Attribute var displayName: String,
1413
@Attribute var host: String,
15-
@XCollection(style = XCollection.Style.v2, elementName = "host")
16-
var hosts: MutableSet<String>,
1714
@Attribute var proxyUrl: String?,
1815
@Attribute var timeout: Int,
1916
@Attribute var modelId: String,
20-
@XCollection(style = XCollection.Style.v2, elementName = "modelId")
21-
var modelIds: List<String>,
2217
@Attribute var temperature: String
2318
) {
2419

@@ -27,6 +22,10 @@ abstract class LLMClient(
2722
get() = retrieveToken(displayName) ?: ""
2823
set(token) = saveToken(token)
2924

25+
abstract fun getHosts(): Set<String>
26+
27+
abstract fun getModelIds(): Set<String>
28+
3029
abstract suspend fun generateCommitMessage(prompt: String): String
3130

3231
abstract suspend fun refreshModels()

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientTable.kt

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ package com.github.blarc.ai.commits.intellij.plugin.settings.clients
33
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
44
import com.github.blarc.ai.commits.intellij.plugin.createColumn
55
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2
6-
import com.intellij.openapi.ui.ComboBox
6+
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettingsListCellRenderer
77
import com.intellij.openapi.ui.DialogWrapper
88
import com.intellij.ui.JBCardLayout
99
import com.intellij.ui.components.Panel
1010
import com.intellij.ui.dsl.builder.Align
1111
import com.intellij.ui.dsl.builder.panel
1212
import com.intellij.ui.table.TableView
1313
import com.intellij.util.ui.ListTableModel
14-
import javax.swing.DefaultComboBoxModel
1514
import javax.swing.ListSelectionModel.SINGLE_SELECTION
1615

1716
class LLMClientTable {
@@ -43,7 +42,6 @@ class LLMClientTable {
4342
private val cardLayout = JBCardLayout()
4443
private val cardPanel = Panel(null, cardLayout)
4544
private val llmClients: List<LLMClient> = getLlmClients(newLlmClient)
46-
private val llmClientsComboBox: ComboBox<LLMClient> = createLlmClientsComboBox(newLlmClient)
4745

4846
init {
4947
title = newLlmClient?.let { "Edit LLM Client" } ?: "Add LLM Client"
@@ -55,12 +53,15 @@ class LLMClientTable {
5553
}
5654

5755
override fun createCenterPanel() = panel {
58-
row("Type") {
59-
cell(llmClientsComboBox)
56+
row("Client") {
57+
comboBox(llmClients, AppSettingsListCellRenderer())
6058
.align(Align.FILL)
6159
.applyToComponent {
6260
addItemListener { cardLayout.show(cardPanel, (it.item as LLMClient).displayName) }
6361
}
62+
.applyToComponent {
63+
isEnabled = newLlmClient == null
64+
}
6465
}
6566
row {
6667
cell(cardPanel)
@@ -78,15 +79,6 @@ class LLMClientTable {
7879
listOf(newLLMClient)
7980
}
8081
}
81-
82-
private fun createLlmClientsComboBox(newLLMClient: LLMClient?) : ComboBox<LLMClient> {
83-
val comboBoxModel = DefaultComboBoxModel(getLlmClients(newLLMClient).toTypedArray())
84-
with(ComboBox(comboBoxModel)){
85-
// Type of LLM Client can not be changed when editing a LLM Client
86-
if (newLLMClient != null) isEnabled = false
87-
return this
88-
}
89-
}
9082
}
9183

9284
}

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/OpenAIClient.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,34 @@ import com.aallam.openai.client.OpenAI
1010
import com.aallam.openai.client.OpenAIConfig
1111
import com.aallam.openai.client.OpenAIHost
1212
import com.aallam.openai.client.ProxyConfig
13+
import com.intellij.util.xmlb.annotations.Attribute
1314
import kotlin.time.Duration.Companion.seconds
1415

1516
class OpenAIClient(displayName: String = "OpenAI") : LLMClient(
1617
displayName,
1718
OpenAIHost.OpenAI.baseUrl,
18-
mutableSetOf(OpenAIHost.OpenAI.baseUrl),
1919
null,
2020
30,
2121
"gpt-3.5-turbo",
22-
listOf("gpt-3.5-turbo", "gpt-4"),
2322
"0.7"
2423
) {
24+
25+
companion object {
26+
// TODO @Blarc: Static fields probably can't be attributes...
27+
@Attribute
28+
val hosts = mutableSetOf(OpenAIHost.OpenAI.baseUrl)
29+
@Attribute
30+
val modelIds = mutableSetOf("gpt-3.5-turbo", "gpt-4")
31+
}
32+
33+
override fun getHosts(): Set<String> {
34+
return hosts
35+
}
36+
37+
override fun getModelIds(): Set<String> {
38+
return modelIds
39+
}
40+
2541
override suspend fun generateCommitMessage(
2642
prompt: String
2743
): String {
@@ -49,7 +65,9 @@ class OpenAIClient(displayName: String = "OpenAI") : LLMClient(
4965

5066
override suspend fun refreshModels() {
5167
val openAI = OpenAI(openAIConfig())
52-
modelIds = openAI.models().map { it.id.id }
68+
openAI.models()
69+
.map { it.id.id }
70+
.forEach { modelIds.add(it) }
5371
}
5472

5573
@Throws(Exception::class)

0 commit comments

Comments
 (0)