Files
Keep/keep-notes/lib/ai/tools/web-search.tool.ts

66 lines
2.2 KiB
TypeScript

/**
* Web Search Tool
* Uses SearXNG or Brave Search API.
*/
import { tool } from 'ai'
import { z } from 'zod'
import { toolRegistry } from './registry'
async function searchSearXNG(query: string, searxngUrl: string): Promise<any> {
const url = `${searxngUrl.replace(/\/+$/, '')}/search?q=${encodeURIComponent(query)}&format=json`
const response = await fetch(url, { headers: { 'Accept': 'application/json' } })
if (!response.ok) throw new Error(`SearXNG error: ${response.status}`)
const data = await response.json()
return (data.results || []).slice(0, 8).map((r: any) => ({
title: r.title,
url: r.url,
snippet: r.content || '',
}))
}
async function searchBrave(query: string, apiKey: string): Promise<any> {
const url = `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=8`
const response = await fetch(url, {
headers: { 'Accept': 'application/json', 'X-Subscription-Token': apiKey }
})
if (!response.ok) throw new Error(`Brave error: ${response.status}`)
const data = await response.json()
return (data.web?.results || []).map((r: any) => ({
title: r.title,
url: r.url,
snippet: r.description || '',
}))
}
toolRegistry.register({
name: 'web_search',
description: 'Search the web for information. Returns a list of results with titles, URLs and snippets.',
isInternal: false,
buildTool: (ctx) =>
tool({
description: 'Search the web for information. Returns results with titles, URLs and snippets.',
inputSchema: z.object({
query: z.string().describe('The search query'),
}),
execute: async ({ query }) => {
try {
const provider = ctx.config.WEB_SEARCH_PROVIDER || 'searxng'
if (provider === 'brave' || provider === 'both') {
const apiKey = ctx.config.BRAVE_SEARCH_API_KEY
if (apiKey) {
return await searchBrave(query, apiKey)
}
}
// Default: SearXNG
const searxngUrl = ctx.config.SEARXNG_URL || 'http://localhost:8080'
return await searchSearXNG(query, searxngUrl)
} catch (e: any) {
return { error: `Web search failed: ${e.message}` }
}
},
}),
})