Docs
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
CodeRabbit
Cloudflare
AG Grid
SerpAPI
Netlify
OpenRouter
Neon
WorkOS
Clerk
Electric
PowerSync
Sentry
Railway
Prisma
Strapi
Unkey
Persistence (Advanced)
Embeddings
Reranking
Evaluate
Sandboxes (Advanced)
Class References
Function References
Interface References
Type Alias References
Variable References
Adapters

Mistral

The Mistral adapter provides access to Mistral's chat models, including Mistral Large, the multimodal Pixtral family, the Magistral reasoning models, and the Codestral code-specialized model.

Installation

sh
npm i @tanstack/ai-mistral
npm i @tanstack/ai-mistral

Basic Usage

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "Hello!" }],
});
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "Hello!" }],
});

Basic Usage - Custom API Key

typescript
import { chat } from "@tanstack/ai";
import { createMistralText } from "@tanstack/ai-mistral";

const adapter = createMistralText(
  "mistral-large-latest",
  process.env.MISTRAL_API_KEY!,
);

const stream = chat({
  adapter,
  messages: [{ role: "user", content: "Hello!" }],
});
import { chat } from "@tanstack/ai";
import { createMistralText } from "@tanstack/ai-mistral";

const adapter = createMistralText(
  "mistral-large-latest",
  process.env.MISTRAL_API_KEY!,
);

const stream = chat({
  adapter,
  messages: [{ role: "user", content: "Hello!" }],
});

Configuration

typescript
import {
  createMistralText,
  type MistralTextConfig,
} from "@tanstack/ai-mistral";

const config: Omit<MistralTextConfig, "apiKey"> = {
  baseURL: "https://api.mistral.ai", // Optional, this is the default
  defaultHeaders: {
    "X-Custom-Header": "value",
  },
};

const adapter = createMistralText(
  "mistral-large-latest",
  process.env.MISTRAL_API_KEY!,
  config,
);
import {
  createMistralText,
  type MistralTextConfig,
} from "@tanstack/ai-mistral";

const config: Omit<MistralTextConfig, "apiKey"> = {
  baseURL: "https://api.mistral.ai", // Optional, this is the default
  defaultHeaders: {
    "X-Custom-Header": "value",
  },
};

const adapter = createMistralText(
  "mistral-large-latest",
  process.env.MISTRAL_API_KEY!,
  config,
);

Behind a proxy

Route every request through a gateway, such as Cloudflare AI Gateway or a corporate proxy, with baseURL and defaultHeaders. These two option names are the same on every TanStack AI adapter, so one gateway config works for all of them.

typescript
import { createMistralText } from "@tanstack/ai-mistral";

const adapter = createMistralText("mistral-large-latest", process.env.MISTRAL_API_KEY!, {
  baseURL: "https://gateway.example.com/mistral",
  defaultHeaders: { "cf-aig-authorization": `Bearer ${process.env.GATEWAY_TOKEN}` },
});
import { createMistralText } from "@tanstack/ai-mistral";

const adapter = createMistralText("mistral-large-latest", process.env.MISTRAL_API_KEY!, {
  baseURL: "https://gateway.example.com/mistral",
  defaultHeaders: { "cf-aig-authorization": `Bearer ${process.env.GATEWAY_TOKEN}` },
});

serverURL is an alias of baseURL. If you set both, baseURL wins.

Need more than a fixed header, such as a log of each model call? This adapter supports wrapFetch.

Mistral on Vertex

Use @tanstack/ai-mistral/vertex when Mistral must run on Vertex AI. That path uses Google Cloud credentials and the publisher rawPredict endpoint.

Mistral on Vertex is regional only. Use us-central1 or europe-west4.

sh
npm i @tanstack/ai-mistral google-auth-library
npm i @tanstack/ai-mistral google-auth-library
typescript
import { chat } from "@tanstack/ai";
import { mistralVertexText } from "@tanstack/ai-mistral/vertex";

const stream = chat({
  adapter: mistralVertexText("mistral-medium-3", {
    project: "my-project",
    location: "europe-west4",
  }),
  messages: [{ role: "user", content: "Hello!" }],
});
import { chat } from "@tanstack/ai";
import { mistralVertexText } from "@tanstack/ai-mistral/vertex";

