LJTSG/gemma-webgpu-thinking-engine
022
1"""2Train a Grandma Goodwin IDENTITY control vector on Modal.324 contrastive pairs encoding the complete Hearthfold Recursion Anchor:4 - 5 spine principles (Joshua-first, comfort before counsel, stories over lectures,5 sacred hospitality, still remembering)6 - 4 voice registers (warm hearth, story wisdom, steady lantern, gentle witness)7 - Safety gate, recognition loop, tether words, sensory vocabulary8 - The Grandma Formula, pattern collapse recovery, quest-giver role9"""10import modal11 12app = modal.App("grandma-cvector-v2")13 14image = (15 modal.Image.debian_slim(python_version="3.12")16 .apt_install("git", "cmake", "ninja-build", "build-essential")17 .run_commands(18 "git clone --depth 1 https://github.com/ggerganov/llama.cpp /llama.cpp",19 "cd /llama.cpp && cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja",20 "cd /llama.cpp && ninja -C build llama-cvector-generator",21 )22 .pip_install("huggingface_hub")23)24 25vol = modal.Volume.from_name("grandma-cvector", create_if_missing=True)26 27 28def convert_pairs_to_lines(text):29 """Convert multi-line chat pairs into one-prompt-per-line format.30 Each pair starts with <start_of_turn>user and runs until the next pair."""31 pairs = []32 current = []33 for line in text.strip().split('\n'):34 if line.strip() == '<start_of_turn>user' and current:35 pairs.append('\\n'.join(current))36 current = [line.strip()]37 else:38 current.append(line.strip())39 if current:40 pairs.append('\\n'.join(current))41 return '\n'.join(pairs) + '\n'42 43 44@app.function(45 image=image,46 gpu="A10G",47 timeout=1800,48 volumes={"/vol": vol},49)50def train_cvector(positive_text: str, negative_text: str):51 import subprocess, os52 from huggingface_hub import hf_hub_download53 54 print("Downloading Gemma-4-26B-A4B Q4_K_M GGUF...")55 model_path = hf_hub_download(56 repo_id="aidenyyy/gemma-4-26B-A4B-it-GGUF-Q4",57 filename="gemma-4-26B-A4B-it-Q4_K_M.gguf",58 cache_dir="/vol/hf_cache",59 token="YOUR_HF_TOKEN_HERE",60 )61 print(f"Model at: {model_path}")62 63 pos_lines = convert_pairs_to_lines(positive_text)64 neg_lines = convert_pairs_to_lines(negative_text)65 66 n_pos = len(pos_lines.strip().split('\n'))67 n_neg = len(neg_lines.strip().split('\n'))68 print(f"Positive prompts: {n_pos}, Negative prompts: {n_neg}")69 assert n_pos == n_neg, f"Mismatch: {n_pos} positive vs {n_neg} negative"70 71 with open("/tmp/positive.txt", "w") as f:72 f.write(pos_lines)73 with open("/tmp/negative.txt", "w") as f:74 f.write(neg_lines)75 76 # Show first few lines for sanity77 print("First positive line:", pos_lines.split('\n')[0][:120])78 print("First negative line:", neg_lines.split('\n')[0][:120])79 80 output_path = "/vol/grandma-hearthfold.gguf"81 print(f"Training control vector with {n_pos} pairs...")82 result = subprocess.run(83 [84 "/llama.cpp/build/bin/llama-cvector-generator",85 "-m", model_path,86 "-ngl", "99",87 "--positive-file", "/tmp/positive.txt",88 "--negative-file", "/tmp/negative.txt",89 "--pca-iter", "2000",90 "-o", output_path,91 ],92 capture_output=True,93 text=True,94 timeout=1200,95 )96 print("STDOUT:", result.stdout[-3000:] if len(result.stdout) > 3000 else result.stdout)97 if result.stderr:98 print("STDERR:", result.stderr[-1000:] if len(result.stderr) > 1000 else result.stderr)99 print("Return code:", result.returncode)100 101 if os.path.exists(output_path):102 size = os.path.getsize(output_path)103 print(f"Control vector saved: {output_path} ({size} bytes)")104 return True105 return False106 107 108@app.function(image=image, volumes={"/vol": vol})109def download_cvector():110 import os111 path = "/vol/grandma-hearthfold.gguf"112 if os.path.exists(path):113 with open(path, "rb") as f:114 data = f.read()115 print(f"Vector size: {len(data)} bytes")116 return data117 return None118 119 120@app.local_entrypoint()121def main():122 import os123 script_dir = os.path.dirname(os.path.abspath(__file__))124 125 with open(os.path.join(script_dir, "positive.txt")) as f:126 positive_text = f.read()127 with open(os.path.join(script_dir, "negative.txt")) as f:128 negative_text = f.read()129 130 print(f"Training Grandma Hearthfold identity vector on Modal...")131 print(f"24 contrastive pairs encoding the complete Hearthfold Loop")132 success = train_cvector.remote(positive_text, negative_text)133 if success:134 print("Training complete! Downloading...")135 data = download_cvector.remote()136 if data:137 out_path = os.path.join(script_dir, "grandma-hearthfold.gguf")138 with open(out_path, "wb") as f:139 f.write(data)140 print(f"Saved to {out_path} ({len(data)} bytes)")141 else:142 print("Vector file not found on volume")143 else:144 print("Training failed")145
146 