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;
private final NullOption forceOption = parser.add(new NullOption("Force overwrite existing configuration"))
.addAliases("--force", "-f");
+ private Configuration existingConfig;
+
@Override
public String getName() {
return "wizard";
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);
model.setContextSizeTokens(contextSize);
models.add(model);
}
+
config.setModels(models);
// Write configuration
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) {
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 {
}
}
- 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 {
}
}
-}
+ 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