Folder Organization
Folders in the Letta Filesystem organize and group files, making them easy to manage and attach to agents
This tutorial demonstrates how to build a PDF chat application using Letta. You’ll learn how to upload PDF documents to the Letta Filesystem, attach them to an agent, and query the agent about the content. Letta automatically extracts text from PDFs using OCR, making the content accessible to your agents.
By the end of this guide, you’ll understand how to create document analysis workflows where agents can read, understand, and answer questions about PDF files.
Install the required dependencies:
npm install @letta-ai/letta-clientpip install letta-client requestsimport Letta from "@letta-ai/letta-client";
// Initialize the Letta client using LETTA_API_KEY environment variableconst client = new Letta({ apiKey: process.env.LETTA_API_KEY });
// If self-hosting, specify the base URL:// const client = new Letta({ baseURL: "http://localhost:8283" });from letta_client import Lettaimport os
# Initialize the Letta client using LETTA_API_KEY environment variableclient = Letta(api_key=os.getenv("LETTA_API_KEY"))
# If self-hosting, specify the base URL:# client = Letta(base_url="http://localhost:8283")Folders in the Letta Filesystem organize files and make them accessible to agents. Create a folder specifically for storing PDF documents:
// Create a folder to store PDF documents (or use existing one)// API Reference: https://docs.letta.com/api-reference/folders/createlet folderId: string;try { // Try to retrieve existing folder by name folderId = await client.folders.retrieveByName("PDF Documents"); console.log(`Using existing folder: ${folderId}\n`);} catch (error: any) { // If folder doesn't exist (404), create it if (error.statusCode === 404) { const folder = await client.folders.create({ name: "PDF Documents", description: "A folder containing PDF files for the agent to read", }); folderId = folder.id; console.log(`Created folder: ${folderId}\n`); } else { throw error; }}# Create a folder to store PDF documents (or use existing one)# API Reference: https://docs.letta.com/api-reference/folders/createfrom letta_client.core.api_error import ApiError
try: # Try to retrieve existing folder by namefolder_id = client.folders.retrieve_by_name("PDF Documents")print(f"Using existing folder: {folder_id}\n")except ApiError as e: # If folder doesn't exist (404), create itif e.status_code == 404:folder = client.folders.create(name="PDF Documents",description="A folder containing PDF files for the agent to read",)folder_id = folder.idprint(f"Created folder: {folder_id}\n")else:raiseCreated folder: folder-a1b2c3d4-e5f6-7890-abcd-ef1234567890If the folder already exists, you’ll see:
Using existing folder: folder-a1b2c3d4-e5f6-7890-abcd-ef1234567890Let’s download a sample PDF (the MemGPT research paper) and upload it to the folder. Letta will automatically extract the text content using OCR.
import * as fs from "fs";import * as https from "https";
// Download the PDF if it doesn't exist locallyconst pdfFilename = "memgpt.pdf";
if (!fs.existsSync(pdfFilename)) { console.log(`Downloading ${pdfFilename}...`);
await new Promise<void>((resolve, reject) => { const file = fs.createWriteStream(pdfFilename); https .get("https://arxiv.org/pdf/2310.08560", (response) => { response.pipe(file); file.on("finish", () => { file.close(); console.log("Download complete\n"); resolve(); }); file.on("error", reject); }) .on("error", reject); });}
// Upload the PDF to the folder// API Reference: https://docs.letta.com/api-reference/folders/files/uploadconst uploadedFile = await client.folders.files.upload( fs.createReadStream(pdfFilename), folderId, { duplicateHandling: "skip" },);
console.log(`Uploaded PDF: ${uploadedFile.id}\n`);import requests
# Download the PDF if it doesn't exist locallypdf_filename = "memgpt.pdf"
if not os.path.exists(pdf_filename): print(f"Downloading {pdf_filename}...") response = requests.get("https://arxiv.org/pdf/2310.08560") with open(pdf_filename, "wb") as f: f.write(response.content) print("Download complete\n")
# Upload the PDF to the folder# API Reference: https://docs.letta.com/api-reference/folders/files/uploadwith open(pdf_filename, "rb") as f: file = client.folders.files.upload( folder_id=folder_id, file=f, duplicate_handling="skip", )
print(f"Uploaded PDF: {file.id}\n")Downloading memgpt.pdf...Download complete
Uploaded PDF: file-a1b2c3d4-e5f6-7890-abcd-ef1234567890Create an agent with a persona configured for analyzing documents. The agent’s memory blocks define its purpose and capabilities:
// Create an agent configured to analyze documents// API Reference: https://docs.letta.com/api-reference/agents/createconst agent = await client.agents.create({ name: "pdf_assistant", model: "openai/gpt-4o-mini", memory_blocks: [ { label: "persona", value: "I am a helpful research assistant that analyzes PDF documents and answers questions about their content.", }, { label: "human", value: "Name: User\nTask: Analyzing PDF documents", }, ],});
console.log(`Created agent: ${agent.id}\n`);# Create an agent configured to analyze documents# API Reference: https://docs.letta.com/api-reference/agents/createagent = client.agents.create( name="pdf_assistant", model="openai/gpt-4o-mini", memory_blocks=[ { "label": "persona", "value": "I am a helpful research assistant that analyzes PDF documents and answers questions about their content." }, { "label": "human", "value": "Name: User\nTask: Analyzing PDF documents" } ],)
print(f"Created agent: {agent.id}\n")Created agent: agent-a1b2c3d4-e5f6-7890-abcd-ef1234567890
Attach the folder containing the PDF to the agent. This gives the agent the ability to search through all files in the folder:
// Attach the folder to the agent// API Reference: https://docs.letta.com/api-reference/agents/folders/attachawait client.agents.folders.attach(agent.id, folderId);
console.log(`Attached folder to agent\n`);# Attach the folder to the agent# API Reference: https://docs.letta.com/api-reference/agents/folders/attachclient.agents.folders.attach( agent_id=agent.id, folder_id=folder_id,)
print(f"Attached folder to agent\n")Attached folder to agent
Now ask the agent questions about the PDF. The agent will search through the document content to find relevant information:
// Ask the agent to summarize the PDF// API Reference: https://docs.letta.com/api-reference/agents/messages/createconst response = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "Can you summarize the main ideas from the MemGPT paper?", }, ],});
for (const msg of response.messages) { if (msg.message_type === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); }}# Ask the agent to summarize the PDF# API Reference: https://docs.letta.com/api-reference/agents/messages/createresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "Can you summarize the main ideas from the MemGPT paper?"}],)
for msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n")Assistant: The MemGPT paper introduces a system that enables LLMs tomanage their own memory hierarchy, similar to how operating systems managememory. It addresses the limited context window problem in large languagemodels by introducing a memory management system inspired by traditionaloperating systems. The key innovation is allowing LLMs to explicitly moveinformation between main context (limited) and external storage (unlimited),enabling extended conversations and document analysis that exceed typicalcontext limits.You can continue the conversation to ask more specific questions about the document:
// Ask a specific question about the PDF contentconst response2 = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "What problem does MemGPT solve?", }, ],});
for (const msg of response2.messages) { if (msg.message_type === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); }}# Ask a specific question about the PDF contentresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "What problem does MemGPT solve?"}],)
for msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n")Assistant: MemGPT addresses the limited context window problem in largelanguage models. Traditional LLMs can only process a fixed amount of text atonce (their context window), which makes it difficult to maintain longconversations or analyze large documents. MemGPT solves this by introducing amemory management system that allows the model to intelligently moveinformation between its limited context and unlimited external storage,enabling extended conversations and document analysis beyond typical contextlimits.Here’s the full code in one place that you can run:
import Letta from "@letta-ai/letta-client";import * as fs from "fs";import * as https from "https";
async function main() { // Initialize client const client = new Letta({ apiKey: process.env.LETTA_API_KEY });
// Create folder (or use existing one) let folderId: string; try { folderId = await client.folders.retrieveByName("PDF Documents"); console.log(`Using existing folder: ${folderId}\n`); } catch (error: any) { if (error.statusCode === 404) { const folder = await client.folders.create({ name: "PDF Documents", description: "A folder containing PDF files for the agent to read", }); folderId = folder.id; console.log(`Created folder: ${folderId}\n`); } else { throw error; } }
// Download and upload PDF const pdfFilename = "memgpt.pdf";
if (!fs.existsSync(pdfFilename)) { console.log(`Downloading ${pdfFilename}...`); await new Promise<void>((resolve, reject) => { const file = fs.createWriteStream(pdfFilename); https .get("https://arxiv.org/pdf/2310.08560", (response) => { response.pipe(file); file.on("finish", () => { file.close(); console.log("Download complete\n"); resolve(); }); file.on("error", reject); }) .on("error", reject); }); }
const uploadedFile = await client.folders.files.upload( fs.createReadStream(pdfFilename), folderId, { duplicateHandling: "skip" }, );
console.log(`Uploaded PDF: ${uploadedFile.id}\n`);
// Create agent const agent = await client.agents.create({ name: "pdf_assistant", model: "openai/gpt-4o-mini", memory_blocks: [ { label: "persona", value: "I am a helpful research assistant that analyzes PDF documents and answers questions about their content.", }, { label: "human", value: "Name: User\nTask: Analyzing PDF documents", }, ], });
console.log(`Created agent: ${agent.id}\n`);
// Attach folder to agent await client.agents.folders.attach(agent.id, folderId);
console.log(`Attached folder to agent\n`);
// Query the PDF const response = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "Can you summarize the main ideas from the MemGPT paper?", }, ], });
for (const msg of response.messages) { if (msg.message_type === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); } }
// Ask specific question const response2 = await client.agents.messages.create(agent.id, { messages: [ { role: "user", content: "What problem does MemGPT solve?", }, ], });
for (const msg of response2.messages) { if (msg.message_type === "assistant_message") { console.log(`Assistant: ${msg.content}\n`); } }}
main();from letta_client import Lettafrom letta_client.core.api_error import ApiErrorimport osimport requests
# Initialize clientclient = Letta(api_key=os.getenv("LETTA_API_KEY"))
# Create folder (or use existing one)try: folder_id = client.folders.retrieve_by_name("PDF Documents") print(f"Using existing folder: {folder_id}\n")except ApiError as e: if e.status_code == 404: folder = client.folders.create( name="PDF Documents", description="A folder containing PDF files for the agent to read", ) folder_id = folder.id print(f"Created folder: {folder_id}\n") else: raise
# Download and upload PDFpdf_filename = "memgpt.pdf"
if not os.path.exists(pdf_filename): print(f"Downloading {pdf_filename}...") response = requests.get("https://arxiv.org/pdf/2310.08560") with open(pdf_filename, "wb") as f: f.write(response.content) print("Download complete\n")
with open(pdf_filename, "rb") as f: file = client.folders.files.upload( folder_id=folder_id, file=f, duplicate_handling="skip", )
print(f"Uploaded PDF: {file.id}\n")
# Create agentagent = client.agents.create( name="pdf_assistant", model="openai/gpt-4o-mini", memory_blocks=[ { "label": "persona", "value": "I am a helpful research assistant that analyzes PDF documents and answers questions about their content." }, { "label": "human", "value": "Name: User\nTask: Analyzing PDF documents" } ],)
print(f"Created agent: {agent.id}\n")
# Attach folder to agentclient.agents.folders.attach( agent_id=agent.id, folder_id=folder_id,)
print(f"Attached folder to agent\n")
# Query the PDFresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "Can you summarize the main ideas from the MemGPT paper?"}],)
for msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n")
# Ask specific questionresponse = client.agents.messages.create( agent_id=agent.id, messages=[{"role": "user", "content": "What problem does MemGPT solve?"}],)
for msg in response.messages: if msg.message_type == "assistant_message": print(f"Assistant: {msg.content}\n")Folder Organization
Folders in the Letta Filesystem organize and group files, making them easy to manage and attach to agents
Automatic OCR
PDFs are automatically processed using OCR to extract searchable text content during upload
Document Access
Attaching folders gives agents search capabilities to retrieve relevant content from files
Contextual Search
Agents use search tools to find relevant passages in documents when answering questions
Upload academic papers and have agents summarize findings, extract key concepts, or compare methodologies.
Build customer support systems that answer questions based on product documentation or manuals.
Analyze contracts, agreements, or legal documents to extract clauses, identify risks, or summarize terms.
Process multiple PDFs to build a searchable knowledge base that agents can query for information.