Skip to content

Commit 209b0f1

Browse files
authored
feat(tools): added duckduckgo (#2258)
1 parent e067b58 commit 209b0f1

File tree

11 files changed

+397
-0
lines changed

11 files changed

+397
-0
lines changed

apps/docs/components/icons.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4129,3 +4129,27 @@ export function CursorIcon(props: SVGProps<SVGSVGElement>) {
41294129
</svg>
41304130
)
41314131
}
4132+
4133+
export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
4134+
return (
4135+
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
4136+
<circle r='108' fill='#d53' />
4137+
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
4138+
<path
4139+
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
4140+
fill='#ddd'
4141+
/>
4142+
<path d='M25 95S1 57 1 32c0-47 31-7 31-44S1-58 1-58c-15-19-44-15-44-15l7 4s-7 2-9 4 19-3 28 5c-37 3-31 33-31 33l21 120' />
4143+
<path d='M25-1l38-10c34 5-29 24-33 23C0 7 9 32 45 24s9 20-24 9C-26 20-1-3 25-1' fill='#fc0' />
4144+
<path
4145+
d='M15 78l2-3c22 8 23 11 22-9s0-20-23-3c0-5-13-3-15 0-21-9-23-12-22 2 2 29 1 24 21 14'
4146+
fill='#6b5'
4147+
/>
4148+
<path d='M-1 67v12c1 2 17 2 17-2s-8 3-13 1-2-13-2-13' fill='#4a4' />
4149+
<path
4150+
d='M-23-32c-5-6-18-1-15 7 1-4 8-10 15-7m32 0c1-6 11-7 14-1-4-2-10-2-14 1m-33 16a2 2 0 1 1 0 1m-8 3a7 7 0 1 0 0-1m52-6a2 2 0 1 1 0 1m-6 3a6 6 0 1 0 0-1'
4151+
fill='#148'
4152+
/>
4153+
</svg>
4154+
)
4155+
}

