|
1 | | -FROM python:3.11-slim-bookworm |
| 1 | +FROM python:3.11-slim-bookworm AS builder |
| 2 | + |
| 3 | +ARG POETRY_VERSION=2.3.1 |
2 | 4 |
|
3 | 5 | WORKDIR /app |
4 | 6 |
|
5 | | -RUN apt-get update && \ |
6 | | - apt-get install -y curl cron gnupg2 lsb-release && \ |
7 | | - echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \ |
8 | | - curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \ |
9 | | - gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg && \ |
10 | | - apt-get update && \ |
11 | | - apt-get install -y postgresql-client-16 && \ |
12 | | - apt-get clean && \ |
13 | | - rm -rf /var/lib/apt/lists/* |
| 7 | +ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \ |
| 8 | + PIP_NO_CACHE_DIR=0 \ |
| 9 | + PYTHONDONTWRITEBYTECODE=1 \ |
| 10 | + PYTHONUNBUFFERED=1 |
14 | 11 |
|
15 | | -RUN pip install --upgrade pip \ |
16 | | - && pip install poetry |
| 12 | +# Install poetry system-level (outside venv) — only used for export |
| 13 | +RUN --mount=type=cache,target=/root/.cache/pip \ |
| 14 | + pip install --timeout=300 "poetry==${POETRY_VERSION}" "poetry-plugin-export" |
17 | 15 |
|
18 | 16 | COPY ./pyproject.toml ./poetry.lock* /app/ |
19 | 17 |
|
20 | | -RUN poetry config virtualenvs.create false |
| 18 | +# Create clean venv, install app deps, strip heavy unused deps, add stubs |
| 19 | +# Merged into one layer so stripped files don't persist in a previous layer. |
| 20 | +# Stripped: |
| 21 | +# - phonenumbers/twilio: required by supertokens at import-time but never called |
| 22 | +# - hf_xet: transitive, never imported by app code |
| 23 | +# Kept: litellm + chain (tokenizers, huggingface_hub, tiktoken) — needed by agenta SDK |
| 24 | +# Kept: newrelic — used for observability |
21 | 25 | RUN --mount=type=cache,target=/root/.cache/pip \ |
22 | 26 | --mount=type=cache,target=/root/.cache/pypoetry \ |
23 | | - poetry install --no-interaction --no-ansi |
| 27 | + python -m venv /opt/venv \ |
| 28 | + && . /opt/venv/bin/activate \ |
| 29 | + && poetry export --only main --without-hashes --format requirements.txt --output /tmp/requirements.txt \ |
| 30 | + && pip install -r /tmp/requirements.txt \ |
| 31 | + && rm -f /tmp/requirements.txt \ |
| 32 | + && (pip uninstall -y pip setuptools 2>/dev/null || true) \ |
| 33 | + && SITE=$(python -c 'import site; print(site.getsitepackages()[0])') \ |
| 34 | + && rm -rf \ |
| 35 | + ${SITE}/phonenumbers* \ |
| 36 | + ${SITE}/twilio* \ |
| 37 | + ${SITE}/hf_xet* \ |
| 38 | + && mkdir -p ${SITE}/phonenumbers ${SITE}/twilio/rest \ |
| 39 | + && printf 'PhoneNumberFormat=type("PhoneNumberFormat",(),{"E164":0,"INTERNATIONAL":1,"NATIONAL":2})()\ndef parse(*a,**k):return None\ndef format_number(*a,**k):return ""\ndef is_valid_number(*a,**k):return False\n' \ |
| 40 | + > ${SITE}/phonenumbers/__init__.py \ |
| 41 | + && printf 'class Client:\n def __init__(self,*a,**k):raise NotImplementedError("twilio stub")\n' \ |
| 42 | + > ${SITE}/twilio/__init__.py \ |
| 43 | + && cp ${SITE}/twilio/__init__.py ${SITE}/twilio/rest/__init__.py \ |
| 44 | + && find /opt/venv -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null; true |
| 45 | + |
| 46 | +# Copy and install SDK first (changes less frequently than app code) |
| 47 | +RUN mkdir -p /app/sdk |
| 48 | +COPY ./sd[k] /app/sdk/ |
24 | 49 |
|
| 50 | +RUN if [ -f /app/sdk/pyproject.toml ]; then \ |
| 51 | + . /opt/venv/bin/activate && \ |
| 52 | + pip install pip && \ |
| 53 | + pip install --editable /app/sdk && \ |
| 54 | + (pip uninstall -y pip 2>/dev/null || true); \ |
| 55 | + fi |
| 56 | + |
| 57 | +# Copy app code last (changes more frequently) |
25 | 58 | COPY ./ee /app/ee/ |
26 | 59 | COPY ./oss /app/oss/ |
27 | 60 | COPY ./entrypoints /app/entrypoints/ |
28 | 61 |
|
29 | | -# Copy local SDK if present (synced by build.sh before docker build) |
30 | | -# Uses glob pattern sd[k] to avoid failure if sdk/ doesn't exist (for OSS users) |
31 | | -COPY ./sd[k] /app/sdk/ |
32 | 62 |
|
33 | | -# Install local SDK in editable mode if present (overrides the PyPI version) |
34 | | -RUN if [ -f /app/sdk/pyproject.toml ]; then pip install -e /app/sdk; fi |
| 63 | +FROM python:3.11-slim-bookworm AS runner |
35 | 64 |
|
36 | | -# Verify which agenta package is active (output visible in build logs) |
37 | | -RUN python -c "\ |
38 | | -import agenta; loc = getattr(agenta, '__file__', '?'); \ |
39 | | -print(f'[SDK CHECK] agenta={loc}'); \ |
40 | | -print(f'[SDK CHECK] is_local={\"app/sdk\" in (loc or \"\")}')" \ |
41 | | -|| echo '[SDK CHECK] verification failed' |
| 65 | +ARG BUILD_DATE |
| 66 | +ARG VCS_REF |
| 67 | +ARG VERSION=0.0.0 |
42 | 68 |
|
43 | | -# PYTHONPATH includes /app/sdk - if local SDK exists it takes precedence over PyPI version |
44 | | -ENV PYTHONPATH=/app:/app/sdk |
45 | | - |
46 | | -COPY ./oss/src/crons/queries.sh /queries.sh |
47 | | -COPY ./oss/src/crons/queries.txt /etc/cron.d/queries-cron |
48 | | -RUN sed -i -e '$a\' /etc/cron.d/queries-cron |
49 | | -RUN cat -A /etc/cron.d/queries-cron |
50 | | - |
51 | | -RUN chmod +x /queries.sh \ |
52 | | - && chmod 0644 /etc/cron.d/queries-cron |
53 | | - |
54 | | -COPY ./ee/src/crons/meters.sh /meters.sh |
55 | | -COPY ./ee/src/crons/meters.txt /etc/cron.d/meters-cron |
56 | | -RUN sed -i -e '$a\' /etc/cron.d/meters-cron |
57 | | -RUN cat -A /etc/cron.d/meters-cron |
| 69 | +WORKDIR /app |
58 | 70 |
|
59 | | -RUN chmod +x /meters.sh \ |
60 | | - && chmod 0644 /etc/cron.d/meters-cron |
| 71 | +ENV PYTHONDONTWRITEBYTECODE=1 \ |
| 72 | + PYTHONUNBUFFERED=1 \ |
| 73 | + PYTHONPATH=/app:/app/sdk \ |
| 74 | + PATH="/opt/venv/bin:${PATH}" |
61 | 75 |
|
62 | | -COPY ./ee/src/crons/spans.sh /spans.sh |
63 | | -COPY ./ee/src/crons/spans.txt /etc/cron.d/spans-cron |
64 | | -RUN sed -i -e '$a\' /etc/cron.d/spans-cron |
65 | | -RUN cat -A /etc/cron.d/spans-cron |
| 76 | +# Hardcode bookworm codename to avoid pulling in lsb-release (+ perl ~56MB) |
| 77 | +RUN apt-get update && \ |
| 78 | + apt-get install -y --no-install-recommends curl cron gnupg2 && \ |
| 79 | + echo "deb http://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list && \ |
| 80 | + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \ |
| 81 | + gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg && \ |
| 82 | + apt-get update && \ |
| 83 | + apt-get install -y --no-install-recommends postgresql-client-17 && \ |
| 84 | + apt-get purge -y --auto-remove gnupg2 && \ |
| 85 | + rm -rf /var/lib/apt/lists/* |
66 | 86 |
|
67 | | -RUN chmod +x /spans.sh \ |
68 | | - && chmod 0644 /etc/cron.d/spans-cron |
| 87 | +# Copy stable cron files first (change less frequently) |
| 88 | +COPY --chmod=755 ./oss/src/crons/queries.sh /queries.sh |
| 89 | +COPY --chmod=644 ./oss/src/crons/queries.txt /etc/cron.d/queries-cron |
| 90 | +COPY --chmod=755 ./ee/src/crons/meters.sh /meters.sh |
| 91 | +COPY --chmod=644 ./ee/src/crons/meters.txt /etc/cron.d/meters-cron |
| 92 | +COPY --chmod=755 ./ee/src/crons/spans.sh /spans.sh |
| 93 | +COPY --chmod=644 ./ee/src/crons/spans.txt /etc/cron.d/spans-cron |
| 94 | + |
| 95 | +# Copy dependencies from builder |
| 96 | +COPY --from=builder /opt/venv /opt/venv |
| 97 | + |
| 98 | +# Copy app code last (changes most frequently) |
| 99 | +COPY --from=builder /app/ee /app/ee |
| 100 | +COPY --from=builder /app/oss /app/oss |
| 101 | +COPY --from=builder /app/entrypoints /app/entrypoints |
| 102 | +COPY --from=builder /app/sdk /app/sdk |
| 103 | + |
| 104 | +RUN set -eux; \ |
| 105 | + for cron_file in /etc/cron.d/queries-cron /etc/cron.d/meters-cron /etc/cron.d/spans-cron; do \ |
| 106 | + sed -i -e '$a\' "${cron_file}"; \ |
| 107 | + done |
| 108 | + |
| 109 | +LABEL org.opencontainers.image.title="agenta-api-ee" \ |
| 110 | + org.opencontainers.image.description="Agenta API EE GH runtime image" \ |
| 111 | + org.opencontainers.image.version="${VERSION}" \ |
| 112 | + org.opencontainers.image.revision="${VCS_REF}" \ |
| 113 | + org.opencontainers.image.created="${BUILD_DATE}" |
69 | 114 |
|
70 | 115 | EXPOSE 8000 |
0 commit comments