UNPKG

generator-begcode

Version:

Spring Boot + Angular/React/Vue in one handy generator

169 lines (165 loc) 6.77 kB
import axios from 'axios'; import { AgentOutputType, ArrayRecombiner, ChatMessageBuilder, Prompt, Rag, TextChunker, trimText, } from '../agent-core/index.js'; import { FUNCTION_CALL_FAILED, FUNCTION_CALL_SUCCESS_CONTENT } from '../agents/Scripter/utils.js'; import { LlmAgentFunctionBase, processWebpage, searchOnGoogle } from './utils/index.js'; export class WebSearchFunction extends LlmAgentFunctionBase { constructor(llm, tokenizer) { super(llm, tokenizer); } name = 'web_search'; description = 'Searches the web for multiple queries.'; parameters = { type: 'object', properties: { queries: { type: 'array', items: { type: 'string', }, description: 'Queries with all details to search the web for in parallel', }, }, required: ['queries'], additionalProperties: false, }; buildExecutor(agent) { return async (params) => { const searches = []; for (const query of params.queries) { searches.push(this.runQuery(agent, query, JSON.stringify({ queries: [query] }))); } const results = await Promise.all(searches); return { outputs: results.flatMap(x => x.outputs), messages: results.flatMap(x => x.messages), }; }; } async runQuery({ context }, query, rawParams) { try { let googleResults; if (typeof window === 'object') { const results = await axios.get(`/api/search?query=${query}`); googleResults = results.data.googleResults; } else { if (!context.env.SERP_API_KEY) { throw new Error('SERP_API_KEY environment variable is required to use the websearch plugin. See env.template for help'); } googleResults = await searchOnGoogle(query, context.env.SERP_API_KEY); } const urls = googleResults.map(x => x.url).filter(u => !u.endsWith('.pdf')); const searchMatches = await this.searchInPages({ urls, query, context, }); const analyzeChunkMatchesPrompt = new Prompt() .text(` I will give you chunks of text from different webpages. I want to extract ${query}. Keep in mind some information may not be properly formatted. Do your best to extract as much information as you can. Prioritize decimal precision. Aim for answers with 3 decimal places, if possible; if not settle for 2 decimal places. Only take 1 decimal or rounded numbers when absolutely necessary. Chunks: ${searchMatches.join('\n------------\n')} `) .line('Specify if the information is incomplete but still return it') .toString(); const analysisFromChunks = await this.askLlm(analyzeChunkMatchesPrompt, { maxResponseTokens: 200, }); return this.onSuccess({ queries: [query] }, analysisFromChunks, rawParams, context.variables); } catch (err) { return this.onError({ queries: [query] }, err.toString(), rawParams, context.variables); } } onSuccess(params, result, rawParams, variables) { return { outputs: [ { type: AgentOutputType.Success, title: `Web search for '${params.queries}'`, content: FUNCTION_CALL_SUCCESS_CONTENT(this.name, params, `Found the following result for the web search: '${params.queries}'` + '\n--------------\n' + `${result}\n` + '\n--------------\n'), }, ], messages: [ ChatMessageBuilder.functionCall(this.name, rawParams), ChatMessageBuilder.functionCallResult(this.name, `Found the following result for the web search: '${params.queries}'\`\`\` ${result}\n\`\`\``), ], }; } onError(params, error, rawParams, variables) { return { outputs: [ { type: AgentOutputType.Error, title: `Web search for '${params.queries}'`, content: FUNCTION_CALL_FAILED(params, this.name, error), }, ], messages: [ ChatMessageBuilder.functionCall(this.name, rawParams), ChatMessageBuilder.functionCallResult(this.name, `Error web searching for '${params.queries}'\n\`\`\` ${trimText(error, 300)}\n\`\`\``), ], }; } async searchInPages(params) { const urlsContents = await Promise.all(params.urls.map(async (url) => { try { let response; if (typeof window === 'object') { const result = await axios.get(`/api/process-web-page?url=${url}`); response = result.data.text; } else { response = await processWebpage(url); } return { url, response, }; } catch (e) { return { url, response: '', }; } })); const webpagesChunks = urlsContents.map(webpageContent => ({ url: webpageContent.url, chunks: TextChunker.fixedCharacterLength(webpageContent.response, { chunkLength: 550, overlap: 110 }), })); const results = await Promise.all(webpagesChunks.map(async (webpageChunks) => { const items = webpageChunks.chunks.map(chunk => ({ chunk, url: webpageChunks.url, })); const matches = await (await Rag.standard(params.context)) .addItems(items) .selector(x => x.chunk) .query(params.query) .recombine(ArrayRecombiner.standard({ limit: 4, unique: true, })); return matches; })); const otherResults = await (await Rag.standard(params.context)) .addItems(results.flat()) .selector(x => x.chunk) .query(params.query) .recombine(ArrayRecombiner.standard({ limit: 10, unique: true, sort: 'index', })); return otherResults.map(x => x.chunk); } }