From d70a83ea72903a2e9b025ae29a779bc89c67315d Mon Sep 17 00:00:00 2001 From: Dani Date: Mon, 14 Apr 2025 19:20:33 -0400 Subject: [PATCH] Adding a dream state --- main.py | 20 +++++++++++++++++--- model.py | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 062611b..36a8185 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,8 @@ import discord +import asyncio import os from dotenv import load_dotenv -from datetime import datetime +from datetime import datetime, timedelta from tokenizer import Tokenizer from model import RubyTrainer @@ -18,17 +19,32 @@ intents.message_content = True intents.dm_messages = True intents = intents + class Ruby(discord.Client): def __init__(self): super().__init__(intents=intents) self.tokenizer = Tokenizer() self.trainer = RubyTrainer(self.tokenizer) self.log_path = os.path.join("logs", "messages.log") + self.last_message_time = datetime.utcnow() + self.idle_threshold = timedelta(seconds=120) # adjust as needed + self.loop.create_task(self.idle_dream_loop()) os.makedirs("logs", exist_ok=True) async def on_ready(self): print(f"[READY] Logged in as {self.user} (ID: {self.user.id})") + async def idle_dream_loop(self): + await self.wait_until_ready() + while not self.is_closed(): + now = datetime.utcnow() + if now - self.last_message_time > self.idle_threshold: + print("[IDLE] Ruby has been idle — entering dream mode.") + self.trainer.dream() + self.trainer.daydream() + self.last_message_time = datetime.utcnow() # reset after dreaming + await asyncio.sleep(30) # check every 30 seconds + async def on_message(self, message: discord.Message): if message.author.id == self.user.id: return @@ -43,7 +59,6 @@ class Ruby(discord.Client): print("[REPLY] Skipped (empty)") - def log_message(self, message: discord.Message): timestamp = datetime.utcnow().isoformat() log_entry = f"{timestamp} | {message.author.name} | {message.content.strip()}\n" @@ -61,6 +76,5 @@ class Ruby(discord.Client): print(f"[TRAIN] Tokens: {tokens} | Loss: {loss:.4f}") # Run Ruby - client = Ruby() client.run(TOKEN) diff --git a/model.py b/model.py index 4d1b4cf..5e30055 100644 --- a/model.py +++ b/model.py @@ -77,7 +77,7 @@ class RubyTrainer: print(f"[TRAIN] Tokens: {tokens} | Loss: {loss.item():.4f}") - def generate_reply(self, max_tokens=15, temperature=1.0, top_k=5): + def generate_reply(self, max_tokens=30, temperature=1.0, top_k=5): self.model.eval() input_ids = torch.tensor([[self.tokenizer.vocab[""]]], dtype=torch.long, device=self.device) @@ -103,4 +103,32 @@ class RubyTrainer: break token_ids = input_ids.squeeze(0).tolist()[1:] # skip - return self.tokenizer.detokenize(token_ids) + reply_tokens = [tid for tid in token_ids if tid != self.tokenizer.vocab.get("")] + return self.tokenizer.detokenize(reply_tokens) + + def dream(self, log_path="logs/messages.log", max_lines=50): + print("[DREAM] Ruby is dreaming...") + + if not os.path.exists(log_path): + print("[DREAM] No memory to dream from.") + return + + with open(log_path, "r", encoding="utf-8") as f: + lines = f.readlines()[-max_lines:] + + for line in lines: + parts = line.strip().split("|") + if len(parts) >= 3: + text = parts[2].strip() + self.train_on_tokens_from_text(text) + + print("[DREAM] Dream complete.") + + def daydream(self, rounds=5): + print("[DAYDREAM] Ruby is imagining new thoughts...") + for _ in range(rounds): + thought = self.generate_reply() + if thought.strip(): + print(f"[THOUGHT] {thought}") + self.train_on_tokens_from_text(thought) + print("[DAYDREAM] Complete.")