Skip to main content
A session is a container for persistent conversation state. Where a plain ai.generate() call is stateless, a session automatically accumulates conversation history across multiple send() calls—you never need to manually pass messages back and forth. Sessions also support multiple independent threads within a single session context, so you can run parallel conversations (e.g., a main conversation thread and a side-channel for tool confirmations) under one session ID.

Creating a session and chatting

import { genkit } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';

const ai = genkit({
  plugins: [googleAI()],
  model: 'googleai/gemini-2.5-flash',
});

// Create a session
const session = ai.createSession();

// Open a chat thread within the session
const chat = session.chat();

// First turn
let response = await chat.send('Hi! My name is Alex.');
console.log(response.text); // → "Hi Alex! Nice to meet you!"

// Second turn — the model remembers earlier context
response = await chat.send('What is my name?');
console.log(response.text); // → "Your name is Alex."

// Inspect the full history at any time
console.log(chat.messages);

Sessions vs. plain generate()

ai.generate()Session + chat
StateStateless — you manage historyStateful — history managed automatically
History passingMust pass messages manuallyAccumulated inside the Chat object
Best forSingle-turn, one-shot tasksMulti-turn conversations
PersistenceNone built-inPluggable via SessionStore

System prompts in sessions

Set a system prompt (persona, instructions, constraints) when creating the chat. It is injected automatically at the start of every request:
const chat = session.chat({
  system: 'You are a friendly pirate. Always respond in pirate speak.',
});

const response = await chat.send('Tell me a joke.');
// → "Arrr, why don't pirates shower before they walk the plank? Because..."

Multi-thread sessions

A session can hold multiple independent conversation threads. Each thread has its own message history:
const session = ai.createSession();

// Two separate threads — each with its own persona and history
const techChat = session.chat('techThread', {
  system: 'You are a senior software engineer. Speak technically.',
});
const simpleChat = session.chat('simpleThread', {
  system: 'You explain things to a five-year-old.',
});

await techChat.send('What is a mutex?');
await simpleChat.send('What is a mutex?');
// Each thread answers with its own persona and maintains separate histories

Prompts as preambles

You can initialize a chat thread with a Dotprompt, which acts as a reusable preamble:
const triageAgent = ai.definePrompt({
  name: 'triageAgent',
  model: 'googleai/gemini-2.5-flash',
  system: 'You are a customer support triage agent. '
    + 'Classify the user\'s issue and collect relevant details.',
});

const session = ai.createSession();
const chat = session.chat(triageAgent);

const response = await chat.send('My internet connection keeps dropping.');

Streaming chat

Use chat.sendStream() to stream the response token-by-token:
const { response, stream } = chat.sendStream('Tell me a long story about a robot.');

for await (const chunk of stream) {
  process.stdout.write(chunk.text);
}

const finalResponse = await response;
console.log('\nDone. Total tokens:', finalResponse.usage?.totalTokens);

Persisting sessions

By default, session state is kept in memory and lost when the process restarts. For production multi-user applications, plug in a persistent store:
import { firestoreSessionStore } from '@genkit-ai/firebase';

const session = ai.createSession({
  sessionId: req.headers['x-session-id'], // load an existing session
  store: firestoreSessionStore(),          // persists to Firestore
});
You can also write your own store by implementing the SessionStore interface:
const redisStore: SessionStore = {
  async get(sessionId) {
    const raw = await redis.get(`session:${sessionId}`);
    return raw ? JSON.parse(raw) : undefined;
  },
  async save(sessionId, data) {
    await redis.set(`session:${sessionId}`, JSON.stringify(data));
  },
};

const session = ai.createSession({ store: redisStore });

Session state

Sessions can carry arbitrary typed state alongside conversation history. This is useful for tracking things like a user’s preferences, shopping cart, or workflow step:
interface UserState {
  preferredLanguage: string;
  creditBalance: number;
}

const session = ai.createSession<UserState>({
  initialState: { preferredLanguage: 'en', creditBalance: 100 },
});

// Read state anywhere inside the session
console.log(session.state?.creditBalance); // 100

// Update state
await session.updateState({ preferredLanguage: 'fr', creditBalance: 90 });

Reading message history

The full conversation history is available on the chat.messages property. Each entry is a MessageData object with role and content:
const chat = session.chat();
await chat.send('Hello!');
await chat.send('Tell me about Genkit.');

for (const msg of chat.messages) {
  console.log(`[${msg.role}]`, msg.content.map((p) => p.text).join(''));
}
// [user]  Hello!
// [model] Hi there! How can I help you today?
// [user]  Tell me about Genkit.
// [model] Genkit is an open-source AI framework...
Message history includes both user messages and model responses. Tool call messages are also recorded in the history when tools are used.

Next steps

Tools

Tools work seamlessly inside chat sessions.

Streaming

Stream responses in real time with sendStream().

Firebase Deployment

Deploy session-aware flows to Firebase.

Agents

Build persistent agents that maintain state across turns.