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);