Skip to main content

LangGraph

LangGraph agents connect to the gateway through @langchain/openai's ChatOpenAI class. Point it at the gateway base URL and all LLM calls are proxied with automatic memory.

Before you begin​

Complete the LangGraph JS quickstart first. It covers installation and the base project setup for LangGraph.

Configure the model​

Create a ChatOpenAI instance with your gateway API key and base URL.

TypeScriptsrc/agent.ts
import { ChatOpenAI } from "@langchain/openai";

export const model = new ChatOpenAI({
  model: "google/gemini-2.5-flash",
  configuration: {
    apiKey: "YOUR_API_KEY",
    baseURL: "https://gateway-api.mastra.ai/v1",
  },
});

All subsequent examples import this model instance from ./agent.

Chat completions​

Send a basic message through the model.

TypeScriptsrc/chat.ts
import { model } from "./agent";

const response = await model.invoke([
  { role: "user", content: "What is 2+2? Reply with just the number." },
]);

console.log(response.content);
// "4"

System messages​

Add a system message at the start of the messages array to define the model's behavior.

const response = await model.invoke([
  { role: "system", content: "You are a calculator. Only respond with numbers, no words." },
  { role: "user", content: "What is 10 * 5?" },
]);

console.log(response.content);
// "50"

Memory with thread and resource IDs​

Pass x-thread-id and x-resource-id as default headers to enable observational memory. The gateway stores observations per thread and injects them as context on subsequent requests.

TypeScriptsrc/agent.ts
import { ChatOpenAI } from "@langchain/openai";

export const model = new ChatOpenAI({
  model: "google/gemini-2.5-flash",
  configuration: {
    apiKey: "YOUR_API_KEY",
    baseURL: "https://gateway-api.mastra.ai/v1",
    defaultHeaders: {
      "x-thread-id": "my-thread-1",
      "x-resource-id": "user-42",
    },
  },
});

// First request: introduce yourself
await model.invoke([
  { role: "user", content: "My name is Alex and I prefer concise answers." },
]);

// Second request: the gateway remembers
const response = await model.invoke([
  { role: "user", content: "What is my name?" },
]);

console.log(response.content);
// "Alex"

Tool calling​

Bind tools using the @langchain/core tool helper with Zod schemas.

TypeScriptsrc/tools.ts
import { ChatOpenAI } from "@langchain/openai";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
  async ({ location }) => {
    return `The weather in ${location} is sunny, 72°F.`;
  },
  {
    name: "get_weather",
    description: "Get the current weather for a given location",
    schema: z.object({
      location: z.string().describe("The city to get weather for"),
    }),
  },
);

const modelWithTools = new ChatOpenAI({
  model: "google/gemini-2.5-flash",
  configuration: {
    apiKey: "YOUR_API_KEY",
    baseURL: "https://gateway-api.mastra.ai/v1",
  },
}).bindTools([getWeather]);

const response = await modelWithTools.invoke([
  { role: "user", content: "What is the weather in San Francisco?" },
]);

// The response contains tool_calls when the model wants to use a tool
if (response.tool_calls && response.tool_calls.length > 0) {
  console.log(response.tool_calls[0].name);  // "get_weather"
  console.log(response.tool_calls[0].args);  // { location: "San Francisco" }
}

Full agent with StateGraph​

Combine ChatOpenAI, tools, and a StateGraph to build a ReAct-style agent that loops between the model and tool execution until the model stops calling tools.

TypeScriptsrc/agent.ts
import { StateGraph, MessagesAnnotation } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { ToolNode } from "@langchain/langgraph/prebuilt";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
  async ({ location }) => {
    return `The weather in ${location} is sunny, 72°F.`;
  },
  {
    name: "get_weather",
    description: "Get the current weather for a given location",
    schema: z.object({
      location: z.string().describe("The city to get weather for"),
    }),
  },
);

const tools = [getWeather];
const toolNode = new ToolNode(tools);

const model = new ChatOpenAI({
  model: "google/gemini-2.5-flash",
  temperature: 0,
  configuration: {
    apiKey: "YOUR_API_KEY",
    baseURL: "https://gateway-api.mastra.ai/v1",
    defaultHeaders: {
      "x-thread-id": "weather-agent-thread",
    },
  },
}).bindTools(tools);

async function agent(state: typeof MessagesAnnotation.State) {
  const response = await model.invoke(state.messages);
  return { messages: [response] };
}

function shouldContinue(state: typeof MessagesAnnotation.State) {
  const lastMessage = state.messages[state.messages.length - 1];
  if (
    "tool_calls" in lastMessage &&
    Array.isArray(lastMessage.tool_calls) &&
    lastMessage.tool_calls.length > 0
  ) {
    return "tools";
  }
  return "__end__";
}

const workflow = new StateGraph(MessagesAnnotation)
  .addNode("agent", agent)
  .addNode("tools", toolNode)
  .addEdge("__start__", "agent")
  .addConditionalEdges("agent", shouldContinue)
  .addEdge("tools", "agent");

const graph = workflow.compile();

// Run the agent
const result = await graph.invoke({
  messages: [{ role: "user", content: "What is the weather in San Francisco?" }],
});

const reply = result.messages.at(-1);
console.log(reply?.content);
// "The weather in San Francisco is sunny, 72°F."

The agent routes __start__ → agent → tools → agent until no tool_calls remain, then exits at __end__.

  • Features: Observational memory, streaming, BYOK, and gateway tools
  • Models: Supported providers and model routing
  • API reference: Complete endpoint documentation