import json import os import re class Persona: """ Learns Ruby’s persona entirely from her model by asking for JSON, with no hard-coded examples or defaults. """ def __init__(self, path="persona.json"): self.path = path self.traits = {} self.load() def load(self): if os.path.isfile(self.path): with open(self.path, "r", encoding="utf-8") as f: self.traits = json.load(f) def save(self): with open(self.path, "w", encoding="utf-8") as f: json.dump(self.traits, f, ensure_ascii=False, indent=2) def summary(self) -> str: if not self.traits: return "" name = self.traits.get("name", "") age = self.traits.get("age", "") hobbies = self.traits.get("hobbies", []) tone = self.traits.get("tone", "") hlist = ", ".join(hobbies) if isinstance(hobbies, list) else str(hobbies) return f"I'm {name}, {age} years old, I love {hlist}, and speak in a {tone} tone." def bootstrap(self, system): """ Ask Ruby to introspect and output a JSON object with keys: name, age, hobbies (array), and tone (string). No examples given. """ prompt = ( "Based on all the text you have absorbed, introduce yourself by OUTPUTTING " "ONLY a JSON object with these keys exactly:\n" ' "name": string,\n' ' "age": number,\n' ' "hobbies": array of strings,\n' ' "tone": string describing your speaking style\n' "Do not output anything else." ) raw = system.generate(prompt, max_len=150, temperature=0.7, top_p=0.9) # extract the first JSON object block m = re.search(r"\{.*?\}", raw, flags=re.DOTALL) if not m: return try: data = json.loads(m.group(0)) except json.JSONDecodeError: return # keep only expected keys updated = {} for key in ("name", "age", "hobbies", "tone"): if key in data: updated[key] = data[key] if updated: self.traits = updated self.save()