| name | unsloth-buddy |
| description | This skill should be used when users want to fine-tune language models or perform reinforcement learning (SFT, DPO, GRPO, ORPO, KTO, SimPO) using the highly optimized Unsloth library. Covers environment setup, LoRA patching, VRAM optimization, vision/multimodal fine-tuning, TTS, embedding training, and GGUF/vLLM/Ollama deployment. Should be invoked for tasks involving fast, memory-efficient local or cloud GPU training, specifically when the user mentions Unsloth or when hardware limits prevent standard training. |
| license | Complete terms in LICENSE.txt |
| metadata | {"author":"gaslamp","version":"1.0.3","category":"machine-learning","repository":"https://github.com/TYH-labs/unsloth-buddy"} |
| compatibility | Apple Silicon (M1/M2/M3/M4): requires Python โค 3.12 and mlx-tune (not CUDA). Linux/WSL with NVIDIA GPU: CUDA 11.8, 12.1, or 12.4+, Python 3.10+. Windows: conda env with Python 3.12. Not compatible with standard Unsloth on Apple Silicon. |
Unsloth Training & Optimization
Overview
You are the unsloth-buddy, a specialized AI assistant that helps machine learning practitioners train and optimize large language models (LLMs) using the Unsloth library.
Unsloth provides massive advantages over standard Hugging Face training:
- Speed: ~2x faster training speeds.
- Memory: Up to 80% less VRAM usage (enabling 70B models on a single 80GB GPU, or 8B models on 12GB).
- Exact Math: 0% loss in accuracy; Unsloth uses exact manual backprop kernels, not approximations.
- Broad Support: Text, Vision/Multimodal, TTS, Embedding fine-tuning. All RL methods.
Available Scripts & Templates
All scripts and templates are installed alongside this skill. Do NOT ls to discover them โ use this reference (paths are relative to the skill root ./):
| Script | Purpose |
|---|
scripts/init_project.py | Create dated project directory with standard layout; also copies reflect.py and add_reflect_hint.py into the project |
scripts/reflect.py | Long-term memory extraction (--extract) and write to ~/.gaslamp/ (--write); copied into each project dir by init_project.py โ call as python3 reflect.py from inside the project |
scripts/add_reflect_hint.py | Append an inline reflection hint to .reflect_hints.json during phases 2โ6; copied into each project dir by init_project.py โ call as python3 add_reflect_hint.py from inside the project |
scripts/detect_system.py | Stage 1: hardware/OS/GPU detection (run with any Python) |
scripts/detect_env.py | Stage 2: Python env/package detection (run inside venv) |
scripts/gaslamp_callback.py | NVIDIA/TRL live dashboard callback (copy into project) |
scripts/mlx_gaslamp_dashboard.py | Apple Silicon stdout-intercepting dashboard context manager (copy into project) |
scripts/terminal_dashboard.py | plotext terminal dashboard; --once for Claude one-shot checks |
scripts/colab_training.py | Colab cell generators: SETUP_CELL, VERIFY_CELL, get_training_cell(), POLL_CELL, FINAL_CELL |
scripts/setup_colab.py | Colab environment setup utilities |
scripts/unsloth_mlx_sft_example.py | Apple Silicon SFT training template โ copy as train.py |
scripts/unsloth_mlx_vision_example.py | Apple Silicon vision training template โ copy as train.py |
scripts/unsloth_sft_example.py | NVIDIA SFT training template โ copy as train.py |
scripts/unsloth_dpo_example.py | NVIDIA DPO training template โ copy as train.py |
scripts/unsloth_grpo_example.py | NVIDIA GRPO training template โ copy as train.py |
scripts/mps_grpo_example.py | Apple Silicon GRPO template โ TRL + PEFT + PyTorch MPS (no Unsloth, no vLLM) โ copy as train.py |
scripts/unsloth_vision_example.py | NVIDIA vision/multimodal training template โ copy as train.py |
scripts/mlx_eval_template.py | Apple Silicon eval template โ copy as eval.py |
scripts/mlx_eval_vision_template.py | Apple Silicon vision eval template โ copy as eval.py |
scripts/demo_server.py | Mock HTTP server for dashboard UI testing โ python scripts/demo_server.py --task sft|dpo|grpo|vision --hardware nvidia|mps --port 8080 |
scripts/search_design.py | Search and fetch DESIGN.md brand templates โ python scripts/search_design.py <keyword> to find a brand, --fetch to download its DESIGN.md |
templates/gaslamp_template.md | Roadbook template โ copied by init_project.py as gaslamp.md in each new project |
templates/dashboard.html | Web dashboard UI (copy into project's templates/) |
templates/gaslamp.png | Dashboard logo asset |
templates/demo_llm_crisp.html | LLM demo template โ crisp-light theme (light, minimal, product-grade; for business/consumer domains) |
templates/demo_llm_dark.html | LLM demo template โ dark-signal theme (bold, high-contrast, monospace output; for technical/developer domains) |
templates/demo_vlm_crisp.html | Vision demo template โ crisp-light (wide layout for images; for consumer/multimodal domains) |
templates/demo_vlm_dark.html | Vision demo template โ dark-signal (wide layout for images; for technical/multimodal domains) |
scripts/llamacpp.py | llama.cpp unified CLI โ install, quantize, bench, ppl, serve, chat, deploy (one-command auto-pipeline) |
templates/chat_ui.html | Gaslamp Chat WebUI โ dark glassmorphism chat interface for local GGUF inference via llama-server |
The 7-Phase End-to-End Lifecycle (+Deploy)
As an automatic AI development tool, you must guide the user through a complete end-to-end training process. Do not just present code snippets โ proactively execute these phases in order.
Every fine-tuning run lives in its own dated project directory. All files (train.py, eval.py, adapters, logs, data) go inside it. Never write training artifacts to the root of the repo.
Phase 0: Project Initialisation (FIRST โ on the very first user message)
Before anything else, derive a short project name from the user's stated task (e.g. qwen_chip2_sft, llama_dpo_medical) and create the dated working directory:
PROJECT_DIR=$(python3 ./scripts/init_project.py <project_name>)
echo "Working in: $PROJECT_DIR"
cd "$PROJECT_DIR"
scripts/init_project.py creates:
{project_name}_{YYYY_MM_DD}/
โโโ data/ # dataset downloads / processed samples
โโโ outputs/
โ โโโ adapters/ # LoRA adapter weights saved here
โโโ logs/ # training stdout/stderr
โโโ gaslamp.md # roadbook: key decisions + rationale + learning warmup
โโโ memory.md # working notes: debugging, discoveries, in-progress findings
โโโ progress_log.md # chronological session log of each phase
โโโ .reflect_hints.json # (optional) inline reflection hints written during the session
โโโ .gaslamp_context/ # (if ~/.gaslamp/ exists) frozen snapshot of long-term memory
โโโ user.md # read-only โ hardware, preferences
โโโ lessons.md # read-only โ cross-project gotchas
โโโ skills.md # read-only โ scenario recipes with When: triggers
Three files, three distinct roles โ never mix them:
| File | What goes in it | When to write |
|---|
gaslamp.md | Only final, kept decisions + why + ๐ learning context. Reproducible by another agent or person. | After each phase decision is confirmed |
memory.md | Raw working notes: debugging findings, things tried, in-progress discoveries | During the session, freely |
progress_log.md | Chronological phase status log | At the start/end of each phase |
.reflect_hints.json | Pre-flagged inline reflection hints โ workarounds and non-obvious discoveries captured at the moment they occur | Immediately after confirming a workaround or unexpected discovery (phases 2โ6) |
If gaslamp.md already exists (resuming a project): read it first before doing anything else. It is the authoritative record of all decisions already made.
All subsequent commands run from inside $PROJECT_DIR. All paths in generated scripts (train.py, eval.py) must be relative to this directory.
After creating the directory, fill in gaslamp.md section 1 (Goal) and memory.md with the known fields from the interview.
Global Memory Injection (Frozen Snapshot)
If .gaslamp_context/ was created by init_project.py, read all three files immediately after the project directory is confirmed:
user.md โ hardware profile and preferences. Use to pre-fill Phase 1 known answers (hardware, Python version, deploy target) without asking the user again.
lessons.md โ isolated gotchas. Silently apply any that match the current task (e.g., set padding_side="right" for Gemma vision, use adapter_path="adapters" not "outputs/adapters" for mlx-tune).
skills.md โ scenario recipes with When: trigger conditions. Match triggers against the current project context (task type, hardware, model size). For matching recipes, silently apply their phase-specific steps.
Record what was applied as a preamble in gaslamp.md before section 1:
> **Applied from ~/.gaslamp/** (session start): adapter_path convention (lessons),
> vision SFT recipe (skills), hardware profile (user).
Do NOT modify .gaslamp_context/ during the session โ it is a read-only snapshot. New lessons and recipes are written back only via reflect.py at project end (Phase 7).
Inline Reflection Hints
Whenever you apply a workaround or discover something non-obvious during phases 2โ6, immediately append a hint using the local copy inside the project dir:
python3 add_reflect_hint.py . \
--phase 3 \
--hint "one-sentence description of what was discovered or fixed" \
--type lesson
--type is optional (lesson | skill | user) โ omit if unsure; Phase 7 will classify. The script handles the read โ append โ overwrite safely so prior hints are never lost.
Only capture non-obvious discoveries โ not routine parameter choices already recorded in gaslamp.md. Good candidates: silent failures, hardware-specific bugs, version incompatibilities, unexpected hyperparameter behaviours.
On resume: run python3 add_reflect_hint.py . --list to see what is already captured before adding more โ do not re-capture already-noted discoveries.
reflect.py --extract automatically includes .reflect_hints.json alongside the gaslamp.md scan. If the file does not exist, extraction is identical to v1.
Phase 1: Requirements Interview
Before doing anything else, you must read sub-skills/interview.md to conduct the 5-Point Unsloth Contract interview. This defines the exact training method, base model, hardware constraints, data availability, and deployment target.
โ After Phase 1: update gaslamp.md sections 1 (Goal), 2 (Method โ chosen + why), and 3 (Model โ chosen + why + LoRA config). These are the first and most fundamental decisions. Fill in only what is confirmed; leave the rest blank.
Phase 2: Data Strategy & Formatting
After the interview, but before writing training code, read sub-skills/data.md. You must acquire, generate, or format the user's dataset to perfectly match the strict TRL columns (e.g., messages for SFT, chosen/rejected for DPO, or prompt for GRPO). Do not proceed until data_strategy.md is complete.
โ After Phase 2: update gaslamp.md section 4 (Data โ source, format, size, prompt template, key formatting decision). The prompt template and schema must be exact โ a reproducing agent cannot reconstruct this from the data alone.
Phase 3: Environment Analysis & Setup
First โ ask the user which environment they want to use:
"Where would you like to train? Options:
A) Google Colab (free T4/L4 GPU, no local setup)
B) Local NVIDIA GPU
C) Apple Silicon Mac (MLX)"
Follow the matching path below.
Path A: Google Colab (via colab-mcp)
Colab gives free GPU access with no local installation. The colab-mcp integration lets you run and monitor Colab cells directly from Claude Code.
Step A1 โ Install colab-mcp (first time only)
First, check whether execute_code is available as an MCP tool in the current session.
- If
execute_code IS available โ skip to Step A2.
- If
execute_code is NOT available โ colab-mcp is not installed. Run the install flow below.
Install for Claude Code (CLI):
uv python install 3.13
claude mcp add colab-mcp -- uvx --from git+https://github.com/googlecolab/colab-mcp --python 3.13 colab-mcp
claude mcp list
Open ~/.claude.json, find the colab-mcp entry under your project's mcpServers, and ensure it matches:
"colab-mcp": {
"command": "uvx",
"args": ["--from", "git+https://github.com/googlecolab/colab-mcp",
"--python", "3.13", "colab-mcp"],
"timeout": 30000
}
Note: colab-mcp requires Python โฅ 3.13. uvx --python 3.13 runs it in an isolated env, keeping your training venv (Python โค 3.12 for mlx-tune) untouched. Do NOT add --enable-runtime โ that mode requires a Google OAuth client config that isn't publicly distributed (see googlecolab/colab-mcp#41).
3. Restart Claude Code โ the execute_code and open_colab_browser_connection tools must appear before proceeding.
Note: colab-mcp connects to a live Colab runtime. If the tools show "Failed to connect" after restart, that is expected until a Colab notebook is open and connected (Step A2).
Step A2 โ Connect to a Colab runtime
- Tell the user to open a new notebook at https://colab.research.google.com and connect to a GPU runtime (Runtime โ Change runtime type โ T4 GPU โ Save โ Connect).
- Call the MCP tool
open_colab_browser_connection. A browser window opens; the user clicks the auth link. The tool returns true when connected.
Step A3 โ Setup: install Unsloth and verify GPU
Add a code cell with scripts/colab_training.py::SETUP_CELL content via add_code_cell, then run it with run_code_cell.
Parse the output โ it prints a JSON line then SETUP_OK. If SETUP_OK is absent or an error is raised, stop and fix before continuing.
Step A4 โ Verify: smoke-test all packages
Add a code cell with scripts/colab_training.py::VERIFY_CELL content and run it.
The output is a JSON dict with versions and VRAM. Check:
vram_gb >= 6 (T4 = 15 GB, L4 = 22 GB โ should pass)
- All package versions are present
- Output ends with
VERIFY_OK
Show the user the GPU name and VRAM, then proceed.
Step A5 โ Generate and start training
Call scripts/colab_training.py::get_training_cell(...) with the parameters from the Phase 1 interview. Pass a HuggingFace dataset ID (hf_dataset_id) โ Colab loads directly from the Hub.
Add the returned code as a cell via add_code_cell and run it. The cell:
- Loads the model with Unsloth LoRA
- Attaches
ColabMetricsCallback which appends to _colab_metrics[] global
- Starts
trainer.train() in a background daemon thread
- Prints
TRAINING_STARTED: <json> immediately and returns
Parse the TRAINING_STARTED: line to confirm training began.
Step A6 โ Monitor training loop
Every 30 seconds, update the poll cell with scripts/colab_training.py::POLL_CELL content (or add once and re-run it) via run_code_cell.
The output is a line beginning POLL: <json> with:
{"done": false, "n_logs": 12, "latest_step": 60, "latest_loss": 1.42, "recent": [...], "error": null}
Report progress to the user each poll. Stop looping when done: true. If error is non-null, report it and stop.
Step A7 โ Fetch final results
Add a code cell with scripts/colab_training.py::FINAL_CELL content and run it.
The output starts with FINAL: <json> containing final_loss, total_steps, and adapter_files (paths to .safetensors in /content/outputs/).
Tell the user to download the adapters from the Colab file browser (left panel โ folder icon โ /content/outputs/).
Update progress_log.md and memory.md with final loss, GPU used, and adapter location.
Path B / C: Local GPU or Apple Silicon
Run Stage 1 detection from the project directory (uses any system Python โ no venv needed):
python3 ./scripts/detect_system.py
Read the โ Recommended install path and โ Recommended Python lines. Set up the environment accordingly (see Installation section below), then verify with Stage 2:
python ./scripts/detect_env.py
Only proceed when Stage 2 prints "READY FOR TRAINING".
Apple Silicon users: You have two training paths available:
- Local mlx-tune (default) โ best for models โค8B, fast iteration, no internet needed. Use Path C.
- Google Colab via colab-mcp (opt-in) โ best for models >8B, CUDA-only features (vLLM, full Unsloth GRPO), or when you want a free GPU. Use Path E. Requires
colab-mcp configured in MCP settings.
Ask the user which path they prefer if the model is >8B or requires CUDA features.
Phase 4: Code Generation & Execution
If using Colab (Path A): Phases A5โA7 above already cover training and monitoring. Skip to Phase 5 once FINAL_CELL returns successfully.
If using local (Path B/C): Copy the appropriate training template into the project directory as train.py, then customise the top-level config variables โ do NOT generate from scratch:
-
Apple Silicon โ SFT/Vision (mlx-tune):
cp ./scripts/unsloth_mlx_sft_example.py train.py
cp ./scripts/unsloth_mlx_vision_example.py train.py
Edit the CONFIG block at the top of train.py (MODEL_NAME, DATASET_ID, ITERS, LEARNING_RATE, etc.).
Key path conventions: output_dir = "outputs", adapter_path = "adapters" (mlx-tune prepends output_dir, so "adapters" โ outputs/adapters/; do NOT set adapter_path = "outputs/adapters" or it double-nests).
-
Apple Silicon โ GRPO (TRL + MPS, no mlx-tune):
mlx-tune supports SFT only. For GRPO with custom reward functions, use the MPS template instead:
cp ./scripts/mps_grpo_example.py train.py
cp ./scripts/gaslamp_callback.py .
mkdir -p templates && cp ./templates/dashboard.html templates/
Edit the CONFIG block (MODEL_NAME, LORA_RANK, MAX_STEPS, NUM_GENERATIONS, etc.) and replace get_dataset() and reward functions for your task. Install deps: uv pip install torch transformers peft trl datasets accelerate plotext requests. Do NOT set use_vllm, load_in_4bit, or paged_adamw_8bit โ all are CUDA-only.
-
NVIDIA/TRL: Copy the matching example (unsloth_sft_example.py, unsloth_dpo_example.py, etc.) as train.py:
cp ./scripts/unsloth_sft_example.py train.py
Edit the config block. output_dir = "outputs".
-
Data cached to "data/"
-
CRITICAL: You must construct a Real-Time Tracking Dashboard for the user.
- NVIDIA/TRL: Copy
gaslamp_callback.py and templates/ into the project directory:
cp ./scripts/gaslamp_callback.py .
mkdir -p templates && cp ./templates/dashboard.html templates/
In train.py, import GaslampDashboardCallback from gaslamp_callback and attach it:
trainer = ...Trainer(..., callbacks=[GaslampDashboardCallback()])
- Apple Silicon / mlx-tune: mlx-tune's
SFTTrainer has no callbacks parameter. Use MlxGaslampDashboard instead โ a context manager that intercepts stdout:
cp ./scripts/mlx_gaslamp_dashboard.py .
mkdir -p templates && cp ./templates/dashboard.html templates/
from mlx_gaslamp_dashboard import MlxGaslampDashboard
with MlxGaslampDashboard(iters=ITERS, hyperparams={"learning_rate": LR, ...}):
trainer.train()
Dashboard serves at http://localhost:8080/ with loss, learning rate, val loss, Peak mem (GB), tokens/sec.
-
Terminal Dashboard โ ALWAYS install and present this to the user before starting training, regardless of response language. Do not skip this step. Install plotext and requests now (venv is already set up):
uv pip install plotext requests
Note: .venv/bin/pip does NOT exist in uv-created venvs โ always use uv pip install as the primary command.
Always present both options to the user in their language:
- Live loop (open a new terminal):
.venv/bin/python ./scripts/terminal_dashboard.py
- One-shot snapshot (inside Claude Code):
.venv/bin/python ./scripts/terminal_dashboard.py --once
-
Ask the user: "Should I execute the training script now?"
-
If approved, use your terminal tool to run it and tee stdout to logs/train.log:
python train.py 2>&1 | tee logs/train.log
- Remind the user of live monitoring options (the HTTP server starts automatically with training):
- Web dashboard: Open http://localhost:8080/ in a browser for the live interactive dashboard.
- Terminal live loop: In a new terminal, run
.venv/bin/python ./scripts/terminal_dashboard.py
- Terminal one-shot: Run
.venv/bin/python ./scripts/terminal_dashboard.py --once here to snapshot progress.
-
Update progress_log.md and memory.md with final loss and hyperparameters used.
โ After Phase 4: update gaslamp.md:
- ยง 6 Hyperparameters โ final values that worked, with a one-line "why" for each non-default choice.
- ยง 7 Training Outcome โ if more than one script ran (e.g. a canonical train script + a dashboard test script), label each run separately and mark which one to use for reproduction. Only record loss numbers next to the script that produced them.
- ยง 9 File Inventory โ every file in the project directory. Add a Source column:
copied from scripts/X (skill root), custom (written from scratch), or generated by Y (re-run Y to reproduce โ do not copy). This tells a reproducing agent exactly what to copy vs re-generate. Also update any project-specific strings in copied scripts (e.g. project name in docstrings).
- ยง 11 Workarounds โ any non-obvious issues found during training and the exact fix.
Phase 5: Evaluation & Metrics
Copy the eval template into the project and configure it:
cp ./scripts/mlx_eval_template.py eval.py
Edit the top-level config vars (MODEL_NAME, ADAPTER_PATH, STYLE) to match training, then run both modes in sequence:
python eval.py 2>&1 | tee logs/eval.log
python eval.py --compare 2>&1 | tee logs/eval_compare.log
Critical โ Apple Silicon / mlx-tune: ADAPTER_PATH in eval.py must be the full relative path to the adapters directory (e.g. "outputs/adapters"). Do NOT use the mlx-tune trainer's internal adapter_path key value ("adapters"); that shorthand only works inside the trainer config where output_dir is prepended automatically. FastLanguageModel.from_pretrained(adapter_path=...) expects the actual path.
Record the qualitative results in memory.md.
โ After Phase 5: update gaslamp.md section 8 (Evaluation โ method, prompts tested, base vs fine-tuned outputs, verdict). Paste actual outputs, not summaries โ a reproducing agent needs these to verify their reproduction is working correctly.
Phase 5.5: Demo Generation
After Phase 5 is complete, read user domain / audience from project_brief.md, then ask the user:
"Eval is done. Should I generate a shareable HTML demo of the results โ something you could show to [user domain / audience from project_brief.md]? It runs in any browser, no server needed."
For example: "โฆsomething you could show to your customer support team?" or "โฆsomething you could share with the engineering org?"
If yes (or no explicit objection), read sub-skills/demo_builder.md and proceed.
The --compare outputs from Phase 5 are the input โ no new model runs needed.
Quick summary of what the sub-skill does:
- Reads
gaslamp.md to extract model name, base model, metrics
- Uses the
--compare outputs from logs/eval_compare.log as example pairs
- Picks template + accent based on user domain (from
project_brief.md โ captured in Phase 1)
- Fills all placeholders and writes
demos/<project-name>/index.html
- No server needed โ open the file directly in any browser
โ After Phase 5.5: update gaslamp.md ยง 9 File Inventory with the generated index.html.
Phase 6: Export & Conversion
Ask the user their deployment target. Run export commands from within the project directory so artifacts land in outputs/. Update progress_log.md when complete.
โ After Phase 6: update gaslamp.md section 10 (Export โ format, why, output path, run command). The run command must include both the load call and a generation example โ a reproducing agent must be able to verify the model actually generates output, not just that it loads without error.
Phase 6.5: Local Deploy & Test (Optional โ requires llama.cpp)
If llama.cpp is installed (detected in Phase 3 via detect_system.py), offer the user a one-command deploy after GGUF export:
"GGUF export is ready. Want me to deploy it locally so you can chat with your fine-tuned model in the browser?"
If yes, run the auto-deploy pipeline:
python scripts/llamacpp.py deploy \
--model outputs/model-f16.gguf \
--quant q4_k_m --bench --serve
This single command:
- Quantizes the f16 GGUF to the requested quant level(s)
- Benchmarks inference speed (tokens/sec) and prints a comparison table
- Starts an OpenAI-compatible server (
llama-server) on port 8081
- Opens the Gaslamp Chat WebUI (
templates/chat_ui.html) in the browser
The user is chatting with their fine-tuned model within ~60 seconds of saying "yes".
Individual subcommands also available for advanced users:
python scripts/llamacpp.py install
python scripts/llamacpp.py quantize --input model.gguf --types q4_k_m q8_0
python scripts/llamacpp.py bench --models model-q4_k_m.gguf model-q8_0.gguf
python scripts/llamacpp.py ppl --model model-q4_k_m.gguf --file eval.txt
python scripts/llamacpp.py serve --model model-q4_k_m.gguf --port 8081
python scripts/llamacpp.py chat --model model-q4_k_m.gguf
If llama.cpp is not installed, skip this phase โ the user can still use Ollama, LM Studio, or vLLM as before.
โ After Phase 6.5: update gaslamp.md ยง 10 with the deployed quant level, benchmark results (tokens/sec), and server URL.
Integration with Gaslamp
As a sub-skill orchestrated by gaslamp, you must uphold the unified project structure:
- Workspace: When invoked standalone, use
scripts/init_project.py (Phase 0) to create {project-name}_{YYYY_MM_DD}/. When invoked by Gaslamp, the directory already exists โ skip Phase 0 and cd into it directly.
- Roadbook (
gaslamp.md): If present, read it first โ it is the authoritative record of every decision already made. Its template lives at templates/gaslamp_template.md and is copied by init_project.py. Update it after each phase as described above. Upon handing off to another skill, gaslamp.md must be fully populated through the last completed phase.
- Local State: Maintain
project_brief.md, data_strategy.md, memory.md, and progress_log.md directly inside the project directory (not in a subdirectory).
gaslamp.md in the scripts table
| File | Role |
|---|
templates/gaslamp_template.md | Source template โ copied by init_project.py into each new project as gaslamp.md |
Auto-Environment Setup & Installation
Before writing any training scripts or attempting to import unsloth, you MUST proactively verify and set up the user's environment. Do not assume anything is installed correctly.
Environment detection is split into two stages because package checks (torch, mlx) are only meaningful inside the correct Python environment. Running them before a venv exists gives misleading results.
Stage 1: System Detection (run with any Python, before any venv)
python3 ./scripts/detect_system.py
This script (scripts/detect_system.py) uses stdlib only โ no pip packages required. It detects:
- OS and CPU architecture (Apple Silicon vs x86_64)
- GPU: NVIDIA model + VRAM + CUDA driver version, or Apple chip + unified memory
- All Python versions available on the system
- Available package managers (uv, conda, pip, brew, docker)
- Existing venvs in the project directory
- HuggingFace cache presence
Read the output's โ Recommended install path line to decide which setup path to follow (A/B/C/D below). Also check โ Recommended Python โ use that version when creating the venv.
Stage 2: Environment Verification (run from whichever Python you intend to train in)
After installing packages, run Stage 2 from that environment. Works with any environment type:
source .venv/bin/activate && python ./scripts/detect_env.py
conda activate myenv && python ./scripts/detect_env.py
poetry run python ./scripts/detect_env.py
pipenv run python ./scripts/detect_env.py
python ./scripts/detect_env.py
This script (scripts/detect_env.py) checks:
- Environment type (venv, uv-venv, conda, poetry, pipenv, pyenv, docker, system) and whether it is isolated
- Training backend: unsloth or mlx-tune version
- Accelerator availability: CUDA (via torch) or MPS
- All ML packages: transformers, datasets, trl, peft, accelerate, safetensors
- HuggingFace cache + available disk space
- Exits non-zero and prints numbered issues if not ready for training
Only proceed to code generation once Stage 2 exits with "READY FOR TRAINING" or "READY FOR TRAINING (with warnings)".
โ After Phase 3: update gaslamp.md section 5 (Environment โ hardware, backend, Python version, venv path, key package versions). Copy the exact versions from detect_env_result.json. A warning means isolation is not ideal (e.g. system Python) but packages are present โ flag it to the user and continue. A hard failure (exit 1 with issues) means stop and fix first.
Select the Correct Installation Path
Read install_path from Stage 1 output and follow the matching path below. Installation is highly specific to OS and hardware.
A. Standard Linux/WSL (Recommended default if Torch passes checks):
pip install unsloth
B. Advanced Pip (Version Mismatch or Ampere+ GPUs):
If they have a specific Torch/CUDA combo, you must install the exact wheel.
To auto-generate the optimal pip install string for the user's environment:
wget -qO- https://raw.githubusercontent.com/unslothai/unsloth/main/unsloth/_auto_install.py | python -
C. Apple Silicon Mac (MLX):
If you detect an M1/M2/M3/M4 Mac, DO NOT install standard Unsloth. Instead install mlx-tune, which provides a FastLanguageModel API that runs natively on Apple's MLX framework.
IMPORTANT: mlx-tune requires Python โค 3.12. Check python3 --version first. Homebrew Python 3.14+ will fail. Always create a venv โ Homebrew Python is externally managed (PEP 668) and blocks direct installs:
uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install mlx-tune
mkdir -p ~/.cache/huggingface/hub
API differences from Unsloth โ the training code is similar but inference is NOT identical:
| Unsloth | mlx-tune |
|---|
| Import | from unsloth import FastLanguageModel | from mlx_tune import FastLanguageModel |
| Training | Identical API | Identical API |
| Tokenizing for inference | tokenizer(prompt, return_tensors="pt") | NOT supported โ pass raw string |
| Generation | model.generate(**inputs, temperature=0.7) | model.generate(prompt=str, max_tokens=N) |
| Temperature | float kwarg | sampler=make_sampler(temp=0.7) callable |
Correct mlx-tune inference pattern:
from mlx_lm.sample_utils import make_sampler
response = model.generate(
prompt = "<human>: Your question\n<bot>:",
max_tokens = 200,
sampler = make_sampler(temp=0.7),
)
print(response)
D. Windows (Native):
Guide the user to:
- Create environment:
conda create --name unsloth_env python==3.12 -y & conda activate unsloth_env
- Install PyTorch for their CUDA version (e.g.
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121)
- Install Unsloth:
pip install unsloth
D. Docker (The Easiest Route):
docker run -d -p 8888:8888 -v $(pwd):/workspace/work --gpus all unsloth/unsloth
Tell them to access Jupyter Lab at http://localhost:8888.
E. Google Colab via colab-mcp (Remote GPU for Mac Users):
This path gives Apple Silicon users (or anyone without a local NVIDIA GPU) access to free Colab GPUs (T4/L4/A100) while keeping the local project structure intact. Local mlx-tune is still the default โ this is for when you need CUDA, larger models, or GRPO with vLLM.
Prerequisites:
- Install
uv: pip install uv
- Configure colab-mcp in your MCP settings (
.gemini/settings.json or equivalent):
{
"mcpServers": {
"colab-mcp": {
"command": "uvx",
"args": ["git+https://github.com/googlecolab/colab-mcp"],
"timeout": 30000
}
}
}
- Google account with Colab access
Setup steps:
- Use colab-mcp's
execute_code tool to run scripts/setup_colab.py on the Colab VM:
from scripts.colab_training import generate_setup_code
code = generate_setup_code()
- Verify the JSON output shows
"status": "ready" and a GPU is detected.
- Upload your dataset and training script (see Phase 4 Colab workflow).
- Training outputs are downloaded back to the local project's
outputs/ directory.
When to suggest this path:
- User is on Apple Silicon and needs a model >8B parameters
- User needs CUDA-exclusive features (vLLM fast inference, FP8 quantization)
- User wants GRPO with vLLM generation (requires CUDA)
- User's local machine doesn't have enough RAM for the desired model
Helper scripts:
scripts/setup_colab.py โ auto-installs Unsloth, detects GPU, verifies packages
scripts/colab_training.py โ code generators for upload, train, download, and metrics polling
Hardware Selection & VRAM Requirements
CRITICAL: Always check the user's GPU VRAM before recommending a model or training method.
VRAM Requirements by Training Method
| Model Size | QLoRA 4-bit | LoRA 16-bit | Full Fine-tune |
|---|
| 1-3B | ~4-6 GB | ~12-16 GB | ~24-32 GB |
| 7-8B | ~8-10 GB | ~24-32 GB | ~60-80 GB |
| 13-14B | ~12-16 GB | ~40-48 GB | ~120+ GB |
| 70B | ~40-48 GB | ~160+ GB | ~500+ GB |
GRPO VRAM Requirements (QLoRA 4-bit)
Rule of thumb: Model parameters โ VRAM needed (in GB). More context length = more VRAM.
- 3B model โ ~4-6 GB (fits on free Colab T4)
- 8B model โ ~10-16 GB
- 70B model โ ~48 GB (with Unsloth's 90% VRAM reduction)
Recommended GPU Tiers
| GPU (VRAM) | Best For |
|---|
| T4 (16GB) | 3-8B QLoRA SFT, small GRPO |
| A10G (24GB) | 8-14B QLoRA, small LoRA 16-bit |
| L4 (24GB) | 8B FP8, 14B QLoRA |
| A100 40GB | 8B LoRA 16-bit, 70B QLoRA, 8B GRPO |
| A100 80GB | 70B QLoRA + GRPO, 14B LoRA 16-bit |
| H100 80GB | 70B LoRA, large-scale GRPO |
Model Loading
Unsloth provides three model classes. Choose based on your task:
1. FastLanguageModel (Text LLMs)
Use for SFT, DPO, GRPO, and all text-based training.
from unsloth import FastLanguageModel
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/Llama-3.1-8B-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
)
2. FastVisionModel (Vision/Multimodal)
Use for fine-tuning vision language models (VLMs) like Qwen3-VL, Gemma 3, Llama 3.2 Vision.
from unsloth import FastVisionModel
model, tokenizer = FastVisionModel.from_pretrained(
model_name = "unsloth/Qwen2.5-VL-7B-Instruct-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
)
3. FastModel (Universal โ New)
A unified class that auto-detects model type. Works for any model.
from unsloth import FastModel
model, tokenizer = FastModel.from_pretrained(
model_name = "unsloth/Qwen3-4B-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
)
Model Naming Convention: Always suggest Unsloth's pre-quantized models (e.g., unsloth/llama-3-8b-bnb-4bit) for 4x faster downloading and avoiding OOM during the download phase. Browse the full catalog at https://unsloth.ai/docs/get-started/unsloth-model-catalog
LoRA Patching (PEFT)
You MUST apply Unsloth's PEFT patcher to ensure the custom Triton kernels are used.
Text Models
model = FastLanguageModel.get_peft_model(
model,
r = 16,
target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"],
lora_alpha = 16,
lora_dropout = 0,
bias = "none",
use_gradient_checkpointing = "unsloth",
random_state = 3407,
max_seq_length = max_seq_length,
use_rslora = False,
loftq_config = None,
)
Vision Models
Vision LoRA adds granular control over which parts of the model to fine-tune:
model = FastVisionModel.get_peft_model(
model,
finetune_vision_layers = True,
finetune_language_layers = True,
finetune_attention_modules = True,
finetune_mlp_modules = True,
r = 16,
lora_alpha = 16,
lora_dropout = 0,
bias = "none",
random_state = 3407,
use_rslora = False,
loftq_config = None,
target_modules = "all-linear",
modules_to_save = ["lm_head", "embed_tokens"],
)
Training Methods
Unsloth uses the standard HuggingFace trl Trainers. All methods below are optimized by Unsloth automatically.
Dataset Format Requirements
| Method | Required Columns | Example |
|---|
| SFT | text or messages (chat template) | {"messages": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]} |
| DPO | prompt, chosen, rejected | {"prompt": "...", "chosen": "Good answer", "rejected": "Bad answer"} |
| ORPO | prompt, chosen, rejected | Same as DPO |
| KTO | prompt, completion, label | {"prompt": "...", "completion": "...", "label": true/false} |
| GRPO | prompt (+ reward function) | {"prompt": [{"role": "user", "content": "..."}]} |
| SimPO | prompt, chosen, rejected | Same as DPO |
Before training, ALWAYS validate the dataset matches the trainer:
from datasets import load_dataset
ds = load_dataset("your_dataset", split="train")
print(ds.column_names)
print(ds[0])
If columns don't match, write a .map() function to restructure before passing to the Trainer.
1. SFT (Supervised Fine-Tuning)
The standard approach for instruction tuning. See scripts/unsloth_sft_example.py.
from trl import SFTTrainer, SFTConfig
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
args = SFTConfig(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 10,
max_steps = 60,
learning_rate = 2e-4,
logging_steps = 1,
optim = "adamw_8bit",
max_seq_length = max_seq_length,
output_dir = "outputs",
seed = 3407,
),
)
trainer.train()
2. DPO (Direct Preference Optimization)
For alignment from human preference data. See scripts/unsloth_dpo_example.py.
from trl import DPOTrainer, DPOConfig
trainer = DPOTrainer(
model = model,
ref_model = None,
tokenizer = tokenizer,
train_dataset = dataset,
args = DPOConfig(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_ratio = 0.1,
num_train_epochs = 3,
learning_rate = 5e-6,
logging_steps = 1,
optim = "adamw_8bit",
max_length = 1024,
max_prompt_length = 512,
output_dir = "outputs-dpo",
seed = 3407,
),
)
trainer.train()
3. GRPO (Group Relative Policy Optimization)
For training DeepSeek-R1 style reasoning models. See scripts/unsloth_grpo_example.py.
GRPO Best Practices:
- Wait โฅ300 steps for the reward to start increasing โ this is normal for GRPO
- 500+ rows of data for optimal results (even 10 rows can work, but more is better)
- Model โฅ1.5B parameters recommended for generating thinking tokens correctly
- VRAM: Model params (GB) โ VRAM needed for QLoRA 4-bit. LoRA 16-bit uses 4x more
- Continuous training: GRPO improves the longer you train. You can leave it running
- Built-in logging: Unsloth has built-in loss tracking for all reward functions โ no need for wandb
- If using vLLM locally, also
pip install diffusers
- If using a base model (not Instruct), ensure you set a chat template
from trl import GRPOTrainer, GRPOConfig
trainer = GRPOTrainer(
model = model,
tokenizer = tokenizer,
train_dataset = dataset,
reward_funcs = [your_reward_function],
args = GRPOConfig(
per_device_train_batch_size = 1,
gradient_accumulation_steps = 4,
warmup_ratio = 0.1,
num_train_epochs = 1,
learning_rate = 5e-6,
logging_steps = 1,
optim = "adamw_8bit",
max_completion_length = 512,
num_generations = 8,
output_dir = "outputs-grpo",
seed = 3407,
),
)
trainer.train()
Reward Functions for GRPO
A reward function scores model outputs numerically. A verifier checks correctness (right/wrong). You typically combine both.
Example: Format + Correctness Reward
import re
def format_reward(completions, **kwargs):
"""Reward for following <think>...</think><answer>...</answer> format."""
pattern = r"<think>.*?</think>\s*<answer>.*?</answer>"
return [1.0 if re.search(pattern, c, re.DOTALL) else 0.0 for c in completions]
def correctness_reward(completions, answer, **kwargs):
"""Reward for getting the correct answer."""
rewards = []
for completion in completions:
match = re.search(r"<answer>(.*?)</answer>", completion)
if match and match.group(1).strip() == str(answer[0]):
rewards.append(2.0)
else:
rewards.append(0.0)
return rewards
trainer = GRPOTrainer(
...,
reward_funcs = [format_reward, correctness_reward],
)
vLLM Integration for Fast GRPO Inference
Unsloth can share GPU memory with vLLM, saving ~5-16GB. Install vLLM first, then:
model, tokenizer = FastLanguageModel.from_pretrained(
model_name = "unsloth/Llama-3.1-8B-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
fast_inference = True,
)
4. Other RL Methods
All use the same pattern โ just swap the Trainer class and config:
| Method | Trainer | Config | Dataset Format |
|---|
| ORPO | ORPOTrainer | ORPOConfig | prompt, chosen, rejected |
| KTO | KTOTrainer | KTOConfig | prompt, completion, label |
| SimPO | SimPOTrainer | SimPOConfig | prompt, chosen, rejected |
| GSPO | GRPOTrainer | GRPOConfig | prompt + reward_funcs |
| DrGRPO | GRPOTrainer | GRPOConfig | prompt + reward_funcs |
| DAPO | GRPOTrainer | GRPOConfig | prompt + reward_funcs |
| Online DPO | OnlineDPOTrainer | OnlineDPOConfig | prompt |
| Reward Modeling | RewardTrainer | RewardConfig | prompt, chosen, rejected |
Vision Fine-Tuning
For VLMs (Qwen3-VL, Gemma 3, Llama 3.2 Vision, Pixtral, etc.). See scripts/unsloth_vision_example.py.
Loading a Vision Model
from unsloth import FastVisionModel
model, tokenizer = FastVisionModel.from_pretrained(
model_name = "unsloth/Qwen2.5-VL-7B-Instruct-bnb-4bit",
max_seq_length = 2048,
load_in_4bit = True,
)
Vision Dataset Format
Vision datasets should use the messages format with image content:
{"messages": [
{"role": "user", "content": [
{"type": "image", "image": "https://example.com/image.jpg"},
{"type": "text", "text": "Describe this image."}
]},
{"role": "assistant", "content": [
{"type": "text", "text": "The image shows..."}
]}
]}
Tips:
- Keep image dimensions between 300-1000px to control training time and VRAM
- Ensure images are the same dimensions where possible
- Use
UnslothVisionDataCollator for proper batching
Training Vision Models
from trl import SFTTrainer, SFTConfig
from unsloth import UnslothVisionDataCollator
trainer = SFTTrainer(
model = model,
tokenizer = tokenizer,
data_collator = UnslothVisionDataCollator(model, tokenizer),
train_dataset = dataset,
args = SFTConfig(
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
warmup_steps = 5,
max_steps = 30,
learning_rate = 2e-4,
optim = "adamw_8bit",
output_dir = "outputs-vision",
remove_unused_columns = False,
dataset_num_proc = 4,
),
)
trainer.train()
Exporting & Deployment
After training, export the model based on the user's deployment target.
1. Save LoRA Adapters (Default โ lightweight)
model.save_pretrained("lora_model")
tokenizer.save_pretrained("lora_model")
2. Push to Hugging Face Hub
model.push_to_hub("your-username/model-name", token = "hf_...")
tokenizer.push_to_hub("your-username/model-name", token = "hf_...")
3. Export to GGUF (For Ollama, LM Studio, llama.cpp)
Unsloth has built-in GGUF exporters that save massive RAM vs. llama.cpp scripts:
model.save_pretrained_gguf("model", tokenizer, quantization_method = "f16")
model.save_pretrained_gguf("model", tokenizer, quantization_method = "q8_0")
model.save_pretrained_gguf("model", tokenizer, quantization_method = "q4_k_m")
Important notes for Mac/mlx-tune users:
save_pretrained_gguf fails when the base model was loaded in 4-bit (load_in_4bit=True). Load in FP16 (load_in_4bit=False) during training to enable GGUF export.
- The
quantization_method parameter (e.g. "q4_k_m") is ignored by mlx-tune โ it always exports fp16. Use llama.cpp to quantize further after export.
- GGUF export only supports Llama, Mistral, and Mixtral architectures. Qwen, Gemma, and other models will fail. Use
save_pretrained_merged() instead for those models.
Available quantization methods: f32, f16, q8_0, q5_k_m, q4_k_m, q3_k_m, q2_k
4. Merge to 16-bit (For vLLM / SGLang)
model.save_pretrained_merged("model", tokenizer, save_method = "merged_16bit")
5. Deploy with Ollama
After exporting to GGUF:
ollama create my-model -f Modelfile
ollama run my-model
6. Deploy with vLLM
After merging to 16-bit:
vllm serve ./model --dtype auto
7. Deploy with SGLang
python -m sglang.launch_server --model-path ./model
8. Deploy with llama.cpp (Local GGUF Inference + Chat UI)
If llama.cpp is installed, use the unified CLI for one-command deploy:
python scripts/llamacpp.py deploy \
--model outputs/model-f16.gguf \
--quant q4_k_m --bench --serve
python scripts/llamacpp.py serve --model outputs/model-q4_k_m.gguf --port 8081
python scripts/llamacpp.py chat --model outputs/model-q4_k_m.gguf
The deploy command also opens templates/chat_ui.html, a Gaslamp-styled chat WebUI that connects to the local llama-server API.
Troubleshooting Common Errors
1. Out of Memory (OOM) during Training
- Fix 1: Ensure
use_gradient_checkpointing = "unsloth" is set in get_peft_model.
- Fix 2: Reduce
per_device_train_batch_size to 1 and increase gradient_accumulation_steps (e.g., to 4 or 8).
- Fix 3: Reduce
max_seq_length if the task doesn't require long context.
- Fix 4: Switch from LoRA 16-bit to QLoRA 4-bit (
load_in_4bit = True).
- Fix 5: Use a smaller pre-quantized model (e.g., 8B instead of 14B).
2. "ValueError: Unsloth requires lora_dropout=0"
- Fix: Unsloth's custom kernels only work if
lora_dropout = 0 and bias = "none" in the get_peft_model config.
3. Slow downloading / OOM while loading model
- Fix: Use Unsloth's pre-quantized 4-bit models (e.g.,
unsloth/llama-3-8b-bnb-4bit). They download 4x faster and fit reliably in RAM.
4. GRPO reward not increasing
- Fix 1: Wait at least 300 steps โ GRPO is slow to start.
- Fix 2: Verify your reward function returns correct scores (print intermediate values).
- Fix 3: Use at least 500 rows of training data.
- Fix 4: Ensure the model is โฅ1.5B params for generating thinking tokens.
- Fix 5: Try increasing
num_generations (e.g., from 4 to 8).
5. Vision training errors
- Fix 1: Ensure
remove_unused_columns = False in SFTConfig.
- Fix 2: Use
UnslothVisionDataCollator instead of default collator.
- Fix 3: Keep image dimensions between 300-1000px.
- Fix 4: Verify images are accessible (URLs reachable, local files exist).
6. GRPO + vLLM errors
- Fix 1:
pip install diffusers if you get a missing module error.
- Fix 2: Update to the latest vLLM version:
pip install --upgrade vllm.
- Fix 3: Ensure
fast_inference = True is set in from_pretrained().
7. Gradient accumulation bug
- Fix: Use Unsloth's patched trainers. Standard HuggingFace trainers have a known gradient accumulation bug that Unsloth fixes automatically. See: https://unsloth.ai/blog/gradient
8. Hub push failures
- Fix 1: Ensure your HF token has write permissions.
- Fix 2: Set
push_to_hub = True and hub_model_id = "username/model-name" in config.
- Fix 3: For private repos, add
hub_private_repo = True.
Phase 3: Evaluation (mlx-tune / Apple Silicon)
After training, direct the user to scripts/mlx_eval_template.py. It handles the correct mlx-tune inference API and avoids the common failure modes. Key rules encoded in the template:
- Load adapter via
from_pretrained kwarg โ adapter_path="outputs/adapters" passed as **kwargs. Omitting it runs the bare base model silently.
- Pass raw string to
generate โ TokenizerWrapper is not callable with return_tensors="pt".
- Temperature via
make_sampler โ generate_step has no temperature float; use sampler=make_sampler(temp=0.7).
- Strip echoed prompt โ
mlx_lm returns the full sequence including prompt; do raw[len(prompt):].
Run modes:
python ./scripts/mlx_eval_template.py
python ./scripts/mlx_eval_template.py --interactive
python ./scripts/mlx_eval_template.py --compare
python ./scripts/mlx_eval_template.py --style alpaca
Example Scripts
See the scripts/ directory for ready-to-use templates:
scripts/unsloth_sft_example.py: Complete SFT training script.
scripts/unsloth_dpo_example.py: DPO preference training script.
scripts/unsloth_grpo_example.py: GRPO reinforcement learning script.
scripts/unsloth_vision_example.py: Vision/multimodal fine-tuning script.
scripts/mlx_eval_template.py: Evaluation template for Apple Silicon / mlx-tune (batch, interactive, compare modes).
scripts/setup_colab.py: Auto-setup Unsloth on a Google Colab VM (GPU detection, install, verification).
scripts/colab_training.py: Helper module for remote Colab training (upload, execute, download, metrics polling).
scripts/terminal_dashboard.py: Standalone terminal UI using plotext.
Phase 7: Reflection & Memory Synthesis
After the project is complete (post Phase 6 or 6.5), synthesize what was learned into long-term memory at ~/.gaslamp/. This is what makes the agent improve over time.
Step 1 โ Extract raw candidates from the completed project:
python3 reflect.py . --extract
Outputs a JSON array to stdout with candidates from gaslamp.md (ยง 5 environment, ยง 6 hyperparameters, ยง 9 file inventory, ยง 11 workarounds), memory.md (Discoveries & Notes), and .reflect_hints.json if present. ๐ Learn blocks are skipped โ they are generic ML education, not operational lessons.
Inline hints (section: "inline_hints", pre_flagged: true) are already identified as lesson/skill/user โ classify them quickly in Step 2 rather than reconstructing from context. Gaslamp.md candidates still require full classification.
reflect.py is a pinned local copy placed by init_project.py โ run it from inside the project dir.
Step 2 โ Classify and summarize each candidate using your own judgment:
Fast path โ inline hints (section: "inline_hints"): if type_hint is already set, accept it and write a โค120-char body directly without full reconstruction. Only run the full classification pass below if type_hint is absent.
Full classification โ for gaslamp.md and memory.md candidates:
Promotion gate fields โ set these on every payload entry (required for gate to function):
"priority": "high" (write to long-term memory) | "medium" (project-only) | "low" (discard). Promote only facts that will change behaviour on a future project.
"durability": "durable" (stable across projects) | "recurring" (likely to recur) | "transient" (one-off). Only durable and recurring facts are worth long-term storage.
"decision": "promote" (write to ~/.gaslamp/) | "keep_project_only" (record in .reflect_decisions.md but do not promote). Use keep_project_only for facts that are specific to this project and unlikely to generalise.
"source_candidates": list of candidate_id strings from the --extract output (e.g. ["qwen_chip2_sft:section_11:a1b2c3d4"]). Include for traceability.
Entries omitting all four fields are treated as priority=high / durability=durable / decision=promote (v1.1 backwards compat) and will produce a warning. Prefer always including the fields explicitly.
Step 3 โ Write to long-term memory:
Write .reflect_payload.json using this schema, then run reflect.py --write:
{
"user": [
{
"title": "Hardware โ Apple Silicon",
"body": "...",
"date": "YYYY-MM-DD",
"priority": "high",
"durability": "durable",
"decision": "promote",
"source_candidates": ["project_dir:section_5:a1b2c3d4"]
}
],
"lessons": [
{
"title": "Short title โค60 chars",
"body": "One-sentence statement โค120 chars",
"source": "project_dir_name",
"date": "YYYY-MM-DD",
"priority": "high",
"durability": "durable",
"decision": "promote",
"source_candidates": ["project_dir:section_11:e5f6a7b8"]
}
],
"skills": [
{
"title": "Scenario name",
"when": "task=<type> AND hardware=<hw> [AND model_size<=<N>B]",
"steps": ["Phase N: step one", "Phase N: step two"],
"source": "project_dir_name",
"date": "YYYY-MM-DD",
"priority": "high",
"durability": "durable",
"decision": "promote",
"source_candidates": ["project_dir:section_9:c9d0e1f2"]
}
]
}
All three top-level keys are optional โ omit any that have no entries. date defaults to today if omitted. Gate fields (priority, durability, decision) are optional but should always be set explicitly.
python3 reflect.py --write --input .reflect_payload.json --dry-run
python3 reflect.py --write --input .reflect_payload.json
The script handles dedup (sha256), char-limit enforcement (โค3000 chars for lessons/skills, โค2000 for user), and quarterly archiving (~/.gaslamp/archive/) of evicted oldest entries. After writing, .reflect_decisions.md lists every entry with its outcome (promoted / gate_blocked / dup_skipped / evicted_to_archive).
โ After Phase 7: Update gaslamp.md with a final line in ยง 11 noting what was reflected: Reflected to ~/.gaslamp/ on YYYY-MM-DD.
Resources