From 5d8019684d375749e54a1ea2dcf2216309787b13 Mon Sep 17 00:00:00 2001 From: Cameron Pfiffer Date: Wed, 30 Jul 2025 10:47:10 -0700 Subject: [PATCH] feat: add filesystem demo with file upload and streaming - Add example demonstrating file upload to Letta data sources - Include text file upload from disk and string creation - Add PDF download and upload functionality - Implement streaming agent responses with interactive chat - Show how to attach data sources to agents --- examples/files/README.md | 34 ++++++ examples/files/example-on-disk.txt | 2 + examples/files/main.py | 190 +++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 examples/files/README.md create mode 100644 examples/files/example-on-disk.txt create mode 100644 examples/files/main.py diff --git a/examples/files/README.md b/examples/files/README.md new file mode 100644 index 00000000..736c3a8a --- /dev/null +++ b/examples/files/README.md @@ -0,0 +1,34 @@ +# Letta Files and Streaming Demo + +This demo shows how to work with Letta's file upload and streaming capabilities. + +## Features + +- Upload files from disk to a Letta data source +- Create files from strings and upload them +- Download and upload PDF files +- Create an agent and attach data sources +- Stream agent responses in real-time +- Interactive chat with file-aware agent + +## Files + +- `main.py` - Main demo script showing file upload and streaming +- `example-on-disk.txt` - Sample text file for upload demonstration +- `memgpt.pdf` - MemGPT paper (downloaded automatically) + +## Setup + +1. Set your Letta API key: `export LETTA_API_KEY=your_key_here` +2. Install dependencies: `pip install letta-client requests rich` +3. Run the demo: `python main.py` + +## Usage + +The demo will: +1. Create a data source called "Example Source" +2. Upload the example text file and PDF +3. Create an agent named "Clippy" +4. Start an interactive chat session + +Type 'quit' or 'exit' to end the conversation. \ No newline at end of file diff --git a/examples/files/example-on-disk.txt b/examples/files/example-on-disk.txt new file mode 100644 index 00000000..7b2fa39d --- /dev/null +++ b/examples/files/example-on-disk.txt @@ -0,0 +1,2 @@ +Hey, you're looking at a different example. +This password is "stateful agents". \ No newline at end of file diff --git a/examples/files/main.py b/examples/files/main.py new file mode 100644 index 00000000..0cf65447 --- /dev/null +++ b/examples/files/main.py @@ -0,0 +1,190 @@ +""" +Letta Filesystem + +This demo shows how to: +1. Create a folder and upload files (both from disk and from strings) +2. Create an agent and attach the data folder +3. Stream the agent's responses +4. Query the agent about the uploaded files + +The demo uploads: +- A text file from disk (example-on-disk.txt) +- A text file created from a string (containing a password) +- The MemGPT paper PDF from arXiv + +Then asks the agent to summarize the paper and find passwords in the files. +""" + +from letta_client import Letta +from letta_client.core.api_error import ApiError +import os +import requests +from rich import print + +LETTA_API_KEY = os.getenv("LETTA_API_KEY") +if LETTA_API_KEY is None: + raise ValueError("LETTA_API_KEY is not set") + +FOLDER_NAME = "Example Folder" + +# Connect to our Letta server +client = Letta(token=LETTA_API_KEY) + +# get an available embedding_config +embedding_configs = client.embedding_models.list() +embedding_config = embedding_configs[0] + +# Check if the folder already exists +try: + folder_id = client.folders.retrieve_by_name(FOLDER_NAME) + +# We got an API error. Check if it's a 404, meaning the folder doesn't exist. +except ApiError as e: + if e.status_code == 404: + # Create a new folder + folder = client.folders.create( + name=FOLDER_NAME, + description="This is an example folder", + instructions="Use this data folder to see how Letta works.", + ) + folder_id = folder.id + else: + raise e + +except Exception as e: + # Something else went wrong + raise e + + +# +# There's two ways to upload a file to a folder. +# +# 1. From an existing file +# 2. From a string by encoding it into a base64 string +# +# + +# 1. From an existing file +# "rb" means "read binary" +file = open("example-on-disk.txt", "rb") + +# Upload the file to the folder +file = client.folders.files.upload( + folder_id=folder_id, + file=file, + duplicate_handling="skip" +) + +# 2. From a string by encoding it into a base64 string +import io +content = """ +This is an example file. If you can read this, +the password is 'letta'. +""" + +# Encode the string into bytes, and then create a file-like object +# that exists only in memory. +file_object = io.BytesIO(content.encode("utf-8")) + +# Set the name of the file +file_object.name = "example.txt" + +# Upload the file to the folder +file = client.folders.files.upload( + folder_id=folder_id, + file=file_object, + duplicate_handling="skip" +) + +# +# You can also upload PDFs! +# Letta extracts text from PDFs using OCR. +# + +# Download the PDF to the local directory if it doesn't exist +if not os.path.exists("memgpt.pdf"): + # Download the PDF + print("Downloading memgpt.pdf") + response = requests.get("https://arxiv.org/pdf/2310.08560") + with open("memgpt.pdf", "wb") as f: + f.write(response.content) + +# Upload the PDF to the folder +file = client.folders.files.upload( + folder_id=folder_id, + file=open("memgpt.pdf", "rb"), + duplicate_handling="skip" +) + +# +# Now we need to create an agent that can use this folder +# + +# Create an agent +agent = client.agents.create( + model="openai/gpt-4o-mini", + name="Example Agent", + description="This agent looks at files and answers questions about them.", + memory_blocks = [ + { + "label": "human", + "value": "The human wants to know about the files." + }, + { + "label": "persona", + "value": "My name is Clippy, I answer questions about files." + } + ] +) + +# Attach the data folder to the agent. +# Once the folder is attached, the agent will be able to see all +# files in the folder. +client.agents.folders.attach( + agent_id=agent.id, + folder_id=folder_id +) + +######################################################## +# This code makes a simple chatbot interface to the agent +######################################################## + +# Wrap this in a try/catch block to remove the agent in the event of an error +try: + print(f"šŸ¤– Connected to agent: {agent.name}") + print("šŸ’” Type 'quit' or 'exit' to end the conversation") + print("=" * 50) + + while True: + # Get user input + try: + user_input = input("\nšŸ‘¤ You: ").strip() + except (EOFError, KeyboardInterrupt): + print("\nšŸ‘‹ Goodbye!") + break + + if user_input.lower() in ['quit', 'exit', 'q']: + print("šŸ‘‹ Goodbye!") + break + + if not user_input: + continue + + # Stream the agent's response + stream = client.agents.messages.create_stream( + agent_id=agent.id, + messages=[ + { + "role": "user", + "content": user_input + } + ], + ) + + for chunk in stream: + print(chunk) + +finally: + client.agents.delete(agent.id) + +