Skip to content

Commit f58cb3d

Browse files
committed
feat(clients): add unique id to clients and enable editing of the name
1 parent 2bc989e commit f58cb3d

File tree

14 files changed

+115
-69
lines changed

14 files changed

+115
-69
lines changed

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
44
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.commonBranch
55
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.computeDiff
66
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.constructPrompt
7-
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.isPromptTooLarge
87
import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification
98
import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification
109
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2
@@ -15,11 +14,14 @@ import com.intellij.openapi.project.DumbAware
1514
import com.intellij.openapi.vcs.VcsDataKeys
1615
import com.intellij.openapi.vcs.ui.CommitMessage
1716
import com.intellij.vcs.commit.AbstractCommitWorkflowHandler
18-
import kotlinx.coroutines.Dispatchers
19-
import kotlinx.coroutines.runBlocking
2017

2118
class AICommitAction : AnAction(), DumbAware {
2219
override fun actionPerformed(e: AnActionEvent) {
20+
val llmClient = AppSettings2.instance.getActiveLLMClient()
21+
if (llmClient == null) {
22+
Notification.clientNotSet()
23+
return
24+
}
2325
val project = e.project ?: return
2426

2527
val commitWorkflowHandler = e.getData(VcsDataKeys.COMMIT_WORKFLOW_HANDLER) as AbstractCommitWorkflowHandler<*, *>?
@@ -42,26 +44,19 @@ class AICommitAction : AnAction(), DumbAware {
4244
val branch = commonBranch(includedChanges, project)
4345
val hint = commitMessage?.text
4446
val prompt = constructPrompt(AppSettings2.instance.activePrompt.content, diff, branch, hint)
45-
if (isPromptTooLarge(prompt)) {
46-
sendNotification(Notification.promptTooLarge())
47-
return@runBackgroundableTask
48-
}
47+
48+
// TODO @Blarc: add support for different clients
49+
// if (isPromptTooLarge(prompt)) {
50+
// sendNotification(Notification.promptTooLarge())
51+
// return@runBackgroundableTask
52+
// }
4953

5054
if (commitMessage == null) {
5155
sendNotification(Notification.noCommitMessage())
5256
return@runBackgroundableTask
5357
}
5458

55-
val llmClient = AppSettings2.instance.getActiveLLMClient()
56-
runBlocking(Dispatchers.Main) {
57-
try {
58-
llmClient.generateCommitMessage(prompt, commitMessage)
59-
} catch (e: Exception) {
60-
// TODO @Blarc: This will never happen, commit message generating is called in a suspended function
61-
commitMessage.setCommitMessage(e.message ?: message("action.error"))
62-
sendNotification(Notification.unsuccessfulRequest(e.message ?: message("action.unknown-error")))
63-
}
64-
}
59+
llmClient.generateCommitMessage(prompt, commitMessage)
6560
}
6661
}
6762
}

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

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder
1212
import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter
1313
import com.intellij.openapi.project.Project
1414
import com.intellij.openapi.vcs.changes.Change
15-
import com.knuddels.jtokkit.Encodings
16-
import com.knuddels.jtokkit.api.ModelType
1715
import git4idea.repo.GitRepositoryManager
1816
import kotlinx.coroutines.Dispatchers
1917
import kotlinx.coroutines.withContext
@@ -124,21 +122,22 @@ object AICommitsUtils {
124122
.joinToString("\n")
125123
}
126124

