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
from livekit.plugins import langchain
from tuner import TunerPlugin
async def entrypoint(ctx: JobContext):
session = AgentSession(...)
plugin = TunerPlugin(session, ctx)
handler = plugin.attach_langgraph()
session = AgentSession(
llm=langchain.LLMAdapter(
graph=my_compiled_graph,
config={"callbacks": [handler]}, # ← pass it here
),
...
)
await session.start(...)
For plain LangChain (non-graph):
handler = plugin.attach_langchain()
chain.invoke(inputs, config={"callbacks": [handler]})
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 attach_langgraph() or
attach_langchain():
from tuner_langchain import CaptureConfig
handler = plugin.attach_langgraph(
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 attach_langgraph() / attach_langchain()
TunerAccumulator,
# Segment builder — used internally by SDK mappers at flush time
segments_from_invocation,
# Models
GraphInvocation,
NodeTransition,
ToolCallEvent,
)
Development
# Install with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest tests/ -v
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tuner_langchain-0.1.0.tar.gz.
File metadata
- Download URL: tuner_langchain-0.1.0.tar.gz
- Upload date:
- Size: 19.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98e1869c1d68c4dd26ba0e7037e7daaa3005b26da91ffca84f3e7fff7a7ec96f
|
|
| MD5 |
de951486435c061150008dd65ebc2eb2
|
|
| BLAKE2b-256 |
68abd4acf2da0a69f0ec892c8117daa5c3d8b92561a5f8745712573c7485957e
|
File details
Details for the file tuner_langchain-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tuner_langchain-0.1.0-py3-none-any.whl
- Upload date:
- Size: 16.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e0b68ce77a2c2482614e24a1c99eedd2632e06ad8d00c3478c43df0f90d456c
|
|
| MD5 |
603939b6dc96f73dad1631ea4f55ab5c
|
|
| BLAKE2b-256 |
04af8c0c9a978d20982512fecc991061fd94364e2c06e6ef721264ccd8ec7ed8
|