@raindrop-ai/langchain
v0.1.0
Published
Raindrop integration for LangChain
Keywords
Readme
@raindrop-ai/langchain
Raindrop integration for LangChain. Automatically captures LLM calls, tool usage, chains, retrievers, and agent actions via LangChain's callback system.
Installation
npm install @raindrop-ai/langchain @langchain/coreUsage
import { createRaindropLangChain } from "@raindrop-ai/langchain";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const raindrop = createRaindropLangChain({
writeKey: "your-write-key",
userId: "user-123",
});
const model = new ChatOpenAI({ model: "gpt-4o" });
const result = await model.invoke(
[new HumanMessage("Hello!")],
{ callbacks: [raindrop.handler] },
);
await raindrop.flush();What gets captured
- LLM calls: model name, input, output, token usage
- Tool calls: tool name, input arguments, output
- Chains: execution spans with parent-child nesting
- Retrievers: query and document count
- Errors: captured with OTLP error status
Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| writeKey | string | - | Raindrop API write key (omit to disable telemetry) |
| endpoint | string | https://api.raindrop.ai/v1/ | API endpoint |
| userId | string | - | Associate all events with a user |
| convoId | string | - | Group events into a conversation |
| projectId | string | - | Route events to a specific project (slug); omit for the default Production project |
| eventName | string | ai_generation | Event name applied to every event (the event field in the dashboard) |
| debug | boolean | false | Enable verbose logging |
| traceChains | boolean | false | Create spans for chain/runnable execution. Off by default because LangGraph agents emit many structural "chain" spans that bury the LLM and tool spans. |
| traceRetrievers | boolean | true | Create spans for retriever calls |
| filterLangGraphInternals | boolean | true | Filter LangGraph-internal chain events and deduplicate LLM callbacks |
| maxTextFieldChars | number | 1000000 | Per-field cap for event input/output and serialized span payloads, enforced before/during serialization (truncated values end with ...[truncated by raindrop]; a stricter OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT env var is honored) |
Projects
If your org has multiple projects, route events to a specific one by passing its slug as projectId:
const raindrop = createRaindropLangChain({
writeKey: "your-write-key",
projectId: "support-prod",
});This sets the X-Raindrop-Project-Id header on every event. Omit it (or pass "default") to use your org's default Production project — the existing behavior. Single-project orgs need nothing new.
Agents (LangGraph / ReAct)
For a prebuilt agent (createReactAgent) or any LangGraph graph, pass the handler
once to the top-level invoke call. LangChain propagates the callback down to
every LLM step and tool call in the run, and the handler threads them onto a single
Raindrop event (with a span tree) via each callback's runId/parentRunId.
import { createRaindropLangChain } from "@raindrop-ai/langchain";
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { z } from "zod";
const raindrop = createRaindropLangChain({
writeKey: "your-write-key",
userId: "user-123",
eventName: "support-agent",
});
const getWeather = tool(async ({ city }) => `It is sunny and 72F in ${city}.`, {
name: "get_weather",
description: "Get the current weather for a city.",
schema: z.object({ city: z.string() }),
});
const agent = createReactAgent({
llm: new ChatOpenAI({ model: "gpt-4o-mini" }),
tools: [getWeather],
});
// One handler on the top-level invoke captures the whole agent run:
// LLM steps + tool calls, all on one event.
await agent.invoke(
{ messages: [{ role: "user", content: "What's the weather in SF?" }] },
{ callbacks: [raindrop.handler] },
);
await raindrop.flush(); // await before the process/lambda exitsSee examples/langchain-agent-basic/ for a full runnable example.
The handler works with LangGraph out of the box and automatically:
- Filters LangGraph-internal chain events (graph executor,
__start__,__end__, channel nodes) - Deduplicates LLM callbacks that LangGraph fires multiple times with the same
runId
Best practices:
- Prebuilt agents / graphs: attach the handler to the top-level
invoke(...)(as above). This is the one place that reliably captures the full run. - Hand-built graphs with custom callback wiring: attach the handler to the model inside your node rather than duplicating it at each layer, to avoid double events from manual callback propagation.
- Create a new handler instance per request in server environments.
- Always
await raindrop.flush()(orshutdown()) before exit — in short scripts and serverless the process can freeze before the batched spans ship.
LangSmith Coexistence
Raindrop and LangSmith can run simultaneously — both receive the same LangChain callbacks independently. Set LANGSMITH_TRACING=false to disable LangSmith if you only want Raindrop.
Testing
pnpm testTests use MSW to intercept HTTP requests — no real LLM calls are made.
