Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions SERPEX_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# LangChain Serpex Integration

Official Serpex search integration for LangChain.js

## Installation

```bash
npm install @langchain/community
```

## Setup

Get your API key from [Serpex](https://serpex.dev) and set it as an environment variable:

```bash
export SERPEX_API_KEY="your-api-key-here"
```

## Usage

### Basic Example

```typescript
import { Serpex } from "@langchain/community/tools/serpex";

const tool = new Serpex(process.env.SERPEX_API_KEY);

const result = await tool.invoke("latest AI developments");
console.log(result);
```

### With Custom Parameters

```typescript
import { Serpex } from "@langchain/community/tools/serpex";

const tool = new Serpex(process.env.SERPEX_API_KEY, {
engine: "google", // auto, google, bing, duckduckgo, brave, yahoo, yandex
category: "web", // currently only "web" supported
time_range: "day" // all, day, week, month, year (not supported by Brave)
});

const result = await tool.invoke("coffee shops near me");
console.log(result);
```

### With LangChain Agent

```typescript
import { Serpex } from "@langchain/community/tools/serpex";
import { ChatOpenAI } from "@langchain/openai";
import { AgentExecutor, createReactAgent } from "langchain/agents";
import { pull } from "langchain/hub";

const searchTool = new Serpex(process.env.SERPEX_API_KEY, {
engine: "auto",
time_range: "week"
});

const model = new ChatOpenAI({
model: "gpt-4",
temperature: 0,
});

const prompt = await pull("hwchase17/react");

const agent = await createReactAgent({
llm: model,
tools: [searchTool],
prompt,
});

const agentExecutor = new AgentExecutor({
agent,
tools: [searchTool],
});

const result = await agentExecutor.invoke({
input: "What are the latest developments in AI?"
});

console.log(result.output);
```

## API Parameters

### Supported Parameters

- **`engine`** (optional): Search engine to use
- Options: `"auto"`, `"google"`, `"bing"`, `"duckduckgo"`, `"brave"`, `"yahoo"`, `"yandex"`
- Default: `"auto"` (automatically routes with retry logic)

- **`category`** (optional): Search category
- Options: `"web"` (more categories coming soon)
- Default: `"web"`

- **`time_range`** (optional): Filter results by time
- Options: `"all"`, `"day"`, `"week"`, `"month"`, `"year"`
- Note: Not supported by Brave engine

### Response Format

The tool returns formatted search results as a string containing:

1. **Instant Answers**: Direct answers from knowledge panels
2. **Infoboxes**: Knowledge panel descriptions
3. **Organic Results**: Web search results with titles, URLs, and snippets
4. **Suggestions**: Related search queries
5. **Corrections**: Suggested query corrections

Example response:
```
Found 10 results:

[1] Starbucks - Coffee Shop
URL: https://www.starbucks.com/store-locator/store/1234
Premium coffee, perfect lattes, and great atmosphere in the heart of downtown...

[2] Local Coffee Roasters
URL: https://localcoffee.com
Artisanal coffee beans, locally sourced and expertly roasted daily...
```

## Features

- **Multi-Engine Support**: Automatically routes requests across multiple search engines
- **Smart Retry Logic**: Built-in retry mechanism for failed requests
- **Real-Time Results**: Get fresh search results from the web
- **Simple Integration**: Easy to use with LangChain agents and chains
- **Structured Output**: Clean, formatted search results ready for LLM consumption

## Cost

All search engines cost 1 credit per successful request. Credits never expire.

## Rate Limits

- 300 requests per second
- No daily limits

## Documentation

For detailed API documentation, visit: [https://serpex.dev/docs](https://serpex.dev/docs)

## Support

- Email: [email protected]
- Documentation: https://serpex.dev/docs
- Dashboard: https://serpex.dev/dashboard

## License

MIT
1 change: 1 addition & 0 deletions libs/langchain-community/src/load/import_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export * as tools__ifttt from "../tools/ifttt.js";
export * as tools__searchapi from "../tools/searchapi.js";
export * as tools__searxng_search from "../tools/searxng_search.js";
export * as tools__serpapi from "../tools/serpapi.js";
export * as tools__serpex from "../tools/serpex.js";
export * as tools__serper from "../tools/serper.js";
export * as tools__stackexchange from "../tools/stackexchange.js";
export * as tools__wikipedia_query_run from "../tools/wikipedia_query_run.js";
Expand Down
237 changes: 237 additions & 0 deletions libs/langchain-community/src/tools/serpex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import { getEnvironmentVariable } from "@langchain/core/utils/env";
import { Tool } from "@langchain/core/tools";

/**
* SERPEX API Parameters
*
* SERPEX provides multi-engine web search results in JSON format.
* Supports Google, Bing, DuckDuckGo, Brave, Yahoo, and Yandex search engines.
*
* For detailed documentation, visit: https://serpex.dev/docs
*/
export interface SerpexParameters {
/**
* Search query string (required)
*/
q: string;

/**
* Search engine to use
* Options: "auto", "google", "bing", "duckduckgo", "brave", "yahoo", "yandex"
* Default: "auto" (automatically routes to best available engine)
*/
engine?: string;

/**
* Search category
* Currently only "web" is supported
* More categories (images, videos, news) coming soon
* Default: "web"
*/
category?: string;

/**
* Time range filter for results
* Options: "all", "day", "week", "month", "year"
* Note: Not supported by Brave engine
*/
time_range?: string;
}

/**
* Serpex Class
*
* A tool for searching the web using the SERPEX API, which provides
* multi-engine search results from Google, Bing, DuckDuckGo, Brave, Yahoo, and Yandex.
*
* Requires SERPEX_API_KEY environment variable or passed as parameter.
* Get your API key at: https://serpex.dev
*
* @example
* ```typescript
* const serpex = new Serpex("your-api-key", {
* engine: "auto",
* category: "web",
* time_range: "day"
* });
*
* const agent = RunnableSequence.from([
* ChatPromptTemplate.fromMessages([
* ["ai", "Answer the following questions using concise bullet points."],
* ["human", "{input}"],
* ]),
* new ChatOpenAI({ model: "gpt-4", temperature: 0 }),
* (input: BaseMessageChunk) => ({
* log: "Processed search results",
* returnValues: {
* output: input,
* },
* }),
* ]);
*
* const executor = AgentExecutor.fromAgentAndTools({
* agent,
* tools: [serpex],
* });
*
* const result = await executor.invoke({
* input: "What are the latest AI developments?"
* });
* console.log(result);
* ```
*/
export class Serpex extends Tool {
static lc_name() {
return "Serpex";
}

name = "serpex_search";

description =
"A powerful multi-engine web search tool. Useful for answering questions about current events, finding information from the web, and getting real-time data. Input should be a search query string. Supports automatic routing with retry logic and multiple search engines (Google, Bing, DuckDuckGo, Brave, Yahoo, Yandex).";

protected apiKey: string;

protected params: Partial<SerpexParameters>;

protected baseURL: string;

/**
* @param apiKey - SERPEX API key (optional if SERPEX_API_KEY env var is set)
* @param params - Default parameters for all searches
* @param baseURL - Base URL for Serpex API (defaults to production API)
*/
constructor(
apiKey: string | undefined = getEnvironmentVariable("SERPEX_API_KEY"),
params: Partial<SerpexParameters> = {},
baseURL: string = getEnvironmentVariable("SERPEX_BASE_URL") || "https://api.serpex.dev"
) {
super();

if (!apiKey) {
throw new Error(
"SERPEX API key is required. Set it as SERPEX_API_KEY in your environment variables, or pass it to the Serpex constructor."
);
}

this.apiKey = apiKey;
this.params = params;
this.baseURL = baseURL;
}

/**
* Converts the Serpex instance to JSON
* @returns Throws an error (not implemented)
*/
toJSON() {
return this.toJSONNotImplemented();
}

/**
* Builds the API request URL with query parameters
* @param searchQuery - The search query string
* @returns Complete API URL with parameters
*/
protected buildUrl(searchQuery: string): string {
const preparedParams: [string, string][] = Object.entries({
engine: "auto",
category: "web",
...this.params,
q: searchQuery,
})
.filter(([key, value]) => value !== undefined && value !== null)
.map(([key, value]) => [key, `${value}`]);

const searchParams = new URLSearchParams(preparedParams);
return `${this.baseURL}/api/search?${searchParams}`;
}

/**
* Executes the search and processes results
* @param input - Search query string
* @returns Formatted search results
*/
async _call(input: string): Promise<string> {
try {
const url = this.buildUrl(input);

const response = await fetch(url, {
method: "GET",
headers: {
"Authorization": `Bearer ${this.apiKey}`,
"Content-Type": "application/json",
},
});

if (!response.ok) {
const errorText = await response.text();
throw new Error(
`SERPEX API request failed with status ${response.status}: ${errorText}`
);
}

const json = await response.json();

if (json.error) {
throw new Error(
`SERPEX API returned an error: ${json.error}`
);
}

// Process response based on actual Serpex API format
// Response structure: { metadata, id, query, engines, results, answers, corrections, infoboxes, suggestions }

// Instant answers (from knowledge panels/answer boxes)
if (json.answers && Array.isArray(json.answers) && json.answers.length > 0) {
const answer = json.answers[0];
if (answer.answer || answer.snippet) {
return answer.answer || answer.snippet;
}
}

// Infoboxes (knowledge panels)
if (json.infoboxes && Array.isArray(json.infoboxes) && json.infoboxes.length > 0) {
const infobox = json.infoboxes[0];
if (infobox.description) {
return infobox.description;
}
}

// Organic search results
if (json.results && Array.isArray(json.results) && json.results.length > 0) {
const snippets = json.results
.filter((result: any) => result.snippet || result.title)
.slice(0, 10) // Limit to top 10 results
.map((result: any, index: number) => {
const title = result.title || "";
const snippet = result.snippet || "";
const url = result.url || "";
const published = result.published_date ? `\nPublished: ${result.published_date}` : "";
return `[${index + 1}] ${title}\nURL: ${url}\n${snippet}${published}`;
});

if (snippets.length > 0) {
const header = `Found ${json.metadata?.number_of_results || json.results.length} results:\n\n`;
return header + snippets.join("\n\n");
}
}

// Search suggestions
if (json.suggestions && Array.isArray(json.suggestions) && json.suggestions.length > 0) {
return `No direct results found. Related searches:\n${json.suggestions.join("\n")}`;
}

// Query corrections
if (json.corrections && Array.isArray(json.corrections) && json.corrections.length > 0) {
return `Did you mean: ${json.corrections.join(", ")}?`;
}

return "No search results found.";
} catch (error) {
if (error instanceof Error) {
return `Error searching with SERPEX: ${error.message}`;
}
return `Unknown error occurred while searching with SERPEX`;
}
}
}
Loading