{"version":3,"file":"searxng_search.cjs","names":["Tool","path: string","parameters: P","baseUrl: string","nonUndefinedParams: [string, string][]","input: string","res: SearxngResults","response: string[]","previous: string"],"sources":["../../src/tools/searxng_search.ts"],"sourcesContent":["import { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { Tool } from \"@langchain/core/tools\";\n\n/**\n * Interface for the results returned by the Searxng search.\n */\ninterface SearxngResults {\n  query: string;\n  number_of_results: number;\n  results: Array<{\n    url: string;\n    title: string;\n    content: string;\n    img_src: string;\n    engine: string;\n    parsed_url: Array<string>;\n    template: string;\n    engines: Array<string>;\n    positions: Array<number>;\n    score: number;\n    category: string;\n    pretty_url: string;\n    open_group?: boolean;\n    close_group?: boolean;\n  }>;\n  answers: Array<string>;\n  corrections: Array<string>;\n  infoboxes: Array<{\n    infobox: string;\n    content: string;\n    engine: string;\n    engines: Array<string>;\n  }>;\n  suggestions: Array<string>;\n  unresponsive_engines: Array<string>;\n}\n\n/**\n * Interface for custom headers used in the Searxng search.\n */\ninterface SearxngCustomHeaders {\n  [key: string]: string;\n}\n\ninterface SearxngSearchParams {\n  /**\n   * @default 10\n   * Number of results included in results\n   */\n  numResults?: number;\n  /** Comma separated list, specifies the active search categories\n   * https://docs.searxng.org/user/configured_engines.html#configured-engines\n   */\n  categories?: string;\n\n  /** Comma separated list, specifies the active search engines\n   * https://docs.searxng.org/user/configured_engines.html#configured-engines\n   */\n  engines?: string;\n\n  /** Code of the language. */\n  language?: string;\n  /** Search page number. */\n  pageNumber?: number;\n  /**\n   * day / month / year\n   *\n   * Time range of search for engines which support it. See if an engine supports time range search in the preferences page of an instance.\n   */\n  timeRange?: number;\n\n  /**\n   * Throws Error if format is set anything other than \"json\"\n   * Output format of results. Format needs to be activated in search:\n   */\n  format?: \"json\";\n  /** Open search results on new tab. */\n  resultsOnNewTab?: 0 | 1;\n  /** Proxy image results through SearXNG. */\n  imageProxy?: boolean;\n  autocomplete?: string;\n  /**\n   * Filter search results of engines which support safe search. See if an engine supports safe search in the preferences page of an instance.\n   */\n  safesearch?: 0 | 1 | 2;\n}\n\n/**\n * SearxngSearch class represents a meta search engine tool.\n * Use this class when you need to answer questions about current events.\n * The input should be a search query, and the output is a JSON array of the query results.\n *\n * note: works best with *agentType*: `structured-chat-zero-shot-react-description`\n * https://github.com/searxng/searxng\n * @example\n * ```typescript\n * const executor = AgentExecutor.fromAgentAndTools({\n *   agent,\n *   tools: [\n *     new SearxngSearch({\n *       params: {\n *         format: \"json\",\n *         engines: \"google\",\n *       },\n *       headers: {},\n *     }),\n *   ],\n * });\n * const result = await executor.invoke({\n *   input: `What is Langchain? Describe in 50 words`,\n * });\n * ```\n */\nexport class SearxngSearch extends Tool {\n  static lc_name() {\n    return \"SearxngSearch\";\n  }\n\n  name = \"searxng-search\";\n\n  description =\n    \"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\";\n\n  protected apiBase?: string;\n\n  protected params?: SearxngSearchParams = {\n    numResults: 10,\n    pageNumber: 1,\n    format: \"json\",\n    imageProxy: true,\n    safesearch: 0,\n  };\n\n  protected headers?: SearxngCustomHeaders;\n\n  get lc_secrets(): { [key: string]: string } | undefined {\n    return {\n      apiBase: \"SEARXNG_API_BASE\",\n    };\n  }\n\n  /**\n   * Constructor for the SearxngSearch class\n   * @param apiBase Base URL of the Searxng instance\n   * @param params SearxNG parameters\n   * @param headers Custom headers\n   */\n  constructor({\n    apiBase,\n    params,\n    headers,\n  }: {\n    /** Base URL of Searxng instance */\n    apiBase?: string;\n\n    /** SearxNG Paramerters\n     *\n     *  https://docs.searxng.org/dev/search_api.html check here for more details\n     */\n    params?: SearxngSearchParams;\n\n    /**\n     * Custom headers\n     * Set custom headers if you're using a api from RapidAPI (https://rapidapi.com/iamrony777/api/searxng)\n     * No headers needed for a locally self-hosted instance\n     */\n    headers?: SearxngCustomHeaders;\n  }) {\n    super(...arguments);\n\n    this.apiBase = getEnvironmentVariable(\"SEARXNG_API_BASE\") || apiBase;\n    this.headers = { \"content-type\": \"application/json\", ...headers };\n\n    if (!this.apiBase) {\n      throw new Error(\n        `SEARXNG_API_BASE not set. You can set it as \"SEARXNG_API_BASE\" in your environment variables.`\n      );\n    }\n\n    if (params) {\n      this.params = { ...this.params, ...params };\n    }\n  }\n\n  /**\n   * Builds the URL for the Searxng search.\n   * @param path The path for the URL.\n   * @param parameters The parameters for the URL.\n   * @param baseUrl The base URL.\n   * @returns The complete URL as a string.\n   */\n  protected buildUrl<P extends SearxngSearchParams>(\n    path: string,\n    parameters: P,\n    baseUrl: string\n  ): string {\n    const nonUndefinedParams: [string, string][] = Object.entries(parameters)\n      .filter(([_, value]) => value !== undefined)\n      .map(([key, value]) => [key, value.toString()]); // Avoid string conversion\n    const searchParams = new URLSearchParams(nonUndefinedParams);\n    return `${baseUrl}/${path}?${searchParams}`;\n  }\n\n  async _call(input: string): Promise<string> {\n    const queryParams = {\n      q: input,\n      ...this.params,\n    };\n    const url = this.buildUrl(\"search\", queryParams, this.apiBase as string);\n\n    const resp = await fetch(url, {\n      method: \"POST\",\n      headers: this.headers,\n      signal: AbortSignal.timeout(5 * 1000), // 5 seconds\n    });\n\n    if (!resp.ok) {\n      throw new Error(resp.statusText);\n    }\n\n    const res: SearxngResults = await resp.json();\n\n    if (\n      !res.results.length &&\n      !res.answers.length &&\n      !res.infoboxes.length &&\n      !res.suggestions.length\n    ) {\n      return \"No good results found.\";\n    } else if (res.results.length) {\n      const response: string[] = [];\n\n      res.results.forEach((r) => {\n        response.push(\n          JSON.stringify({\n            title: r.title || \"\",\n            link: r.url || \"\",\n            snippet: r.content || \"\",\n          })\n        );\n      });\n\n      return response.slice(0, this.params?.numResults).toString();\n    } else if (res.answers.length) {\n      return res.answers[0];\n    } else if (res.infoboxes.length) {\n      // Apply HTML tag stripping repeatedly until no more replacements occur\n      // to prevent incomplete sanitization from crafted inputs like \"<scr<script>ipt>\"\n      let content = res.infoboxes[0]?.content ?? \"\";\n      let previous: string;\n      do {\n        previous = content;\n        content = content.replace(/<[^>]+>/gi, \"\");\n      } while (content !== previous);\n      return content;\n    } else if (res.suggestions.length) {\n      let suggestions = \"Suggestions: \";\n      res.suggestions.forEach((s) => {\n        suggestions += `${s}, `;\n      });\n      return suggestions;\n    } else {\n      return \"No good results found.\";\n    }\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHA,IAAa,gBAAb,cAAmCA,4BAAK;CACtC,OAAO,UAAU;AACf,SAAO;CACR;CAED,OAAO;CAEP,cACE;CAEF,AAAU;CAEV,AAAU,SAA+B;EACvC,YAAY;EACZ,YAAY;EACZ,QAAQ;EACR,YAAY;EACZ,YAAY;CACb;CAED,AAAU;CAEV,IAAI,aAAoD;AACtD,SAAO,EACL,SAAS,mBACV;CACF;;;;;;;CAQD,YAAY,EACV,SACA,QACA,SAiBD,EAAE;EACD,MAAM,GAAG,UAAU;EAEnB,KAAK,iEAAiC,mBAAmB,IAAI;EAC7D,KAAK,UAAU;GAAE,gBAAgB;GAAoB,GAAG;EAAS;AAEjE,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MACR,CAAC,6FAA6F,CAAC;AAInG,MAAI,QACF,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;EAAQ;CAE9C;;;;;;;;CASD,AAAU,SACRC,MACAC,YACAC,SACQ;EACR,MAAMC,qBAAyC,OAAO,QAAQ,WAAW,CACtE,OAAO,CAAC,CAAC,GAAG,MAAM,KAAK,UAAU,OAAU,CAC3C,IAAI,CAAC,CAAC,KAAK,MAAM,KAAK,CAAC,KAAK,MAAM,UAAU,AAAC,EAAC;EACjD,MAAM,eAAe,IAAI,gBAAgB;AACzC,SAAO,GAAG,QAAQ,CAAC,EAAE,KAAK,CAAC,EAAE,cAAc;CAC5C;CAED,MAAM,MAAMC,OAAgC;EAC1C,MAAM,cAAc;GAClB,GAAG;GACH,GAAG,KAAK;EACT;EACD,MAAM,MAAM,KAAK,SAAS,UAAU,aAAa,KAAK,QAAkB;EAExE,MAAM,OAAO,MAAM,MAAM,KAAK;GAC5B,QAAQ;GACR,SAAS,KAAK;GACd,QAAQ,YAAY,QAAQ,IAAI,IAAK;EACtC,EAAC;AAEF,MAAI,CAAC,KAAK,GACR,OAAM,IAAI,MAAM,KAAK;EAGvB,MAAMC,MAAsB,MAAM,KAAK,MAAM;AAE7C,MACE,CAAC,IAAI,QAAQ,UACb,CAAC,IAAI,QAAQ,UACb,CAAC,IAAI,UAAU,UACf,CAAC,IAAI,YAAY,OAEjB,QAAO;WACE,IAAI,QAAQ,QAAQ;GAC7B,MAAMC,WAAqB,CAAE;GAE7B,IAAI,QAAQ,QAAQ,CAAC,MAAM;IACzB,SAAS,KACP,KAAK,UAAU;KACb,OAAO,EAAE,SAAS;KAClB,MAAM,EAAE,OAAO;KACf,SAAS,EAAE,WAAW;IACvB,EAAC,CACH;GACF,EAAC;AAEF,UAAO,SAAS,MAAM,GAAG,KAAK,QAAQ,WAAW,CAAC,UAAU;EAC7D,WAAU,IAAI,QAAQ,OACrB,QAAO,IAAI,QAAQ;WACV,IAAI,UAAU,QAAQ;GAG/B,IAAI,UAAU,IAAI,UAAU,IAAI,WAAW;GAC3C,IAAIC;AACJ,MAAG;IACD,WAAW;IACX,UAAU,QAAQ,QAAQ,aAAa,GAAG;GAC3C,SAAQ,YAAY;AACrB,UAAO;EACR,WAAU,IAAI,YAAY,QAAQ;GACjC,IAAI,cAAc;GAClB,IAAI,YAAY,QAAQ,CAAC,MAAM;IAC7B,eAAe,GAAG,EAAE,EAAE,CAAC;GACxB,EAAC;AACF,UAAO;EACR,MACC,QAAO;CAEV;AACF"}