From b8b55b5899a9bd5f095d8ad18e4b7ddaf5651221 Mon Sep 17 00:00:00 2001
From: Timothy Carambat <rambat1010@gmail.com>
Date: Thu, 5 Sep 2024 10:36:46 -0700
Subject: [PATCH] Feature/add searchapi web browsing (#2224)

* Add SearchApi to web browsing

* UI modifications for SearchAPI

---------

Co-authored-by: Sebastjan Prachovskij <sebastjan.prachovskij@gmail.com>
---
 .vscode/settings.json                         |   1 +
 docker/.env.example                           |   4 +
 docker/HOW_TO_USE_DOCKER.md                   |   4 +-
 .../SearchProviderOptions/index.jsx           |  77 ++++++++++++++++++
 .../WebSearchSelection/icons/searchapi.png    | Bin 0 -> 4477 bytes
 .../Admin/Agents/WebSearchSelection/index.jsx |  10 +++
 server/.env.example                           |   4 +
 server/models/systemSettings.js               |   3 +
 .../agents/aibitat/plugins/web-browsing.js    |  69 ++++++++++++++++
 server/utils/helpers/updateENV.js             |   8 ++
 10 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 frontend/src/pages/Admin/Agents/WebSearchSelection/icons/searchapi.png

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 549fd1574..4769a939c 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -41,6 +41,7 @@
     "Qdrant",
     "royalblue",
     "searxng",
+    "SearchApi",
     "Serper",
     "Serply",
     "streamable",
diff --git a/docker/.env.example b/docker/.env.example
index 56be87cb4..1521a307a 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -252,6 +252,10 @@ GID='1000'
 # AGENT_GSE_KEY=
 # AGENT_GSE_CTX=
 
+#------ SearchApi.io ----------- https://www.searchapi.io/
+# AGENT_SEARCHAPI_API_KEY=
+# AGENT_SEARCHAPI_ENGINE=google
+
 #------ Serper.dev ----------- https://serper.dev/
 # AGENT_SERPER_DEV_KEY=
 
diff --git a/docker/HOW_TO_USE_DOCKER.md b/docker/HOW_TO_USE_DOCKER.md
index 1e95bd8a0..2eeaee060 100644
--- a/docker/HOW_TO_USE_DOCKER.md
+++ b/docker/HOW_TO_USE_DOCKER.md
@@ -117,8 +117,8 @@ services:
       - WHISPER_PROVIDER=local
       - TTS_PROVIDER=native
       - PASSWORDMINCHAR=8
-      - AGENT_SERPER_DEV_KEY="SERPER DEV API KEY"
-      - AGENT_SERPLY_API_KEY="Serply.io API KEY"
+      # Add any other keys here for services or settings
+      # you can find in the docker/.env.example file
     volumes:
       - anythingllm_storage:/app/server/storage
     restart: always
diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
index f7ad09c03..1e5349857 100644
--- a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
+++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
@@ -50,6 +50,83 @@ export function GoogleSearchOptions({ settings }) {
   );
 }
 
