or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced

error-handling.mdtype-inference.md
glossary.mdindex.mdquick-reference.mdtask-index.md
tile.json

storage.mddocs/guides/

Storage Guide

This guide covers using storage implementations for persisting key-value data.

In-Memory Store

Basic Usage

import { InMemoryStore } from "langchain";

const store = new InMemoryStore<{ name: string; age: number }>();

// Set values
await store.mset([
  ["user:1", { name: "Alice", age: 30 }],
  ["user:2", { name: "Bob", age: 25 }],
]);

// Get values
const [user1, user2] = await store.mget(["user:1", "user:2"]);
console.log(user1); // { name: "Alice", age: 30 }

// Delete values
await store.mdelete(["user:1"]);

// Iterate keys
for await (const key of store.yieldKeys("user:")) {
  console.log(key); // "user:1", "user:2"
}

With Agent

import { createAgent, InMemoryStore } from "langchain";

const store = new InMemoryStore();

// Pre-populate with data
await store.mset([
  ["user:preferences", { theme: "dark", language: "en" }],
  ["user:history", { lastVisit: "2024-01-15" }],
]);

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [],
  store: store,
});

// Agent can access store during execution

File System Store

Basic Usage

import { LocalFileStore } from "langchain/storage/file_system";

// Create store at specific path
const store = await LocalFileStore.fromPath("./data/store");

// Store binary data
const encoder = new TextEncoder();
await store.mset([
  ["doc:1", encoder.encode("Document 1 content")],
  ["doc:2", encoder.encode("Document 2 content")],
]);

// Retrieve data
const [doc1, doc2] = await store.mget(["doc:1", "doc:2"]);
const decoder = new TextDecoder();
console.log(decoder.decode(doc1)); // "Document 1 content"

// Iterate keys
for await (const key of store.yieldKeys("doc:")) {
  console.log(key);
}

// Delete
await store.mdelete(["doc:1"]);

Direct File Access

import { LocalFileStore } from "langchain/storage/file_system";

const store = new LocalFileStore({ rootPath: "./data" });

// Get single file
const file = await store.getParsedFile("config.json");
if (file) {
  const decoder = new TextDecoder();
  console.log(decoder.decode(file));
}

Encoder-Backed Store

With Type Safety

import { EncoderBackedStore, LocalFileStore } from "langchain/storage/encoder_backed";

interface User {
  id: number;
  name: string;
  email: string;
}

// Create file-backed store with JSON encoding
const fileStore = await LocalFileStore.fromPath("./users");

const userStore = new EncoderBackedStore<number, User, Uint8Array>({
  store: fileStore,
  keyEncoder: (id) => `user:${id}`,
  valueSerializer: (user) => new TextEncoder().encode(JSON.stringify(user)),
  valueDeserializer: (data) => JSON.parse(new TextDecoder().decode(data)),
});

// Use with type safety
await userStore.mset([
  [1, { id: 1, name: "Alice", email: "alice@example.com" }],
  [2, { id: 2, name: "Bob", email: "bob@example.com" }],
]);

const [user1] = await userStore.mget([1]);
console.log(user1); // { id: 1, name: "Alice", email: "alice@example.com" }

Custom Serialization

import { EncoderBackedStore } from "langchain/storage/encoder_backed";
import msgpack from "msgpack-lite";

const store = new EncoderBackedStore({
  store: baseStore,
  keyEncoder: (key) => `data:${key}`,
  valueSerializer: (value) => msgpack.encode(value),
  valueDeserializer: (data) => msgpack.decode(data),
});

Document Store

Creating Document Store

import { createDocumentStoreFromByteStore, LocalFileStore } from "langchain/storage/encoder_backed";
import { Document } from "langchain";

// Create document store from file store
const fileStore = await LocalFileStore.fromPath("./documents");
const docStore = createDocumentStoreFromByteStore(fileStore);

// Store documents
await docStore.mset([
  [
    "doc1",
    new Document({
      pageContent: "Content of document 1",
      metadata: { author: "Alice", date: "2024-01-01" },
    }),
  ],
  [
    "doc2",
    new Document({
      pageContent: "Content of document 2",
      metadata: { author: "Bob", date: "2024-01-02" },
    }),
  ],
]);

// Retrieve documents
const [doc1, doc2] = await docStore.mget(["doc1", "doc2"]);
console.log(doc1.pageContent);
console.log(doc1.metadata);

Batch Operations

// Store multiple documents
const documents = [
  ["doc:1", new Document({ pageContent: "Content 1" })],
  ["doc:2", new Document({ pageContent: "Content 2" })],
  ["doc:3", new Document({ pageContent: "Content 3" })],
];

