# Negoptim AI backend — built for Hugging Face Spaces (Docker Space, free CPU tier) # # Build strategy: everything heavy happens at BUILD time so the free tier's # ephemeral disk doesn't matter at runtime — # 1. CPU-only torch (several GB smaller than the default CUDA build) # 2. the e5 embedding model is pre-downloaded into the image # 3. the knowledge base is ingested into ChromaDB inside the image # Runtime then cold-starts in seconds with the index already in place. # # Secrets (set in the Space settings, never in this file): # GROQ_API_KEY (required) # CORS_ORIGINS e.g. https://your-app.vercel.app,http://localhost:3000 # SMTP_USER / SMTP_PASSWORD (optional, real email delivery) # CEREBRAS_API_KEY / GEMINI_API_KEY (optional, extra LLM fallback) FROM python:3.11-slim # HF Spaces runs containers as UID 1000 RUN useradd -m -u 1000 user USER user ENV PATH="/home/user/.local/bin:$PATH" \ HF_HOME=/home/user/.cache/huggingface \ PYTHONUNBUFFERED=1 WORKDIR /app # CPU-only torch first, so sentence-transformers doesn't pull the CUDA build COPY --chown=user backend/requirements.txt . RUN pip install --no-cache-dir --user torch --index-url https://download.pytorch.org/whl/cpu && \ pip install --no-cache-dir --user -r requirements.txt # Pre-download the embedding model into the image layer cache RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('intfloat/multilingual-e5-small')" COPY --chown=user backend/ /app/backend/ COPY --chown=user knowledge/ /app/knowledge/ COPY --chown=user scripts/ /app/scripts/ WORKDIR /app/backend ENV CHROMA_PERSIST_DIRECTORY=/app/backend/chroma_db \ KNOWLEDGE_BASE_PATH=/app/knowledge # Build the vector index into the image (no persistent disk needed at runtime) RUN python ../scripts/ingest.py # HF Spaces expects the app on port 7860 EXPOSE 7860 CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]