2026-07-28T11:35:09.090Z

LangChain Token Counter: Audit Estimate and Usage Coverage

Use approximation before a LangChain model call, provider usage after it, and an expected-call manifest to catch missing token evidence.

The useful answer is not “pick one LangChain token counter.” Use two different counters for two different decisions. Run count tokens approximately() before a model call when you need a fast context pressure estimate. Read provider reported AIMessage.usage metadata after the call when you need observed input, output, cache, or reasoning usage. Then compare the usage records with an expected model call manifest. Without that last coverage check, a tidy total can be low only because one call was never counted. That distinction matters in an agent. A message history estimate can help decide whether to trim context. It cannot prove what the provider processed, what it billed, whether a retry emitted usage, or whether a nested model call escaped the callback. The operational question is therefore: which counting rail supports this decision, and how do we know every expected call reached it? Treat approximation and provider usage as separate evidence LangChain's current Python reference describes count tokens approximately() as a simple approximation. At its defaults, it divides characters by four, adds three tokens per message, and rounds conservatively. The function counts message content and roles. It also accounts for AI tool calls, tool message call IDs, optional names, a fixed image allowance, and tool schemas supplied through the tools argument. The documentation explicitly says model specific tokenizers are required for accurate counts. That makes the function useful before invocation: The tools=bound tools detail is not cosmetic. The implementation serializes each supplied schema and adds its characters to the approximation. If a model is bound to tools but the counter receives only messages , the estimate may omit a large repeated input surface. Conversely, passing tools does not make the result provider exact. It remains an estimate based on a generic ratio and fixed allowances. After invocation, use the metadata on the returned AIMessage : LangChain standardizes UsageMetadata around input tokens , output tokens , and total tokens , with optional input and output detail maps. Its own example includes cache creation, cache reads, audio, and reasoning. “Optional” is the important word. A missing cache read field is unavailable evidence, not proof that the value was zero. Preserve that distinction in storage instead of filling absent detail with 0 . For multiple calls, UsageMetadataCallbackHandler aggregates AIMessage.usage metadata across models: Aggregation is convenient, but an aggregate answers “what the handler saw,” not “what the workflow should have called.” Keep per attempt records as well. Give every model attempt a stable call id , an attempt id , the resolved provider/model name, and a timestamp. A retry is a second attempt, not a correction to the first counter. Build a coverage test around the model call manifest Start from expected work, not from whatever usage rows happen to exist. For a four stage run, the manifest might require plan:1 , retrieve:1 , draft:2 , and verify:1 . The suffix is the attempt number. The verifier then joins expected attempts to three forms of evidence: a preflight approximation, including messages and required tool schemas; provider reported usage from the returned message or callback; the task level receipt that says the stage produced its expected effect. The join produces more useful states than one total: State What exists Safe interpretation provider reported provider usage, with the call identity observed usage for that attempt approximate only preflight estimate, no provider usage context estimate; billing usage unavailable missing call manifest row, no observation instrumentation gap or stage never ran detail unavailable provider total, missing expected cache/reasoning detail total may be usable; component analysis is blocked duplicate attempt two usage rows for one attempt ID aggregation risk; fix identity before summing The accompanying fixture deliberately looks plausible while remaining incomplete. It contains four expected calls. Two have provider usage, one has only an approximation, and one has no observation. The two provider rows sum to 1,451 tokens. That number is arithmetically correct and operationally incomplete. Run the audit: The result is: The two calls that have both forms of evidence also demonstrate why an estimate should retain its label. The approximation was 5.0% below provider total for one call and 16.5% below for another. This fixture does not claim those percentages generalize; the values are fixed test data. It proves the audit keeps estimates out of the observed provider total and can expose disagreement without treating either example as a universal calibration factor. One subtle current implementation detail deserves caution. LangChain's optional use usage metadata scaling=True takes the most recent AI message with usage, requires a consistent provider, and scales the approximation upward. The source clamps that factor between 1.0 and 1.25 ; it does not scale an estimate downward. This can be a useful conservative history estimate. It is not a reconciliation algorithm for invoices, mixed providers, or missing calls. Decide what each counter is allowed to drive Attach a decision boundary to every stored number. Use an approximation to: warn before a history approaches a soft context limit; compare two prompt or tool schema variants before sending them; decide whether to summarize, retrieve, or drop replaceable context; estimate the relative effect of including another message or tool schema. Use provider reported usage to: attribute observed input and output to a completed model attempt; separate cache, audio, or reasoning components when the provider returns them; reconcile provider/model totals across attempts; calculate cost only with a dated price source and explicit handling for unavailable detail. Use neither counter alone to prove: that every expected model call was instrumented; that a tool call reached its destination; that the expected deliverable exists; that a retry was safe or useful; that a low token run achieved the requested outcome. Those claims need call coverage and outcome evidence. A compact implementation can enforce four promotion rules: 1. Every expected attempt id has exactly one observation. 2. Every observed call is labeled approximate or provider reported ; the labels are never silently merged. 3. Tool bearing preflight estimates prove the schema set was passed to the counter. 4. Missing provider or cache detail stays null /unavailable and blocks only the decisions that require it. The threshold does not have to be universally 100%. A non production preview could permit approximate only coverage. A budget alert or customer chargeback should not. Encode the policy beside the consumer: context warning may accept estimates, while cost reconciliation requires complete provider usage and unique attempt IDs. Check the boundary before optimizing The reasonable default is simple: estimate before, observe after, audit coverage at the run boundary. Optimize only after all three work. If a context estimate is high, inspect its inputs before trimming. Was the full tool set included? Are tool results still needed for the next decision? Is a long message a durable decision receipt or replaceable narration? Removing the wrong context can make a run cheaper and less reliable. If provider usage is unexpectedly low, check for missing calls before celebrating. Confirm streaming chunks were combined into the final message, callbacks propagated into child runnables, retries received distinct attempt IDs, and the expected verifier stage actually ran. A cost graph with missing spans is not an optimization result. If cache savings matter, require the provider specific detail map and record its availability. LangChain gives you a common envelope, but providers do not necessarily populate every component. Do not infer a cache miss from a missing key. Compare like with like: same provider, model, prompt/tool surface, cache state, and outcome requirement. Finally, join token evidence to a task receipt. For a document review agent, the receipt might contain the source revision, required sections checked, failed assertions, and output hash. Tokens per successful call are still a weak denominator if the final artifact is absent. This two rail design is intentionally narrower than a generic observability stack. It answers a concrete decision: whether a LangChain token number is a context estimate, an observed provider measurement, or an incomplete view that must not drive cost or optimization claims. Sidewisp is currently in private preview. Token usage and estimated cost analytics are planned, not shipped. The product direction is to connect cost signals to useful progress and verified outcomes while keeping evidence and uncertainty visible. If that operational boundary matches the way you run agents, you can join the private preview. Sources LangChain Python reference: count tokens approximately LangChain source snapshot for the approximate counter LangChain messages guide: token usage on AIMessage LangChain reference: UsageMetadata LangChain reference: UsageMetadataCallbackHandler