diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx index 1e53498576..2dae3f7db4 100644 --- a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx +++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx @@ -281,3 +281,38 @@ export function SearXNGOptions({ settings }) { ); } + +export function TavilySearchOptions({ settings }) { + return ( + <> +

+ You can get an API key{" "} + + from Tavily. + +

+
+
+ + +
+
+ + ); +} diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/tavily.svg b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/tavily.svg new file mode 100644 index 0000000000..ce15510577 --- /dev/null +++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/tavily.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx index 345d3ef05e..cba4397c79 100644 --- a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx +++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx @@ -7,6 +7,7 @@ import SerperDotDevIcon from "./icons/serper.png"; import BingSearchIcon from "./icons/bing.png"; import SerplySearchIcon from "./icons/serply.png"; import SearXNGSearchIcon from "./icons/searxng.png"; +import TavilySearchIcon from "./icons/tavily.svg"; import { CaretUpDown, MagnifyingGlass, @@ -22,6 +23,7 @@ import { BingSearchOptions, SerplySearchOptions, SearXNGOptions, + TavilySearchOptions, } from "./SearchProviderOptions"; const SEARCH_PROVIDERS = [ @@ -81,6 +83,14 @@ const SEARCH_PROVIDERS = [ description: "Free, open-source, internet meta-search engine with no tracking.", }, + { + name: "Tavily Search", + value: "tavily-search", + logo: TavilySearchIcon, + options: (settings) => , + description: + "Tavily Search API. Offers a free tier with 1000 queries per month.", + }, ]; export default function AgentWebSearchSelection({ diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index c510001917..e4c0fa9d9b 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -103,6 +103,7 @@ const SystemSettings = { "bing-search", "serply-engine", "searxng-engine", + "tavily-search", ].includes(update) ) throw new Error("Invalid SERP provider."); @@ -242,6 +243,7 @@ const SystemSettings = { AgentBingSearchApiKey: !!process.env.AGENT_BING_SEARCH_API_KEY || null, AgentSerplyApiKey: !!process.env.AGENT_SERPLY_API_KEY || null, AgentSearXNGApiUrl: process.env.AGENT_SEARXNG_API_URL || null, + AgentTavilyApiKey: !!process.env.AGENT_TAVILY_API_KEY || null, }; }, diff --git a/server/utils/agents/aibitat/plugins/web-browsing.js b/server/utils/agents/aibitat/plugins/web-browsing.js index 76849056ee..31e06cab00 100644 --- a/server/utils/agents/aibitat/plugins/web-browsing.js +++ b/server/utils/agents/aibitat/plugins/web-browsing.js @@ -77,6 +77,9 @@ const webBrowsing = { case "searxng-engine": engine = "_searXNGEngine"; break; + case "tavily-search": + engine = "_tavilySearch"; + break; default: engine = "_googleSearchEngine"; } @@ -436,6 +439,59 @@ const webBrowsing = { }); }); + 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); + }, + _tavilySearch: async function (query) { + if (!process.env.AGENT_TAVILY_API_KEY) { + this.super.introspect( + `${this.caller}: I can't use Tavily searching because the user has not defined the required API key.\nVisit: https://tavily.com/ to create the API key.` + ); + 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 Tavily to search for "${ + query.length > 100 ? `${query.slice(0, 100)}...` : query + }"` + ); + + const url = "https://api.tavily.com/search"; + const { response, error } = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + api_key: process.env.AGENT_TAVILY_API_KEY, + query: query, + }), + }) + .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 = []; + response.results?.forEach((searchResult) => { + const { title, url, content } = searchResult; + data.push({ + title, + link: url, + snippet: content, + }); + }); + if (data.length === 0) return `No information was found online for the search query.`; this.super.introspect( diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js index e898d4b098..db5cfe0e3a 100644 --- a/server/utils/helpers/updateENV.js +++ b/server/utils/helpers/updateENV.js @@ -469,6 +469,10 @@ const KEY_MAPPING = { envKey: "AGENT_SEARXNG_API_URL", checks: [], }, + AgentTavilyApiKey: { + envKey: "AGENT_TAVILY_API_KEY", + checks: [], + }, // TTS/STT Integration ENVS TextToSpeechProvider: {