File size: 8,754 Bytes
b1fe0dd e2ffbc8 962f379 b1fe0dd f9e683f b1fe0dd f9e683f 962f379 f9e683f 6862189 f9e683f b1fe0dd f9e683f b1fe0dd 6862189 a58b36c f9e683f 6862189 f9e683f e2ffbc8 6862189 e2ffbc8 f9e683f 6862189 f9e683f 6862189 e2ffbc8 6862189 e2ffbc8 6862189 e2ffbc8 f9e683f 6862189 f9e683f 6862189 a58b36c 6862189 d597cda 6862189 d597cda f9e683f b1fe0dd f9e683f b1fe0dd dfdec71 962f379 6862189 962f379 b1fe0dd 6862189 ca4200f 6862189 ca4200f 6862189 ca4200f f9e683f ca4200f f9e683f ca4200f f9e683f ca4200f f9e683f ca4200f f9e683f 6862189 ca4200f 6862189 f9e683f 6862189 f9e683f 6862189 f9e683f dfdec71 f9e683f dfdec71 ca4200f dfdec71 6862189 f9e683f dfdec71 f9e683f dfdec71 ca4200f f9e683f ca4200f f9e683f 6862189 ca4200f 6862189 ca4200f dfdec71 ca4200f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
import gradio as gr
import asyncio
import websockets
import json
import uuid
import argparse
from datetime import datetime
import logging
import sys
import os
import random
# Configure logging - the gossip queen π’π sets up the chatterbox!
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger("chat-node")
# Fun usernames with emojis - the VIP list of quirky characters! ππ
FUN_USERNAMES = [
"CosmicJester π", "PixelPanda πΌ", "QuantumQuack π¦", "StellarSquirrel πΏοΈ",
"GizmoGuru βοΈ", "NebulaNinja π ", "ByteBuster πΎ", "GalacticGopher π",
"RocketRaccoon π", "EchoElf π§", "PhantomFox π¦", "WittyWizard π§",
"LunarLlama π", "SolarSloth βοΈ", "AstroAlpaca π¦", "CyberCoyote πΊ",
"MysticMoose π¦", "GlitchGnome π§", "VortexViper π", "ChronoChimp π"
]
# Directory for logs - the secret vault where tales are stashed! ποΈπ
LOG_DIR = "user_logs"
os.makedirs(LOG_DIR, exist_ok=True)
# Node name - the appβs codename generator, sneaky and slick! π΅οΈββοΈπΎ
def get_node_name():
"""π² Spins the wheel of fate to name our node - a random alias or user pick! π·οΈ"""
parser = argparse.ArgumentParser(description='Start a chat node with a specific name')
parser.add_argument('--node-name', type=str, default=None, help='Name for this chat node')
parser.add_argument('--port', type=int, default=7860, help='Port to run the Gradio interface on')
args = parser.parse_args()
return args.node_name or f"node-{uuid.uuid4().hex[:8]}", args.port
# Log file generator - crafts a shiny new logbook with a timestamp twist! π
βοΈ
def get_log_file(username):
"""β° Stamps the clock and names the log after our witty hero - fresh and ready! π"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
safe_username = username.replace(" ", "_").encode('ascii', 'ignore').decode('ascii')
return os.path.join(LOG_DIR, f"{safe_username}_{timestamp}.md")
# Log saver - the scribe that pens epic log tales into Markdown glory! ποΈπ
def save_log_entry(username, entry):
"""ποΈ Scribbles a log entry into a Markdown masterpiece - tales of triumph and woe! π"""
log_file = get_log_file(username)
try:
with open(log_file, 'a') as f:
f.write(f"```log\n[{entry['timestamp']}] {entry['level']}: {entry['message']}\n```\n")
logger.info(f"Saved log entry to {log_file}")
return log_file
except Exception as e:
logger.error(f"Error saving log to {log_file}: {e}")
return None
# Log loader - the treasure hunter digging up the latest log loot! π΄ββ οΈπ°
def load_latest_log(username):
"""π Hunts down the freshest log file and dresses it up with line numbers - snazzy! β¨"""
safe_username = username.replace(" ", "_").encode('ascii', 'ignore').decode('ascii')
log_files = [f for f in os.listdir(LOG_DIR) if f.startswith(safe_username) and f.endswith(".md")]
if not log_files:
return "# No logs yet\nStart interacting to generate logs!"
latest_file = max(log_files, key=lambda f: os.path.getmtime(os.path.join(LOG_DIR, f)))
try:
with open(os.path.join(LOG_DIR, latest_file), 'r') as f:
content = f.read()
lines = content.strip().split('\n')
numbered_content = "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines))
return f"# Log for {username} ({latest_file})\n\n{numbered_content}"
except Exception as e:
logger.error(f"Error loading log {latest_file}: {e}")
return "# Error loading log\nCheck server logs for details."
active_connections = {}
# WebSocket handler - the bouncer at the log party, keeping the vibe alive! ππͺ
async def websocket_handler(websocket, path):
"""π§ Listens at the door, letting log fans in and kicking out crashers! π¨"""
try:
client_id = str(uuid.uuid4())
room_id = "logs"
active_connections.setdefault(room_id, {})[client_id] = websocket
logger.info(f"Client {client_id} connected to logs")
async for message in websocket:
try:
data = json.loads(message)
data["timestamp"] = datetime.now().isoformat()
await broadcast_message(data, room_id)
except json.JSONDecodeError:
await websocket.send(json.dumps({"type": "error", "content": "Invalid JSON", "timestamp": datetime.now().isoformat(), "sender": "system"}))
except websockets.ConnectionClosed:
logger.info(f"Client {client_id} disconnected from logs")
finally:
if room_id in active_connections and client_id in active_connections[room_id]:
del active_connections[room_id][client_id]
if not active_connections[room_id]:
del active_connections[room_id]
# Broadcaster - the town crier shouting log updates to all whoβll listen! π£π
async def broadcast_message(message, room_id):
"""π’ Blasts the latest log scoop to every ear in the room - no one misses out! ποΈ"""
if room_id in active_connections:
disconnected = []
for client_id, ws in active_connections[room_id].items():
try:
await ws.send(json.dumps(message))
except websockets.ConnectionClosed:
disconnected.append(client_id)
for client_id in disconnected:
del active_connections[room_id][client_id]
# WebSocket server starter - lights the fuse on the log-streaming rocket! ππ₯
async def start_websocket_server(host='0.0.0.0', port=8765):
"""π Fires up the WebSocket engine, ready to zip logs at warp speed! β‘"""
server = await websockets.serve(websocket_handler, host, port)
logger.info(f"WebSocket server started on ws://{host}:{port}")
return server
# Log handler - the sneaky spy logging every move with a witty twist! π΅οΈββοΈπ
class LogBroadcastHandler(logging.Handler):
def __init__(self, username):
super().__init__()
self.username = username
def emit(self, record):
"""π΅οΈββοΈ Catches every whisper and scribbles it into the logbook with flair! βοΈ"""
entry = {
"timestamp": datetime.now().isoformat(),
"level": record.levelname,
"message": self.format(record),
"name": record.name
}
log_file = save_log_entry(self.username, entry)
if log_file:
asyncio.create_task(broadcast_message({
"type": "log",
"content": f"New log entry saved to {log_file}",
"timestamp": entry["timestamp"],
"sender": "system",
"username": self.username
}, "logs"))
# Interface maker - the artist painting a live log masterpiece! π¨πΌοΈ
def create_gradio_interface(username):
"""ποΈ Whips up a snazzy UI canvas that updates live with log magic! π"""
logger.handlers = [LogBroadcastHandler(username)]
def update_log_display(dummy_input):
"""π The puppet master pulling fresh log strings for the show! π¬"""
return load_latest_log(username)
with gr.Blocks(title=f"Log Viewer: {NODE_NAME}", css=".code-container { font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; }") as interface:
gr.Markdown(f"# Log Viewer for {username} on Node: {NODE_NAME}")
gr.Markdown("Logs update live as new entries are written - watch the action unfold! π₯")
dummy_input = gr.Textbox(visible=False, value="trigger", interactive=True)
log_display = gr.Code(label="Your Latest Log", language="markdown", lines=20, elem_classes=["code-container"])
# Live interface FTW! π
interface = gr.Interface(
fn=update_log_display,
inputs=[dummy_input],
outputs=[log_display],
live=True,
title=f"Log Viewer: {NODE_NAME}"
)
return interface
# Main event - the ringmaster kicking off the log circus! πͺπ€‘
async def main():
"""π€ Drops the mic and starts the log party - itβs showtime, folks! π"""
global NODE_NAME
NODE_NAME, port = get_node_name()
await start_websocket_server()
username = random.choice(FUN_USERNAMES)
logger.info(f"Assigned username: {username}")
interface = create_gradio_interface(username)
import uvicorn
await uvicorn.Server(uvicorn.Config(interface, host="0.0.0.0", port=port)).serve()
if __name__ == "__main__":
asyncio.run(main()) |