Skip to content

Commit 04fe183

Browse files
committed
feat(settings): save multiple llm clients
1 parent 6604477 commit 04fe183

File tree

9 files changed

+127
-85
lines changed

9 files changed

+127
-85
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class AICommitAction : AnAction(), DumbAware {
4040

4141
val branch = commonBranch(includedChanges, project)
4242
val hint = commitMessage?.text
43-
val prompt = constructPrompt(AppSettings2.instance.currentPrompt.content, diff, branch, hint)
43+
val prompt = constructPrompt(AppSettings2.instance.activePrompt.content, diff, branch, hint)
4444
if (isPromptTooLarge(prompt)) {
4545
sendNotification(Notification.promptTooLarge())
4646
return@runBackgroundableTask

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,10 @@ data class Notification(
4343
fun promptTooLarge() = Notification(DEFAULT_TITLE, message = message("notifications.prompt-too-large"))
4444
fun unsuccessfulRequest(message: String) = Notification(message = message("notifications.unsuccessful-request", message))
4545
fun noCommitMessage() = Notification(message = message("notifications.no-commit-message"))
46-
fun unableToSaveToken() = Notification(message = message("notifications.unable-to-save-token"))
46+
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"))
4848

4949
}
50-
51-
fun isTransient() = type == Type.TRANSIENT
52-
fun isPersistent() = !isTransient();
5350
}
5451

5552
data class NotificationAction(val title: String, val run: (dismiss: () -> Unit) -> Unit) {

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

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package com.github.blarc.ai.commits.intellij.plugin.settings
33
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils
44
import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification
55
import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification
6+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClient
7+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.OpenAIClient
8+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.TestAIClient
69
import com.github.blarc.ai.commits.intellij.plugin.settings.prompts.DefaultPrompts
7-
import com.github.blarc.ai.commits.intellij.plugin.settings.providers.LLMClient
8-
import com.github.blarc.ai.commits.intellij.plugin.settings.providers.OpenAIClient
910
import com.intellij.openapi.application.ApplicationManager
1011
import com.intellij.openapi.components.PersistentStateComponent
1112
import com.intellij.openapi.components.Service
@@ -14,6 +15,8 @@ import com.intellij.openapi.components.Storage
1415
import com.intellij.util.xmlb.Converter
1516
import com.intellij.util.xmlb.XmlSerializerUtil
1617
import com.intellij.util.xmlb.annotations.OptionTag
18+
import com.intellij.util.xmlb.annotations.XCollection
19+
import com.intellij.util.xmlb.annotations.XMap
1720
import java.util.*
1821

1922
@State(
@@ -38,13 +41,24 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
3841
@OptionTag(converter = LocaleConverter::class)
3942
var locale: Locale = Locale.ENGLISH
4043

41-
private var activeLlmClient = "OpenAI"
42-
var llmClients = mapOf(
43-
"OpenAI" to OpenAIClient()
44+
45+
@XCollection(
46+
elementTypes = [
47+
OpenAIClient::class,
48+
TestAIClient::class
49+
],
50+
style = XCollection.Style.v2
51+
)
52+
var llmClients = setOf(
53+
OpenAIClient(),
54+
TestAIClient(),
55+
TestAIClient("TestAI2")
4456
)
57+
private var activeLlmClient = "OpenAI"
4558

59+
@XMap
4660
var prompts = DefaultPrompts.toPromptsMap()
47-
var currentPrompt = prompts["basic"]!!
61+
var activePrompt = prompts["basic"]!!
4862

4963
var appExclusions: Set<String> = setOf()
5064

@@ -56,27 +70,32 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
5670

5771
override fun noStateLoaded() {
5872
val appSettings = AppSettings.instance
73+
migrateSettingsFromVersion1(appSettings)
74+
val openAiLlmClient = llmClients.find { it.displayName == "OpenAI" }
75+
migrateOpenAiClientFromVersion1(openAiLlmClient, appSettings)
76+
}
5977

60-
// Migration from old settings
78+
private fun migrateSettingsFromVersion1(appSettings: AppSettings) {
6179
hits = appSettings.hits
6280
locale = appSettings.locale
6381
lastVersion = appSettings.lastVersion
6482
requestSupport = appSettings.requestSupport
65-
6683
prompts = appSettings.prompts
67-
currentPrompt = appSettings.currentPrompt
68-
84+
activePrompt = appSettings.currentPrompt
6985
appExclusions = appSettings.appExclusions
86+
}
7087

71-
// TODO: currentLlmClient vs OpenAIClient.instance()
72-
llmClients["OpenAI"]?.host = appSettings.openAIHost
73-
llmClients["OpenAI"]?.hosts = appSettings.openAIHosts
74-
appSettings.openAISocketTimeout.toIntOrNull()?.let { llmClients["OpenAI"]?.timeout = it }
75-
appSettings.proxyUrl?.let { llmClients["OpenAI"]?.proxyUrl = it }
76-
llmClients["OpenAI"]?.modelId = appSettings.openAIModelId
77-
llmClients["OpenAI"]?.modelIds = appSettings.openAIModelIds
78-
llmClients["OpenAI"]?.temperature = appSettings.openAITemperature
79-
AICommitsUtils.retrieveToken( appSettings.openAITokenTitle)?.let { llmClients["OpenAI"]?.token = it }
88+
private fun migrateOpenAiClientFromVersion1(openAiLlmClient: LLMClient?, appSettings: AppSettings) {
89+
openAiLlmClient?.apply {
90+
host = appSettings.openAIHost
91+
hosts = appSettings.openAIHosts
92+
appSettings.openAISocketTimeout.toIntOrNull()?.let { timeout = it }
93+
proxyUrl = appSettings.proxyUrl
94+
modelId = appSettings.openAIModelId
95+
modelIds = appSettings.openAIModelIds
96+
temperature = appSettings.openAITemperature
97+
AICommitsUtils.retrieveToken(appSettings.openAITokenTitle)?.let { token = it }
98+
}
8099
}
81100

82101
fun recordHit() {
@@ -91,12 +110,12 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
91110
}
92111

93112
fun getActiveLLMClient(): LLMClient {
94-
return llmClients[activeLlmClient]!!
113+
return llmClients.find { it.displayName == activeLlmClient }!!
95114
}
96115

97116
fun setActiveLlmClient(llmClientName: String) {
98117
// TODO @Blarc: Throw exception if llm client name is not valid
99-
llmClients[llmClientName]?.let {
118+
llmClients.find { it.displayName == llmClientName }?.let {
100119
activeLlmClient = llmClientName
101120
}
102121
}

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
@@ -152,7 +152,7 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
152152
row {
153153
label(message("settings.prompt")).widthGroup("labelPrompt")
154154
promptComboBox = comboBox(AppSettings2.instance.prompts.values, AppSettingsListCellRenderer())
155-
.bindItem(AppSettings2.instance::currentPrompt.toNullableProperty())
155+
.bindItem(AppSettings2.instance::activePrompt.toNullableProperty())
156156
}
157157
row {
158158
toolbarDecorator = ToolbarDecorator.createDecorator(promptTable.table)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients
2+
3+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.getCredentialAttributes
4+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.retrieveToken
5+
import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification
6+
import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification
7+
import com.intellij.ide.passwordSafe.PasswordSafe
8+
import com.intellij.util.xmlb.annotations.Attribute
9+
import com.intellij.util.xmlb.annotations.Transient
10+
import com.intellij.util.xmlb.annotations.XCollection
11+
12+
abstract class LLMClient(
13+
@Attribute var displayName: String,
14+
@Attribute var host: String,
15+
@XCollection(style = XCollection.Style.v2, elementName = "host")
16+
var hosts: MutableSet<String>,
17+
@Attribute var proxyUrl: String?,
18+
@Attribute var timeout: Int,
19+
@Attribute var modelId: String,
20+
@XCollection(style = XCollection.Style.v2, elementName = "modelId")
21+
var modelIds: List<String>,
22+
@Attribute var temperature: String
23+
) {
24+
25+
@get:Transient
26+
var token: String
27+
get() = retrieveToken(displayName) ?: ""
28+
set(token) = saveToken(token)
29+
30+
abstract suspend fun generateCommitMessage(prompt: String): String
31+
32+
abstract suspend fun refreshModels()
33+
34+
@Throws(Exception::class)
35+
abstract suspend fun verifyConfiguration(
36+
newHost: String,
37+
newProxy: String?,
38+
newTimeout: String,
39+
newToken: String
40+
)
41+
42+
private fun saveToken(token: String) {
43+
try {
44+
PasswordSafe.instance.setPassword(getCredentialAttributes(displayName), token)
45+
} catch (e: Exception) {
46+
sendNotification(Notification.unableToSaveToken(e.message))
47+
}
48+
}
49+
}
Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.github.blarc.ai.commits.intellij.plugin.settings.providers
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients
22

33
import com.aallam.openai.api.chat.ChatCompletion
44
import com.aallam.openai.api.chat.ChatCompletionRequest
@@ -10,21 +10,18 @@ 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.Tag
1413
import kotlin.time.Duration.Companion.seconds
1514

16-
data class OpenAIClient(
17-
override var host: String = OpenAIHost.OpenAI.baseUrl,
18-
override var hosts: MutableSet<String> = mutableSetOf(OpenAIHost.OpenAI.baseUrl),
19-
override var proxyUrl: String? = null,
20-
override var timeout: Int = 30,
21-
override var modelId: String = "gpt-3.5-turbo",
22-
override var modelIds: List<String> = listOf("gpt-3.5-turbo", "gpt-4"),
23-
override var temperature: String = "0.7"
24-
) : LLMClient {
25-
26-
override fun displayName() = "OpenAI"
27-
15+
class OpenAIClient(displayName: String = "OpenAI") : LLMClient(
16+
displayName,
17+
OpenAIHost.OpenAI.baseUrl,
18+
mutableSetOf(OpenAIHost.OpenAI.baseUrl),
19+
null,
20+
30,
21+
"gpt-3.5-turbo",
22+
listOf("gpt-3.5-turbo", "gpt-4"),
23+
"0.7"
24+
) {
2825
override suspend fun generateCommitMessage(
2926
prompt: String
3027
): String {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients
2+
3+
class TestAIClient(displayName: String = "TestAI") : LLMClient(
4+
displayName,
5+
"testAiBaseUrl",
6+
mutableSetOf("testAiBaseUrl"),
7+
null,
8+
30,
9+
"gpt-3.5-turbo",
10+
listOf("gpt-3.5-turbo", "gpt-4"),
11+
"0.7"
12+
) {
13+
override suspend fun generateCommitMessage(prompt: String): String {
14+
return "hello world"
15+
}
16+
17+
override suspend fun refreshModels() {
18+
}
19+
20+
override suspend fun verifyConfiguration(newHost: String, newProxy: String?, newTimeout: String, newToken: String) {
21+
}
22+
23+
}

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

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/main/resources/messages/MyBundle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ actions.take-me-there = Take me there.
4040
actions.sure-take-me-there=Sure, take me there.
4141
notifications.prompt-too-large=The diff is too large for the OpenAI API. Try reducing the number of staged changes, or write your own commit message.
4242
notifications.no-commit-message=Commit field has not been initialized correctly.
43-
notifications.unable-to-save-token=Token could not be saved.
43+
notifications.unable-to-save-token=Token could not be saved: {0}.
4444
settings.addPrompt=Add prompt
4545
settings.prompt.name=Name
4646
settings.prompt.content=Content

0 commit comments

Comments
 (0)