initial release
This commit is contained in:
188
Program.cs
Normal file
188
Program.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
using System.CommandLine;
|
||||
using OpenQuery;
|
||||
using OpenQuery.Models;
|
||||
using OpenQuery.Services;
|
||||
using OpenQuery.Tools;
|
||||
|
||||
var config = ConfigManager.Load();
|
||||
|
||||
var chunksOption = new Option<int>(
|
||||
aliases: ["-c", "--chunks"],
|
||||
getDefaultValue: () => config.DefaultChunks,
|
||||
description: "Amount of top chunks to pass to the LLM overall"
|
||||
);
|
||||
|
||||
var resultsOption = new Option<int>(
|
||||
aliases: ["-r", "--results"],
|
||||
getDefaultValue: () => config.DefaultResults,
|
||||
description: "Amount of search results to choose from per query"
|
||||
);
|
||||
|
||||
var queriesOption = new Option<int>(
|
||||
aliases: ["-q", "--queries"],
|
||||
getDefaultValue: () => config.DefaultQueries,
|
||||
description: "Amount of search queries the LLM should generate before starting the searches"
|
||||
);
|
||||
|
||||
var shortOption = new Option<bool>(
|
||||
aliases: ["-s", "--short"],
|
||||
description: "Give a very short concise answer"
|
||||
);
|
||||
|
||||
var longOption = new Option<bool>(
|
||||
aliases: ["-l", "--long"],
|
||||
description: "Give a long elaborate detailed answer"
|
||||
);
|
||||
|
||||
var questionArgument = new Argument<string[]>(
|
||||
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<bool>(["-i", "--interactive"], "Interactive configuration");
|
||||
var keyOption = new Option<string>("--key", "Set API key");
|
||||
var modelOption = new Option<string>("--model", "Set default model");
|
||||
var defQueriesOption = new Option<int?>("--queries", "Set default queries");
|
||||
var defChunksOption = new Option<int?>("--chunks", "Set default chunks");
|
||||
var defResultsOption = new Option<int?>("--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);
|
||||
Reference in New Issue
Block a user