diff --git a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/ollama/OllamaSettingsForm.kt b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/ollama/OllamaSettingsForm.kt index 3631a059..348e3046 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/settings/service/ollama/OllamaSettingsForm.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/settings/service/ollama/OllamaSettingsForm.kt @@ -1,8 +1,8 @@ package ee.carlrobert.codegpt.settings.service.ollama import com.intellij.notification.NotificationType -import com.intellij.openapi.application.invokeLater -import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.application.ReadAction import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.observable.util.whenTextChangedFromUi @@ -11,6 +11,7 @@ import com.intellij.openapi.ui.MessageType import com.intellij.ui.TitledSeparator import com.intellij.ui.components.JBPasswordField import com.intellij.ui.components.JBTextField +import com.intellij.util.concurrency.AppExecutorUtil import com.intellij.util.ui.FormBuilder import ee.carlrobert.codegpt.CodeGPTBundle import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.OLLAMA_API_KEY @@ -26,6 +27,7 @@ import kotlinx.coroutines.runBlocking import java.awt.BorderLayout import java.lang.String.format import java.net.ConnectException +import java.util.concurrent.TimeoutException import javax.swing.ComboBoxModel import javax.swing.DefaultComboBoxModel import javax.swing.JButton @@ -62,7 +64,9 @@ class OllamaSettingsForm { modelComboBox.isEnabled = false } } - refreshModelsButton.addActionListener { refreshModels(getModel() ?: settings.model) } + refreshModelsButton.addActionListener { + refreshModels(getModel() ?: settings.model) + } apiKeyField = JBPasswordField().apply { columns = 30 text = runBlocking(Dispatchers.IO) { @@ -140,10 +144,10 @@ class OllamaSettingsForm { || getApiKey() != getCredential(OLLAMA_API_KEY) } - fun refreshModels(currentModel: String?) { + private fun refreshModels(currentModel: String?) { disableModelComboBoxWithPlaceholder(DefaultComboBoxModel(arrayOf("Loading"))) - try { - val models = runBlocking(Dispatchers.IO) { + ReadAction.nonBlocking> { + try { OllamaClient.Builder() .setHost(hostField.text) .setApiKey(getApiKey()) @@ -151,52 +155,62 @@ class OllamaSettingsForm { .modelTags .models .map { it.name } + } catch (t: Throwable) { + handleModelLoadingError(t) + throw t } - service().state.availableModels = models.toMutableList() - runInEdt { - modelComboBox.apply { - if (models.isNotEmpty()) { - model = DefaultComboBoxModel(models.toTypedArray()) - currentModel?.let { - if (models.contains(currentModel)) { - selectedItem = currentModel - } else { - OverlayUtil.showBalloon( - format( - CodeGPTBundle.get("validation.error.model.notExists"), - currentModel - ), - MessageType.ERROR, - modelComboBox - ) - } - } - isEnabled = true - } else { - model = DefaultComboBoxModel(arrayOf("No models")) - } + } + .finishOnUiThread(ModalityState.defaultModalityState()) { models -> + updateModelComboBoxState(models, currentModel) + } + .submit(AppExecutorUtil.getAppExecutorService()) + } + + private fun updateModelComboBoxState(models: List, currentModel: String?) { + if (models.isNotEmpty()) { + modelComboBox.model = DefaultComboBoxModel(models.toTypedArray()) + modelComboBox.isEnabled = true + currentModel?.let { + if (models.contains(currentModel)) { + modelComboBox.selectedItem = currentModel + } else { + OverlayUtil.showBalloon( + format( + CodeGPTBundle.get("validation.error.model.notExists"), + currentModel + ), + MessageType.ERROR, + modelComboBox + ) } } - } catch (ex: RuntimeException) { - logger.error(ex) - if (ex.cause is ConnectException) { - OverlayUtil.showNotification( - "Unable to connect to Ollama server", - NotificationType.ERROR - ) - } else { - OverlayUtil.showNotification(ex.message ?: "Error", NotificationType.ERROR) - } - disableModelComboBoxWithPlaceholder(DefaultComboBoxModel(arrayOf("Unable to load models"))) + } else { + modelComboBox.model = DefaultComboBoxModel(arrayOf("No models")) } } + private fun handleModelLoadingError(ex: Throwable) { + logger.error(ex) + when (ex) { + is TimeoutException -> OverlayUtil.showNotification( + "Connection to Ollama server timed out", + NotificationType.ERROR + ) + + is ConnectException -> OverlayUtil.showNotification( + "Unable to connect to Ollama server", + NotificationType.ERROR + ) + + else -> OverlayUtil.showNotification(ex.message ?: "Error", NotificationType.ERROR) + } + disableModelComboBoxWithPlaceholder(DefaultComboBoxModel(arrayOf("Unable to load models"))) + } + private fun disableModelComboBoxWithPlaceholder(placeholderModel: ComboBoxModel) { - invokeLater { - modelComboBox.apply { - model = placeholderModel - isEnabled = false - } + modelComboBox.apply { + model = placeholderModel + isEnabled = false } } }