const stream = chat({
  adapter: mistralVertexText("mistral-medium-3", {
    project: "my-project",
    location: "europe-west4",
  }),
  messages: [{ role: "user", content: "Hello!" }],
});

project and location use the same names as @tanstack/ai-vertex. location is required.

mistralVertexText accepts only the Mistral chat models that Vertex lists:

  • mistral-medium-3
  • mistral-small-2503
  • codestral-2

Mistral API aliases such as mistral-large-latest and mistral-medium-latest are not Vertex model ids. Vertex also lists mistral-ocr-2505, but that model is OCR, not chat.

Install google-auth-library for Application Default Credentials, or pass authClient or getAccessToken.

Gemini on Vertex lives in @tanstack/ai-vertex.

Example: Chat Completion

typescript
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

export async function POST(request: Request) {
  const { messages } = await request.json();

  const stream = chat({
    adapter: mistralText("mistral-large-latest"),
    messages,
  });

  return toServerSentEventsResponse(stream);
}
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

export async function POST(request: Request) {
  const { messages } = await request.json();

  const stream = chat({
    adapter: mistralText("mistral-large-latest"),
    messages,
  });

  return toServerSentEventsResponse(stream);
}

Example: With Tools

typescript
import { chat, toolDefinition } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";
import { z } from "zod";

const getWeatherDef = toolDefinition({
  name: "get_weather",
  description: "Get the current weather for a location",
  inputSchema: z.object({
    location: z.string(),
  }),
});

const getWeather = getWeatherDef.server(async ({ location }) => {
  return { temperature: 72, conditions: "sunny" };
});

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "What's the weather in Paris?" }],
  tools: [getWeather],
});
import { chat, toolDefinition } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";
import { z } from "zod";

const getWeatherDef = toolDefinition({
  name: "get_weather",
  description: "Get the current weather for a location",
  inputSchema: z.object({
    location: z.string(),
  }),
});

const getWeather = getWeatherDef.server(async ({ location }) => {
  return { temperature: 72, conditions: "sunny" };
});

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "What's the weather in Paris?" }],
  tools: [getWeather],
});

Example: Multimodal (Vision)

Use a vision-capable model — pixtral-large-latest, pixtral-12b-2409, mistral-medium-latest, or mistral-small-latest — to send images alongside text:

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("pixtral-large-latest"),
  messages: [
    {
      role: "user",
      content: [
        { type: "text", content: "What's in this image?" },
        {
          type: "image",
          source: {
            type: "url",
            value: "https://example.com/photo.jpg",
          },
        },
      ],
    },
  ],
});
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("pixtral-large-latest"),
  messages: [
    {
      role: "user",
      content: [
        { type: "text", content: "What's in this image?" },
        {
          type: "image",
          source: {
            type: "url",
            value: "https://example.com/photo.jpg",
          },
        },
      ],
    },
  ],
});

For data-URL or base64 images, set source.type to "data" and provide mimeType:

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const base64String = "..."; // your base64-encoded image bytes

const stream = chat({
  adapter: mistralText("pixtral-large-latest"),
  messages: [
    {
      role: "user",
      content: [
        { type: "text", content: "What's in this image?" },
        {
          type: "image",
          source: {
            type: "data",
            mimeType: "image/png",
            value: base64String,
          },
        },
      ],
    },
  ],
});
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const base64String = "..."; // your base64-encoded image bytes

const stream = chat({
  adapter: mistralText("pixtral-large-latest"),
  messages: [
    {
      role: "user",
      content: [
        { type: "text", content: "What's in this image?" },
        {
          type: "image",
          source: {
            type: "data",
            mimeType: "image/png",
            value: base64String,
          },
        },
      ],
    },
  ],
});

See Multimodal Content for the full content-part shape.

Example: Reasoning (Magistral)

Magistral models (magistral-medium-latest, magistral-small-latest) stream their reasoning as separate events before the final answer. The adapter emits AG-UI REASONING_* chunks for the thinking content and TEXT_MESSAGE_* chunks for the answer:

