Skip to content

Commit a2eced0

Browse files
authored
DuckDuckGo web search agent skill support (#2584)
* duckduckgo web search agent skill support * move ddg to first option in dropdown menu * lint * add duckduckgo option stating no config required
1 parent 04e2920 commit a2eced0

File tree

5 files changed

+84
-0
lines changed

5 files changed

+84
-0
lines changed

frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx

+10
Original file line numberDiff line numberDiff line change
@@ -316,3 +316,13 @@ export function TavilySearchOptions({ settings }) {
316316
</>
317317
);
318318
}
319+
320+
export function DuckDuckGoOptions() {
321+
return (
322+
<>
323+
<p className="text-sm text-white/60 my-2">
324+
DuckDuckGo is ready to use without any additional configuration.
325+
</p>
326+
</>
327+
);
328+
}
Loading

frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import BingSearchIcon from "./icons/bing.png";
88
import SerplySearchIcon from "./icons/serply.png";
99
import SearXNGSearchIcon from "./icons/searxng.png";
1010
import TavilySearchIcon from "./icons/tavily.svg";
11+
import DuckDuckGoIcon from "./icons/duckduckgo.png";
1112
import {
1213
CaretUpDown,
1314
MagnifyingGlass,
@@ -24,6 +25,7 @@ import {
2425
SerplySearchOptions,
2526
SearXNGOptions,
2627
TavilySearchOptions,
28+
DuckDuckGoOptions,
2729
} from "./SearchProviderOptions";
2830

2931
const SEARCH_PROVIDERS = [
@@ -35,6 +37,14 @@ const SEARCH_PROVIDERS = [
3537
description:
3638
"Web search will be disabled until a provider and keys are provided.",
3739
},
40+
{
41+
name: "DuckDuckGo",
42+
value: "duckduckgo-engine",
43+
logo: DuckDuckGoIcon,
44+
options: () => <DuckDuckGoOptions />,
45+
description:
46+
"Free and privacy-focused web search using DuckDuckGo's HTML interface.",
47+
},
3848
{
3949
name: "Google Search Engine",
4050
value: "google-search-engine",

server/models/systemSettings.js

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const SystemSettings = {
100100
"serply-engine",
101101
"searxng-engine",
102102
"tavily-search",
103+
"duckduckgo-engine",
103104
].includes(update)
104105
)
105106
throw new Error("Invalid SERP provider.");

server/utils/agents/aibitat/plugins/web-browsing.js

+63
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ const webBrowsing = {
8080
case "tavily-search":
8181
engine = "_tavilySearch";
8282
break;
83+
case "duckduckgo-engine":
84+
engine = "_duckDuckGoEngine";
85+
break;
8386
default:
8487
engine = "_googleSearchEngine";
8588
}
@@ -499,6 +502,66 @@ const webBrowsing = {
499502
);
500503
return JSON.stringify(data);
501504
},
505+
_duckDuckGoEngine: async function (query) {
506+
this.super.introspect(
507+
`${this.caller}: Using DuckDuckGo to search for "${
508+
query.length > 100 ? `${query.slice(0, 100)}...` : query
509+
}"`
510+
);
511+
512+
const searchURL = new URL("https://html.duckduckgo.com/html");
513+
searchURL.searchParams.append("q", query);
514+
515+
const response = await fetch(searchURL.toString());
516+
517+
if (!response.ok) {
518+
return `There was an error searching DuckDuckGo. Status: ${response.status}`;
519+
}
520+
521+
const html = await response.text();
522+
const data = [];
523+
524+
const results = html.split('<div class="result results_links');
525+
526+
// Skip first element since it's before the first result
527+
for (let i = 1; i < results.length; i++) {
528+
const result = results[i];
529+
530+
// Extract title
531+
const titleMatch = result.match(
532+
/<a[^>]*class="result__a"[^>]*>(.*?)<\/a>/
533+
);
534+
const title = titleMatch ? titleMatch[1].trim() : "";
535+
536+
// Extract URL
537+
const urlMatch = result.match(
538+
/<a[^>]*class="result__a"[^>]*href="([^"]*)">/
539+
);
540+
const link = urlMatch ? urlMatch[1] : "";
541+
542+
// Extract snippet
543+
const snippetMatch = result.match(
544+
/<a[^>]*class="result__snippet"[^>]*>(.*?)<\/a>/
545+
);
546+
const snippet = snippetMatch
547+
? snippetMatch[1].replace(/<\/?b>/g, "").trim()
548+
: "";
549+
550+
if (title && link && snippet) {
551+
data.push({ title, link, snippet });
552+
}
553+
}
554+
555+
if (data.length === 0) {
556+
return `No information was found online for the search query.`;
557+
}
558+
559+
this.super.introspect(
560+
`${this.caller}: I found ${data.length} results - looking over them now.`
561+
);
562+
563+
return JSON.stringify(data);
564+
},
502565
});
503566
},
504567
};

0 commit comments

Comments
 (0)