Syncing a local folder to Letta Filesystem

We recently changed Letta Filesystem to allow you to set custom file names & allow slashes in file names, which means your files can mimic the structure of a local directory. Here’s a quick snippet to get you up and running:

"""
Upload directory with path-based file names
"""
import os
from pathlib import Path
from letta_client import Letta

# Setup
client = Letta(token=os.getenv("LETTA_API_KEY"))

# Check if the folder exists. If not, create it.
FOLDER_NAME = "Directory Upload Test"
try:
    folder_id = client.folders.retrieve_by_name(folder_name=FOLDER_NAME)
except:
    print(f"Folder '{FOLDER_NAME}' not found, creating...")
    folder_id = client.folders.create(name=FOLDER_NAME).id

# Directory to upload
local_dir = Path("example_text")

# Upload all files with relative paths as names
for file_path in local_dir.rglob("*"):
    if file_path.is_file():
        # Use relative path as the file name in Letta
        relative_name = str(file_path.relative_to(local_dir.parent))

        with open(file_path, "rb") as f:
            result = client.folders.files.upload(
                folder_id=folder_id,
                file=f,
                name=relative_name,  # e.g. "example_text/document1.txt"
                duplicate_handling="replace"
            )

        print(f"Uploaded: {relative_name}")

print(f"Folder ID: {folder_id}")

You can attach this to your agent with

client.agents.folders.attach(agent_id="blah blah blah", folder_id=folder_id)

This will attach filesystem tools (open, grep, semantic search) to your agent so they can flick through the files in the folders.

Here’s the typescript as well:

/**
 * MWE: Upload directory with path-based file names
 */
import * as fs from "fs";
import * as path from "path";
import { LettaClient } from "@letta-ai/letta-client";

async function main() {
  // Setup
  const client = new LettaClient({ token: process.env.LETTA_API_KEY });

  // Check if the folder exists. If not, create it.
  const FOLDER_NAME = "Directory Upload Test";
  let folderId: string;

  try {
    folderId = await client.folders.retrieveByName(FOLDER_NAME);
  } catch {
    console.log(`Folder '${FOLDER_NAME}' not found, creating...`);
    const folder = await client.folders.create({ name: FOLDER_NAME });
    folderId = folder.id!;
  }

  // Directory to upload
  const localDir = "example_text";

  // Function to recursively get all files in directory
  function getAllFiles(dirPath: string): string[] {
    const files: string[] = [];
    const items = fs.readdirSync(dirPath);

    for (const item of items) {
      const fullPath = path.join(dirPath, item);
      const stat = fs.statSync(fullPath);

      if (stat.isFile()) {
        files.push(fullPath);
      } else if (stat.isDirectory()) {
        files.push(...getAllFiles(fullPath));
      }
    }

    return files;
  }

  // Upload all files with relative paths as names
  const filePaths = getAllFiles(localDir);

  for (const filePath of filePaths) {
    // Use relative path as the file name in Letta
    const relativeName = path.relative(path.dirname(localDir), filePath);

    const fileStream = fs.createReadStream(filePath);

    const result = await client.folders.files.upload(
      fileStream,
      folderId,
      {
        name: relativeName, // e.g. "example_text/document1.txt"
        duplicateHandling: "replace",
      }
    );

    console.log(`Uploaded: ${relativeName}`);
  }

  console.log(`Folder ID: ${folderId}`);

  // Attach this folder to an agent with
  // client.agents.folders.attach({ agentId: "blah blah blah", folderId });
}

main().catch(console.error);