--- title: EuropaLex emoji: 📚 colorFrom: blue colorTo: indigo sdk: docker sdk_version: "latest" python_version: "3.12" app_file: app.py pinned: false tags: - track:backyard - sponsor:openbmb - achievement:offgrid - achievement:offbrand - achievement:llama - achievement:sharing --- # EuropaLex — Docker / Hugging Face Spaces Deployment AI-powered flashcard generator for European languages, deployed as a Docker container on [Hugging Face Spaces](https://huggingface.co/spaces). All four AI models are baked into the image at build time — the app starts instantly with zero wait. > **CPU-only inference:** All inference runs on CPU. Expect slower performance (30+ seconds per sentence for translation, longer for TTS/images) in exchange for free hosting. ## Model weights | Model | HF Hub Repo | GGUF File | Runtime | Params | Size | Role | |---|---|---|---|---|---|---| | MiniCPM5-1B Q8_0 | [Abiray/MiniCPM5-1B-GGUF](https://huggingface.co/Abiray/MiniCPM5-1B-GGUF) | `minicpm5-1b-Q8_0.gguf` | llama-cpp-python | 1.08 B | ~1.1 GB | English text generation (Phase 1) | | tiny-aya-water Q4_K_M | [CohereLabs/tiny-aya-water-GGUF](https://huggingface.co/CohereLabs/tiny-aya-water-GGUF) | `tiny-aya-water-q4_k_m.gguf` | llama-cpp-python | 3.35 B | ~2.1 GB | Translation (active) | | OmniVoice Q8_0 (base + tokenizer) | [Serveurperso/OmniVoice-GGUF](https://huggingface.co/Serveurperso/OmniVoice-GGUF) | `omnivoice-base-Q8_0.gguf` + `omnivoice-tokenizer-Q8_0.gguf` | omnivoice.cpp | 0.6 B | ~950 MB | Text-to-speech | | FLUX.2-klein 4B Q4_K_M | [unsloth/FLUX.2-klein-4B-GGUF](https://huggingface.co/unsloth/FLUX.2-klein-4B-GGUF) | `flux-2-klein-4b-Q4_K_M.gguf` | diffusers | 4 B | ~2.6 GB | Image generation | ## Links [Social Media Post](https://www.linkedin.com/posts/gonzalo-gamez_buildsmallhackathon-huggingface-ai-activity-7472322421472051200-iUBa/?rcm=ACoAADjnxE4BIBmKXlLcq6ecaJUWIc8fuJGMb9g) [Traces](https://huggingface.co/datasets/Takosaga/EuropaLex-session-traces) [Demo Video](https://youtu.be/OVBDAwCTuxk) The demo works on my machine, two days to figure out how to deploy and still was stuck. ## How It Works ``` Docker build: python:3.12-slim → pip install CPU deps → huggingface-cli login (build secret) → download all models → CMD ["python", "app.py"] HF Spaces runtime: Container starts → _auto_download_models() finds GGUF files → skips download → launches Gradio on :7860 ``` The Dockerfile downloads all models during `docker build` using your HF token as a build secret. At runtime, the app detects pre-existing model files and skips download entirely — no authentication needed, no waiting. ## CPU Performance Expectations | Operation | Expected Time | |---|---| | Phase 1: Generate 3 English sentences | ~30–60 seconds | | Phase 2: Translate 3 sentences (tiny-aya) | ~1–3 minutes | | Phase 2: TTS audio per sentence | ~5–15 seconds | | Phase 2: Image generation per card | ~30–60+ seconds | These are approximate and depend on the HF Spaces CPU tier. All features remain functional — just slower than a GPU setup. ## Local Docker Testing (Optional) Build and test locally before deploying: ```bash # Build the image (requires your HF token) docker build \ --secret id=hf_token,env=HUGGING_FACE_HUB_TOKEN \ -t europalex . # Run locally (port 7860) docker run -p 7860:7860 europalex ``` The container serves Gradio on `http://localhost:7860`. Press Ctrl+C to stop. ## Architecture EuropaLex uses a two-phase generation workflow: 1. **Phase 1** — Enter a scenario, select CEFR level (A0–C2), set batch size → MiniCPM5-1B generates English sentences 2. **Phase 2** — Select target language, toggle Audio/Images → tiny-aya translates, OmniVoice generates TTS, FLUX generates illustrations Cards export as Anki `.apkg` files or zipped CSV folders with flat media files. ## Repository Structure ``` EuropaLex/ ├── Dockerfile # Single-stage build: deps + model download + Gradio launch ├── .dockerignore # Exclude .venv, .git, models from build context ├── README.md # This file — HF Spaces deployment guide ├── app.py # Entry point — Gradio UI wiring, two-phase generation handlers ├── pyproject.toml # Project config (uv) ├── requirements.txt # pip install dependencies ├── configs/settings.yaml # App settings, model paths, batch defaults ├── core/ # Business logic │ ├── types.py # Pydantic models: CardData, CEFRLevel, TextResult, etc. │ ├── engine.py # MiniCPMTextEngine, LlamaCppTextEngine, EnginePool │ ├── audio_gen.py # TTSEngine (OmniVoice) │ ├── image_gen.py # ImageGenEngine (diffusers Flux2KleinPipeline) │ ├── text_gen.py # Sentence extraction + generation with retry loop │ └── pipeline.py # Phase 2 translation orchestration ├── frontend/ # Gradio 6 UI │ ├── ui/ │ │ ├── widgets.py # Styled toggle checkbox wrappers, Blocks builder │ │ └── cards.py # Card rendering, gallery layout, progress bar │ └── css/custom.css # Plain-white theme, card styling, disabled states ├── models/ │ └── download_models.py # HF Hub model downloader (runtime fallback) ├── export/ # Export formats │ ├── apkg_export.py # Anki .apkg export via genanki │ ├── csv_export.py # CSV zip export with flat media files │ └── anki_tunnel.py # MCP tunnel sync for live Anki import ├── docs/ # Design specs and implementation plans │ └── superpowers/ # Planning documents └── tests/ # Test suite (pytest) ```