Vercel AI SDK
Give Vercel AI SDK agents long-term memory by retrieving FishMem facts into the prompt, or exposing search as a tool.
The @fishmem/ai-sdk adapter connects
FishMem to the Vercel AI SDK (ai package). There are two patterns: retrieve
relevant memories and inject them into the prompt before the model runs, or
expose memory search as a tool() the model can call on demand.
Install
npm install ai @fishmem/ai-sdk fishmemSet your key in the environment. Both the adapter and the fishmem client read
FISHMEM_API_KEY by default.
export FISHMEM_API_KEY="fm_..."Pattern A — Retrieve and inject
Search FishMem before the turn, prepend the results to the system prompt, then write the exchange back afterward. This keeps the model grounded in what it already knows about the user without any tool round-trips.
Recall before the turn
import { FishMem } from "fishmem";
const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });
export async function recall(query: string, userId: string) {
const { results } = await memory.search(query, { user_id: userId, top_k: 5 });
if (results.length === 0) return "";
const facts = results.map((m) => `- ${m.memory}`).join("\n");
return `What you know about this user:\n${facts}`;
}Inject and generate
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import { FishMem } from "fishmem";
import { recall } from "./recall";
const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });
export async function chat(userId: string, prompt: string) {
const context = await recall(prompt, userId);
const { text } = await generateText({
model: openai("gpt-4o"),
system: `You are a helpful assistant.\n\n${context}`,
prompt,
});
// Persist the exchange so the next turn can recall it.
await memory.add(
[
{ role: "user", content: prompt },
{ role: "assistant", content: text },
],
{ user_id: userId },
);
return text;
}The same pattern works with streamText — call recall before streaming, and
memory.add once the stream completes.
Pattern B — Memory as a tool
Instead of injecting upfront, give the model a searchMemory tool so it can
pull memories only when it decides they're relevant. This costs fewer recall
calls on turns that don't need memory, at the price of an extra round-trip when
they do.
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { FishMem } from "fishmem";
import { z } from "zod";
const memory = new FishMem({ apiKey: process.env.FISHMEM_API_KEY });
export async function chatWithTool(userId: string, prompt: string) {
const searchMemory = tool({
description:
"Search the user's long-term memory for relevant facts before answering.",
parameters: z.object({
query: z.string().describe("What to look up in memory."),
}),
execute: async ({ query }) => {
const { results } = await memory.search(query, {
user_id: userId,
top_k: 5,
});
return results.map((m) => m.memory);
},
});
const { text } = await generateText({
model: openai("gpt-4o"),
system: "Use searchMemory when the user references past context.",
prompt,
tools: { searchMemory },
maxSteps: 4,
});
await memory.add(
[
{ role: "user", content: prompt },
{ role: "assistant", content: text },
],
{ user_id: userId },
);
return text;
}Always pass user_id (and agent_id / run_id where relevant) so memories
stay isolated per user. See scoping.