recallrai
v0.6.6
Published
Official Node.js SDK for RecallrAI - Revolutionary contextual memory system that enables AI assistants to form meaningful connections between conversations, just like human memory.
Maintainers
Readme
RecallrAI Node.js SDK
Official Node.js SDK for RecallrAI – a revolutionary contextual memory system that enables AI assistants to form meaningful connections between conversations, just like human memory.
Note: All datetime objects returned by the SDK are JavaScript Date objects in UTC timezone.
Installation
Install the SDK via npm or yarn:
npm install recallrai
# or
yarn add recallraiInitialization
Create a client instance with your API key and project ID:
import { RecallrAI } from "recallrai";
const client = new RecallrAI({
apiKey: "rai_yourapikey",
projectId: "project-uuid",
baseUrl: "https://api.recallrai.com", // custom endpoint if applicable
timeout: 60, // seconds
});User Management
Create a User
import { UserAlreadyExistsError } from "recallrai";
try {
const user = await client.createUser("user123", { name: "John Doe" });
console.log(`Created user: ${user.userId}`);
console.log(`User metadata:`, user.metadata);
console.log(`Created at: ${user.createdAt}`);
} catch (error) {
if (error instanceof UserAlreadyExistsError) {
console.log(`Error: ${error.message}`);
}
}Get a User
import { UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
console.log(`User metadata:`, user.metadata);
console.log(`Last active: ${user.lastActiveAt}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}If userId is already trusted, you can skip the lookup request:
const user = await client.getUser("user123", { validate: false });List Users
const userList = await client.listUsers({
offset: 0,
limit: 10,
metadataFilter: { role: "admin" },
});
console.log(`Total users: ${userList.total}`);
console.log(`Has more users: ${userList.hasMore}`);
console.log("---");
for (const u of userList.users) {
console.log(`User ID: ${u.userId}`);
console.log(`Metadata:`, u.metadata);
console.log(`Created at: ${u.createdAt}`);
console.log(`Last active: ${u.lastActiveAt}`);
console.log("---");
}Update a User
import { UserNotFoundError, UserAlreadyExistsError } from "recallrai";
try {
const user = await client.getUser("user123");
await user.update({
newMetadata: { name: "John Doe", role: "admin" },
newUserId: "john_doe",
mergeConflictEnabled: true, // override: always raise merge conflicts for this user
});
console.log(`Updated user ID: ${user.userId}`);
console.log(`Updated metadata:`, user.metadata);
console.log(`Merge conflict enabled: ${user.mergeConflictEnabled}`);
console.log(`Last active: ${user.lastActiveAt}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof UserAlreadyExistsError) {
console.log(`Error: ${error.message}`);
}
}Refresh User Instance
import { UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("john_doe");
await user.refresh();
console.log(`Refreshed user metadata:`, user.metadata);
console.log(`Last active: ${user.lastActiveAt}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Delete a User
import { UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("john_doe");
await user.delete();
console.log("User deleted successfully");
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Session Management
Create a Session
import { UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const session = await user.createSession({
autoProcessAfterSeconds: 600,
metadata: { type: "chat" },
});
console.log("Created session id:", session.sessionId);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Get an Existing Session
import { UserNotFoundError, SessionNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const session = await user.getSession("session-uuid");
console.log("Session status:", session.status);
console.log("Session metadata:", session.metadata);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}If sessionId is trusted, you can skip this lookup request:
const session = await user.getSession("session-uuid", { validate: false });Trusted IDs - Skip Validation Lookups
import { RecallStrategy } from "recallrai";
// Skips GET /api/v1/users/{userId}
const user = await client.getUser("user123", { validate: false });
// Skips GET /api/v1/users/{userId}/sessions/{sessionId}
const session = await user.getSession("session-uuid", { validate: false });
// Goes directly to context retrieval
const context = await session.getContext({
recallStrategy: RecallStrategy.LOW_LATENCY,
});
console.log(context.context);Use this only when IDs are already trusted by your system. This optimization skips SDK pre-validation calls.
When validate: false is used, unknown reference fields are set to UNAVAILABLE until you call refresh().
Import it from recallrai when you need to check for this sentinel.
Update a Session
import { UserNotFoundError, SessionNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const session = await user.getSession("session-uuid");
await session.update({ type: "support_chat" });
console.log("Updated session metadata:", session.metadata);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Refresh a Session
import { UserNotFoundError, SessionNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const session = await user.getSession("session-uuid");
await session.refresh();
console.log("Session status:", session.status);
console.log("Refreshed session metadata:", session.metadata);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Delete a Session
import { UserNotFoundError, SessionNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const session = await user.getSession("session-uuid");
await session.delete();
console.log("Session deleted successfully");
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}List Sessions
import { SessionStatus, UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const sessionList = await user.listSessions({
offset: 0,
limit: 10,
metadataFilter: { type: "chat" },
statusFilter: [SessionStatus.PENDING, SessionStatus.PROCESSING],
});
console.log(`Total sessions: ${sessionList.total}`);
console.log(`Has more sessions: ${sessionList.hasMore}`);
for (const s of sessionList.sessions) {
console.log(s.sessionId, s.status, s.metadata);
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Session – Adding Messages
import { UserNotFoundError, SessionNotFoundError, InvalidSessionStateError, MessageRole } from "recallrai";
try {
await session.addMessage(MessageRole.USER, "Hello! How are you?");
await session.addMessage(MessageRole.ASSISTANT, "I'm an assistant. How can I help you?");
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof InvalidSessionStateError) {
console.log(`Error: ${error.message}`);
}
}Session – Retrieving Context
import { UserNotFoundError, SessionNotFoundError, RecallStrategy } from "recallrai";
try {
let context = await session.getContext();
console.log("Context:", context.context);
context = await session.getContext({
recallStrategy: RecallStrategy.LOW_LATENCY,
});
console.log("Context:", context.context);
context = await session.getContext({
recallStrategy: RecallStrategy.BALANCED,
minTopK: 10,
maxTopK: 100,
memoriesThreshold: 0.6,
summariesThreshold: 0.5,
lastNMessages: 20,
lastNSummaries: 5,
timezone: "America/Los_Angeles",
});
console.log("Context:", context.context);
context = await session.getContext({ includeMetadataIds: true });
if (context.metadata) {
console.log("Memory IDs:", context.metadata.memoryIds);
console.log("Session IDs:", context.metadata.sessionIds);
console.log("Vector Queries:", context.metadata.vectorSearchQueries);
console.log("Keywords:", context.metadata.keywords);
console.log("Summary Queries:", context.metadata.sessionSummariesSearchQueries);
console.log("Date Filters:", context.metadata.dateRangeFilters);
console.log("Agent Reasoning:", context.metadata.agentReasoning);
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Session – Streaming Context
import { UserNotFoundError, SessionNotFoundError, RecallStrategy } from "recallrai";
try {
for await (const event of session.getContextStream({
recallStrategy: RecallStrategy.BALANCED,
timezone: "America/Los_Angeles",
})) {
if (event.statusUpdateMessage) {
console.log("Status:", event.statusUpdateMessage);
}
if (event.metadata) {
console.log("Metadata:", event.metadata);
}
if (event.isFinal) {
if (event.errorMessage) {
console.log("Error:", event.errorMessage);
} else {
console.log("Final context:", event.context);
}
}
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Session – Process Session
import { UserNotFoundError, SessionNotFoundError, InvalidSessionStateError } from "recallrai";
try {
await session.process();
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof InvalidSessionStateError) {
console.log(`Error: ${error.message}`);
}
}Session – List Messages
import { UserNotFoundError, SessionNotFoundError } from "recallrai";
try {
const messages = await session.getMessages(0, 50);
for (const msg of messages.messages) {
console.log(`${msg.role.toUpperCase()} (at ${msg.timestamp}): ${msg.content}`);
}
console.log(`Has more?: ${messages.hasMore}`);
console.log(`Total messages: ${messages.total}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof SessionNotFoundError) {
console.log(`Error: ${error.message}`);
}
}User Memories
List User Memories (with optional category filters)
import { UserNotFoundError, InvalidCategoriesError } from "recallrai";
try {
const user = await client.getUser("user123");
const memories = await user.listMemories({
categories: ["food_preferences", "allergies"],
sessionIdFilter: ["session-uuid-1", "session-uuid-2"], // optional: filter by specific sessions
sessionMetadataFilter: { environment: "production" }, // optional: filter by session metadata
offset: 0,
limit: 20,
includePreviousVersions: true,
includeConnectedMemories: true,
});
for (const mem of memories.items) {
console.log(`Memory ID: ${mem.memoryId}`);
console.log(`Categories: ${mem.categories}`);
console.log(`Content: ${mem.content}`);
console.log(`Created at: ${mem.createdAt}`);
console.log(`Session ID: ${mem.sessionId}`);
console.log(`Version: ${mem.versionNumber} of ${mem.totalVersions}`);
console.log(`Has previous versions: ${mem.hasPreviousVersions}`);
if (mem.previousVersions) {
console.log(`Previous versions: ${mem.previousVersions.length}`);
for (const version of mem.previousVersions) {
console.log(` - Version ${version.versionNumber}: ${version.content}`);
console.log(` Created: ${version.createdAt}, Expired: ${version.expiredAt}`);
console.log(` Expiration reason: ${version.expirationReason}`);
}
}
if (mem.connectedMemories) {
console.log(`Connected memories: ${mem.connectedMemories.length}`);
for (const connected of mem.connectedMemories) {
console.log(` - ${connected.memoryId}: ${connected.content}`);
}
}
console.log(`Merge conflict in progress: ${mem.mergeConflictInProgress}`);
console.log("---");
}
console.log(`Has more?: ${memories.hasMore}`);
console.log(`Total memories: ${memories.total}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof InvalidCategoriesError) {
console.log(`Error: ${error.message}`);
}
}User Messages
Get Last N Messages
import { UserNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const messages = await user.getLastNMessages(5);
for (const msg of messages.messages) {
console.log(`Session ID: ${msg.sessionId}`);
console.log(`${msg.role.toUpperCase()} (at ${msg.timestamp}): ${msg.content}`);
console.log("---");
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Merge Conflict Management
List Merge Conflicts
import { UserNotFoundError, MergeConflictStatus } from "recallrai";
try {
const user = await client.getUser("user123");
const conflicts = await user.listMergeConflicts({
offset: 0,
limit: 10,
status: MergeConflictStatus.PENDING,
sortBy: "created_at",
sortOrder: "desc",
});
console.log(`Total conflicts: ${conflicts.total}`);
console.log(`Has more: ${conflicts.hasMore}`);
for (const conf of conflicts.conflicts) {
console.log(`Conflict ID: ${conf.conflictId}`);
console.log(`Status: ${conf.status}`);
console.log(`New memory: ${conf.proposedMemoryContent}`);
console.log(`Conflicting memories: ${conf.conflictingMemories.length}`);
console.log(`Questions: ${conf.clarifyingQuestions.length}`);
console.log(`Created at: ${conf.createdAt}`);
console.log("---");
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Get a Specific Merge Conflict
import { UserNotFoundError, MergeConflictNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const conflict = await user.getMergeConflict("conflict-uuid");
console.log(`Conflict ID: ${conflict.conflictId}`);
console.log(`Status: ${conflict.status}`);
console.log(`New memory content: ${conflict.proposedMemoryContent}`);
console.log("\nConflicting memories:");
for (const mem of conflict.conflictingMemories) {
console.log(` Content: ${mem.content}`);
console.log(` Reason: ${mem.reason}`);
console.log();
}
console.log("Clarifying questions:");
for (const ques of conflict.clarifyingQuestions) {
console.log(` Question: ${ques.question}`);
console.log(` Options: ${ques.options}`);
console.log();
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Resolve a Merge Conflict
import {
UserNotFoundError,
MergeConflictNotFoundError,
MergeConflictAlreadyResolvedError,
MergeConflictInvalidQuestionsError,
MergeConflictMissingAnswersError,
MergeConflictInvalidAnswerError,
ValidationError,
} from "recallrai";
try {
const user = await client.getUser("user123");
const conflict = await user.getMergeConflict("conflict-uuid");
const answers = [];
for (const ques of conflict.clarifyingQuestions) {
console.log(` Question: ${ques.question}`);
console.log(` Options: ${ques.options}`);
console.log();
answers.push({
question: ques.question,
answer: ques.options[0],
message: "User prefers this option based on recent conversation",
});
}
await conflict.resolve(answers);
console.log(`Conflict resolved! Status: ${conflict.status}`);
console.log(`Resolved at: ${conflict.resolvedAt}`);
if (conflict.resolutionData) {
console.log(`Resolution data:`, conflict.resolutionData);
}
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictAlreadyResolvedError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictInvalidQuestionsError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictMissingAnswersError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictInvalidAnswerError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof ValidationError) {
console.log(`Error: ${error.message}`);
}
}Refresh Merge Conflict Data
import { UserNotFoundError, MergeConflictNotFoundError } from "recallrai";
try {
const user = await client.getUser("user123");
const conflict = await user.getMergeConflict("conflict-uuid");
await conflict.refresh();
console.log(`Current status: ${conflict.status}`);
console.log(`Last updated: ${conflict.resolvedAt}`);
} catch (error) {
if (error instanceof UserNotFoundError) {
console.log(`Error: ${error.message}`);
} else if (error instanceof MergeConflictNotFoundError) {
console.log(`Error: ${error.message}`);
}
}Example Usage with LLMs
import OpenAI from "openai";
import { RecallrAI, UserNotFoundError, MessageRole } from "recallrai";
const raiClient = new RecallrAI({
apiKey: "rai_yourapikey",
projectId: "your-project-uuid",
});
const oaiClient = new OpenAI({
apiKey: "your-openai-api-key",
});
async function chatWithMemory(userId: string, sessionId?: string) {
let user;
try {
user = await raiClient.getUser(userId);
} catch (error) {
if (error instanceof UserNotFoundError) {
user = await raiClient.createUser(userId);
} else {
throw error;
}
}
let session;
if (sessionId) {
session = await user.getSession(sessionId);
} else {
session = await user.createSession({ autoProcessAfterSeconds: 1800 });
console.log(`Created new session: ${session.sessionId}`);
}
console.log("Chat session started. Type 'exit' to end the conversation.");
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const askQuestion = (query: string): Promise<string> => {
return new Promise((resolve) => rl.question(query, resolve));
};
while (true) {
const userMessage = await askQuestion("You: ");
if (userMessage.toLowerCase() === "exit") {
break;
}
await session.addMessage(MessageRole.USER, userMessage);
const context = await session.getContext();
const systemPrompt = "You are a helpful assistant" + context.context;
const messagesData = await session.getMessages(0, 50);
const previousMessages = messagesData.messages.map((message) => ({
role: message.role,
content: message.content,
}));
const response = await oaiClient.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "system", content: systemPrompt }, ...previousMessages],
temperature: 0.7,
});
const assistantMessage = response.choices[0].message.content || "";
console.log(`Assistant: ${assistantMessage}`);
await session.addMessage(MessageRole.ASSISTANT, assistantMessage);
}
rl.close();
console.log("Processing session to update memory...");
await session.process();
console.log(`Session ended. Session ID: ${session.sessionId}`);
return session.sessionId;
}
chatWithMemory("user123").then((sessionId) => {
console.log(`To continue this conversation later, use session ID: ${sessionId}`);
});Exception Handling
The RecallrAI SDK implements a comprehensive exception hierarchy to help you handle different error scenarios gracefully.
Available Exceptions
- RecallrAIError: Base exception for all SDK-specific errors
- AuthenticationError: Authentication issues with API key or project ID
- TimeoutError: Request timeout
- ConnectionError: Connection issues to the API
- InternalServerError: API returns 5xx error
- RateLimitError: API rate limit exceeded
- UserNotFoundError: User doesn't exist
- UserAlreadyExistsError: User creation with existing ID
- InvalidCategoriesError: Invalid memory categories
- SessionNotFoundError: Session doesn't exist
- InvalidSessionStateError: Invalid operation for session state
- MergeConflictNotFoundError: Merge conflict doesn't exist
- MergeConflictAlreadyResolvedError: Conflict already processed
- MergeConflictInvalidQuestionsError: Invalid clarifying questions
- MergeConflictMissingAnswersError: Missing required answers
- MergeConflictInvalidAnswerError: Invalid answer option
- ValidationError: Request validation failure
Importing Exceptions
import {
UserNotFoundError,
SessionNotFoundError,
InvalidCategoriesError,
MergeConflictNotFoundError,
MergeConflictAlreadyResolvedError,
} from "recallrai";Best Practices for Error Handling
try {
// SDK operation
} catch (error) {
if (error instanceof UserNotFoundError) {
// Specific handling
} else if (error instanceof RecallrAIError) {
// General fallback
}
}Conclusion
This README outlines the basic usage of the RecallrAI SDK functions for user and session management. For additional documentation and advanced usage, please see the official documentation or the source code repository on GitHub.
