| const { SystemSettings } = require("../../../../models/systemSettings"); | |
| const { TokenManager } = require("../../../helpers/tiktoken"); | |
| const tiktoken = new TokenManager(); | |
| const webBrowsing = { | |
| name: "web-browsing", | |
| startupConfig: { | |
| params: {}, | |
| }, | |
| plugin: function () { | |
| return { | |
| name: this.name, | |
| setup(aibitat) { | |
| aibitat.function({ | |
| super: aibitat, | |
| name: this.name, | |
| countTokens: (string) => | |
| tiktoken | |
| .countFromString(string) | |
| .toString() | |
| .replace(/\B(?=(\d{3})+(?!\d))/g, ","), | |
| description: | |
| "Searches for a given query using a search engine to get better results for the user query.", | |
| examples: [ | |
| { | |
| prompt: "Who won the world series today?", | |
| call: JSON.stringify({ query: "Winner of today's world series" }), | |
| }, | |
| { | |
| prompt: "What is AnythingLLM?", | |
| call: JSON.stringify({ query: "AnythingLLM" }), | |
| }, | |
| { | |
| prompt: "Current AAPL stock price", | |
| call: JSON.stringify({ query: "AAPL stock price today" }), | |
| }, | |
| ], | |
| parameters: { | |
| $schema: "http://json-schema.org/draft-07/schema#", | |
| type: "object", | |
| properties: { | |
| query: { | |
| type: "string", | |
| description: "A search query.", | |
| }, | |
| }, | |
| additionalProperties: false, | |
| }, | |
| handler: async function ({ query }) { | |
| try { | |
| if (query) return await this.search(query); | |
| return "There is nothing we can do. This function call returns no information."; | |
| } catch (error) { | |
| return `There was an error while calling the function. No data or response was found. Let the user know this was the error: ${error.message}`; | |
| } | |
| }, | |
| /** | |
| * Use Google Custom Search Engines | |
| * Free to set up, easy to use, 100 calls/day! | |
| * https://programmablesearchengine.google.com/controlpanel/create | |
| */ | |
| search: async function (query) { | |
| const provider = | |
| (await SystemSettings.get({ label: "agent_search_provider" })) | |
| ?.value ?? "unknown"; | |
| let engine; | |
| switch (provider) { | |
| case "google-search-engine": | |
| engine = "_googleSearchEngine"; | |
| break; | |
| case "searchapi": | |
| engine = "_searchApi"; | |
| break; | |
| case "serper-dot-dev": | |
| engine = "_serperDotDev"; | |
| break; | |
| case "bing-search": | |
| engine = "_bingWebSearch"; | |
| break; | |
| case "serply-engine": | |
| engine = "_serplyEngine"; | |
| break; | |
| case "searxng-engine": | |
| engine = "_searXNGEngine"; | |
| break; | |
| case "tavily-search": | |
| engine = "_tavilySearch"; | |
| break; | |
| case "duckduckgo-engine": | |
| engine = "_duckDuckGoEngine"; | |
| break; | |
| case "exa-search": | |
| engine = "_exaSearch"; | |
| break; | |
| default: | |
| engine = "_googleSearchEngine"; | |
| } | |
| return await this[engine](query); | |
| }, | |
| /** | |
| * Utility function to truncate a string to a given length for debugging | |
| * calls to the API while keeping the actual values mostly intact | |
| * @param {string} str - The string to truncate | |
| * @param {number} length - The length to truncate the string to | |
| * @returns {string} The truncated string | |
| */ | |
| middleTruncate(str, length = 5) { | |
| if (str.length <= length) return str; | |
| return `${str.slice(0, length)}...${str.slice(-length)}`; | |
| }, | |
| /** | |
| * Use Google Custom Search Engines | |
| * Free to set up, easy to use, 100 calls/day | |
| * https://programmablesearchengine.google.com/controlpanel/create | |
| */ | |
| _googleSearchEngine: async function (query) { | |
| if (!process.env.AGENT_GSE_CTX || !process.env.AGENT_GSE_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Google searching because the user has not defined the required API keys.\nVisit: https://programmablesearchengine.google.com/controlpanel/create to create the API keys.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| const searchURL = new URL( | |
| "https://www.googleapis.com/customsearch/v1" | |
| ); | |
| searchURL.searchParams.append("key", process.env.AGENT_GSE_KEY); | |
| searchURL.searchParams.append("cx", process.env.AGENT_GSE_CTX); | |
| searchURL.searchParams.append("q", query); | |
| this.super.introspect( | |
| `${this.caller}: Searching on Google for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const data = await fetch(searchURL) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ key: this.middleTruncate(process.env.AGENT_GSE_KEY, 5), cx: this.middleTruncate(process.env.AGENT_GSE_CTX, 5), q: query })}` | |
| ); | |
| }) | |
| .then((searchResult) => searchResult?.items || []) | |
| .then((items) => { | |
| return items.map((item) => { | |
| return { | |
| title: item.title, | |
| link: item.link, | |
| snippet: item.snippet, | |
| }; | |
| }); | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log( | |
| `${this.name}: Google Search Error: ${e.message}` | |
| ); | |
| return []; | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| /** | |
| * Use SearchApi | |
| * SearchApi supports multiple search engines like Google Search, Bing Search, Baidu Search, Google News, YouTube, and many more. | |
| * https://www.searchapi.io/ | |
| */ | |
| _searchApi: async function (query) { | |
| if (!process.env.AGENT_SEARCHAPI_API_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use SearchApi searching because the user has not defined the required API key.\nVisit: https://www.searchapi.io/ to create the API key for free.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using SearchApi to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const engine = process.env.AGENT_SEARCHAPI_ENGINE; | |
| const params = new URLSearchParams({ | |
| engine: engine, | |
| q: query, | |
| }); | |
| const url = `https://www.searchapi.io/api/v1/search?${params.toString()}`; | |
| const { response, error } = await fetch(url, { | |
| method: "GET", | |
| headers: { | |
| Authorization: `Bearer ${process.env.AGENT_SEARCHAPI_API_KEY}`, | |
| "Content-Type": "application/json", | |
| "X-SearchApi-Source": "AnythingLLM", | |
| }, | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_SEARCHAPI_API_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log(`SearchApi Error: ${e.message}`); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| if (response.hasOwnProperty("knowledge_graph")) | |
| data.push(response.knowledge_graph?.description); | |
| if (response.hasOwnProperty("answer_box")) | |
| data.push(response.answer_box?.answer); | |
| response.organic_results?.forEach((searchResult) => { | |
| const { title, link, snippet } = searchResult; | |
| data.push({ | |
| title, | |
| link, | |
| snippet, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| /** | |
| * Use Serper.dev | |
| * Free to set up, easy to use, 2,500 calls for free one-time | |
| * https://serper.dev | |
| */ | |
| _serperDotDev: async function (query) { | |
| if (!process.env.AGENT_SERPER_DEV_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Serper.dev searching because the user has not defined the required API key.\nVisit: https://serper.dev to create the API key for free.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using Serper.dev to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const { response, error } = await fetch( | |
| "https://google.serper.dev/search", | |
| { | |
| method: "POST", | |
| headers: { | |
| "X-API-KEY": process.env.AGENT_SERPER_DEV_KEY, | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ q: query }), | |
| redirect: "follow", | |
| } | |
| ) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_SERPER_DEV_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log(`Serper.dev Error: ${e.message}`); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| if (response.hasOwnProperty("knowledgeGraph")) | |
| data.push(response.knowledgeGraph); | |
| response.organic?.forEach((searchResult) => { | |
| const { title, link, snippet } = searchResult; | |
| data.push({ | |
| title, | |
| link, | |
| snippet, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _bingWebSearch: async function (query) { | |
| if (!process.env.AGENT_BING_SEARCH_API_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Bing Web Search because the user has not defined the required API key.\nVisit: https://portal.azure.com/ to create the API key.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| const searchURL = new URL( | |
| "https://api.bing.microsoft.com/v7.0/search" | |
| ); | |
| searchURL.searchParams.append("q", query); | |
| this.super.introspect( | |
| `${this.caller}: Using Bing Web Search to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const searchResponse = await fetch(searchURL, { | |
| headers: { | |
| "Ocp-Apim-Subscription-Key": | |
| process.env.AGENT_BING_SEARCH_API_KEY, | |
| }, | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_BING_SEARCH_API_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| const searchResults = data.webPages?.value || []; | |
| return searchResults.map((result) => ({ | |
| title: result.name, | |
| link: result.url, | |
| snippet: result.snippet, | |
| })); | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log( | |
| `Bing Web Search Error: ${e.message}` | |
| ); | |
| return []; | |
| }); | |
| if (searchResponse.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(searchResponse); | |
| this.super.introspect( | |
| `${this.caller}: I found ${searchResponse.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _serplyEngine: async function ( | |
| query, | |
| language = "en", | |
| hl = "us", | |
| limit = 100, | |
| device_type = "desktop", | |
| proxy_location = "US" | |
| ) { | |
| // query (str): The query to search for | |
| // hl (str): Host Language code to display results in (reference https://developers.google.com/custom-search/docs/xml_results?hl=en#wsInterfaceLanguages) | |
| // limit (int): The maximum number of results to return [10-100, defaults to 100] | |
| // device_type: get results based on desktop/mobile (defaults to desktop) | |
| if (!process.env.AGENT_SERPLY_API_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Serply.io searching because the user has not defined the required API key.\nVisit: https://serply.io to create the API key for free.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using Serply to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const params = new URLSearchParams({ | |
| q: query, | |
| language: language, | |
| hl, | |
| gl: proxy_location.toUpperCase(), | |
| }); | |
| const url = `https://api.serply.io/v1/search/${params.toString()}`; | |
| const { response, error } = await fetch(url, { | |
| method: "GET", | |
| headers: { | |
| "X-API-KEY": process.env.AGENT_SERPLY_API_KEY, | |
| "Content-Type": "application/json", | |
| "User-Agent": "anything-llm", | |
| "X-Proxy-Location": proxy_location, | |
| "X-User-Agent": device_type, | |
| }, | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_SERPLY_API_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| if (data?.message === "Unauthorized") | |
| throw new Error( | |
| "Unauthorized. Please double check your AGENT_SERPLY_API_KEY" | |
| ); | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log(`Serply Error: ${e.message}`); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| response.results?.forEach((searchResult) => { | |
| const { title, link, description } = searchResult; | |
| data.push({ | |
| title, | |
| link, | |
| snippet: description, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _searXNGEngine: async function (query) { | |
| let searchURL; | |
| if (!process.env.AGENT_SEARXNG_API_URL) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use SearXNG searching because the user has not defined the required base URL.\nPlease set this value in the agent skill settings.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| try { | |
| searchURL = new URL(process.env.AGENT_SEARXNG_API_URL); | |
| searchURL.searchParams.append("q", encodeURIComponent(query)); | |
| searchURL.searchParams.append("format", "json"); | |
| } catch (e) { | |
| this.super.handlerProps.log(`SearXNG Search: ${e.message}`); | |
| this.super.introspect( | |
| `${this.caller}: I can't use SearXNG searching because the url provided is not a valid URL.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using SearXNG to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const { response, error } = await fetch(searchURL.toString(), { | |
| method: "GET", | |
| headers: { | |
| "Content-Type": "application/json", | |
| "User-Agent": "anything-llm", | |
| }, | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ url: searchURL.toString() })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log( | |
| `SearXNG Search Error: ${e.message}` | |
| ); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| response.results?.forEach((searchResult) => { | |
| const { url, title, content, publishedDate } = searchResult; | |
| data.push({ | |
| title, | |
| link: url, | |
| snippet: content, | |
| publishedDate, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _tavilySearch: async function (query) { | |
| if (!process.env.AGENT_TAVILY_API_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Tavily searching because the user has not defined the required API key.\nVisit: https://tavily.com/ to create the API key.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using Tavily to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const url = "https://api.tavily.com/search"; | |
| const { response, error } = await fetch(url, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ | |
| api_key: process.env.AGENT_TAVILY_API_KEY, | |
| query: query, | |
| }), | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_TAVILY_API_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log( | |
| `Tavily Search Error: ${e.message}` | |
| ); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| response.results?.forEach((searchResult) => { | |
| const { title, url, content } = searchResult; | |
| data.push({ | |
| title, | |
| link: url, | |
| snippet: content, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _duckDuckGoEngine: async function (query) { | |
| this.super.introspect( | |
| `${this.caller}: Using DuckDuckGo to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const searchURL = new URL("https://html.duckduckgo.com/html"); | |
| searchURL.searchParams.append("q", query); | |
| const response = await fetch(searchURL.toString()) | |
| .then((res) => { | |
| if (res.ok) return res.text(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ url: searchURL.toString() })}` | |
| ); | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log( | |
| `DuckDuckGo Search Error: ${e.message}` | |
| ); | |
| return null; | |
| }); | |
| if (!response) return `There was an error searching DuckDuckGo.`; | |
| const html = response; | |
| const data = []; | |
| const results = html.split('<div class="result results_links'); | |
| // Skip first element since it's before the first result | |
| for (let i = 1; i < results.length; i++) { | |
| const result = results[i]; | |
| // Extract title | |
| const titleMatch = result.match( | |
| /<a[^>]*class="result__a"[^>]*>(.*?)<\/a>/ | |
| ); | |
| const title = titleMatch ? titleMatch[1].trim() : ""; | |
| // Extract URL | |
| const urlMatch = result.match( | |
| /<a[^>]*class="result__a"[^>]*href="([^"]*)">/ | |
| ); | |
| const link = urlMatch ? urlMatch[1] : ""; | |
| // Extract snippet | |
| const snippetMatch = result.match( | |
| /<a[^>]*class="result__snippet"[^>]*>(.*?)<\/a>/ | |
| ); | |
| const snippet = snippetMatch | |
| ? snippetMatch[1].replace(/<\/?b>/g, "").trim() | |
| : ""; | |
| if (title && link && snippet) { | |
| data.push({ title, link, snippet }); | |
| } | |
| } | |
| if (data.length === 0) { | |
| return `No information was found online for the search query.`; | |
| } | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| _exaSearch: async function (query) { | |
| if (!process.env.AGENT_EXA_API_KEY) { | |
| this.super.introspect( | |
| `${this.caller}: I can't use Exa searching because the user has not defined the required API key.\nVisit: https://exa.ai to create the API key.` | |
| ); | |
| return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`; | |
| } | |
| this.super.introspect( | |
| `${this.caller}: Using Exa to search for "${ | |
| query.length > 100 ? `${query.slice(0, 100)}...` : query | |
| }"` | |
| ); | |
| const url = "https://api.exa.ai/search"; | |
| const { response, error } = await fetch(url, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| "x-api-key": process.env.AGENT_EXA_API_KEY, | |
| }, | |
| body: JSON.stringify({ | |
| query: query, | |
| type: "auto", | |
| numResults: 10, | |
| contents: { | |
| text: true, | |
| }, | |
| }), | |
| }) | |
| .then((res) => { | |
| if (res.ok) return res.json(); | |
| throw new Error( | |
| `${res.status} - ${res.statusText}. params: ${JSON.stringify({ auth: this.middleTruncate(process.env.AGENT_EXA_API_KEY, 5), q: query })}` | |
| ); | |
| }) | |
| .then((data) => { | |
| return { response: data, error: null }; | |
| }) | |
| .catch((e) => { | |
| this.super.handlerProps.log(`Exa Search Error: ${e.message}`); | |
| return { response: null, error: e.message }; | |
| }); | |
| if (error) | |
| return `There was an error searching for content. ${error}`; | |
| const data = []; | |
| response.results?.forEach((searchResult) => { | |
| const { title, url, text, publishedDate } = searchResult; | |
| data.push({ | |
| title, | |
| link: url, | |
| snippet: text, | |
| publishedDate, | |
| }); | |
| }); | |
| if (data.length === 0) | |
| return `No information was found online for the search query.`; | |
| const result = JSON.stringify(data); | |
| this.super.introspect( | |
| `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)` | |
| ); | |
| return result; | |
| }, | |
| }); | |
| }, | |
| }; | |
| }, | |
| }; | |
| module.exports = { | |
| webBrowsing, | |
| }; | |