Persisting States with Databases and Callbacks

Introduction: Why Database Persistence Matters

In the previous lesson, you built a FastAPI server that exposes AI agents through REST endpoints, storing agent states in a Python dictionary. This worked well for learning the basics, but had a critical flaw: when your server restarts, all agent states disappear. For Factor 6 — Launch / Pause / Resume with simple APIs to work reliably, an agent that was paused yesterday must still be resumable today. Database persistence solves this by storing states on disk. In this lesson, you'll replace in-memory storage with SQLite using SQLAlchemy and implement progress callbacks that save state after each agent step, enabling real-time progress tracking for clients. This persistent foundation is what makes the pause and resume endpoints you'll build in the next lesson truly practical.

Setting Up SQLAlchemy with SQLite

To begin working with a database, you need to create a connection engine and configure where the database file will be stored. SQLAlchemy provides this through the create_engine function, which takes a connection string specifying the database type and location:

Python
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import declarative_base

# Build an absolute path to the database file inside the data/ directory
db_path = Path(__file__).resolve().parent.parent / "data" / "agent_states.db"

# Create the directory if it doesn't already exist
db_path.parent.mkdir(parents=True, exist_ok=True)

# Connect to the SQLite database at the given path
engine = create_engine(f"sqlite:///{db_path}", echo=False)

# Base class that all SQLAlchemy models will inherit from
Base = declarative_base()

# Factory that creates new database sessions for each transaction
SessionLocal = sessionmaker(bind=engine)

The db_path variable uses Path to construct an absolute path to a file named agent_states.db inside a data directory. The mkdir(parents=True, exist_ok=True) call ensures the directory exists before SQLite tries to create the database file. The connection string sqlite:///{db_path} tells SQLAlchemy to use SQLite with the specified file path, and setting echo=False prevents SQLAlchemy from logging all SQL statements to the console. The declarative_base() function creates a base class that all your database models will inherit from, while sessionmaker creates a factory for database sessions that represent transaction boundaries. With this foundation in place, you can now define how agent states will be stored.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal