Created the project, added files named similar to body parts.

This commit is contained in:
Dani 2025-05-03 20:00:45 -04:00
parent 58442b86ee
commit a655d73813
6 changed files with 227 additions and 0 deletions

95
body.py Normal file
View File

@ -0,0 +1,95 @@
import os
import torch
import torch.nn.functional as F
import discord
from discord import Intents
from sensory_system.eyes import Eyes
from nervous_system.cortex import Cortex
from nervous_system.meta_learning import MetaLearner
from memory.hippocampus import Hippocampus
from motor_system.motor_cortex import MotorCortex
from headspace.dashboard import run_dashboard
class Organism:
def __init__(self) -> None:
# Device
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Sensory organ
self.eyes = Eyes(books_path="content/books")
# Memory & learning
self.memory = Hippocampus()
self.nervous_system = Cortex(...).to(self.device)
self.meta = MetaLearner(self.nervous_system)
self.motor = MotorCortex()
# (Optional) Pre-load your 21+ books for future pre-training:
self._load_corpus("content/books")
def _load_corpus(self, folder_path: str) -> None:
"""Read all text files in content/books into memory for later use."""
self.corpus = []
for fn in os.listdir(folder_path):
if fn.lower().endswith(".txt"):
with open(os.path.join(folder_path, fn), encoding="utf-8") as f:
self.corpus.append(f.read())
def learn_and_respond(self, message: str) -> str:
# 1) Perception via eyes
input_ids = self.eyes.preprocess(message)
input_tensor = torch.tensor([input_ids], dtype=torch.long, device=self.device)
# 2) Inference
logits = self.nervous_system(input_tensor)
response_ids = logits.argmax(dim=-1)[0].tolist()
response = self.motor.decode(response_ids)
# 3) Self-supervised loss (predict input back)
loss = F.cross_entropy(
logits.view(-1, logits.size(-1)),
input_tensor.view(-1),
)
# 4) Online meta-learning update
self.meta.meta_update(loss)
# 5) Store interaction
self.memory.store({
"input_ids": input_tensor.cpu(),
"output_ids": response_ids,
"input_text": message,
"output_text": response
})
return response
# ————— Discord setup (all in one “body” file) —————
intents = Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
organism = Organism()
@client.event
async def on_ready() -> None:
print(f"Logged in as {client.user}")
@client.event
async def on_message(message: discord.Message) -> None:
if message.author == client.user or not message.content:
return
reply = organism.learn_and_respond(message.content)
await message.channel.send(reply)
if __name__ == "__main__":
TOKEN = os.getenv("DISCORD_TOKEN")
if not TOKEN:
raise RuntimeError("DISCORD_TOKEN environment variable not set.")
run_dashboard(organism, host="0.0.0.0", port=5000)
client.run(TOKEN)

40
headspace/dashboard.py Normal file
View File

@ -0,0 +1,40 @@
# dashboard.py
import threading
from flask import Flask, render_template_string
app = Flask(__name__)
_organism = None # will be set by run_dashboard()
@app.route("/")
def index():
# Basic stats
total = len(_organism.memory.memory)
# Show up to 10 most recent text interactions
recent = list(_organism.memory.memory)[-10:][::-1]
items = ""
for i in recent:
inp = i.get("input_text", "<no input>")
out = i.get("output_text", "<no output>")
items += f"<li><b>In:</b> {inp}<br/><b>Out:</b> {out}</li>"
html = f"""
<h1>Ruby Dashboard</h1>
<p><strong>Total stored interactions:</strong> {total}</p>
<h2>Last {len(recent)} exchanges</h2>
<ul>{items}</ul>
"""
return render_template_string(html)
def run_dashboard(organism, host="0.0.0.0", port=5000):
"""Call this to launch the dashboard in a background thread."""
global _organism
_organism = organism
# start Flask in its own thread so it doesnt block Discord
thread = threading.Thread(
target=lambda: app.run(host=host, port=port, debug=False, use_reloader=False),
daemon=True,
)
thread.start()

View File

@ -0,0 +1,10 @@
from typing import List
class MotorCortex:
"""Converts model outputs (char codes) back into a string."""
def __init__(self) -> None:
pass
def decode(self, output_ids: List[int]) -> str:
return "".join(chr(i) for i in output_ids)

45
nervous_system/context.py Normal file
View File

@ -0,0 +1,45 @@
import torch
import torch.nn as nn
class Cortex(nn.Module):
"""The brain: a charlevel Transformer encoder for self-supervised learning."""
def __init__(
self,
embed_dim: int = 256,
num_heads: int = 4,
num_layers: int = 4,
ff_dim: int = 512,
max_seq_len: int = 1024,
) -> None:
super().__init__()
self.vocab_size = 256 # ASCII
self.embed_dim = embed_dim
self.token_embedding = nn.Embedding(self.vocab_size, embed_dim)
self.position_embedding = nn.Embedding(max_seq_len, embed_dim)
encoder_layer = nn.TransformerEncoderLayer(
d_model=embed_dim,
nhead=num_heads,
dim_feedforward=ff_dim,
)
self.transformer = nn.TransformerEncoder(
encoder_layer, num_layers=num_layers
)
self.fc_out = nn.Linear(embed_dim, self.vocab_size)
self.max_seq_len = max_seq_len
def forward(self, input_ids: torch.Tensor) -> torch.Tensor:
# input_ids: (batch, seq_len)
batch_size, seq_len = input_ids.size()
positions = (
torch.arange(0, seq_len, device=input_ids.device)
.unsqueeze(0)
.expand(batch_size, -1)
)
x = self.token_embedding(input_ids) + self.position_embedding(positions)
x = x.permute(1, 0, 2) # (seq_len, batch, embed_dim)
x = self.transformer(x)
x = x.permute(1, 0, 2) # back to (batch, seq_len, embed_dim)
logits = self.fc_out(x)
return logits

View File

@ -0,0 +1,13 @@
import torch
class MetaLearner:
"""Handles online, first-order meta-updates to the cortex."""
def __init__(self, model: torch.nn.Module, lr: float = 1e-4) -> None:
self.model = model
self.meta_optimizer = torch.optim.Adam(model.parameters(), lr=lr)
def meta_update(self, loss: torch.Tensor) -> None:
self.meta_optimizer.zero_grad()
loss.backward(retain_graph=True)
self.meta_optimizer.step()

24
sensory_system/eyes.py Normal file
View File

@ -0,0 +1,24 @@
# sensory_system/eyes.py
import os
from typing import List
class Eyes:
"""The eyes read both live input and, on startup, your book corpus."""
def __init__(self, books_path: str = None) -> None:
self.corpus: List[str] = []
if books_path:
self.load_books(books_path)
def load_books(self, folder_path: str) -> None:
"""Load all .txt files from folder_path into self.corpus."""
for fn in os.listdir(folder_path):
if fn.lower().endswith(".txt"):
full = os.path.join(folder_path, fn)
with open(full, encoding="utf-8") as f:
self.corpus.append(f.read())
def preprocess(self, text: str) -> List[int]:
"""Turn an input string into a list of char codes (0255)."""
return [ord(c) % 256 for c in text]