Add configuration wizard
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 24 Feb 2025 15:32:41 +0000 (17:32 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Mon, 24 Feb 2025 15:32:41 +0000 (17:32 +0200)
src/main/java/eu/svjatoslav/alyverkko_cli/Main.java
src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java

index 049f966..4bd8d57 100644 (file)
@@ -1,10 +1,7 @@
 package eu.svjatoslav.alyverkko_cli;
 
-import eu.svjatoslav.alyverkko_cli.commands.JoinFilesCommand;
-import eu.svjatoslav.alyverkko_cli.commands.ListModelsCommand;
-import eu.svjatoslav.alyverkko_cli.commands.SelftestCommand;
+import eu.svjatoslav.alyverkko_cli.commands.*;
 import eu.svjatoslav.alyverkko_cli.configuration.Configuration;
-import eu.svjatoslav.alyverkko_cli.commands.MailCorrespondentCommand;
 
 import java.io.IOException;
 import java.util.Optional;
@@ -17,7 +14,8 @@ public class Main {
             new ListModelsCommand(),
             new MailCorrespondentCommand(),
             new JoinFilesCommand(),
-            new SelftestCommand()
+            new SelftestCommand(),
+            new WizardCommand()
     );
 
     public static void main(final String[] args) throws IOException, InterruptedException {
diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/WizardCommand.java
new file mode 100644 (file)
index 0000000..9ddf06e
--- /dev/null
@@ -0,0 +1,162 @@
+package eu.svjatoslav.alyverkko_cli.commands;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import eu.svjatoslav.alyverkko_cli.Command;
+import eu.svjatoslav.alyverkko_cli.configuration.Configuration;
+import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationModel;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.NullOption;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.DEFAULT_CONFIG_FILE_PATH;
+
+public class WizardCommand implements Command {
+
+    private final Parser parser = new Parser();
+    private final NullOption forceOption = parser.add(new NullOption("Force overwrite existing configuration"))
+            .addAliases("--force", "-f");
+
+    @Override
+    public String getName() {
+        return "wizard";
+    }
+
+    @Override
+    public void execute(String[] cliArguments) throws IOException {
+        if (!parser.parse(cliArguments)) {
+            System.out.println("Failed to parse commandline arguments");
+            parser.showHelp();
+            return;
+        }
+
+        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");
+        config.setMailDirectory(new File(mailDir));
+
+        // Models Directory
+        String modelsDir = getInput("Enter the directory for AI models",
+                "~/.config/alyverkko-cli/models");
+        config.setModelsDirectory(new File(modelsDir));
+
+        // Prompts Directory
+        String promptsDir = getInput("Enter the directory for prompts",
+                "~/.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");
+        config.setLlamaCliPath(new File(llamaCliPath));
+
+        // Default Temperature
+        float defaultTemp = getFloatInput("Enter default temperature", 0.7f, 0f, 1f);
+        config.setDefaultTemperature(defaultTemp);
+
+        // Thread Counts
+        int threadCount = getIntInput("Enter number of threads for AI processing", 6, 1, 16);
+        config.setThreadCount(threadCount);
+
+        int batchThreadCount = getIntInput("Enter number of batch threads", 10, 1, 32);
+        config.setBatchThreadCount(batchThreadCount);
+
+        // Models Setup
+        System.out.println("\nModel configuration:");
+        List<ConfigurationModel> models = new ArrayList<>();
+        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);
+
+            ConfigurationModel model = new ConfigurationModel();
+            model.setAlias(alias);
+            model.setFilesystemPath(filePath);
+            model.setContextSizeTokens(contextSize);
+            models.add(model);
+        }
+        config.setModels(models);
+
+        // Write configuration
+        Path configPath = Path.of(DEFAULT_CONFIG_FILE_PATH);
+        if (Files.exists(configPath) && !forceOption.isPresent()) {
+            String confirm = getInput("Configuration file already exists. Overwrite? (y/N)", "N");
+            if (!confirm.equalsIgnoreCase("Y")) {
+                System.out.println("Configuration not saved. Run with --force to overwrite.");
+                return;
+            }
+        }
+
+        // Create parent directories
+        Files.createDirectories(configPath.getParent());
+
+        // Save configuration
+        try (var writer = Files.newBufferedWriter(configPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
+            new ObjectMapper(new YAMLFactory()).writeValue(writer, config);
+        }
+
+        System.out.println("\nConfiguration saved to: " + configPath);
+        System.out.println("You can now run 'alyverkko-cli selftest' to verify everything is set up correctly.");
+    }
+
+    private String getInput(String prompt, String defaultValue) {
+        Scanner scanner = new Scanner(System.in);
+
+        if (defaultValue == null) {
+            System.out.print(prompt + ": ");
+        } else {
+            System.out.print(prompt + " [" + defaultValue + "]: ");
+        }
+        String input = scanner.nextLine().trim();
+
+        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) {
+        while (true) {
+            String input = getInput(prompt, String.valueOf(defaultValue));
+            try {
+                float value = Float.parseFloat(input);
+                if (value >= min && value <= max) return value;
+                System.out.println("Value must be between " + min + " and " + max);
+            } catch (NumberFormatException e) {
+                System.out.println("Invalid number format. Try again.");
+            }
+        }
+    }
+
+    private int getIntInput(String prompt, int defaultValue, int min, int 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);
+            } catch (NumberFormatException e) {
+                System.out.println("Invalid number format. Try again.");
+            }
+        }
+    }
+
+}
index b301325..63769f8 100644 (file)
@@ -14,7 +14,7 @@ import static eu.svjatoslav.commons.file.IOHelper.getFileContentsAsString;
 @Data
 public class Configuration {
 
-    private static final String DEFAULT_CONFIG_FILE_PATH = "~/.config/alyverkko-cli/alyverkko-cli.yaml".replaceFirst("^~", System.getProperty("user.home"));
+    public static final String DEFAULT_CONFIG_FILE_PATH = "~/.config/alyverkko-cli/alyverkko-cli.yaml".replaceFirst("^~", System.getProperty("user.home"));
 
     @JsonProperty("mail_directory")
     private File mailDirectory;