> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pylva.com/llms.txt
> Use this file to discover all available pages before exploring further.

# LangGraph Cost Tracking

> Track token cost per LangGraph node, run, and customer with the Pylva LangChain callback handler.

Use the Pylva callback when LangGraph is your orchestration layer and you want
cost attribution by graph node and end customer.

## Python

```bash theme={null}
pip install "pylva-sdk[langchain]"
```

```python theme={null}
import os
from pylva.langchain import PylvaCallbackHandler

handler = PylvaCallbackHandler(api_key=os.environ["PYLVA_API_KEY"])

result = graph.invoke(
    {"question": "Where did spend increase?"},
    config={
        "callbacks": [handler],
        "metadata": {"pylva_customer_id": "cust_acme"},
    },
)
```

Pylva reads LangChain usage metadata at callback time. It sends model, provider,
tokens in, tokens out, latency, status, LangGraph run ids, `customer_id`, and
`step_name`. It does not send prompts, completions, tool arguments, or tool
outputs.

## TypeScript

```bash theme={null}
pnpm add @pylva/sdk @langchain/core @langchain/langgraph
```

```ts theme={null}
import { PylvaCallbackHandler } from "@pylva/sdk/langgraph";

const handler = new PylvaCallbackHandler({
  apiKey: process.env.PYLVA_API_KEY!,
});

const result = await graph.invoke(
  { question: "Where did spend increase?" },
  {
    callbacks: [handler],
    metadata: { pylva_customer_id: "cust_acme" },
  },
);
```

`@pylva/sdk/langchain` re-exports the same TypeScript handler for LangChain.js
callback discoverability. The deep entrypoints do not import the root SDK, so
they do not auto-patch provider clients.

## Node Attribution

LangGraph adds run metadata such as `langgraph_node`. Pylva uses that as the
event `step_name`, so costs naturally group by graph node.

```python theme={null}
graph.invoke(
    inputs,
    config={
        "callbacks": [handler],
        "metadata": {
            "pylva_customer_id": "cust_acme",
            "pylva_step": "support_triage",
        },
    },
)
```

Step resolution order:

* `metadata["langgraph_node"]`
* `metadata["pylva_step"]`
* `metadata["langgraph_step"]`
* LangChain run name

Customer resolution order:

* `PylvaCallbackHandler(customer_id="...")`
* `new PylvaCallbackHandler({ customerId: "..." })` in TypeScript
* `metadata["pylva_customer_id"]`
* `metadata["customer_id"]`
* Active `pylva.track_context`
* Active `track()` context in TypeScript
* `anonymous`

## Per-Customer Billing

Pass one stable opaque customer id per graph invocation.

```python theme={null}
for customer_id, question in pending_questions:
    graph.invoke(
        {"question": question},
        config={
            "callbacks": [handler],
            "metadata": {"pylva_customer_id": customer_id},
        },
    )
```

Pylva prices usage on the server, so pricing changes do not require changing
the graph code that reports raw usage.

## Tool Calls

Tool-call billing is opt-in because many tools are not billable. Enable it only
when a tool execution itself should create a configured usage event.

```python theme={null}
handler = PylvaCallbackHandler(
    api_key=os.environ["PYLVA_API_KEY"],
    track_tool_calls=True,
)
```

Tool events report `metric="calls"` and `metric_value=1`. Configure the price
for that metric in Pylva before using it for invoices.

## Avoid Double Counting

Use either the LangGraph callback path or the provider auto-instrumentation path
for the same runtime. In TypeScript, importing `@pylva/sdk/langgraph` or
`@pylva/sdk/langchain` alone does not auto-patch providers; importing the root
`@pylva/sdk` package does.

## Failure Behavior

Callback errors are swallowed. If Pylva is unavailable, the graph continues.
When LangChain does not provide token usage, Pylva records the run with zero
tokens and `metadata.usage_missing=true` rather than reading prompt text to
estimate usage.