typescript
// ignore: narrowing the raw AG-UI stream by `chunk.type` relies on @ag-ui/core's
// discriminated-union `type` field, which kiira can't resolve in a source-only
// check. The runtime behaviour is exactly as shown.
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("magistral-medium-latest"),
  messages: [{ role: "user", content: "Why is the sky blue?" }],
});

for await (const chunk of stream) {
  if (chunk.type === "REASONING_MESSAGE_CONTENT") {
    process.stdout.write(`[thinking] ${chunk.delta}`);
  } else if (chunk.type === "TEXT_MESSAGE_CONTENT") {
    process.stdout.write(chunk.delta);
  }
}
// ignore: narrowing the raw AG-UI stream by `chunk.type` relies on @ag-ui/core's
// discriminated-union `type` field, which kiira can't resolve in a source-only
// check. The runtime behaviour is exactly as shown.
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("magistral-medium-latest"),
  messages: [{ role: "user", content: "Why is the sky blue?" }],
});

for await (const chunk of stream) {
  if (chunk.type === "REASONING_MESSAGE_CONTENT") {
    process.stdout.write(`[thinking] ${chunk.delta}`);
  } else if (chunk.type === "TEXT_MESSAGE_CONTENT") {
    process.stdout.write(chunk.delta);
  }
}

Reasoning events are always closed before any text or tool output begins, so consumers see a complete REASONING_START → REASONING_MESSAGE_START → REASONING_MESSAGE_CONTENT* → REASONING_MESSAGE_END → REASONING_END sequence first.

See Thinking & Reasoning for the cross-provider event spec.

Reasoning

Set reasoning on chat() to turn thinking up, down, or off:

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-medium-latest"),
  messages: [{ role: "user", content: "Why is the sky blue?" }],
  reasoning: "high",
});
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-medium-latest"),
  messages: [{ role: "user", content: "Why is the sky blue?" }],
  reasoning: "high",
});

What goes on the wire:

  • mistral-medium-latest, mistral-small-latest: reasoning_effort. They take off (sent as none) and high.
  • magistral-medium-latest, magistral-small-latest: they take off and high. high sends prompt_mode: "reasoning". off sends nothing.

The other models take no reasoning.

Example: Structured Output

Generate JSON that conforms to a Zod schema using Mistral's json_schema response format:

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";
import { z } from "zod";

const recipeSchema = z.object({
  name: z.string(),
  ingredients: z.array(z.string()),
  steps: z.array(z.string()),
});

const recipe = await chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [
    { role: "user", content: "Give me a chocolate chip cookie recipe." },
  ],
  outputSchema: recipeSchema,
});

console.log(recipe.name); // typed as z.infer<typeof recipeSchema>
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";
import { z } from "zod";

const recipeSchema = z.object({
  name: z.string(),
  ingredients: z.array(z.string()),
  steps: z.array(z.string()),
});

const recipe = await chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [
    { role: "user", content: "Give me a chocolate chip cookie recipe." },
  ],
  outputSchema: recipeSchema,
});

console.log(recipe.name); // typed as z.infer<typeof recipeSchema>

See Structured Outputs for the full guide.

Model Options

Mistral exposes provider-specific options via modelOptions:

typescript
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "Hello!" }],
  modelOptions: {
    temperature: 0.7,
    top_p: 0.9,
    max_tokens: 1024,
    random_seed: 42,
    stop: ["END"],
    safe_prompt: true,
    frequency_penalty: 0.5,
    presence_penalty: 0.5,
    parallel_tool_calls: true,
    tool_choice: "auto",
  },
});
import { chat } from "@tanstack/ai";
import { mistralText } from "@tanstack/ai-mistral";

const stream = chat({
  adapter: mistralText("mistral-large-latest"),
  messages: [{ role: "user", content: "Hello!" }],
  modelOptions: {
    temperature: 0.7,
    top_p: 0.9,
    max_tokens: 1024,
    random_seed: 42,
    stop: ["END"],
    safe_prompt: true,
    frequency_penalty: 0.5,
    presence_penalty: 0.5,
    parallel_tool_calls: true,
    tool_choice: "auto",
  },
});

All sampling parameters — including temperature, top_p, and max_tokens — go inside modelOptions using Mistral's native (snake_case) names.

