Skip to main content

Installation

Quick Start

What Gets Traced

The LangChain integration automatically captures:
  • LLM calls — model name, input messages, output text, token usage (prompt/completion/total), finish reason
  • Tool calls — tool name, input arguments, output, duration (via interaction.track_tool() spans)
  • Chains — chain execution with nested spans
  • Retrievers — query text and document count
  • Agent actions — tool selection and execution
  • Errors — captured with error status on the span
  • Tags and metadata — LangChain tags and metadata are forwarded to Raindrop event properties
  • Extended token categories — cached tokens (ai.usage.cached_tokens) and reasoning tokens (ai.usage.thoughts_tokens) when available from the provider (e.g. OpenAI)
  • Finish reason — captured as ai.finish_reason in event properties (e.g. "stop", "length")
All operations are linked with parent-child relationships, so you can see the full execution tree in the Raindrop dashboard.

Configuration

Event name

Every event this integration ships carries an event name (the event field in the dashboard). It defaults to ai_generation. Set eventName (event_name in Python) to label LangChain traffic — e.g. distinguish a support agent from a billing agent:

Projects

Route events to a specific project by passing its slug as projectId (project_id in Python):
This sets the X-Raindrop-Project-Id header on every event. Omit it (or pass "default") to use your org’s default Production project, which is the existing behavior. Single-project orgs need nothing new.

Multiple projects in one process

Available in raindrop-ai>=0.0.56. When one service runs several LangChain agents that should report to different projects, create one RaindropLangchain wrapper per project. Each wrapper owns its own raindrop.Raindrop client, so the two route independently — there is no shared module-level state:
Each wrapper owns its configuration and delivery pipeline, so concurrent requests handled by different agents route independently. To share a single client across wrappers (or with the module-level API), construct a raindrop.Raindrop yourself and pass it via client=:

Using with LangGraph

The Raindrop handler works with LangGraph out of the box. It automatically filters LangGraph-internal chain events (graph executor, __start__, __end__, channel nodes) and deduplicates LLM callbacks that LangGraph may fire multiple times. Pass the handler both at graph invocation time and inside your LLM node:

LangGraph Best Practices

  • Pass callbacks to the model, not the graph — use callbacks: [raindrop.handler] inside your LLM node function only. Do NOT pass callbacks to graph.invoke() — LangGraph’s internal callback propagation causes duplicate events and noisy chain spans when callbacks are on the graph level.
  • Create a new handler per request in server environments to avoid state collisions between concurrent graph executions.
  • LangGraph-internal filtering is on by default — the filterLangGraphInternals option (default: true) skips noisy internal chain events from the graph executor and node wrappers. Set to false if you want full visibility into LangGraph internals.

Using with LangSmith

Raindrop and LangSmith can coexist — both use LangChain’s callback system and receive the same events independently. There is no conflict, but you should be aware of the following:
  • Both tracers are active simultaneously when LANGSMITH_TRACING=true is set. Each builds its own trace tree from the same callback events. This is safe but means LLM calls are traced twice (once by each system).
  • To use Raindrop only, disable LangSmith tracing:
  • To use both, no changes needed. Both handlers receive callbacks and ship data independently. Raindrop traces appear in the Raindrop dashboard; LangSmith traces appear in LangSmith.
  • Performance: having two tracers adds minimal overhead since both operate asynchronously and don’t block the LLM pipeline.

Usage with Chains

The same handler works with plain LangChain chains:

Passing Tags and Metadata

LangChain tags and metadata are forwarded to Raindrop event properties:

Identify Users

Associate events with a user identity after initialization:

Track Signals

Send feedback, edits, or custom signals tied to a specific event:

Tool catalog capture (ai.prompt.tools)

TypeScript. 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. When the model reports no list the attribute is absent; pass tools on createRaindropLangChain(...) to record a catalog explicitly (tools: [] records an empty one). Python. raindrop-langchain ≥ 0.0.13 reads the list from the callback itself: RaindropCallbackHandler.on_chat_model_start / on_llm_start receive invocation_params["tools"] (or legacy functions), the list bind_tools() / bind(tools=...) put on the request, and the handler binds it until on_llm_end / on_llm_error, so the model span the OpenLLMetry langchain instrumentation starts inside the call carries ai.prompt.tools with the JSON Schema intact. Capture is exact; a call with neither key records []; sync, async and streaming calls are covered. The instrumentation writes the same list to gen_ai.tool.definitions (OpenLLMetry ≥ 0.62 no longer emits llm.request.functions.*), which raindrop-ai ≥ 0.0.72 also rebuilds from; the wrapper does not depend on that, and its bound list takes precedence when both are present. Pass tools= on RaindropLangchain(...), RaindropCallbackHandler(...) or create_raindrop_langchain(...) as the default, or per call through config={"metadata": {"raindrop_tools": [...]}} (stripped from the event properties); the per-call value replaces the default, either replaces the inferred list, and tools=[] records an empty catalog. Follows TRACELOOP_TRACE_CONTENT; needs raindrop-ai ≥ 0.0.70 and a model span: RaindropLangchain(...) builds its client with auto_instrument=False, so pass client=Raindrop(..., tracing_enabled=True, instruments={Instruments.LANGCHAIN}) or there is no span to carry the attribute. See Tool catalog capture for the attribute format, the absent / [] semantics and the override shapes accepted.

Application Git metadata (Python)

RaindropLangchain(...) and create_raindrop_langchain(...) accept the keyword-only app_git option. It defaults to True: explicit Raindrop Git environment or deployment context is applied immediately, and the base SDK may perform one bounded background local-Git lookup from the process working directory. Event capture, flush, and shutdown never wait for that lookup. Pass False to disable enrichment, or pass an AppGitOptions mapping with commit_sha, commit_dirty, branch, source_directory, detect_branch, and/or auto_detect. Automatic branch discovery remains opt-in through detect_branch=True (or RAINDROP_GIT_DETECT_BRANCH=true). For an ordinary in-process application, the process working directory is treated as the application-under-test checkout. A remote, coding, workflow, or observer process must not rely on its own checkout: pass app_git=False, provide explicit revision values, or set source_directory to the actual application checkout. Canonical per-operation properties remain authoritative. When supplying client=, configure app_git while constructing that Raindrop client; the supplied client is authoritative and the wrapper’s app_git argument does not reconfigure it. Release order is deliberate: first publish the base SDK feature, then publish the wrapper feature release with its minimum dependency coordinated to that base release. The existing raindrop-ai lower bound remains compatible, but application Git metadata is unavailable on an older core and must not be claimed complete until the base is upgraded. Until coordination assigns a released version, the wrapper checks for an explicit base app_git parameter and omits the option when unsupported. Explicit non-default configuration is debug-logged and omitted. Unsupported app_git is determined by signature inspection before construction, not by retrying initialization after a TypeError; Git configuration adds no initialization attempts and does not change any existing framework-specific initialization fallback. RaindropCallbackHandler(...) is unchanged and does not accept this option.

Flushing and Shutdown

Always call flush() before your process exits to ensure all telemetry is shipped: