diff --git a/build.gradle.kts b/build.gradle.kts index 77ff0325..e8105050 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -149,6 +149,7 @@ tasks { runIde { enabled = true environment("ENVIRONMENT", "LOCAL") + autoReloadPlugins.set(false) // is triggered when building llama server } test { 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 6e3ac219..a837150a 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerAgent.java @@ -14,14 +14,17 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.Service; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.util.Key; import ee.carlrobert.codegpt.CodeGPTBundle; import ee.carlrobert.codegpt.CodeGPTPlugin; import ee.carlrobert.codegpt.settings.service.llama.LlamaSettings; import ee.carlrobert.codegpt.settings.service.llama.form.ServerProgressPanel; +import ee.carlrobert.codegpt.ui.OverlayUtil; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,65 +35,94 @@ public final class LlamaServerAgent implements Disposable { private @Nullable OSProcessHandler makeProcessHandler; private @Nullable OSProcessHandler startServerProcessHandler; + private ServerProgressPanel activeServerProgressPanel; + private boolean stoppedByUser; public void startAgent( LlamaServerStartupParams params, ServerProgressPanel serverProgressPanel, Runnable onSuccess, - Runnable onServerTerminated) { + Consumer onServerTerminated) { + this.activeServerProgressPanel = serverProgressPanel; ApplicationManager.getApplication().invokeLater(() -> { try { - serverProgressPanel.updateText( + stoppedByUser = false; + serverProgressPanel.displayText( CodeGPTBundle.get("llamaServerAgent.buildingProject.description")); - makeProcessHandler = new OSProcessHandler(getMakeCommandLinde()); + makeProcessHandler = new OSProcessHandler( + getMakeCommandLine(params.additionalBuildParameters())); makeProcessHandler.addProcessListener( - getMakeProcessListener(params, serverProgressPanel, onSuccess, onServerTerminated)); + getMakeProcessListener(params, onSuccess, onServerTerminated)); makeProcessHandler.startNotify(); } catch (ExecutionException e) { - throw new RuntimeException(e); + showServerError(e.getMessage(), onServerTerminated); } }); } public void stopAgent() { + stoppedByUser = true; + if (makeProcessHandler != null) { + makeProcessHandler.destroyProcess(); + } if (startServerProcessHandler != null) { startServerProcessHandler.destroyProcess(); } } public boolean isServerRunning() { - return startServerProcessHandler != null + return (makeProcessHandler != null + && makeProcessHandler.isStartNotified() + && !makeProcessHandler.isProcessTerminated()) + || (startServerProcessHandler != null && startServerProcessHandler.isStartNotified() - && !startServerProcessHandler.isProcessTerminated(); + && !startServerProcessHandler.isProcessTerminated()); } private ProcessListener getMakeProcessListener( LlamaServerStartupParams params, - ServerProgressPanel serverProgressPanel, Runnable onSuccess, - Runnable onServerTerminated) { + Consumer onServerTerminated) { LOG.info("Building llama project"); return new ProcessAdapter() { + + private final List errorLines = new CopyOnWriteArrayList<>(); + @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { + if (ProcessOutputType.isStderr(outputType)) { + errorLines.add(event.getText()); + return; + } LOG.info(event.getText()); } @Override public void processTerminated(@NotNull ProcessEvent event) { + int exitCode = event.getExitCode(); + LOG.info(format("Server build exited with code %d", exitCode)); + if (stoppedByUser) { + onServerTerminated.accept(activeServerProgressPanel); + return; + } + if (exitCode != 0) { + showServerError(String.join(",", errorLines), onServerTerminated); + return; + } + try { LOG.info("Booting up llama server"); - serverProgressPanel.updateText( + activeServerProgressPanel.displayText( CodeGPTBundle.get("llamaServerAgent.serverBootup.description")); startServerProcessHandler = new OSProcessHandler.Silent(getServerCommandLine(params)); startServerProcessHandler.addProcessListener( - getProcessListener(params.port(), onSuccess, onServerTerminated)); + getProcessListener(params.port(), onSuccess, + onServerTerminated)); startServerProcessHandler.startNotify(); } catch (ExecutionException ex) { - LOG.error("Unable to start llama server", ex); - throw new RuntimeException(ex); + showServerError(ex.getMessage(), onServerTerminated); } } }; @@ -99,27 +131,25 @@ public final class LlamaServerAgent implements Disposable { private ProcessListener getProcessListener( int port, Runnable onSuccess, - Runnable onServerTerminated) { + Consumer onServerTerminated) { return new ProcessAdapter() { private final ObjectMapper objectMapper = new ObjectMapper(); private final List errorLines = new CopyOnWriteArrayList<>(); @Override public void processTerminated(@NotNull ProcessEvent event) { - if (errorLines.isEmpty()) { - LOG.info(format("Server terminated with code %d", event.getExitCode())); + LOG.info(format("Server terminated with code %d", event.getExitCode())); + if (stoppedByUser) { + onServerTerminated.accept(activeServerProgressPanel); } else { - LOG.info(String.join("", errorLines)); + showServerError(String.join(",", errorLines), onServerTerminated); } - - onServerTerminated.run(); } @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { if (ProcessOutputType.isStderr(outputType)) { errorLines.add(event.getText()); - return; } if (ProcessOutputType.isStdout(outputType)) { @@ -141,11 +171,18 @@ public final class LlamaServerAgent implements Disposable { }; } - private static GeneralCommandLine getMakeCommandLinde() { + private void showServerError(String errorText, Consumer onServerTerminated) { + onServerTerminated.accept(activeServerProgressPanel); + LOG.info("Unable to start llama server:\n" + errorText); + OverlayUtil.showClosableBalloon(errorText, MessageType.ERROR, activeServerProgressPanel); + } + + private static GeneralCommandLine getMakeCommandLine(List additionalCompileParameters) { GeneralCommandLine commandLine = new GeneralCommandLine().withCharset(StandardCharsets.UTF_8); commandLine.setExePath("make"); commandLine.withWorkDirectory(CodeGPTPlugin.getLlamaSourcePath()); commandLine.addParameters("-j"); + commandLine.addParameters(additionalCompileParameters); commandLine.setRedirectErrorStream(false); return commandLine; } @@ -159,11 +196,16 @@ public final class LlamaServerAgent implements Disposable { "-c", String.valueOf(params.contextLength()), "--port", String.valueOf(params.port()), "-t", String.valueOf(params.threads())); - commandLine.addParameters(params.additionalParameters()); + commandLine.addParameters(params.additionalRunParameters()); commandLine.setRedirectErrorStream(false); return commandLine; } + public void setActiveServerProgressPanel( + ServerProgressPanel activeServerProgressPanel) { + this.activeServerProgressPanel = activeServerProgressPanel; + } + @Override public void dispose() { if (makeProcessHandler != null && !makeProcessHandler.isProcessTerminated()) { 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 7b259a1f..d15c274f 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/llama/LlamaServerStartupParams.java @@ -3,5 +3,6 @@ package ee.carlrobert.codegpt.completions.llama; import java.util.List; public record LlamaServerStartupParams(String modelPath, int contextLength, int threads, int port, - List additionalParameters) { + List additionalRunParameters, + List additionalBuildParameters) { } diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/LlamaSettingsState.java b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/LlamaSettingsState.java index cb81c722..fc3ca47b 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/LlamaSettingsState.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/LlamaSettingsState.java @@ -23,6 +23,7 @@ public class LlamaSettingsState { private int contextSize = 2048; private int threads = 8; private String additionalParameters = ""; + private String additionalBuildParameters = ""; private int topK = 40; private double topP = 0.9; private double minP = 0.05; @@ -138,6 +139,14 @@ public class LlamaSettingsState { this.additionalParameters = additionalParameters; } + public String getAdditionalBuildParameters() { + return additionalBuildParameters; + } + + public void setAdditionalBuildParameters(String additionalBuildParameters) { + this.additionalBuildParameters = additionalBuildParameters; + } + public int getTopK() { return topK; } @@ -220,6 +229,7 @@ public class LlamaSettingsState { && Objects.equals(baseHost, that.baseHost) && Objects.equals(serverPort, that.serverPort) && Objects.equals(additionalParameters, that.additionalParameters) + && Objects.equals(additionalBuildParameters, that.additionalBuildParameters) && codeCompletionsEnabled == that.codeCompletionsEnabled && codeCompletionMaxTokens == that.codeCompletionMaxTokens; } @@ -229,7 +239,7 @@ public class LlamaSettingsState { return Objects.hash(runLocalServer, useCustomModel, customLlamaModelPath, huggingFaceModel, localModelPromptTemplate, remoteModelPromptTemplate, localModelInfillPromptTemplate, remoteModelInfillPromptTemplate, baseHost, serverPort, contextSize, threads, - additionalParameters, topK, topP, minP, repeatPenalty, codeCompletionsEnabled, - codeCompletionMaxTokens); + additionalParameters, additionalBuildParameters, topK, topP, minP, repeatPenalty, + codeCompletionsEnabled, codeCompletionMaxTokens); } } diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java index 1935c74e..cbd2bae3 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaServerPreferencesForm.java @@ -57,6 +57,7 @@ public class LlamaServerPreferencesForm { private final IntegerField maxTokensField; private final IntegerField threadsField; private final JBTextField additionalParametersField; + private final JBTextField additionalBuildParametersField; private final ChatPromptTemplatePanel remotePromptTemplatePanel; private final InfillPromptTemplatePanel infillPromptTemplatePanel; @@ -79,6 +80,9 @@ public class LlamaServerPreferencesForm { additionalParametersField = new JBTextField(settings.getAdditionalParameters(), 30); additionalParametersField.setEnabled(!serverRunning); + additionalBuildParametersField = new JBTextField(settings.getAdditionalBuildParameters(), 30); + additionalBuildParametersField.setEnabled(!serverRunning); + baseHostField = new JBTextField(settings.getBaseHost(), 30); apiKeyField = new JBPasswordField(); apiKeyField.setColumns(30); @@ -124,6 +128,7 @@ public class LlamaServerPreferencesForm { maxTokensField.setValue(state.getContextSize()); threadsField.setValue(state.getThreads()); additionalParametersField.setText(state.getAdditionalParameters()); + additionalBuildParametersField.setText(state.getAdditionalBuildParameters()); remotePromptTemplatePanel.setPromptTemplate(state.getRemoteModelPromptTemplate()); // ? infillPromptTemplatePanel.setPromptTemplate(state.getRemoteModelInfillPromptTemplate()); apiKeyField.setText(CredentialsStore.INSTANCE.getCredential(LLAMA_API_KEY)); @@ -184,9 +189,17 @@ public class LlamaServerPreferencesForm { createComment("settingsConfigurable.service.llama.threads.comment")) .addLabeledComponent( CodeGPTBundle.get("settingsConfigurable.service.llama.additionalParameters.label"), - additionalParametersField) - .addComponentToRightColumn( - createComment("settingsConfigurable.service.llama.additionalParameters.comment")) + additionalParametersField) + .addComponentToRightColumn( + createComment( + "settingsConfigurable.service.llama.additionalParameters.comment")) + .addLabeledComponent( + CodeGPTBundle.get( + "settingsConfigurable.service.llama.additionalBuildParameters.label"), + additionalBuildParametersField) + .addComponentToRightColumn( + createComment( + "settingsConfigurable.service.llama.additionalBuildParameters.comment")) .addVerticalGap(4) .addComponentFillVertically(new JPanel(), 0) .getPanel())) @@ -196,6 +209,7 @@ public class LlamaServerPreferencesForm { private JButton getServerButton( LlamaServerAgent llamaServerAgent, ServerProgressPanel serverProgressPanel) { + llamaServerAgent.setActiveServerProgressPanel(serverProgressPanel); var serverRunning = llamaServerAgent.isServerRunning(); var serverButton = new JButton(); serverButton.setText(serverRunning @@ -218,7 +232,9 @@ public class LlamaServerPreferencesForm { getContextSize(), getThreads(), getServerPort(), - getListOfAdditionalParameters()), + getListOfAdditionalParameters(), + getListOfAdditionalBuildParameters() + ), serverProgressPanel, () -> { setFormEnabled(false); @@ -227,12 +243,12 @@ public class LlamaServerPreferencesForm { Actions.Checked, SwingConstants.LEADING)); }, - () -> { + (activeServerProgressPanel) -> { setFormEnabled(true); serverButton.setText( CodeGPTBundle.get("settingsConfigurable.service.llama.startServer.label")); serverButton.setIcon(Actions.Execute); - serverProgressPanel.displayComponent(new JBLabel( + activeServerProgressPanel.displayComponent(new JBLabel( CodeGPTBundle.get("settingsConfigurable.service.llama.progress.serverTerminated"), Actions.Cancel, SwingConstants.LEADING)); @@ -282,7 +298,7 @@ public class LlamaServerPreferencesForm { serverButton.setText( CodeGPTBundle.get("settingsConfigurable.service.llama.startServer.label")); serverButton.setIcon(Actions.Execute); - progressPanel.updateText( + progressPanel.displayText( CodeGPTBundle.get("settingsConfigurable.service.llama.progress.stoppingServer")); } @@ -291,7 +307,7 @@ public class LlamaServerPreferencesForm { serverButton.setText( CodeGPTBundle.get("settingsConfigurable.service.llama.stopServer.label")); serverButton.setIcon(Actions.Suspend); - progressPanel.startProgress( + progressPanel.displayText( CodeGPTBundle.get("settingsConfigurable.service.llama.progress.startingServer")); } @@ -301,6 +317,7 @@ public class LlamaServerPreferencesForm { maxTokensField.setEnabled(enabled); threadsField.setEnabled(enabled); additionalParametersField.setEnabled(enabled); + additionalBuildParametersField.setEnabled(enabled); } public boolean isRunLocalServer() { @@ -337,9 +354,20 @@ public class LlamaServerPreferencesForm { public List getListOfAdditionalParameters() { return Arrays.stream(additionalParametersField.getText().split(",")) - .map(String::trim) - .filter(s -> !s.isBlank()) - .toList(); + .map(String::trim) + .filter(s -> !s.isBlank()) + .toList(); + } + + public String getAdditionalBuildParameters() { + return additionalBuildParametersField.getText(); + } + + public List getListOfAdditionalBuildParameters() { + return Arrays.stream(additionalBuildParametersField.getText().split(",")) + .map(String::trim) + .filter(s -> !s.isBlank()) + .toList(); } public PromptTemplate getPromptTemplate() { diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaSettingsForm.java b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaSettingsForm.java index 7147eceb..d5260b24 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaSettingsForm.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/LlamaSettingsForm.java @@ -41,6 +41,7 @@ public class LlamaSettingsForm extends JPanel { state.setContextSize(llamaServerPreferencesForm.getContextSize()); state.setThreads(llamaServerPreferencesForm.getThreads()); state.setAdditionalParameters(llamaServerPreferencesForm.getAdditionalParameters()); + state.setAdditionalBuildParameters(llamaServerPreferencesForm.getAdditionalBuildParameters()); var modelPreferencesForm = llamaServerPreferencesForm.getLlamaModelPreferencesForm(); state.setCustomLlamaModelPath(modelPreferencesForm.getCustomLlamaModelPath()); diff --git a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/ServerProgressPanel.java b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/ServerProgressPanel.java index 9a755003..4d348865 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/ServerProgressPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/service/llama/form/ServerProgressPanel.java @@ -8,20 +8,15 @@ import javax.swing.JPanel; public class ServerProgressPanel extends JPanel { private final JBLabel label = new JBLabel(); + private final AsyncProcessIcon loadingSpinner = new AsyncProcessIcon("sign_in_spinner"); - public ServerProgressPanel() { - setVisible(false); - add(new AsyncProcessIcon("sign_in_spinner")); - add(label); - } - - public void startProgress(String text) { - setVisible(true); - updateText(text); - } - - public void updateText(String text) { + public void displayText(String text) { label.setText(text); + removeAll(); + add(loadingSpinner); + add(label); + revalidate(); + repaint(); } public void displayComponent(JComponent component) { diff --git a/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java b/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java index bc16cbb0..394da503 100644 --- a/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java +++ b/src/main/java/ee/carlrobert/codegpt/ui/OverlayUtil.java @@ -149,4 +149,13 @@ public class OverlayUtil { .createBalloon() .show(RelativePoint.getSouthOf(component), Position.below); } + + public static void showClosableBalloon(String content, MessageType messageType, + JComponent component) { + JBPopupFactory.getInstance() + .createHtmlTextBalloonBuilder(content, messageType, null) + .setCloseButtonEnabled(true) + .createBalloon() + .show(RelativePoint.getSouthOf(component), Position.below); + } } diff --git a/src/main/resources/messages/codegpt.properties b/src/main/resources/messages/codegpt.properties index b8be890a..2b0aa0fe 100644 --- a/src/main/resources/messages/codegpt.properties +++ b/src/main/resources/messages/codegpt.properties @@ -63,6 +63,8 @@ settingsConfigurable.service.llama.threads.label=Threads: settingsConfigurable.service.llama.threads.comment=The number of threads available to execute the model. It is not recommended to specify a number greater than the number of processor cores. settingsConfigurable.service.llama.additionalParameters.label=Additional parameters: settingsConfigurable.service.llama.additionalParameters.comment=Additional command-line parameters for the server startup process, separated by commas. See the full list of options.

Example: "--n-gpu-layers, 1, --no-mmap, --mlock"

+settingsConfigurable.service.llama.additionalBuildParameters.label=Additional build parameters: +settingsConfigurable.service.llama.additionalBuildParameters.comment=Additional command-line parameters for the server build process, separated by commas. See the full list of build options.

Example: "LLAMA_CUBLAS=1,CUDA_DOCKER_ARCH=all"

settingsConfigurable.service.llama.baseHost.label=Base host: settingsConfigurable.service.llama.baseHost.comment=URL to existing LLama server settingsConfigurable.service.llama.startServer.label=Start server @@ -179,7 +181,7 @@ validation.error.mustBeGreaterThanZero=Value must be greater than 0 checkForUpdatesTask.title=Checking for CodeGPT update... checkForUpdatesTask.notification.message=An update for CodeGPT is available. checkForUpdatesTask.notification.installButton=Install update -llamaServerAgent.buildingProject.description=Building llama.cpp... +llamaServerAgent.buildingProject.description=Building server... llamaServerAgent.serverBootup.description=Booting up server... notification.compilationError.description=CodeGPT has detected a compilation error. Would you like assistance in resolving it? notification.compilationError.okLabel=Resolve errors