From 744571119daf06f3c22d14dbdb30e408b0c48b8f Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Wed, 26 Feb 2025 23:31:23 +0200 Subject: [PATCH] Add improving configuration wizard --- .../alyverkko_cli/commands/WizardCommand.java | 140 +++++++++++++++--- 1 file changed, 119 insertions(+), 21 deletions(-) diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java index 9ddf06e..6835db9 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java @@ -17,6 +17,7 @@ import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; import java.util.Scanner; +import java.util.stream.Collectors; import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.DEFAULT_CONFIG_FILE_PATH; @@ -26,6 +27,8 @@ public class WizardCommand implements Command { private final NullOption forceOption = parser.add(new NullOption("Force overwrite existing configuration")) .addAliases("--force", "-f"); + private Configuration existingConfig; + @Override public String getName() { return "wizard"; @@ -39,52 +42,92 @@ public class WizardCommand implements Command { return; } + // Load existing configuration if it exists + existingConfig = Configuration.loadConfiguration(new File(DEFAULT_CONFIG_FILE_PATH)); + if (existingConfig != null) { + System.out.println("Found existing configuration. Current values will be used as defaults."); + } + Configuration config = new Configuration(); Scanner scanner = new Scanner(System.in); System.out.println("\nStarting Älyverkko CLI configuration wizard.\n"); // Mail Directory - String mailDir = getInput("Enter the mail directory for AI tasks", - "~/.config/alyverkko-cli/mail"); + String mailDir = getInputWithDefault( + "Enter the mail directory for AI tasks. This is where your task files will be stored.", + existingConfig != null ? existingConfig.getMailDirectory().getPath() : "~/.config/alyverkko-cli/mail" + ); config.setMailDirectory(new File(mailDir)); // Models Directory - String modelsDir = getInput("Enter the directory for AI models", - "~/.config/alyverkko-cli/models"); + String modelsDir = getInputWithDefault( + "Enter the directory for AI models. This should contain your GGUF model files.", + existingConfig != null ? existingConfig.getModelsDirectory().getPath() : "~/.config/alyverkko-cli/models" + ); config.setModelsDirectory(new File(modelsDir)); // Prompts Directory - String promptsDir = getInput("Enter the directory for prompts", - "~/.config/alyverkko-cli/prompts"); + String promptsDir = getInputWithDefault( + "Enter the directory for prompts. This should contain text files with your AI prompts.", + existingConfig != null ? existingConfig.getPromptsDirectory().getPath() : "~/.config/alyverkko-cli/prompts" + ); config.setPromptsDirectory(new File(promptsDir)); // Llama CLI Path - String llamaCliPath = getInput("Enter the path to llama-cli executable", - "/usr/local/bin/llama-cli"); + String llamaCliPath = getInputWithDefault( + "Enter the path to the llama-cli executable. This is the command-line interface for llama.cpp.", + existingConfig != null ? existingConfig.getLlamaCliPath().getPath() : "/usr/local/bin/llama-cli" + ); config.setLlamaCliPath(new File(llamaCliPath)); // Default Temperature - float defaultTemp = getFloatInput("Enter default temperature", 0.7f, 0f, 1f); + float defaultTemp = getFloatInputWithDefault( + "Enter default temperature (0-1). A lower value makes the AI more deterministic, while a higher value makes it more creative.", + existingConfig != null ? existingConfig.getDefaultTemperature() : 0.7f, + 0f, 1f + ); config.setDefaultTemperature(defaultTemp); // Thread Counts - int threadCount = getIntInput("Enter number of threads for AI processing", 6, 1, 16); + int threadCount = getIntInputWithDefault( + "Enter number of threads for AI processing. This should typically match the number of CPU cores available.", + existingConfig != null ? existingConfig.getThreadCount() : 6, + 1, 16 + ); config.setThreadCount(threadCount); - int batchThreadCount = getIntInput("Enter number of batch threads", 10, 1, 32); + int batchThreadCount = getIntInputWithDefault( + "Enter number of batch threads. This controls how many prompts are processed in parallel.", + existingConfig != null ? existingConfig.getBatchThreadCount() : 10, + 1, 32 + ); config.setBatchThreadCount(batchThreadCount); // Models Setup System.out.println("\nModel configuration:"); List models = new ArrayList<>(); + + // Suggest models based on models directory + suggestModels(config.getModelsDirectory(), models); + while (true) { System.out.println("\nEnter model details or leave alias empty to finish."); String alias = getInput("Model alias: "); if (StringUtils.isBlank(alias)) break; - String filePath = getInput("Filesystem path: "); - int contextSize = getIntInput("Context size (tokens): ", 64000, 1024, 128000); + String filePath = getInputWithDefault( + "Enter filesystem path relative to models directory. " + + "Press Tab for suggestions or Enter to accept default.", + null + ); + + int contextSize = getIntInputWithDefault( + "Enter context size in tokens. A higher value allows the model to remember more context.", + 64000, + 1024, + 128000 + ); ConfigurationModel model = new ConfigurationModel(); model.setAlias(alias); @@ -92,6 +135,7 @@ public class WizardCommand implements Command { model.setContextSizeTokens(contextSize); models.add(model); } + config.setModels(models); // Write configuration @@ -116,7 +160,7 @@ public class WizardCommand implements Command { System.out.println("You can now run 'alyverkko-cli selftest' to verify everything is set up correctly."); } - private String getInput(String prompt, String defaultValue) { + private String getInputWithDefault(String prompt, String defaultValue) { Scanner scanner = new Scanner(System.in); if (defaultValue == null) { @@ -129,11 +173,7 @@ public class WizardCommand implements Command { return input.isEmpty() ? defaultValue : input; } - private String getInput(String prompt) { - return getInput(prompt, null); - } - - private float getFloatInput(String prompt, float defaultValue, float min, float max) { + private float getFloatInputWithDefault(String prompt, float defaultValue, float min, float max) { while (true) { String input = getInput(prompt, String.valueOf(defaultValue)); try { @@ -146,7 +186,7 @@ public class WizardCommand implements Command { } } - private int getIntInput(String prompt, int defaultValue, int min, int max) { + private int getIntInputWithDefault(String prompt, int defaultValue, int min, int max) { while (true) { String input = getInput(prompt, String.valueOf(defaultValue)); try { @@ -159,4 +199,62 @@ public class WizardCommand implements Command { } } -} + private void suggestModels(File modelsDir, List existingModels) { + if (!modelsDir.exists()) { + System.out.println("Models directory does not exist. Please create it first."); + return; + } + + try { + List modelFiles = Files.walk(modelsDir.toPath()) + .filter(Files::isRegularFile) + .map(Path::toFile) + .filter(file -> file.getName().endsWith(".gguf") && !file.getName().contains("-of-")) + .collect(Collectors.toList()); + + List existingAliases = existingModels.stream() + .map(ConfigurationModel::getAlias) + .collect(Collectors.toList()); + + for (File modelFile : modelFiles) { + String relativePath = modelsDir.toPath().relativize(modelFile.toPath()).toString(); + String alias = suggestAlias(relativePath); + + if (!existingAliases.contains(alias)) { + System.out.println("\nFound new model: " + relativePath); + System.out.println("Suggested alias: " + alias); + System.out.println("Would you like to add this model? (y/N)"); + String choice = getInput("Add model? ", "N"); + if (choice.equalsIgnoreCase("Y")) { + ConfigurationModel model = new ConfigurationModel(); + model.setAlias(alias); + model.setFilesystemPath(relativePath); + model.setContextSizeTokens(64000); // Default value + existingModels.add(model); + existingAliases.add(alias); + } + } + } + } catch (IOException e) { + System.err.println("Error scanning models directory: " + e.getMessage()); + } + } + + private String suggestAlias(String filePath) { + String fileName = new File(filePath).getName(); + String alias = fileName.replaceAll("[^a-zA-Z0-9]", "-").toLowerCase(); + return alias.replaceAll("-+", "-").replaceAll("^-|-$", ""); + } + + + private String getInput(String prompt, String defaultValue) { + Scanner scanner = new Scanner(System.in); + System.out.print(prompt + (defaultValue != null ? " [" + defaultValue + "]" : "") + ": "); + String input = scanner.nextLine().trim(); + return input.isEmpty() && defaultValue != null ? defaultValue : input; + } + + private String getInput(String prompt) { + return getInput(prompt, null); + } +} \ No newline at end of file -- 2.20.1