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

# LangChain

> Trace LangChain chains, agents, tools, and retrievers automatically using ZespanCallbackHandler — covering TypeScript and Python with full span-level detail.

<Note>
  Available for: **Python** and **TypeScript**.
</Note>

The `ZespanCallbackHandler` integrates with LangChain's callback system to automatically capture every LLM call, tool invocation, chain step, and retrieval operation as linked spans in Zespan. You pass it once and it instruments the entire execution graph — no changes to your chain or agent logic are needed.

## How it works

LangChain calls lifecycle methods on every registered `BaseCallbackHandler` at each stage of execution. The Zespan handler subscribes to:

| LangChain event                               | What Zespan captures                                  |
| --------------------------------------------- | ----------------------------------------------------- |
| `handleLLMStart` / `handleLLMEnd`             | Model, provider, latency, tokens, cost, finish reason |
| `handleChainStart` / `handleChainEnd`         | Chain type, input/output, duration                    |
| `handleToolStart` / `handleToolEnd`           | Tool name, input args, output, duration               |
| `handleRetrieverStart` / `handleRetrieverEnd` | Query, retrieved document count, duration             |
| `handleAgentAction`                           | Tool selected, reasoning, input                       |

All events share the same `trace_id` so you see the entire chain as a single flame graph in the Traces view.

***

## TypeScript

### Installation

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
npm install @zespan/sdk @langchain/core @langchain/openai
```

### Basic usage

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { zespan, ZespanCallbackHandler } from "@zespan/sdk";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";

zespan.init({ apiKey: process.env.ZESPAN_API_KEY! });

const handler = new ZespanCallbackHandler();

const llm = new ChatOpenAI({
  model: "gpt-4o",
  callbacks: [handler],
});

const response = await llm.invoke([
  new SystemMessage("You are a helpful assistant."),
  new HumanMessage("Summarize the history of the internet in three sentences."),
]);

console.log(response.content);
```

### Chains

Pass the handler to `RunnableSequence` or any chain using the `config` argument:

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence } from "@langchain/core/runnables";

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are an expert summarizer. Be concise."],
  ["human", "{topic}"],
]);

const chain = RunnableSequence.from([
  prompt,
  new ChatOpenAI({ model: "gpt-4o" }),
  new StringOutputParser(),
]);

const result = await chain.invoke(
  { topic: "quantum computing" },
  { callbacks: [new ZespanCallbackHandler()] }
);
```

### Agents and tools

The handler captures the full agent loop: plan → tool call → observation → next plan. Each iteration appears as linked spans.

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { createOpenAIFunctionsAgent, AgentExecutor } from "langchain/agents";
import { DynamicTool } from "@langchain/core/tools";
import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
import { zespan, ZespanCallbackHandler } from "@zespan/sdk";

zespan.init({ apiKey: process.env.ZESPAN_API_KEY! });

const tools = [
  new DynamicTool({
    name: "get-weather",
    description: "Get current weather for a city",
    func: async (city: string) => `Weather in ${city}: 22°C, sunny`,
  }),
];

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful assistant with access to tools."],
  ["human", "{input}"],
  new MessagesPlaceholder("agent_scratchpad"),
]);

const agent = await createOpenAIFunctionsAgent({
  llm: new ChatOpenAI({ model: "gpt-4o" }),
  tools,
  prompt,
});

const executor = new AgentExecutor({
  agent,
  tools,
  callbacks: [new ZespanCallbackHandler()],
});

const result = await executor.invoke({ input: "What's the weather in Tokyo?" });
```

### RAG with retriever tracing

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { createRetrievalChain } from "langchain/chains/retrieval";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import { Document } from "@langchain/core/documents";

const vectorStore = await MemoryVectorStore.fromDocuments(
  [new Document({ pageContent: "LangChain is a framework for building LLM apps." })],
  new OpenAIEmbeddings()
);

const retriever = vectorStore.asRetriever();

const combineDocsChain = await createStuffDocumentsChain({
  llm: new ChatOpenAI({ model: "gpt-4o" }),
  prompt: ChatPromptTemplate.fromMessages([
    ["system", "Answer based on context: {context}"],
    ["human", "{input}"],
  ]),
});

const retrievalChain = await createRetrievalChain({
  retriever,
  combineDocsChain,
});

// All retrieval and LLM calls appear as linked spans
const result = await retrievalChain.invoke(
  { input: "What is LangChain?" },
  { callbacks: [new ZespanCallbackHandler()] }
);
```

***

## Python

### Installation

```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
pip install zespan langchain-core langchain-openai
```

### Basic usage

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
from zespan.integrations.langchain_handler import ZespanCallbackHandler
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage

zespan.init(api_key="zsp_your_api_key_here")

handler = ZespanCallbackHandler()

llm = ChatOpenAI(model="gpt-4o", callbacks=[handler])

response = llm.invoke([
    SystemMessage(content="You are a helpful assistant."),
    HumanMessage(content="What is the Turing test?"),
])
print(response.content)
```

### Chains

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", "Summarize the following topic in two sentences."),
    ("human", "{topic}"),
])

chain = prompt | ChatOpenAI(model="gpt-4o") | StrOutputParser()

result = chain.invoke(
    {"topic": "large language models"},
    config={"callbacks": [ZespanCallbackHandler()]},
)
```

### Agents

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_core.tools import Tool
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

def get_weather(city: str) -> str:
    return f"Weather in {city}: 22°C, sunny"

tools = [Tool(name="get-weather", func=get_weather, description="Get weather for a city")]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant with access to tools."),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),
])

agent = create_openai_functions_agent(
    llm=ChatOpenAI(model="gpt-4o"),
    tools=tools,
    prompt=prompt,
)

executor = AgentExecutor(
    agent=agent,
    tools=tools,
    callbacks=[ZespanCallbackHandler()],
)

result = executor.invoke({"input": "What's the weather in Paris?"})
```

***

## How traces look in the dashboard

A LangChain agent run produces a trace like this in the flame graph:

```
AgentExecutor (chain)          ─────────────────────────────── 1.8s
  ├── ChatOpenAI (llm)         ──────────────── 820ms  gpt-4o
  │     → tool_calls: [get-weather]
  ├── get-weather (tool)       ──── 12ms
  └── ChatOpenAI (llm)         ──────── 640ms  gpt-4o
        → finish_reason: stop
```

Each row is a span. The total trace cost is the sum of both LLM spans. Tool call spans show the tool name and execution time but have no token cost.

<Tip>
  To associate a LangChain trace with a user session, wrap the chain invocation in `withZespanContext`:

  ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
  import { withZespanContext } from "@zespan/sdk";

  return withZespanContext({ userId, sessionId }, () =>
    chain.invoke({ input: userMessage }, { callbacks: [handler] })
  );
  ```
</Tip>

***

## Guardrails with LangChain

Pass guardrail options to `ZespanCallbackHandler` to enable pre/post content checks on LLM calls made by the chain:

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
const handler = new ZespanCallbackHandler({
  guardrails: { pre: true, post: true, failClosed: false },
});
```

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
handler = ZespanCallbackHandler(guardrails={"pre": True, "post": True})
```
