Skip to content

Commit 14f3254

Browse files
sisvebosweljacarlrobertoh
authored
feat: code completion for "Custom OpenAI Service" (#476)
* Add code completion setting states for custom service * Add settings for code completion in Custom OpenAI service * Move code completion section to the bottom * Create test testFetchCodeCompletionCustomService * Add Custom OpenAI to the "Enable/Disable Completion" actions * New configuration UI separating /v1/chat/completions from /v1/completions * Code completion for Custom Service * Formatting fixes * Move prefix and suffix to templates in body * Message updates * New tabbed UI for Chat and Code Completions * convert to kotlin, improve ui and other minor changes * fix test connection for chat completions * add help tooltips * allow backward compatibility * support prefix and suffix placeholders * fix initial state loading --------- Co-authored-by: Jack Boswell (boswelja) <boswelja@outlook.com> Co-authored-by: Carl-Robert Linnupuu <carlrobertoh@gmail.com>
1 parent c8181a6 commit 14f3254

26 files changed

+1001
-592
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Receive single-line or whole-function autocomplete suggestions as you type.
5050

5151
![Code Completions](https://github.com/carlrobertoh/CodeGPT-docs/blob/main/images/new/inline-completion.png?raw=true)
5252

53-
> **Note**: Currently supported only on GPT-3.5 and locally-hosted models.
53+
> **Note**: Currently only supported with OpenAI, Custom OpenAI, or LLaMA.
5454
5555
### Chat (with Vision)
5656

src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestProvider.java

+22-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey.CUSTOM_SERVICE_API_KEY;
55
import static ee.carlrobert.codegpt.util.file.FileUtil.getResourceContent;
66
import static java.lang.String.format;
7+
import static java.util.Objects.requireNonNull;
78
import static java.util.stream.Collectors.joining;
89
import static java.util.stream.Collectors.toList;
910

@@ -23,8 +24,9 @@
2324
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings;
2425
import ee.carlrobert.codegpt.settings.service.ServiceType;
2526
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
27+
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceChatCompletionSettingsState;
2628
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings;
27-
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettingsState;
29+
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceState;
2830
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
2931
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
3032
import ee.carlrobert.codegpt.settings.service.you.YouSettings;
@@ -114,16 +116,27 @@ public static OpenAIChatCompletionRequest buildOpenAILookupCompletionRequest(Str
114116

115117
public static Request buildCustomOpenAICompletionRequest(String system, String context) {
116118
return buildCustomOpenAIChatCompletionRequest(
117-
CustomServiceSettings.getCurrentState(),
119+
ApplicationManager.getApplication().getService(CustomServiceState.class)
120+
.getChatCompletionSettings(),
118121
List.of(
119122
new OpenAIChatCompletionStandardMessage("system", system),
120123
new OpenAIChatCompletionStandardMessage("user", context)),
121124
true);
122125
}
123126

127+
public static Request buildCustomOpenAICompletionRequest(String input) {
128+
return buildCustomOpenAIChatCompletionRequest(
129+
ApplicationManager.getApplication().getService(CustomServiceSettings.class)
130+
.getState()
131+
.getChatCompletionSettings(),
132+
List.of(new OpenAIChatCompletionStandardMessage("user", input)),
133+
true);
134+
}
135+
124136
public static Request buildCustomOpenAILookupCompletionRequest(String context) {
125137
return buildCustomOpenAIChatCompletionRequest(
126-
CustomServiceSettings.getCurrentState(),
138+
ApplicationManager.getApplication().getService(CustomServiceState.class)
139+
.getChatCompletionSettings(),
127140
List.of(
128141
new OpenAIChatCompletionStandardMessage(
129142
"system",
@@ -199,29 +212,29 @@ public OpenAIChatCompletionRequest buildOpenAIChatCompletionRequest(
199212
}
200213

201214
public Request buildCustomOpenAIChatCompletionRequest(
202-
CustomServiceSettingsState customConfiguration,
215+
CustomServiceChatCompletionSettingsState settings,
203216
CallParameters callParameters) {
204217
return buildCustomOpenAIChatCompletionRequest(
205-
customConfiguration,
218+
settings,
206219
buildMessages(callParameters),
207220
true);
208221
}
209222

210223
private static Request buildCustomOpenAIChatCompletionRequest(
211-
CustomServiceSettingsState customConfiguration,
224+
CustomServiceChatCompletionSettingsState settings,
212225
List<OpenAIChatCompletionMessage> messages,
213226
boolean streamRequest) {
214-
var requestBuilder = new Request.Builder().url(customConfiguration.getUrl().trim());
227+
var requestBuilder = new Request.Builder().url(requireNonNull(settings.getUrl()).trim());
215228
var credential = CredentialsStore.INSTANCE.getCredential(CUSTOM_SERVICE_API_KEY);
216-
for (var entry : customConfiguration.getHeaders().entrySet()) {
229+
for (var entry : settings.getHeaders().entrySet()) {
217230
String value = entry.getValue();
218231
if (credential != null && value.contains("$CUSTOM_SERVICE_API_KEY")) {
219232
value = value.replace("$CUSTOM_SERVICE_API_KEY", credential);
220233
}
221234
requestBuilder.addHeader(entry.getKey(), value);
222235
}
223236

224-
var body = customConfiguration.getBody().entrySet().stream()
237+
var body = settings.getBody().entrySet().stream()
225238
.collect(Collectors.toMap(
226239
Map.Entry::getKey,
227240
entry -> {

src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestService.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import ee.carlrobert.llm.client.anthropic.completion.ClaudeCompletionStandardMessage;
3030
import ee.carlrobert.llm.client.llama.completion.LlamaCompletionRequest;
3131
import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionEventSourceListener;
32+
import ee.carlrobert.llm.client.openai.completion.OpenAITextCompletionEventSourceListener;
3233
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionRequest;
3334
import ee.carlrobert.llm.client.openai.completion.request.OpenAIChatCompletionStandardMessage;
3435
import ee.carlrobert.llm.client.openai.completion.response.OpenAIChatCompletionResponse;
@@ -55,6 +56,15 @@ public static CompletionRequestService getInstance() {
5556
return ApplicationManager.getApplication().getService(CompletionRequestService.class);
5657
}
5758

59+
public EventSource getCustomOpenAICompletionAsync(
60+
Request customRequest,
61+
CompletionEventListener<String> eventListener) {
62+
var httpClient = CompletionClientProvider.getDefaultClientBuilder().build();
63+
return EventSources.createFactory(httpClient).newEventSource(
64+
customRequest,
65+
new OpenAITextCompletionEventSourceListener(eventListener));
66+
}
67+
5868
public EventSource getCustomOpenAIChatCompletionAsync(
5969
Request customRequest,
6070
CompletionEventListener<String> eventListener) {
@@ -76,7 +86,10 @@ public EventSource getChatCompletionAsync(
7686
eventListener);
7787
case CUSTOM_OPENAI -> getCustomOpenAIChatCompletionAsync(
7888
requestProvider.buildCustomOpenAIChatCompletionRequest(
79-
CustomServiceSettings.getCurrentState(),
89+
ApplicationManager.getApplication()
90+
.getService(CustomServiceSettings.class)
91+
.getState()
92+
.getChatCompletionSettings(),
8093
callParameters),
8194
eventListener);
8295
case ANTHROPIC -> CompletionClientProvider.getClaudeClient().getCompletionAsync(
@@ -99,14 +112,18 @@ public EventSource getChatCompletionAsync(
99112
public EventSource getCodeCompletionAsync(
100113
InfillRequestDetails requestDetails,
101114
CompletionEventListener<String> eventListener) {
115+
var httpClient = CompletionClientProvider.getDefaultClientBuilder().build();
102116
return switch (GeneralSettings.getCurrentState().getSelectedService()) {
103117
case OPENAI -> CompletionClientProvider.getOpenAIClient()
104118
.getCompletionAsync(
105-
CodeCompletionRequestFactory.INSTANCE.buildOpenAIRequest(requestDetails),
119+
CodeCompletionRequestFactory.buildOpenAIRequest(requestDetails),
106120
eventListener);
121+
case CUSTOM_OPENAI -> EventSources.createFactory(httpClient).newEventSource(
122+
CodeCompletionRequestFactory.buildCustomRequest(requestDetails),
123+
new OpenAITextCompletionEventSourceListener(eventListener));
107124
case LLAMA_CPP -> CompletionClientProvider.getLlamaClient()
108125
.getChatCompletionAsync(
109-
CodeCompletionRequestFactory.INSTANCE.buildLlamaRequest(requestDetails),
126+
CodeCompletionRequestFactory.buildLlamaRequest(requestDetails),
110127
eventListener);
111128
default ->
112129
throw new IllegalArgumentException("Code completion not supported for selected service");

src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsComponent.java

+63-15
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,18 @@
1212
import com.intellij.ui.components.JBTextField;
1313
import com.intellij.util.ui.FormBuilder;
1414
import ee.carlrobert.codegpt.CodeGPTBundle;
15-
import ee.carlrobert.codegpt.settings.service.ServiceSelectionForm;
1615
import ee.carlrobert.codegpt.settings.service.ServiceType;
16+
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettings;
17+
import ee.carlrobert.codegpt.settings.service.anthropic.AnthropicSettingsForm;
18+
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
19+
import ee.carlrobert.codegpt.settings.service.azure.AzureSettingsForm;
20+
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceForm;
21+
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
22+
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm;
23+
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
24+
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettingsForm;
25+
import ee.carlrobert.codegpt.settings.service.you.YouSettings;
26+
import ee.carlrobert.codegpt.settings.service.you.YouSettingsForm;
1727
import java.awt.CardLayout;
1828
import java.awt.Component;
1929
import java.awt.Container;
@@ -29,21 +39,30 @@ public class GeneralSettingsComponent {
2939
private final JPanel mainPanel;
3040
private final JBTextField displayNameField;
3141
private final ComboBox<ServiceType> serviceComboBox;
32-
private final ServiceSelectionForm serviceSelectionForm;
42+
private final OpenAISettingsForm openAISettingsForm;
43+
private final CustomServiceForm customConfigurationSettingsForm;
44+
private final AnthropicSettingsForm anthropicSettingsForm;
45+
private final AzureSettingsForm azureSettingsForm;
46+
private final YouSettingsForm youSettingsForm;
47+
private final LlamaSettingsForm llamaSettingsForm;
3348

3449
public GeneralSettingsComponent(Disposable parentDisposable, GeneralSettings settings) {
3550
displayNameField = new JBTextField(settings.getState().getDisplayName(), 20);
36-
serviceSelectionForm = new ServiceSelectionForm(parentDisposable);
51+
openAISettingsForm = new OpenAISettingsForm(OpenAISettings.getCurrentState());
52+
customConfigurationSettingsForm = new CustomServiceForm();
53+
anthropicSettingsForm = new AnthropicSettingsForm(AnthropicSettings.getCurrentState());
54+
azureSettingsForm = new AzureSettingsForm(AzureSettings.getCurrentState());
55+
youSettingsForm = new YouSettingsForm(YouSettings.getCurrentState(), parentDisposable);
56+
llamaSettingsForm = new LlamaSettingsForm(LlamaSettings.getCurrentState());
57+
3758
var cardLayout = new DynamicCardLayout();
3859
var cards = new JPanel(cardLayout);
39-
cards.add(serviceSelectionForm.getOpenAISettingsForm().getForm(), OPENAI.getCode());
40-
cards.add(
41-
serviceSelectionForm.getCustomConfigurationSettingsForm().getForm(),
42-
CUSTOM_OPENAI.getCode());
43-
cards.add(serviceSelectionForm.getAnthropicSettingsForm().getForm(), ANTHROPIC.getCode());
44-
cards.add(serviceSelectionForm.getAzureSettingsForm().getForm(), AZURE.getCode());
45-
cards.add(serviceSelectionForm.getYouSettingsForm(), YOU.getCode());
46-
cards.add(serviceSelectionForm.getLlamaSettingsForm(), LLAMA_CPP.getCode());
60+
cards.add(openAISettingsForm.getForm(), OPENAI.getCode());
61+
cards.add(customConfigurationSettingsForm.getForm(), CUSTOM_OPENAI.getCode());
62+
cards.add(anthropicSettingsForm.getForm(), ANTHROPIC.getCode());
63+
cards.add(azureSettingsForm.getForm(), AZURE.getCode());
64+
cards.add(youSettingsForm, YOU.getCode());
65+
cards.add(llamaSettingsForm, LLAMA_CPP.getCode());
4766
var serviceComboBoxModel = new DefaultComboBoxModel<ServiceType>();
4867
serviceComboBoxModel.addAll(Arrays.stream(ServiceType.values()).toList());
4968
serviceComboBox = new ComboBox<>(serviceComboBoxModel);
@@ -63,6 +82,30 @@ public GeneralSettingsComponent(Disposable parentDisposable, GeneralSettings set
6382
.getPanel();
6483
}
6584

85+
public OpenAISettingsForm getOpenAISettingsForm() {
86+
return openAISettingsForm;
87+
}
88+
89+
public CustomServiceForm getCustomConfigurationSettingsForm() {
90+
return customConfigurationSettingsForm;
91+
}
92+
93+
public AnthropicSettingsForm getAnthropicSettingsForm() {
94+
return anthropicSettingsForm;
95+
}
96+
97+
public AzureSettingsForm getAzureSettingsForm() {
98+
return azureSettingsForm;
99+
}
100+
101+
public LlamaSettingsForm getLlamaSettingsForm() {
102+
return llamaSettingsForm;
103+
}
104+
105+
public YouSettingsForm getYouSettingsForm() {
106+
return youSettingsForm;
107+
}
108+
66109
public ServiceType getSelectedService() {
67110
return serviceComboBox.getItem();
68111
}
@@ -79,10 +122,6 @@ public JComponent getPreferredFocusedComponent() {
79122
return displayNameField;
80123
}
81124

82-
public ServiceSelectionForm getServiceSelectionForm() {
83-
return serviceSelectionForm;
84-
}
85-
86125
public String getDisplayName() {
87126
return displayNameField.getText();
88127
}
@@ -91,6 +130,15 @@ public void setDisplayName(String displayName) {
91130
displayNameField.setText(displayName);
92131
}
93132

133+
public void resetForms() {
134+
openAISettingsForm.resetForm();
135+
customConfigurationSettingsForm.resetForm();
136+
anthropicSettingsForm.resetForm();
137+
azureSettingsForm.resetForm();
138+
youSettingsForm.resetForm();
139+
llamaSettingsForm.resetForm();
140+
}
141+
94142
static class DynamicCardLayout extends CardLayout {
95143

96144
@Override

src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsConfigurable.java

+15-19
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import ee.carlrobert.codegpt.settings.service.azure.AzureSettings;
1919
import ee.carlrobert.codegpt.settings.service.azure.AzureSettingsForm;
2020
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceForm;
21-
import ee.carlrobert.codegpt.settings.service.custom.CustomServiceSettings;
2221
import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings;
2322
import ee.carlrobert.codegpt.settings.service.llama.form.LlamaSettingsForm;
2423
import ee.carlrobert.codegpt.settings.service.openai.OpenAISettings;
@@ -61,17 +60,15 @@ public JComponent createComponent() {
6160
@Override
6261
public boolean isModified() {
6362
var settings = GeneralSettings.getCurrentState();
64-
var serviceSelectionForm = component.getServiceSelectionForm();
63+
6564
return !component.getDisplayName().equals(settings.getDisplayName())
6665
|| component.getSelectedService() != settings.getSelectedService()
67-
|| OpenAISettings.getInstance().isModified(serviceSelectionForm.getOpenAISettingsForm())
68-
|| CustomServiceSettings.getInstance()
69-
.isModified(serviceSelectionForm.getCustomConfigurationSettingsForm())
70-
|| AnthropicSettings.getInstance()
71-
.isModified(serviceSelectionForm.getAnthropicSettingsForm())
72-
|| AzureSettings.getInstance().isModified(serviceSelectionForm.getAzureSettingsForm())
73-
|| YouSettings.getInstance().isModified(serviceSelectionForm.getYouSettingsForm())
74-
|| LlamaSettings.getInstance().isModified(serviceSelectionForm.getLlamaSettingsForm());
66+
|| OpenAISettings.getInstance().isModified(component.getOpenAISettingsForm())
67+
|| component.getCustomConfigurationSettingsForm().isModified()
68+
|| AnthropicSettings.getInstance().isModified(component.getAnthropicSettingsForm())
69+
|| AzureSettings.getInstance().isModified(component.getAzureSettingsForm())
70+
|| YouSettings.getInstance().isModified(component.getYouSettingsForm())
71+
|| LlamaSettings.getInstance().isModified(component.getLlamaSettingsForm());
7572
}
7673

7774
@Override
@@ -80,14 +77,13 @@ public void apply() {
8077
settings.setDisplayName(component.getDisplayName());
8178
settings.setSelectedService(component.getSelectedService());
8279

83-
var serviceSelectionForm = component.getServiceSelectionForm();
84-
var openAISettingsForm = serviceSelectionForm.getOpenAISettingsForm();
80+
var openAISettingsForm = component.getOpenAISettingsForm();
8581
applyOpenAISettings(openAISettingsForm);
86-
applyCustomOpenAISettings(serviceSelectionForm.getCustomConfigurationSettingsForm());
87-
applyAnthropicSettings(serviceSelectionForm.getAnthropicSettingsForm());
88-
applyAzureSettings(serviceSelectionForm.getAzureSettingsForm());
89-
applyYouSettings(serviceSelectionForm.getYouSettingsForm());
90-
applyLlamaSettings(serviceSelectionForm.getLlamaSettingsForm());
82+
applyCustomOpenAISettings(component.getCustomConfigurationSettingsForm());
83+
applyAnthropicSettings(component.getAnthropicSettingsForm());
84+
applyAzureSettings(component.getAzureSettingsForm());
85+
applyYouSettings(component.getYouSettingsForm());
86+
applyLlamaSettings(component.getLlamaSettingsForm());
9187

9288
var serviceChanged = component.getSelectedService() != settings.getSelectedService();
9389
var modelChanged = !OpenAISettings.getCurrentState().getModel()
@@ -109,7 +105,7 @@ private void applyOpenAISettings(OpenAISettingsForm form) {
109105

110106
private void applyCustomOpenAISettings(CustomServiceForm form) {
111107
CredentialsStore.INSTANCE.setCredential(CUSTOM_SERVICE_API_KEY, form.getApiKey());
112-
CustomServiceSettings.getInstance().loadState(form.getCurrentState());
108+
form.applyChanges();
113109
}
114110

115111
private void applyLlamaSettings(LlamaSettingsForm form) {
@@ -142,7 +138,7 @@ public void reset() {
142138
var settings = GeneralSettings.getCurrentState();
143139
component.setDisplayName(settings.getDisplayName());
144140
component.setSelectedService(settings.getSelectedService());
145-
component.getServiceSelectionForm().resetForms();
141+
component.resetForms();
146142
}
147143

148144
@Override

0 commit comments

Comments
 (0)