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'
{line}
' rows = ''.join(color(l) for l in lines) or '
No logs yet...
' return f'
{rows}
' 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 """
■ LOADING...
drag to rotate · scroll to zoom · signals travel left→right every 800ms
""" 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'' def get_img_viz(): s = img_network.get_viz_state() h = build_viz(json.dumps(s)) e = h.replace('"','"').replace('\n',' ') return f'' # ── 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('
Click 🔄 Refresh to load the 3D network
') 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('
Click 🔄 Refresh to load CNN visualization
') 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)