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

# LangChain

> Use Mesh as the model behind LangChain and LangGraph — set base_url on ChatOpenAI and every Mesh model, including the auto-router, is available.

LangChain's `ChatOpenAI` takes a `base_url`, so Mesh needs no adapter, no shim, and no LangChain-specific package. Tool binding, structured output, streaming, and LangGraph all work unchanged.

## Install

```bash theme={null}
pip install langchain langchain-openai
```

## Connect

```python theme={null}
import os

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="auto",                              # or any Mesh catalog id
    base_url="https://api.meshapi.ai/v1",
    api_key=os.environ["MESH_API_KEY"],        # rsk_...
)
```

That is the whole integration. `model="auto"` hands model choice to Mesh's [auto-router](https://developers.meshapi.ai/docs/capabilities/auto-routing.md); pass a catalog id such as `openai/gpt-4o-mini` to pin one.

## Tool calling

Define tools with the `@tool` decorator — LangChain derives the JSON schema from the signature and docstring, and emits the standard `tools` payload that Mesh forwards:

```python theme={null}
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool

BOOK = {"A-1042": {"state": "in transit", "eta_days": 2}}


@tool
def get_shipment_status(order_id: str) -> dict:
    """Look up the shipment status of an order."""
    if order_id in BOOK:
        return {"status": "success", "order_id": order_id, **BOOK[order_id]}
    return {"status": "error", "error_message": f"No order {order_id}."}


llm_with_tools = llm.bind_tools([get_shipment_status])

messages = [HumanMessage("What's the shipment status of order A-1042?")]
first = llm_with_tools.invoke(messages)

messages.append(first)
for call in first.tool_calls:
    result = get_shipment_status.invoke(call["args"])
    messages.append(ToolMessage(content=str(result), tool_call_id=call["id"]))

final = llm_with_tools.invoke(messages)
print(final.content)
```

> [!NOTE]
>
> The model Mesh served is on the response: `first.response_metadata["model_name"]`. With `model="auto"` this is how you see which model handled the request.

## LangGraph

LangGraph takes the same `llm` object, so a prebuilt ReAct agent needs nothing Mesh-specific:

```python theme={null}
from langgraph.prebuilt import create_react_agent

agent = create_react_agent(llm, tools=[get_shipment_status])

result = agent.invoke({"messages": [("user", "Status of order A-1042?")]})
print(result["messages"][-1].content)
```

> [!WARNING]
>
> Pass **callable** tools to `create_react_agent`, not a raw JSON tool schema. A bare schema dict is accepted without error, but the graph has no function to execute, so the run finishes with empty content instead of an answer. A `@tool`-decorated function works; a plain dict silently does not.

## Caveats

Everything in [Caveats that apply to every framework](https://developers.meshapi.ai/agent/frameworks.md#caveats-that-apply-to-every-framework) applies here — in particular, pin a non-thinking model if you depend on multi-turn tool reasoning.

## Related

* [Agent frameworks](https://developers.meshapi.ai/agent/frameworks.md) — the shared pattern and compatibility matrix
* [Auto-routing](https://developers.meshapi.ai/docs/capabilities/auto-routing.md) — how `auto` picks a model
* [Streaming](https://developers.meshapi.ai/sdk/streaming.md) — streaming behaviour on Mesh


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.