using System.CommandLine; using OpenQuery; using OpenQuery.Models; using OpenQuery.Services; using OpenQuery.Tools; var config = ConfigManager.Load(); var chunksOption = new Option( aliases: ["-c", "--chunks"], getDefaultValue: () => config.DefaultChunks, description: "Amount of top chunks to pass to the LLM overall" ); var resultsOption = new Option( aliases: ["-r", "--results"], getDefaultValue: () => config.DefaultResults, description: "Amount of search results to choose from per query" ); var queriesOption = new Option( aliases: ["-q", "--queries"], getDefaultValue: () => config.DefaultQueries, description: "Amount of search queries the LLM should generate before starting the searches" ); var shortOption = new Option( aliases: ["-s", "--short"], description: "Give a very short concise answer" ); var longOption = new Option( aliases: ["-l", "--long"], description: "Give a long elaborate detailed answer" ); var questionArgument = new Argument( name: "question", description: "The question to ask" ) { Arity = ArgumentArity.ZeroOrMore // Changed to ZeroOrMore so 'configure' works without error }; var configureCommand = new Command("configure", "Configure OpenQuery settings"); var interactiveOption = new Option(["-i", "--interactive"], "Interactive configuration"); var keyOption = new Option("--key", "Set API key"); var modelOption = new Option("--model", "Set default model"); var defQueriesOption = new Option("--queries", "Set default queries"); var defChunksOption = new Option("--chunks", "Set default chunks"); var defResultsOption = new Option("--results", "Set default results"); configureCommand.AddOption(interactiveOption); configureCommand.AddOption(keyOption); configureCommand.AddOption(modelOption); configureCommand.AddOption(defQueriesOption); configureCommand.AddOption(defChunksOption); configureCommand.AddOption(defResultsOption); configureCommand.SetHandler((isInteractive, key, model, queries, chunks, results) => { var cfg = ConfigManager.Load(); if (isInteractive) { Console.Write($"API Key [{cfg.ApiKey}]: "); var k = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(k)) cfg.ApiKey = k; Console.WriteLine("Available models:"); Console.WriteLine("1. qwen/qwen3.5-flash-02-23"); Console.WriteLine("2. qwen/qwen3.5-122b-a10b"); Console.WriteLine("3. minimax/minimax-m2.5"); Console.WriteLine("4. google/gemini-3-flash-preview"); Console.WriteLine("5. deepseek/deepseek-v3.2"); Console.WriteLine("6. moonshotai/kimi-k2.5"); Console.Write($"Model [{cfg.Model}]: "); var m = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(m)) { var models = new[] { "qwen/qwen3.5-flash-02-23", "qwen/qwen3.5-122b-a10b", "minimax/minimax-m2.5", "google/gemini-3-flash-preview", "deepseek/deepseek-v3.2", "moonshotai/kimi-k2.5" }; if (int.TryParse(m, out var idx) && idx >= 1 && idx <= 6) { cfg.Model = models[idx - 1]; } else { cfg.Model = m; } } Console.Write($"Default Queries [{cfg.DefaultQueries}]: "); var q = Console.ReadLine(); if (int.TryParse(q, out var qi)) cfg.DefaultQueries = qi; Console.Write($"Default Chunks [{cfg.DefaultChunks}]: "); var c = Console.ReadLine(); if (int.TryParse(c, out var ci)) cfg.DefaultChunks = ci; Console.Write($"Default Results [{cfg.DefaultResults}]: "); var r = Console.ReadLine(); if (int.TryParse(r, out var ri)) cfg.DefaultResults = ri; } else { cfg.ApiKey = key; cfg.Model = model; if (queries.HasValue) cfg.DefaultQueries = queries.Value; if (chunks.HasValue) cfg.DefaultChunks = chunks.Value; if (results.HasValue) cfg.DefaultResults = results.Value; } ConfigManager.Save(cfg); Console.WriteLine("Configuration saved to " + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/.config/openquery/config"); }, interactiveOption, keyOption, modelOption, defQueriesOption, defChunksOption, defResultsOption); var rootCommand = new RootCommand("OpenQuery - AI powered search and answer") { chunksOption, resultsOption, queriesOption, shortOption, longOption, questionArgument, configureCommand }; rootCommand.SetHandler(async (chunks, results, queries, isShort, isLong, questionArgs) => { var question = string.Join(" ", questionArgs); if (string.IsNullOrWhiteSpace(question)) { rootCommand.Invoke("--help"); return; } var options = new OpenQueryOptions(chunks, results, queries, isShort, isLong, question); var apiKey = Environment.GetEnvironmentVariable("OPENROUTER_API_KEY"); if (string.IsNullOrEmpty(apiKey)) { apiKey = config.ApiKey; } if (string.IsNullOrEmpty(apiKey)) { Console.Error.WriteLine("Error: API Key is missing. Set OPENROUTER_API_KEY environment variable or run 'configure -i' to set it up."); Environment.Exit(1); } var model = Environment.GetEnvironmentVariable("OPENROUTER_MODEL"); if (string.IsNullOrEmpty(model)) { model = config.Model; } var searxngUrl = Environment.GetEnvironmentVariable("SEARXNG_URL") ?? "http://localhost:8002"; var client = new OpenRouterClient(apiKey); var searxngClient = new SearxngClient(searxngUrl); var embeddingService = new EmbeddingService(client); var searchTool = new SearchTool(searxngClient, embeddingService); try { var openQuery = new OpenQueryApp(client, searchTool, model); await openQuery.RunAsync(options); } catch (HttpRequestException ex) { Console.Error.WriteLine($"\n[Error] Network request failed. Details: {ex.Message}"); Environment.Exit(1); } catch (Exception ex) { Console.Error.WriteLine($"\n[Error] An unexpected error occurred: {ex.Message}"); Environment.Exit(1); } }, chunksOption, resultsOption, queriesOption, shortOption, longOption, questionArgument); return await rootCommand.InvokeAsync(args);