apps/docs/components/ui/icon-mapping.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
DiscordIcon,
2121
DocumentIcon,
2222
DropboxIcon,
23+
DuckDuckGoIcon,
2324
DynamoDBIcon,
2425
ElasticsearchIcon,
2526
ElevenLabsIcon,
@@ -212,6 +213,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
212213
elevenlabs: ElevenLabsIcon,
213214
elasticsearch: ElasticsearchIcon,
214215
dynamodb: DynamoDBIcon,
216+
duckduckgo: DuckDuckGoIcon,
215217
dropbox: DropboxIcon,
216218
discord: DiscordIcon,
217219
datadog: DatadogIcon,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
title: DuckDuckGo
3+
description: Search with DuckDuckGo
4+
---
5+
6+
import { BlockInfoCard } from "@/components/ui/block-info-card"
7+
8+
<BlockInfoCard
9+
type="duckduckgo"
10+
color="#FFFFFF"
11+
/>
12+
13+
{/* MANUAL-CONTENT-START:intro */}
14+
[DuckDuckGo](https://duckduckgo.com/) is a privacy-focused web search engine that delivers instant answers, abstracts, related topics, and more — without tracking you or your searches. DuckDuckGo makes it easy to find information without any user profiling or targeted ads.
15+
16+
With DuckDuckGo in Sim, you can:
17+
18+
- **Search the web**: Instantly find answers, facts, and overviews for a given search query
19+
- **Get direct answers**: Retrieve specific responses for calculations, conversions, or factual queries
20+
- **Access abstracts**: Receive short summaries or descriptions for your search topics
21+
- **Fetch related topics**: Discover links and references relevant to your search
22+
- **Filter output**: Optionally remove HTML or skip disambiguation for cleaner results
23+
24+
These features enable your Sim agents to automate access to fresh web knowledge — from surfacing facts in a workflow, to enriching documents and analysis with up-to-date information. Because DuckDuckGo’s Instant Answers API is open and does not require an API key, it’s simple and privacy-safe to integrate into your automated business processes.
25+
{/* MANUAL-CONTENT-END */}
26+
27+
28+
## Usage Instructions
29+
30+
Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, related topics, and more. Free to use without an API key.
31+
32+
33+
34+
## Tools
35+
36+
### `duckduckgo_search`
37+
38+
Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, and related topics for your query. Free to use without an API key.
39+
40+
#### Input
41+
42+
| Parameter | Type | Required | Description |
43+
| --------- | ---- | -------- | ----------- |
44+
| `query` | string | Yes | The search query to execute |
45+
| `noHtml` | boolean | No | Remove HTML from text in results \(default: true\) |
46+
| `skipDisambig` | boolean | No | Skip disambiguation results \(default: false\) |
47+
48+
#### Output
49+
50+
| Parameter | Type | Description |
51+
| --------- | ---- | ----------- |
52+
| `heading` | string | The heading/title of the instant answer |
53+
| `abstract` | string | A short abstract summary of the topic |
54+
| `abstractText` | string | Plain text version of the abstract |
55+
| `abstractSource` | string | The source of the abstract \(e.g., Wikipedia\) |
56+
| `abstractURL` | string | URL to the source of the abstract |
57+
| `image` | string | URL to an image related to the topic |
58+
| `answer` | string | Direct answer if available \(e.g., for calculations\) |
59+
| `answerType` | string | Type of the answer \(e.g., calc, ip, etc.\) |
60+
| `type` | string | Response type: A \(article\), D \(disambiguation\), C \(category\), N \(name\), E \(exclusive\) |
61+
| `relatedTopics` | array | Array of related topics with URLs and descriptions |
62+
63+
64+
65+
## Notes
66+
67+
- Category: `tools`
68+
- Type: `duckduckgo`

apps/docs/content/docs/en/tools/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"datadog",
1616
"discord",
1717
"dropbox",
18+
"duckduckgo",
1819
"dynamodb",
1920
"elasticsearch",
2021
"elevenlabs",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { DuckDuckGoIcon } from '@/components/icons'
2+
import type { BlockConfig } from '@/blocks/types'
3+
import type { DuckDuckGoResponse } from '@/tools/duckduckgo/types'
4+
5+
export const DuckDuckGoBlock: BlockConfig<DuckDuckGoResponse> = {
6+
type: 'duckduckgo',
7+
name: 'DuckDuckGo',
8+
description: 'Search with DuckDuckGo',
9+
longDescription:
10+
'Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, related topics, and more. Free to use without an API key.',
11+
docsLink: 'https://docs.sim.ai/tools/duckduckgo',
12+
category: 'tools',
13+
bgColor: '#FFFFFF',
14+
icon: DuckDuckGoIcon,
15+
subBlocks: [
16+
{
17+
id: 'query',
18+
title: 'Search Query',
19+
type: 'long-input',
20+
placeholder: 'Enter your search query...',
21+
required: true,
22+
},
23+
{
24+
id: 'noHtml',
25+
title: 'Remove HTML',
26+
type: 'switch',
27+
defaultValue: true,
28+
},
29+
{
30+
id: 'skipDisambig',
31+
title: 'Skip Disambiguation',
32+
type: 'switch',
33+
},
34+
],
35+
tools: {
36+
access: ['duckduckgo_search'],
37+
config: {
38+
tool: () => 'duckduckgo_search',
39+
},
40+
},
41+
inputs: {
42+
query: { type: 'string', description: 'Search query terms' },
43+
noHtml: { type: 'boolean', description: 'Remove HTML from text in results' },
44+
skipDisambig: { type: 'boolean', description: 'Skip disambiguation results' },
45+
},
46+
outputs: {
47+
heading: { type: 'string', description: 'The heading/title of the instant answer' },
48+
abstract: { type: 'string', description: 'A short abstract summary of the topic' },
49+
abstractText: { type: 'string', description: 'Plain text version of the abstract' },
50+
abstractSource: { type: 'string', description: 'The source of the abstract' },
51+
abstractURL: { type: 'string', description: 'URL to the source of the abstract' },
52+
image: { type: 'string', description: 'URL to an image related to the topic' },
53+
answer: { type: 'string', description: 'Direct answer if available' },
54+
answerType: { type: 'string', description: 'Type of the answer' },
55+
type: { type: 'string', description: 'Response type (A, D, C, N, E)' },
56+
relatedTopics: { type: 'json', description: 'Array of related topics' },
57+
results: { type: 'json', description: 'Array of external link results' },
58+
},
59+
}

apps/sim/blocks/registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { CursorBlock } from '@/blocks/blocks/cursor'
1818
import { DatadogBlock } from '@/blocks/blocks/datadog'
1919
import { DiscordBlock } from '@/blocks/blocks/discord'
2020
import { DropboxBlock } from '@/blocks/blocks/dropbox'
21+
import { DuckDuckGoBlock } from '@/blocks/blocks/duckduckgo'
2122
import { DynamoDBBlock } from '@/blocks/blocks/dynamodb'
2223
import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
2324
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
@@ -157,6 +158,7 @@ export const registry: Record<string, BlockConfig> = {
157158
datadog: DatadogBlock,
158159
discord: DiscordBlock,
159160
dropbox: DropboxBlock,
161+
duckduckgo: DuckDuckGoBlock,
160162
elevenlabs: ElevenLabsBlock,
161163
elasticsearch: ElasticsearchBlock,
162164
evaluator: EvaluatorBlock,

apps/sim/components/icons.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4129,3 +4129,27 @@ export function CursorIcon(props: SVGProps<SVGSVGElement>) {
41294129
</svg>
41304130
)
41314131
}
4132+
4133+
export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
4134+
return (
4135+
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
4136+
<circle r='108' fill='#d53' />
4137+
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
4138+
<path
4139+
d='M-32-55C-62-48-51-6-51-6l19 93 7 3M-39-73h-8l11 4s-11 0-11 7c24-1 35 5 35 5'
4140+
fill='#ddd'
4141+
/>
4142+
<path d='M25 95S1 57 1 32c0-47 31-7 31-44S1-58 1-58c-15-19-44-15-44-15l7 4s-7 2-9 4 19-3 28 5c-37 3-31 33-31 33l21 120' />
4143+
<path d='M25-1l38-10c34 5-29 24-33 23C0 7 9 32 45 24s9 20-24 9C-26 20-1-3 25-1' fill='#fc0' />
4144+
<path
4145+
d='M15 78l2-3c22 8 23 11 22-9s0-20-23-3c0-5-13-3-15 0-21-9-23-12-22 2 2 29 1 24 21 14'
4146+
fill='#6b5'
4147+
/>
4148+
<path d='M-1 67v12c1 2 17 2 17-2s-8 3-13 1-2-13-2-13' fill='#4a4' />
4149+
<path
4150+
d='M-23-32c-5-6-18-1-15 7 1-4 8-10 15-7m32 0c1-6 11-7 14-1-4-2-10-2-14 1m-33 16a2 2 0 1 1 0 1m-8 3a7 7 0 1 0 0-1m52-6a2 2 0 1 1 0 1m-6 3a6 6 0 1 0 0-1'
4151+
fill='#148'
4152+
/>
4153+
</svg>
4154+
)
4155+
}

