From b8b55b5899a9bd5f095d8ad18e4b7ddaf5651221 Mon Sep 17 00:00:00 2001 From: Timothy Carambat 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 --- .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 ( + <> +

+ You can get a free API key{" "} + + from SearchApi. + +

+
+
+ + +
+
+ + + {/* */} +
+
+ + ); +} + 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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 , + 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: [],