Skip to main content

LangChain and LangGraph observability for Tuner SDKs

Project description

tuner-langchain

LangChain and LangGraph observability for Tuner SDKs.

Captures node transitions, tool calls, and tool results from LangChain and LangGraph agents and feeds them into the Tuner transcript pipeline — giving you full visibility into what your orchestration layer did on every call.


Overview

When users run LangChain or LangGraph as the orchestration layer inside a LiveKit or Pipecat voice agent, tool calls and node transitions happen inside the graph — invisible to the voice framework. This package bridges that gap.

It works by attaching a LangChain callback handler to the graph invocation. The handler captures every node transition and tool call with wall-clock timestamps, and the Tuner SDK mappers inject them into the transcript at flush time — in the correct position between the user message and the bot response.


Installation

Not yet on PyPI. Install locally from the repo:

pip install -e /path/to/tuner-langchain

Once published:

pip install tuner-langchain

Requirements: Python ≥ 3.10, langchain-core >= 1.0, < 2.0


Usage

With LiveKit

from livekit.agents import AgentSession, JobContext, Agent
from livekit.plugins import langchain
from tuner import TunerPlugin

async def entrypoint(ctx: JobContext):
    session = AgentSession(...)

    plugin = TunerPlugin(session, ctx)
    wrapped_graph = plugin.wrap_graph(my_compiled_graph)

    agent = Agent(
        instructions="",
        llm=langchain.LLMAdapter(
            wrapped_graph,
            stream_mode="custom",
            config={"configurable": {"thread_id": thread_id}},
        ),
    )

    await session.start(agent=agent, room=ctx.room)

For plain LangChain (non-graph):

wrapped_chain = plugin.wrap_chain(my_chain)
agent = Agent(
    instructions="",
    llm=langchain.LLMAdapter(
        wrapped_chain,
        stream_mode="messages",
        config={"configurable": {"thread_id": thread_id}},
    ),
)

What gets captured

Node transitions (node_transition)

Every named LangGraph node or LangChain chain step:

Field Description
node_name The node/step name as defined in the graph
start_ms Start time relative to call start
end_ms End time relative to call start
duration_ms Execution time in milliseconds
inputs Node inputs (omitted when empty)
outputs Node outputs (omitted when empty)
error Error message if the node failed
node_instructions System prompt active during this node's LLM call

LangGraph internal nodes (__start__, __end__, compiled graph root) are filtered out automatically.

Tool calls (agent_function + agent_result)

Every tool invocation inside the graph:

Field Description
tool_name Tool name
inputs Raw input string passed to the tool
output Tool result or error message
is_error Whether the tool raised an error
duration_ms Tool execution time in milliseconds
start_ms Invocation time relative to call start

Data Privacy

By default, tuner-langchain forwards the following data to the Tuner ingestion API:

Field Captured by default How to disable
Node instructions node_instructions=False
Tool inputs tool_inputs=False
Tool outputs tool_outputs=False
Node inputs node_inputs=False
Node outputs node_outputs=False

Node instructions are capped at 300 characters. Tool error output is always captured regardless of tool_outputs — errors are not considered sensitive and are required for debugging.

To disable specific fields, pass a CaptureConfig to wrap_graph() or wrap_chain():

from tuner_langchain import CaptureConfig

wrapped_graph = plugin.wrap_graph(
    my_graph,
    capture=CaptureConfig(
        node_instructions=False,
        tool_inputs=False,
        tool_outputs=False,
        node_inputs=False,
        node_outputs=False,
    )
)

How it fits in the transcript

All segments are sorted chronologically by start_ms — the correct execution order:

user message
  node_transition  "intent_classifier"   (start_ms, end_ms, duration_ms)
  node_transition  "booking_node"        (start_ms, end_ms, inputs, outputs)
  agent_function   "get_patient_info"    (start_ms, inputs)
  agent_result     "get_patient_info"    (start_ms, output, duration_ms)
agent text response
user message
  ...

The segment shape is identical to what Tuner produces for non-LangGraph tool calls — same role, same tool object structure, same timing fields. The Tuner API and frontend handle both paths transparently.


Architecture

src/tuner_langchain/
├── __init__.py          public API
├── models.py            NodeTransition, ToolCallEvent, GraphInvocation
├── accumulator.py       TunerAccumulator — stores events per session
├── segment_builder.py   segments_from_invocation() — used by SDK mappers at flush
└── handlers/
    ├── __init__.py
    ├── base.py          TunerBaseHandler — shared tool + chain-end logic
    ├── langgraph.py     TunerLangGraphHandler — filters __start__/__end__
    └── langchain.py     TunerLangChainHandler — filters anonymous wrappers

TunerBaseHandler holds all shared logic. TunerLangGraphHandler and TunerLangChainHandler only override on_chain_start — the one place where the two frameworks differ in how chain names are interpreted.


Public API

from tuner_langchain import (
    # Handlers — pass to graph/chain config
    TunerLangGraphHandler,
    TunerLangChainHandler,

    # Accumulator — one per session, created by wrap_graph() / wrap_chain()
    TunerAccumulator,

    # Segment builder — used internally by SDK mappers at flush time
    segments_from_invocation,

    # Models
    GraphInvocation,
    NodeTransition,
    ToolCallEvent,
)

Development

uv sync
uv run pytest
uv run ruff check .
uv run mypy

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tuner_langchain-0.1.1.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tuner_langchain-0.1.1-py3-none-any.whl (17.7 kB view details)

Uploaded Python 3

File details

Details for the file tuner_langchain-0.1.1.tar.gz.

File metadata

  • Download URL: tuner_langchain-0.1.1.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for tuner_langchain-0.1.1.tar.gz
Algorithm Hash digest
SHA256 279121ed509d84b19667fb190441254b6a85966b87fd974b7be6c261a1665d73
MD5 8564898220a87720905ef8178e8a9b50
BLAKE2b-256 df5f0c53ebea11254d4794fa0286a53c3f6151db5ca9809ab0fd81f369bf3084

See more details on using hashes here.

File details

Details for the file tuner_langchain-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for tuner_langchain-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c57df2e5c3760599bd2ec4aa5ac68b9782b050824567280fc122e3fa90d9a636
MD5 ac0218de9552ae3a097a88871d13bdd0
BLAKE2b-256 6e7cece66e96b298991dca42fba1e7f01a7e0c9ba6705b47c4e9da6f50cc4efd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page