Skip to content

feat(langchain): add the langchain and langgraph callback handler - #217

Merged
ephraimduncan merged 1 commit into
mainfrom
feat/langchain
Nov 7, 2026
Merged

ephraimduncan merged 1 commit into
mainfrom
feat/langchain

Conversation

@ephraimduncan

@ephraimduncan ephraimduncan commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

LangChain.js and LangGraph.js apps can now send traces to telemetry.dev through our own package. At this time the LangChain guide covers only Python, through OpenLLMetry, and a TypeScript app must use a third-party instrumentation.

import { init } from "@telemetry-dev/sdk";
import { TelemetryDevCallbackHandler } from "@telemetry-dev/langchain";

init({ serviceName: "support-agent" });

const agent = graph.compile().withConfig({ callbacks: [new TelemetryDevCallbackHandler()] });

The handler is a LangChain callback handler on @telemetry-dev/sdk, as the provider wrappers are. Thus the init() options (capture, mask, batching), propagateAttributes(), and the spans of observe() operate on the LangChain spans too. A LangGraph run becomes an invoke_agent span, and each node becomes a span under it. Each model call becomes a chat <model> span with usage, finish reason, and the type and name of the bound tools in gen_ai.tool.definitions. Each tool becomes an execute_tool span. LangGraph copies configurable.thread_id into the metadata of each run, and the handler uses it as the session.

The handler does not record runs that LangChain tags langsmith:hidden, such as __start__. Their child runs go under the nearest recorded run.

The handler sets awaitHandlers. Without it, LangChain calls handlers from a shared background queue. There the active context is not the context of the caller, and concurrent runs go under the wrong span of the app.

The declaration of handleChainStart in @langchain/core lists the parameters in a different order from the call that LangChain makes. The handler uses the order of the call, and a comment in handler.ts tells this.

If the state of a graph has a messages list, the output of the graph span is the last message. The trace list shows the first 4,000 characters of the root output, and a full message state puts the final answer after that limit.

Each span has langchain.run.id. With telemetry-dev/telemetry.dev#1113, telemetry.dev shows these spans as framework langchain. An older ingest shows them as otel.

The lockfile adds @langchain/core 1.3.4 and @langchain/langgraph 1.7.0 as dev dependencies. pnpm then shares their @cfworker/json-schema and ws 8.21.3 with the @modelcontextprotocol/sdk and @google/genai entries, and the peer suffixes of those entries change.

The LangGraph quickstart agent from the LangChain docs, with Claude Sonnet 4.6, in telemetry.dev:

LangGraph quickstart trace

Related: telemetry-dev/telemetry.dev#1113

Follow-up for the docs page and the setup flow: https://github.com/telemetry-dev/telemetry.dev/issues/1114

TelemetryDevCallbackHandler records the runs of LangChain.js and
LangGraph.js as spans of @telemetry-dev/sdk: graphs as invoke_agent
spans, nodes and chains, model calls with usage and bound tools, tools,
and retrievers. Each span has langchain.run.id, and the ingest uses it
to classify the span as framework langchain.
@ephraimduncan
ephraimduncan merged commit b7650a4 into main Nov 7, 2026
10 checks passed
@aikins01 aikins01 mentioned this pull request Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant