Persistent memory for AI agents. Your agent remembers users and teams across conversations — personal preferences, shared knowledge, and context — automatically.
npm install @grillo-ai/grillo-aiThen install the packages you need:
# Pick an LLM provider (at least one required)
npm install openai # OpenAI
npm install @anthropic-ai/sdk # Anthropic
npm install ollama # Ollama (local)
# Pick a storage backend (optional — InMemoryBackend is built-in)
npm install better-sqlite3 # SQLite (persistent local storage)
npm install pg # PostgreSQL (production)
# Pick a framework integration (optional)
npm install @openai/agents zod # OpenAI Agents SDK
npm install @google/adk zod # Google ADK
npm install @langchain/core zod # LangGraph / LangChainimport { Grillo, InMemoryBackend, OpenAIProvider } from "@grillo-ai/grillo-ai";
const grillo = new Grillo({
storage: new InMemoryBackend(),
llm: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }),
});
// Store memories — the LLM extracts facts automatically
await grillo.addMemories(
{ userId: "alice" },
"Hi, I'm Alice. I'm a TypeScript developer and I prefer dark mode.",
);
// Retrieve memories
const { personal } = await grillo.getMemories({ userId: "alice" });
console.log(personal?.content);
// => "- Name: Alice\n- TypeScript developer\n- Prefers dark mode"Memories are stored as versioned markdown documents. The LLM decides what to extract, how to organize it, and automatically handles updates and contradictions.
Conversation
↓
Consciousness Router (LLM)
↓
┌──────────────────────┐
│ Personal memories │ ← user preferences, individual history
│ (scoped to userId) │
├──────────────────────┤
│ Group memories │ ← team norms, shared knowledge
│ (scoped to groupId) │
└──────────────────────┘
↓
Storage Backend (SQLite, PostgreSQL, in-memory, or custom)
When you call addMemories, GrilloAI:
- Loads the current personal and group memory documents
- Asks the LLM to update them with any new facts from the conversation
- Saves the updated documents as new revisions (with optimistic locking for concurrency)
For single-user contexts (chat apps, CLI tools), use .for() to bind a scope:
const memory = grillo.for({ userId: "alice", groupId: "team-1" });
await memory.addMemories("I'm working on GrilloAI.");
const { personal, group } = await memory.getMemories();Pass the full conversation for better memory extraction. GrilloAI accepts result.history from the OpenAI Agents SDK (or any array of items with role and content) — no transformation needed:
// OpenAI Agents SDK — pass result.history directly
const result = await run(agent, "I just got married to Emily!");
await memory.addMemories(result.history);You can also pass a simple array of messages or a plain string:
// Array of messages
await memory.addMemories([
{ role: "user", content: "I just got married to Emily!" },
{ role: "assistant", content: "Congratulations!" },
{ role: "user", content: "Her birthday is May 9th" },
]);
// Simple string
await memory.addMemories("My name is Alice and I prefer dark mode.");Every save creates a new revision. You can read or restore any previous state:
// What did we know last week?
const { personal } = await grillo.getMemoriesAt(
{ userId: "alice" },
new Date("2025-03-01"),
);
// Restore a single user's memories to that point
await grillo.reset({ userId: "alice" }, new Date("2025-03-01"));
// Restore both personal and group memories
await grillo.reset(
{ userId: "alice", groupId: "team-1" },
new Date("2025-03-01"),
);reset() restores each scope in the given object independently. If you pass both userId and groupId, both the personal and group documents are restored to the revision that existed at that timestamp.
Each integration gives you a ready-to-use tool and a recommended system prompt:
npm install @openai/agents zodimport { Grillo, OpenAIProvider } from "@grillo-ai/grillo-ai";
import { SQLiteBackend } from "@grillo-ai/grillo-ai/backends/sqlite";
import {
createOpenAIMemoryTool,
RECOMMENDED_PROMPT_PREFIX,
} from "@grillo-ai/grillo-ai/integrations/openai-agents";
import { Agent, run } from "@openai/agents";
const grillo = new Grillo({
storage: new SQLiteBackend({ dbPath: "./memory.db" }),
llm: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }),
});
const memoryTool = await createOpenAIMemoryTool(
grillo.for({ userId: "alice" }),
);
const agent = new Agent({
name: "Assistant",
instructions: RECOMMENDED_PROMPT_PREFIX + "\n\nYou are a helpful assistant.",
tools: [memoryTool],
});
const result = await run(agent, "Hi, I'm Alice! I prefer dark mode.");npm install @anthropic-ai/sdkimport { Grillo, AnthropicProvider } from "@grillo-ai/grillo-ai";
import { SQLiteBackend } from "@grillo-ai/grillo-ai/backends/sqlite";
import {
createAnthropicMemoryTool,
RECOMMENDED_PROMPT_PREFIX,
} from "@grillo-ai/grillo-ai/integrations/anthropic";
import Anthropic from "@anthropic-ai/sdk";
const grillo = new Grillo({
storage: new SQLiteBackend({ dbPath: "./memory.db" }),
llm: new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }),
});
const memoryTool = createAnthropicMemoryTool(grillo.for({ userId: "alice" }));
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-haiku-4-5",
max_tokens: 1024,
system: RECOMMENDED_PROMPT_PREFIX + "\n\nYou are a helpful assistant.",
tools: [memoryTool.definition],
messages: [{ role: "user", content: "Hi, I'm Alice!" }],
});
// Handle tool calls
for (const block of response.content) {
if (block.type === "tool_use" && block.name === "memory") {
const result = await memoryTool.execute(block.input);
}
}npm install @google/adk zodimport { Grillo, OpenAIProvider } from "@grillo-ai/grillo-ai";
import { SQLiteBackend } from "@grillo-ai/grillo-ai/backends/sqlite";
import {
createADKMemoryTool,
RECOMMENDED_PROMPT_PREFIX,
} from "@grillo-ai/grillo-ai/integrations/google-adk";
const grillo = new Grillo({
storage: new SQLiteBackend({ dbPath: "./memory.db" }),
llm: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }),
});
const memoryTool = await createADKMemoryTool(grillo.for({ userId: "alice" }));
// Pass to your LlmAgent's tools arraynpm install @langchain/core zodimport { Grillo, OpenAIProvider } from "@grillo-ai/grillo-ai";
import { SQLiteBackend } from "@grillo-ai/grillo-ai/backends/sqlite";
import {
createLangGraphMemoryTool,
RECOMMENDED_PROMPT_PREFIX,
} from "@grillo-ai/grillo-ai/integrations/langgraph";
const grillo = new Grillo({
storage: new SQLiteBackend({ dbPath: "./memory.db" }),
llm: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }),
});
const memoryTool = await createLangGraphMemoryTool(
grillo.for({ userId: "alice" }),
);
// Pass to your agent's tools arrayThe LLM provider powers the consciousness router — it decides what facts to extract and how to organize the memory document.
All providers accept model to override the default, and modelOptions to pass additional SDK-specific parameters directly to the API call:
import { OpenAIProvider, AnthropicProvider, OllamaProvider } from "@grillo-ai/grillo-ai";
// OpenAI (default model: gpt-5-mini)
const llm = new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY!,
});
// OpenAI with reasoning
const llm = new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY!,
model: "gpt-5.4",
modelOptions: { reasoning: { effort: "low" } },
});
// Anthropic (default model: claude-haiku-4-5)
const llm = new AnthropicProvider({
apiKey: process.env.ANTHROPIC_API_KEY!,
});
// Anthropic with extended thinking
const llm = new AnthropicProvider({
apiKey: process.env.ANTHROPIC_API_KEY!,
model: "claude-sonnet-4-5",
modelOptions: { thinking: { type: "enabled", budget_tokens: 5000 } },
});
// Ollama — fully local, no API key (model is required)
const llm = new OllamaProvider({ model: "qwen3.5" });modelOptions is spread directly into the API call, so any parameter supported by the SDK works.
GrilloAI ships with three backends. You can also build your own by implementing the StorageBackend interface (getDocument, saveDocument, getDocumentAt).
// In-memory — built-in, no extra install. Good for dev/test.
import { InMemoryBackend } from "@grillo-ai/grillo-ai";
const storage = new InMemoryBackend();
// SQLite — persistent local storage.
// npm install better-sqlite3
import { SQLiteBackend } from "@grillo-ai/grillo-ai/backends/sqlite";
const storage = new SQLiteBackend({ dbPath: "./memory.db" });
// PostgreSQL — production deployments.
// npm install pg
import { PostgresBackend } from "@grillo-ai/grillo-ai/backends/postgres";
const storage = new PostgresBackend({
connectionString: process.env.DATABASE_URL!,
});Implement the StorageBackend interface to use any database:
import type { StorageBackend } from "@grillo-ai/grillo-ai";
class MyCustomBackend implements StorageBackend {
async getDocument(scope) {
/* return latest revision or null */
}
async saveDocument(scope, content, expectedVersion?) {
/* save new revision */
}
async getDocumentAt(scope, timestamp) {
/* return revision at timestamp or null */
}
}When your scope includes a groupId, the consciousness router may decide to update shared group memories. You can intercept this with an approval callback to let the user decide:
// Express.js example — show the user a confirmation dialog
const grillo = new Grillo({
storage,
llm,
onGroupMemoryUpdate: async (proposedContent) => {
// `proposedContent` is the full updated group memory document.
// Return true to save it, false to discard.
// How you ask for approval depends on your app:
// CLI app:
console.log("Proposed group memory:\n" + proposedContent);
return await askYesNo("Save to group memory?");
// Web app (via WebSocket, SSE, etc.):
return await sendApprovalRequest(userId, proposedContent);
// Auto-approve (default behavior when callback is not set):
return true;
},
});When onGroupMemoryUpdate is set:
- Personal memories are always saved immediately — no approval needed
- Group memories are passed to your callback first — only saved if you return
true
When onGroupMemoryUpdate is not set, group memories are saved automatically (no approval step).
const grillo = new Grillo({ storage, llm, onGroupMemoryUpdate? });| Parameter | Type | Description |
|---|---|---|
storage |
StorageBackend |
Where to store memory documents (InMemoryBackend, SQLiteBackend, PostgresBackend, or custom) |
llm |
LLMProvider |
LLM for the consciousness router (OpenAIProvider, AnthropicProvider, OllamaProvider) |
onGroupMemoryUpdate |
(content: string) => Promise<boolean> |
Optional. Called before saving group memories. Return true to save, false to discard. |
// With explicit scope
grillo.getMemories(scope); // → { personal: MemoryDocument | null, group: MemoryDocument | null }
grillo.addMemories(scope, input); // input: string or { role, content }[]
grillo.getMemoriesAt(scope, timestamp); // → memories at that point in time
grillo.reset(scope, timestamp); // restore to a previous revision
// With a scoped handle
const memory = grillo.for(scope);
memory.getMemories();
memory.addMemories(input);
memory.getMemoriesAt(timestamp);
memory.reset(timestamp);// Scope — identifies whose memory this is
{ orgId?: string, userId?: string, groupId?: string }
// MemoryDocument — a versioned memory document
{ content: string, version: number, updatedAt: Date }
// Memories — returned by getMemories()
{ personal: MemoryDocument | null, group: MemoryDocument | null }See the examples/ directory for working chat applications.
See CONTRIBUTING.md for development setup and release process.
MIT
