awacke1's picture
Update app.py
dfdec71 verified
raw
history blame
6.89 kB
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
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
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
LOG_DIR = "user_logs"
os.makedirs(LOG_DIR, exist_ok=True)
# Node name
def get_node_name():
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
def get_log_file(username):
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")
def save_log_entry(username, entry):
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
def load_latest_log(username):
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 = {}
async def websocket_handler(websocket, path):
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]
async def broadcast_message(message, room_id):
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]
async def start_websocket_server(host='0.0.0.0', port=8765):
server = await websockets.serve(websocket_handler, host, port)
logger.info(f"WebSocket server started on ws://{host}:{port}")
return server
class LogBroadcastHandler(logging.Handler):
def __init__(self, username):
super().__init__()
self.username = username
def emit(self, record):
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"))
def create_gradio_interface(username):
logger.handlers = [LogBroadcastHandler(username)]
def update_log_display(dummy_input):
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.")
# Dummy input to trigger live updates
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"])
# Set live=True to continuously update the log display
interface = gr.Interface(
fn=update_log_display,
inputs=[dummy_input],
outputs=[log_display],
live=True,
title=f"Log Viewer: {NODE_NAME}"
)
return interface
async def main():
global NODE_NAME
NODE_NAME, port = get_node_name()
await start_websocket_server()
# Assign a random username
username = random.choice(FUN_USERNAMES)
logger.info(f"Assigned username: {username}")
# Create and run Gradio interface
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())