import { getEnvironmentVariable } from "@langchain/core/utils/env";
import { Tool } from "@langchain/core/tools";
/**
 * SearxngSearch class represents a meta search engine tool.
 * Use this class when you need to answer questions about current events.
 * The input should be a search query, and the output is a JSON array of the query results.
 *
 * note: works best with *agentType*: `structured-chat-zero-shot-react-description`
 * https://github.com/searxng/searxng
 * @example
 * ```typescript
 * const executor = AgentExecutor.fromAgentAndTools({
 *   agent,
 *   tools: [
 *     new SearxngSearch({
 *       params: {
 *         format: "json",
 *         engines: "google",
 *       },
 *       headers: {},
 *     }),
 *   ],
 * });
 * const result = await executor.invoke({
 *   input: `What is Langchain? Describe in 50 words`,
 * });
 * ```
 */
export class SearxngSearch extends Tool {
    static lc_name() {
        return "SearxngSearch";
    }
    get lc_secrets() {
        return {
            apiBase: "SEARXNG_API_BASE",
        };
    }
    /**
     * Constructor for the SearxngSearch class
     * @param apiBase Base URL of the Searxng instance
     * @param params SearxNG parameters
     * @param headers Custom headers
     */
    constructor({ apiBase, params, headers, }) {
        super(...arguments);
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "searxng-search"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "A meta search engine. Useful for when you need to answer questions about current events. Input should be a search query. Output is a JSON array of the query results"
        });
        Object.defineProperty(this, "apiBase", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "params", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {
                numResults: 10,
                pageNumber: 1,
                format: "json",
                imageProxy: true,
                safesearch: 0,
            }
        });
        Object.defineProperty(this, "headers", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        this.apiBase = getEnvironmentVariable("SEARXNG_API_BASE") || apiBase;
        this.headers = { "content-type": "application/json", ...headers };
        if (!this.apiBase) {
            throw new Error(`SEARXNG_API_BASE not set. You can set it as "SEARXNG_API_BASE" in your environment variables.`);
        }
        if (params) {
            this.params = { ...this.params, ...params };
        }
    }
    /**
     * Builds the URL for the Searxng search.
     * @param path The path for the URL.
     * @param parameters The parameters for the URL.
     * @param baseUrl The base URL.
     * @returns The complete URL as a string.
     */
    buildUrl(path, parameters, baseUrl) {
        const nonUndefinedParams = Object.entries(parameters)
            .filter(([_, value]) => value !== undefined)
            .map(([key, value]) => [key, value.toString()]); // Avoid string conversion
        const searchParams = new URLSearchParams(nonUndefinedParams);
        return `${baseUrl}/${path}?${searchParams}`;
    }
    async _call(input) {
        const queryParams = {
            q: input,
            ...this.params,
        };
        const url = this.buildUrl("search", queryParams, this.apiBase);
        const resp = await fetch(url, {
            method: "POST",
            headers: this.headers,
            signal: AbortSignal.timeout(5 * 1000), // 5 seconds
        });
        if (!resp.ok) {
            throw new Error(resp.statusText);
        }
        const res = await resp.json();
        if (!res.results.length &&
            !res.answers.length &&
            !res.infoboxes.length &&
            !res.suggestions.length) {
            return "No good results found.";
        }
        else if (res.results.length) {
            const response = [];
            res.results.forEach((r) => {
                response.push(JSON.stringify({
                    title: r.title || "",
                    link: r.url || "",
                    snippet: r.content || "",
                }));
            });
            return response.slice(0, this.params?.numResults).toString();
        }
        else if (res.answers.length) {
            return res.answers[0];
        }
        else if (res.infoboxes.length) {
            return res.infoboxes[0]?.content.replaceAll(/<[^>]+>/gi, "");
        }
        else if (res.suggestions.length) {
            let suggestions = "Suggestions: ";
            res.suggestions.forEach((s) => {
                suggestions += `${s}, `;
            });
            return suggestions;
        }
        else {
            return "No good results found.";
        }
    }
}