Initial commit of Websearch

This commit is contained in:
2024-11-10 16:23:51 -07:00
parent 8275fca4a7
commit 60c7822b04
5 changed files with 258 additions and 1 deletions

111
main.ts Normal file
View File

@@ -0,0 +1,111 @@
import { parseArgs } from "@std/cli/parse-args";
import type { Args } from "@std/cli/parse-args";
import { ensureDir } from "@std/fs";
import { parse, stringify } from "@std/yaml";
import { answerQuery, cleanTextFromHtml, getNewsUrls } from "./websearch.ts";
export interface Config {
model?: string;
search_url?: string;
}
const parsed: Args = parseArgs(Deno.args, {
boolean: ["help"],
string: ["model", "search_url"],
});
if (parsed.help) {
console.log(`
Usage:
websearch [--model MODEL] [--search_url SEARCH_URL] QUERY
Options:
--model Ollama model to use
--search_url URL for SearXNG endpoint
Arguments:
QUERY The topic to search for news to summarize
Configuration:
The websearch configuration file is stored in the XDG Config directory
/home/$USER/.config/websearch/config.yml. Both the model and search_url
can be customized as an alternative to providing the options for each.
If both the Ollama model and SearXNG endpoint are successful, the
configuration is automatically saved/updated.
`);
Deno.exit();
}
const configDir = `${Deno.env.get("HOME")}/.config/websearch`;
const configPath = `${Deno.env.get("HOME")}/.config/websearch/config.yml`;
async function loadConfig(): Promise<Config> {
try {
const yamlString = await Deno.readTextFile(configPath);
return parse(yamlString) as Config;
} catch {
return {};
}
}
async function saveConfig(config: Config) {
await ensureDir(configDir);
await Deno.writeTextFile(
configPath,
stringify(config),
);
}
async function main(args: Args) {
const query = args._.join(" ");
if (!query) {
throw new Error("Please provide a search query");
}
console.log(`Query: ${query}`);
try {
const config = await loadConfig();
if (!config.model) {
if (args.model) {
config.model = args.model;
} else {
throw new Error("Provide --model or add Ollama model to configuration");
}
} else if (args.model && args.model !== config.model) {
config.model = args.model;
}
if (!config.search_url) {
if (args.search_url) {
config.search_url = args.search_url;
} else {
throw new Error(
"Provide --search_url or add search_url to configuration",
);
}
} else if (args.search_url && args.search_url !== config.search_url) {
config.search_url = args.search_url;
}
const urls = await getNewsUrls(config, query);
if (!urls || urls.length === 0) {
console.log("No results");
Deno.exit(1);
}
const cleanedTexts = await Promise.all(
urls.map((url) => cleanTextFromHtml(url)),
);
await answerQuery(config, query, cleanedTexts.join("\n\n"));
await saveConfig(config);
} catch (error: any) {
console.error(`Error processing query "${query}":`, error.message);
Deno.exit(1);
}
}
if (import.meta.main) {
main(parsed).catch((error) => {
console.error("Unhandled exception:", error);
Deno.exit(1);
});
}