127-
fun isPromptTooLarge(prompt: String): Boolean {
128-
val registry = Encodings.newDefaultEncodingRegistry()
129-
130-
/*
131-
* Try to find the model type based on the model id by finding the longest matching model type
132-
* If no model type matches, let the request go through and let the OpenAI API handle it
133-
*/
134-
val modelType = ModelType.entries
135-
.filter { AppSettings2.instance.getActiveLLMClient().modelId.contains(it.name) }
136-
.maxByOrNull { it.name.length }
137-
?: return false
138-
139-
val encoding = registry.getEncoding(modelType.encodingType)
140-
return encoding.countTokens(prompt) > modelType.maxContextLength
141-
}
125+
// TODO @Blarc: This only works for OpenAI
126+
// fun isPromptTooLarge(prompt: String): Boolean {
127+
// val registry = Encodings.newDefaultEncodingRegistry()
128+
//
129+
// /*
130+
// * Try to find the model type based on the model id by finding the longest matching model type
131+
// * If no model type matches, let the request go through and let the OpenAI API handle it
132+
// */
133+
// val modelType = ModelType.entries
134+
// .filter { AppSettings2.instance.getActiveLLMClient().modelId.contains(it.name) }
135+
// .maxByOrNull { it.name.length }
136+
// ?: return false
137+
//
138+
// val encoding = registry.getEncoding(modelType.encodingType)
139+
// return encoding.countTokens(prompt) > modelType.maxContextLength
140+
// }
142141

143142
// TODO @Blarc: Slow operations are prohibited on EDT
144143
fun saveToken(title: String, token: String) {

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/notifications/Notification.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ data class Notification(
4545
fun noCommitMessage() = Notification(message = message("notifications.no-commit-message"))
4646
fun unableToSaveToken(message: String?) = Notification(message = message("notifications.unable-to-save-token", message ?: "Unknown error"))
4747
fun noCommonBranch() = Notification(message = message("notifications.no-common-branch"))
48+
fun clientNotSet() = Notification(message = message("notifications.client-not-set"), type = Type.TRANSIENT)
4849

4950
}
5051
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ class AICommitsListCellRenderer : DefaultListCellRenderer() {
2525
text = value.name
2626
}
2727

28+
// This is used for combo box in settings dialog
2829
is LLMClientConfiguration -> {
29-
icon = value.getIcon()
30-
text = value.displayName
30+
text = value.name
31+
icon = value.getClientIcon()
3132
}
3233
}
3334
return component

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import com.intellij.openapi.components.State
1818
import com.intellij.openapi.components.Storage
1919
import com.intellij.util.xmlb.Converter
2020
import com.intellij.util.xmlb.XmlSerializerUtil
21+
import com.intellij.util.xmlb.annotations.Attribute
2122
import com.intellij.util.xmlb.annotations.OptionTag
2223
import com.intellij.util.xmlb.annotations.XCollection
2324
import com.intellij.util.xmlb.annotations.XMap
@@ -45,7 +46,6 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
4546
@OptionTag(converter = LocaleConverter::class)
4647
var locale: Locale = Locale.ENGLISH
4748

48-
4949
@XCollection(
5050
elementTypes = [
5151
OpenAiClientConfiguration::class,
@@ -57,7 +57,8 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
5757
OpenAiClientConfiguration()
5858
)
5959

60-
private var activeLlmClient = "OpenAI"
60+
@Attribute
61+
var activeLlmClientId: String? = null
6162

6263
@XMap
6364
var prompts = DefaultPrompts.toPromptsMap()
@@ -74,7 +75,7 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
7475
override fun noStateLoaded() {
7576
val appSettings = AppSettings.instance
7677
migrateSettingsFromVersion1(appSettings)
77-
val openAiLlmClient = llmClientConfigurations.find { it.displayName == "OpenAI" }
78+
val openAiLlmClient = llmClientConfigurations.find { it.getClientName() == OpenAiClientConfiguration.CLIENT_NAME }
7879
migrateOpenAiClientFromVersion1(openAiLlmClient as OpenAiClientConfiguration, appSettings)
7980
}
8081

@@ -100,7 +101,7 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
100101
PasswordSafe.instance.getAsync(credentialAttributes)
101102
.onSuccess {
102103
it?.password?.let { token ->
103-
saveToken(displayName, token.toString(false))
104+
saveToken(id, token.toString(false))
104105
tokenIsStored = true
105106
}
106107
}
@@ -121,14 +122,15 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
121122
return AICommitsUtils.matchesGlobs(path, appExclusions)
122123
}
123124

