diff --git a/Dockerfile-litellm b/Dockerfile-litellm new file mode 100644 index 000000000..e44198aba --- /dev/null +++ b/Dockerfile-litellm @@ -0,0 +1,122 @@ +# syntax=docker/dockerfile:1-labs + +FROM node:20-alpine AS node_base + +FROM node_base AS node_deps +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci --legacy-peer-deps + +FROM node_base AS node_builder +WORKDIR /app +COPY --from=node_deps /app/node_modules ./node_modules +COPY --exclude=./api . . +RUN NODE_ENV=production npm run build + +FROM python:3.11-slim AS py_deps +WORKDIR /api +COPY api/pyproject.toml . +COPY api/poetry.lock . +RUN python -m pip install poetry==2.0.1 --no-cache-dir && \ + poetry config virtualenvs.create true --local && \ + poetry config virtualenvs.in-project true --local && \ + poetry config virtualenvs.options.always-copy --local true && \ + POETRY_MAX_WORKERS=10 poetry install --no-interaction --no-ansi --only main && \ + poetry cache clear --all . + +FROM python:3.11-slim AS ollama_base +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl zstd && rm -rf /var/lib/apt/lists/* + +# Detect architecture and download appropriate Ollama version +# ARG TARGETARCH can be set at build time with --build-arg TARGETARCH=arm64 or TARGETARCH=amd64 +ARG TARGETARCH +RUN OLLAMA_ARCH="" && \ + if [ "$TARGETARCH" = "arm64" ]; then \ + echo "Building for ARM64 architecture." && \ + OLLAMA_ARCH="arm64"; \ + elif [ "$TARGETARCH" = "amd64" ]; then \ + echo "Building for AMD64 architecture." && \ + OLLAMA_ARCH="amd64"; \ + else \ + echo "Error: Unsupported architecture '$TARGETARCH'. Supported architectures are 'arm64' and 'amd64'." >&2 && \ + exit 1; \ + fi && \ + (set -o pipefail; \ + curl -fL "https://ollama.com/download/ollama-linux-${OLLAMA_ARCH}.tar.zst" \ + | zstd -d | tar -x -C /usr) + +RUN ollama serve > /dev/null 2>&1 & \ + sleep 20 && \ + ollama pull nomic-embed-text && \ + ollama pull qwen3:1.7b + +# Use Python 3.11 as final image +FROM python:3.11-slim + +# Set working directory +WORKDIR /app + +# Install Node.js and npm +RUN apt-get update && apt-get install -y \ + curl \ + gnupg \ + git \ + ca-certificates \ + && mkdir -p /etc/apt/keyrings \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ENV PATH="/opt/venv/bin:$PATH" + +# Copy Python dependencies +COPY --from=py_deps /api/.venv /opt/venv +COPY api/ ./api/ + +# Copy Node app +COPY --from=node_builder /app/public ./public +COPY --from=node_builder /app/.next/standalone ./ +COPY --from=node_builder /app/.next/static ./.next/static +COPY --from=ollama_base /usr/bin/ollama /usr/local/bin/ +COPY --from=ollama_base /root/.ollama /root/.ollama + +# Expose the port the app runs on +EXPOSE ${PORT:-8001} 3000 + +# Create a script to run both backend and frontend +RUN echo '#!/bin/bash\n\ +# Start ollama serve in background\n\ +ollama serve > /dev/null 2>&1 &\n\ +\n\ +# Load environment variables from .env file if it exists\n\ +if [ -f .env ]; then\n\ + export $(grep -v "^#" .env | xargs -r)\n\ +fi\n\ +\n\ +# Check for required environment variables\n\ +if [ -z "$OPENAI_API_KEY" ] || [ -z "$GOOGLE_API_KEY" ]; then\n\ + echo "Warning: OPENAI_API_KEY and/or GOOGLE_API_KEY environment variables are not set."\n\ + echo "These are required for DeepWiki to function properly."\n\ + echo "You can provide them via a mounted .env file or as environment variables when running the container."\n\ +fi\n\ +\n\ +# Start the API server in the background with the configured port\n\ +python -m api.main --port ${PORT:-8001} &\n\ +PORT=3000 HOSTNAME=0.0.0.0 node server.js &\n\ +wait -n\n\ +exit $?' > /app/start.sh && chmod +x /app/start.sh + +# Set environment variables +ENV PORT=8001 +ENV NODE_ENV=production +ENV SERVER_BASE_URL=http://localhost:${PORT:-8001} + +# Create empty .env file (will be overridden if one exists at runtime) +RUN touch .env + +# Command to run the application +CMD ["/app/start.sh"] diff --git a/docker-compose-litellm.env b/docker-compose-litellm.env new file mode 100644 index 000000000..0a246ec4e --- /dev/null +++ b/docker-compose-litellm.env @@ -0,0 +1,7 @@ +# Same Compose network http://litellm:4000 +# LiteLLM on host OS http://host.docker.internal:4000 +# LiteLLM on another server http://server-ip:4000 +LITELLM_BASE_URL=http://litellm:4000 +# Use env. variable LITELLM_API_KEY +# But if it doesn't exist, default to `sk-1234` +LITELLM_API_KEY=${LITELLM_API_KEY:-sk-1234} \ No newline at end of file diff --git a/docker-compose-litellm.yml b/docker-compose-litellm.yml new file mode 100644 index 000000000..6f90878a2 --- /dev/null +++ b/docker-compose-litellm.yml @@ -0,0 +1,70 @@ +services: + db: + image: postgres:16 + environment: + POSTGRES_DB: litellm + POSTGRES_USER: litellm + # Use env. variable LITELLM_DB_PASSWORD + # otherwise default to litellm_password + POSTGRES_PASSWORD: ${LITELLM_DB_PASSWORD:-litellm_password} + ports: + - "5432:5432" + volumes: + - deepwiki_litellm_db:/var/lib/postgresql/data + + litellm: + image: ghcr.io/berriai/litellm:v1.83.10-stable + ports: + - "4000:4000" + volumes: + - ./litellm-config.yml:/app/config.yaml + environment: + # Use env. variable LITELLM_MASTER_KEY + # otherwise default to sk-1234 + - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY:-sk-1234} + # Using from litellm-config.yml + #environment: + # DATABASE_URL: postgres://litellm:litellm_password@db:5432/litellm + command: [ "--config", "/app/config.yaml", "--port", "4000" ] + extra_hosts: + - "host.docker.internal:host-gateway" + depends_on: + - db + + deepwiki: + image: deepwiki-litellm # Using image name to show it depends on LiteLLM + build: + context: . + dockerfile: Dockerfile-litellm # ← Using Dockerfile that depends on LiteLLM + ports: + - "${PORT:-8001}:${PORT:-8001}" # API port + - "3000:3000" # Next.js port + env_file: + - docker-compose-litellm.env + environment: + - PORT=${PORT:-8001} + - NODE_ENV=production + - SERVER_BASE_URL=http://localhost:${PORT:-8001} + - LOG_LEVEL=${LOG_LEVEL:-INFO} + - LOG_FILE_PATH=${LOG_FILE_PATH:-api/logs/application.log} + # For LiteLLM + - LITELLM_BASE_URL=${LITELLM_BASE_URL} + - LITELLM_API_KEY=${LITELLM_API_KEY} + volumes: + - ~/.adalflow:/root/.adalflow # Persist repository and embedding data + - ./api/logs:/app/api/logs # Persist log files across container restarts + # Resource limits for docker-compose up (not Swarm mode) + mem_limit: 6g + mem_reservation: 2g + # Health check configuration + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:${PORT:-8001}/health"] + interval: 60s + timeout: 10s + retries: 3 + start_period: 30s + depends_on: + - litellm + +volumes: + deepwiki_litellm_db: \ No newline at end of file diff --git a/litellm-config.yml b/litellm-config.yml new file mode 100644 index 000000000..004bccdea --- /dev/null +++ b/litellm-config.yml @@ -0,0 +1,26 @@ +model_list: + # ----------------------- + # Chat / Generation models + # ----------------------- + - model_name: qwen3:1.7b #Can be named anything - qwen3-1.7b + litellm_params: + model: ollama/qwen3:1.7b + api_base: http://host.docker.internal:11434 + api_key: "not-needed" + + # ----------------------- + # Embedding models + # ----------------------- + - model_name: nomic-embed-text + litellm_params: + model: ollama/nomic-embed-text + api_base: http://host.docker.internal:11434 + api_key: "not-needed" + +# This overrides the one in the docker-compose file +general_settings: + database_url: postgres://litellm:litellm_password@db:5432/litellm + +litellm_settings: + set_verbose: true + drop_params: true