forked from HKUDS/DeepTutor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
346 lines (286 loc) · 10 KB
/
Dockerfile
File metadata and controls
346 lines (286 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# ============================================
# DeepTutor Multi-Stage Dockerfile
# ============================================
# This Dockerfile builds a production-ready image for DeepTutor
# containing both the FastAPI backend and Next.js frontend
#
# Build: docker compose build
# Run: docker compose up -d
#
# Prerequisites:
# 1. Copy .env.example to .env and configure your API keys
# 2. Optionally customize config/main.yaml
# ============================================
# ============================================
# Stage 1: Frontend Builder
# ============================================
FROM node:20-slim AS frontend-builder
WORKDIR /app/web
# Accept build argument for backend port
ARG BACKEND_PORT=8001
# Copy package files first for better caching
COPY web/package.json web/package-lock.json* ./
# Install dependencies
RUN npm ci --legacy-peer-deps
# Copy frontend source code
COPY web/ ./
# Create .env.local with placeholder that will be replaced at runtime
# Use a unique placeholder that can be safely replaced
RUN echo "NEXT_PUBLIC_API_BASE=__NEXT_PUBLIC_API_BASE_PLACEHOLDER__" > .env.local
# Build Next.js for production with standalone output
# This allows runtime environment variable injection
RUN npm run build
# ============================================
# Stage 2: Python Base with Dependencies
# ============================================
FROM python:3.11-slim AS python-base
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
# Install system dependencies
# Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
build-essential \
libgl1 \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender1 \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements and install Python dependencies
COPY requirements.txt ./
RUN pip install --upgrade pip && \
pip install -r requirements.txt
# ============================================
# Stage 3: Production Image
# ============================================
FROM python:3.11-slim AS production
# Labels
LABEL maintainer="DeepTutor Team" \
description="DeepTutor: AI-Powered Personalized Learning Assistant" \
version="0.1.0"
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8 \
NODE_ENV=production \
# Default ports (can be overridden)
BACKEND_PORT=8001 \
FRONTEND_PORT=3782
WORKDIR /app
# Install system dependencies
# Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
ca-certificates \
supervisor \
libgl1 \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender1 \
&& rm -rf /var/lib/apt/lists/*
# Copy Node.js from frontend-builder stage (avoids re-downloading from NodeSource)
COPY --from=frontend-builder /usr/local/bin/node /usr/local/bin/node
COPY --from=frontend-builder /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -sf /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -sf /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
&& node --version && npm --version
# Copy Python packages from builder stage
COPY --from=python-base /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=python-base /usr/local/bin /usr/local/bin
# Copy built frontend from frontend-builder stage
COPY --from=frontend-builder /app/web/.next ./web/.next
COPY --from=frontend-builder /app/web/public ./web/public
COPY --from=frontend-builder /app/web/package.json ./web/package.json
COPY --from=frontend-builder /app/web/next.config.js ./web/next.config.js
COPY --from=frontend-builder /app/web/node_modules ./web/node_modules
# Copy application source code
COPY src/ ./src/
COPY config/ ./config/
COPY scripts/ ./scripts/
COPY pyproject.toml ./
COPY requirements.txt ./
# Create necessary directories (these will be overwritten by volume mounts)
RUN mkdir -p \
data/user/solve \
data/user/question \
data/user/research/cache \
data/user/research/reports \
data/user/guide \
data/user/notebook \
data/user/co-writer/audio \
data/user/co-writer/tool_calls \
data/user/logs \
data/user/run_code_workspace \
data/user/performance \
data/knowledge_bases
# Create supervisord configuration for running both services
# Log output goes to stdout/stderr so docker logs can capture them
RUN mkdir -p /etc/supervisor/conf.d
COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
[program:backend]
command=/bin/bash /app/start-backend.sh
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
[program:frontend]
command=/bin/bash /app/start-frontend.sh
directory=/app/web
autostart=true
autorestart=true
startsecs=5
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=NODE_ENV="production"
EOF
# Create backend startup script
COPY <<'EOF' /app/start-backend.sh
#!/bin/bash
set -e
BACKEND_PORT=${BACKEND_PORT:-8001}
echo "[Backend] 🚀 Starting FastAPI backend on port ${BACKEND_PORT}..."
# Run uvicorn directly - the application's logging system already handles:
# 1. Console output (visible in docker logs)
# 2. File logging to data/user/logs/ai_tutor_*.log
exec python -m uvicorn src.api.main:app --host 0.0.0.0 --port ${BACKEND_PORT}
EOF
RUN chmod +x /app/start-backend.sh
# Create frontend startup script
# This script handles runtime environment variable injection for Next.js
COPY <<'EOF' /app/start-frontend.sh
#!/bin/bash
set -e
# Get the backend port (default to 8001)
BACKEND_PORT=${BACKEND_PORT:-8001}
FRONTEND_PORT=${FRONTEND_PORT:-3782}
# Determine the API base URL
if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then
API_BASE="$NEXT_PUBLIC_API_BASE_EXTERNAL"
else
API_BASE="http://localhost:${BACKEND_PORT}"
fi
echo "[Frontend] 🚀 Starting Next.js frontend on port ${FRONTEND_PORT}..."
echo "[Frontend] 📌 API base URL: ${API_BASE}"
# Replace placeholder in built Next.js files
# This is necessary because NEXT_PUBLIC_* vars are inlined at build time
find /app/web/.next -type f \( -name "*.js" -o -name "*.json" \) -exec \
sed -i "s|__NEXT_PUBLIC_API_BASE_PLACEHOLDER__|${API_BASE}|g" {} \; 2>/dev/null || true
# Also update .env.local for any runtime reads
echo "NEXT_PUBLIC_API_BASE=${API_BASE}" > /app/web/.env.local
# Start Next.js
cd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT}
EOF
RUN chmod +x /app/start-frontend.sh
# Create entrypoint script
COPY <<'EOF' /app/entrypoint.sh
#!/bin/bash
set -e
echo "============================================"
echo "🚀 Starting DeepTutor"
echo "============================================"
# Set default ports if not provided
export BACKEND_PORT=${BACKEND_PORT:-8001}
export FRONTEND_PORT=${FRONTEND_PORT:-3782}
echo "📌 Backend Port: ${BACKEND_PORT}"
echo "📌 Frontend Port: ${FRONTEND_PORT}"
# Check for required environment variables
if [ -z "$LLM_BINDING_API_KEY" ]; then
echo "⚠️ Warning: LLM_BINDING_API_KEY not set"
echo " Please provide LLM configuration via environment variables or .env file"
fi
if [ -z "$LLM_MODEL" ]; then
echo "⚠️ Warning: LLM_MODEL not set"
echo " Please configure LLM_MODEL in your .env file"
fi
# Initialize user data directories if empty
echo "📁 Checking data directories..."
if [ ! -f "/app/data/user/user_history.json" ]; then
echo " Initializing user data directories..."
python -c "
from pathlib import Path
from src.core.setup import init_user_directories
init_user_directories(Path('/app'))
" 2>/dev/null || echo " ⚠️ Directory initialization skipped (will be created on first use)"
fi
echo "============================================"
echo "📦 Configuration loaded from:"
echo " - Environment variables (.env file)"
echo " - config/main.yaml"
echo " - config/agents.yaml"
echo "============================================"
# Start supervisord
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/deeptutor.conf
EOF
RUN chmod +x /app/entrypoint.sh
# Expose ports
EXPOSE 8001 3782
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:${BACKEND_PORT:-8001}/ || exit 1
# Set entrypoint
ENTRYPOINT ["/app/entrypoint.sh"]
# ============================================
# Stage 4: Development Image (Optional)
# ============================================
FROM production AS development
# Install development tools
RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
git \
&& rm -rf /var/lib/apt/lists/*
# Install development Python packages
RUN pip install --no-cache-dir \
pre-commit \
black \
ruff
# Override supervisord config for development (with reload)
# Log output goes to stdout/stderr so docker logs can capture them
COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/var/run/supervisord.pid
[program:backend]
command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s --reload
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
[program:frontend]
command=/bin/bash -c "cd /app/web && node node_modules/next/dist/bin/next dev -H 0.0.0.0 -p ${FRONTEND_PORT:-3782}"
directory=/app/web
autostart=true
autorestart=true
startsecs=5
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
stderr_logfile=/dev/fd/2
stderr_logfile_maxbytes=0
environment=NODE_ENV="development"
EOF
# Development ports
EXPOSE 8001 3782