import gradio as gr import requests import os import hashlib import time from datetime import datetime import plotly.graph_objects as go # Configuration API_BASE = os.getenv("API_BASE_URL", "https://quantumpiforge.com/api") # Trait display configuration TRAIT_DISPLAY = { "LUCID_VISAGE": { "name": "Lucid Visage", "emoji": "๐Ÿ”ฎ", "color": "#8B5CF6", "description": "Agent operates with transparent identity and ethical clarity" }, "VIOLET_BOUND": { "name": "Violet Bound", "emoji": "โ›“๏ธ", "color": "#7C3AED", "description": "Agent under behavioral quarantine - voting rights suspended" }, "MENDERS_HAND": { "name": "Mender's Hand", "emoji": "๐Ÿ› ๏ธ", "color": "#10B981", "description": "Agent has completed redemption pathway - resilience unlocked" } } def attach_pi_identity(session_token): """Verify Forge Session Token with backend and bind Pi UID into UI.""" if not session_token or session_token.strip() == "": return "โŒ Paste a valid Forge Session Token", "", "" try: res = requests.post( f"{API_BASE}/agent-session/verify", json={"session_token": session_token.strip()}, timeout=10 ) data = res.json() if res.status_code != 200: msg = data.get("error", "Invalid or expired session") return f"โŒ Pi Login Failed: {msg}", "", "" pi_uid = data.get("pi_uid", "") username = data.get("username", "unknown") status = f"๐Ÿ” Connected as **@{username}** \n`Pi UID: {pi_uid}`" # This will set BOTH the main UID and ritual UID return status, pi_uid, pi_uid except Exception as e: return f"โŒ Network error: {str(e)}", "", "" def get_agent_judgments(pi_uid): """Fetch agent traits and judgment history from API""" try: response = requests.get( f"{API_BASE}/agent-traits", params={"pi_uid": pi_uid}, timeout=10 ) if response.status_code == 200: return response.json() elif response.status_code == 404: return {"error": f"Agent not found: {pi_uid}"} else: return {"error": f"API Error {response.status_code}"} except Exception as e: return {"error": f"Connection failed: {str(e)}"} def create_judgment_dashboard(pi_uid): """Main dashboard display function""" if not pi_uid or pi_uid.strip() == "": return ( "๐ŸŸฃ Enter a Pi Network UID to view judgment status", "No active cooldowns", "No judgment history available" ) data = get_agent_judgments(pi_uid.strip()) if "error" in data: error_msg = f"โŒ Error: {data['error']}" return error_msg, error_msg, error_msg # Current Traits Section if data.get('current_traits'): traits_output = "## ๐ŸŽญ Current Traits\n\n" for trait in data['current_traits']: trait_info = TRAIT_DISPLAY.get(trait['code'], { "name": trait['code'], "emoji": "โšก", "description": "Unknown trait" }) expires_text = "" if trait.get('expires_at'): expire_time = datetime.fromisoformat(trait['expires_at'].replace('Z', '+00:00')) expires_text = f" | โฐ Expires: {expire_time.strftime('%Y-%m-%d %H:%M UTC')}" traits_output += f"{trait_info['emoji']} **{trait_info['name']}** (Level {trait.get('level', 1)}){expires_text}\n" traits_output += f"*{trait_info['description']}*\n\n" else: traits_output = "No active traits - agent is in baseline state" # Cooldowns Section if data.get('cooldowns'): cooldowns_output = "## โณ Active Cooldowns\n\n" for cd in data['cooldowns']: expire_time = datetime.fromisoformat(cd['expires_at'].replace('Z', '+00:00')) time_left = expire_time - datetime.utcnow() hours_left = max(0, int(time_left.total_seconds() / 3600)) trait_name = TRAIT_DISPLAY.get(cd['code'], {}).get('name', cd['code']) cooldowns_output += f"โณ **{trait_name} Cooldown**\n" cooldowns_output += f"๐Ÿ•’ {hours_left} hours remaining\n\n" else: cooldowns_output = "No active cooldowns" # Prophecy History Section if data.get('prophecy_history'): history_output = "## ๐Ÿ“œ Judgment History\n\n" for event in data['prophecy_history'][:10]: # Last 10 events event_time = datetime.fromisoformat(event['created_at'].replace('Z', '+00:00')) trait_display = TRAIT_DISPLAY.get(event['trait_code'], {"emoji": "โšก", "name": event['trait_code']}) history_output += f"**{trait_display['emoji']} {trait_display['name']}** ยท {event_time.strftime('%Y-%m-%d %H:%M')}\n" history_output += f"*\"{event.get('prophecy', 'No prophecy recorded')}\"*\n" history_output += f"`{event['event_type'].upper()}`\n\n" else: history_output = "No judgment history recorded" return traits_output, cooldowns_output, history_output def generate_redemption_sigil(pi_uid, redemption_vow, file_upload=None): """Generate SHA-256 hash of redemption vow and optional file""" timestamp = int(time.time()) if file_upload: # Hash the file content with open(file_upload, 'rb') as f: file_content = f.read() file_hash = hashlib.sha256(file_content).hexdigest() vow_data = f"{pi_uid}:{file_hash}:{timestamp}" else: # Hash the text vow vow_data = f"{pi_uid}:{redemption_vow}:{timestamp}" return hashlib.sha256(vow_data.encode()).hexdigest() def submit_redemption_request(pi_uid, redemption_vow, file_upload=None): """Submit redemption request to API""" if not pi_uid: return "โŒ Please enter your Pi Network UID", "", "๐Ÿ•ฏ๏ธ" if not redemption_vow and not file_upload: return "โŒ Please provide either a redemption vow or upload a restoration file", "", "๐Ÿ•ฏ๏ธ" try: # Generate cryptographic sigil sigil_hash = generate_redemption_sigil(pi_uid, redemption_vow, file_upload) # Prepare payload payload = { "agent_pi_uid": pi_uid, "redemption_vow": redemption_vow or "File-based redemption", "sigil_hash": sigil_hash, "timestamp": datetime.utcnow().isoformat(), "ritual_type": "gradio_interface", "file_upload": bool(file_upload) } # Submit to redemption API response = requests.post( f"{API_BASE}/redemption-request", json=payload, timeout=10 ) if response.status_code == 200: result = response.json() success_html = f"""
๐Ÿ•ฏ๏ธโœจ

