Skip to content

Commit 112f090

Browse files
0xZZSheepBlarc
authored andcommitted
feat: support ernie
1 parent 13ddc3d commit 112f090

File tree

10 files changed

+239
-1
lines changed

10 files changed

+239
-1
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ dependencies {
126126
// langchain4j integrations
127127
implementation("dev.langchain4j:langchain4j-open-ai:0.32.0")
128128
implementation("dev.langchain4j:langchain4j-ollama:0.32.0")
129+
implementation("dev.langchain4j:langchain4j-qianfan:0.32.0") // The Baidu Qianfan Large Model Platform, including the ERNIE series, can be accessed at https://docs.langchain4j.dev/integrations/language-models/qianfan/.
129130
// implementation("dev.langchain4j:langchain4j-hugging-face:0.28.0")
130131
// implementation("dev.langchain4j:langchain4j-milvus:0.28.0")
131132
// implementation("dev.langchain4j:langchain4j-local-ai:0.28.0")

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ object Icons {
66
val AI_COMMITS = IconLoader.getIcon("/icons/aiCommits15.svg", javaClass)
77
val OPEN_AI = IconLoader.getIcon("/icons/openai.svg", javaClass)
88
val OLLAMA = IconLoader.getIcon("/icons/ollama15.svg", javaClass)
9+
val ERNIE = IconLoader.getIcon("/icons/ernie.svg", javaClass)
910
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.getCredentialA
55
import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification
66
import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification
77
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientConfiguration
8+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie.ErnieClientConfiguration
89
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.ollama.OllamaClientConfiguration
910
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.openAi.OpenAiClientConfiguration
1011
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.openAi.OpenAiClientSharedState
@@ -50,6 +51,7 @@ class AppSettings2 : PersistentStateComponent<AppSettings2> {
5051
elementTypes = [
5152
OpenAiClientConfiguration::class,
5253
OllamaClientConfiguration::class,
54+
ErnieClientConfiguration::class
5355
],
5456
style = XCollection.Style.v2
5557
)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.github.blarc.ai.commits.intellij.plugin.createColumn
55
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2
66
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.ollama.OllamaClientConfiguration
77
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.openAi.OpenAiClientConfiguration
8+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie.ErnieClientConfiguration
89
import com.intellij.openapi.ui.DialogPanel
910
import com.intellij.openapi.ui.DialogWrapper
1011
import com.intellij.openapi.ui.Splitter
@@ -134,7 +135,8 @@ class LLMClientTable {
134135
// TODO(@Blarc): Is there a better way to create the list of all possible LLM Clients that implement LLMClient abstract class
135136
listOf(
136137
OpenAiClientConfiguration(),
137-
OllamaClientConfiguration()
138+
OllamaClientConfiguration(),
139+
ErnieClientConfiguration()
138140
)
139141
} else {
140142
listOf(newLLMClientConfiguration)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie
2+
3+
import com.github.blarc.ai.commits.intellij.plugin.Icons
4+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientConfiguration
5+
import com.intellij.openapi.project.Project
6+
import com.intellij.openapi.vcs.ui.CommitMessage
7+
import com.intellij.util.xmlb.annotations.Attribute
8+
import com.intellij.util.xmlb.annotations.Transient
9+
import java.util.*
10+
import javax.swing.Icon
11+
import dev.langchain4j.model.qianfan.QianfanChatModelNameEnum
12+
13+
class ErnieClientConfiguration : LLMClientConfiguration(
14+
"Ernie",
15+
"https://aip.baidubce.com",
16+
null,
17+
30,
18+
QianfanChatModelNameEnum.ERNIE_SPEED_128K.modelName,
19+
"0.7"
20+
) {
21+
var apiKeyIsStored : Boolean = false
22+
var secretKeyIsStored: Boolean = false
23+
@Transient
24+
var apiKey: String = ""
25+
26+
@Transient
27+
var secretKey: String = ""
28+
29+
@Attribute
30+
var apiKeyId: String = UUID.randomUUID().toString()
31+
32+
@Attribute
33+
var secretKeyId: String = UUID.randomUUID().toString()
34+
35+
companion object {
36+
const val CLIENT_NAME = "Ernie"
37+
}
38+
39+
override fun getClientName(): String {
40+
return CLIENT_NAME
41+
}
42+
43+
override fun getClientIcon(): Icon {
44+
return Icons.ERNIE
45+
}
46+
47+
override fun getSharedState(): ErnieClientSharedState {
48+
return ErnieClientSharedState.getInstance()
49+
}
50+
51+
override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) {
52+
return ErnieClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage)
53+
}
54+
55+
// Model names are retrieved from Enum and do not need to be refreshed.
56+
override fun getRefreshModelsFunction() = null
57+
58+
override fun clone(): LLMClientConfiguration {
59+
val copy = ErnieClientConfiguration()
60+
copy.id = id
61+
copy.apiKey = apiKey
62+
copy.secretKey = secretKey
63+
copy.secretKeyId = secretKeyId
64+
copy.apiKeyId = apiKeyId
65+
copy.name = name
66+
copy.host = host
67+
copy.proxyUrl = proxyUrl
68+
copy.timeout = timeout
69+
copy.modelId = modelId
70+
copy.temperature = temperature
71+
copy.apiKeyIsStored = apiKeyIsStored
72+
copy.secretKeyIsStored = secretKeyIsStored
73+
return copy
74+
}
75+
76+
override fun panel() = ErnieClientPanel(this)
77+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie
2+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
3+
import com.github.blarc.ai.commits.intellij.plugin.emptyText
4+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientPanel
5+
import com.intellij.ui.components.JBPasswordField
6+
import com.intellij.ui.dsl.builder.bindText
7+
import com.intellij.ui.dsl.builder.panel
8+
9+
class ErnieClientPanel(private val clientConfiguration: ErnieClientConfiguration) : LLMClientPanel(clientConfiguration) {
10+
11+
private val apiKeyField = JBPasswordField()
12+
private val secretKeyField = JBPasswordField()
13+
14+
override fun create() = panel {
15+
nameRow()
16+
hostRow()
17+
modelIdRow()
18+
19+
row {
20+
label(message("settings.ernie.apiKey"))
21+
.widthGroup("label")
22+
cell(apiKeyField)
23+
.bindText(getter = {""}, setter = {
24+
ErnieClientService.getInstance().saveApiKey(clientConfiguration, it)
25+
})
26+
.emptyText(if (clientConfiguration.apiKeyIsStored) message("settings.openAI.token.stored") else "")
27+
.resizableColumn()
28+
.widthGroup("input")
29+
}
30+
row {
31+
label(message("settings.ernie.secretKey"))
32+
.widthGroup("label")
33+
cell(secretKeyField)
34+
.bindText(getter = {""}, setter = {
35+
ErnieClientService.getInstance().saveSecretKey(clientConfiguration, it)
36+
})
37+
.emptyText(if (clientConfiguration.secretKeyIsStored) message("settings.openAI.token.stored") else "")
38+
.resizableColumn()
39+
.widthGroup("input")
40+
}
41+
42+
temperatureRow()
43+
verifyRow()
44+
}
45+
46+
override fun verifyConfiguration() {
47+
48+
clientConfiguration.host = hostComboBox.item
49+
// clientConfiguration.proxyUrl = proxyTextField.text
50+
// clientConfiguration.timeout = socketTimeoutTextField.text.toInt()
51+
clientConfiguration.modelId = modelComboBox.item
52+
clientConfiguration.temperature = temperatureTextField.text
53+
clientConfiguration.apiKey = String(apiKeyField.password)
54+
clientConfiguration.secretKey = String(secretKeyField.password)
55+
56+
ErnieClientService.getInstance().verifyConfiguration(clientConfiguration, verifyLabel)
57+
}
58+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie
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.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientService
8+
import com.intellij.ide.passwordSafe.PasswordSafe
9+
import com.intellij.openapi.components.Service
10+
import com.intellij.openapi.components.service
11+
import com.intellij.util.text.nullize
12+
import dev.langchain4j.model.chat.ChatLanguageModel
13+
import dev.langchain4j.model.qianfan.QianfanChatModel
14+
import dev.langchain4j.model.qianfan.QianfanChatModelNameEnum
15+
import kotlinx.coroutines.CoroutineScope
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.launch
18+
19+
@Service(Service.Level.APP)
20+
class ErnieClientService(private val cs: CoroutineScope) : LLMClientService<ErnieClientConfiguration>(cs) {
21+
22+
companion object {
23+
@JvmStatic
24+
fun getInstance(): ErnieClientService = service()
25+
}
26+
27+
override suspend fun buildChatModel(client: ErnieClientConfiguration): ChatLanguageModel {
28+
val apiKey = client.apiKey.nullize(true) ?: retrieveToken(client.apiKeyId)?.toString(true)
29+
val secretKey = client.secretKey.nullize(true) ?: retrieveToken(client.secretKeyId)?.toString(true)
30+
31+
val builder = QianfanChatModel.builder()
32+
.baseUrl(client.host)
33+
.apiKey(apiKey)
34+
.secretKey(secretKey)
35+
.modelName(client.modelId)
36+
.temperature(client.temperature.toDouble())
37+
// Fix https://github.com/langchain4j/langchain4j/pull/1426. Remove this 'if' statement if langchain4j releases a new version that resolves this issue.
38+
if (client.modelId == QianfanChatModelNameEnum.ERNIE_SPEED_128K.modelName){
39+
builder.endpoint("ernie-speed-128k")
40+
}
41+
42+
return builder.build()
43+
}
44+
45+
private fun saveToken(token: String, title: String) {
46+
cs.launch(Dispatchers.Default) {
47+
try {
48+
PasswordSafe.instance.setPassword(getCredentialAttributes(title), token)
49+
} catch (e: Exception) {
50+
sendNotification(Notification.unableToSaveToken(e.message))
51+
}
52+
}
53+
}
54+
fun saveApiKey(client: ErnieClientConfiguration, apiKey: String) {
55+
saveToken(apiKey, client.apiKeyId)
56+
client.apiKeyIsStored = true
57+
}
58+
fun saveSecretKey(client: ErnieClientConfiguration, secretKey: String) {
59+
saveToken(secretKey, client.secretKeyId)
60+
client.secretKeyIsStored = true
61+
}
62+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.settings.clients.ernie
2+
3+
import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientSharedState
4+
import com.intellij.openapi.components.*
5+
import com.intellij.util.xmlb.XmlSerializerUtil
6+
import com.intellij.util.xmlb.annotations.XCollection
7+
import dev.langchain4j.model.qianfan.QianfanChatModelNameEnum
8+
9+
@Service(Service.Level.APP)
10+
@State(name = "ErnieClientSharedState", storages = [Storage("AICommitsOpenAi.xml")])
11+
class ErnieClientSharedState : PersistentStateComponent<ErnieClientSharedState>, LLMClientSharedState {
12+
13+
companion object {
14+
@JvmStatic
15+
fun getInstance(): ErnieClientSharedState = service()
16+
}
17+
18+
@XCollection(style = XCollection.Style.v2)
19+
override val hosts = mutableSetOf("https://aip.baidubce.com")
20+
21+
@XCollection(style = XCollection.Style.v2)
22+
override val modelIds = QianfanChatModelNameEnum.entries.stream()
23+
.map { it.toString() }
24+
.toList()
25+
.toMutableSet()
26+
27+
override fun getState(): ErnieClientSharedState = this
28+
29+
override fun loadState(state: ErnieClientSharedState) {
30+
XmlSerializerUtil.copyBean(state, this)
31+
}
32+
}

src/main/resources/icons/ernie.svg

Lines changed: 1 addition & 0 deletions
Loading

src/main/resources/messages/AiCommitsBundle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,6 @@ settings.openAI.token.stored=<hidden>
7676
settings.openAi.token.comment=\
7777
<p>You can get your token <a href="https://platform.openai.com/account/api-keys">here.</a/></p>
7878
settings.openAi.organizationId=Organization ID
79+
settings.ernie.apiKey=API key
80+
settings.ernie.secretKey=Secret key
7981
notifications.client-not-set=LLM client is not set.

0 commit comments

Comments
 (0)