A sentiment-routed medical Mixture-of-Experts (MoE) LLM
a tiny sentiment classifier routes each patient query to one of two LoRA expert adapters, all sharing a single 4-bit quantized Gemma 3 4B base model (QLoRA) from Ollama, trained by Google Deepmind.
Lotus 1.2 Mini is a open-sourced, specialized patient-centered Mixture-of-Experts architecture built around a LoRA-adapted Gemma 3 fine-tuned by CodeTheCure Labs.
user prompt
|--> SentimentRouter (DistilBERT SST-2 via ONNX)
| P(POS) >= 0.5 -> POS expert (grateful / reassured patients)
| else -> NEG expert (worried / anxious patients)
v
shared 4-bit Gemma 3 4B (mlx-community/gemma-3-4b-it-4bit)
+ teh chosen expert's LoRA adapter
v
some kind of medical answer
Requires (for now… because fine-tuning was run on MLX, which is macOS-native) macOS with Apple Silicon (not Intel) and uv.
git clone https://github.com/2028badivi/lotus-mini.git
cd lotus-mini
./scripts/setup.sh #should be around 256 ish MB, excluding the 3.4 GB download for the gemma 3 4b base model
uv run moe.py "I've had chest pain for three days and I'm scared.” # or whatever example you would like to test
First inference downloads the ~3.4 GB 4-bit base model from Hugging Face once,
then caches it under ~/.cache/huggingface.
uv run moe.py "<question>" # routed by sentiment
uv run moe.py --expert NEG "just a dry cough" # force an expert (remembr that experts are rooted by negative or positive sentiment classified under DistilBERT)
uv run moe.py --compare "<question>" # base vs NEG vs POS
uv run moe.py --interactive # chat loop
uv run moe.py --verbosity 2 "<question>" # longer answers
uv run moe.py --verbose "<question>" # debug logsuv run prepare_data.py # download medical dataset originally used to train Lotus 1.2 mini
uv run train.py # orig script w/o verbosity
#uv run train.py --experts POS #if you want to do only certain experts
#uv run train.py —verbose #recommended if you want to retrain w/ verbosityTraining streams a live tqdm bar (loss, tok/s, ETA) plus a heartbeat every 20 s,
and writes full logs to logs/<expert>_train.log.
lotus/ core package
constants.py paths + model/dataset config
router.py SentimentRouter (ONNX) + auto-download of artifacts
expert.py ExpertLoader : hot-swaps LoRA adapters on the shared base
chat.py chat-template / dataset formatting helpers
trainer.py QLoRA training driver (tqdm + heartbeat streaming)
moe.py inference CLI (sentiment-routed MoE)
prepare_data.py dataset download + sentiment stratification
train.py training CLI (both experts)
scripts/
setup.sh one-command setup (deps + router + smoke test)
download_router.py fetch the ONNX router artifacts from HF
smoke_test.py verify the setup (router-only, or full inference)
upload_hf.py publish adapters + router + model card to HF Hub
router/ gitignored: downloaded ONNX artifacts (see setup.sh)
data/ sentiment-stratified JSONL expert subsets + manifest
experts/ trained LoRA adapters (neg/, pos/) : committed
logs/ gitignored: per-expert training logs
- Router is auto-downloaded. The ~256 MB ONNX model is never committed to
git;
SentimentRouterfetches it from Hugging Face on first use, andscripts/setup.shpre-fetches it. - Expert hot-swap. All experts live on one in-memory base model; switching experts is a ~0.01 s adapter-weight swap, so routing costs nothing at inference.
- No torch. The router runs on
onnxruntime(CPU) and the LLM runs on Apple's MLX, so a full clone does not technically install PyTorch. - Deadlock-free trainer streaming. The trainer consumes the child process's
byte stream (including tqdm
\rredraws) so a pipe-buffer stall can never freeze training silently.
Research/demo only. Trained on medical Q&A data; not a substitute for professional medical advice, diagnosis, or treatment.