Database Storage
This example demonstrates how to use persistent database storage with Tyler, including PostgreSQL integration and metrics tracking.
Overview
The example shows:
- Setting up PostgreSQL database storage
- Initializing and using ThreadStore
- Saving and retrieving threads
- Tracking message metrics
- Using file storage
Code
from dotenv import load_dotenv
import os
import asyncio
from tyler.models.agent import Agent
from tyler.models.thread import Thread, Message
from tyler.database.thread_store import ThreadStore
from tyler.storage import get_file_store
import weave
# Load environment variables from .env file
load_dotenv()
# Initialize weave for tracing (optional)
if os.getenv("WANDB_API_KEY"):
weave.init("tyler")
async def main():
"""
Demonstrates database persistence with metrics tracking.
Uses environment variables for database configuration.
"""
# Construct PostgreSQL URL from environment variables
db_url = f"postgresql+asyncpg://{os.getenv('TYLER_DB_USER')}:{os.getenv('TYLER_DB_PASSWORD')}@{os.getenv('TYLER_DB_HOST')}:{os.getenv('TYLER_DB_PORT')}/{os.getenv('TYLER_DB_NAME')}"
# Initialize ThreadStore with PostgreSQL URL
store = ThreadStore(db_url)
await store.initialize() # Required to create tables
# Initialize file store
file_store = get_file_store()
print(f"Initialized file store at: {file_store.base_path}")
# Create agent with database storage
agent = Agent(
model_name="gpt-4o",
purpose="To help with general questions",
thread_store=store
)
# Create and save a new thread
thread = Thread()
await store.save(thread)
# Add a message
message = Message(
role="user",
content="What are the benefits of database storage over memory storage?"
)
thread.add_message(message)
await store.save(thread)
# Get response
processed_thread, new_messages = await agent.go(thread)
print("Assistant's response:")
for message in new_messages:
if message.role == "assistant":
print(message.content)
print("\nMessage metrics:")
print(f"- Tokens: {message.metrics['completion_tokens']} completion, {message.metrics['prompt_tokens']} prompt")
print(f"- Model: {message.metrics['model']}")
print(f"- Latency: {message.metrics['latency']:.0f}ms")
if __name__ == "__main__":
asyncio.run(main())
Step-by-Step Explanation
1. Database Setup
db_url = f"postgresql+asyncpg://{os.getenv('TYLER_DB_USER')}:{os.getenv('TYLER_DB_PASSWORD')}@{os.getenv('TYLER_DB_HOST')}:{os.getenv('TYLER_DB_PORT')}/{os.getenv('TYLER_DB_NAME')}"
store = ThreadStore(db_url)
await store.initialize()
- Constructs database URL from environment variables
- Initializes ThreadStore with PostgreSQL
- Creates necessary database tables
2. File Storage Setup
file_store = get_file_store()
print(f"Initialized file store at: {file_store.base_path}")
- Initializes file storage for attachments
- Uses default or configured storage path
3. Agent Configuration
agent = Agent(
model_name="gpt-4o",
purpose="To help with general questions",
thread_store=store
)
- Creates agent with database storage
- Sets model and purpose
- Connects to thread store
4. Thread Management
thread = Thread()
await store.save(thread)
message = Message(
role="user",
content="What are the benefits of database storage over memory storage?"
)
thread.add_message(message)
await store.save(thread)
- Creates and saves threads
- Adds messages
- Persists changes to database
Docker Database Setup
Tyler includes a built-in Docker Compose configuration to easily set up a PostgreSQL database for development and testing.
Prerequisites
- Docker and Docker Compose installed on your system
- Tyler installed via pip (
pip install tyler-agent
)
Setup Steps
-
Create Environment Variables
Create or update your
.env
file in your project directory with PostgreSQL configuration:# Database Configuration for Docker PostgreSQL
TYLER_DB_TYPE=postgresql
TYLER_DB_HOST=localhost
TYLER_DB_PORT=5433 # Using 5433 to avoid conflicts with local PostgreSQL
TYLER_DB_NAME=tyler
TYLER_DB_USER=tyler
TYLER_DB_PASSWORD=tyler_dev -
Create a Docker Compose File
Create a
docker-compose.yml
file in your project directory:version: '3.8'
services:
postgres:
image: postgres:16
container_name: tyler-postgres
environment:
POSTGRES_DB: ${TYLER_DB_NAME:-tyler}
POSTGRES_USER: ${TYLER_DB_USER:-tyler}
POSTGRES_PASSWORD: ${TYLER_DB_PASSWORD:-tyler_dev}
ports:
- "${TYLER_DB_PORT:-5433}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${TYLER_DB_USER:-tyler}"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data: -
Start PostgreSQL with Docker
Start the PostgreSQL container using Docker Compose:
# Start the PostgreSQL container
docker-compose up -dThis command will:
- Create a Docker container named
tyler-postgres
running PostgreSQL 16 - Configure it with the credentials from your environment variables
- Map the PostgreSQL port to your local machine
- Create a persistent volume for your data
- Create a Docker container named
-
Initialize the Database
After starting the container, initialize the database schema using the Tyler CLI:
# Initialize the database schema
tyler-db init -
Optional: Run Migrations
If you need to update an existing database:
# Apply pending migrations
tyler-db upgrade -
Verify Connection
You can test your database connection using:
# Check database status
tyler-db current
Additional Database Commands
Tyler provides several CLI commands for database management:
# Generate a new migration
tyler-db migrate
# Show migration history
tyler-db history
# Downgrade database by one version
tyler-db downgrade
# Initialize with specific options
tyler-db init --db-type postgresql --db-host localhost --db-port 5433 --db-name tyler --verbose
Additional Docker Commands
-
View Container Status
docker ps
-
Stop the Container
docker-compose down
-
View Container Logs
docker logs tyler-postgres
Configuration Requirements
Environment Variables
# Database Configuration
TYLER_DB_TYPE=postgresql
TYLER_DB_HOST=localhost
TYLER_DB_PORT=5432
TYLER_DB_NAME=tyler
TYLER_DB_USER=tyler
TYLER_DB_PASSWORD=tyler_dev
# Optional Database Settings
TYLER_DB_ECHO=false
TYLER_DB_POOL_SIZE=5
TYLER_DB_MAX_OVERFLOW=10
TYLER_DB_POOL_TIMEOUT=30
TYLER_DB_POOL_RECYCLE=1800
# File Storage Configuration
TYLER_FILE_STORAGE_TYPE=local
TYLER_FILE_STORAGE_PATH=/path/to/files
# Monitoring
WANDB_API_KEY=your-wandb-api-key
Expected Output
When you run this example, you'll see output similar to:
Initialized file store at: /Users/username/.tyler/files
Assistant's response:
Database storage offers several advantages over in-memory storage:
1. Persistence across sessions
2. Scalability for large datasets
3. Concurrent access support
4. Data integrity and recovery
5. Query and filtering capabilities
Message metrics:
- Tokens: 128 completion, 84 prompt
- Model: gpt-4o
- Latency: 450ms
Key Concepts
-
Database Storage
- Thread persistence
- Message history
- Concurrent access
- Data integrity
-
File Storage
- Attachment handling
- File organization
- Path management
-
Metrics Tracking
- Token usage
- Model performance
- Response latency
-
Environment Configuration
- Database settings
- Connection pooling
- Storage paths
Common Customizations
SQLite Storage
store = ThreadStore("sqlite:///path/to/database.db")
Custom Pool Settings
store = ThreadStore(
url=db_url,
pool_size=10,
max_overflow=20,
pool_timeout=60
)
Custom File Storage
from tyler.storage import FileStorage
store = FileStorage(
storage_type="local",
base_path="/path/to/files"
)