Add improving configuration wizard
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 26 Feb 2025 21:31:23 +0000 (23:31 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 26 Feb 2025 21:31:23 +0000 (23:31 +0200)
src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java

index 9ddf06e..6835db9 100644 (file)
@@ -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<ConfigurationModel> 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<ConfigurationModel> existingModels) {
+        if (!modelsDir.exists()) {
+            System.out.println("Models directory does not exist. Please create it first.");
+            return;
+        }
+
+        try {
+            List<File> 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<String> 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