From 390d8cdd5ef1a661aa32145d11e25e1181033024 Mon Sep 17 00:00:00 2001 From: Phil <39240633+PhilKes@users.noreply.github.com> Date: Tue, 30 Jan 2024 10:22:22 +0100 Subject: [PATCH] Add setting for custom LLama server executable (#344) --- .../completions/llama/LlamaServerAgent.java | 60 ++++++----- .../llama/LlamaServerStartupParams.java | 24 ++++- .../service/LlamaModelPreferencesForm.java | 18 +--- .../service/LlamaServiceSelectionForm.java | 101 ++++++++++++++++++ .../service/ServiceSelectionForm.java | 16 +++ .../settings/state/LlamaSettingsState.java | 24 +++++ .../java/ee/carlrobert/codegpt/ui/UIUtil.java | 17 +++ .../resources/messages/codegpt.properties | 2 + 8 files changed, 222 insertions(+), 40 deletions(-) diff --git a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java index 345689974..f09a98039 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java @@ -39,15 +39,19 @@ public void startAgent( Runnable onSuccess, Runnable onServerTerminated) { ApplicationManager.getApplication().invokeLater(() -> { - try { - serverProgressPanel.updateText( - CodeGPTBundle.get("llamaServerAgent.buildingProject.description")); - makeProcessHandler = new OSProcessHandler(getMakeCommandLinde()); - makeProcessHandler.addProcessListener( - getMakeProcessListener(params, serverProgressPanel, onSuccess, onServerTerminated)); - makeProcessHandler.startNotify(); - } catch (ExecutionException e) { - throw new RuntimeException(e); + if (!params.isUseCustomServer()) { + try { + serverProgressPanel.updateText( + CodeGPTBundle.get("llamaServerAgent.buildingProject.description")); + makeProcessHandler = new OSProcessHandler(getMakeCommandLinde()); + makeProcessHandler.addProcessListener( + getMakeProcessListener(params, serverProgressPanel, onSuccess, onServerTerminated)); + makeProcessHandler.startNotify(); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } else { + startServer(params, serverProgressPanel, onSuccess, onServerTerminated); } }); } @@ -79,23 +83,31 @@ public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType @Override public void processTerminated(@NotNull ProcessEvent event) { - try { - LOG.info("Booting up llama server"); - - serverProgressPanel.updateText( - CodeGPTBundle.get("llamaServerAgent.serverBootup.description")); - startServerProcessHandler = new OSProcessHandler.Silent(getServerCommandLine(params)); - startServerProcessHandler.addProcessListener( - getProcessListener(params.getPort(), onSuccess, onServerTerminated)); - startServerProcessHandler.startNotify(); - } catch (ExecutionException ex) { - LOG.error("Unable to start llama server", ex); - throw new RuntimeException(ex); - } + startServer(params, serverProgressPanel, onSuccess, onServerTerminated); } }; } + private void startServer( + LlamaServerStartupParams params, + ServerProgressPanel serverProgressPanel, + Runnable onSuccess, + Runnable onServerTerminated) { + try { + LOG.info("Booting up llama server"); + + serverProgressPanel.updateText( + CodeGPTBundle.get("llamaServerAgent.serverBootup.description")); + startServerProcessHandler = new OSProcessHandler.Silent(getServerCommandLine(params)); + startServerProcessHandler.addProcessListener( + getProcessListener(params.getPort(), onSuccess, onServerTerminated)); + startServerProcessHandler.startNotify(); + } catch (ExecutionException ex) { + LOG.error("Unable to start llama server", ex); + throw new RuntimeException(ex); + } + } + private ProcessListener getProcessListener( int port, Runnable onSuccess, @@ -152,8 +164,8 @@ private static GeneralCommandLine getMakeCommandLinde() { private GeneralCommandLine getServerCommandLine(LlamaServerStartupParams params) { GeneralCommandLine commandLine = new GeneralCommandLine().withCharset(StandardCharsets.UTF_8); - commandLine.setExePath("./server"); - commandLine.withWorkDirectory(CodeGPTPlugin.getLlamaSourcePath()); + commandLine.setExePath("./" + params.getServerFileName()); + commandLine.withWorkDirectory(params.getServerDirectory()); commandLine.addParameters( "-m", params.getModelPath(), "-c", String.valueOf(params.getContextLength()), diff --git a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java index 5ea266797..fcb948378 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java @@ -1,9 +1,12 @@ package ee.carlrobert.codegpt.completions.llama; +import java.io.File; import java.util.List; public class LlamaServerStartupParams { + private final String serverPath; + private final boolean useCustomServer; private final String modelPath; private final int contextLength; private final int threads; @@ -11,11 +14,14 @@ public class LlamaServerStartupParams { private final List additionalParameters; public LlamaServerStartupParams( - String modelPath, + String serverPath, + boolean useCustomServer, String modelPath, int contextLength, int threads, int port, List additionalParameters) { + this.serverPath = serverPath; + this.useCustomServer = useCustomServer; this.modelPath = modelPath; this.contextLength = contextLength; this.threads = threads; @@ -23,6 +29,22 @@ public LlamaServerStartupParams( this.additionalParameters = additionalParameters; } + public String getServerPath() { + return serverPath; + } + + public String getServerFileName() { + return serverPath.substring(serverPath.lastIndexOf(File.separator) + 1); + } + + public String getServerDirectory() { + return serverPath.substring(0, serverPath.lastIndexOf(File.separator) + 1); + } + + public boolean isUseCustomServer() { + return useCustomServer; + } + public String getModelPath() { return modelPath; } diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaModelPreferencesForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaModelPreferencesForm.java index d800e96fb..d951fc1ea 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaModelPreferencesForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaModelPreferencesForm.java @@ -1,5 +1,6 @@ package ee.carlrobert.codegpt.settings.service; +import static ee.carlrobert.codegpt.ui.UIUtil.createRadioButtonsPanel; import static java.lang.String.format; import static java.util.stream.Collectors.toList; @@ -151,7 +152,8 @@ public LlamaModelPreferencesForm() { public JPanel getForm() { JPanel finalPanel = new JPanel(new BorderLayout()); - finalPanel.add(createRadioButtonsPanel(), BorderLayout.NORTH); + finalPanel.add(createRadioButtonsPanel(predefinedModelRadioButton, customModelRadioButton), + BorderLayout.NORTH); finalPanel.add(createFormPanelCards(), BorderLayout.CENTER); return finalPanel; } @@ -227,20 +229,6 @@ private JPanel createFormPanelCards() { return formPanelCards; } - private JPanel createRadioButtonsPanel() { - var buttonGroup = new ButtonGroup(); - buttonGroup.add(predefinedModelRadioButton); - buttonGroup.add(customModelRadioButton); - - var radioPanel = new JPanel(); - radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.PAGE_AXIS)); - radioPanel.add(predefinedModelRadioButton); - radioPanel.add(Box.createVerticalStrut(4)); - radioPanel.add(customModelRadioButton); - radioPanel.add(Box.createVerticalStrut(8)); - return radioPanel; - } - private JPanel createCustomModelForm() { var customModelHelpText = ComponentPanelBuilder.createCommentComponent( CodeGPTBundle.get("settingsConfigurable.service.llama.customModelPath.comment"), diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaServiceSelectionForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaServiceSelectionForm.java index 58e6bed87..5b77d8ad3 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaServiceSelectionForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/LlamaServiceSelectionForm.java @@ -1,15 +1,20 @@ package ee.carlrobert.codegpt.settings.service; +import static ee.carlrobert.codegpt.ui.UIUtil.createRadioButtonsPanel; import static java.util.stream.Collectors.toList; import com.intellij.icons.AllIcons.Actions; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.TextBrowseFolderListener; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.panel.ComponentPanelBuilder; import com.intellij.openapi.util.io.FileUtil; import com.intellij.ui.PortField; import com.intellij.ui.TitledSeparator; import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBRadioButton; import com.intellij.ui.components.JBTextField; import com.intellij.ui.components.fields.IntegerField; import com.intellij.util.ui.FormBuilder; @@ -22,6 +27,7 @@ import ee.carlrobert.codegpt.settings.state.LlamaSettingsState; import ee.carlrobert.codegpt.ui.OverlayUtil; import java.awt.BorderLayout; +import java.awt.CardLayout; import java.io.File; import java.util.Arrays; import java.util.Collections; @@ -34,12 +40,19 @@ public class LlamaServiceSelectionForm extends JPanel { + private static final String BUNDLED_SERVER_FORM_CARD_CODE = "BundledServerSettings"; + private static final String CUSTOM_SERVER_FORM_CARD_CODE = "CustomServerSettings"; + private final LlamaModelPreferencesForm llamaModelPreferencesForm; private final LlamaRequestPreferencesForm llamaRequestPreferencesForm; private final PortField portField; private final IntegerField maxTokensField; private final IntegerField threadsField; private final JBTextField additionalParametersField; + private final CardLayout cardLayout; + private final JBRadioButton bundledServerRadioButton; + private final JBRadioButton customServerRadioButton; + private final TextFieldWithBrowseButton browsableCustomServerTextField; public LlamaServiceSelectionForm() { var llamaServerAgent = @@ -65,6 +78,15 @@ public LlamaServiceSelectionForm() { additionalParametersField = new JBTextField(llamaSettings.getAdditionalParameters(), 30); additionalParametersField.setEnabled(!serverRunning); + cardLayout = new CardLayout(); + bundledServerRadioButton = new JBRadioButton("Use bundled server", + !llamaSettings.isUseCustomServer()); + customServerRadioButton = new JBRadioButton("Use custom server", + llamaSettings.isUseCustomServer()); + browsableCustomServerTextField = createBrowsableCustomServerTextField( + !llamaServerAgent.isServerRunning()); + browsableCustomServerTextField.setText(llamaSettings.getCustomLlamaServerPath()); + init(llamaServerAgent); } @@ -89,6 +111,12 @@ private JComponent withEmptyLeftBorder(JComponent component) { return component; } + public String getActualServerPath() { + return isUseCustomServer() + ? getCustomServerPath() + : CodeGPTPlugin.getLlamaSourcePath() + File.separator + "server"; + } + public int getContextSize() { return maxTokensField.getValue(); } @@ -123,6 +151,22 @@ public List getListOfAdditionalParameters() { .collect(toList()); } + public void setIsUseCustomServer(boolean useCustomServer) { + customServerRadioButton.setSelected(useCustomServer); + } + + public boolean isUseCustomServer() { + return customServerRadioButton.isSelected(); + } + + public void setCustomServerPath(String customServerPath) { + browsableCustomServerTextField.setText(customServerPath); + } + + public String getCustomServerPath() { + return browsableCustomServerTextField.getText(); + } + private void init(LlamaServerAgent llamaServerAgent) { var serverProgressPanel = new ServerProgressPanel(); serverProgressPanel.setBorder(JBUI.Borders.emptyRight(16)); @@ -134,6 +178,7 @@ private void init(LlamaServerAgent llamaServerAgent) { .addComponent(new TitledSeparator( CodeGPTBundle.get("settingsConfigurable.service.llama.serverPreferences.title"))) .addComponent(withEmptyLeftBorder(FormBuilder.createFormBuilder() + .addComponent(getForm()) .addLabeledComponent( CodeGPTBundle.get("shared.port"), JBUI.Panels.simplePanel() @@ -165,6 +210,60 @@ private void init(LlamaServerAgent llamaServerAgent) { .getPanel()); } + public JPanel getForm() { + JPanel finalPanel = new JPanel(new BorderLayout()); + finalPanel.add(createRadioButtonsPanel(bundledServerRadioButton, customServerRadioButton), + BorderLayout.NORTH); + finalPanel.add(createFormPanelCards(), BorderLayout.CENTER); + return finalPanel; + } + + private JPanel createFormPanelCards() { + var formPanelCards = new JPanel(cardLayout); + formPanelCards.setBorder(JBUI.Borders.emptyLeft(16)); + formPanelCards.add(new JPanel(), BUNDLED_SERVER_FORM_CARD_CODE); + formPanelCards.add(createCustomServerForm(), CUSTOM_SERVER_FORM_CARD_CODE); + cardLayout.show( + formPanelCards, + bundledServerRadioButton.isSelected() + ? BUNDLED_SERVER_FORM_CARD_CODE + : CUSTOM_SERVER_FORM_CARD_CODE); + + bundledServerRadioButton.addActionListener(e -> + cardLayout.show(formPanelCards, BUNDLED_SERVER_FORM_CARD_CODE)); + customServerRadioButton.addActionListener(e -> + cardLayout.show(formPanelCards, CUSTOM_SERVER_FORM_CARD_CODE)); + + return formPanelCards; + } + + private JPanel createCustomServerForm() { + var customModelHelpText = ComponentPanelBuilder.createCommentComponent( + CodeGPTBundle.get("settingsConfigurable.service.llama.customServerPath.comment"), + true); + customModelHelpText.setBorder(JBUI.Borders.empty(0, 4)); + + return FormBuilder.createFormBuilder() + .addLabeledComponent( + CodeGPTBundle.get("settingsConfigurable.service.llama.customServerPath.label"), + browsableCustomServerTextField) + .addComponentToRightColumn(customModelHelpText) + .addVerticalGap(4) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + private TextFieldWithBrowseButton createBrowsableCustomServerTextField(boolean enabled) { + var browseButton = new TextFieldWithBrowseButton(); + browseButton.setEnabled(enabled); + + var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFileDescriptor(); + fileChooserDescriptor.setForcedToUseIdeaFileChooser(true); + fileChooserDescriptor.setHideIgnored(false); + browseButton.addBrowseFolderListener(new TextBrowseFolderListener(fileChooserDescriptor)); + return browseButton; + } + private JLabel createComment(String messageKey) { var comment = ComponentPanelBuilder.createCommentComponent( CodeGPTBundle.get(messageKey), true); @@ -193,6 +292,8 @@ private JButton getServerButton( disableForm(serverButton, serverProgressPanel); llamaServerAgent.startAgent( new LlamaServerStartupParams( + getActualServerPath(), + isUseCustomServer(), llamaModelPreferencesForm.getActualModelPath(), getContextSize(), getThreads(), diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/ServiceSelectionForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/ServiceSelectionForm.java index dceef24de..3f77d163a 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/ServiceSelectionForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/ServiceSelectionForm.java @@ -432,4 +432,20 @@ public String getAdditionalParameters() { public void setAdditionalParameters(String additionalParameters) { llamaServiceSectionPanel.setAdditionalParameters(additionalParameters); } + + public void setUseCustomLlamaServer(boolean useCustomLlamaServer) { + llamaServiceSectionPanel.setIsUseCustomServer(useCustomLlamaServer); + } + + public boolean isUseCustomLlamaServer() { + return llamaServiceSectionPanel.isUseCustomServer(); + } + + public void setCustomLlamaServerPath(String serverPath) { + llamaServiceSectionPanel.setCustomServerPath(serverPath); + } + + public String getCustomLlamaServerPath() { + return llamaServiceSectionPanel.getCustomServerPath(); + } } diff --git a/src/main/java/ee/carlrobert/codegpt/settings/state/LlamaSettingsState.java b/src/main/java/ee/carlrobert/codegpt/settings/state/LlamaSettingsState.java index b813fc437..84d8d395f 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/state/LlamaSettingsState.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/state/LlamaSettingsState.java @@ -17,6 +17,8 @@ public class LlamaSettingsState implements PersistentStateComponentLearn more settingsConfigurable.service.llama.customModelPath.label=Model path: settingsConfigurable.service.llama.customModelPath.comment=Only .gguf files are supported +settingsConfigurable.service.llama.customServerPath.label=Server path: +settingsConfigurable.service.llama.customServerPath.comment=Precompiled executable llama-cpp server, only .exe (Windows) or executable File (Linux) are supported settingsConfigurable.service.llama.promptTemplate.comment=Choose the template to use during interactions with the language model. Make sure it matches the custom model you're working with. settingsConfigurable.service.llama.downloadModelLink.label=Download Model settingsConfigurable.service.llama.cancelDownloadLink.label=Cancel Downloading