# LangChain

Both SDKs ship LangChain integrations: three agent tools (search, scrape and research) for LangChain in Python and LangChain.js, and a document loader for Python. The tools return compact text built for a model's context window: the search tool returns the passages of each result that match the query, not whole pages.

## Install

**Python**

```bash
pip install "xdive[langchain]"   # installs langchain-core
```

**JavaScript**

```bash
npm install xdive @langchain/core zod
```

In Python the tools live in `xdive.langchain`; in JavaScript they are imported from `xdive/langchain`. `@langchain/core` and `zod` are optional peer dependencies of the npm package, needed only for this entry point.

The tools read `XDIVE_API_KEY` (and `XDIVE_BASE_URL`) like the SDK clients. The key needs the `search` scope for the search and research tools and the `scrape` scope for the scrape tool; the loader's crawl mode needs `crawl`.

## Tools

| Class | Tool name | Input | Returns |
|---|---|---|---|
| `XDiveSearchTool` | `xdive_search` | `query`, `limit` (default 5, 1 to 20), `freshness` (`pd`, `pw`, `pm` or `py`, optional) | JSON array of results, each with its relevant passages. |
| `XDiveScrapeTool` | `xdive_scrape` | `url` | The page's Markdown, cut at 60,000 characters. |
| `XDiveResearchTool` | `xdive_research` | `query`, `depth` (`quick` or `deep`, default `quick`) | JSON object with a cited answer and its citations. |

The tool names and descriptions are what the model sees when it decides which tool to call. The search tool's description points the model to `xdive_research` when it needs a synthesized answer.

### xdive_search

