166 lines
5.7 KiB
JavaScript
166 lines
5.7 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.SearchApi = void 0;
|
|
const env_1 = require("@langchain/core/utils/env");
|
|
const tools_1 = require("@langchain/core/tools");
|
|
function isJSONObject(value) {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
}
|
|
/**
|
|
* SearchApi Class Definition.
|
|
*
|
|
* Provides a wrapper around the SearchApi.
|
|
*
|
|
* Ensure you've set the SEARCHAPI_API_KEY environment variable for authentication.
|
|
* You can obtain a free API key from https://www.searchapi.io/.
|
|
* @example
|
|
* ```typescript
|
|
* const searchApi = new SearchApi("your-api-key", {
|
|
* engine: "google_news",
|
|
* });
|
|
* const agent = RunnableSequence.from([
|
|
* ChatPromptTemplate.fromMessages([
|
|
* ["ai", "Answer the following questions using a bulleted list markdown format.""],
|
|
* ["human", "{input}"],
|
|
* ]),
|
|
* new ChatOpenAI({ temperature: 0 }),
|
|
* (input: BaseMessageChunk) => ({
|
|
* log: "test",
|
|
* returnValues: {
|
|
* output: input,
|
|
* },
|
|
* }),
|
|
* ]);
|
|
* const executor = AgentExecutor.fromAgentAndTools({
|
|
* agent,
|
|
* tools: [searchApi],
|
|
* });
|
|
* const res = await executor.invoke({
|
|
* input: "What's happening in Ukraine today?"",
|
|
* });
|
|
* console.log(res);
|
|
* ```
|
|
*/
|
|
class SearchApi extends tools_1.Tool {
|
|
static lc_name() {
|
|
return "SearchApi";
|
|
}
|
|
/**
|
|
* Converts the SearchApi instance to JSON. This method is not implemented
|
|
* and will throw an error if called.
|
|
* @returns Throws an error.
|
|
*/
|
|
toJSON() {
|
|
return this.toJSONNotImplemented();
|
|
}
|
|
constructor(apiKey = (0, env_1.getEnvironmentVariable)("SEARCHAPI_API_KEY"), params = {}) {
|
|
super(...arguments);
|
|
Object.defineProperty(this, "apiKey", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: void 0
|
|
});
|
|
Object.defineProperty(this, "params", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: void 0
|
|
});
|
|
Object.defineProperty(this, "name", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: "search"
|
|
});
|
|
Object.defineProperty(this, "description", {
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: true,
|
|
value: "a search engine. useful for when you need to answer questions about current events. input should be a search query."
|
|
});
|
|
if (!apiKey) {
|
|
throw new Error("SearchApi requires an API key. Please set it as SEARCHAPI_API_KEY in your .env file, or pass it as a parameter to the SearchApi constructor.");
|
|
}
|
|
this.apiKey = apiKey;
|
|
this.params = params;
|
|
}
|
|
/**
|
|
* Builds a URL for the SearchApi request.
|
|
* @param parameters The parameters for the request.
|
|
* @returns A string representing the built URL.
|
|
*/
|
|
buildUrl(searchQuery) {
|
|
const preparedParams = Object.entries({
|
|
engine: "google",
|
|
api_key: this.apiKey,
|
|
...this.params,
|
|
q: searchQuery,
|
|
})
|
|
.filter(([key, value]) => value !== undefined && value !== null && key !== "apiKey")
|
|
.map(([key, value]) => [key, `${value}`]);
|
|
const searchParams = new URLSearchParams(preparedParams);
|
|
return `https://www.searchapi.io/api/v1/search?${searchParams}`;
|
|
}
|
|
/** @ignore */
|
|
/**
|
|
* Calls the SearchAPI.
|
|
*
|
|
* Accepts an input query and fetches the result from SearchApi.
|
|
*
|
|
* @param {string} input - Search query.
|
|
* @returns {string} - Formatted search results or an error message.
|
|
*
|
|
* NOTE: This method is the core search handler and processes various types
|
|
* of search results including Google organic results, videos, jobs, and images.
|
|
*/
|
|
async _call(input) {
|
|
const resp = await fetch(this.buildUrl(input));
|
|
const json = await resp.json();
|
|
if (json.error) {
|
|
throw new Error(`Failed to load search results from SearchApi due to: ${json.error}`);
|
|
}
|
|
// Google Search results
|
|
if (json.answer_box?.answer) {
|
|
return json.answer_box.answer;
|
|
}
|
|
if (json.answer_box?.snippet) {
|
|
return json.answer_box.snippet;
|
|
}
|
|
if (json.knowledge_graph?.description) {
|
|
return json.knowledge_graph.description;
|
|
}
|
|
// Organic results (Google, Google News)
|
|
if (json.organic_results) {
|
|
const snippets = json.organic_results
|
|
.filter((r) => r.snippet)
|
|
.map((r) => r.snippet);
|
|
return snippets.join("\n");
|
|
}
|
|
// Google Jobs results
|
|
if (json.jobs) {
|
|
const jobDescriptions = json.jobs
|
|
.slice(0, 1)
|
|
.filter((r) => r.description)
|
|
.map((r) => r.description);
|
|
return jobDescriptions.join("\n");
|
|
}
|
|
// Google Videos results
|
|
if (json.videos) {
|
|
const videoInfo = json.videos
|
|
.filter((r) => r.title && r.link)
|
|
.map((r) => `Title: "${r.title}" Link: ${r.link}`);
|
|
return videoInfo.join("\n");
|
|
}
|
|
// Google Images results
|
|
if (json.images) {
|
|
const image_results = json.images.slice(0, 15);
|
|
const imageInfo = image_results
|
|
.filter((r) => r.title && r.original && isJSONObject(r.original) && r.original.link)
|
|
.map((r) => `Title: "${r.title}" Link: ${r.original.link}`);
|
|
return imageInfo.join("\n");
|
|
}
|
|
return "No good search result found";
|
|
}
|
|
}
|
|
exports.SearchApi = SearchApi;
|