← All source files

V1 / 8f6c47b125e8

ProviderSetup.cs

Interfaces/ConsoleApp/LilithConsole/Setup/ProviderSetup.cs

Changed in this version
CS
using System.Text.Json;
using System.Text.RegularExpressions;
using Lilith.Agent;
using Lilith.Core.LMStudio;
using Lilith.Core.Logging;
using Lilith.Core.Extensions;

namespace LilithConsole.Setup;

public static class ProviderSetup
{
    public static async Task<LilithCredentials?> RunAsync(TextReader input, TextWriter output, Logger logger, string settingsPath)
    {
        string? selected = Environment.GetEnvironmentVariable("LILITH_PROVIDER");
        if (string.IsNullOrWhiteSpace(selected))
        {
            output.WriteLine("Choose your provider: 1. Ollama  2. LM Studio (Windows GUI)  0. Exit");
            output.Write("Provider [1]: ");
            selected = input.ReadLine()?.Trim();
            if (selected is null or "0" || selected.Equals("exit", StringComparison.OrdinalIgnoreCase)) return null;
            if (selected == "") selected = "1";
        }
        if (selected is "1" || selected.Equals("Ollama", StringComparison.OrdinalIgnoreCase))
            return await new ConsoleSetup(input, output, new OllamaSetupServices(logger), logger, settingsPath).RunAsync();
        if (selected != "2" && !selected.Equals("LMStudio", StringComparison.OrdinalIgnoreCase))
            throw new ArgumentException("Select 1/Ollama or 2/LMStudio.");

        string path = settingsPath + ".lmstudio.json";
        ConsoleSettings preferences = new();
        if (File.Exists(path))
        {
            try { preferences = JsonSettings.Load<ConsoleSettings>(path, settings => settings.IsValid()); }
            catch (JsonException) { output.WriteLine("Saved LM Studio preferences are invalid; choose them again."); }
        }
        if (!LMStudioSetup.IsInstalled())
        {
            output.Write("Download and install the LM Studio Windows GUI? [y/N]: ");
            string? answer = input.ReadLine()?.Trim();
            if (!string.Equals(answer, "y", StringComparison.OrdinalIgnoreCase) && !string.Equals(answer, "yes", StringComparison.OrdinalIgnoreCase)) return null;
            await LMStudioSetup.InstallGuiAsync(logger);
        }
        await LMStudioSetup.EnsureServerAsync(logger);
        if (preferences.AgentName is null)
        {
            output.Write("Agent name [Lilith]: ");
            string? name = input.ReadLine()?.Trim();
            if (name is null || name.Equals("/exit", StringComparison.OrdinalIgnoreCase)) return null;
            if (name.Length > 80 || name.Any(char.IsControl)) throw new ArgumentException("Invalid agent name.");
            preferences = preferences with { AgentName = name.Length == 0 ? "Lilith" : name };
        }
        void Save() => JsonSettings.Save(path, preferences);
        Save();
        var models = await LMStudioSetup.ListModelsAsync();
        if (preferences.Model is null || !models.Any(m => m.Id == preferences.Model))
        {
            output.WriteLine("Downloaded LM Studio models:");
            for (int i = 0; i < models.Count; i++) output.WriteLine($"{i + 1}. {models[i].Name} ({models[i].Id})");
            output.Write("Choose a number or enter a catalog ID (e.g. ibm/granite-4-micro); 0 exits: ");
            string? model = input.ReadLine()?.Trim();
            if (model is null or "0" || model.Equals("exit", StringComparison.OrdinalIgnoreCase)) return null;
            if (int.TryParse(model, out int index))
            {
                if (index < 1 || index > models.Count) throw new ArgumentException("Invalid model number.");
                model = models[index - 1].Id;
            }
            if (model.Length > 200 || !RegexPatterns.ModelIdentifier().IsMatch(model)) throw new ArgumentException("Invalid LM Studio model identifier.");
            preferences = preferences with { Model = model };
            Save();
            if (!models.Any(m => m.Id == model))
            {
                output.Write($"Download LM Studio model '{model}'? [y/N]: ");
                string? answer = input.ReadLine()?.Trim();
                if (!string.Equals(answer, "y", StringComparison.OrdinalIgnoreCase) && !string.Equals(answer, "yes", StringComparison.OrdinalIgnoreCase)) return null;
                await LMStudioSetup.DownloadModelAsync(model, logger);
            }
        }
        return new LilithCredentials(preferences.Model!,
            $"You are {preferences.AgentName}, a helpful AI assistant. Greet the user warmly when the conversation begins.",
            preferences.AgentName!, Lilith.Core.Providers.ProviderKind.LMStudio);
    }
}