await docStore.mset(documents);

// Retrieve multiple
const docs = await docStore.mget(["doc:1", "doc:2", "doc:3"]);

// Delete multiple
await docStore.mdelete(["doc:1", "doc:2"]);

Common Patterns

Namespaced Keys

const store = new InMemoryStore();

// Organize with prefixes
await store.mset([
  ["users:1", userData1],
  ["users:2", userData2],
  ["sessions:abc", sessionData1],
  ["sessions:xyz", sessionData2],
  ["config:app", configData],
]);

// Query by namespace
for await (const key of store.yieldKeys("users:")) {
  console.log(key); // "users:1", "users:2"
}

for await (const key of store.yieldKeys("sessions:")) {
  console.log(key); // "sessions:abc", "sessions:xyz"
}

Caching

const cache = new InMemoryStore<string>();

async function getCachedData(key: string) {
  // Check cache
  const [cached] = await cache.mget([key]);
  if (cached) {
    return cached;
  }

  // Fetch and cache
  const data = await fetchExpensiveData(key);
  await cache.mset([[key, data]]);
  return data;
}

Session Storage

interface Session {
  userId: string;
  data: Record<string, any>;
  expiresAt: number;
}

const sessionStore = new InMemoryStore<Session>();

async function saveSession(sessionId: string, userId: string, data: any) {
  await sessionStore.mset([
    [
      `session:${sessionId}`,
      {
        userId,
        data,
        expiresAt: Date.now() + 3600000, // 1 hour
      },
    ],
  ]);
}

async function getSession(sessionId: string) {
  const [session] = await sessionStore.mget([`session:${sessionId}`]);
  if (!session) return null;

  // Check expiration
  if (session.expiresAt < Date.now()) {
    await sessionStore.mdelete([`session:${sessionId}`]);
    return null;
  }

  return session;
}

Configuration Storage

const configStore = new InMemoryStore<any>();

async function loadConfig() {
  const [config] = await configStore.mget(["app:config"]);
  return config || getDefaultConfig();
}

async function saveConfig(config: any) {
  await configStore.mset([["app:config", config]]);
}

async function updateConfig(updates: Partial<any>) {
  const config = await loadConfig();
  const newConfig = { ...config, ...updates };
  await saveConfig(newConfig);
}

User Preferences

interface UserPreferences {
  theme: "light" | "dark";
  language: string;
  notifications: boolean;
  customSettings: Record<string, any>;
}

const prefStore = new InMemoryStore<UserPreferences>();

async function getUserPreferences(userId: string) {
  const [prefs] = await prefStore.mget([`prefs:${userId}`]);
  return prefs || getDefaultPreferences();
}

async function updateUserPreferences(
  userId: string,
  updates: Partial<UserPreferences>
) {
  const prefs = await getUserPreferences(userId);
  const newPrefs = { ...prefs, ...updates };
  await prefStore.mset([[`prefs:${userId}`, newPrefs]]);
}

Persistence Strategies

Development

// Use in-memory for development
const devStore = new InMemoryStore();

Production

// Use file system for persistence
const prodStore = await LocalFileStore.fromPath("./data/storage");

Hybrid

// In-memory cache backed by file system
const fileStore = await LocalFileStore.fromPath("./data");
const cache = new InMemoryStore();

async function getWithCache(key: string) {
  // Check cache first
  const [cached] = await cache.mget([key]);
  if (cached) return cached;

  // Load from disk
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();
  const [data] = await fileStore.mget([key]);
  if (!data) return null;

  const value = JSON.parse(decoder.decode(data));

  // Cache for next time
  await cache.mset([[key, value]]);

  return value;
}

Best Practices

Key Naming

  • Use consistent prefixes: user:123, session:abc
  • Include type information in prefix
  • Use hierarchical keys: app:config:feature
  • Keep keys readable and meaningful

In-Memory Store

  • Fast but not persistent
  • Good for caching and temporary data
  • Lost on process restart
  • Use for development and testing

File System Store

  • Persistent across restarts
  • Good for local development
  • Not suitable for distributed systems
  • Ensure proper file permissions
  • Handle disk space limitations

Encoder-Backed Store

  • Adds type safety to byte stores
  • Use appropriate serialization (JSON, MessagePack, etc.)
  • Handle serialization errors gracefully
  • Consider compression for large values

Performance

  • Batch operations when possible (mset, mget, mdelete)
  • Implement key expiration for caches
  • Monitor storage size and clean up old data
  • Use appropriate data structures for your use case

Error Handling

  • Handle missing keys appropriately
  • Catch and log serialization errors
  • Implement retry logic for transient failures
  • Validate data before storing

See Storage API Reference for complete API documentation.