@raindrop-ai/langchain
v0.2.1
Published
Raindrop integration for LangChain
Maintainers
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) |
| tools | ToolDeclaration[] | - | Overrides the tool catalog recorded as ai.prompt.tools on every model span. Use when the integration cannot see the tools the model was given. |
Tool catalog capture
Every model span (llm) records the tools LangChain handed the provider for that
call as ai.prompt.tools: one JSON document per tool with name, description
and the full JSON Schema as inputSchema. The list comes from the callback's
invocation_params.tools (or legacy functions), so bindTools() / bind({ tools })
on ChatOpenAI, ChatAnthropic and similar models is captured automatically. Provider
server tools (Anthropic web_search_*, ...) are recorded as provider-defined
without an invented schema. When the model reports no list the attribute is absent;
pass tools to record a catalog explicitly (tools: [] records an empty one).
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.
Application Git identity
Each client reports the application commit SHA when it can determine one. Local Git discovery runs in the background and never delays or fails application calls. Automatic branch discovery is off by default.
Use appGit to override detection or opt out:
const raindrop = createRaindropLangChain({
writeKey: "your-write-key",
appGit: {
commitSha: "0123456789abcdef0123456789abcdef01234567",
commitDirty: false,
branch: "main",
detectBranch: true,
sourceDirectory: "/path/to/application",
},
});
// Disable all application Git reporting.
const withoutGit = createRaindropLangChain({
writeKey: "your-write-key",
appGit: false,
});commitSha, commitDirty, and branch are explicit overrides. detectBranch opts into automatic branch lookup, sourceDirectory selects the application repository, and autoDetect: false disables automatic sources while retaining explicit values.
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.
