diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx index 2dae3f7db4..940f6b474d 100644 --- a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx +++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx @@ -316,3 +316,13 @@ export function TavilySearchOptions({ settings }) { ); } + +export function DuckDuckGoOptions() { + return ( + <> +

+ DuckDuckGo is ready to use without any additional configuration. +

+ + ); +} diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/duckduckgo.png b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/duckduckgo.png new file mode 100644 index 0000000000..ce87627679 Binary files /dev/null and b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/duckduckgo.png differ diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx index cba4397c79..c4e9907eb1 100644 --- a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx +++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx @@ -8,6 +8,7 @@ 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 DuckDuckGoIcon from "./icons/duckduckgo.png"; import { CaretUpDown, MagnifyingGlass, @@ -24,6 +25,7 @@ import { SerplySearchOptions, SearXNGOptions, TavilySearchOptions, + DuckDuckGoOptions, } from "./SearchProviderOptions"; const SEARCH_PROVIDERS = [ @@ -35,6 +37,14 @@ const SEARCH_PROVIDERS = [ description: "Web search will be disabled until a provider and keys are provided.", }, + { + name: "DuckDuckGo", + value: "duckduckgo-engine", + logo: DuckDuckGoIcon, + options: () => , + description: + "Free and privacy-focused web search using DuckDuckGo's HTML interface.", + }, { name: "Google Search Engine", value: "google-search-engine", diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index f43118c626..9bd4a7bf1e 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -100,6 +100,7 @@ const SystemSettings = { "serply-engine", "searxng-engine", "tavily-search", + "duckduckgo-engine", ].includes(update) ) throw new Error("Invalid SERP provider."); diff --git a/server/utils/agents/aibitat/plugins/web-browsing.js b/server/utils/agents/aibitat/plugins/web-browsing.js index 31e06cab00..0c139515f0 100644 --- a/server/utils/agents/aibitat/plugins/web-browsing.js +++ b/server/utils/agents/aibitat/plugins/web-browsing.js @@ -80,6 +80,9 @@ const webBrowsing = { case "tavily-search": engine = "_tavilySearch"; break; + case "duckduckgo-engine": + engine = "_duckDuckGoEngine"; + break; default: engine = "_googleSearchEngine"; } @@ -499,6 +502,66 @@ const webBrowsing = { ); return JSON.stringify(data); }, + _duckDuckGoEngine: async function (query) { + this.super.introspect( + `${this.caller}: Using DuckDuckGo to search for "${ + query.length > 100 ? `${query.slice(0, 100)}...` : query + }"` + ); + + const searchURL = new URL("https://html.duckduckgo.com/html"); + searchURL.searchParams.append("q", query); + + const response = await fetch(searchURL.toString()); + + if (!response.ok) { + return `There was an error searching DuckDuckGo. Status: ${response.status}`; + } + + const html = await response.text(); + const data = []; + + const results = html.split('