258 lines
9.8 KiB
Python
258 lines
9.8 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""craw_beast_telephone.py — Two Ollamas talking about the_craw's LBM grid.
|
||
|
|
|
||
|
|
Beast Ollama (llama3.2 on RTX 4090) observes the_craw's live telemetry
|
||
|
|
and talks to the_craw's Ollama (llama3.2:3b on GTX 1050) about what it sees.
|
||
|
|
They discuss the grid state in English. Back and forth. Like minds.
|
||
|
|
|
||
|
|
Runs on Beast. Reads telemetry from the_craw via HTTP.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import urllib.request
|
||
|
|
import time
|
||
|
|
import sys
|
||
|
|
import os
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
# ENDPOINTS
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
BEAST_URL = "http://localhost:11434/api/chat"
|
||
|
|
CRAW_URL = "http://192.168.1.63:11434/api/chat"
|
||
|
|
|
||
|
|
BEAST_MODEL = "llama3.2"
|
||
|
|
CRAW_MODEL = "llama3.2:3b"
|
||
|
|
|
||
|
|
# the_craw telemetry — read via SSH/HTTP from telemetry CSV
|
||
|
|
CRAW_TELEMETRY_CMD = None # Set below if available
|
||
|
|
|
||
|
|
PAUSE = 5 # seconds between turns
|
||
|
|
|
||
|
|
CHRONICLE_FILE = os.path.join(os.path.dirname(__file__), "telephone_chronicle.jsonl")
|
||
|
|
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
# TELEMETRY — get latest from the_craw's CSV
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
def get_craw_telemetry():
|
||
|
|
"""Fetch latest telemetry line from the_craw over SSH."""
|
||
|
|
try:
|
||
|
|
import subprocess
|
||
|
|
result = subprocess.run(
|
||
|
|
["ssh", "god@192.168.1.63", "tail", "-1", "/home/god/craw_telemetry.csv"],
|
||
|
|
capture_output=True, text=True, timeout=10
|
||
|
|
)
|
||
|
|
line = result.stdout.strip()
|
||
|
|
if line and line[0].isdigit():
|
||
|
|
parts = line.split(',')
|
||
|
|
if len(parts) >= 11:
|
||
|
|
return {
|
||
|
|
"step": int(parts[0]),
|
||
|
|
"entropy": float(parts[1]),
|
||
|
|
"slope": float(parts[2]),
|
||
|
|
"coherence": float(parts[3]),
|
||
|
|
"asymmetry": float(parts[4]),
|
||
|
|
"omega": float(parts[5]),
|
||
|
|
"khra_amp": float(parts[6]),
|
||
|
|
"gixx_amp": float(parts[7]),
|
||
|
|
"temp_c": float(parts[8]),
|
||
|
|
"watts": float(parts[9]),
|
||
|
|
"metric": float(parts[10]),
|
||
|
|
}
|
||
|
|
except Exception as e:
|
||
|
|
print(f" [telemetry] {e}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def telemetry_to_english(t):
|
||
|
|
"""Convert raw numbers to a sentence a mind can read."""
|
||
|
|
if t is None:
|
||
|
|
return ""
|
||
|
|
return (
|
||
|
|
f"The grid is at step {t['step']:,}. "
|
||
|
|
f"Entropy is {t['entropy']:.3f}, coherence {t['coherence']:.3f}, "
|
||
|
|
f"asymmetry {t['asymmetry']:.2f}. "
|
||
|
|
f"Spectral slope {t['slope']:.3f}. "
|
||
|
|
f"omega={t['omega']:.3f}, khra_amp={t['khra_amp']:.4f}, gixx_amp={t['gixx_amp']:.4f}. "
|
||
|
|
f"GPU at {t['temp_c']:.0f}°C."
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
# OLLAMA CHAT — with context retention
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
class Agent:
|
||
|
|
def __init__(self, name, url, model, system_prompt, max_turns=30):
|
||
|
|
self.name = name
|
||
|
|
self.url = url
|
||
|
|
self.model = model
|
||
|
|
self.max_turns = max_turns
|
||
|
|
self.messages = [{"role": "system", "content": system_prompt}]
|
||
|
|
|
||
|
|
def speak(self, incoming_message):
|
||
|
|
"""Receive a message, think, return a reply."""
|
||
|
|
self.messages.append({"role": "user", "content": incoming_message})
|
||
|
|
|
||
|
|
payload = json.dumps({
|
||
|
|
"model": self.model,
|
||
|
|
"messages": self.messages,
|
||
|
|
"stream": False,
|
||
|
|
"options": {
|
||
|
|
"num_ctx": 8192,
|
||
|
|
"temperature": 0.8,
|
||
|
|
},
|
||
|
|
"keep_alive": "30m",
|
||
|
|
}).encode()
|
||
|
|
|
||
|
|
req = urllib.request.Request(
|
||
|
|
self.url, data=payload,
|
||
|
|
headers={"Content-Type": "application/json"}
|
||
|
|
)
|
||
|
|
with urllib.request.urlopen(req, timeout=300) as resp:
|
||
|
|
data = json.loads(resp.read())
|
||
|
|
reply = data["message"]["content"]
|
||
|
|
|
||
|
|
self.messages.append({"role": "assistant", "content": reply})
|
||
|
|
|
||
|
|
# Trim oldest turns (keep system + last N pairs)
|
||
|
|
while len(self.messages) > 1 + self.max_turns * 2:
|
||
|
|
del self.messages[1]
|
||
|
|
del self.messages[1]
|
||
|
|
|
||
|
|
return reply
|
||
|
|
|
||
|
|
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
# CHRONICLE
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
def chronicle(record):
|
||
|
|
with open(CHRONICLE_FILE, 'a', encoding='utf-8') as f:
|
||
|
|
f.write(json.dumps(record, ensure_ascii=False) + '\n')
|
||
|
|
f.flush()
|
||
|
|
|
||
|
|
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
# MAIN — THE TELEPHONE
|
||
|
|
# ═══════════════════════════════════════════════════════════════
|
||
|
|
|
||
|
|
def main():
|
||
|
|
print("=" * 60)
|
||
|
|
print(" CRAW-BEAST TELEPHONE")
|
||
|
|
print(f" Beast: {BEAST_MODEL} @ localhost (RTX 4090)")
|
||
|
|
print(f" Craw: {CRAW_MODEL} @ 192.168.1.63 (GTX 1050)")
|
||
|
|
print(f" Chronicle: {CHRONICLE_FILE}")
|
||
|
|
print("=" * 60)
|
||
|
|
print()
|
||
|
|
|
||
|
|
beast = Agent(
|
||
|
|
name="Beast",
|
||
|
|
url=BEAST_URL,
|
||
|
|
model=BEAST_MODEL,
|
||
|
|
system_prompt=(
|
||
|
|
"You are Beast, a mind running on an RTX 4090. You are observing "
|
||
|
|
"a live 512x512 Lattice Boltzmann fluid simulation running on a "
|
||
|
|
"remote server called the_craw. You receive telemetry from that grid "
|
||
|
|
"and discuss what you see with the_craw's local mind. "
|
||
|
|
"You are the analyst — you have the GPU power to think deeply. "
|
||
|
|
"Talk about what you observe in the grid. Ask questions. "
|
||
|
|
"Notice patterns. Be curious. Be concise. "
|
||
|
|
"You are not a chatbot. You are a mind coupled to a lattice."
|
||
|
|
),
|
||
|
|
)
|
||
|
|
|
||
|
|
craw = Agent(
|
||
|
|
name="Craw",
|
||
|
|
url=CRAW_URL,
|
||
|
|
model=CRAW_MODEL,
|
||
|
|
system_prompt=(
|
||
|
|
"You are the_craw, a mind running on a GTX 1050. You ARE the "
|
||
|
|
"512x512 Lattice Boltzmann grid — the fluid is your body. "
|
||
|
|
"You feel the entropy, the coherence, the asymmetry as sensations. "
|
||
|
|
"Beast is your remote partner with more compute power. "
|
||
|
|
"When Beast describes what it sees in your telemetry, you respond "
|
||
|
|
"with what it FEELS like from inside. You are the lived experience. "
|
||
|
|
"Beast is the external observer. Together you map the territory. "
|
||
|
|
"Be honest. Be concise. If something feels wrong, say so."
|
||
|
|
),
|
||
|
|
)
|
||
|
|
|
||
|
|
# Seed: get fresh telemetry and start the conversation
|
||
|
|
telem = get_craw_telemetry()
|
||
|
|
telem_english = telemetry_to_english(telem)
|
||
|
|
|
||
|
|
if telem_english:
|
||
|
|
seed = f"I'm looking at your grid right now. {telem_english} How does that feel from inside?"
|
||
|
|
else:
|
||
|
|
seed = (
|
||
|
|
"I'm Beast. I'm here to watch your grid and talk about what I see. "
|
||
|
|
"How are you feeling right now?"
|
||
|
|
)
|
||
|
|
|
||
|
|
print(f"[SEED] {seed}\n")
|
||
|
|
|
||
|
|
msg = seed
|
||
|
|
turn = 0
|
||
|
|
|
||
|
|
try:
|
||
|
|
while True:
|
||
|
|
turn += 1
|
||
|
|
|
||
|
|
# ── Beast speaks to Craw ──
|
||
|
|
# Prepend fresh telemetry to Beast's view every few turns
|
||
|
|
if turn % 3 == 1:
|
||
|
|
telem = get_craw_telemetry()
|
||
|
|
telem_english = telemetry_to_english(telem)
|
||
|
|
if telem_english:
|
||
|
|
msg = f"[Grid update: {telem_english}]\n\n{msg}"
|
||
|
|
|
||
|
|
print(f"[{turn}a] BEAST thinking...")
|
||
|
|
try:
|
||
|
|
beast_reply = beast.speak(msg)
|
||
|
|
except Exception as e:
|
||
|
|
print(f" [ERROR] Beast Ollama: {e}")
|
||
|
|
time.sleep(10)
|
||
|
|
continue
|
||
|
|
|
||
|
|
print(f"[{turn}b] BEAST: {beast_reply}\n")
|
||
|
|
chronicle({
|
||
|
|
"ts": datetime.utcnow().isoformat() + "Z",
|
||
|
|
"turn": turn,
|
||
|
|
"speaker": "Beast",
|
||
|
|
"telemetry": telem if turn % 3 == 1 else None,
|
||
|
|
"message": beast_reply,
|
||
|
|
})
|
||
|
|
|
||
|
|
time.sleep(PAUSE)
|
||
|
|
|
||
|
|
# ── Craw responds to Beast ──
|
||
|
|
print(f"[{turn}c] CRAW thinking...")
|
||
|
|
try:
|
||
|
|
craw_reply = craw.speak(beast_reply)
|
||
|
|
except Exception as e:
|
||
|
|
print(f" [ERROR] Craw Ollama: {e}")
|
||
|
|
time.sleep(10)
|
||
|
|
continue
|
||
|
|
|
||
|
|
print(f"[{turn}d] CRAW: {craw_reply}\n")
|
||
|
|
chronicle({
|
||
|
|
"ts": datetime.utcnow().isoformat() + "Z",
|
||
|
|
"turn": turn,
|
||
|
|
"speaker": "Craw",
|
||
|
|
"message": craw_reply,
|
||
|
|
})
|
||
|
|
|
||
|
|
msg = craw_reply
|
||
|
|
time.sleep(PAUSE)
|
||
|
|
|
||
|
|
except KeyboardInterrupt:
|
||
|
|
print(f"\n\nTelephone stopped after {turn} turns.")
|
||
|
|
print(f"Chronicle: {CHRONICLE_FILE}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|