This is a Custom Integration library.
What it does
Every call has two layers:- Voice layer: STT hears the user and TTS speaks the reply (your transport). You already track this.
- Brain layer: LangGraph decides which step runs next, and LangChain runs the LLM and tools inside each step. This is invisible unless you instrument it.
tuner-langchain makes the brain layer visible. Instead of hand-wrapping every node and tool, you attach one callback handler to graph.invoke(). It records node transitions, timing, and tool calls automatically. At hang-up you format those events and include them in your Create Call payload.
The library owns brain-layer tracing. You still own the voice transport and the API POST.
How it works
Three pieces do the work. A simple way to picture them:- Handler (
TunerLangGraphHandlerorTunerLangChainHandler): a LangChain callback. You pass it toinvoke()and never call its methods yourself. - Accumulator (
TunerAccumulator): one per call. It stores every event the brain layer produced. Read it back withget_invocations(). - Segment builder (
segments_from_invocation()): converts one turn’s events into Tuner transcript rows, with timestamps as milliseconds since call start.
One handler sees both frameworks. LangGraph is built on LangChain, so a single handler on
graph.invoke() captures the graph’s nodes and the LLM/tool calls nested inside them, with no extra wiring per LLM.| Framework | Role | You’ll recognize |
|---|---|---|
| LangGraph | Orchestration (which step runs next) | StateGraph, conditional edges |
| LangChain | Execution (LLM calls, tools, prompts) | ChatOpenAI.invoke(), @tool |
Install
langchain-core ≥ 1.0 (langgraph ≥ 1.0 for graphs). Pin a version in production. See the package on PyPI.
The flow
You touch the library at three moments in a call: set up once, attach the handler on every turn, then format and send when the call ends.Attach the handler on every turn
Pass the handler in
config each time you run the graph or chain. This is the only wiring you add.Format and send when the call ends
Turn the captured events into transcript rows, merge them with your own voice turns, and POST.Then include
transcript in your Create Call request.Data privacy
By default the library forwards prompts, inputs, and tool payloads. To keep sensitive data out, pass aCaptureConfig to the accumulator:
Public API
| Symbol | Role |
|---|---|
TunerLangGraphHandler | Callback for a LangGraph StateGraph |
TunerLangChainHandler | Callback for a plain LangChain chain |
TunerAccumulator | Per-call event storage; read with get_invocations() |
segments_from_invocation() | Turns raw events into Tuner transcript rows |
CaptureConfig | Privacy / redaction flags |
GraphInvocation, NodeTransition, ToolCallEvent | Data models, mostly for reading/debugging |