124-
fun getActiveLLMClient(): LLMClientConfiguration {
125-
return llmClientConfigurations.find { it.displayName == activeLlmClient }!!
125+
fun getActiveLLMClient(): LLMClientConfiguration? {
126+
return llmClientConfigurations.find { it.id == activeLlmClientId }
127+
?: llmClientConfigurations.firstOrNull()
126128
}
127129

128-
fun setActiveLlmClient(llmClientConfiguration: LLMClientConfiguration) {
129-
// TODO @Blarc: Throw exception if llm client name is not valid
130-
llmClientConfigurations.find { it.displayName == llmClientConfiguration.displayName }?.let {
131-
activeLlmClient = llmClientConfiguration.displayName
130+
fun setActiveLlmClient(newId: String) {
131+
// TODO @Blarc: Throw exception if llm client id is not valid
132+
llmClientConfigurations.find { it.id == newId }?.let {
133+
activeLlmClientId = newId
132134
}
133135
}
134136

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
2929
llmClientConfigurationComboBox = comboBox(AppSettings2.instance.llmClientConfigurations, AICommitsListCellRenderer())
3030
.bindItem(getter = AppSettings2.instance::getActiveLLMClient) {
3131
it?.let {
32-
AppSettings2.instance.setActiveLlmClient(it)
32+
AppSettings2.instance.setActiveLlmClient(it.id)
3333
}
3434
}.widthGroup("input")
3535
.component

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ package com.github.blarc.ai.commits.intellij.plugin.settings.clients
33
import com.intellij.openapi.ui.ComboBox
44
import com.intellij.openapi.vcs.ui.CommitMessage
55
import com.intellij.util.xmlb.annotations.Attribute
6+
import java.util.*
67
import javax.swing.Icon
78

89
abstract class LLMClientConfiguration(
9-
@Attribute var displayName: String,
10+
@Attribute var name: String,
1011
@Attribute var host: String,
1112
@Attribute var proxyUrl: String?,
1213
@Attribute var timeout: Int,
1314
@Attribute var modelId: String,
1415
@Attribute var temperature: String,
1516
) : Cloneable, Comparable<LLMClientConfiguration> {
1617

17-
abstract fun getIcon(): Icon
18+
@Attribute
19+
var id: String = UUID.randomUUID().toString()
20+
21+
abstract fun getClientName(): String
22+
23+
abstract fun getClientIcon(): Icon
1824

1925
abstract fun getSharedState(): LLMClientSharedState
2026

@@ -43,7 +49,14 @@ abstract class LLMClientConfiguration(
4349
abstract fun panel(): LLMClientPanel
4450

4551
override fun compareTo(other: LLMClientConfiguration): Int {
46-
return displayName.compareTo(other.displayName)
52+
return name.compareTo(other.name)
4753
}
4854

55+
override fun equals(other: Any?): Boolean {
56+
return other is LLMClientConfiguration && other.id == id
57+
}
58+
59+
override fun hashCode(): Int {
60+
return id.hashCode()
61+
}
4962
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.github.blarc.ai.commits.intellij.plugin.settings.clients
22

33
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
44
import com.github.blarc.ai.commits.intellij.plugin.isInt
5+
import com.github.blarc.ai.commits.intellij.plugin.notBlank
56
import com.github.blarc.ai.commits.intellij.plugin.temperatureValid
67
import com.intellij.openapi.ui.ComboBox
78
import com.intellij.ui.components.JBLabel
@@ -22,13 +23,25 @@ abstract class LLMClientPanel(
2223
val verifyLabel = JBLabel()
2324

2425
open fun create() = panel {
26+
nameRow()
2527
hostRow()
2628
timeoutRow()
2729
modelIdRow()
2830
temperatureRow()
2931
verifyRow()
3032
}
3133

34+
open fun Panel.nameRow() {
35+
row {
36+
label(message("settings.llmClient.name"))
37+
.widthGroup("label")
38+
textField()
39+
.bindText(clientConfiguration::name)
40+
.widthGroup("input")
41+
.validationOnInput { notBlank(it.text) }
42+
}
43+
}
44+
3245
open fun Panel.hostRow() {
3346
row {
3447
label(message("settings.llmClient.host"))

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class LLMClientTable {
4646

4747
private fun createTableModel(): ListTableModel<LLMClientConfiguration> = ListTableModel(
4848
arrayOf(
49-
createColumn<LLMClientConfiguration>(message("settings.llmClient.name")) { llmClient -> llmClient.displayName },
49+
createColumn<LLMClientConfiguration>(message("settings.llmClient.name")) { llmClient -> llmClient.name },
5050
createColumn(message("settings.llmClient.host")) { llmClient -> llmClient.host },
5151
createColumn(message("settings.llmClient.modelId")) { llmClient -> llmClient.modelId }
5252
),
@@ -115,7 +115,7 @@ class LLMClientTable {
115115

116116
override fun doOKAction() {
117117
if (newLlmClientConfiguration == null) {
118-
(cardLayout.findComponentById(llmClient.displayName) as DialogPanel).apply()
118+
(cardLayout.findComponentById(llmClient.getClientName()) as DialogPanel).apply()
119119
}
120120
// TODO: Figure out how to call apply of the currently active panel
121121
super.doOKAction()
@@ -147,19 +147,20 @@ class LLMClientTable {
147147
val cardPanel = JPanel(cardLayout).apply {
148148
preferredSize = JBUI.size(640, 480)
149149
llmClientConfigurations.forEach {
150-
add(it.displayName, it.panel().create())
150+
add(it.getClientName(), it.panel().create())
151151
}
152152
}
153153

154154
// Register validators of the currently active cards
155-
(cardLayout.findComponentById(llmClient.displayName) as DialogPanel).registerValidators(myDisposable) {
155+
val dialogPanel = cardLayout.findComponentById(llmClient.getClientName()) as DialogPanel
156+
dialogPanel.registerValidators(myDisposable) {
156157
isOKActionEnabled = ContainerUtil.and(it.values) { info: ValidationInfo -> info.okEnabled }
157158
}
158159

159160
val cardsList = JBList(llmClientConfigurations).apply {
160161
val descriptor = object : ListItemDescriptorAdapter<LLMClientConfiguration>() {
161-
override fun getTextFor(value: LLMClientConfiguration) = value.displayName
162-
override fun getIconFor(value: LLMClientConfiguration) = value.getIcon()
162+
override fun getTextFor(value: LLMClientConfiguration) = value.getClientName()
163+
override fun getIconFor(value: LLMClientConfiguration) = value.getClientIcon()
163164
}
164165
cellRenderer = object : GroupedItemsListRenderer<LLMClientConfiguration>(descriptor) {
165166
override fun customizeComponent(list: JList<out LLMClientConfiguration>?, value: LLMClientConfiguration?, isSelected: Boolean) {
@@ -168,7 +169,7 @@ class LLMClientTable {
168169
}
169170
addListSelectionListener {
170171
llmClient = selectedValue
171-
cardLayout.show(cardPanel, llmClient.displayName)
172+
cardLayout.show(cardPanel, llmClient.getClientName())
172173
}
173174
setSelectedValue(llmClient, true)
174175
}

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,24 @@ import com.intellij.openapi.ui.ComboBox
77
import com.intellij.openapi.vcs.ui.CommitMessage
88
import javax.swing.Icon
99

10-
class OllamaClientConfiguration(displayName: String = "Ollama") : LLMClientConfiguration(
11-
displayName,
10+
class OllamaClientConfiguration : LLMClientConfiguration(
11+
"Ollama",
1212
"http://localhost:11434/",
1313
null,
1414
30,
1515
"llama3",
1616
"0.7"
1717
) {
18-
override fun getIcon(): Icon {
18+
19+
companion object {
20+
const val CLIENT_NAME = "Ollama"
21+
}
22+
23+
override fun getClientName(): String {
24+
return CLIENT_NAME
25+
}
26+
27+
override fun getClientIcon(): Icon {
1928
return Icons.OLLAMA
2029
}
2130

@@ -32,7 +41,9 @@ class OllamaClientConfiguration(displayName: String = "Ollama") : LLMClientConfi
3241
}
3342

3443
override fun clone(): LLMClientConfiguration {
35-
val copy = OllamaClientConfiguration(displayName)
44+
val copy = OllamaClientConfiguration()
45+
copy.id = id
46+
copy.name = name
3647
copy.host = host
3748
copy.proxyUrl = proxyUrl
3849
copy.timeout = timeout

0 commit comments

Comments
 (0)