Integrations

Four ways to get spans flowing, from zero-code to fully manual. All produce the same typed cost graph and compose freely inside one trace.

IntegrationCode per call siteCovers
LangChain / LangGraph0 lines (one callback)chains, graphs, tools, retrievers
LiteLLM0 lines (one callback)100+ providers via SDK or proxy
Client wrappers0 lines (wrap once)direct OpenAI / Anthropic SDK use
Manual2 linesanything else

LangChain / LangGraph

pip install "stepcost[langchain]"
from stepcost import StepCost
from stepcost.integrations.langchain import StepCostCallbackHandler

cc = StepCost(project="my-app", sink="sqlite:///~/.stepcost/my-app.db")
handler = StepCostCallbackHandler(cc)

with cc.trace(feature_id="support-bot", customer_id="acme") as trace:
    agent.invoke(inputs, config={"callbacks": [handler]})

Every chain/graph node becomes an agent_step, model calls become priced llm_generation spans, tools and retrievers are captured. Parenting follows LangChain's own run tree, so parallel branches attribute correctly; LCEL plumbing (RunnableSequence etc.) is filtered out of the tree.

LangChain folds cache reads/writes into usage_metadata.input_tokens. The handler unfolds them, so cached tokens are billed at the cache rate — not double-counted at the full input rate.

LiteLLM

No extra dependency — LiteLLM accepts plain callables:

import litellm
from stepcost.integrations.litellm import StepCostLiteLLMLogger

logger = StepCostLiteLLMLogger(cc)
litellm.success_callback = [logger.log_success_event]
litellm.failure_callback = [logger.log_failure_event]

with cc.trace(feature_id="chat") as trace:
    litellm.completion(model="claude-haiku-4-5", messages=[...])

Handles LiteLLM's normalized usage for both OpenAI-style (cached subset subtraction) and Anthropic-style (cache read/write counters) responses, and records call duration. Because the LiteLLM proxy sees traffic from every language, this is also the cheapest way to cover non-Python services.

OpenAI / Anthropic client wrappers

Wrap the official SDK clients once; every call is priced automatically:

from stepcost.integrations.openai import instrument_openai
from stepcost.integrations.anthropic import instrument_anthropic

openai_client = instrument_openai(OpenAI(), cc)              # chat, responses, embeddings
claude = instrument_anthropic(anthropic.Anthropic(), cc)     # messages

with cc.trace(feature_id="chat") as trace:
    openai_client.chat.completions.create(model="gpt-4o-mini", messages=[...])
    claude.messages.create(model="claude-haiku-4-5", max_tokens=64, messages=[...])

Manual context managers

For custom providers, tool calls, and retrieval steps:

from stepcost import agent_step, llm_call

with cc.trace(feature_id="pipeline") as trace:
    with agent_step("summarize"):
        with llm_call(model="my-model", provider="other") as call:
            resp = my_client.generate(...)
            call.record_usage(input_tokens=resp.in_toks, output_tokens=resp.out_toks)

Unknown models warn loudly and show as "unpriced spans" in stepcost report — add them with register_custom_model() or a custom price table (Concepts & pricing).