Prompt caching

Mistral caches the start of your requests by itself. chat() sends prompt_cache_key by default. The key is the threadId that you pass, so one conversation shares a cache. The key goes only to api.mistral.ai, on chat() and on structured output. With promptCache: 'none', no key goes out. See Prompt Caching.

Cache reads show up in usage.promptTokensDetails.cachedTokens.

Embeddings

Generate embedding vectors with mistral-embed or codestral-embed:

typescript
import { embed } from "@tanstack/ai";
import { mistralEmbedding } from "@tanstack/ai-mistral";

const result = await embed({
  adapter: mistralEmbedding("mistral-embed"),
  input: ["a red guitar", "a blue drum kit"],
});

console.log(result.embeddings[0]?.vector); // 1024 dimensions
import { embed } from "@tanstack/ai";
import { mistralEmbedding } from "@tanstack/ai-mistral";

const result = await embed({
  adapter: mistralEmbedding("mistral-embed"),
  input: ["a red guitar", "a blue drum kit"],
});

console.log(result.embeddings[0]?.vector); // 1024 dimensions

mistral-embed has fixed 1024-dimension output; codestral-embed (tuned for code) supports the top-level dimensions option:

typescript
import { embed } from "@tanstack/ai";
import { mistralEmbedding } from "@tanstack/ai-mistral";

const result = await embed({
  adapter: mistralEmbedding("codestral-embed"),
  input: "function add(a, b) { return a + b }",
  dimensions: 512,
});
import { embed } from "@tanstack/ai";
import { mistralEmbedding } from "@tanstack/ai-mistral";

const result = await embed({
  adapter: mistralEmbedding("codestral-embed"),
  input: "function add(a, b) { return a + b }",
  dimensions: 512,
});

See the Embeddings guide for the full API.

Environment Variables

Set your API key in environment variables:

sh
MISTRAL_API_KEY=...
MISTRAL_API_KEY=...

Get a key from the Mistral Console.

Supported Models

Chat

  • mistral-large-latest — Flagship general-purpose model (128k context)
  • mistral-medium-latest — Multimodal mid-tier model with vision
  • mistral-small-latest — Fast, affordable multimodal model with vision
  • ministral-8b-latest — 8B edge model
  • ministral-3b-latest — 3B edge model
  • open-mistral-nemo — Open 12B model

Code

  • codestral-latest — Code-specialized model (256k context)

Vision

  • pixtral-large-latest — Large vision model
  • pixtral-12b-2409 — 12B vision model

Reasoning

Reasoning content is streamed as REASONING_* events before the final answer.

  • magistral-medium-latest — Mid-tier reasoning model
  • magistral-small-latest — Small reasoning model

See Mistral's model comparison for full details.

API Reference

mistralText(model, config?)

Creates a Mistral text adapter using the MISTRAL_API_KEY environment variable.

adapter.inputModalities lists the input kinds of the selected model. It is undefined for a model that this package does not know. See Check what a model accepts.

Parameters:

  • model — The model name (e.g., 'mistral-large-latest')
  • config.baseURL?: custom base URL (optional). serverURL is an alias.
  • config.defaultHeaders? — Headers to attach to every request (optional)

Returns: A Mistral text adapter instance.

createMistralText(model, apiKey, config?)

Creates a Mistral text adapter with an explicit API key.

Parameters:

  • model — The model name
  • apiKey — Your Mistral API key
  • config.baseURL?: custom base URL (optional). serverURL is an alias.
  • config.defaultHeaders? — Headers to attach to every request (optional)

Returns: A Mistral text adapter instance.

Limitations

  • Embeddings: Use the Mistral SDK directly for mistral-embed.
  • Image / Audio / Video Generation: Mistral does not provide these endpoints. Use OpenAI, Gemini, or fal.ai.
  • Text-to-Speech / Transcription: Not supported. Use OpenAI or ElevenLabs.

Next Steps

Provider Tools

Mistral does not currently expose provider-specific tool factories. Define your own tools with toolDefinition() from @tanstack/ai.

See Tools for the general tool-definition flow, or Provider Tools for other providers' native-tool offerings.