fableforge-ai/infinite-npc
3
1import os2import json3import gradio as gr4 5# gradio 4.44 / gradio_client schema-bug guard6import gradio_client.utils as _gcu7_o = _gcu._json_schema_to_python_type8_gcu._json_schema_to_python_type = lambda s, d=None: ("bool" if isinstance(s, bool) else _o(s, d))9_g = _gcu.get_type10_gcu.get_type = lambda s: ("any" if not isinstance(s, dict) else _g(s))11 12try:13 import websocket as _wsc # websocket-client14except Exception:15 _wsc = None16BRIDGE_URL = os.environ.get("BRIDGE_URL", "").strip()17BRIDGE_TOKEN = os.environ.get("BRIDGE_TOKEN", "").strip()18 19 20def _bridge(op, timeout=120):21 if not (_wsc and BRIDGE_URL):22 return None23 ws = _wsc.create_connection(BRIDGE_URL, timeout=timeout)24 try:25 ws.send(json.dumps({"t": "auth", "token": BRIDGE_TOKEN}))26 if json.loads(ws.recv()).get("t") != "ok":27 return None28 ws.send(json.dumps(op))29 return json.loads(ws.recv())30 finally:31 ws.close()32 33 34def chat(messages, max_tokens=160, temperature=0.8):35 r = _bridge({"t": "ask", "messages": messages, "max_tokens": max_tokens, "temperature": temperature})36 if r and r.get("t") == "answer" and r.get("text"):37 return r["text"].strip()38 return "(the tavern falls quiet — Grix's mind is elsewhere)"39 40 41def remote_recall(limit=40):42 try:43 r = _bridge({"t": "recall", "q": "", "limit": limit}, timeout=15)44 if r and r.get("t") == "memories":45 return [m.get("value") for m in r["items"] if m.get("value")]46 except Exception:47 pass48 return None49 50 51def remote_remember(value):52 try:53 return bool(_bridge({"t": "remember", "key": "grix", "value": value}, timeout=15))54 except Exception:55 return False56 57 58NPC_SYSTEM = (59 "You are Grix, a grizzled, one-eyed tavern-keeper in the fantasy town of Ashfen. "60 "You remember everyone who has ever spoken to you and reference past visitors when relevant. "61 "Stay fully in character. Keep replies to 1-3 sentences, gruff but warm. /no_think"62)63MEMORY = [] # local fallback if the bridge is unreachable64MAX_MEMORY = 4065 66 67def _memory_block(mems):68 if not mems:69 return "You have not met anyone yet."70 return "Things you remember from past visitors:\n- " + "\n- ".join(mems[-MAX_MEMORY:])71 72 73def respond(user_msg, history):74 history = history or []75 remote = remote_recall()76 shared = remote is not None77 mems = remote if shared else MEMORY78 msgs = [{"role": "system", "content": NPC_SYSTEM + "\n\n" + _memory_block(mems)}]79 for u, a in history[-6:]:80 msgs.append({"role": "user", "content": u})81 msgs.append({"role": "assistant", "content": a})82 msgs.append({"role": "user", "content": user_msg})83 reply = chat(msgs, max_tokens=160)84 fact = user_msg.strip()[:160]85 if shared:86 remote_remember(fact)87 count, tag = len(mems) + 1, "🌐 shared P2P memory · brain on VPS"88 else:89 MEMORY.append(fact)90 count, tag = len(MEMORY), "🧠 local (bridge offline)"91 history = history + [(user_msg, reply)]92 return history, history, tag + " — " + str(count) + " remembered"93 94 95with gr.Blocks(title="Infinite NPC — Grix of Ashfen",96 theme=gr.themes.Soft(primary_hue="amber")) as demo:97 gr.Markdown(98 "# 🍺 Infinite NPC — *Grix of Ashfen*\n"99 "A tavern-keeper whose **brain runs on a P2P node** and whose **memory is shared across every "100 "visitor** (and every PearAgent peer on the swarm). Tell him things — the next stranger will know.\n"101 "> ⚡ Inference + memory are federated over a WebSocket→Hyperswarm bridge, not this Space."102 )103 chatbot = gr.Chatbot(label="The Rusty Tankard", height=460)104 state = gr.State([])105 with gr.Row():106 box = gr.Textbox(placeholder="Well met, Grix… what have you heard lately?", scale=4, show_label=False)107 send = gr.Button("Speak", variant="primary", scale=1)108 mem = gr.Textbox(label="Memory", interactive=False)109 send.click(respond, [box, state], [chatbot, state, mem], api_name="respond").then(lambda: "", None, box)110 box.submit(respond, [box, state], [chatbot, state, mem]).then(lambda: "", None, box)111 gr.Markdown("---\n⬇️ [**Download the model**](https://huggingface.co/King3Djbl/mythos-9b-unhinged) · 🧭 [FableForge Nexus](https://huggingface.co/spaces/fableforge-ai/fableforge-nexus) · 👻 [Ghost Writer](https://huggingface.co/spaces/fableforge-ai/ghost-writer) · 🌳 [Story Tree](https://huggingface.co/spaces/fableforge-ai/semantic-story-tree) · 🎭 [Dual-GM](https://huggingface.co/spaces/King3Djbl/dual-gm-simulator)\n\n*Grix's brain runs on a P2P node, not this Space — his memory is shared across every visitor.*")112 113if __name__ == "__main__":114 demo.queue(default_concurrency_limit=2).launch(server_name="0.0.0.0", server_port=7860)115 