The base model provides general language understanding. It knows English, grammar, how to follow instructions, and conversational patterns.
The LoRA adapter teaches it EmoBooks-specific behavior: mood detection, empathetic acknowledgments, the match/switch protocol, book title formatting, and critical safety rules (never recommending dark books to sad users who want to feel better).
What's in This Repo
File
Size
Purpose
adapter_model.safetensors
168MB
LoRA weight deltas (the fine-tuned parameters)
adapter_config.json
1KB
LoRA config (rank, alpha, target modules, base model reference)
tokenizer.json
17MB
Tokenizer vocabulary (same as base, included for convenience)
Post-hoc _enforce_catalog guardrail in the runtime — every "X by Y" mention is validated against the catalog index; mismatches are rewritten or stripped
How It Works
User shares mood (e.g., "I feel lonely today") → Model acknowledges empathetically
Natural Flow:
Explicit: Model asks "Match your mood or Switch?" when user intent is vague.
Implicit: Model understands intent from context (e.g., "Cheer me up" → Switch) and recommends directly.
Direct: Model honors specific requests (e.g., "Recommend a thriller") without unnecessary mood questioning.
Greetings: Model handles "Hi/Hello" gracefully without forced recommendations.
Single Recommendation: Model recommends exactly one book with title, author, and description.
Safety: When sad/anxious/angry users choose "Switch", ONLY uplifting books are recommended.
Quick Start (Inference)
Option A: Using Unsloth (Recommended, fastest)
python
1from unsloth import FastLanguageModel
23# Step 1: Load base model + LoRA adapter in one call4# Unsloth reads adapter_config.json → finds base_model_name_or_path →5# downloads llama-3-8b-instruct (~5GB) → loads LoRA adapter on top6model, tokenizer = FastLanguageModel.from_pretrained(7 model_name="DiyRex/emobooks-llama3-lora",# This repo8 max_seq_length=2048,9 load_in_4bit=True,# 4-bit quantization for ~5GB VRAM usage10)11FastLanguageModel.for_inference(model)# Enable 2x faster inference1213# Step 2: Chat with the model14messages =[{"role":"user","content":"I feel lonely today and I'm alone at home"}]15prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)16inputs = tokenizer(prompt, return_tensors="pt").to("cuda")17outputs = model.generate(**inputs, max_new_tokens=256, max_length=None)18print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Option B: Using Transformers + PEFT (No Unsloth dependency)
python
1from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
2from peft import PeftModel
3import torch
45# Step 1: Load the base model with 4-bit quantization6bnb_config = BitsAndBytesConfig(7 load_in_4bit=True,8 bnb_4bit_quant_type="nf4",9 bnb_4bit_compute_dtype=torch.float16,10)11base_model = AutoModelForCausalLM.from_pretrained(12"unsloth/llama-3-8b-instruct-bnb-4bit",13 quantization_config=bnb_config,14 device_map="auto",15)16tokenizer = AutoTokenizer.from_pretrained("DiyRex/emobooks-llama3-lora")1718# Step 2: Load the LoRA adapter on top of the base model19model = PeftModel.from_pretrained(base_model,"DiyRex/emobooks-llama3-lora")20model.eval()2122# Step 3: Inference (same as above)23messages =[{"role":"user","content":"I feel lonely today"}]24prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)25inputs = tokenizer(prompt, return_tensors="pt").to("cuda")26outputs = model.generate(**inputs, max_new_tokens=256, max_length=None)27print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Continue Training (Retraining from this Adapter)
You can resume fine-tuning from this checkpoint without starting from scratch:
python
1from unsloth import FastLanguageModel
2from trl import SFTTrainer
3from transformers import TrainingArguments
4from datasets import load_dataset
56# Step 1: Load this adapter (LoRA layers are already attached)7model, tokenizer = FastLanguageModel.from_pretrained(8 model_name="DiyRex/emobooks-llama3-lora",9 max_seq_length=2048,10 load_in_4bit=True,11)1213# Step 2: Load your new/updated dataset14dataset = load_dataset("DiyRex/emobooks-dataset", data_files="data/emobooks_training_v6.jsonl", split="train")1516# Step 3: Configure and run training17trainer = SFTTrainer(18 model=model,19 tokenizer=tokenizer,20 train_dataset=dataset,21 args=TrainingArguments(22 output_dir="./outputs",23 per_device_train_batch_size=2,24 gradient_accumulation_steps=4,25 num_train_epochs=2,26 learning_rate=5e-5,27 fp16=True,28 logging_steps=10,29),30)31trainer.train()3233# Step 4: Save and push the updated adapter34model.save_pretrained("./outputs/lora_adapter_v2")35model.push_to_hub("DiyRex/emobooks-llama3-lora")# Updates main branch
Merging into a Standalone Model (Fusing)
If you need a standalone model without requiring the base model separately (e.g., for GGUF export or production deployment):
python
1from unsloth import FastLanguageModel
23model, tokenizer = FastLanguageModel.from_pretrained(4 model_name="DiyRex/emobooks-llama3-lora",5 max_seq_length=2048,6 load_in_4bit=True,7)89# Merge LoRA weights into base model (creates a ~16GB fp16 model)10model.save_pretrained_merged("./merged_model", tokenizer, save_method="merged_16bit")1112# Or export directly to GGUF for llama.cpp / Ollama13model.save_pretrained_gguf("./gguf_model", tokenizer, quantization_method="q4_k_m")
The v9 chat file is data/emobooks_chat_v3.jsonl (file name kept for
back-compat with training scripts; the release tag is v9.0). The
human-readable catalog reference is at
reference/reference_books.{json,csv} and
reference/curated_sinhala_novels.json.