+const SearchApiEngines = [
+  { name: "Google Search", value: "google" },
+  { name: "Google Maps", value: "google_maps" },
+  { name: "Google Shopping", value: "google_shopping" },
+  { name: "Google News", value: "google_news" },
+  { name: "Google Jobs", value: "google_jobs" },
+  { name: "Google Scholar", value: "google_scholar" },
+  { name: "Google Finance", value: "google_finance" },
+  { name: "Google Patents", value: "google_patents" },
+  { name: "YouTube", value: "youtube" },
+  { name: "Bing", value: "bing" },
+  { name: "Bing News", value: "bing_news" },
+  { name: "Amazon Product Search", value: "amazon_search" },
+  { name: "Baidu", value: "baidu" },
+];
+export function SearchApiOptions({ settings }) {
+  return (
+    <>
+      <p className="text-sm text-white/60 my-2">
+        You can get a free API key{" "}
+        <a
+          href="https://www.searchapi.io/"
+          target="_blank"
+          rel="noreferrer"
+          className="text-blue-300 underline"
+        >
+          from SearchApi.
+        </a>
+      </p>
+      <div className="flex gap-x-4">
+        <div className="flex flex-col w-60">
+          <label className="text-white text-sm font-semibold block mb-3">
+            API Key
+          </label>
+          <input
+            type="password"
+            name="env::AgentSearchApiKey"
+            className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+            placeholder="SearchApi API Key"
+            defaultValue={settings?.AgentSearchApiKey ? "*".repeat(20) : ""}
+            required={true}
+            autoComplete="off"
+            spellCheck={false}
+          />
+        </div>
+        <div className="flex flex-col w-60">
+          <label className="text-white text-sm font-semibold block mb-3">
+            Engine
+          </label>
+          <select
+            name="env::AgentSearchApiEngine"
+            required={true}
+            className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+            defaultValue={settings?.AgentSearchApiEngine || "google"}
+          >
+            {SearchApiEngines.map(({ name, value }) => (
+              <option key={name} value={value}>
+                {name}
+              </option>
+            ))}
+          </select>
+          {/* <input
+            type="text"
+            name="env::AgentSearchApiEngine"
+            className="border-none bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
+            placeholder="SearchApi engine (Google, Bing...)"
+            defaultValue={settings?.AgentSearchApiEngine || "google"}
+            required={true}
+            autoComplete="off"
+            spellCheck={false}
+          /> */}
+        </div>
+      </div>
+    </>
+  );
+}
+
 export function SerperDotDevOptions({ settings }) {
   return (
     <>
diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/searchapi.png b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/searchapi.png
new file mode 100644
index 0000000000000000000000000000000000000000..65bae79bf4363ac4a534143177b244a8b0161b26
GIT binary patch
literal 4477
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
Ry!#YBqnikLsowo3{{(zoV95Xg

literal 0
HcmV?d00001

diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
index c1f14cc6a..fd201fb90 100644
--- a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
+++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
@@ -1,6 +1,7 @@
 import React, { useEffect, useRef, useState } from "react";
 import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
 import GoogleSearchIcon from "./icons/google.png";
+import SearchApiIcon from "./icons/searchapi.png";
 import SerperDotDevIcon from "./icons/serper.png";
 import BingSearchIcon from "./icons/bing.png";
 import SerplySearchIcon from "./icons/serply.png";
@@ -14,6 +15,7 @@ import {
 import SearchProviderItem from "./SearchProviderItem";
 import WebSearchImage from "@/media/agents/scrape-websites.png";
 import {
+  SearchApiOptions,
   SerperDotDevOptions,
   GoogleSearchOptions,
   BingSearchOptions,
@@ -38,6 +40,14 @@ const SEARCH_PROVIDERS = [
     description:
       "Web search powered by a custom Google Search Engine. Free for 100 queries per day.",
   },
+  {
+    name: "SearchApi",
+    value: "searchapi",
+    logo: SearchApiIcon,
+    options: (settings) => <SearchApiOptions settings={settings} />,
+    description:
+      "SearchApi delivers structured data from multiple search engines. Free for 100 queries, but then paid. ",
+  },
   {
     name: "Serper.dev",
     value: "serper-dot-dev",
diff --git a/server/.env.example b/server/.env.example
index 22bd557ee..f942d6832 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -241,6 +241,10 @@ TTS_PROVIDER="native"
 # AGENT_GSE_KEY=
 # AGENT_GSE_CTX=
 
+#------ SearchApi.io ----------- https://www.searchapi.io/
+# AGENT_SEARCHAPI_API_KEY=
+# AGENT_SEARCHAPI_ENGINE=google
+
 #------ Serper.dev ----------- https://serper.dev/
 # AGENT_SERPER_DEV_KEY=
 
diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js
index b85f3cb8c..e9ae3f3e9 100644
--- a/server/models/systemSettings.js
+++ b/server/models/systemSettings.js
@@ -81,6 +81,7 @@ const SystemSettings = {
         if (
           ![
             "google-search-engine",
+            "searchapi",
             "serper-dot-dev",
             "bing-search",
             "serply-engine",
@@ -218,6 +219,8 @@ const SystemSettings = {
       // --------------------------------------------------------
       AgentGoogleSearchEngineId: process.env.AGENT_GSE_CTX || null,
       AgentGoogleSearchEngineKey: !!process.env.AGENT_GSE_KEY || null,
+      AgentSearchApiKey: !!process.env.AGENT_SEARCHAPI_API_KEY || null,
+      AgentSearchApiEngine: process.env.AGENT_SEARCHAPI_ENGINE || "google",
       AgentSerperApiKey: !!process.env.AGENT_SERPER_DEV_KEY || null,
       AgentBingSearchApiKey: !!process.env.AGENT_BING_SEARCH_API_KEY || null,
       AgentSerplyApiKey: !!process.env.AGENT_SERPLY_API_KEY || null,
diff --git a/server/utils/agents/aibitat/plugins/web-browsing.js b/server/utils/agents/aibitat/plugins/web-browsing.js
index f4269fe13..76849056e 100644
--- a/server/utils/agents/aibitat/plugins/web-browsing.js
+++ b/server/utils/agents/aibitat/plugins/web-browsing.js
@@ -62,6 +62,9 @@ const webBrowsing = {
               case "google-search-engine":
                 engine = "_googleSearchEngine";
                 break;
+              case "searchapi":
+                engine = "_searchApi";
+                break;
               case "serper-dot-dev":
                 engine = "_serperDotDev";
                 break;
@@ -130,6 +133,72 @@ const webBrowsing = {
             return JSON.stringify(data);
           },
 
+          /**
+           * Use SearchApi
+           * SearchApi supports multiple search engines like Google Search, Bing Search, Baidu Search, Google News, YouTube, and many more.
+           * https://www.searchapi.io/
+           */
+          _searchApi: async function (query) {
+            if (!process.env.AGENT_SEARCHAPI_API_KEY) {
+              this.super.introspect(
+                `${this.caller}: I can't use SearchApi searching because the user has not defined the required API key.\nVisit: https://www.searchapi.io/ to create the API key for free.`
+              );
+              return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`;
+            }
+
+            this.super.introspect(
+              `${this.caller}: Using SearchApi to search for "${
+                query.length > 100 ? `${query.slice(0, 100)}...` : query
+              }"`
+            );
+
+            const engine = process.env.AGENT_SEARCHAPI_ENGINE;
+            const params = new URLSearchParams({
+              engine: engine,
+              q: query,
+            });
+
+            const url = `https://www.searchapi.io/api/v1/search?${params.toString()}`;
+            const { response, error } = await fetch(url, {
+              method: "GET",
+              headers: {
+                Authorization: `Bearer ${process.env.AGENT_SEARCHAPI_API_KEY}`,
+                "Content-Type": "application/json",
+                "X-SearchApi-Source": "AnythingLLM",
+              },
+            })
+              .then((res) => res.json())
+              .then((data) => {
+                return { response: data, error: null };
+              })
+              .catch((e) => {
+                return { response: null, error: e.message };
+              });
+            if (error)
+              return `There was an error searching for content. ${error}`;
+
+            const data = [];
+            if (response.hasOwnProperty("knowledge_graph"))
+              data.push(response.knowledge_graph?.description);
+            if (response.hasOwnProperty("answer_box"))
+              data.push(response.answer_box?.answer);
+            response.organic_results?.forEach((searchResult) => {
+              const { title, link, snippet } = searchResult;
+              data.push({
+                title,
+                link,
+                snippet,
+              });
+            });
+
+            if (data.length === 0)
+              return `No information was found online for the search query.`;
+            this.super.introspect(
+              `${this.caller}: I found ${data.length} results - looking over them now.`
+            );
+            return JSON.stringify(data);
+          },
+
           /**
            * Use Serper.dev
            * Free to set up, easy to use, 2,500 calls for free one-time
diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js
index c579da188..af5a460db 100644
--- a/server/utils/helpers/updateENV.js
+++ b/server/utils/helpers/updateENV.js
@@ -435,6 +435,14 @@ const KEY_MAPPING = {
     envKey: "AGENT_GSE_KEY",
     checks: [],
   },
+  AgentSearchApiKey: {
+    envKey: "AGENT_SEARCHAPI_API_KEY",
+    checks: [],
+  },
+  AgentSearchApiEngine: {
+    envKey: "AGENT_SEARCHAPI_ENGINE",
+    checks: [],
+  },
   AgentSerperApiKey: {
     envKey: "AGENT_SERPER_DEV_KEY",
     checks: [],