Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import threading, time, json | |
| from datetime import datetime, UTC | |
| from collections import deque | |
| from pathlib import Path | |
| from neural_network import LivingNetwork, CATEGORIES as TEXT_CATS, KNOWLEDGE_FILE | |
| from data_fetcher import DataFetcher | |
| from image_fetcher import ImageFetcher, IMAGE_DIR, CATEGORIES as IMG_CATS | |
| from image_model import LivingImageNetwork | |
| from chatbot import RAGChatbot | |
| # โโ globals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| network = LivingNetwork() | |
| fetcher = DataFetcher() | |
| img_fetcher = ImageFetcher() | |
| img_network = LivingImageNetwork() | |
| chatbot = RAGChatbot() | |
| app_log = deque(maxlen=300) | |
| is_alive = False | |
| img_alive = False | |
| def log(msg): | |
| app_log.appendleft(f"[{datetime.now(UTC).strftime('%H:%M:%S')}] {msg}") | |
| log("๐ง Text network ready") | |
| log("๐๏ธ Image CNN ready") | |
| kf = network.get_knowledge_file_stats() | |
| if kf['exists']: log(f"๐ Restored {kf['lines']} articles from knowledge.jsonl") | |
| if network.epoch: log(f"โ Text model: epoch {network.epoch:,}") | |
| if img_network.epoch: log(f"โ Image CNN: epoch {img_network.epoch:,}") | |
| # โโ background loops โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def text_loop(): | |
| global is_alive | |
| lf = ls = 0 | |
| log("๐ Text network LIVE") | |
| while is_alive: | |
| now = time.time() | |
| if now - lf >= 40: | |
| try: | |
| items = fetcher.fetch_round() | |
| for it in items: | |
| network.ingest(it['text'], it['category'], source=it.get('source','web')) | |
| kf2 = network.get_knowledge_file_stats() | |
| log(f"๐ก +{len(items)} articles | knowledge: {kf2['lines']}") | |
| lf = now | |
| except Exception as e: | |
| log(f"โ fetch: {str(e)[:50]}") | |
| if network.stats['buffer_size'] >= 32 and network.vocab.is_built: | |
| r = network.train_n_steps(80) | |
| if r['steps']: | |
| s = network.stats | |
| log(f"๐ Epoch {s['epoch']:,} loss {s['loss']} acc {s['accuracy']}%") | |
| else: | |
| log(f"โณ need {max(0,32-network.stats['buffer_size'])} more items") | |
| if now - ls >= 180: | |
| network.save_checkpoint(); log("๐พ saved"); ls = now | |
| time.sleep(2) | |
| log("โน text stopped") | |
| def image_loop(): | |
| global img_alive | |
| lf = ls = 0 | |
| log("๐ Image CNN LIVE") | |
| while img_alive: | |
| now = time.time() | |
| if now - lf >= 60: | |
| try: | |
| r = img_fetcher.fetch_round() | |
| log(f"๐ผ๏ธ +{r['downloaded']} images | total {r['total']}") | |
| lf = now | |
| except Exception as e: | |
| log(f"โ img fetch: {str(e)[:50]}") | |
| if img_fetcher.get_stats()['total'] >= 16: | |
| r = img_network.train_n_steps(IMAGE_DIR, 20) | |
| if r['steps']: | |
| s = img_network.stats | |
| log(f"๐๏ธ CNN epoch {s['epoch']:,} loss {s['loss']} acc {s['accuracy']}%") | |
| else: | |
| log(f"โณ img CNN needs {max(0,16-img_fetcher.get_stats()['total'])} more images") | |
| if now - ls >= 180: | |
| img_network._save_checkpoint(); log("๐พ img saved"); ls = now | |
| time.sleep(3) | |
| log("โน image stopped") | |
| # โโ controls โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def start_text(): | |
| global is_alive | |
| if is_alive: return "โ already running" | |
| is_alive = True | |
| threading.Thread(target=text_loop, daemon=True).start() | |
| return "๐ข text network LIVE" | |
| def stop_text(): | |
| global is_alive; is_alive = False | |
| network.save_checkpoint(); return "๐ด stopped + saved" | |
| def start_img(): | |
| global img_alive | |
| if img_alive: return "โ already running" | |
| img_alive = True | |
| threading.Thread(target=image_loop, daemon=True).start() | |
| return "๐ข image CNN LIVE" | |
| def stop_img(): | |
| global img_alive; img_alive = False | |
| img_network._save_checkpoint(); return "๐ด stopped + saved" | |
| def do_fetch_text(): | |
| items = fetcher.fetch_round() | |
| for it in items: network.ingest(it['text'], it['category'], source=it.get('source','web')) | |
| return f"โ +{len(items)} articles" | |
| def do_fetch_img(): | |
| r = img_fetcher.fetch_round() | |
| return f"โ +{r['downloaded']} images" | |
| def do_train_text(n): | |
| r = network.train_n_steps(int(n)) | |
| return f"โ {r['steps']} steps ยท loss {r['avg_loss']}" if r['steps'] else "โ need more data" | |
| def do_train_img(n): | |
| r = img_network.train_n_steps(IMAGE_DIR, int(n)) | |
| return f"โ {r['steps']} steps ยท loss {r['avg_loss']}" if r['steps'] else "โ need more images" | |
| # โโ stats fns โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def status_bar(): | |
| s = network.stats; si = img_network.stats | |
| kf2 = network.get_knowledge_file_stats(); ig = img_fetcher.get_stats() | |
| return (f"{'๐ข' if is_alive else '๐ด'} TEXT ep{s['epoch']:,} loss{s['loss']} acc{s['accuracy']}% | " | |
| f"{'๐ข' if img_alive else '๐ด'} CNN ep{si['epoch']:,} loss{si['loss']} acc{si['accuracy']}% | " | |
| f"๐{kf2['lines']} articles ๐ผ๏ธ{ig['total']} images") | |
| def text_stats(): | |
| s=network.stats; fs=fetcher.get_stats(); cc=network.category_counts | |
| hist=network.loss_history[-30:] | |
| spark=''.join(' โโโโโ โโโ'[min(8,int(((v-min(hist))/(max(hist)-min(hist)+1e-9))*8))] for v in hist) if len(hist)>=2 else 'โฆ' | |
| rows='\n'.join(f"|{c}|{cc.get(c,0)}|" for c in TEXT_CATS) | |
| return f"""### ๐ Text Network | |
| |Metric|Value| | |
| |---|---| | |
| |Epoch|{s['epoch']:,}|Loss|{s['loss']}| | |
| |Accuracy|{s['accuracy']}%|Samples|{s['total_samples']:,}| | |
| |Knowledge|{s['knowledge_count']} articles|Vocab|{s['vocab_size']:,}| | |
| |LR|{s['lr']}|Buffer|{s['buffer_size']}| | |
| Loss `{spark}` | |
| ### Sources โ {fs['total_fetched']} total | |
| RSSยท{fs['sources']['rss']} Redditยท{fs['sources']['reddit']} Wikiยท{fs['sources']['wikipedia']} HNยท{fs['sources']['hackernews']} | |
| ### Categories | |
| |Cat|Count| | |
| |---|---| | |
| {rows} | |
| > _{s['last_text']}_""" | |
| def img_stats(): | |
| s=img_network.stats; ifs=img_fetcher.get_stats() | |
| hist=img_network.loss_history[-30:] | |
| spark=''.join(' โโโโโ โโโ'[min(8,int(((v-min(hist))/(max(hist)-min(hist)+1e-9))*8))] for v in hist) if len(hist)>=2 else 'โฆ' | |
| rows='\n'.join(f"|{c}|{ifs['by_category'].get(c,0)}|" for c in IMG_CATS) | |
| return f"""### ๐๏ธ Image CNN | |
| |Metric|Value| | |
| |---|---| | |
| |Epoch|{s['epoch']:,}|Loss|{s['loss']}| | |
| |Accuracy|{s['accuracy']}%|Images|{s['total_images']:,}| | |
| |On Disk|{ifs['total']}|Disk|{ifs['disk_mb']} MB| | |
| Loss `{spark}` | |
| ### By Category | |
| |Cat|Count| | |
| |---|---| | |
| {rows} | |
| **What each block learns:** | |
| Block 1 โ edges & colours ยท Block 2 โ shapes & textures ยท Block 3 โ objects & parts""" | |
| def get_log(): | |
| lines = list(app_log)[:80] | |
| def color(line): | |
| if 'โ ' in line or '๐ข' in line: c = '#4ade80' | |
| elif 'โ ' in line or '๐ด' in line or 'error' in line.lower(): c = '#f87171' | |
| elif '๐' in line or '๐ค' in line: c = '#818cf8' | |
| elif '๐ฐ' in line or '๐ฆ' in line or '๐ ' in line: c = '#38bdf8' | |
| elif '๐ป' in line or '๐' in line: c = '#fb923c' | |
| elif '๐ฅ' in line or 'loss' in line.lower(): c = '#facc15' | |
| else: c = '#94a3b8' | |
| return f'<div style="color:{c};font-family:monospace;font-size:12px;padding:1px 0">{line}</div>' | |
| rows = ''.join(color(l) for l in lines) or '<div style="color:#64748b;font-style:italic">No logs yet...</div>' | |
| return f'<div style="background:#0a0f1e;padding:12px;border-radius:8px;max-height:420px;overflow-y:auto;border:1px solid #1e293b">{rows}</div>' | |
| def get_feed(): | |
| items=fetcher.get_recent_items(10) | |
| if not items: return "_No data yet_" | |
| return '\n\n---\n\n'.join(f"**[{i['source'].upper()}]** `{i['category'].upper()}`\n{i['text'][:130]}โฆ" for i in items) | |
| def get_knowledge(): | |
| kf2=network.get_knowledge_file_stats(); items=network.get_recent_knowledge(20) | |
| if not kf2['exists'] or not items: return "### ๐ญ Empty โ start text network" | |
| rows=[] | |
| for it in items: | |
| rows.append(f"**`{it.get('category','?').upper()}`** `{it.get('source','?')}` `{it.get('timestamp','')[:10]}`\n> {it.get('text','')[:130]}โฆ") | |
| return f"### ๐ {kf2['lines']} articles ยท {kf2['size_kb']} KB\n\n---\n\n"+'\n\n---\n\n'.join(rows) | |
| def predict_text(txt): | |
| if not txt.strip(): return "Enter text." | |
| r=network.predict(txt) | |
| if 'error' in r: return f"โ {r['error']}" | |
| bars=''.join(f"`{c:<15}` {'โ'*int(p/5)}{'โ'*(20-int(p/5))} **{p:.1f}%**\n\n" for c,p in sorted(r['all_probs'].items(),key=lambda x:-x[1])) | |
| return f"## โ {r['prediction'].upper()}\n**{r['confidence']}% confidence**\n\n{bars}" | |
| def predict_img(path): | |
| if not path: return "Upload image." | |
| r=img_network.predict_image(path) | |
| if 'error' in r: return f"โ {r['error']}" | |
| bars=''.join(f"`{c:<15}` {'โ'*int(p/5)}{'โ'*(20-int(p/5))} **{p:.1f}%**\n\n" for c,p in sorted(r['all_probs'].items(),key=lambda x:-x[1])) | |
| return f"## โ {r['prediction'].upper()}\n**{r['confidence']}% confidence**\n\n{bars}" | |
| def chat_fn(msg, history): | |
| new_history = chatbot.chat(msg, history or []) | |
| return new_history, "" # also clear the input box | |
| # โโ 3D VIZ โ fully animated with signal pulses โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def build_viz(state_json: str) -> str: | |
| return """<!DOCTYPE html><html><head><meta charset="UTF-8"> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| body{background:#030610;overflow:hidden;font-family:monospace} | |
| #c{width:100vw;height:100vh;display:block} | |
| #hud{position:fixed;top:12px;left:14px;color:#00f5c4;font-size:11px;line-height:2;pointer-events:none;text-shadow:0 0 8px #00f5c466} | |
| #tip{position:fixed;bottom:12px;left:50%;transform:translateX(-50%);font-size:10px;color:#2a3a5a} | |
| </style></head><body> | |
| <canvas id="c"></canvas> | |
| <div id="hud"> | |
| <div id="h1">โ LOADING...</div> | |
| <div id="h2"></div><div id="h3"></div><div id="h4"></div> | |
| </div> | |
| <div id="tip">drag to rotate ยท scroll to zoom ยท signals travel leftโright every 800ms</div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <script> | |
| // โโ initial state from Python โโ | |
| const INIT = """ + state_json + """; | |
| // โโ renderer โโ | |
| const canvas = document.getElementById('c'); | |
| const renderer = new THREE.WebGLRenderer({canvas, antialias:true, alpha:false}); | |
| renderer.setPixelRatio(Math.min(devicePixelRatio,2)); | |
| renderer.setSize(innerWidth, innerHeight); | |
| renderer.shadowMap.enabled = true; | |
| const scene = new THREE.Scene(); | |
| scene.background = new THREE.Color(0x030610); | |
| scene.fog = new THREE.FogExp2(0x030610, 0.010); | |
| const camera = new THREE.PerspectiveCamera(52, innerWidth/innerHeight, 0.1, 600); | |
| camera.position.set(0,1,26); | |
| // โโ lights โโ | |
| scene.add(new THREE.AmbientLight(0x0a1020, 4)); | |
| const kl = new THREE.PointLight(0x00f5c4, 8, 80); kl.position.set(0,10,8); scene.add(kl); | |
| const bl = new THREE.PointLight(0x5b6fff, 4, 50); bl.position.set(-12,-6,4); scene.add(bl); | |
| const rl = new THREE.PointLight(0xff6b35, 2, 40); rl.position.set(12,-4,-2); scene.add(rl); | |
| // โโ orbit โโ | |
| let rX=0.18, rY=0, zoom=26, drag=false, last={x:0,y:0}; | |
| canvas.addEventListener('mousedown', e=>{drag=true;last={x:e.clientX,y:e.clientY}}); | |
| window.addEventListener('mouseup', ()=>drag=false); | |
| window.addEventListener('mousemove', e=>{ | |
| if(!drag)return; | |
| rY += (e.clientX-last.x)*0.011; | |
| rX += (e.clientY-last.y)*0.007; | |
| rX = Math.max(-1.1, Math.min(1.1, rX)); | |
| last = {x:e.clientX, y:e.clientY}; | |
| }); | |
| canvas.addEventListener('wheel', e=>{zoom=Math.max(7,Math.min(45,zoom+e.deltaY*0.025));e.preventDefault();},{passive:false}); | |
| let lt=null; | |
| canvas.addEventListener('touchstart',e=>{lt=e.touches[0];},{passive:true}); | |
| canvas.addEventListener('touchmove',e=>{ | |
| if(!lt)return; const t=e.touches[0]; | |
| rY+=(t.clientX-lt.clientX)*0.011; rX+=(t.clientY-lt.clientY)*0.007; | |
| lt=t; e.preventDefault(); | |
| },{passive:false}); | |
| // โโ starfield โโ | |
| const sg=new THREE.BufferGeometry(); | |
| const sp=new Float32Array(600*3); | |
| for(let i=0;i<sp.length;i++) sp[i]=(Math.random()-0.5)*120; | |
| sg.setAttribute('position',new THREE.BufferAttribute(sp,3)); | |
| scene.add(new THREE.Points(sg, new THREE.PointsMaterial({color:0x0d1a33,size:0.06}))); | |
| // โโ build network from state โโ | |
| const group = new THREE.Group(); | |
| scene.add(group); | |
| // โโ DYNAMIC BRAIN SIZE โ grows as the AI learns more โโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const rawSizes = INIT.layer_sizes || [64,256,128,64,8]; | |
| const kc = (INIT.stats && (INIT.stats.knowledge_count || INIT.stats.total_images)) || 0; | |
| // Scale: 0 articles = 1 neuron, 10=2, 30=3, 60=4, 100=5, 200=7, 500=10, 1000+=12 | |
| const MAX_N = kc===0 ? 1 : kc<5 ? 2 : kc<15 ? 3 : kc<40 ? 4 : kc<80 ? 5 : kc<150 ? 6 : kc<300 ? 8 : kc<600 ? 10 : 12; | |
| // Scale all layer sizes proportionally | |
| const scaleFactor = MAX_N / 12; | |
| const sizes = rawSizes.map((s,i) => Math.max(1, Math.round(s * scaleFactor))); | |
| const nL = sizes.length; | |
| const LGAP = 5.8; | |
| const startX = -(nL-1)*LGAP/2; | |
| const acts = INIT.activations || {}; | |
| const aKeys = Object.keys(acts); | |
| function getAct(li, ni){ | |
| const k=aKeys[li]; if(!k) return Math.random()*0.4+0.1; | |
| const a=acts[k]; | |
| return (a&&a[ni]!=null) ? Math.max(0,Math.min(1,a[ni])) : Math.random()*0.3; | |
| } | |
| // Shared geometries | |
| const nGeo = new THREE.SphereGeometry(0.22, 18, 18); | |
| const gGeo = new THREE.SphereGeometry(0.34, 8, 8); | |
| const LAYER_NAMES = ['INPUT','HIDDEN-1','HIDDEN-2','HIDDEN-3','OUTPUT', | |
| 'CONV-1','CONV-2','FC-1','FC-2']; | |
| // Store neuron meshes for animation | |
| const neurons = []; // neurons[layerIdx] = [{mesh, glowMesh, baseAct, fireTimer, pos}] | |
| const connections = []; // {from, to, line, fromIdx, toIdx, lFromIdx, lToIdx} | |
| for(let li=0;li<nL;li++){ | |
| const show = Math.min(sizes[li], MAX_N); | |
| const yGap = Math.min(1.9, 11/show); | |
| const yStart= -(show-1)*yGap/2; | |
| const layer = []; | |
| for(let ni=0;ni<show;ni++){ | |
| const act = getAct(li,ni); | |
| const x = startX + li*LGAP; | |
| const y = yStart + ni*yGap; | |
| const z = (Math.random()-0.5)*0.5; | |
| // Neuron colour based on activation | |
| const r=Math.floor(act*240), g=Math.floor(act*200), b=Math.floor(60+act*100); | |
| const col = new THREE.Color(`rgb(${r},${g},${b})`); | |
| const mat = new THREE.MeshStandardMaterial({ | |
| color: col, emissive: col, | |
| emissiveIntensity: 0.3+act*1.8, | |
| roughness:0.2, metalness:0.9, | |
| }); | |
| const mesh = new THREE.Mesh(nGeo, mat); | |
| mesh.position.set(x,y,z); | |
| group.add(mesh); | |
| // Glow halo | |
| const gMat = new THREE.MeshBasicMaterial({ | |
| color:0x00f5c4, transparent:true, | |
| opacity: act>0.4?(act-0.4)*0.5:0, | |
| wireframe:true | |
| }); | |
| const gMesh = new THREE.Mesh(gGeo, gMat); | |
| gMesh.position.set(x,y,z); | |
| group.add(gMesh); | |
| layer.push({mesh, gMesh, baseAct:act, fireTimer:0, pos:new THREE.Vector3(x,y,z)}); | |
| } | |
| // Layer label | |
| const lc=document.createElement('canvas'); | |
| lc.width=256; lc.height=40; | |
| const lx=lc.getContext('2d'); | |
| lx.fillStyle='rgba(0,245,196,0.5)'; | |
| lx.font='bold 18px monospace'; lx.textAlign='center'; | |
| lx.fillText(LAYER_NAMES[li]||`L${li}`, 128, 28); | |
| const lbl = new THREE.Mesh( | |
| new THREE.PlaneGeometry(2.6,0.45), | |
| new THREE.MeshBasicMaterial({map:new THREE.CanvasTexture(lc),transparent:true,side:THREE.DoubleSide}) | |
| ); | |
| lbl.position.set(startX+li*LGAP, yStart-2.0, 0); | |
| group.add(lbl); | |
| neurons.push(layer); | |
| } | |
| // Build connections between adjacent layers | |
| let connDrawn = 0; | |
| const MAX_CONN = 160; | |
| for(let li=0;li<nL-1&&connDrawn<MAX_CONN;li++){ | |
| const fromL=neurons[li], toL=neurons[li+1]; | |
| for(let fi=0;fi<fromL.length&&connDrawn<MAX_CONN;fi++){ | |
| for(let ti=0;ti<toL.length&&connDrawn<MAX_CONN;ti++){ | |
| const sig=(fromL[fi].baseAct+toL[ti].baseAct)/2; | |
| if(sig<0.05&&Math.random()>0.3) continue; | |
| const positive=Math.random()>0.35; | |
| const col=positive?0x00f5c4:0xff6b35; | |
| const pts=[fromL[fi].pos.clone(), toL[ti].pos.clone()]; | |
| const geo=new THREE.BufferGeometry().setFromPoints(pts); | |
| const mat=new THREE.LineBasicMaterial({color:col,transparent:true,opacity:0.04+sig*0.35}); | |
| const line=new THREE.Line(geo,mat); | |
| group.add(line); | |
| connections.push({from:fromL[fi],to:toL[ti],line,mat,lFromIdx:li,lToIdx:li+1,fi,ti}); | |
| connDrawn++; | |
| } | |
| } | |
| } | |
| // โโ SIGNAL PARTICLE SYSTEM โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| // This is what makes the network look ALIVE โ glowing orbs traveling between neurons | |
| const signals = []; | |
| const sigGeo = new THREE.SphereGeometry(0.12, 8, 8); | |
| function spawnSignal(fromNeuron, toNeuron, color=0x00f5c4){ | |
| const mat = new THREE.MeshBasicMaterial({color, transparent:true, opacity:0.95}); | |
| const mesh = new THREE.Mesh(sigGeo, mat); | |
| mesh.position.copy(fromNeuron.pos); | |
| scene.add(mesh); // add to scene not group so it stays in world space | |
| // Trail | |
| const trailMat = new THREE.MeshBasicMaterial({color, transparent:true, opacity:0.4}); | |
| const trail = new THREE.Mesh(new THREE.SphereGeometry(0.07,6,6), trailMat); | |
| scene.add(trail); | |
| signals.push({ | |
| mesh, trail, mat, trailMat, | |
| from: fromNeuron.pos.clone(), | |
| to: toNeuron.pos.clone(), | |
| toNeuron, | |
| t: 0, | |
| speed: 0.028 + Math.random()*0.015, | |
| color, | |
| }); | |
| } | |
| function fireNeuron(layerIdx, neuronIdx, cascade=true){ | |
| if(layerIdx>=neurons.length || neuronIdx>=neurons[layerIdx].length) return; | |
| const n = neurons[layerIdx][neuronIdx]; | |
| n.fireTimer = 1.0; | |
| // Spawn signals to next layer | |
| if(cascade && layerIdx < nL-1){ | |
| const nextL = neurons[layerIdx+1]; | |
| const count = Math.min(nextL.length, 2+Math.floor(Math.random()*3)); | |
| const shuffled = [...nextL].sort(()=>Math.random()-0.5).slice(0, count); | |
| shuffled.forEach(target => { | |
| spawnSignal(n, target, layerIdx===0?0x00f5c4:0x5b9fff); | |
| }); | |
| } | |
| } | |
| // Every 800ms: fire random input neurons โ cascade through the network | |
| let fireTimer = 0; | |
| function triggerFiring(){ | |
| if(neurons.length===0) return; | |
| const inputLayer = neurons[0]; | |
| const count = 1+Math.floor(Math.random()*3); | |
| for(let i=0;i<count;i++){ | |
| const ni = Math.floor(Math.random()*inputLayer.length); | |
| fireNeuron(0, ni, true); | |
| } | |
| } | |
| // โโ LOSS HISTORY GRAPH โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| (function(){ | |
| const hist = INIT.loss_history||[]; | |
| if(hist.length<3) return; | |
| const gc=document.createElement('canvas'); | |
| gc.width=200; gc.height=50; | |
| gc.style.cssText='position:fixed;bottom:14px;right:14px;border:1px solid #1a2240;border-radius:6px;background:rgba(11,15,30,0.8)'; | |
| const ctx=gc.getContext('2d'); | |
| const mn=Math.min(...hist), mx=Math.max(...hist), rng=mx-mn||1; | |
| // fill | |
| ctx.beginPath(); | |
| hist.forEach((v,i)=>{const x=(i/(hist.length-1))*200,y=50-4-((v-mn)/rng)*42; i===0?ctx.moveTo(x,50):ctx.lineTo(x,y)}); | |
| ctx.lineTo(200,50); ctx.closePath(); | |
| ctx.fillStyle='rgba(0,245,196,0.08)'; ctx.fill(); | |
| // line | |
| ctx.beginPath(); | |
| hist.forEach((v,i)=>{const x=(i/(hist.length-1))*200,y=50-4-((v-mn)/rng)*42; i===0?ctx.moveTo(x,y):ctx.lineTo(x,y)}); | |
| ctx.strokeStyle='#00f5c4'; ctx.lineWidth=1.5; ctx.stroke(); | |
| document.body.appendChild(gc); | |
| const lb=document.createElement('div'); | |
| lb.style.cssText='position:fixed;bottom:68px;right:14px;font-size:9px;color:#2a3a5a;font-family:monospace'; | |
| lb.textContent='LOSS โ'; document.body.appendChild(lb); | |
| })(); | |
| // โโ HUD โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| const st = INIT.stats||{}; | |
| const isImg = INIT.type==='cnn'; | |
| const kCount = st.knowledge_count || st.total_images || 0; | |
| document.getElementById('h1').textContent = (isImg?'๐๏ธ IMAGE CNN':'๐ TEXT NETWORK')+' โ LIVE'; | |
| document.getElementById('h2').textContent = `EPOCH ${(st.epoch||0).toLocaleString()}`; | |
| document.getElementById('h3').textContent = `LOSS ${st.loss||'โ'} ACC ${st.accuracy||'โ'}%`; | |
| document.getElementById('h4').textContent = `BRAIN ${kCount} ${isImg?'images':'articles'} ยท ${sizes.map((s,i)=>Math.min(s,MAX_N)).join('โ')} neurons`; | |
| // โโ ANIMATE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| let frame = 0; | |
| const clock = new THREE.Clock(); | |
| function animate(){ | |
| requestAnimationFrame(animate); | |
| const dt = clock.getDelta(); | |
| const elaps = clock.getElapsedTime(); | |
| frame++; | |
| // โ Auto-fire every 800ms | |
| fireTimer += dt; | |
| if(fireTimer > 0.8){ | |
| triggerFiring(); | |
| fireTimer = 0; | |
| } | |
| // โก Update neurons โ pulse and fire glow | |
| neurons.forEach((layer, li)=>{ | |
| layer.forEach((n, ni)=>{ | |
| // Base sinusoidal pulse โ each neuron breathes at its own rate | |
| const phase = li*0.7 + ni*0.3; | |
| const pulse = Math.sin(elaps*2.1+phase)*0.15 + Math.sin(elaps*0.9+phase*2)*0.08; | |
| const act = Math.max(0, Math.min(1, n.baseAct + pulse + n.fireTimer*0.6)); | |
| const r=Math.floor(act*240), g=Math.floor(act*200), b=Math.floor(60+act*100); | |
| n.mesh.material.emissive.setRGB(r/255, g/255, b/255); | |
| n.mesh.material.emissiveIntensity = 0.3 + act*2.2; | |
| n.mesh.scale.setScalar(1 + n.fireTimer*0.6 + pulse*0.05); | |
| // Glow halo | |
| n.gMesh.material.opacity = n.fireTimer>0 ? n.fireTimer*0.7 : Math.max(0,(act-0.45)*0.4); | |
| n.gMesh.scale.setScalar(1 + n.fireTimer*1.0); | |
| // Decay fire | |
| if(n.fireTimer > 0) n.fireTimer = Math.max(0, n.fireTimer - dt*1.8); | |
| }); | |
| }); | |
| // โข Update connection lines โ flash when signals pass | |
| connections.forEach(conn=>{ | |
| const sig=(conn.from.baseAct+conn.to.baseAct)/2; | |
| const flash = conn.from.fireTimer*0.5; | |
| conn.mat.opacity = Math.min(0.9, 0.04 + sig*0.3 + flash); | |
| }); | |
| // โฃ Move signal particles | |
| for(let i=signals.length-1;i>=0;i--){ | |
| const s=signals[i]; | |
| s.t += s.speed; | |
| if(s.t>=1){ | |
| scene.remove(s.mesh); scene.remove(s.trail); | |
| signals.splice(i,1); | |
| // Fire destination neuron (next layer cascade) | |
| s.toNeuron.fireTimer = 0.8; | |
| // If not at output, cascade further | |
| const lIdx = neurons.findIndex(l=>l.includes(s.toNeuron)); | |
| if(lIdx>=0 && lIdx<nL-1){ | |
| const nextL=neurons[lIdx+1]; | |
| if(Math.random()>0.3){ | |
| const t2=nextL[Math.floor(Math.random()*nextL.length)]; | |
| spawnSignal(s.toNeuron, t2, 0x5b9fff); | |
| } | |
| } | |
| } else { | |
| s.mesh.position.lerpVectors(s.from, s.to, s.t); | |
| s.trail.position.lerpVectors(s.from, s.to, Math.max(0,s.t-0.08)); | |
| // Fade out near end | |
| s.mat.opacity = s.t<0.8 ? 0.95 : (1-s.t)*4.75; | |
| s.trailMat.opacity = s.t<0.8 ? 0.35 : (1-s.t)*1.75; | |
| } | |
| } | |
| // โค Camera orbit + gentle network bob | |
| const autoY = elaps * 0.12; | |
| camera.position.x = Math.sin(rY + autoY)*zoom; | |
| camera.position.y = Math.sin(rX)*zoom*0.45 + 1; | |
| camera.position.z = Math.cos(rY + autoY)*zoom; | |
| camera.lookAt(0, 0, 0); | |
| kl.intensity = 7 + Math.sin(elaps*1.5)*2; | |
| bl.intensity = 3 + Math.cos(elaps*2.3)*1; | |
| kl.position.x = Math.sin(elaps*0.4)*6; | |
| group.position.y = Math.sin(elaps*0.6)*0.15; | |
| renderer.render(scene, camera); | |
| } | |
| animate(); | |
| window.addEventListener('resize',()=>{ | |
| camera.aspect=innerWidth/innerHeight; | |
| camera.updateProjectionMatrix(); | |
| renderer.setSize(innerWidth,innerHeight); | |
| }); | |
| </script></body></html>""" | |
| def get_text_viz(): | |
| s = network.get_viz_state(); s['type']='text' | |
| h = build_viz(json.dumps(s)) | |
| e = h.replace('"','"').replace('\n',' ') | |
| return f'<iframe srcdoc="{e}" style="width:100%;height:650px;border:none;border-radius:12px"></iframe>' | |
| def get_img_viz(): | |
| s = img_network.get_viz_state() | |
| h = build_viz(json.dumps(s)) | |
| e = h.replace('"','"').replace('\n',' ') | |
| return f'<iframe srcdoc="{e}" style="width:100%;height:650px;border:none;border-radius:12px"></iframe>' | |
| # โโ UI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| with gr.Blocks(title="Living Neural Network") as demo: | |
| gr.Markdown("# ๐ง Living Neural Network\n*Two AIs training on live internet data โ one reads, one looks*") | |
| sbar = gr.Textbox(label="", value=status_bar(), interactive=False, lines=2) | |
| with gr.Tabs(): | |
| # CONTROL | |
| with gr.Tab("๐๏ธ Control"): | |
| gr.Markdown("### ๐ Text Network") | |
| with gr.Row(): | |
| gr.Button("โถ Start Text", variant="primary").click(start_text, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| gr.Button("โน Stop Text", variant="stop" ).click(stop_text, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| gr.Button("๐ก Fetch Text" ).click(do_fetch_text, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| with gr.Row(): | |
| ts=gr.Slider(10,500,100,step=10,label="Steps") | |
| gr.Button("๐ฅ Train Text").click(do_train_text,inputs=ts,outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| gr.Markdown("---\n### ๐๏ธ Image CNN") | |
| with gr.Row(): | |
| gr.Button("โถ Start Images",variant="primary").click(start_img, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| gr.Button("โน Stop Images",variant="stop" ).click(stop_img, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| gr.Button("๐ผ๏ธ Fetch Images" ).click(do_fetch_img, outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| with gr.Row(): | |
| is_=gr.Slider(5,100,20,step=5,label="Steps") | |
| gr.Button("๐ฅ Train CNN").click(do_train_img,inputs=is_,outputs=gr.Textbox(label="",lines=1,interactive=False)) | |
| # STATS | |
| with gr.Tab("๐ Stats"): | |
| with gr.Row(): | |
| tmd=gr.Markdown(value=text_stats()) | |
| imd=gr.Markdown(value=img_stats()) | |
| lbox=gr.HTML(value=get_log(),label="Live Logs") | |
| # 3D TEXT VIZ | |
| with gr.Tab("๐ฎ Text Network 3D"): | |
| gr.Markdown("**Neurons fire & signals travel leftโright in real time.** Drag=rotate Scroll=zoom") | |
| tvb=gr.Button("๐ Refresh",variant="primary") | |
| tvo=gr.HTML('<div style="height:650px;background:#030610;border-radius:12px;display:flex;align-items:center;justify-content:center;color:#00f5c4;font-family:monospace;font-size:14px">Click ๐ Refresh to load the 3D network</div>') | |
| tvb.click(get_text_viz,outputs=tvo) | |
| # 3D IMAGE VIZ | |
| with gr.Tab("๐๏ธ Image CNN 3D"): | |
| gr.Markdown("**Conv layers visualised in 3D.** Each block learns progressively deeper features.") | |
| ivb=gr.Button("๐ Refresh",variant="primary") | |
| ivo=gr.HTML('<div style="height:650px;background:#030610;border-radius:12px;display:flex;align-items:center;justify-content:center;color:#00f5c4;font-family:monospace;font-size:14px">Click ๐ Refresh to load CNN visualization</div>') | |
| ivb.click(get_img_viz,outputs=ivo) | |
| # CHAT | |
| with gr.Tab("๐ฌ Chat with my AI"): | |
| gr.Markdown( | |
| "### Talk to your AI โ it answers from what it has actually learned\n" | |
| "The more articles it collects, the smarter the answers.\n" | |
| "Type `stats` to see what it knows. Type `help` for tips." | |
| ) | |
| chatbox = gr.Chatbot(label="", height=480) | |
| with gr.Row(): | |
| msg_in = gr.Textbox(label="", placeholder="Ask anything โ e.g. 'What's happening in AI?'", scale=5) | |
| send_b = gr.Button("Send", variant="primary", scale=1) | |
| send_b.click(chat_fn, inputs=[msg_in, chatbox], outputs=[chatbox, msg_in]) | |
| msg_in.submit(chat_fn, inputs=[msg_in, chatbox], outputs=[chatbox, msg_in]) | |
| gr.Examples( | |
| [["What's happening in AI and technology?"], | |
| ["Tell me about recent science discoveries"], | |
| ["What's in the news about sports?"], | |
| ["stats"],["help"]], | |
| inputs=msg_in | |
| ) | |
| # KNOWLEDGE | |
| with gr.Tab("๐ Knowledge Base"): | |
| gr.Markdown("Everything the AI has read โ saved to `knowledge.jsonl` instantly") | |
| kmd=gr.Markdown(value=get_knowledge()) | |
| # DATA FEED | |
| with gr.Tab("๐ฐ Data Feed"): | |
| fmd=gr.Markdown(value=get_feed()) | |
| # PREDICT TEXT | |
| with gr.Tab("๐ Classify Text"): | |
| gr.Markdown("Test your trained text model") | |
| pt=gr.Textbox(label="Text",lines=3,placeholder="Scientists discover...") | |
| pb=gr.Button("Classify",variant="primary") | |
| po=gr.Markdown() | |
| pb.click(predict_text,inputs=pt,outputs=po) | |
| # PREDICT IMAGE | |
| with gr.Tab("๐ผ๏ธ Classify Image"): | |
| gr.Markdown("Test your trained image CNN") | |
| pi=gr.Image(label="Upload image",type="filepath") | |
| pib=gr.Button("Classify",variant="primary") | |
| pio=gr.Markdown() | |
| pib.click(predict_img,inputs=pi,outputs=pio) | |
| # timers | |
| t3=gr.Timer(3); t5=gr.Timer(5); t8=gr.Timer(8); t12=gr.Timer(12) | |
| t3.tick(status_bar,outputs=sbar) | |
| t3.tick(get_log,outputs=lbox) | |
| t5.tick(text_stats,outputs=tmd) | |
| t5.tick(img_stats,outputs=imd) | |
| t8.tick(get_knowledge,outputs=kmd) | |
| t8.tick(get_feed,outputs=fmd) | |
| t12.tick(get_text_viz,outputs=tvo) | |
| t12.tick(get_img_viz,outputs=ivo) | |
| # โโ AUTO-START: both networks begin training immediately on startup โโโโโโโโโโ | |
| def _auto_start(): | |
| import time as _t | |
| _t.sleep(3) # give Gradio time to finish starting | |
| try: | |
| start_text() | |
| log("๐ค Auto-started TEXT network") | |
| except Exception as e: | |
| log(f"โ Auto-start text error: {e}") | |
| try: | |
| start_img() | |
| log("๐ค Auto-started IMAGE network") | |
| except Exception as e: | |
| log(f"โ Auto-start image error: {e}") | |
| threading.Thread(target=_auto_start, daemon=True).start() | |
| if __name__=="__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860, share=True, ssr_mode=False) | |