feat(tools): added duckduckgo (#2258)

This commit is contained in:
Waleed
2025-12-08 17:34:18 -08:00
committed by GitHub
parent e067b584ee
commit 209b0f1906
11 changed files with 397 additions and 0 deletions

View File

@@ -4129,3 +4129,27 @@ export function CursorIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
<circle r='108' fill='#d53' />
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
<path
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'
fill='#ddd'
/>
<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' />
<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' />
<path
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'
fill='#6b5'
/>
<path d='M-1 67v12c1 2 17 2 17-2s-8 3-13 1-2-13-2-13' fill='#4a4' />
<path
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'
fill='#148'
/>
</svg>
)
}

View File

@@ -20,6 +20,7 @@ import {
DiscordIcon,
DocumentIcon,
DropboxIcon,
DuckDuckGoIcon,
DynamoDBIcon,
ElasticsearchIcon,
ElevenLabsIcon,
@@ -212,6 +213,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
elevenlabs: ElevenLabsIcon,
elasticsearch: ElasticsearchIcon,
dynamodb: DynamoDBIcon,
duckduckgo: DuckDuckGoIcon,
dropbox: DropboxIcon,
discord: DiscordIcon,
datadog: DatadogIcon,

View File

@@ -0,0 +1,68 @@
---
title: DuckDuckGo
description: Search with DuckDuckGo
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="duckduckgo"
color="#FFFFFF"
/>
{/* MANUAL-CONTENT-START:intro */}
[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.
With DuckDuckGo in Sim, you can:
- **Search the web**: Instantly find answers, facts, and overviews for a given search query
- **Get direct answers**: Retrieve specific responses for calculations, conversions, or factual queries
- **Access abstracts**: Receive short summaries or descriptions for your search topics
- **Fetch related topics**: Discover links and references relevant to your search
- **Filter output**: Optionally remove HTML or skip disambiguation for cleaner results
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 DuckDuckGos Instant Answers API is open and does not require an API key, its simple and privacy-safe to integrate into your automated business processes.
{/* MANUAL-CONTENT-END */}
## Usage Instructions
Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, related topics, and more. Free to use without an API key.
## Tools
### `duckduckgo_search`
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.
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `query` | string | Yes | The search query to execute |
| `noHtml` | boolean | No | Remove HTML from text in results \(default: true\) |
| `skipDisambig` | boolean | No | Skip disambiguation results \(default: false\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `heading` | string | The heading/title of the instant answer |
| `abstract` | string | A short abstract summary of the topic |
| `abstractText` | string | Plain text version of the abstract |
| `abstractSource` | string | The source of the abstract \(e.g., Wikipedia\) |
| `abstractURL` | string | URL to the source of the abstract |
| `image` | string | URL to an image related to the topic |
| `answer` | string | Direct answer if available \(e.g., for calculations\) |
| `answerType` | string | Type of the answer \(e.g., calc, ip, etc.\) |
| `type` | string | Response type: A \(article\), D \(disambiguation\), C \(category\), N \(name\), E \(exclusive\) |
| `relatedTopics` | array | Array of related topics with URLs and descriptions |
## Notes
- Category: `tools`
- Type: `duckduckgo`

View File

@@ -15,6 +15,7 @@
"datadog",
"discord",
"dropbox",
"duckduckgo",
"dynamodb",
"elasticsearch",
"elevenlabs",

View File

@@ -0,0 +1,59 @@
import { DuckDuckGoIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import type { DuckDuckGoResponse } from '@/tools/duckduckgo/types'
export const DuckDuckGoBlock: BlockConfig<DuckDuckGoResponse> = {
type: 'duckduckgo',
name: 'DuckDuckGo',
description: 'Search with DuckDuckGo',
longDescription:
'Search the web using DuckDuckGo Instant Answers API. Returns instant answers, abstracts, related topics, and more. Free to use without an API key.',
docsLink: 'https://docs.sim.ai/tools/duckduckgo',
category: 'tools',
bgColor: '#FFFFFF',
icon: DuckDuckGoIcon,
subBlocks: [
{
id: 'query',
title: 'Search Query',
type: 'long-input',
placeholder: 'Enter your search query...',
required: true,
},
{
id: 'noHtml',
title: 'Remove HTML',
type: 'switch',
defaultValue: true,
},
{
id: 'skipDisambig',
title: 'Skip Disambiguation',
type: 'switch',
},
],
tools: {
access: ['duckduckgo_search'],
config: {
tool: () => 'duckduckgo_search',
},
},
inputs: {
query: { type: 'string', description: 'Search query terms' },
noHtml: { type: 'boolean', description: 'Remove HTML from text in results' },
skipDisambig: { type: 'boolean', description: 'Skip disambiguation results' },
},
outputs: {
heading: { type: 'string', description: 'The heading/title of the instant answer' },
abstract: { type: 'string', description: 'A short abstract summary of the topic' },
abstractText: { type: 'string', description: 'Plain text version of the abstract' },
abstractSource: { type: 'string', description: 'The source of the abstract' },
abstractURL: { type: 'string', description: 'URL to the source of the abstract' },
image: { type: 'string', description: 'URL to an image related to the topic' },
answer: { type: 'string', description: 'Direct answer if available' },
answerType: { type: 'string', description: 'Type of the answer' },
type: { type: 'string', description: 'Response type (A, D, C, N, E)' },
relatedTopics: { type: 'json', description: 'Array of related topics' },
results: { type: 'json', description: 'Array of external link results' },
},
}

View File

@@ -18,6 +18,7 @@ import { CursorBlock } from '@/blocks/blocks/cursor'
import { DatadogBlock } from '@/blocks/blocks/datadog'
import { DiscordBlock } from '@/blocks/blocks/discord'
import { DropboxBlock } from '@/blocks/blocks/dropbox'
import { DuckDuckGoBlock } from '@/blocks/blocks/duckduckgo'
import { DynamoDBBlock } from '@/blocks/blocks/dynamodb'
import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
@@ -157,6 +158,7 @@ export const registry: Record<string, BlockConfig> = {
datadog: DatadogBlock,
discord: DiscordBlock,
dropbox: DropboxBlock,
duckduckgo: DuckDuckGoBlock,
elevenlabs: ElevenLabsBlock,
elasticsearch: ElasticsearchBlock,
evaluator: EvaluatorBlock,

View File

@@ -4129,3 +4129,27 @@ export function CursorIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function DuckDuckGoIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='-108 -108 216 216'>
<circle r='108' fill='#d53' />
<circle r='96' fill='none' stroke='#ffffff' stroke-width='7' />
<path
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'
fill='#ddd'
/>
<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' />
<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' />
<path
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'
fill='#6b5'
/>
<path d='M-1 67v12c1 2 17 2 17-2s-8 3-13 1-2-13-2-13' fill='#4a4' />
<path
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'
fill='#148'
/>
</svg>
)
}

View File

@@ -0,0 +1,3 @@
import { searchTool } from '@/tools/duckduckgo/search'
export const duckduckgoSearchTool = searchTool

View File

@@ -0,0 +1,161 @@
import type { DuckDuckGoSearchParams, DuckDuckGoSearchResponse } from '@/tools/duckduckgo/types'
import type { ToolConfig } from '@/tools/types'
export const searchTool: ToolConfig<DuckDuckGoSearchParams, DuckDuckGoSearchResponse> = {
id: 'duckduckgo_search',
name: 'DuckDuckGo Search',
description:
'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.',
version: '1.0.0',
params: {
query: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The search query to execute',
},
noHtml: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Remove HTML from text in results (default: true)',
},
skipDisambig: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Skip disambiguation results (default: false)',
},
},
request: {
url: (params) => {
const baseUrl = 'https://api.duckduckgo.com/'
const searchParams = new URLSearchParams({
q: params.query,
format: 'json',
no_html: params.noHtml !== false ? '1' : '0',
skip_disambig: params.skipDisambig ? '1' : '0',
})
return `${baseUrl}?${searchParams.toString()}`
},
method: 'GET',
headers: () => ({
Accept: 'application/json',
}),
},
transformResponse: async (response: Response) => {
const data = await response.json()
// Map related topics
const relatedTopics = (data.RelatedTopics || []).map((topic: any) => ({
FirstURL: topic.FirstURL,
Text: topic.Text,
Result: topic.Result,
Icon: topic.Icon
? {
URL: topic.Icon.URL,
Height: topic.Icon.Height,
Width: topic.Icon.Width,
}
: undefined,
}))
// Map results (external links)
const results = (data.Results || []).map((result: any) => ({
FirstURL: result.FirstURL,
Text: result.Text,
Result: result.Result,
Icon: result.Icon
? {
URL: result.Icon.URL,
Height: result.Icon.Height,
Width: result.Icon.Width,
}
: undefined,
}))
return {
success: true,
output: {
heading: data.Heading || '',
abstract: data.Abstract || '',
abstractText: data.AbstractText || '',
abstractSource: data.AbstractSource || '',
abstractURL: data.AbstractURL || '',
image: data.Image || '',
answer: data.Answer || '',
answerType: data.AnswerType || '',
type: data.Type || '',
relatedTopics,
results,
},
}
},
outputs: {
heading: {
type: 'string',
description: 'The heading/title of the instant answer',
},
abstract: {
type: 'string',
description: 'A short abstract summary of the topic',
},
abstractText: {
type: 'string',
description: 'Plain text version of the abstract',
},
abstractSource: {
type: 'string',
description: 'The source of the abstract (e.g., Wikipedia)',
},
abstractURL: {
type: 'string',
description: 'URL to the source of the abstract',
},
image: {
type: 'string',
description: 'URL to an image related to the topic',
},
answer: {
type: 'string',
description: 'Direct answer if available (e.g., for calculations)',
},
answerType: {
type: 'string',
description: 'Type of the answer (e.g., calc, ip, etc.)',
},
type: {
type: 'string',
description:
'Response type: A (article), D (disambiguation), C (category), N (name), E (exclusive)',
},
relatedTopics: {
type: 'array',
description: 'Array of related topics with URLs and descriptions',
items: {
type: 'object',
properties: {
FirstURL: { type: 'string', description: 'URL to the related topic' },
Text: { type: 'string', description: 'Description of the related topic' },
Result: { type: 'string', description: 'HTML result snippet' },
},
},
},
results: {
type: 'array',
description: 'Array of external link results',
items: {
type: 'object',
properties: {
FirstURL: { type: 'string', description: 'URL of the result' },
Text: { type: 'string', description: 'Description of the result' },
Result: { type: 'string', description: 'HTML result snippet' },
},
},
},
},
}

