diff --git a/api/app/clients/tools/index.js b/api/app/clients/tools/index.js index 60a7419904..301e94a398 100644 --- a/api/app/clients/tools/index.js +++ b/api/app/clients/tools/index.js @@ -18,6 +18,7 @@ const StructuredACS = require('./structured/AzureAISearch'); const CodeSherpaTools = require('./structured/CodeSherpaTools'); const StructuredWolfram = require('./structured/Wolfram'); const TavilySearchResults = require('./structured/TavilySearchResults'); +const TraversaalSearch = require('./structured/TraversaalSearch'); module.exports = { availableTools, @@ -39,4 +40,5 @@ module.exports = { CodeSherpaTools, StructuredWolfram, TavilySearchResults, + TraversaalSearch, }; diff --git a/api/app/clients/tools/manifest.json b/api/app/clients/tools/manifest.json index 3a79ec02b1..3daaf9dd3b 100644 --- a/api/app/clients/tools/manifest.json +++ b/api/app/clients/tools/manifest.json @@ -1,4 +1,17 @@ [ + { + "name": "Traversaal", + "pluginKey": "traversaal_search", + "description": "Traversaal is a robust search API tailored for LLM Agents. Get an API key here: https://api.traversaal.ai", + "icon": "https://traversaal.ai/favicon.ico", + "authConfig": [ + { + "authField": "TRAVERSAAL_API_KEY", + "label": "Traversaal API Key", + "description": "Get your API key here: https://api.traversaal.ai" + } + ] + }, { "name": "Google", "pluginKey": "google", @@ -111,7 +124,7 @@ { "name": "Tavily Search", "pluginKey": "tavily_search_results_json", - "description": "Tavily Search is a robust search API tailored specifically for LLM Agents. It seamlessly integrates with diverse data sources to ensure a superior, relevant search experience.", + "description": "Tavily Search is a robust search API tailored for LLM Agents. It seamlessly integrates with diverse data sources to ensure a superior, relevant search experience.", "icon": "https://tavily.com/favicon.ico", "authConfig": [ { diff --git a/api/app/clients/tools/structured/TraversaalSearch.js b/api/app/clients/tools/structured/TraversaalSearch.js new file mode 100644 index 0000000000..5de60b8200 --- /dev/null +++ b/api/app/clients/tools/structured/TraversaalSearch.js @@ -0,0 +1,89 @@ +const { z } = require('zod'); +const { Tool } = require('@langchain/core/tools'); +const { getEnvironmentVariable } = require('@langchain/core/utils/env'); +const { logger } = require('~/config'); + +/** + * Tool for the Traversaal AI search API, Ares. + */ +class TraversaalSearch extends Tool { + static lc_name() { + return 'TraversaalSearch'; + } + constructor(fields) { + super(fields); + this.name = 'traversaal_search'; + this.description = `An AI search engine optimized for comprehensive, accurate, and trusted results. + Useful for when you need to answer questions about current events. Input should be a search query.`; + this.description_for_model = + '\'Please create a specific sentence for the AI to understand and use as a query to search the web based on the user\'s request. For example, "Find information about the highest mountains in the world." or "Show me the latest news articles about climate change and its impact on polar ice caps."\''; + this.schema = z.object({ + query: z + .string() + .describe( + 'A properly written sentence to be interpreted by an AI to search the web according to the user\'s request.', + ), + }); + + this.apiKey = fields?.apiKey ?? this.getApiKey(); + } + + getApiKey() { + const apiKey = getEnvironmentVariable('TRAVERSAAL_API_KEY'); + if (!apiKey && this.override) { + throw new Error( + 'No Traversaal API key found. Either set an environment variable named "TRAVERSAAL_API_KEY" or pass an API key as "apiKey".', + ); + } + return apiKey; + } + + // eslint-disable-next-line no-unused-vars + async _call({ query }, _runManager) { + const body = { + query: [query], + }; + try { + const response = await fetch('https://api-ares.traversaal.ai/live/predict', { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-api-key': this.apiKey, + }, + body: JSON.stringify({ ...body }), + }); + const json = await response.json(); + if (!response.ok) { + throw new Error( + `Request failed with status code ${response.status}: ${json.error ?? json.message}`, + ); + } + if (!json.data) { + throw new Error('Could not parse Traversaal API results. Please try again.'); + } + + const baseText = json.data?.response_text ?? ''; + const sources = json.data?.web_url; + const noResponse = 'No response found in Traversaal API results'; + + if (!baseText && !sources) { + return noResponse; + } + + const sourcesText = sources?.length ? '\n\nSources:\n - ' + sources.join('\n - ') : ''; + + const result = baseText + sourcesText; + + if (!result) { + return noResponse; + } + + return result; + } catch (error) { + logger.error('Traversaal API request failed', error); + return `Traversaal API request failed: ${error.message}`; + } + } +} + +module.exports = TraversaalSearch; diff --git a/api/app/clients/tools/util/handleTools.js b/api/app/clients/tools/util/handleTools.js index 2733791e3b..6c9e43f03d 100644 --- a/api/app/clients/tools/util/handleTools.js +++ b/api/app/clients/tools/util/handleTools.js @@ -20,6 +20,7 @@ const { StructuredSD, StructuredACS, CodeSherpaTools, + TraversaalSearch, StructuredWolfram, TavilySearchResults, } = require('../'); @@ -165,6 +166,7 @@ const loadTools = async ({ 'stable-diffusion': functions ? StructuredSD : StableDiffusionAPI, 'azure-ai-search': functions ? StructuredACS : AzureAISearch, CodeBrew: CodeBrew, + traversaal_search: TraversaalSearch, }; const openAIApiKey = await getOpenAIKey(options, user);