Training nibiru-coder
How to register a Nibiru-flavoured chat model on your own Ollama. One Modelfile, one shell script, sixty seconds.
The framework’s default chat model is nibiru-coder:1.0 — a Nibiru-flavoured Qwen 2.5 Coder 14B that you register on your Ollama server. The training pipeline lives in application/module/ai/training/.
What nibiru-coder is (and isn’t)
Section titled “What nibiru-coder is (and isn’t)”nibiru-coder:1.0 is not a LoRA fine-tune. It’s the same qwen2.5-coder:14b weights wrapped with a baked-in system prompt that:
- explains MMVC, modules, the dispatcher, the singletons,
- enforces Nibiru’s conventions (
pageAction,navigationAction,View::assign,Form::create, the spelling ofPageination), - pushes the model toward Nibiru-idiomatic answers instead of generic Laravel / Symfony advice.
System-prompt customisation runs instantly — no GPU training, no dataset preparation. It gives roughly 80 % of the value of a real LoRA at zero training cost. When you have budget for a real fine-tune, see Real LoRA path below.
Build it
Section titled “Build it”./application/module/ai/training/build.sh # builds nibiru-coder:1.0./application/module/ai/training/build.sh 1.1 # bump tag for iterationsThe script:
- Reads the Modelfile next to it.
- POSTs to
${OLLAMA_BASE_URL}/api/create(defaulthttps://your-ollama-host.example). - Runs a smoke-test chat call to confirm the new tag responds.
After it succeeds, set the model in application/module/ai/settings/ai.ini:
[AI]chat.model = "nibiru-coder:1.0"chat.fallback_model = "qwen2.5-coder:14b"…and every \Nibiru\Module\Ai\Ai instance in your app talks to it. The fallback ensures nothing breaks if you’ve not built the tag yet.
Iterate on the system prompt
Section titled “Iterate on the system prompt”The Modelfile’s SYSTEM """ ... """ block is the lever. Tighten the conventions, add new examples, add citations to specific framework files. Re-run build.sh with a new tag (1.1, 1.2, …) and A/B against the previous tag in your app.
./application/module/ai/training/build.sh 1.1# Edit ai.ini → chat.model = "nibiru-coder:1.1"# Compare answers in the Oracle widget or via smoke-test.phpReal LoRA path
Section titled “Real LoRA path”When you want a model whose weights know Nibiru — not just its system prompt — the corpus exporter has you covered.
cd docsnpm run build:corpusOutputs JSONL files under dist/corpus/:
| File | Format | Use |
|---|---|---|
chat.jsonl | sharegpt-style messages | Axolotl, LLaMA-Factory, Unsloth |
instructions.jsonl | instruction/input/output | Alpaca-style trainers |
completion.jsonl | prompt/completion | Legacy text-completion fine-tunes |
chunks.jsonl | chunk metadata | RAG / evaluation set construction |
A pragmatic recipe for an 8B base on a single A100 / 4090:
base_model: meta-llama/Llama-3.1-8B-Instructadapter: loralora_r: 16lora_alpha: 32lora_dropout: 0.05lora_target_modules: [q_proj, k_proj, v_proj, o_proj]
datasets: - path: docs/dist/corpus/chat.jsonl type: sharegpt
sequence_len: 4096sample_packing: truegradient_accumulation_steps: 4micro_batch_size: 2num_epochs: 3optimizer: adamw_bnb_8bitlearning_rate: 0.0002warmup_ratio: 0.05bf16: trueAfter training:
- Convert the LoRA to GGUF (
llama.cpp’sconvert_hf_to_gguf.py). - Build an Ollama Modelfile with
FROM ./your-lora.gguf. ./build.sh 2.0registers it asnibiru-coder:2.0.
The framework code doesn’t change — flip chat.model in ai.ini and you’re on the new weights.
Smoke test
Section titled “Smoke test”php application/module/ai/training/smoke-test.phpVerifies:
- The Ollama server is reachable.
- The model responds to a single-turn ask.
- Multi-turn conversation context works.
- Embeddings work (skipped with a clear message if
nomic-embed-textisn’t pulled).
Run after every Modelfile change, before deploying.
Common pitfalls
Section titled “Common pitfalls”Modelfilesystem prompt too long. Some Ollama versions cap system prompts. Keep it under ~3000 tokens.- Forgetting to pull the FROM model.
qwen2.5-coder:14bmust already be on the server.curl ${OLLAMA_BASE_URL}/api/tagsto check. - Tag collisions. Re-running
build.sh 1.0overwrites the existingnibiru-coder:1.0. Use new tags for iteration; pin specific tags inai.inifor production. --no-streamconfusion. The build script usesstream: falseso the response comes back as one JSON. If you change to streamed, parse line-by-line.