@raindrop-ai/deep-agents
v0.1.4
Published
Raindrop integration for LangChain Deep Agents
Maintainers
Keywords
Readme
@raindrop-ai/deep-agents
Raindrop integration for LangChain Deep Agents. Automatically captures LLM calls, tool usage (planning, filesystem, shell, subagent delegation), chains, and agent actions via LangChain's callback system.
Installation
npm install @raindrop-ai/deep-agents @langchain/core deepagentsUsage
import { createDeepAgent } from "deepagents";
import { ChatAnthropic } from "@langchain/anthropic";
import { createRaindropDeepAgents } from "@raindrop-ai/deep-agents";
const raindrop = createRaindropDeepAgents({
writeKey: "your-write-key",
userId: "user-123",
convoId: "session-abc",
});
const agent = createDeepAgent({
model: new ChatAnthropic({ model: "claude-sonnet-4-20250514" }),
tools: [/* filesystem, shell, etc. */],
});
const result = await agent.invoke(
{ messages: [{ role: "user", content: "Create a hello world file" }] },
{ callbacks: [raindrop.handler] },
);
await raindrop.shutdown();What gets captured
- LLM calls: model name, input, output, token usage (
prompt_tokens,completion_tokens) - Extended token categories:
ai.usage.cached_tokens(OpenAI prompt cache hits, Anthropiccache_read),ai.usage.thoughts_tokens(o1/o3 reasoning tokens) — when reported by the provider - Finish reason: captured as
ai.finish_reasonin event properties ("stop","length","tool_calls","content_filter", etc.) - Tool calls: tool name (
write_todos,read_file,write_file,edit_file,ls,glob,grep,execute,task), input, output - Chains: execution spans with parent-child nesting
- Agent actions: tool selection and finish events
- Multi-modal messages: typed-parts content (
[{type: "text", text: "..."}, ...]) is extracted cleanly — text-type parts are concatenated; non-text parts (images, audio) are skipped - Errors: captured with
error.type+error.messageproperties and[Error] ...output on the event, plus OTLP error status on the span
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 |
| debug | boolean | false | Enable verbose logging |
| traceChains | boolean | true | Create spans for chain execution |
| 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 the agent's chat model was given for
that call as ai.prompt.tools (one JSON document per tool: name, description,
full JSON Schema as inputSchema). It is read from the callback's
invocation_params.tools / functions, which ChatOpenAI, ChatAnthropic and similar
models populate from bindTools(). Absent when the model reports no list; 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 = createRaindropDeepAgents({
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 = createRaindropDeepAgents({
writeKey: "your-write-key",
appGit: {
commitSha: "0123456789abcdef0123456789abcdef01234567",
commitDirty: false,
branch: "main",
detectBranch: true,
sourceDirectory: "/path/to/application",
},
});
// Disable all application Git reporting.
const withoutGit = createRaindropDeepAgents({
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.
LangGraph Support
Deep Agents is built on LangGraph. The handler automatically:
- Filters LangGraph-internal chain events (
__start__,__end__,ChannelWrite:*,ChannelRead:*,Branch:*) — the colon is part of LangGraph's naming convention, user-defined chains whose names happen to start with "Branch" (e.g.BranchRouter) are NOT filtered - Keeps the root
LangGraphchain so the OTel association context is in place before any inner span fires (filtering it orphans all downstream spans) - Deduplicates LLM callbacks that LangGraph fires multiple times with the same
runId - Links parent-child spans for chain→LLM→tool hierarchies
API Surface
The createRaindropDeepAgents() factory returns:
handler— LangChainBaseCallbackHandlerto pass viaconfig.callbackslastEventId—event_idof the most recently finalized root event (orundefinedif none has landed). Use this to attach a follow-upsignals.track()to the agent invocation you just ran without polling the dashboard. Example:await agent.invoke({ messages: [...] }, { callbacks: [raindrop.handler] }); if (raindrop.lastEventId) { await raindrop.signals.track({ eventId: raindrop.lastEventId, name: "thumbs_up", type: "feedback", sentiment: "POSITIVE", }); }events.patch(eventId, patch)— update event propertiesevents.finish(eventId, patch)— finalize an eventevents.addAttachments(eventId, attachments)— attach files/text to an eventevents.setProperties(eventId, properties)— set custom propertiesusers.identify(users)— identify users with traitssignals.track(signal)— track user signals/feedbackflush()— flush pending telemetryshutdown()— flush and close connections
Known Limitations
- Beta status: API surface may change in future releases.
- Deep Agents version: Requires
deepagents >= 0.5.0. - Streaming: Token-by-token streaming events are not captured individually; only the final aggregated response is tracked.
- Subagent isolation: When using the
tasktool for subagent delegation, each subagent's callbacks fire independently. - Concurrent invocations on a shared handler: A single handler instance keeps one in-flight span map at a time. Running multiple
agent.invoke(...)calls concurrently with the same handler (e.g.Promise.all([agent.invoke(...), agent.invoke(...)])with the sameraindrop.handler) can scramble the linkage between events and traces. Instantiate onecreateRaindropDeepAgents()per concurrent request; sequential invocations on a shared handler are fully supported. - Long inputs/outputs are truncated at
maxTextFieldChars: The root event'sinputis the latest user message and itsoutputthe final response, each capped at the configuredmaxTextFieldChars(default 1,000,000 chars), the same cap the other JavaScript integrations use; the full conversation history lives on the LLM span (ai.prompt.messages). Tool payloads are pruned to the cap before JSON serialization, so a multi-MB tool output costs the cap — not the payload — on your event loop (truncated values carry a...[truncated by raindrop]marker).
Testing
# Unit tests (no external services; use MSW to intercept HTTP)
pnpm test -- tests/handler.test.tsEnd-to-end coverage runs through the cross-SDK conformance harness. The deep-agents conformance driver (conformance/deep-agents-driver.mjs) maps the shared scenario corpus onto this package's public API. The fault lane runs locally and on every PR against a fault-injecting fake ingest; the prod lane verifies real ingest through the public Query API readback. See conformance/README.md and the harness README / HOW-IT-WORKS.
Python
A Python version of this integration is available as raindrop-deep-agents on PyPI. See the Python package for details.
