From 37887a175aa03f07f870d11473fdcbe4cdc00047 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sat, 23 Nov 2024 21:13:51 -0800 Subject: [PATCH 01/23] Speed up Docker image builds using multi-stage parallel pipelines Decouple web app, server builds in parallel to speed up Docker builds --- Dockerfile | 44 +++++++++++++++++++++++--------------------- prod.Dockerfile | 42 ++++++++++++++++++++++-------------------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/Dockerfile b/Dockerfile index b481a8e6..458038f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM ubuntu:jammy +FROM ubuntu:jammy AS base LABEL homepage="https://khoj.dev" LABEL repository="https://github.com/khoj-ai/khoj" LABEL org.opencontainers.image.source="https://github.com/khoj-ai/khoj" @@ -10,44 +10,46 @@ RUN apt update -y && apt -y install \ python3-pip \ swig \ curl \ - # Required by llama-cpp-python pre-built wheels. See #1628 - musl-dev \ # Required by RapidOCR libgl1 \ libglx-mesa0 \ - libglib2.0-0 && \ - # Required by Next.js Web app - curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ - apt update -y && apt -y --no-install-recommends install nodejs yarn && \ - apt clean && rm -rf /var/lib/apt/lists/* && \ + libglib2.0-0 \ # Required by llama-cpp-python pre-built wheels. See #1628 - ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 + musl-dev && \ + ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 && \ + # Clean up + apt clean && rm -rf /var/lib/apt/lists/* -# Install Application +# Build Server +FROM base AS server-deps WORKDIR /app COPY pyproject.toml . COPY README.md . ARG VERSION=0.0.0 +# use the pre-built llama-cpp-python cpu wheel ENV PIP_EXTRA_INDEX_URL=https://abetlen.github.io/llama-cpp-python/whl/cpu RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \ pip install --no-cache-dir . -# Copy Source Code -COPY . . - -# Set the PYTHONPATH environment variable in order for it to find the Django app. -ENV PYTHONPATH=/app/src:$PYTHONPATH - -# Go to the directory src/interface/web and export the built Next.js assets +# Build Web App +FROM node:20-alpine AS web-app +COPY src/interface/web /app/src/interface/web WORKDIR /app/src/interface/web -RUN bash -c "yarn install --frozen-lockfile && yarn ciexport && yarn cache clean" +RUN yarn install --frozen-lockfile && \ + yarn build + +# Merge the Server and Web App into a Single Image +FROM base +ENV PYTHONPATH=/app/src WORKDIR /app +COPY --from=server-deps /usr/local/lib/python3.10/dist-packages /usr/local/lib/python3.10/dist-packages +COPY --from=web-app /app/src/interface/web/out ./src/khoj/interface/built +COPY . . +RUN cd src && python3 khoj/manage.py collectstatic --noinput # Run the Application # There are more arguments required for the application to run, -# but these should be passed in through the docker-compose.yml file. +# but those should be passed in through the docker-compose.yml file. ARG PORT EXPOSE ${PORT} ENTRYPOINT ["python3", "src/khoj/main.py"] diff --git a/prod.Dockerfile b/prod.Dockerfile index 8c467b93..f0911a05 100644 --- a/prod.Dockerfile +++ b/prod.Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM ubuntu:jammy +FROM ubuntu:jammy AS base LABEL homepage="https://khoj.dev" LABEL repository="https://github.com/khoj-ai/khoj" LABEL org.opencontainers.image.source="https://github.com/khoj-ai/khoj" @@ -16,38 +16,40 @@ RUN apt update -y && apt -y install \ curl \ # Required by llama-cpp-python pre-built wheels. See #1628 musl-dev && \ - # Required by Next.js Web app - curl -sL https://deb.nodesource.com/setup_20.x | bash - && \ - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ - apt update -y && apt -y --no-install-recommends install nodejs yarn && \ - apt clean && rm -rf /var/lib/apt/lists/* && \ - # Required by llama-cpp-python pre-built wheels. See #1628 - ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 + ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 && \ + # Clean up + apt clean && rm -rf /var/lib/apt/lists/* -# Install Application +# Build Server +FROM base AS server-deps WORKDIR /app COPY pyproject.toml . COPY README.md . ARG VERSION=0.0.0 +# use the pre-built llama-cpp-python cpu wheel ENV PIP_EXTRA_INDEX_URL=https://abetlen.github.io/llama-cpp-python/whl/cpu RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \ - pip install --no-cache-dir -e .[prod] + pip install --no-cache-dir .[prod] -# Copy Source Code -COPY . . - -# Set the PYTHONPATH environment variable in order for it to find the Django app. -ENV PYTHONPATH=/app/src:$PYTHONPATH - -# Go to the directory src/interface/web and export the built Next.js assets +# Build Web App +FROM node:20-alpine AS web-app +COPY src/interface/web /app/src/interface/web WORKDIR /app/src/interface/web -RUN bash -c "yarn install --frozen-lockfile && yarn ciexport && yarn cache clean" +RUN yarn install --frozen-lockfile && \ + yarn build + +# Merge the Server and Web App into a Single Image +FROM base +ENV PYTHONPATH=/app/src WORKDIR /app +COPY --from=server-deps /usr/local/lib/python3.10/dist-packages /usr/local/lib/python3.10/dist-packages +COPY --from=web-app /app/src/interface/web/out ./src/khoj/interface/built +COPY . . +RUN cd src && python3 khoj/manage.py collectstatic --noinput # Run the Application # There are more arguments required for the application to run, -# but these should be passed in through the docker-compose.yml file. +# but those should be passed in through the docker-compose.yml file. ARG PORT EXPOSE ${PORT} ENTRYPOINT ["gunicorn", "-c", "gunicorn-config.py", "src.khoj.main:app"] From 78d8ca49ec18e1da1237f67edf66c07e22174803 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sat, 23 Nov 2024 22:24:45 -0800 Subject: [PATCH 02/23] Skip Nvidia GPU python packages during Server install in Dockerfiles --- Dockerfile | 6 ++++-- prod.Dockerfile | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 458038f7..202d1e92 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,8 +26,10 @@ WORKDIR /app COPY pyproject.toml . COPY README.md . ARG VERSION=0.0.0 -# use the pre-built llama-cpp-python cpu wheel -ENV PIP_EXTRA_INDEX_URL=https://abetlen.github.io/llama-cpp-python/whl/cpu +# use the pre-built llama-cpp-python, torch cpu wheel +ENV PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu https://abetlen.github.io/llama-cpp-python/whl/cpu" +# avoid downloading unused cuda specific python packages +ENV CUDA_VISIBLE_DEVICES="" RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \ pip install --no-cache-dir . diff --git a/prod.Dockerfile b/prod.Dockerfile index f0911a05..07a5c0d4 100644 --- a/prod.Dockerfile +++ b/prod.Dockerfile @@ -26,8 +26,10 @@ WORKDIR /app COPY pyproject.toml . COPY README.md . ARG VERSION=0.0.0 -# use the pre-built llama-cpp-python cpu wheel -ENV PIP_EXTRA_INDEX_URL=https://abetlen.github.io/llama-cpp-python/whl/cpu +# use the pre-built llama-cpp-python, torch cpu wheel +ENV PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu https://abetlen.github.io/llama-cpp-python/whl/cpu" +# avoid downloading unused cuda specific python packages +ENV CUDA_VISIBLE_DEVICES="" RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \ pip install --no-cache-dir .[prod] From 4b486ea5f6dcd93ae178615c87d7b5756261c83c Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sun, 24 Nov 2024 00:24:23 -0800 Subject: [PATCH 03/23] Exclude unnecessary directories from final docker builds --- .dockerignore | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.dockerignore b/.dockerignore index 8802acb5..6668a7b5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,11 @@ -.git/ -.pytest_cache/ -.vscode/ -.venv/ -docs/ +.* +**/__pycache__/ +*.egg-info/ +documentation/ tests/ build/ dist/ scripts/ -*.egg-info/ +src/interface/ +src/telemetry/ +!src/interface/web From 710e00ad9e951e35f09a3a738e7a41deb6d297e0 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sun, 24 Nov 2024 12:53:01 -0800 Subject: [PATCH 04/23] Make tailwind extensions prod, instead of dev, deps of web app --- src/interface/web/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interface/web/package.json b/src/interface/web/package.json index 710daec6..bcc8e5e7 100644 --- a/src/interface/web/package.json +++ b/src/interface/web/package.json @@ -62,6 +62,9 @@ "react-hook-form": "^7.52.1", "shadcn-ui": "^0.8.0", "swr": "^2.2.5", + "tailwind-merge": "^2.3.0", + "tailwindcss": "^3.4.6", + "tailwindcss-animate": "^1.0.7", "typescript": "^5", "vaul": "^0.9.1", "zod": "^3.23.8" @@ -82,9 +85,6 @@ "lint-staged": "^15.2.7", "nodemon": "^3.1.3", "prettier": "3.3.3", - "tailwind-merge": "^2.3.0", - "tailwindcss": "^3.4.6", - "tailwindcss-animate": "^1.0.7", "typescript": "^5" }, "prettier": { From f51e0f7859023b0c9f7acbe657f7cccb0407d3c5 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sun, 24 Nov 2024 12:59:17 -0800 Subject: [PATCH 05/23] Make Next.js create production builds of web app for Docker images --- Dockerfile | 3 +++ prod.Dockerfile | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Dockerfile b/Dockerfile index 202d1e92..597e3d47 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,9 @@ RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.tom # Build Web App FROM node:20-alpine AS web-app +# Set build optimization env vars +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 COPY src/interface/web /app/src/interface/web WORKDIR /app/src/interface/web RUN yarn install --frozen-lockfile && \ diff --git a/prod.Dockerfile b/prod.Dockerfile index 07a5c0d4..82948445 100644 --- a/prod.Dockerfile +++ b/prod.Dockerfile @@ -35,6 +35,9 @@ RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.tom # Build Web App FROM node:20-alpine AS web-app +# Set build optimization env vars +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 COPY src/interface/web /app/src/interface/web WORKDIR /app/src/interface/web RUN yarn install --frozen-lockfile && \ From 6a39651ad3974272daf5865586af98c5ff091355 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Sun, 24 Nov 2024 15:02:14 -0800 Subject: [PATCH 06/23] Standardize loading fonts locally across pages on web app --- src/interface/web/app/agents/layout.tsx | 8 +++----- src/interface/web/app/chat/layout.tsx | 8 +++----- src/interface/web/app/fonts.ts | 13 +++++++++++++ src/interface/web/app/globals.css | 5 ++--- src/interface/web/app/layout.tsx | 8 +++----- src/interface/web/app/settings/layout.tsx | 8 +++----- src/interface/web/app/share/chat/layout.tsx | 8 +++----- src/interface/web/tailwind.config.ts | 3 +++ 8 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 src/interface/web/app/fonts.ts diff --git a/src/interface/web/app/agents/layout.tsx b/src/interface/web/app/agents/layout.tsx index b30c782d..c7779b06 100644 --- a/src/interface/web/app/agents/layout.tsx +++ b/src/interface/web/app/agents/layout.tsx @@ -1,9 +1,7 @@ import type { Metadata } from "next"; -import { Noto_Sans } from "next/font/google"; +import { noto_sans, noto_sans_arabic } from "@/app/fonts"; import "../globals.css"; -const inter = Noto_Sans({ subsets: ["latin"] }); - export const metadata: Metadata = { title: "Khoj AI - Agents", description: "Find a specialized agent that can help you address more specific needs.", @@ -33,7 +31,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} + {children} ); } diff --git a/src/interface/web/app/chat/layout.tsx b/src/interface/web/app/chat/layout.tsx index 09c3afb7..4e4661f1 100644 --- a/src/interface/web/app/chat/layout.tsx +++ b/src/interface/web/app/chat/layout.tsx @@ -1,9 +1,7 @@ import type { Metadata } from "next"; -import { Noto_Sans } from "next/font/google"; +import { noto_sans, noto_sans_arabic } from "@/app/fonts"; import "../globals.css"; -const inter = Noto_Sans({ subsets: ["latin"] }); - export const metadata: Metadata = { title: "Khoj AI - Chat", description: @@ -34,7 +32,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - + {children}