From: Svjatoslav Agejenko Date: Sat, 8 Mar 2025 22:56:45 +0000 (+0200) Subject: Configuration wizard usability improvements X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=3fd88b7d776f58a7c7d39c8a59a344d662c109aa;p=alyverkko-cli.git Configuration wizard usability improvements --- 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 6835db9..9f00a24 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java @@ -55,75 +55,87 @@ public class WizardCommand implements Command { // Mail Directory String mailDir = getInputWithDefault( - "Enter the mail directory for AI tasks. This is where your task files will be stored.", + "\nEnter 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 = getInputWithDefault( - "Enter the directory for AI models. This should contain your GGUF model files.", + "\nEnter 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 = getInputWithDefault( - "Enter the directory for prompts. This should contain text files with your AI prompts.", + "\nEnter 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 = getInputWithDefault( - "Enter the path to the llama-cli executable. This is the command-line interface for llama.cpp.", + "\nEnter 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 = getFloatInputWithDefault( - "Enter default temperature (0-1). A lower value makes the AI more deterministic, while a higher value makes it more creative.", + "\nEnter default temperature (0-2). A lower value makes the AI more deterministic, while a higher value makes it more creative.", existingConfig != null ? existingConfig.getDefaultTemperature() : 0.7f, - 0f, 1f + 0f, 2f ); config.setDefaultTemperature(defaultTemp); // Thread Counts int threadCount = getIntInputWithDefault( - "Enter number of threads for AI processing. This should typically match the number of CPU cores available.", + "\nEnter number of CPU threads for AI response generation. " + + "RAM data transfer speed is usually the bottleneck here. " + + "On a typical PC, while you might have 12 CPU cores, " + + "RAM bandwidth can get already saturated with only 6 CPU threads. " + + "Once RAM bandwidth is saturated, using more CPU threads " + + "will not improve performance while it will keep CPU cores needlessly busy.", existingConfig != null ? existingConfig.getThreadCount() : 6, - 1, 16 + 1, null ); config.setThreadCount(threadCount); int batchThreadCount = getIntInputWithDefault( - "Enter number of batch threads. This controls how many prompts are processed in parallel.", + "\nEnter number of CPU threads for input prompt processing. " + + "CPU computing power is usually the bottleneck here (not the RAM bandwidth). " + + "So you can utilize all CPU cores that you have here.", existingConfig != null ? existingConfig.getBatchThreadCount() : 10, - 1, 32 + 1, null ); config.setBatchThreadCount(batchThreadCount); // Models Setup - System.out.println("\nModel configuration:"); + System.out.println("\nModel configuration."); List models = new ArrayList<>(); + if (existingConfig != null) { + models.addAll(existingConfig.getModels()); + } - // Suggest models based on models directory - suggestModels(config.getModelsDirectory(), models); + String choice = getInput("Would you like to try to autodiscover available models ?", "Y"); + if (choice.equalsIgnoreCase("Y")) { + discoverAndSuggestNewModels(config.getModelsDirectory(), models); + } + System.out.println("\nYou can now add models manually."); while (true) { - System.out.println("\nEnter model details or leave alias empty to finish."); - String alias = getInput("Model alias: "); + String alias = getInput("Enter desired model alias (leave empty to finish adding models): "); if (StringUtils.isBlank(alias)) break; String filePath = getInputWithDefault( - "Enter filesystem path relative to models directory. " + + "\nEnter 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.", + "\nEnter context size in tokens. A higher value allows the model to remember more context.", 64000, 1024, 128000 @@ -186,20 +198,30 @@ public class WizardCommand implements Command { } } - private int getIntInputWithDefault(String prompt, int defaultValue, int min, int max) { + private int getIntInputWithDefault(String prompt, int defaultValue, Integer min, Integer max) { while (true) { String input = getInput(prompt, String.valueOf(defaultValue)); try { int value = Integer.parseInt(input); - if (value >= min && value <= max) return value; - System.out.println("Value must be between " + min + " and " + max); + + if (max != null && value > max) { + System.out.println("Value must be less than or equal to " + max); + continue; + } + + if (min != null && value < min) { + System.out.println("Value must be greater than or equal to " + min); + continue; + } + + return value; } catch (NumberFormatException e) { System.out.println("Invalid number format. Try again."); } } } - private void suggestModels(File modelsDir, List existingModels) { + private void discoverAndSuggestNewModels(File modelsDir, List existingModels) { if (!modelsDir.exists()) { System.out.println("Models directory does not exist. Please create it first."); return; @@ -218,35 +240,45 @@ public class WizardCommand implements Command { 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); - } - } + + boolean alreadyExists = existingModels.stream() + .anyMatch(m -> m.getFilesystemPath().equals(relativePath)); + + if (!alreadyExists) suggestAddingModel(existingModels, relativePath, existingAliases); + } } catch (IOException e) { System.err.println("Error scanning models directory: " + e.getMessage()); } } + private void suggestAddingModel(List existingModels, String relativePath, List existingAliases) { + String alias = suggestAlias(relativePath); + System.out.println("\nFound new model: " + relativePath); + System.out.println("Suggested alias: " + alias); + String choice = getInput("Would you like to add this model? (y/N) ", "N"); + if (choice.equalsIgnoreCase("Y")) { + ConfigurationModel model = new ConfigurationModel(); + model.setAlias(alias); + model.setFilesystemPath(relativePath); + int contextSize = getIntInputWithDefault( + "\nEnter context size in tokens. A higher value allows the model to remember more context.", + 64000, + 1024, + null + ); + model.setContextSizeTokens(contextSize); + existingModels.add(model); + existingAliases.add(alias); + } + } + 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 + "]" : "") + ": ");