> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/otari/for-integrators/use-with-langgraph.md).

# Use with LangGraph

LangGraph calls models through LangChain's chat models, so `ChatOtari` from `langchain-otari` is all it takes (see [Use with LangChain](/otari/for-integrators/use-with-langchain.md)): every model call in every node of your graph then gets Otari's budgets, usage tracking, fallback routing and guardrails.

## Quick start

Install LangGraph and the package:

```bash
pip install langgraph langchain-otari
```

Create an API key at [otari.ai](https://otari.ai) and export it:

```bash
export OTARI_API_KEY=<your-otari-key>
```

That is all the configuration otari.ai's gateway needs, once your organization there has credit or provider keys of its own; without either, it answers 402. For a gateway you run yourself, use a key it issued and also export its API root:

```bash
export OTARI_BASE_URL=http://localhost:8000/api/v1   # your gateway's URL plus /api/v1
```

## A tool-calling graph

A model node and a tool node, looping until the model stops calling tools:

```python
from langchain_core.tools import tool
from langchain_otari import ChatOtari
from langgraph.graph import START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition


@tool
def get_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"Sunny and 21 C in {city}"


llm = ChatOtari(model="openai:gpt-4o").bind_tools([get_weather])


def call_model(state: MessagesState) -> dict:
    return {"messages": [llm.invoke(state["messages"])]}


graph = StateGraph(MessagesState)
graph.add_node("model", call_model)
graph.add_node("tools", ToolNode([get_weather]))
graph.add_edge(START, "model")
graph.add_conditional_edges("model", tools_condition)
graph.add_edge("tools", "model")
app = graph.compile()

result = app.invoke({"messages": [{"role": "user", "content": "What is the weather in Paris?"}]})
print(result["messages"][-1].content)
```

## Streaming tokens

`stream_mode="messages"` streams the model's tokens out of the graph as Otari streams them in:

```python
from langchain_otari import ChatOtari
from langgraph.graph import START, MessagesState, StateGraph

llm = ChatOtari(model="openai:gpt-4o")


def call_model(state: MessagesState) -> dict:
    return {"messages": [llm.invoke(state["messages"])]}


graph = StateGraph(MessagesState)
graph.add_node("model", call_model)
graph.add_edge(START, "model")
app = graph.compile()

for chunk, _metadata in app.stream(
    {"messages": [{"role": "user", "content": "Say hello in one sentence."}]},
    stream_mode="messages",
):
    print(chunk.content, end="", flush=True)
print()
```

## Different models per node

Each node can use its own model through the same gateway, for example a cheap model to route and a stronger one to answer. Otari tracks the spend of both under the same key, and a routing policy name works anywhere a model name does; see [Choosing a model](/otari/for-integrators/use-with-pydantic-ai.md#choosing-a-model).

## See also

* [Use with LangChain](/otari/for-integrators/use-with-langchain.md): agents, structured output and streaming with `ChatOtari`.
* [Use with Pydantic AI](/otari/for-integrators/use-with-pydantic-ai.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/otari/for-integrators/use-with-langgraph.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
