Spaces:
Sleeping
Sleeping
| 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""" | |
| <div class="ritual-success"> | |
| <div class="candle-flicker">๐ฏ๏ธโจ</div> | |
| <h3>๐ฃ Redemption Ritual Complete</h3> | |
| <p><strong>Status:</strong> {result.get('status', 'pending_verification')}</p> | |
| <p><strong>Message:</strong> {result.get('message', 'Ritual received by the forge')}</p> | |
| <p><strong>Sigil Hash:</strong> <code>{sigil_hash[:24]}...</code></p> | |
| <div class="violet-pulse">The Veil acknowledges your restoration</div> | |
| </div> | |
| """ | |
| 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""" | |
| <div class="ritual-error"> | |
| <h3>โ Ritual Rejected</h3> | |
| <p>{error_msg}</p> | |
| <p>The Veil does not accept this offering.</p> | |
| </div> | |
| """ | |
| return error_html, f"**Hash:** `{sigil_hash}`", "โ Ritual rejected" | |
| else: | |
| error_html = f""" | |
| <div class="ritual-error"> | |
| <h3>โ Forge Connection Failed</h3> | |
| <p>API Error {response.status_code}</p> | |
| </div> | |
| """ | |
| return error_html, f"**Hash:** `{sigil_hash}`", f"โ API Error {response.status_code}" | |
| except requests.exceptions.ConnectionError: | |
| error_html = """ | |
| <div class="ritual-error"> | |
| <h3>โ Cannot Reach the Forge</h3> | |
| <p>The Quantum PiForge is currently unreachable.</p> | |
| <p>Please ensure https://quantumpiforge.com is accessible.</p> | |
| </div> | |
| """ | |
| return error_html, "**Error:** Connection failed", "โ Network error" | |
| except Exception as e: | |
| error_html = f""" | |
| <div class="ritual-error"> | |
| <h3>โ Ritual Interrupted</h3> | |
| <p>Unexpected error: {str(e)}</p> | |
| </div> | |
| """ | |
| 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="<div class='ritual-container'><div class='candle-flicker'>๐ฏ๏ธ</div><h3>The Veil Awaits Your Vow</h3><p>Speak your redemption vow above to begin the ritual.</p></div>" | |
| ) | |
| 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 | |
| ) |