View File

@@ -0,0 +1,51 @@
// Common types for DuckDuckGo tools
import type { ToolResponse } from '@/tools/types'
// Search tool types
export interface DuckDuckGoSearchParams {
query: string
noHtml?: boolean
skipDisambig?: boolean
}
export interface DuckDuckGoRelatedTopic {
FirstURL?: string
Text?: string
Result?: string
Icon?: {
URL?: string
Height?: string
Width?: string
}
}
export interface DuckDuckGoResult {
FirstURL?: string
Text?: string
Result?: string
Icon?: {
URL?: string
Height?: string
Width?: string
}
}
export interface DuckDuckGoSearchOutput {
heading: string
abstract: string
abstractText: string
abstractSource: string
abstractURL: string
image: string
answer: string
answerType: string
type: string
relatedTopics: DuckDuckGoRelatedTopic[]
results: DuckDuckGoResult[]
}
export interface DuckDuckGoSearchResponse extends ToolResponse {
output: DuckDuckGoSearchOutput
}
export type DuckDuckGoResponse = DuckDuckGoSearchResponse

View File

@@ -153,6 +153,7 @@ import {
dropboxSearchTool,
dropboxUploadTool,
} from '@/tools/dropbox'
import { duckduckgoSearchTool } from '@/tools/duckduckgo'
import {
dynamodbDeleteTool,
dynamodbGetTool,
@@ -1587,6 +1588,7 @@ export const tools: Record<string, ToolConfig> = {
dropbox_get_metadata: dropboxGetMetadataTool,
dropbox_create_shared_link: dropboxCreateSharedLinkTool,
dropbox_search: dropboxSearchTool,
duckduckgo_search: duckduckgoSearchTool,
mongodb_query: mongodbQueryTool,
mongodb_insert: mongodbInsertTool,
mongodb_update: mongodbUpdateTool,