Skip to content

Commit 758def6

Browse files
committed
feat(settings): option to set OpenAI socket timeout
Closes #118
1 parent 36e0b60 commit 758def6

File tree

5 files changed

+77
-52
lines changed

5 files changed

+77
-52
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ fun ValidationInfoBuilder.temperatureValid(value: String): ValidationInfo? {
3434
fun ValidationInfoBuilder.unique(value: String, existingValues: Set<String>): ValidationInfo? =
3535
if (existingValues.contains(value)) error(message("validation.unique")) else null
3636

37-
fun ValidationInfoBuilder.isLong(value: String): ValidationInfo? {
37+
fun ValidationInfoBuilder.isInt(value: String): ValidationInfo? {
3838
if (value.isBlank()){
3939
return null
4040
}
4141

42-
value.toLongOrNull().let {
42+
value.toIntOrNull().let {
4343
if (it == null) {
44-
return error(message("validation.number"))
44+
return error(message("validation.integer"))
4545
} else {
4646
return null
4747
}

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

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

33
import com.aallam.openai.api.chat.*
4+
import com.aallam.openai.api.http.Timeout
45
import com.aallam.openai.api.model.ModelId
56
import com.aallam.openai.client.OpenAI
67
import com.aallam.openai.client.OpenAIConfig
@@ -9,6 +10,7 @@ import com.aallam.openai.client.ProxyConfig
910
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings
1011
import com.intellij.openapi.application.ApplicationManager
1112
import com.intellij.openapi.components.Service
13+
import kotlin.time.Duration.Companion.seconds
1214

1315

1416
@Service(Service.Level.APP)
@@ -48,12 +50,13 @@ class OpenAIService {
4850
}
4951

5052
@Throws(Exception::class)
51-
suspend fun verifyOpenAIConfiguration(host: String, token: String, proxy: String?){
53+
suspend fun verifyOpenAIConfiguration(host: String, token: String, proxy: String?, socketTimeout: String){
5254

5355
val config = OpenAIConfig(
5456
token,
5557
host = host.takeIf { it.isNotBlank() }?.let { OpenAIHost(it) } ?: OpenAIHost.OpenAI,
56-
proxy = proxy?.takeIf { it.isNotBlank() }?.let { ProxyConfig.Http(it) }
58+
proxy = proxy?.takeIf { it.isNotBlank() }?.let { ProxyConfig.Http(it) },
59+
timeout = Timeout(socket = socketTimeout.toInt().seconds)
5760
)
5861
val openAI = OpenAI(config)
5962
openAI.models()

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

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.blarc.ai.commits.intellij.plugin.settings
22

3+
import com.aallam.openai.api.http.Timeout
34
import com.aallam.openai.client.OpenAIConfig
45
import com.aallam.openai.client.OpenAIHost
56
import com.aallam.openai.client.ProxyConfig
@@ -18,22 +19,25 @@ import com.intellij.util.xmlb.Converter
1819
import com.intellij.util.xmlb.XmlSerializerUtil
1920
import com.intellij.util.xmlb.annotations.OptionTag
2021
import java.util.*
22+
import kotlin.time.Duration.Companion.seconds
2123

2224
@State(
23-
name = AppSettings.SERVICE_NAME,
24-
storages = [Storage("AICommit.xml")]
25+
name = AppSettings.SERVICE_NAME,
26+
storages = [Storage("AICommit.xml")]
2527
)
2628
class AppSettings : PersistentStateComponent<AppSettings> {
2729

2830
private val openAITokenTitle = "OpenAIToken"
2931
private var hits = 0
32+
3033
@OptionTag(converter = LocaleConverter::class)
3134
var locale: Locale = Locale.ENGLISH
3235

3336
var requestSupport = true
3437
var lastVersion: String? = null
3538
var openAIHost = OpenAIHost.OpenAI.baseUrl
3639
var openAIHosts = mutableSetOf(OpenAIHost.OpenAI.baseUrl)
40+
var openAISocketTimeout = "30"
3741
var proxyUrl: String? = null
3842

3943
var prompts = initPrompts()
@@ -62,9 +66,10 @@ class AppSettings : PersistentStateComponent<AppSettings> {
6266
fun getOpenAIConfig(): OpenAIConfig {
6367
val token = getOpenAIToken() ?: throw Exception("OpenAI Token is not set.")
6468
return OpenAIConfig(
65-
token,
66-
host = openAIHost.takeIf { it.isNotBlank() }?.let { OpenAIHost(it) } ?: OpenAIHost.OpenAI,
67-
proxy = proxyUrl?.takeIf { it.isNotBlank() }?.let { ProxyConfig.Http(it) }
69+
token,
70+
host = openAIHost.takeIf { it.isNotBlank() }?.let { OpenAIHost(it) } ?: OpenAIHost.OpenAI,
71+
proxy = proxyUrl?.takeIf { it.isNotBlank() }?.let { ProxyConfig.Http(it) },
72+
timeout = Timeout(socket = openAISocketTimeout.toInt().seconds)
6873
)
6974
}
7075

@@ -76,10 +81,10 @@ class AppSettings : PersistentStateComponent<AppSettings> {
7681

7782
private fun getCredentialAttributes(title: String): CredentialAttributes {
7883
return CredentialAttributes(
79-
title,
80-
null,
81-
this.javaClass,
82-
false
84+
title,
85+
null,
86+
this.javaClass,
87+
false
8388
)
8489
}
8590

@@ -101,38 +106,44 @@ class AppSettings : PersistentStateComponent<AppSettings> {
101106
}
102107

103108
private fun initPrompts() = mutableMapOf(
104-
// Generate UUIDs for game objects in Mine.py and call the function in start_game().
105-
"basic" to Prompt("Basic",
106-
"Basic prompt that generates a decent commit message.",
107-
"Write an insightful but concise Git commit message in a complete sentence in present tense for the " +
108-
"following diff without prefacing it with anything, the response must be in the language {locale} and must " +
109-
"NOT be longer than 74 characters. The sent text will be the differences between files, where deleted lines" +
110-
" are prefixed with a single minus sign and added lines are prefixed with a single plus sign.\n" +
111-
"{diff}",
112-
false),
113-
// feat: generate unique UUIDs for game objects on Mine game start
114-
"conventional" to Prompt("Conventional",
115-
"Prompt for commit message in the conventional commit convention.",
116-
"Write a commit message in the conventional commit convention. I'll send you an output " +
117-
"of 'git diff --staged' command, and you convert it into a commit message. " +
118-
"Lines must not be longer than 74 characters. Use {locale} language to answer. " +
119-
"End commit title with issue number if you can get it from the branch name: " +
120-
"{branch} in parenthesis.\n" +
121-
"{diff}",
122-
false),
123-
// ✨ feat(mine): Generate objects UUIDs and start team timers on game start
124-
"emoji" to Prompt("Emoji",
125-
"Prompt for commit message in the conventional commit convention with GitMoji convention.",
126-
"Write a clean and comprehensive commit message in the conventional commit convention. " +
127-
"I'll send you an output of 'git diff --staged' command, and you convert " +
128-
"it into a commit message. " +
129-
"Use GitMoji convention to preface the commit. " +
130-
"Do NOT add any descriptions to the commit, only commit message. " +
131-
"Use the present tense. " +
132-
"Lines must not be longer than 74 characters. " +
133-
"Use {locale} language to answer.\n" +
134-
"{diff}",
135-
false)
109+
// Generate UUIDs for game objects in Mine.py and call the function in start_game().
110+
"basic" to Prompt(
111+
"Basic",
112+
"Basic prompt that generates a decent commit message.",
113+
"Write an insightful but concise Git commit message in a complete sentence in present tense for the " +
114+
"following diff without prefacing it with anything, the response must be in the language {locale} and must " +
115+
"NOT be longer than 74 characters. The sent text will be the differences between files, where deleted lines" +
116+
" are prefixed with a single minus sign and added lines are prefixed with a single plus sign.\n" +
117+
"{diff}",
118+
false
119+
),
120+
// feat: generate unique UUIDs for game objects on Mine game start
121+
"conventional" to Prompt(
122+
"Conventional",
123+
"Prompt for commit message in the conventional commit convention.",
124+
"Write a commit message in the conventional commit convention. I'll send you an output " +
125+
"of 'git diff --staged' command, and you convert it into a commit message. " +
126+
"Lines must not be longer than 74 characters. Use {locale} language to answer. " +
127+
"End commit title with issue number if you can get it from the branch name: " +
128+
"{branch} in parenthesis.\n" +
129+
"{diff}",
130+
false
131+
),
132+
// ✨ feat(mine): Generate objects UUIDs and start team timers on game start
133+
"emoji" to Prompt(
134+
"Emoji",
135+
"Prompt for commit message in the conventional commit convention with GitMoji convention.",
136+
"Write a clean and comprehensive commit message in the conventional commit convention. " +
137+
"I'll send you an output of 'git diff --staged' command, and you convert " +
138+
"it into a commit message. " +
139+
"Use GitMoji convention to preface the commit. " +
140+
"Do NOT add any descriptions to the commit, only commit message. " +
141+
"Use the present tense. " +
142+
"Lines must not be longer than 74 characters. " +
143+
"Use {locale} language to answer.\n" +
144+
"{diff}",
145+
false
146+
)
136147
)
137148

138149
class LocaleConverter : Converter<Locale>() {

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package com.github.blarc.ai.commits.intellij.plugin.settings
22

33
import com.aallam.openai.api.exception.OpenAIAPIException
4-
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
4+
import com.github.blarc.ai.commits.intellij.plugin.*
55
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
6-
import com.github.blarc.ai.commits.intellij.plugin.OpenAIService
7-
import com.github.blarc.ai.commits.intellij.plugin.emptyText
86
import com.github.blarc.ai.commits.intellij.plugin.settings.prompt.Prompt
97
import com.github.blarc.ai.commits.intellij.plugin.settings.prompt.PromptTable
10-
import com.github.blarc.ai.commits.intellij.plugin.temperatureValid
118
import com.intellij.icons.AllIcons
129
import com.intellij.openapi.options.BoundConfigurable
1310
import com.intellij.openapi.progress.runBackgroundableTask
@@ -30,6 +27,7 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
3027
private val tokenPasswordField = JBPasswordField()
3128
private val verifyLabel = JBLabel()
3229
private val proxyTextField = JBTextField()
30+
private val socketTimeoutTextField = JBTextField()
3331
private var modelComboBox = ComboBox<String>()
3432
private val promptTable = PromptTable()
3533
private lateinit var toolbarDecorator: ToolbarDecorator
@@ -64,6 +62,15 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
6462
row {
6563
comment(message("settings.openAIProxyComment"))
6664
}
65+
row {
66+
label(message("settings.openAISocketTimeout")).widthGroup("label")
67+
cell(socketTimeoutTextField)
68+
.bindText(AppSettings.instance::openAISocketTimeout)
69+
.applyToComponent { minimumWidth = 400 }
70+
.resizableColumn()
71+
.widthGroup("input")
72+
.validationOnInput { isInt(it.text) }
73+
}
6774
row {
6875
label(message("settings.openAIToken"))
6976
.widthGroup("label")
@@ -218,12 +225,15 @@ class AppSettingsConfigurable : BoundConfigurable(message("settings.general.grou
218225

219226
GlobalScope.launch(Dispatchers.IO) {
220227
try {
221-
OpenAIService.instance.verifyOpenAIConfiguration(hostComboBox.item, String(tokenPasswordField.password), proxyTextField.text)
228+
OpenAIService.instance.verifyOpenAIConfiguration(hostComboBox.item, String(tokenPasswordField.password), proxyTextField.text, socketTimeoutTextField.text)
222229
verifyLabel.text = message("settings.verify.valid")
223230
verifyLabel.icon = AllIcons.General.InspectionsOK
224231
} catch (e: OpenAIAPIException) {
225232
verifyLabel.text = message("settings.verify.invalid", e.statusCode)
226233
verifyLabel.icon = AllIcons.General.InspectionsError
234+
} catch (e: NumberFormatException) {
235+
verifyLabel.text = message("settings.verify.invalid", e.localizedMessage)
236+
verifyLabel.icon = AllIcons.General.InspectionsError
227237
} catch (e: Exception) {
228238
verifyLabel.text = message("settings.verify.invalid", "Unknown")
229239
verifyLabel.icon = AllIcons.General.InspectionsError

src/main/resources/messages/MyBundle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ settings.openAITokenExample=sk-ABCdefgHIjKlxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
77
settings.locale=Locale
88
settings.prompt=Prompt
99
settings.openAIModel=OpenAI model
10+
settings.openAISocketTimeout=OpenAI socket timeout
1011
settings.openAITemperature=OpenAI temperature
1112
settings.openAITokenComment=\
1213
<p>You can get your token <a href="https://platform.openai.com/account/api-keys">here.</a/></p>
@@ -44,7 +45,7 @@ settings.addPrompt=Add prompt
4445
settings.prompt.name=Name
4546
settings.prompt.content=Content
4647
validation.required=This value is required.
47-
validation.number=Value is not a number.
48+
validation.integer=Value should be an integer.
4849
validation.temperature=Temperature should be between 0 and 2.
4950
settings.prompt.comment=You can use variables {locale}, {diff} and {branch} to customise your prompt. Prompt preview shows only the first 10000 characters.
5051
actions.update=Update

0 commit comments

Comments
 (0)