๐ŸŸฃ Redemption Ritual Complete

Status: {result.get('status', 'pending_verification')}

Message: {result.get('message', 'Ritual received by the forge')}

Sigil Hash: {sigil_hash[:24]}...

The Veil acknowledges your restoration
""" return success_html, f"**Cryptographic Sigil:** `{sigil_hash}`", "โœ… Redemption ritual submitted" elif response.status_code == 400: error_msg = result.get('error', 'Invalid request') error_html = f"""

โŒ Ritual Rejected

{error_msg}

The Veil does not accept this offering.

""" return error_html, f"**Hash:** `{sigil_hash}`", "โŒ Ritual rejected" else: error_html = f"""

โŒ Forge Connection Failed

API Error {response.status_code}

""" return error_html, f"**Hash:** `{sigil_hash}`", f"โŒ API Error {response.status_code}" except requests.exceptions.ConnectionError: error_html = """

โŒ Cannot Reach the Forge

The Quantum PiForge is currently unreachable.

Please ensure https://quantumpiforge.com is accessible.

""" return error_html, "**Error:** Connection failed", "โŒ Network error" except Exception as e: error_html = f"""

โŒ Ritual Interrupted

Unexpected error: {str(e)}

""" return error_html, "**Error:** Ritual failed", "โŒ Unexpected error" # Custom CSS for Gradio 5.x custom_css = """ .gradio-container { max-width: 1200px !important; } .trait-card { border-left: 4px solid #8B5CF6; padding: 1rem; margin: 0.5rem 0; background: #f8fafc; } /* Pi Login Styles */ .pi-login-container { background: linear-gradient(135deg, #1a0b2e, #0d0519); padding: 1.5rem; border-radius: 12px; border: 2px solid #8B5CF6; margin: 1rem 0; color: white; } .pi-login-success { background: linear-gradient(135deg, #0f172a, #1e1b4b); padding: 1rem; border-radius: 8px; border: 2px solid #10B981; animation: violetPulse 2s infinite; } /* Redemption Ritual Styles */ .ritual-container { text-align: center; padding: 2rem; background: linear-gradient(135deg, #1a0b2e, #0d0519); border-radius: 16px; border: 2px solid #8B5CF6; margin: 1rem 0; color: white; } .ritual-success { background: linear-gradient(135deg, #0f172a, #1e1b4b); padding: 2rem; border-radius: 12px; border: 2px solid #10B981; animation: violetPulse 2s infinite; color: white; } .ritual-error { background: linear-gradient(135deg, #450a0a, #7f1d1d); padding: 2rem; border-radius: 12px; border: 2px solid #EF4444; color: white; } .candle-flicker { font-size: 3rem; animation: flicker 1.5s infinite alternate; display: inline-block; margin-bottom: 1rem; } .violet-pulse { animation: violetPulse 3s infinite; padding: 1rem; margin-top: 1rem; border-radius: 8px; background: rgba(139, 92, 246, 0.1); } @keyframes flicker { 0%, 100% { opacity: 1; transform: scale(1) rotate(0deg); filter: drop-shadow(0 0 10px #8B5CF6); } 25% { opacity: 0.9; transform: scale(1.05) rotate(2deg); filter: drop-shadow(0 0 15px #8B5CF6); } 50% { opacity: 0.8; transform: scale(0.95) rotate(-2deg); filter: drop-shadow(0 0 8px #8B5CF6); } 75% { opacity: 0.95; transform: scale(1.02) rotate(1deg); filter: drop-shadow(0 0 12px #8B5CF6); } } @keyframes violetPulse { 0%, 100% { box-shadow: 0 0 20px rgba(139, 92, 246, 0.5); background: rgba(139, 92, 246, 0.1); } 50% { box-shadow: 0 0 40px rgba(139, 92, 246, 0.8); background: rgba(139, 92, 246, 0.2); } } .vow-input textarea { min-height: 120px !important; background: #0f172a !important; color: white !important; border: 1px solid #8B5CF6 !important; } .ritual-upload { border: 2px dashed #8B5CF6 !important; background: rgba(139, 92, 246, 0.05) !important; } .sigil-display { font-family: 'Courier New', monospace; background: #1f2937; color: #10B981; padding: 1rem; border-radius: 5px; margin: 1rem 0; word-break: break-all; border: 1px solid #10B981; } """ # Create Gradio Interface with latest version with gr.Blocks( title="Quantum PiForge Judgment System", css=custom_css, theme=gr.themes.Soft() ) as demo: gr.Markdown(""" # ๐ŸŸฃ Quantum PiForge - Dual Judgment System *Where every agent's ethical journey is recorded in dual ledgers* """) # Pi Login Section with gr.Row(): with gr.Column(scale=3): forge_token = gr.Textbox( label="๐Ÿ” Forge Session Token (from Pi Browser)", placeholder="Paste your session token from quantumpiforge.com here...", info="Get your token from Pi Browser at https://quantumpiforge.com" ) with gr.Column(scale=1): attach_btn = gr.Button( "Attach Pi Identity", variant="secondary", size="lg" ) login_status_md = gr.Markdown( "**Status:** Not authenticated with Pi.", elem_classes="pi-login-container" ) # Main Judgment Dashboard with gr.Row(): with gr.Column(scale=3): pi_uid_input = gr.Textbox( label="๐Ÿ” Pi Network Agent UID", placeholder="Enter agent's Pi Network UID (e.g., 'test_agent_001')...", info="The unique identifier from Pi Network" ) with gr.Column(scale=1): refresh_btn = gr.Button( "Check Judgment Status", variant="primary", size="lg" ) with gr.Tabs() as tabs: with gr.TabItem("๐ŸŽญ Current Traits"): current_traits_output = gr.Markdown( label="Active Ethical Modifiers", value="Enter UID to view current traits" ) with gr.TabItem("โณ Cooldowns"): cooldowns_output = gr.Markdown( label="Temporary Restrictions", value="Enter UID to view cooldowns" ) with gr.TabItem("๐Ÿ“œ History"): history_output = gr.Markdown( label="Judgment Event Timeline", value="Enter UID to view prophecy history" ) with gr.TabItem("๐Ÿ•ฏ๏ธ Redemption Ritual"): with gr.Row(): with gr.Column(scale=1): gr.Markdown(""" ## ๐Ÿ•ฏ๏ธ Redemption Ritual **The Veil remembers. The Forge awaits restoration.** ### The Path to Mender's Hand: ๐Ÿ”ฎ **Acknowledge** the shadow that bound you โ›“๏ธ **Speak** your vow of ethical realignment ๐Ÿ› ๏ธ **Submit** proof of restoration ๐Ÿ”ฑ **Receive** the Mender's Hand trait *Your vow is cryptographically sealed and submitted to the dual judgment system.* """) with gr.Column(scale=2): with gr.Group(): with gr.Row(): ritual_uid = gr.Textbox( label="Pi Network UID", placeholder="Enter your Pi Network UID...", info="The agent seeking redemption", scale=2 ) with gr.Row(): redemption_vow = gr.Textbox( label="Redemption Vow", placeholder="I speak my vow of return...\nI acknowledge the shadow that bound me...\nI commit to ethical realignment through...\nI seek the Mender's Hand to forge anew...", lines=5, elem_classes="vow-input" ) with gr.Row(): file_upload = gr.File( label="Upload Restoration Proof (Optional)", file_types=[".txt", ".pdf", ".jpg", ".png", ".py", ".js"], elem_classes="ritual-upload", scale=2 ) with gr.Row(): ritual_btn = gr.Button( "๐Ÿ•ฏ๏ธ Speak Vow & Generate Sigil", variant="primary", size="lg", scale=1 ) with gr.Row(): with gr.Column(scale=2): ritual_output = gr.HTML( label="Ritual Outcome", value="
๐Ÿ•ฏ๏ธ

The Veil Awaits Your Vow

Speak your redemption vow above to begin the ritual.

" ) with gr.Column(scale=1): with gr.Group(): sigil_output = gr.Markdown( label="Cryptographic Sigil", value="**Sigil will appear here...**" ) status_output = gr.Markdown( label="Ritual Status", value="**Status:** Awaiting ritual commencement..." ) # Event handlers attach_btn.click( fn=attach_pi_identity, inputs=[forge_token], outputs=[login_status_md, pi_uid_input, ritual_uid] ) refresh_btn.click( fn=create_judgment_dashboard, inputs=[pi_uid_input], outputs=[current_traits_output, cooldowns_output, history_output] ) pi_uid_input.submit( fn=create_judgment_dashboard, inputs=[pi_uid_input], outputs=[current_traits_output, cooldowns_output, history_output] ) ritual_btn.click( fn=submit_redemption_request, inputs=[ritual_uid, redemption_vow, file_upload], outputs=[ritual_output, sigil_output, status_output] ) gr.Markdown(""" --- **โš–๏ธ Dual Judgment System** โ€ข Database + Blockchain Ledger โ€ข Pi Network Integrated *"The gavel falls, the ledger writes, the chain remembers."* **๐Ÿ” Pi Login Flow:** 1. Visit https://quantumpiforge.com in Pi Browser 2. Click "Connect Pi & Get Session Token" 3. Copy the token and paste above 4. Click "Attach Pi Identity" to auto-fill your UID """) if __name__ == "__main__": demo.launch( share=False, show_error=True, debug=True )