Skip to content

Add separate model selection for code completion #1032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/main/java/ee/carlrobert/codegpt/settings/GeneralSettings.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package ee.carlrobert.codegpt.settings;

import static ee.carlrobert.codegpt.settings.service.ModelRole.CHAT_ROLE;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
Expand All @@ -8,6 +10,7 @@
import ee.carlrobert.codegpt.completions.HuggingFaceModel;
import ee.carlrobert.codegpt.completions.llama.LlamaModel;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.settings.service.ModelRole;
import ee.carlrobert.codegpt.settings.service.ProviderChangeNotifier;
import ee.carlrobert.codegpt.settings.service.ServiceType;
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
Expand Down Expand Up @@ -45,13 +48,21 @@ public static GeneralSettings getInstance() {
}

public static ServiceType getSelectedService() {
return getCurrentState().getSelectedService();
return getCurrentState().getSelectedService(CHAT_ROLE);
}

public static ServiceType getSelectedService(ModelRole role) {
return getCurrentState().getSelectedService(role);
}

public static boolean isSelected(ServiceType serviceType) {
return getSelectedService() == serviceType;
}

public static boolean isSelected(ModelRole role, ServiceType serviceType) {
return getSelectedService(role) == serviceType;
}

public void sync(Conversation conversation) {
var project = ApplicationUtil.findCurrentProject();
var provider = ServiceType.fromClientCode(conversation.getClientCode());
Expand Down Expand Up @@ -94,7 +105,7 @@ public void sync(Conversation conversation) {
default:
break;
}
state.setSelectedService(provider);
state.setSelectedService(CHAT_ROLE, provider);
if (project != null) {
project.getMessageBus()
.syncPublisher(ProviderChangeNotifier.getTOPIC())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package ee.carlrobert.codegpt.settings;

import static ee.carlrobert.codegpt.settings.service.ModelRole.CHAT_ROLE;
import static ee.carlrobert.codegpt.settings.service.ModelRole.CODECOMPLETION_ROLE;

import com.intellij.openapi.application.ApplicationManager;
import ee.carlrobert.codegpt.settings.service.ModelRole;
import ee.carlrobert.codegpt.settings.service.ProviderChangeNotifier;
import ee.carlrobert.codegpt.settings.service.ServiceType;

Expand All @@ -9,6 +13,7 @@ public class GeneralSettingsState {
private String displayName = "";
private String avatarBase64 = "";
private ServiceType selectedService = ServiceType.CODEGPT;
private ServiceType codeCompletionService = ServiceType.CODEGPT;

public String getDisplayName() {
if (displayName == null || displayName.isEmpty()) {
Expand All @@ -33,16 +38,52 @@ public void setAvatarBase64(String avatarBase64) {
this.avatarBase64 = avatarBase64;
}

public ServiceType getSelectedService(ModelRole role) {
switch (role) {
case CHAT_ROLE -> {
return selectedService;
}
case CODECOMPLETION_ROLE -> {
return codeCompletionService;
}
default -> {
throw new AssertionError();
}
}
}

public ServiceType getSelectedService() {
return selectedService;
return getSelectedService(CHAT_ROLE);
}

public ServiceType getSelectedCodeCompletionService() {
return getSelectedService(CODECOMPLETION_ROLE);
}

public void setSelectedService(ModelRole role, ServiceType selectedService) {
switch (role) {
case CHAT_ROLE -> {
this.selectedService = selectedService;

ApplicationManager.getApplication()
.getMessageBus()
.syncPublisher(ProviderChangeNotifier.getTOPIC())
.providerChanged(selectedService);
}
case CODECOMPLETION_ROLE -> {
this.codeCompletionService = selectedService;
}
default -> {
throw new AssertionError();
}
}
}

public void setSelectedService(ServiceType selectedService) {
this.selectedService = selectedService;
setSelectedService(CHAT_ROLE, selectedService);
}

ApplicationManager.getApplication()
.getMessageBus()
.syncPublisher(ProviderChangeNotifier.getTOPIC())
.providerChanged(selectedService);
public void setSelectedCodeCompletionService(ServiceType selectedService) {
setSelectedService(CODECOMPLETION_ROLE, selectedService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package ee.carlrobert.codegpt.settings.service;

public enum ModelRole {
CHAT_ROLE,
CODECOMPLETION_ROLE
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.project.DumbAwareAction
import ee.carlrobert.codegpt.codecompletions.CodeCompletionService
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ModelRole.CODECOMPLETION_ROLE
import ee.carlrobert.codegpt.settings.service.ServiceType.*
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServicesSettings
Expand All @@ -17,7 +18,7 @@ abstract class CodeCompletionFeatureToggleActions(
private val enableFeatureAction: Boolean
) : DumbAwareAction() {

override fun actionPerformed(e: AnActionEvent) = when (GeneralSettings.getSelectedService()) {
override fun actionPerformed(e: AnActionEvent) = when (GeneralSettings.getSelectedService(CODECOMPLETION_ROLE)) {
CODEGPT -> service<CodeGPTServiceSettings>().state.codeCompletionSettings::codeCompletionsEnabled::set

OPENAI -> OpenAISettings.getCurrentState()::setCodeCompletionsEnabled
Expand All @@ -34,7 +35,7 @@ abstract class CodeCompletionFeatureToggleActions(
}(enableFeatureAction)

override fun update(e: AnActionEvent) {
val selectedService = GeneralSettings.getSelectedService()
val selectedService = GeneralSettings.getSelectedService(CODECOMPLETION_ROLE)
val codeCompletionEnabled =
e.project?.service<CodeCompletionService>()?.isCodeCompletionsEnabled(selectedService)
?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ee.carlrobert.codegpt.CodeGPTKeys
import ee.carlrobert.codegpt.codecompletions.edit.GrpcClientService
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
import ee.carlrobert.codegpt.settings.service.ModelRole.CODECOMPLETION_ROLE
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.treesitter.CodeCompletionParserFactory
Expand Down Expand Up @@ -156,7 +157,7 @@ class CodeCompletionEventListener(
}

override fun onError(error: ErrorDetails, ex: Throwable) {
val isCodeGPTService = GeneralSettings.getSelectedService() == ServiceType.CODEGPT
val isCodeGPTService = GeneralSettings.getSelectedService(CODECOMPLETION_ROLE) == ServiceType.CODEGPT
if (isCodeGPTService && "RATE_LIMIT_EXCEEDED" == error.code) {
service<CodeGPTServiceSettings>().state
.codeCompletionSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ object CodeCompletionRequestFactory {
}

return OllamaCompletionRequest.Builder(
settings.model,
settings.codeCompletionModel,
prompt
)
.setSuffix(if (settings.fimOverride) null else details.suffix)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ee.carlrobert.codegpt.codecompletions.edit.GrpcClientService
import ee.carlrobert.codegpt.completions.CompletionClientProvider
import ee.carlrobert.codegpt.completions.llama.LlamaModel
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ModelRole.CODECOMPLETION_ROLE
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.ServiceType.*
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
Expand Down Expand Up @@ -41,12 +42,12 @@ class CodeCompletionService(private val project: Project) {
.getOrDefault("model", null) as String

LLAMA_CPP -> LlamaModel.findByHuggingFaceModel(LlamaSettings.getCurrentState().huggingFaceModel).label
OLLAMA -> service<OllamaSettings>().state.model
OLLAMA -> service<OllamaSettings>().state.codeCompletionModel
else -> null
}
}

fun isCodeCompletionsEnabled(): Boolean = isCodeCompletionsEnabled(GeneralSettings.getSelectedService())
fun isCodeCompletionsEnabled(): Boolean = isCodeCompletionsEnabled(GeneralSettings.getSelectedService(CODECOMPLETION_ROLE))

fun isCodeCompletionsEnabled(selectedService: ServiceType): Boolean =
when (selectedService) {
Expand All @@ -62,7 +63,7 @@ class CodeCompletionService(private val project: Project) {
infillRequest: InfillRequest,
eventListener: CompletionEventListener<String>
): EventSource {
return when (val selectedService = GeneralSettings.getSelectedService()) {
return when (val selectedService = GeneralSettings.getSelectedService(CODECOMPLETION_ROLE)) {
OPENAI -> CompletionClientProvider.getOpenAIClient()
.getCompletionAsync(buildOpenAIRequest(infillRequest), eventListener)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.intellij.platform.workspace.storage.impl.cache.cache
import ee.carlrobert.codegpt.CodeGPTKeys.REMAINING_CODE_COMPLETION
import ee.carlrobert.codegpt.codecompletions.edit.GrpcClientService
import ee.carlrobert.codegpt.settings.GeneralSettings
import ee.carlrobert.codegpt.settings.service.ModelRole.*
import ee.carlrobert.codegpt.settings.service.ServiceType
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.settings.service.custom.CustomServicesSettings
Expand Down Expand Up @@ -74,7 +75,7 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {

var eventListener = CodeCompletionEventListener(request.editor, this)

if (GeneralSettings.getSelectedService() == ServiceType.CODEGPT) {
if (GeneralSettings.getSelectedService(CODECOMPLETION_ROLE) == ServiceType.CODEGPT) {
project.service<GrpcClientService>().getCodeCompletionAsync(eventListener, request, this)
return@channelFlow
}
Expand Down Expand Up @@ -103,7 +104,7 @@ class DebouncedCodeCompletionProvider : DebouncedInlineCompletionProvider() {
}

override fun isEnabled(event: InlineCompletionEvent): Boolean {
val selectedService = GeneralSettings.getSelectedService()
val selectedService = GeneralSettings.getSelectedService(CODECOMPLETION_ROLE)
val codeCompletionsEnabled = when (selectedService) {
ServiceType.CODEGPT -> service<CodeGPTServiceSettings>().state.codeCompletionSettings.codeCompletionsEnabled
ServiceType.OPENAI -> OpenAISettings.getCurrentState().isCodeCompletionsEnabled
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.settings.service

import ee.carlrobert.codegpt.settings.service.ModelRole.*
import com.intellij.openapi.components.service
import com.intellij.openapi.options.Configurable
import ee.carlrobert.codegpt.conversations.ConversationsState
Expand All @@ -23,24 +24,28 @@ class ServiceConfigurable : Configurable {
}

override fun isModified(): Boolean {
return component.getSelectedService() != service<GeneralSettings>().state.selectedService
return component.getSelectedService(CHAT_ROLE) != service<GeneralSettings>().state.getSelectedService(CHAT_ROLE)
|| component.getSelectedService(CODECOMPLETION_ROLE) != service<GeneralSettings>().state.getSelectedService(CODECOMPLETION_ROLE)
}

override fun apply() {
val state = service<GeneralSettings>().state
state.selectedService = component.getSelectedService()
state.setSelectedService(CHAT_ROLE, component.getSelectedService(CHAT_ROLE))

val serviceChanged = component.getSelectedService() != state.selectedService
val serviceChanged = component.getSelectedService(CHAT_ROLE) != state.selectedService
if (serviceChanged) {
resetActiveTab()
TelemetryAction.SETTINGS_CHANGED.createActionMessage()
.property("service", component.getSelectedService().code.lowercase())
.property("service", component.getSelectedService(CHAT_ROLE).code.lowercase())
.send()
}

state.setSelectedService(CODECOMPLETION_ROLE, component.getSelectedService(CODECOMPLETION_ROLE))
}

override fun reset() {
component.setSelectedService(service<GeneralSettings>().state.selectedService)
component.setSelectedService(CHAT_ROLE,service<GeneralSettings>().state.getSelectedService(CHAT_ROLE))
component.setSelectedService(CODECOMPLETION_ROLE,service<GeneralSettings>().state.getSelectedService(CODECOMPLETION_ROLE))
}

private fun resetActiveTab() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.settings.service

import ee.carlrobert.codegpt.settings.service.ModelRole.*
import com.intellij.ide.DataManager
import com.intellij.openapi.components.service
import com.intellij.openapi.options.ex.Settings
Expand All @@ -23,22 +24,36 @@ class ServiceConfigurableComponent {

private var serviceComboBox: ComboBox<ServiceType> =
ComboBox(EnumComboBoxModel(ServiceType::class.java)).apply {
selectedItem = service<GeneralSettings>().state.selectedService
selectedItem = service<GeneralSettings>().state.getSelectedService(CHAT_ROLE)
}
private var codeCompletionServiceComboBox: ComboBox<ServiceType> =
ComboBox(EnumComboBoxModel(ServiceType::class.java)).apply {
selectedItem = service<GeneralSettings>().state.getSelectedService(CODECOMPLETION_ROLE)
}

fun getSelectedService(): ServiceType {
return serviceComboBox.selectedItem as ServiceType
fun getSelectedService(role: ModelRole): ServiceType {
return when(role) {
CHAT_ROLE -> serviceComboBox
CODECOMPLETION_ROLE -> codeCompletionServiceComboBox
}.selectedItem as ServiceType
}

fun setSelectedService(serviceType: ServiceType) {
serviceComboBox.selectedItem = serviceType
fun setSelectedService(role: ModelRole, serviceType: ServiceType) {
when(role) {
CHAT_ROLE -> serviceComboBox
CODECOMPLETION_ROLE -> codeCompletionServiceComboBox
}.selectedItem = serviceType
}

fun getPanel(): JPanel = FormBuilder.createFormBuilder()
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.label"),
serviceComboBox
)
.addLabeledComponent(
CodeGPTBundle.get("settingsConfigurable.service.codeCompletion.label"),
codeCompletionServiceComboBox
)
.addVerticalGap(8)
.addComponent(JBLabel("All available providers that can be used with CodeGPT:"))
.addVerticalGap(8)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class OllamaSettings :
class OllamaSettingsState : BaseState() {
var host by string("http://localhost:11434")
var model by string()
var codeCompletionModel by string()
var codeCompletionsEnabled by property(false)
var fimOverride by property(true)
var fimTemplate by enum<InfillPromptTemplate>(InfillPromptTemplate.CODE_QWEN_2_5)
Expand Down
Loading