diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e287e2..24bc0a0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,18 +18,18 @@ repos: hooks: - id: ruff-check name: Ruff Linter - entry: bash -c 'cd brain && poetry run ruff check --fix' + entry: bash -c 'make lint' language: system types: [python] - id: ruff-format name: Ruff Formatter - entry: bash -c 'cd brain && poetry run ruff format' + entry: bash -c 'make format' language: system types: [python] - id: system-pytest name: Pytest - entry: bash -c 'cd brain && poetry run pytest -n auto --dist=loadscope' + entry: bash -c 'make test' language: system always_run: true diff --git a/Makefile b/Makefile index 8134849..75ecc56 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ # --- SETTINGS --- # Change to 'uv' when ready. -RUNNER = poetry +RUNNER ?= poetry +export RUNNER # --- VARIABLES --- CORE_DIR = brain @@ -19,7 +20,7 @@ INSTALL_CMD = $(if $(filter uv,$(RUNNER)),sync,install) ARGS = $(filter-out $@,$(MAKECMDGOALS)) BUMP_CMD = cd $(CORE_DIR) && $(RUNNER) run bump-my-version bump COMPOSE_CMD = docker-compose -DOCKER_CMD = cd $(CORE_DIR) && docker build -t $(IMAGE_NAME):latest . +DOCKER_CMD = cd $(CORE_DIR) && docker build --build-arg RUNNER=$(RUNNER) -t $(IMAGE_NAME):latest . RUNNER_ADD = cd $(CORE_DIR) && $(RUNNER) add RUNNER_HOOKS = cd $(CORE_DIR) && $(RUNNER) run pre-commit install -c ../.pre-commit-config.yaml @@ -195,7 +196,7 @@ shell: check-docker test: check-runner @echo "$(T)๐Ÿงช Running tests...$(R)" - $(RUNNER_RUN) pytest $(ARGS) + $(RUNNER_RUN) pytest -n auto --dist=loadscope $(ARGS) up: check-docker @echo "$(T)๐Ÿš€ Starting Agent Media...$(R)" diff --git a/brain/Dockerfile b/brain/Dockerfile index a00ef64..d18e4b9 100644 --- a/brain/Dockerfile +++ b/brain/Dockerfile @@ -1,47 +1,56 @@ # Dockerfile for Agent Media # Multi-stage build for smaller image size - +ARG PYTHON_VERSION=3.14.2 +ARG RUNNER # =========================================== # Stage 1: Builder # =========================================== -FROM python:3.12.7-slim as builder +FROM python:${PYTHON_VERSION}-slim-bookworm as builder -# STFU (please) -ENV DEBIAN_FRONTEND=noninteractive +# STFU - No need - Write logs asap +ENV DEBIAN_FRONTEND=noninteractive \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 # Install build dependencies (needs root) RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* -# Install Poetry globally (needs root) -RUN pip install --no-cache-dir poetry +# Install runner globally (needs root) - Save cache for future +RUN --mount=type=cache,target=/root/.cache/pip \ + pip install $RUNNER -# Copy dependency files (as root for now) -COPY pyproject.toml poetry.lock* /tmp/ +# Set working directory for dependency installation +WORKDIR /tmp + +# Copy dependency files +COPY pyproject.toml poetry.lock* uv.lock* Makefile ./ # Install dependencies as root (to avoid permission issues with system packages) -WORKDIR /tmp -RUN poetry config virtualenvs.create false \ - && poetry install --only main --no-root --no-cache - -# Create non-root user -RUN useradd -m -u 1000 -s /bin/bash appuser - -# Switch to non-root user -USER appuser - -# Set working directory (owned by appuser) -WORKDIR /home/appuser/app +RUN --mount=type=cache,target=/root/.cache/pip \ + --mount=type=cache,target=/root/.cache/pypoetry \ + --mount=type=cache,target=/root/.cache/uv \ + if [ "$RUNNER" = "poetry" ]; then \ + poetry config virtualenvs.create false && \ + poetry install --only main --no-root; \ + elif [ "$RUNNER" = "uv" ]; then \ + uv pip install --system -r pyproject.toml; \ + fi # =========================================== # Stage 2: Runtime # =========================================== -FROM python:3.12.7-slim as runtime +FROM python:${PYTHON_VERSION}-slim-bookworm as runtime + +ENV LLM_PROVIDER=deepseek \ + MEMORY_STORAGE_DIR=/data/memory \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONPATH=/home/appuser/app \ + PYTHONUNBUFFERED=1 # Install runtime dependencies (needs root) RUN apt-get update && apt-get install -y --no-install-recommends \ - curl \ ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && apt-get clean @@ -78,14 +87,7 @@ EXPOSE 8000 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD curl -f http://localhost:8000/health || exit 1 - -# Environment variables (can be overridden) -ENV PYTHONUNBUFFERED=1 \ - PYTHONDONTWRITEBYTECODE=1 \ - PYTHONPATH=/home/appuser/app \ - LLM_PROVIDER=deepseek \ - MEMORY_STORAGE_DIR=/data/memory + CMD python -c "import requests; requests.get('http://localhost:8000/health', timeout=5).raise_for_status()" || exit 1 # Run the application CMD ["python", "-m", "uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.yml b/docker-compose.yml index 22d2291..547c1dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,8 @@ services: build: context: ./brain dockerfile: Dockerfile + args: + RUNNER: ${RUNNER} # Get it from Makefile container_name: agent-brain restart: unless-stopped env_file: .env @@ -49,21 +51,21 @@ services: environment: # MongoDB connection (no auth, matching LibreChat default) MONGO_URI: mongodb://mongodb:27017/LibreChat - + # App configuration HOST: 0.0.0.0 PORT: 3080 - + # Security JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-change-this-in-production} JWT_REFRESH_SECRET: ${JWT_REFRESH_SECRET:-your-super-secret-refresh-key-change-this-too} CREDS_KEY: ${CREDS_KEY:-your-32-character-secret-key-here} CREDS_IV: ${CREDS_IV:-your-16-character-iv-here} - + # Session SESSION_EXPIRY: ${SESSION_EXPIRY:-1000 * 60 * 15} REFRESH_TOKEN_EXPIRY: ${REFRESH_TOKEN_EXPIRY:-1000 * 60 * 60 * 24 * 7} - + # Domain DOMAIN_CLIENT: ${DOMAIN_CLIENT:-http://localhost:3080} DOMAIN_SERVER: ${DOMAIN_SERVER:-http://localhost:3080} @@ -201,4 +203,4 @@ volumes: # Network for inter-service communication networks: agent-network: - driver: bridge \ No newline at end of file + driver: bridge