import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import eu.svjatoslav.alyverkko_cli.Command;
+import eu.svjatoslav.alyverkko_cli.Utils;
import eu.svjatoslav.alyverkko_cli.configuration.Configuration;
import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationModel;
+import eu.svjatoslav.commons.cli_helper.CLIHelper;
import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.FileOption;
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 java.io.*;
+import java.nio.file.*;
+import java.util.*;
import java.util.stream.Collectors;
-import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.DEFAULT_CONFIG_FILE_PATH;
-import static eu.svjatoslav.commons.cli_helper.CLIHelper.*;
+import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.getConfigurationFile;
+import static eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper.loadConfiguration;
+import static eu.svjatoslav.commons.cli_helper.CLIHelper.askBoolean;
+import static eu.svjatoslav.commons.cli_helper.CLIHelper.askFloat;
+import static eu.svjatoslav.commons.cli_helper.CLIHelper.askInteger;
+import static java.lang.Boolean.TRUE;
/**
- * The WizardCommand provides an interactive, console-based
- * configuration wizard for setting up the Älyverkko CLI application.
+ * A single WizardCommand that:
+ * 1. Loads existing configuration (if any).
+ * 2. Performs "selftest" style validation checks interactively,
+ * prompting the user to fix invalid or missing items.
+ * 3. If no config file exists, it goes through all config parameters
+ * from scratch.
+ * 4. Offers to remove model entries that reference missing files.
+ * 5. Autodiscovers new .gguf files and lets the user add them to the config.
+ * 6. Saves the resulting (fixed) config file.
+ * 7. Prints a final pass/fail summary for the user.
*/
public class WizardCommand implements Command {
+ // Command-line parser to handle wizard arguments
private final Parser parser = new Parser();
- /**
- * Command-line option to force overwriting an existing configuration file.
- */
+ // If present, force overwriting existing config
private final NullOption forceOverwriteOption = parser.add(new NullOption("Force overwrite existing configuration"))
.addAliases("--force", "-f");
/**
- * An existing configuration, if one is found on disk.
+ * Optional CLI argument for specifying a configuration file path.
*/
- private Configuration existingConfig;
+ public FileOption configFileOption = parser.add(new FileOption("Configuration file path"))
+ .addAliases("--config", "-c");
+
+ // The config object (loaded or newly created)
+ private Configuration config;
- /**
- * @return the name of this command, i.e., "wizard".
- */
@Override
public String getName() {
return "wizard";
}
- /**
- * Executes this command, launching an interactive configuration
- * wizard in the console.
- *
- * @param cliArguments the command-line arguments following the "wizard" subcommand.
- * @throws IOException in case of IO failures while reading/writing config files.
- */
@Override
public void execute(String[] cliArguments) throws IOException {
if (!parser.parse(cliArguments)) {
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 newConfig = new Configuration();
+ // 1. Load existing config if possible
+ File configFile = getConfigurationFile(configFileOption);
- System.out.println("\nStarting Älyverkko CLI configuration wizard.\n");
-
- // Mail Directory
- String mailDirectory = getInputWithDefault(
- "\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"
- );
- newConfig.setMailDirectory(new File(mailDirectory));
-
- // Models Directory
- String modelsDirectory = getInputWithDefault(
- "\nEnter the directory for AI models. This should contain your GGUF model files.",
- existingConfig != null ? existingConfig.getModelsDirectory().getPath() : "~/.config/alyverkko-cli/models"
- );
- newConfig.setModelsDirectory(new File(modelsDirectory));
+ if (configFile.exists() && configFile.isFile()) {
+ System.out.println("Found existing configuration at " + configFile.getAbsolutePath());
+ System.out.println("I will check whether everything is valid, and if not, allow you to fix it.\n");
+ config = loadConfiguration(configFile);
+ } else {
+ // If no config found, create a fresh one
+ System.out.println("No existing configuration found. Let's create one!\n");
+ config = new Configuration();
+ }
- // Prompts Directory
- String promptsDirectory = getInputWithDefault(
- "\nEnter the directory for prompts. This should contain text files with your AI prompts.",
- existingConfig != null ? existingConfig.getPromptsDirectory().getPath() : "~/.config/alyverkko-cli/prompts"
- );
- newConfig.setPromptsDirectory(new File(promptsDirectory));
+ // 2. Validate and fix each parameter
+ checkAndFixAllParameters();
- // Llama CLI Path
- String llamaCliExecutablePath = getInputWithDefault(
- "\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"
- );
- newConfig.setLlamaCliPath(new File(llamaCliExecutablePath));
+ // 3. Validate the models: remove or fix broken references, autodiscover new models, etc.
+ fixModelEntries();
- // Default Temperature
- float defaultTemperature = askFloat(
- "\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, 2f
- );
- newConfig.setDefaultTemperature(defaultTemperature);
-
- // Thread Counts
- int threadCount = askInteger(
- "\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, null
- );
- newConfig.setThreadCount(threadCount);
+ // 4. If the user is satisfied, attempt to save
+ trySaveConfiguration(configFile);
- int batchThreadCount = askInteger(
- "\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, null
- );
- newConfig.setBatchThreadCount(batchThreadCount);
+ // 5. Final selftest pass
+ boolean finalOk = doFinalCheck();
+ if (finalOk) {
+ System.out.println("\nConfiguration looks good. You're all set! You can now run:");
+ System.out.println(" alyverkko-cli mail (to start the mail-based AI service)");
+ System.out.println(" alyverkko-cli joinfiles ... (to prepare tasks for AI)\n");
+ } else {
+ System.out.println("\nSome checks did not pass, but we've saved the best config we could.");
+ System.out.println("You might want to run 'wizard' again or manually edit the YAML file:\n "
+ + configFile.getAbsolutePath() + "\n");
+ }
+ }
- // Models Setup
- System.out.println("\nModel configuration.");
- List<ConfigurationModel> models = new ArrayList<>();
- if (existingConfig != null) {
- models.addAll(existingConfig.getModels());
+ /**
+ * Step-by-step checking (and possibly fixing) of each main config parameter.
+ */
+ private void checkAndFixAllParameters() {
+ // 2.1 mail_directory
+ while (true) {
+ File mailDir = config.getMailDirectory();
+ if (mailDir == null || !mailDir.isDirectory() || !mailDir.exists()) {
+ System.out.println("Mail directory is missing or invalid.");
+ String defaultVal = (mailDir == null) ? "~/.config/alyverkko-cli/mail" : mailDir.getPath();
+ String userInput = CLIHelper.askString(
+ "Enter the mail directory path (will create if it doesn't exist)",
+ defaultVal
+ );
+ mailDir = new File(userInput);
+ config.setMailDirectory(mailDir);
+
+ // Attempt to create if not exist
+ if (!mailDir.exists()) {
+ boolean created = mailDir.mkdirs();
+ if (!created) {
+ Utils.printRedMessageToConsole("Failed to create mail directory. Check permissions?");
+ }
+ }
+ } else {
+ // If valid, confirm or let user fix
+ if (askBoolean("Mail directory is: " + mailDir.getAbsolutePath() + " . Is this OK?", TRUE)) {
+ break;
+ }
+ // Otherwise loop around to let them reenter
+ config.setMailDirectory(null); // Force re-check
+ }
}
- if (askBoolean("Would you like to try to autodiscover available undefined models ?", true)) {
- discoverAndSuggestNewModels(newConfig.getModelsDirectory(), models);
+ // 2.2 models_directory
+ while (true) {
+ File modelsDir = config.getModelsDirectory();
+ if (modelsDir == null || !modelsDir.isDirectory() || !modelsDir.exists()) {
+ System.out.println("Models directory is missing or invalid.");
+ String defaultVal = (modelsDir == null) ? "~/.config/alyverkko-cli/models" : modelsDir.getPath();
+ String userInput = CLIHelper.askString(
+ "Enter the models directory path (where your .gguf files are located)",
+ defaultVal
+ );
+ modelsDir = new File(userInput);
+ config.setModelsDirectory(modelsDir);
+
+ // Attempt to create if not exist
+ if (!modelsDir.exists()) {
+ boolean created = modelsDir.mkdirs();
+ if (!created) {
+ Utils.printRedMessageToConsole("Failed to create models directory. Check permissions?");
+ }
+ }
+ } else {
+ if (askBoolean("Models directory is: " + modelsDir.getAbsolutePath() + " . Is this OK?", true)) {
+ break;
+ }
+ config.setModelsDirectory(null);
+ }
}
- System.out.println("\nYou can now add models manually.");
+ // 2.3 prompts_directory
while (true) {
- String alias = askString("Enter desired model alias (leave empty to finish adding models): ");
- if (StringUtils.isBlank(alias)) break;
+ File promptsDir = config.getPromptsDirectory();
+ if (promptsDir == null || !promptsDir.isDirectory() || !promptsDir.exists()) {
+ System.out.println("Prompts directory is missing or invalid.");
+ String defaultVal = (promptsDir == null) ? "~/.config/alyverkko-cli/prompts" : promptsDir.getPath();
+ String userInput = CLIHelper.askString(
+ "Enter the prompts directory path",
+ defaultVal
+ );
+ promptsDir = new File(userInput);
+ config.setPromptsDirectory(promptsDir);
+
+ // Attempt to create if not exist
+ if (!promptsDir.exists()) {
+ boolean created = promptsDir.mkdirs();
+ if (!created) {
+ Utils.printRedMessageToConsole("Failed to create prompts directory. Check permissions?");
+ }
+ }
+ } else {
+ if (askBoolean("Prompts directory is: " + promptsDir.getAbsolutePath() + " . Is this OK?", true)) {
+ break;
+ }
+ config.setPromptsDirectory(null);
+ }
+ }
- String filePath = getInputWithDefault(
- "\nEnter filesystem path relative to models directory. " +
- "Press Tab for suggestions or Enter to accept default.",
- null
- );
+ // 2.4 llama_cli_path
+ while (true) {
+ File llamaCli = config.getLlamaCliPath();
+ boolean valid = (llamaCli != null && llamaCli.isFile() && llamaCli.exists());
+ if (!valid) {
+ System.out.println("The 'llama-cli' executable path is missing or invalid.");
+ String defaultVal = (llamaCli == null) ? "/usr/local/bin/llama-cli" : llamaCli.getPath();
+ String userInput = CLIHelper.askString(
+ "Enter the path to the llama-cli executable",
+ defaultVal
+ );
+ llamaCli = new File(userInput);
+ config.setLlamaCliPath(llamaCli);
+ } else {
+ if (askBoolean("Llama-cli path is: " + llamaCli.getAbsolutePath() + " . Is this OK?", true)) {
+ break;
+ }
+ config.setLlamaCliPath(null);
+ }
+ }
- int contextSize = askInteger(
- "\nEnter context size in tokens. A higher value allows the model to remember more context.",
- 64000,
- 1024,
- 128000
- );
+ // 2.5 default_temperature
+ float oldTemp = config.getDefaultTemperature();
+ if (oldTemp < 0.0f || oldTemp > 3.0f) {
+ oldTemp = 0.7f;
+ }
+ float newTemp = askFloat(
+ "\nEnter default temperature (0-3). Lower => more deterministic, higher => more creative.",
+ oldTemp,
+ 0f, 3f
+ );
+ config.setDefaultTemperature(newTemp);
- ConfigurationModel model = new ConfigurationModel();
- model.setAlias(alias);
- model.setFilesystemPath(filePath);
- model.setContextSizeTokens(contextSize);
- models.add(model);
+ // 2.6 thread_count
+ int oldThreadCount = config.getThreadCount();
+ if (oldThreadCount < 1) {
+ oldThreadCount = 6;
}
+ int newThreadCount = askInteger(
+ "\nEnter number of CPU threads for AI generation. Typically 6 for a 12-core CPU, for example.",
+ oldThreadCount,
+ 1, null
+ );
+ config.setThreadCount(newThreadCount);
- newConfig.setModels(models);
+ // 2.7 batch_thread_count
+ int oldBatchThreadCount = config.getBatchThreadCount();
+ if (oldBatchThreadCount < 1) {
+ oldBatchThreadCount = 10;
+ }
+ int newBatchCount = askInteger(
+ "\nEnter number of CPU threads for input prompt processing (all cores is often fine).",
+ oldBatchThreadCount,
+ 1, null
+ );
+ config.setBatchThreadCount(newBatchCount);
+ }
- // Write configuration
- Path configPath = Path.of(DEFAULT_CONFIG_FILE_PATH);
- if (Files.exists(configPath) && !forceOverwriteOption.isPresent()) {
- if (!askBoolean("Configuration file already exists. Overwrite?", false)) {
- System.out.println("Configuration not saved. Run with --force to overwrite.");
- return;
- }
+ /**
+ * Offer to remove model references in config if the file does not exist.
+ * Then autodiscover new .gguf files and offer to add them.
+ * Then let user manually add more if desired.
+ */
+ private void fixModelEntries() {
+ if (config.getModels() == null) {
+ config.setModels(new ArrayList<>());
}
- // Create parent directories
- Files.createDirectories(configPath.getParent());
+ // Remove references to nonexistent model files
+ removeDeadModelReferences();
- // Save configuration
- try (var writer = Files.newBufferedWriter(configPath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
- new ObjectMapper(new YAMLFactory()).writeValue(writer, newConfig);
+ // Offer to autodiscover new model files
+ if (askBoolean("\nAttempt to autodiscover new .gguf model files in the models directory?", true)) {
+ discoverAndSuggestNewModels(config.getModelsDirectory(), config.getModels());
}
- System.out.println("\nConfiguration saved to: " + configPath);
- System.out.println("You can now run 'alyverkko-cli selftest' to verify everything is set up correctly.");
+ // Let user manually add new models
+ addModelsManually();
}
/**
- * Prompts the user for an input value, showing a default value if provided.
- *
- * @param prompt the prompt to display to the user.
- * @param defaultValue the default value to use if the user simply presses Enter.
- * @return the user's input, or the default if no input is provided.
+ * Loops through the config's list of models. If the file doesn't exist,
+ * let user decide whether to remove that model or keep it (perhaps to fix the path).
*/
- private String getInputWithDefault(String prompt, String defaultValue) {
- Scanner scanner = new Scanner(System.in);
-
- if (defaultValue == null) {
- System.out.print(prompt + ": ");
- } else {
- System.out.print(prompt + " [" + defaultValue + "]: ");
+ private void removeDeadModelReferences() {
+ Iterator<ConfigurationModel> it = config.getModels().iterator();
+ while (it.hasNext()) {
+ ConfigurationModel cm = it.next();
+ File f = new File(config.getModelsDirectory(), cm.getFilesystemPath());
+ if (!f.exists()) {
+ System.out.println("\nModel with alias '" + cm.getAlias()
+ + "' references missing file: " + f.getAbsolutePath());
+ boolean remove = askBoolean("Remove this from config?", true);
+ if (remove) {
+ it.remove();
+ } else {
+ // Let them fix the filesystem_path or do so in next iteration
+ System.out.println("Leaving it as-is for now. You can fix the path manually if needed.");
+ }
+ }
}
- String input = scanner.nextLine().trim();
-
- return input.isEmpty() ? defaultValue : input;
}
/**
- * Attempts to discover new .gguf model files in the given models directory, and
- * suggests adding them to the configuration if they aren't already defined.
- *
- * @param modelsDir the directory containing model files.
- * @param existingModels existing model definitions to compare against.
+ * Scans the models directory for .gguf files not yet in the config,
+ * and offers to add them with user-chosen alias, etc.
*/
private void discoverAndSuggestNewModels(File modelsDir, List<ConfigurationModel> existingModels) {
if (!modelsDir.exists()) {
- System.out.println("Models directory does not exist. Please create it first.");
+ System.out.println("Models directory does not exist. Please create it first or fix path above.");
return;
}
.filter(file -> file.getName().endsWith(".gguf") && !file.getName().contains("-of-"))
.collect(Collectors.toList());
- List<String> existingAliases = existingModels.stream()
- .map(ConfigurationModel::getAlias)
+ List<String> existingPaths = existingModels.stream()
+ .map(ConfigurationModel::getFilesystemPath)
.collect(Collectors.toList());
for (File modelFile : modelFiles) {
String relativePath = modelsDir.toPath().relativize(modelFile.toPath()).toString();
-
- boolean alreadyExists = existingModels.stream()
- .anyMatch(m -> m.getFilesystemPath().equals(relativePath));
-
- if (!alreadyExists) {
- suggestAddingModel(existingModels, relativePath, existingAliases);
+ if (!existingPaths.contains(relativePath)) {
+ System.out.println("\nDiscovered new model file: " + relativePath);
+ String guessAlias = suggestAlias(relativePath);
+ System.out.println(" Suggest alias: " + guessAlias);
+
+ if (askBoolean("Would you like to add this model?", false)) {
+ ConfigurationModel cm = new ConfigurationModel();
+ cm.setAlias(CLIHelper.askString("Enter alias for this model", guessAlias));
+ cm.setFilesystemPath(relativePath);
+ int ctxSize = askInteger(
+ "Enter context size in tokens (e.g. 64000).",
+ 64000, 1024, 128000
+ );
+ cm.setContextSizeTokens(ctxSize);
+ cm.setEndOfTextMarker(null);
+ existingModels.add(cm);
+ existingPaths.add(relativePath);
+ }
}
}
} catch (IOException e) {
}
/**
- * Suggests to the user adding a newly-discovered model to the configuration.
- *
- * @param existingModels the list of existing models to which we might add a new one.
- * @param relativePath the model file's path relative to the models directory.
- * @param existingAliases the aliases already defined for known models.
+ * Lets the user add one or more new models by specifying alias, path, context size, etc.
*/
- private void suggestAddingModel(List<ConfigurationModel> existingModels, String relativePath, List<String> existingAliases) {
- String alias = suggestAlias(relativePath);
- System.out.println("\nFound new model: " + relativePath);
- System.out.println("Suggested alias: " + alias);
- if (askBoolean("Would you like to add this model? (y/N) ", false)) {
+ private void addModelsManually() {
+ System.out.println("\nNow you can add models manually, if you wish.");
+ while (true) {
+ String alias = CLIHelper.askString("Enter model alias (leave empty to finish adding models): ");
+ if (StringUtils.isBlank(alias)) {
+ break;
+ }
+ String filePath = CLIHelper.askString("Enter filesystem path relative to models directory: ");
+ if (StringUtils.isBlank(filePath)) {
+ System.out.println("Skipping because no path given.");
+ continue;
+ }
+ int contextSize = askInteger(
+ "Enter context size in tokens (e.g. 64000).",
+ 64000, 1024, 128000
+ );
+
+ // Check for duplicates
+ boolean alreadyExists = config.getModels().stream()
+ .anyMatch(m -> m.getAlias().equals(alias));
+ if (alreadyExists) {
+ Utils.printRedMessageToConsole("Model with alias '" + alias + "' already exists! Skipping.");
+ continue;
+ }
+
ConfigurationModel model = new ConfigurationModel();
model.setAlias(alias);
- model.setFilesystemPath(relativePath);
- int contextSize = askInteger("\nEnter context size in tokens. A higher value allows the model to remember more context.",
- 64000,
- 1024,
- null
- );
+ model.setFilesystemPath(filePath);
model.setContextSizeTokens(contextSize);
- existingModels.add(model);
- existingAliases.add(alias);
+ model.setEndOfTextMarker(null);
+
+ config.getModels().add(model);
}
}
/**
- * Generates a suggested model alias by removing non-alphanumeric characters,
- * converting to lower case, and normalizing multiple dashes.
- *
- * @param filePath path string from which to suggest an alias.
- * @return a suggested alias, derived from the model file name.
+ * Saves the config to the default path, verifying if user wants to
+ * overwrite if it already exists, etc.
+ */
+ private void trySaveConfiguration(File configFile) {
+ boolean alreadyExists = Files.exists(configFile.toPath());
+
+ if (alreadyExists && !forceOverwriteOption.isPresent()) {
+ boolean userOk = askBoolean("\nConfiguration file already exists. Overwrite it?", true);
+ if (!userOk) {
+ System.out.println("Not overwriting. If you want to do so, run again with --force.");
+ return;
+ }
+ }
+
+ try {
+ Files.createDirectories(configFile.toPath().getParent());
+ try (BufferedWriter writer = Files.newBufferedWriter(
+ configFile.toPath(),
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING
+ )) {
+ new ObjectMapper(new YAMLFactory()).writeValue(writer, config);
+ }
+ System.out.println("\nConfiguration saved to: " + configFile.toPath());
+ } catch (IOException e) {
+ Utils.printRedMessageToConsole("Error saving configuration: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Runs a final check across the config to confirm everything is
+ * valid. Returns true if all checks pass, false otherwise.
+ */
+ private boolean doFinalCheck() {
+ System.out.println("\nPerforming final checks...\n");
+ boolean allOk = true;
+
+ // Check mail dir
+ if (config.getMailDirectory() == null || !config.getMailDirectory().isDirectory()) {
+ System.err.println("Mail directory is invalid: " + config.getMailDirectory());
+ allOk = false;
+ }
+
+ // Check llama-cli
+ if (config.getLlamaCliPath() == null || !config.getLlamaCliPath().isFile()) {
+ System.err.println("llama-cli path is invalid: " + config.getLlamaCliPath());
+ allOk = false;
+ }
+
+ // Check prompts dir
+ if (config.getPromptsDirectory() == null || !config.getPromptsDirectory().isDirectory()) {
+ System.err.println("Prompts directory is invalid: " + config.getPromptsDirectory());
+ allOk = false;
+ } else {
+ File[] pFiles = config.getPromptsDirectory().listFiles();
+ if (pFiles == null || pFiles.length == 0) {
+ System.out.println("Warning: No prompt files found in " + config.getPromptsDirectory());
+ // not necessarily fatal, so we won't mark allOk = false
+ }
+ }
+
+ // Check models
+ if (config.getModels() == null || config.getModels().isEmpty()) {
+ System.err.println("No models are defined in the configuration.");
+ allOk = false;
+ } else {
+ // Check each model file
+ for (ConfigurationModel m : config.getModels()) {
+ File f = new File(config.getModelsDirectory(), m.getFilesystemPath());
+ if (!f.exists()) {
+ System.err.println("Model alias '" + m.getAlias()
+ + "' references missing file: " + f.getAbsolutePath());
+ allOk = false;
+ }
+ }
+ }
+
+ // Check temperature
+ if (config.getDefaultTemperature() < 0 || config.getDefaultTemperature() > 3) {
+ System.err.println("Default temperature must be between 0 and 3. Found: " + config.getDefaultTemperature());
+ allOk = false;
+ }
+
+ if (config.getThreadCount() < 1) {
+ System.err.println("thread_count must be >= 1. Found: " + config.getThreadCount());
+ allOk = false;
+ }
+
+ if (config.getBatchThreadCount() < 1) {
+ System.err.println("batch_thread_count must be >= 1. Found: " + config.getBatchThreadCount());
+ allOk = false;
+ }
+
+ if (allOk) {
+ System.out.println("All final checks passed!\n");
+ } else {
+ System.out.println("\nSome checks failed (see messages above).");
+ }
+ return allOk;
+ }
+
+ /**
+ * Generates an alias from a .gguf filename by removing non-alphanumeric chars.
*/
private String suggestAlias(String filePath) {
String fileName = new File(filePath).getName();
String alias = fileName.replaceAll("[^a-zA-Z0-9]", "-").toLowerCase();
return alias.replaceAll("-+", "-").replaceAll("^-|-$", "");
}
+
}