Spaces:
Runtime error
Runtime error
| title: MyPal (9XAIPal) | |
| emoji: π | |
| colorFrom: blue | |
| colorTo: indigo | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| license: mit | |
| # 9XAIPal β local-first reading companion | |
| Single-container Hugging Face Space deployment. A plain FastAPI app (`app.py`) | |
| mounts the real 9XAIPal backend API at `/api/v1` and serves the compiled React | |
| UI at `/` (SPA with client-side routing) β all on port `7860`. | |
| ## Layout | |
| | Path | Purpose | | |
| | ----------------- | -------------------------------------------------- | | |
| | `app.py` | FastAPI entrypoint (API + SPA, port 7860) | | |
| | `Dockerfile` | Builds the image; bakes in the Ollama embedder | | |
| | `start.sh` | Boot script: Redis, Ollama, then supervisord (API + Celery worker + optional local Postgres) | | |
| | `supervisord.conf`| Process supervisor config: keeps API + worker alive | | |
| | `requirements.txt`| Python dependencies | | |
| | `app/` | 9XAIPal backend package | | |
| | `dist/` | Compiled React UI | | |
| ## Links | |
| - **YouTube Demo Video:** [https://youtu.be/m-uIaNKOOrk?si=4FpcZtlKJwZpPJnz](https://youtu.be/m-uIaNKOOrk?si=4FpcZtlKJwZpPJnz) | |
| - **GitHub Repository:** [https://github.com/Khaled-Saleh-KL1/9XAIPal](https://github.com/Khaled-Saleh-KL1/9XAIPal) | |
| - **X (Twitter) Post:** [https://x.com/KL1_Suii/status/2066237932713775393?s=20](https://x.com/KL1_Suii/status/2066237932713775393?s=20) | |
| ## Configuration (set as Space **Secrets / Variables**) | |
| All config is read from environment variables β **no `.env` file is bundled**. | |
| Set these under *Settings β Variables and secrets*: | |
| - `OLLAMA_BASE_URL`, `OLLAMA_API_KEY` β chat/VLM backend (Ollama Cloud) | |
| - `CHAT_MODEL`, `VLM_MODEL` β **must match an exact tag served by your | |
| `OLLAMA_BASE_URL`**. If the endpoint lists `gemma4:31b` and not | |
| `gemma4:31b-cloud`, set `CHAT_MODEL=gemma4:31b` and `VLM_MODEL=gemma4:31b`. | |
| - `EMBEDDING_PROVIDER=ollama`, `EMBEDDING_BASE_URL=http://localhost:11434`, | |
| `EMBEDDING_MODEL=qwen3-embedding` β embeddings run on the **in-container** | |
| Ollama (the 31B chat model lives on Ollama Cloud, which hosts no embedding | |
| models, so a small embedding model is baked into the image and served locally) | |
| - `HF_TOKEN` / `HUGGING_FACE_HUB_TOKEN` β model-weight downloads | |
| - **Database** β `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, | |
| `POSTGRES_USER`, `POSTGRES_PASSWORD`, `PGSSLMODE=require`. The Space container | |
| disk is **ephemeral**, so this deployment points at an **external managed | |
| Postgres** (Supabase, with `pgvector`) so uploaded papers persist across | |
| restarts/rebuilds. Use Supabase's **session-mode connection pooler** host | |
| (`aws-0-<region>.pooler.supabase.com:5432`, user `postgres.<project-ref>`) β | |
| the direct `db.<ref>.supabase.co` host is IPv6-only and unreachable from the | |
| IPv4-only Space container. | |
| - (optional) `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` / β¦ for cloud fallback | |
| > Note: `start.sh` boots Redis and an in-container Ollama (for embeddings), then | |
| > launches `supervisord` to keep the FastAPI app **and a Celery worker** alive. | |
| > PostgreSQL runs **externally** (managed) whenever `POSTGRES_HOST` is set to a | |
| > non-local host β the bundled in-container Postgres is started only as a fallback | |
| > for local/no-external-DB runs, and its data is ephemeral. Redis here is just an | |
| > ephemeral cache/queue. | |
| > | |
| > **PDF extraction:** the image enables the PyMuPDF text-only fallback by default | |
| > (`ALLOW_PYMUPDF_FALLBACK=1`) so uploads process immediately without baking | |
| > several gigabytes of MinerU + torch into the image. For higher-fidelity output | |
| > (OCR, tables, equations, figures), install `mineru[pipeline]>=3.2.0`, `torch`, | |
| > `torchvision`, and `scipy>=1.17.1` in `requirements.txt` and set | |
| > `ALLOW_PYMUPDF_FALLBACK=0` (or unset) in the Space variables. | |
| > | |
| > **Self-healing:** the container has a Docker `HEALTHCHECK` on `/healthz` and an | |
| > in-app watchdog that detects stuck ingestion jobs. If a paper hangs in | |
| > `queued` / `extracting` / `chunking` / `embedding` / `summarizing` for too long, | |
| > the watchdog revokes the stuck Celery task, cleans up partial state, and | |
| > re-dispatches the job so processing completes. | |