Calls [search](https://xdive.dev/docs/search.md) with `search_depth: "advanced"` and `auto_parameters: true`: the API reads the top web results and keeps the excerpts most relevant to the query, and infers news sources and a time range from the wording when the query asks for recent information. The tool returns a JSON array:

```json
[
  {
    "title": "What's new in Python packaging",
    "url": "https://packaging.example.org/news/license-metadata",
    "snippet": "License-Expression and License-File fields replace the old classifiers…",
    "passages": [
      "PEP 639 adds the License-Expression field, which holds an SPDX expression such as MIT OR Apache-2.0…",
      "Build backends copy the files named by license-files into the .dist-info/licenses directory…"
    ],
    "relevance": 11.82
  }
]
```

| Field | Description |
|---|---|
| `title`, `url` | The search result. |
| `snippet` | The search engine's description of the result. |
| `passages` | Up to 3 excerpts of the page that best match the query, best first. Empty for results the API did not read or found nothing relevant in. |
| `relevance` | The score of the best passage: BM25 relevance to the query, not normalized; higher is more relevant. Absent (JavaScript) or `null` (Python) when `passages` is empty. |

The API reads at most 8 results per call, keeping the search engine's order. A call counts as one search plus one page for each result read.

In Python, `limit` is clamped to 1 to 20; in JavaScript the input schema rejects values outside that range and `freshness` must be one of the four values.

### xdive_scrape

Calls [scrape](https://xdive.dev/docs/scrape.md) with the Markdown format and returns the Markdown as plain text, cut at 60,000 characters so one long page cannot fill the context. In JavaScript the input schema requires a valid URL. A call counts as one page.

### xdive_research

Calls [research](https://xdive.dev/docs/research.md) and returns a JSON object:

```json
{
  "answer": "PEP 639 replaces license classifiers with an SPDX License-Expression field [1] and records license files in the wheel [2].",
  "confidence": "high",
  "citations": [
    { "n": 1, "url": "https://peps.python.org/pep-0639/", "title": "PEP 639" },
    { "n": 2, "url": "https://packaging.example.org/news/license-metadata", "title": "What's new in Python packaging" }
  ]
}
```

`answer` holds the answer text with numbered citation markers; `n` is the marker's number. `quick` research takes seconds, `deep` one to two minutes. A call counts the searches and pages the research used; a result served from the short-lived cache is not counted. See [Usage and billing](https://xdive.dev/docs/usage.md).

### Sharing a client

Each tool creates its own client from the environment unless you give it one. To share configuration and the cached organization id, pass a client:

**Python**

```python
from xdive import XDive
from xdive.langchain import XDiveResearchTool, XDiveScrapeTool, XDiveSearchTool

xdive = XDive(api_key="ck_live_…", base_url="https://your-host/api")
tools = [
    XDiveSearchTool(client=xdive),
    XDiveScrapeTool(client=xdive),
    XDiveResearchTool(client=xdive),
]
```

**JavaScript**

```ts
import { XDive } from "xdive";
import { XDiveResearchTool, XDiveScrapeTool, XDiveSearchTool } from "xdive/langchain";

const xdive = new XDive({ apiKey: process.env.XDIVE_API_KEY, baseUrl: "https://your-host/api" });
const tools = [new XDiveSearchTool(xdive), new XDiveScrapeTool(xdive), new XDiveResearchTool(xdive)];
```

In Python the client is the `client` keyword argument; in JavaScript it is the constructor's only argument.

## Build an agent

The tools work with any LangChain chat model that supports tool calling. This example uses LangChain's `create_agent` / `createAgent` with an Anthropic model, which needs `langchain` and the Anthropic integration installed and `ANTHROPIC_API_KEY` set:

**Python**

```bash
pip install "xdive[langchain]" langchain langchain-anthropic
```

**JavaScript**

```bash
npm install xdive @langchain/core zod langchain @langchain/anthropic
```

**Python**

```python
from langchain.agents import create_agent

from xdive import XDive
from xdive.langchain import XDiveResearchTool, XDiveScrapeTool, XDiveSearchTool

xdive = XDive()  # reads XDIVE_API_KEY
agent = create_agent(
    model="anthropic:claude-opus-5-5",
    tools=[
        XDiveSearchTool(client=xdive),
        XDiveScrapeTool(client=xdive),
        XDiveResearchTool(client=xdive),
    ],
    system_prompt="Answer from the web and cite the URLs you used.",
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What changed in the latest Node.js LTS release?"}]}
)
print(result["messages"][-1].content)
```

**JavaScript**

```ts
import { createAgent } from "langchain";
import { XDive } from "xdive";
import { XDiveResearchTool, XDiveScrapeTool, XDiveSearchTool } from "xdive/langchain";

const xdive = new XDive(); // reads XDIVE_API_KEY
const agent = createAgent({
  model: "anthropic:claude-opus-5-5",
  tools: [new XDiveSearchTool(xdive), new XDiveScrapeTool(xdive), new XDiveResearchTool(xdive)],
  systemPrompt: "Answer from the web and cite the URLs you used.",
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "What changed in the latest Node.js LTS release?" }],
});
console.log(result.messages.at(-1)?.content);
```

A typical run calls `xdive_search` to find sources, reads the passages, and calls `xdive_scrape` on a result when it needs the whole page. For questions that need several sources, `xdive_research` returns a cited answer in one call.

The tools raise the SDK's errors (`XDiveError` and its subclasses in Python, the matching error classes in JavaScript) after the client's own retries. How the agent reacts to a failed tool call depends on your LangChain version's tool-error handling. See [Python SDK errors](https://xdive.dev/docs/sdk-python.md#errors) and [JavaScript SDK errors](https://xdive.dev/docs/sdk-javascript.md#errors).

## Document loader

`XDiveLoader` (Python) loads one page or a whole site as LangChain `Document`s, for indexing in a vector store or passing to a chain.

```python
from xdive.langchain import XDiveLoader

page = XDiveLoader("https://example.com/pricing").load()
site = XDiveLoader("https://docs.example.com", mode="crawl", max_pages=100, max_depth=3).load()

for doc in site:
    print(doc.metadata["source"], len(doc.page_content))
```

`XDiveLoader(url, *, mode="scrape", client=None, **options)`

| Argument | Description |
|---|---|
| `url` | The page to scrape, or the seed URL of the crawl. |
| `mode` | `"scrape"` loads the one URL; `"crawl"` crawls from it and loads every page. Any other value raises `ValueError`. |
| `client` | An `XDive` client to use. Defaults to `XDive()`, which reads the environment. |
| `**options` | Passed to `XDive.scrape` or `XDive.crawl`: crawl parameters such as `max_pages`, `max_depth`, `include_paths` and `exclude_paths`, and the client's `timeout` and `poll_interval` for crawls. |

`load()` returns a list; `lazy_load()` yields the documents one by one. Each document's `page_content` is the page's Markdown. Its `metadata` holds `source` (the page URL) and, when the page has them, `url`, `title`, `description`, `lang` and `fetched_at`. In crawl mode, pages that failed are skipped.

A crawl waits for the whole crawl to finish before the first document is returned; the client's default wait is 600 seconds (`timeout`), after which `CrawlTimeout` is raised.

The JavaScript package has no loader class. Build documents from a crawl with `pageMarkdown`:

```ts
import { Document } from "@langchain/core/documents";
import { XDive, pageMarkdown } from "xdive";

const xdive = new XDive();
const record = await xdive.crawl("https://docs.example.com", { max_pages: 100 });
const docs = (record.results ?? [])
  .filter((page) => !page.error)
  .map((page) => new Document({ pageContent: pageMarkdown(page), metadata: { source: page.url, title: page.title } }));
```

## Related

- [LlamaIndex](https://xdive.dev/docs/llamaindex.md)
- [Python SDK](https://xdive.dev/docs/sdk-python.md) and [JavaScript SDK](https://xdive.dev/docs/sdk-javascript.md)
- [Search](https://xdive.dev/docs/search.md) and [Research](https://xdive.dev/docs/research.md)

---

Source: https://xdive.dev/docs/langchain · All docs for agents: https://xdive.dev/llms.txt
