This project contains the complete pipeline to train a specialized LoRA adapter for Rust programming, covering the language standard, top 100 libraries, best practices, and ESP32/Embedded IoT development.
train_granite.py, train_qwen.py): Trains LoRA using Unsloth.Modelfile.# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate
# Install all dependencies from pyproject.toml
uv sync
# Optional: Install dev dependencies (ruff for formatting)
uv pip install -e ".[dev]"
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
Unsloth Studio is highly recommended if you don't have a local GPU with >24GB VRAM.
Note: Do this locally or on a cheap CPU VM. You don't need a GPU for data collection.
The GitHub collection script will work with or without a token, but with higher rate limits when authenticated:
GITHUB_TOKEN set: 0.2s delay between API calls (60 requests/min)To speed up collection on large repositories, set your GitHub token:
export GITHUB_TOKEN="ghp_your_personal_access_token"
Generate a token at: https://github.com/settings/tokens (requires repo and read:user scopes)
Run Data Collection Pipeline:
uv run python scripts/01_collect_rust_book.py
uv run python scripts/02_collect_docs_rs.py
uv run python scripts/03_collect_github.py
uv run python scripts/04_collect_esp_rs.py
uv run python scripts/05_collect_blogs.py
Collection output appears in data/raw/ organized by source. Each item is saved as JSON with:
source: Collection origin (rust_book, docs_rs, github, esp_rs, blogs, best_practices)source_type: Category (official_docs, api_docs, github_repo, blogs, best_practices)url: Original URLtitle: Page/file titlecontent: Markdown-formatted contentmetadata: Additional context (crate name, repo name, etc.)collected_at: ISO timestampError Handling: All errors are logged to stdout. If collection is interrupted, you can resume by re-running the same script—already-collected pages are cached and skipped.
Version Awareness: Crate versions, GitHub release tags, changelog sections, and Rust editions are automatically extracted and partitioned under data/raw/{source}/{version}/. When new versions are pulled in subsequent runs, previous versions are preserved. See VERSIONING.md for full details.
Transform and Chunk Data:
uv run python scripts/06_transform_data.py
Outputs to data/processed/all_chunks.jsonl
Data Quality: The transform script applies several validation checks:
sha256(source_type:library:version:content), ensuring shared code between different versions is preserved for each version while removing identical duplicates within the same version.The script outputs a validation summary showing:
(library, version) pairs trackedStore in Vector Database (For Updates & Version-Aware RAG):
# Index all chunks into ChromaDB
uv run python scripts/07_vector_store.py
# Query with version filtering
uv run python scripts/07_vector_store.py --library axum --version 0.7.5 --query "routing and state"
# Query migration guidance between versions
uv run python scripts/07_vector_store.py --library axum --migrate-from 0.6.20 --migrate-to 0.7.5 --query "handlers"
# List tracked libraries and versions
uv run python scripts/07_vector_store.py --list-versions
Create Training Dataset:
uv run python scripts/08_create_dataset.py
Outputs data/datasets/train.jsonl and val.jsonl in Alpaca format with version-grounded instructions and migration questions.
For complete architectural details on multi-version retention, changelog section extraction, RAG querying, and Alpaca prompt generation, see VERSIONING.md.
This project uses Ruff for code formatting and linting.
# Format all Python files
uv run ruff format .
# Check for lint issues
uv run ruff check .
# Fix lint issues automatically
uv run ruff check . --fix
Uses the base HuggingFace model (ibm-granite/granite-4.2-8b) or a local model directory. The export script will later merge LoRA and create GGUF.
Upload your project folder to Unsloth Studio (or upload data/datasets/train.jsonl directly).
Open train_granite.py in the studio.
Ensure the dataset path matches the uploaded location.
Run the script:
Option 1: Use HuggingFace model (downloads if not cached)
uv run python train_granite.py
Option 2: Use local model directory
uv run python train_granite.py --model-path /path/to/granite-4.2-8b
Advanced: override training sizing
uv run python train_granite.py --max-seq-length 4096 --batch-size 1 --gradient-accumulation-steps 8
Training parameters:
409618train_qwen.py.The Nemotron 3 Nano training script provides production-grade features including validation, checkpointing, and hardware-aware configuration.
Review the configuration:
cat config/nemotron_training_config.yaml
Validate configuration before training (recommended):
uv run python train_nemotron3_nano.py --dry-run
Run training with default settings (RTX 4070 profile):
uv run python train_nemotron3_nano.py
Alternative: Use different hardware profile:
# RTX 6000 (24GB VRAM)
uv run python train_nemotron3_nano.py --hardware-profile rtx6000
# A100 (40GB VRAM)
uv run python train_nemotron3_nano.py --hardware-profile a100
# Development (minimal resources, fast iteration)
uv run python train_nemotron3_nano.py --hardware-profile dev
Resume from checkpoint:
uv run python train_nemotron3_nano.py --resume-from-checkpoint models/nemotron3_nano_rust_lora/checkpoints/checkpoint-50
Enable experiment tracking (optional W&B):
uv run python train_nemotron3_nano.py --wandb-project my-project --wandb-entity my-team
Features:
models/nemotron3_nano_rust_lora/manifest.json for artifact lineageTraining Parameters (RTX 4070 default):
Advanced: Custom hyperparameters:
uv run python train_nemotron3_nano.py \
--batch-size 2 \
--max-seq-length 2048 \
--num-epochs 5 \
--learning-rate 1e-4 \
--verbose
The current implementation uses three separate LoRA recipes:
train_granite.py (Granite 4.2 8B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projtrain_qwen.py (Qwen 3.5 4B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projtrain_nemotron3_nano.py (Nemotron 3 Nano 4B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projOnce training is complete (either locally or downloaded from Unsloth Studio):
Package the trained adapter with Script 09:
Script scripts/09_create_modelfile.py now exports the trained LoRA adapter as an adapter-only package and writes a Modelfile with base model metadata and training economics.
uv run python scripts/09_create_modelfile.py --model models/granite_rust_lora --name rust-granite --base granite
uv run python scripts/09_create_modelfile.py --model models/qwen3_5_4b_rust_lora --name rust-qwen --base qwen
uv run python scripts/09_create_modelfile.py --model models/nemotron3_nano_rust_lora --name rust-nemotron --base nemotron
Optionally, provide --base-model to override the base model ID or local path used in the generated Modelfile.
Merge and export GGUF with Script 10:
Use scripts/10_export_ollama.py to merge the adapter with the base model and export a runnable GGUF package.
Option 1: Standard export (downloads base model from HuggingFace if not cached)
# For Granite 4.2 8B
uv run python scripts/10_export_ollama.py --model models/granite_rust_lora --name rust-granite --base ibm-granite/granite-4.2-8b
# For Qwen 3.5 4B
uv run python scripts/10_export_ollama.py --model models/qwen3_5_4b_rust_lora --name rust-qwen --base Qwen/Qwen3.5-4B
# For Nemotron 3 Nano 4B
uv run python scripts/10_export_ollama.py --model models/nemotron3_nano_rust_lora --name rust-nemotron --base NVIDIA/Nemotron-3-Nano-4B
Option 2: Use local GGUF or model file
uv run python scripts/10_export_ollama.py --model models/granite_rust_lora --name rust-granite --base /path/to/granite-4.2-8b-Q4_K_M.gguf
(This skips downloading from HuggingFace and uses your local file directly.)
Script 10 merges the LoRA weights with the specified base model and quantizes the merged model to Q4_K_M.
Import into Ollama:
ollama create rust-granite -f models/rust-granite_gguf/Modelfile
Test the Model:
ollama run rust-granite "How do I set up a WiFi connection on an ESP32 using esp-wifi and async Rust?"
Because we stored the raw data in ChromaDB (a local vector database), you do not need to re-scrape everything when Rust updates.
Collect only new data:
python scripts/01_collect_rust_book.py # Gets updated docs
python scripts/02_collect_docs_rs.py # Gets new crate versions
Re-transform the new raw data:
python scripts/06_transform_data.py
Update the Vector Store: Instead of re-indexing everything, update specific sources:
from scripts.07_vector_store import VectorStore
vs = VectorStore()
# Deletes old docs_rs chunks, adds new ones
vs.update_source("docs_rs", "data/processed/docs_rs_chunks.jsonl")
Re-generate Dataset & Re-train:
uv run python scripts/08_create_dataset.py
# Re-run train_granite.py in Unsloth Studio
21 commits
Python
97.2%
PowerShell
1.8%
Shell
1.0%
This project contains the complete pipeline to train a specialized LoRA adapter for Rust programming, covering the language standard, top 100 libraries, best practices, and ESP32/Embedded IoT development.
train_granite.py, train_qwen.py): Trains LoRA using Unsloth.Modelfile.# Create virtual environment and install dependencies
uv venv
source .venv/bin/activate
# Install all dependencies from pyproject.toml
uv sync
# Optional: Install dev dependencies (ruff for formatting)
uv pip install -e ".[dev]"
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
Unsloth Studio is highly recommended if you don't have a local GPU with >24GB VRAM.
Note: Do this locally or on a cheap CPU VM. You don't need a GPU for data collection.
The GitHub collection script will work with or without a token, but with higher rate limits when authenticated:
GITHUB_TOKEN set: 0.2s delay between API calls (60 requests/min)To speed up collection on large repositories, set your GitHub token:
export GITHUB_TOKEN="ghp_your_personal_access_token"
Generate a token at: https://github.com/settings/tokens (requires repo and read:user scopes)
Run Data Collection Pipeline:
uv run python scripts/01_collect_rust_book.py
uv run python scripts/02_collect_docs_rs.py
uv run python scripts/03_collect_github.py
uv run python scripts/04_collect_esp_rs.py
uv run python scripts/05_collect_blogs.py
Collection output appears in data/raw/ organized by source. Each item is saved as JSON with:
source: Collection origin (rust_book, docs_rs, github, esp_rs, blogs, best_practices)source_type: Category (official_docs, api_docs, github_repo, blogs, best_practices)url: Original URLtitle: Page/file titlecontent: Markdown-formatted contentmetadata: Additional context (crate name, repo name, etc.)collected_at: ISO timestampError Handling: All errors are logged to stdout. If collection is interrupted, you can resume by re-running the same script—already-collected pages are cached and skipped.
Version Awareness: Crate versions, GitHub release tags, changelog sections, and Rust editions are automatically extracted and partitioned under data/raw/{source}/{version}/. When new versions are pulled in subsequent runs, previous versions are preserved. See VERSIONING.md for full details.
Transform and Chunk Data:
uv run python scripts/06_transform_data.py
Outputs to data/processed/all_chunks.jsonl
Data Quality: The transform script applies several validation checks:
sha256(source_type:library:version:content), ensuring shared code between different versions is preserved for each version while removing identical duplicates within the same version.The script outputs a validation summary showing:
(library, version) pairs trackedStore in Vector Database (For Updates & Version-Aware RAG):
# Index all chunks into ChromaDB
uv run python scripts/07_vector_store.py
# Query with version filtering
uv run python scripts/07_vector_store.py --library axum --version 0.7.5 --query "routing and state"
# Query migration guidance between versions
uv run python scripts/07_vector_store.py --library axum --migrate-from 0.6.20 --migrate-to 0.7.5 --query "handlers"
# List tracked libraries and versions
uv run python scripts/07_vector_store.py --list-versions
Create Training Dataset:
uv run python scripts/08_create_dataset.py
Outputs data/datasets/train.jsonl and val.jsonl in Alpaca format with version-grounded instructions and migration questions.
For complete architectural details on multi-version retention, changelog section extraction, RAG querying, and Alpaca prompt generation, see VERSIONING.md.
This project uses Ruff for code formatting and linting.
# Format all Python files
uv run ruff format .
# Check for lint issues
uv run ruff check .
# Fix lint issues automatically
uv run ruff check . --fix
Uses the base HuggingFace model (ibm-granite/granite-4.2-8b) or a local model directory. The export script will later merge LoRA and create GGUF.
Upload your project folder to Unsloth Studio (or upload data/datasets/train.jsonl directly).
Open train_granite.py in the studio.
Ensure the dataset path matches the uploaded location.
Run the script:
Option 1: Use HuggingFace model (downloads if not cached)
uv run python train_granite.py
Option 2: Use local model directory
uv run python train_granite.py --model-path /path/to/granite-4.2-8b
Advanced: override training sizing
uv run python train_granite.py --max-seq-length 4096 --batch-size 1 --gradient-accumulation-steps 8
Training parameters:
409618train_qwen.py.The Nemotron 3 Nano training script provides production-grade features including validation, checkpointing, and hardware-aware configuration.
Review the configuration:
cat config/nemotron_training_config.yaml
Validate configuration before training (recommended):
uv run python train_nemotron3_nano.py --dry-run
Run training with default settings (RTX 4070 profile):
uv run python train_nemotron3_nano.py
Alternative: Use different hardware profile:
# RTX 6000 (24GB VRAM)
uv run python train_nemotron3_nano.py --hardware-profile rtx6000
# A100 (40GB VRAM)
uv run python train_nemotron3_nano.py --hardware-profile a100
# Development (minimal resources, fast iteration)
uv run python train_nemotron3_nano.py --hardware-profile dev
Resume from checkpoint:
uv run python train_nemotron3_nano.py --resume-from-checkpoint models/nemotron3_nano_rust_lora/checkpoints/checkpoint-50
Enable experiment tracking (optional W&B):
uv run python train_nemotron3_nano.py --wandb-project my-project --wandb-entity my-team
Features:
models/nemotron3_nano_rust_lora/manifest.json for artifact lineageTraining Parameters (RTX 4070 default):
Advanced: Custom hyperparameters:
uv run python train_nemotron3_nano.py \
--batch-size 2 \
--max-seq-length 2048 \
--num-epochs 5 \
--learning-rate 1e-4 \
--verbose
The current implementation uses three separate LoRA recipes:
train_granite.py (Granite 4.2 8B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projtrain_qwen.py (Qwen 3.5 4B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projtrain_nemotron3_nano.py (Nemotron 3 Nano 4B):
q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_projOnce training is complete (either locally or downloaded from Unsloth Studio):
Package the trained adapter with Script 09:
Script scripts/09_create_modelfile.py now exports the trained LoRA adapter as an adapter-only package and writes a Modelfile with base model metadata and training economics.
uv run python scripts/09_create_modelfile.py --model models/granite_rust_lora --name rust-granite --base granite
uv run python scripts/09_create_modelfile.py --model models/qwen3_5_4b_rust_lora --name rust-qwen --base qwen
uv run python scripts/09_create_modelfile.py --model models/nemotron3_nano_rust_lora --name rust-nemotron --base nemotron
Optionally, provide --base-model to override the base model ID or local path used in the generated Modelfile.
Merge and export GGUF with Script 10:
Use scripts/10_export_ollama.py to merge the adapter with the base model and export a runnable GGUF package.
Option 1: Standard export (downloads base model from HuggingFace if not cached)
# For Granite 4.2 8B
uv run python scripts/10_export_ollama.py --model models/granite_rust_lora --name rust-granite --base ibm-granite/granite-4.2-8b
# For Qwen 3.5 4B
uv run python scripts/10_export_ollama.py --model models/qwen3_5_4b_rust_lora --name rust-qwen --base Qwen/Qwen3.5-4B
# For Nemotron 3 Nano 4B
uv run python scripts/10_export_ollama.py --model models/nemotron3_nano_rust_lora --name rust-nemotron --base NVIDIA/Nemotron-3-Nano-4B
Option 2: Use local GGUF or model file
uv run python scripts/10_export_ollama.py --model models/granite_rust_lora --name rust-granite --base /path/to/granite-4.2-8b-Q4_K_M.gguf
(This skips downloading from HuggingFace and uses your local file directly.)
Script 10 merges the LoRA weights with the specified base model and quantizes the merged model to Q4_K_M.
Import into Ollama:
ollama create rust-granite -f models/rust-granite_gguf/Modelfile
Test the Model:
ollama run rust-granite "How do I set up a WiFi connection on an ESP32 using esp-wifi and async Rust?"
Because we stored the raw data in ChromaDB (a local vector database), you do not need to re-scrape everything when Rust updates.
Collect only new data:
python scripts/01_collect_rust_book.py # Gets updated docs
python scripts/02_collect_docs_rs.py # Gets new crate versions
Re-transform the new raw data:
python scripts/06_transform_data.py
Update the Vector Store: Instead of re-indexing everything, update specific sources:
from scripts.07_vector_store import VectorStore
vs = VectorStore()
# Deletes old docs_rs chunks, adds new ones
vs.update_source("docs_rs", "data/processed/docs_rs_chunks.jsonl")
Re-generate Dataset & Re-train:
uv run python scripts/08_create_dataset.py
# Re-run train_granite.py in Unsloth Studio
21 commits
Python
97.2%
PowerShell
1.8%
Shell
1.0%