diff --git a/.dockerignore b/.dockerignore index 39976e44..ffe57a73 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,9 @@ -chatui +**/__pycache__ +**/.pytest_cache +**/*.pyc +**/*.pyo +**/*.pyd +.git +.gitignore +.env +*.log diff --git a/Dockerfile b/Dockerfile index 7bea0360..42ee3d37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,69 @@ -# The builder image, used to build the virtual environment -FROM python:3.12.2-bookworm as builder -ARG LETTA_ENVIRONMENT=PRODUCTION -ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} -RUN pip install poetry==1.8.2 +# Start with pgvector base for builder +FROM ankane/pgvector:v0.5.1 AS builder -ENV POETRY_NO_INTERACTION=1 \ +# Install Python and required packages +RUN apt-get update && apt-get install -y \ + python3 \ + python3-venv \ + python3-pip \ + python3-full \ + build-essential \ + libpq-dev \ + python3-dev \ + && rm -rf /var/lib/apt/lists/* + +ARG LETTA_ENVIRONMENT=PRODUCTION +ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} \ + POETRY_NO_INTERACTION=1 \ POETRY_VIRTUALENVS_IN_PROJECT=1 \ POETRY_VIRTUALENVS_CREATE=1 \ POETRY_CACHE_DIR=/tmp/poetry_cache WORKDIR /app +# Create and activate virtual environment +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Now install poetry in the virtual environment +RUN pip install --no-cache-dir poetry==1.8.2 + +# Copy dependency files first COPY pyproject.toml poetry.lock ./ -RUN poetry lock --no-update -RUN if [ "$LETTA_ENVIRONMENT" = "DEVELOPMENT" ] ; then \ - poetry install --no-root -E "postgres server dev" ; \ - else \ - poetry install --no-root --all-extras && \ - rm -rf $POETRY_CACHE_DIR ; \ - fi +# Then copy the rest of the application code +COPY . . +RUN poetry lock --no-update && \ + poetry install --all-extras && \ + rm -rf $POETRY_CACHE_DIR + +# Runtime stage +FROM ankane/pgvector:v0.5.1 AS runtime + +# Install Python packages +RUN apt-get update && apt-get install -y \ + python3 \ + python3-venv \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /app -# The runtime image, used to just run the code provided its virtual environment -FROM python:3.12.2-slim-bookworm as runtime ARG LETTA_ENVIRONMENT=PRODUCTION -ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} -ENV VIRTUAL_ENV=/app/.venv \ - PATH="/app/.venv/bin:$PATH" +ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} \ + VIRTUAL_ENV="/app/.venv" \ + PATH="/app/.venv/bin:$PATH" \ + POSTGRES_USER=letta \ + POSTGRES_PASSWORD=letta \ + POSTGRES_DB=letta -COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV} +WORKDIR /app -COPY ./letta /letta -COPY ./alembic.ini /alembic.ini -COPY ./alembic /alembic +# Copy virtual environment and app from builder +COPY --from=builder /app . -EXPOSE 8283 +# Copy initialization SQL if it exists +COPY init.sql /docker-entrypoint-initdb.d/ -CMD ./letta/server/startup.sh +EXPOSE 8283 5432 -# allow for in-container development and testing -FROM builder as development -ARG LETTA_ENVIRONMENT=PRODUCTION -ENV LETTA_ENVIRONMENT=${LETTA_ENVIRONMENT} -ENV VIRTUAL_ENV=/app/.venv \ - PATH="/app/.venv/bin:$PATH" -ENV PYTHONPATH=/ -WORKDIR / -COPY ./tests /tests -COPY ./letta /letta -COPY ./alembic.ini /alembic.ini -COPY ./alembic /alembic -#COPY ./configs/server_config.yaml /root/.letta/config -EXPOSE 8083 - -CMD ./letta/server/startup.sh +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD ["./letta/server/startup.sh"] diff --git a/letta/cli/cli.py b/letta/cli/cli.py index d55d3b74..bedab429 100644 --- a/letta/cli/cli.py +++ b/letta/cli/cli.py @@ -51,6 +51,7 @@ def server( port: Annotated[Optional[int], typer.Option(help="Port to run the server on")] = None, host: Annotated[Optional[str], typer.Option(help="Host to run the server on (default to localhost)")] = None, debug: Annotated[bool, typer.Option(help="Turn debugging output on")] = False, + ade: Annotated[bool, typer.Option(help="Allows remote access")] = False, # NOTE: deprecated secure: Annotated[bool, typer.Option(help="Adds simple security access")] = False, ): """Launch a Letta server process""" diff --git a/letta/server/startup.sh b/letta/server/startup.sh index e0556f70..2e9d7c30 100755 --- a/letta/server/startup.sh +++ b/letta/server/startup.sh @@ -1,17 +1,49 @@ #!/bin/sh -echo "Starting Letta server at http://localhost:8283" +set -e # Exit on any error -# Check if LETTA_PG_URI or LETTA_PG_DB is set and run alembic upgrade if either is -if [ -n "$LETTA_PG_URI" ] || [ -n "$LETTA_PG_DB" ]; then - echo "LETTA_PG_URI or LETTA_PG_DB is set, running alembic upgrade head" - alembic upgrade head -fi +HOST="${HOST:-0.0.0.0}" +PORT="${PORT:-8283}" -if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ]; then - echo "Starting in development mode!" - uvicorn letta.server.rest_api.app:app --reload --reload-dir /letta --host 0.0.0.0 --port 8283 +# Function to wait for PostgreSQL to be ready +wait_for_postgres() { + until pg_isready -U "${POSTGRES_USER:-letta}" -h localhost; do + echo "Waiting for PostgreSQL to be ready..." + sleep 2 + done +} + +# Check if we're configured for external Postgres +if [ -n "$LETTA_PG_URI" ]; then + echo "External Postgres configuration detected, using $LETTA_PG_URI" else - # Production start command here (replace with the actual production command) - echo "Starting in production mode!" - uvicorn letta.server.rest_api.app:app --host 0.0.0.0 --port 8283 + echo "No external Postgres configuration detected, starting internal PostgreSQL..." + # Start PostgreSQL using the base image's entrypoint script + /usr/local/bin/docker-entrypoint.sh postgres & + + # Wait for PostgreSQL to be ready + wait_for_postgres + + # Set default connection URI for internal postgres + export LETTA_PG_URI="postgresql://${POSTGRES_USER:-letta}:${POSTGRES_PASSWORD:-letta}@localhost:5432/${POSTGRES_DB:-letta}" + echo "Using internal PostgreSQL at: $LETTA_PG_URI" fi + +# Attempt database migration +echo "Attempting to migrate database..." +if ! alembic upgrade head; then + echo "ERROR: Database migration failed!" + echo "Please check your database connection and try again." + echo "If the problem persists, check the logs for more details." + exit 1 +fi +echo "Database migration completed successfully." + +# If ADE is enabled, add the --ade flag to the command +CMD="letta server --host $HOST --port $PORT" +if [ "${SECURE:-false}" = "true" ]; then + CMD="$CMD --secure" +fi + +echo "Starting Letta server at http://$HOST:$PORT..." +echo "Executing: $CMD" +exec $CMD