apps/sim/tools/duckduckgo/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { searchTool } from '@/tools/duckduckgo/search'
2+
3+
export const duckduckgoSearchTool = searchTool
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import type { DuckDuckGoSearchParams, DuckDuckGoSearchResponse } from '@/tools/duckduckgo/types'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
export const searchTool: ToolConfig<DuckDuckGoSearchParams, DuckDuckGoSearchResponse> = {
5+
id: 'duckduckgo_search',
6+
name: 'DuckDuckGo Search',
7+
description:
8+
'Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, and related topics for your query. Free to use without an API key.',
9+
version: '1.0.0',
10+
11+
params: {
12+
query: {
13+
type: 'string',
14+
required: true,
15+
visibility: 'user-or-llm',
16+
description: 'The search query to execute',
17+
},
18+
noHtml: {
19+
type: 'boolean',
20+
required: false,
21+
visibility: 'user-only',
22+
description: 'Remove HTML from text in results (default: true)',
23+
},
24+
skipDisambig: {
25+
type: 'boolean',
26+
required: false,
27+
visibility: 'user-only',
28+
description: 'Skip disambiguation results (default: false)',
29+
},
30+
},
31+
32+
request: {
33+
url: (params) => {
34+
const baseUrl = 'https://api.duckduckgo.com/'
35+
const searchParams = new URLSearchParams({
36+
q: params.query,
37+
format: 'json',
38+
no_html: params.noHtml !== false ? '1' : '0',
39+
skip_disambig: params.skipDisambig ? '1' : '0',
40+
})
41+
return `${baseUrl}?${searchParams.toString()}`
42+
},
43+
method: 'GET',
44+
headers: () => ({
45+
Accept: 'application/json',
46+
}),
47+
},
48+
49+
transformResponse: async (response: Response) => {
50+
const data = await response.json()
51+
52+
// Map related topics
53+
const relatedTopics = (data.RelatedTopics || []).map((topic: any) => ({
54+
FirstURL: topic.FirstURL,
55+
Text: topic.Text,
56+
Result: topic.Result,
57+
Icon: topic.Icon
58+
? {
59+
URL: topic.Icon.URL,
60+
Height: topic.Icon.Height,
61+
Width: topic.Icon.Width,
62+
}
63+
: undefined,
64+
}))
65+
66+
// Map results (external links)
67+
const results = (data.Results || []).map((result: any) => ({
68+
FirstURL: result.FirstURL,
69+
Text: result.Text,
70+
Result: result.Result,
71+
Icon: result.Icon
72+
? {
73+
URL: result.Icon.URL,
74+
Height: result.Icon.Height,
75+
Width: result.Icon.Width,
76+
}
77+
: undefined,
78+
}))
79+
80+
return {
81+
success: true,
82+
output: {
83+
heading: data.Heading || '',
84+
abstract: data.Abstract || '',
85+
abstractText: data.AbstractText || '',
86+
abstractSource: data.AbstractSource || '',
87+
abstractURL: data.AbstractURL || '',
88+
image: data.Image || '',
89+
answer: data.Answer || '',
90+
answerType: data.AnswerType || '',
91+
type: data.Type || '',
92+
relatedTopics,
93+
results,
94+
},
95+
}
96+
},
97+
98+
outputs: {
99+
heading: {
100+
type: 'string',
101+
description: 'The heading/title of the instant answer',
102+
},
103+
abstract: {
104+
type: 'string',
105+
description: 'A short abstract summary of the topic',
106+
},
107+
abstractText: {
108+
type: 'string',
109+
description: 'Plain text version of the abstract',
110+
},
111+
abstractSource: {
112+
type: 'string',
113+
description: 'The source of the abstract (e.g., Wikipedia)',
114+
},
115+
abstractURL: {
116+
type: 'string',
117+
description: 'URL to the source of the abstract',
118+
},
119+
image: {
120+
type: 'string',
121+
description: 'URL to an image related to the topic',
122+
},
123+
answer: {
124+
type: 'string',
125+
description: 'Direct answer if available (e.g., for calculations)',
126+
},
127+
answerType: {
128+
type: 'string',
129+
description: 'Type of the answer (e.g., calc, ip, etc.)',
130+
},
131+
type: {
132+
type: 'string',
133+
description:
134+
'Response type: A (article), D (disambiguation), C (category), N (name), E (exclusive)',
135+
},
136+
relatedTopics: {
137+
type: 'array',
138+
description: 'Array of related topics with URLs and descriptions',
139+
items: {
140+
type: 'object',
141+
properties: {
142+
FirstURL: { type: 'string', description: 'URL to the related topic' },
143+
Text: { type: 'string', description: 'Description of the related topic' },
144+
Result: { type: 'string', description: 'HTML result snippet' },
145+
},
146+
},
147+
},
148+
results: {
149+
type: 'array',
150+
description: 'Array of external link results',
151+
items: {
152+
type: 'object',
153+
properties: {
154+
FirstURL: { type: 'string', description: 'URL of the result' },
155+
Text: { type: 'string', description: 'Description of the result' },
156+
Result: { type: 'string', description: 'HTML result snippet' },
157+
},
158+
},
159+
},
160+
},
161+
}

0 commit comments

Comments
 (0)