FROM node:20-alpine AS frontend-builder RUN apk add --no-cache git python3 RUN git clone --depth 1 https://huggingface.co/spaces/smolagents/ml-intern /source || true COPY patch_restore_full_app.py /tmp/patch_restore_full_app.py RUN python3 /tmp/patch_restore_full_app.py || true COPY patch_frontend.py /tmp/patch_frontend.py RUN python3 /tmp/patch_frontend.py || true COPY patch_space_bunny.py /tmp/patch_space_bunny.py RUN python3 /tmp/patch_space_bunny.py || true COPY patch_frontend_uploads.py /tmp/patch_frontend_uploads.py RUN python3 /tmp/patch_frontend_uploads.py || true COPY patch_frontend_multi.py /tmp/patch_frontend_multi.py RUN python3 /tmp/patch_frontend_multi.py || true COPY patch_frontend_new_models.py /tmp/patch_frontend_new_models.py RUN python3 /tmp/patch_frontend_new_models.py || true COPY patch_frontend_apodex_ling_beatapi.py /tmp/patch_frontend_apodex_ling_beatapi.py RUN python3 /tmp/patch_frontend_apodex_ling_beatapi.py || true COPY patch_frontend_xing_nemotron_vl.py /tmp/patch_frontend_xing_nemotron_vl.py RUN python3 /tmp/patch_frontend_xing_nemotron_vl.py || true COPY patch_catch_up_dataset_fallback.py /tmp/patch_catch_up_dataset_fallback.py RUN python3 /tmp/patch_catch_up_dataset_fallback.py || true # Run auto_continue patch BEFORE our frontend fix, since it also patches useAgentChat.ts COPY patch_auto_continue_v17.py /tmp/patch_auto_continue_v17.py RUN python3 /tmp/patch_auto_continue_v17.py || true # Run our frontend catch-up fix AFTER all other frontend patches COPY patch_catch_up_fix_frontend.py /tmp/patch_catch_up_fix_frontend.py RUN python3 /tmp/patch_catch_up_fix_frontend.py || true # v2: comprehensive catch-up fix + shareable URLs (frontend) COPY patch_catch_up_fix_v2.py /tmp/patch_catch_up_fix_v2.py RUN python3 /tmp/patch_catch_up_fix_v2.py || true # v3: FIX THE BUILD — v2 left dead imports (_catchupDone, ShareIcon) and an # untyped param that fail `npm run build` -> empty /app/static -> NO frontend. # This MUST run after v2 so it can complete v2's half-applied patches. COPY patch_catch_up_fix_v3.py /tmp/patch_catch_up_fix_v3.py RUN python3 /tmp/patch_catch_up_fix_v3.py || true WORKDIR /source/frontend RUN npm config set fetch-timeout 120000 && npm config set fetch-retries 3 && npm install && npm run build || echo "FRONTEND BUILD FAILED" # Guarantee the dist dir exists so COPY --from below never fails, even if npm failed. RUN mkdir -p /source/frontend/dist FROM python:3.12-slim COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ RUN useradd -m -u 1000 user WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends git curl ca-certificates tesseract-ocr tesseract-ocr-eng tesseract-ocr-vie libreoffice-writer libreoffice-calc libreoffice-impress && rm -rf /var/lib/apt/lists/* RUN git clone --depth 1 https://huggingface.co/spaces/smolagents/ml-intern /tmp/source && cp /tmp/source/pyproject.toml /tmp/source/uv.lock ./ && cp -r /tmp/source/agent ./agent && cp -r /tmp/source/backend ./backend && (cp /tmp/source/backend/document_parser.py ./backend/document_parser.py 2>/dev/null || true) && cp -r /tmp/source/configs ./configs && cp -r /tmp/source/scripts ./scripts && rm -rf /tmp/source COPY scripts/build_kpis.py /tmp/build_kpis_patched.py RUN cp /tmp/build_kpis_patched.py /app/scripts/build_kpis.py && python3 -c "import ast; ast.parse(open('/app/scripts/build_kpis.py').read())" || echo "KP FAILED" COPY patch_restore_full_app.py /tmp/patch_restore_full_app.py RUN python3 /tmp/patch_restore_full_app.py || true RUN uv sync --no-dev --frozen || echo "UV SYNC FAILED" COPY backend/session_logger.py /app/backend/session_logger.py COPY backend/document_parser.py /app/backend/document_parser.py COPY patch_models.py /tmp/patch_models.py RUN python /tmp/patch_models.py || true COPY patch_new_models.py /tmp/patch_new_models.py RUN python /tmp/patch_new_models.py || true COPY patch_apodex_ling_beatapi.py /tmp/patch_apodex_ling_beatapi.py RUN python3 /tmp/patch_apodex_ling_beatapi.py || true COPY patch_xing_nemotron_vl.py /tmp/patch_xing_nemotron_vl.py RUN python3 /tmp/patch_xing_nemotron_vl.py || true COPY patch_openrouter_fix.py /tmp/patch_openrouter_fix.py RUN python3 /tmp/patch_openrouter_fix.py || true COPY patch_beatapi_routing.py /tmp/patch_beatapi_routing.py RUN python3 /tmp/patch_beatapi_routing.py || true COPY patch_agent_backend.py /tmp/patch_agent_backend.py RUN python3 /tmp/patch_agent_backend.py || true COPY patch_document_upload.py /tmp/patch_document_upload.py RUN python3 /tmp/patch_document_upload.py || true COPY patch_multi_upload.py /tmp/patch_multi_upload.py RUN python3 /tmp/patch_multi_upload.py || true COPY patch_permanent_session_logging.py /tmp/patch_permanent_session_logging.py RUN python3 /tmp/patch_permanent_session_logging.py || true COPY patch_catch_up_fix_backend.py /tmp/patch_catch_up_fix_backend.py RUN python3 /tmp/patch_catch_up_fix_backend.py || true # v2: comprehensive catch-up fix + shareable URLs (backend — runs after all other backend patches) COPY patch_catch_up_fix_v2.py /tmp/patch_catch_up_fix_v2.py RUN python3 /tmp/patch_catch_up_fix_v2.py || true # v3 backend: fix the event-row filter in session_logger.py that v2 broke # (wrong indent -> SyntaxError -> filter never applied -> catch-up errors). COPY patch_catch_up_fix_v3.py /tmp/patch_catch_up_fix_v3.py RUN python3 /tmp/patch_catch_up_fix_v3.py || true # === PATCH: fix 404 on root and /api when frontend build is missing === # Root cause: mkdir -p /app/static creates an empty dir; main.py mounts # StaticFiles on the empty dir, shadowing ALL API routes with 404s. COPY patch_fix_static_404.py /tmp/patch_fix_static_404.py RUN python3 /tmp/patch_fix_static_404.py || echo "STATIC FIX PATCH WARN (BUILD CONTINUES)" # === PATCH: lossless long-context memory (fix context loss for long sessions) === COPY patch_lossless_context.py /tmp/patch_lossless_context.py RUN python /tmp/patch_lossless_context.py || echo "LOSSLESS CONTEXT PATCH WARN (BUILD CONTINUES)" RUN python3 -c "import ast; [ast.parse(open(f).read()) for f in ['/app/agent/context_manager/manager.py','/app/agent/core/agent_loop.py','/app/agent/core/session.py','/app/agent/core/session_resume.py','/app/agent/config.py']]; print('ALL PATCHED FILES SYNTAX OK')" || echo "SYNTAX CHECK WARN" # === PATCH: restore full session context from HF dataset === # Fixes: (1) "Catch me up" banner appears after every Space restart because # NoopSessionStore never restores sessions; (2) context lost after catch-up # because seed_from_summary only used a lossy summary. This patch makes the # backend reload full conversations from the permanent sessions dataset. COPY patch_catch_up_context_restore.py /tmp/patch_catch_up_context_restore.py RUN python3 /tmp/patch_catch_up_context_restore.py || echo "CONTEXT RESTORE PATCH WARN (BUILD CONTINUES)" RUN python3 -c "import ast; ast.parse(open('/app/backend/session_manager.py').read()); print('session_manager.py SYNTAX OK')" || echo "SESSION MANAGER SYNTAX CHECK WARN" # Copy frontend dist from builder stage using COPY --from (multi-stage) # The frontend-builder stage guarantees /source/frontend/dist exists (mkdir -p) COPY --from=frontend-builder /source/frontend/dist /app/static RUN echo "Static dir ready: $(ls /app/static 2>/dev/null | wc -l) files" RUN mkdir -p /app/configs RUN cp configs/frontend_agent_config.json /app/configs/frontend_agent_config.json 2>/dev/null || echo "No frontend config" RUN grep -rl "where we here now" /app/static/ 2>/dev/null | xargs sed -i "s/where we here now//g" 2>/dev/null || true RUN mkdir -p /app/session_logs && chown -R user:user /app USER user ENV HOME=/home/user PYTHONUNBUFFERED=1 PYTHONPATH=/app PATH="/app/.venv/bin:$PATH" EXPOSE 7860 WORKDIR /app/backend CMD ["bash", "start.sh"]