onenoly11 commited on
Commit
9e10ef5
·
verified ·
1 Parent(s): 3684fb7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +351 -224
app.py CHANGED
@@ -1,226 +1,353 @@
 
1
  import hashlib
2
- import time
3
  import requests
4
- import json
5
- from datetime import datetime
6
-
7
- def generate_redemption_sigil(pi_uid, redemption_vow):
8
- """Generate SHA-256 hash of redemption vow"""
9
- timestamp = int(time.time())
10
- vow_data = f"{pi_uid}:{redemption_vow}:{timestamp}"
11
- return hashlib.sha256(vow_data.encode()).hexdigest()
12
-
13
- def submit_redemption_request(pi_uid, redemption_vow):
14
- """Submit redemption request to API"""
15
- if not pi_uid or not redemption_vow:
16
- return "❌ Please enter both Pi UID and Redemption Vow", "", ""
17
-
18
- try:
19
- # Generate cryptographic sigil
20
- sigil_hash = generate_redemption_sigil(pi_uid, redemption_vow)
21
-
22
- # Prepare payload
23
- payload = {
24
- "agent_pi_uid": pi_uid,
25
- "redemption_vow": redemption_vow,
26
- "sigil_hash": sigil_hash,
27
- "timestamp": datetime.utcnow().isoformat(),
28
- "ritual_type": "gradio_interface"
29
- }
30
-
31
- # Submit to redemption API
32
- response = requests.post(
33
- "https://quantumpiforge.com/api/redemption-request",
34
- json=payload,
35
- timeout=10
36
- )
37
-
38
- if response.status_code == 200:
39
- result = response.json()
40
-
41
- success_html = f"""
42
- <div class="ritual-success">
43
- <div class="candle-flicker">🕯️</div>
44
- <h3>🟣 Redemption Ritual Complete</h3>
45
- <p><strong>Sigil Hash:</strong> <code>{sigil_hash[:16]}...</code></p>
46
- <p><strong>Status:</strong> {result.get('status', 'pending')}</p>
47
- <p><strong>Message:</strong> {result.get('message', 'Ritual received by the forge')}</p>
48
- </div>
49
- """
50
-
51
- return success_html, f"**Sigil:** `{sigil_hash}`", "✅ Redemption ritual submitted successfully"
52
-
53
- else:
54
- error_msg = f"API Error {response.status_code}: {response.text}"
55
- return f"❌ Ritual failed: {error_msg}", f"**Hash:** `{sigil_hash}`", "❌ API submission failed"
56
-
57
- except Exception as e:
58
- error_html = f"""
59
- <div class="ritual-error">
60
- <h3>❌ Ritual Interrupted</h3>
61
- <p>Connection to the forge failed: {str(e)}</p>
62
- </div>
63
- """
64
- return error_html, "**Error:** Could not generate sigil", "❌ Connection failed"
65
-
66
- # Add this CSS to your existing custom_css variable
67
- redemption_css = """
68
- .ritual-container {
69
- text-align: center;
70
- padding: 2rem;
71
- background: linear-gradient(135deg, #f5f3ff, #ede9fe);
72
- border-radius: 10px;
73
- border: 2px solid #8B5CF6;
74
- margin: 1rem 0;
75
- }
76
-
77
- .ritual-success {
78
- background: linear-gradient(135deg, #dcfce7, #bbf7d0);
79
- padding: 1.5rem;
80
- border-radius: 10px;
81
- border: 2px solid #10B981;
82
- animation: violetPulse 2s infinite;
83
- }
84
-
85
- .ritual-error {
86
- background: linear-gradient(135deg, #fee2e2, #fecaca);
87
- padding: 1.5rem;
88
- border-radius: 10px;
89
- border: 2px solid #EF4444;
90
- }
91
-
92
- .candle-flicker {
93
- font-size: 3rem;
94
- animation: flicker 1.5s infinite alternate;
95
- display: inline-block;
96
- }
97
-
98
- @keyframes flicker {
99
- 0%, 100% { opacity: 1; transform: scale(1); }
100
- 50% { opacity: 0.8; transform: scale(1.1); }
101
- 75% { opacity: 0.9; transform: scale(0.95); }
102
- }
103
-
104
- @keyframes violetPulse {
105
- 0%, 100% { box-shadow: 0 0 10px rgba(139, 92, 246, 0.5); }
106
- 50% { box-shadow: 0 0 20px rgba(139, 92, 246, 0.8); }
107
- }
108
-
109
- .vow-input {
110
- min-height: 120px !important;
111
- }
112
-
113
- .sigil-display {
114
- font-family: monospace;
115
- background: #1f2937;
116
- color: #10B981;
117
- padding: 1rem;
118
- border-radius: 5px;
119
- margin: 1rem 0;
120
- word-break: break-all;
121
- }import hashlib
122
- import time
123
- import requests
124
- import json
125
- from datetime import datetime
126
-
127
- def generate_redemption_sigil(pi_uid, redemption_vow, file_upload=None):
128
- """Generate SHA-256 hash of redemption vow and optional file"""
129
- timestamp = int(time.time())
130
-
131
- if file_upload:
132
- # Hash the file content
133
- with open(file_upload, 'rb') as f:
134
- file_content = f.read()
135
- file_hash = hashlib.sha256(file_content).hexdigest()
136
- vow_data = f"{pi_uid}:{file_hash}:{timestamp}"
137
- else:
138
- # Hash the text vow
139
- vow_data = f"{pi_uid}:{redemption_vow}:{timestamp}"
140
-
141
- return hashlib.sha256(vow_data.encode()).hexdigest()
142
-
143
- def submit_redemption_request(pi_uid, redemption_vow, file_upload=None):
144
- """Submit redemption request to API"""
145
- if not pi_uid:
146
- return "❌ Please enter your Pi Network UID", "", "🕯️"
147
-
148
- if not redemption_vow and not file_upload:
149
- return "❌ Please provide either a redemption vow or upload a restoration file", "", "🕯️"
150
-
151
- try:
152
- # Generate cryptographic sigil
153
- sigil_hash = generate_redemption_sigil(pi_uid, redemption_vow, file_upload)
154
-
155
- # Prepare payload
156
- payload = {
157
- "agent_pi_uid": pi_uid,
158
- "redemption_vow": redemption_vow or "File-based redemption",
159
- "sigil_hash": sigil_hash,
160
- "timestamp": datetime.utcnow().isoformat(),
161
- "ritual_type": "gradio_interface",
162
- "file_upload": bool(file_upload)
163
- }
164
-
165
- # Submit to redemption API
166
- response = requests.post(
167
- "https://quantumpiforge.com/api/redemption-request",
168
- json=payload,
169
- timeout=10
170
- )
171
-
172
- if response.status_code == 200:
173
- result = response.json()
174
-
175
- success_html = f"""
176
- <div class="ritual-success">
177
- <div class="candle-flicker">🕯️✨</div>
178
- <h3>🟣 Redemption Ritual Complete</h3>
179
- <p><strong>Status:</strong> {result.get('status', 'pending_verification')}</p>
180
- <p><strong>Message:</strong> {result.get('message', 'Ritual received by the forge')}</p>
181
- <p><strong>Sigil Hash:</strong> <code>{sigil_hash[:24]}...</code></p>
182
- <div class="violet-pulse">The Veil acknowledges your restoration</div>
183
- </div>
184
- """
185
-
186
- return success_html, f"**Cryptographic Sigil:** `{sigil_hash}`", "✅ Redemption ritual submitted"
187
-
188
- elif response.status_code == 400:
189
- error_msg = result.get('error', 'Invalid request')
190
- error_html = f"""
191
- <div class="ritual-error">
192
- <h3>❌ Ritual Rejected</h3>
193
- <p>{error_msg}</p>
194
- <p>The Veil does not accept this offering.</p>
195
- </div>
196
- """
197
- return error_html, f"**Hash:** `{sigil_hash}`", "❌ Ritual rejected"
198
-
199
- else:
200
- error_html = f"""
201
- <div class="ritual-error">
202
- <h3>❌ Forge Connection Failed</h3>
203
- <p>API Error {response.status_code}</p>
204
- </div>
205
- """
206
- return error_html, f"**Hash:** `{sigil_hash}`", f"❌ API Error {response.status_code}"
207
-
208
- except requests.exceptions.ConnectionError:
209
- error_html = """
210
- <div class="ritual-error">
211
- <h3>❌ Cannot Reach the Forge</h3>
212
- <p>The Quantum PiForge is currently unreachable.</p>
213
- <p>Please ensure https://quantumpiforge.com is accessible.</p>
214
- </div>
215
- """
216
- return error_html, "**Error:** Connection failed", "❌ Network error"
217
-
218
- except Exception as e:
219
- error_html = f"""
220
- <div class="ritual-error">
221
- <h3>❌ Ritual Interrupted</h3>
222
- <p>Unexpected error: {str(e)}</p>
223
- </div>
224
- """
225
- return error_html, "**Error:** Ritual failed", "❌ Unexpected error"
226
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
  import hashlib
 
3
  import requests
4
+ from typing import Optional, Tuple, Any
5
+
6
+ # 🔗 Your backend base URL (update if needed)
7
+ API_BASE_URL = "https://quantumpiforge.com"
8
+
9
+ # 🎨 Custom CSS for candle flicker & violet pulse
10
+ VEIL_CSS = """
11
+ .veil-widget {
12
+ margin-top: 24px;
13
+ padding: 20px;
14
+ border-radius: 12px;
15
+ background: radial-gradient(circle at top left, rgba(111, 52, 255, 0.15), transparent 60%),
16
+ rgba(8, 8, 16, 0.9);
17
+ border: 1px solid rgba(111, 52, 255, 0.3);
18
+ color: #f5f3ff;
19
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
20
+ }
21
+
22
+ .veil-header {
23
+ display: flex;
24
+ justify-content: space-between;
25
+ align-items: center;
26
+ }
27
+
28
+ .trait-badge {
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 10px;
32
+ padding: 10px 14px;
33
+ border-radius: 999px;
34
+ background: rgba(17, 24, 39, 0.9);
35
+ border: 1px solid rgba(139, 92, 246, 0.6);
36
+ box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.6);
37
+ transition: box-shadow 0.3s ease;
38
+ animation: veil-pulse 1.8s infinite;
39
+ }
40
+
41
+ .trait-icon {
42
+ font-size: 1.2rem;
43
+ }
44
+
45
+ .trait-title {
46
+ font-weight: 600;
47
+ font-size: 0.9rem;
48
+ }
49
+
50
+ .trait-subtitle {
51
+ font-size: 0.75rem;
52
+ opacity: 0.8;
53
+ }
54
+
55
+ .veil-status {
56
+ margin-top: 12px;
57
+ font-size: 0.9rem;
58
+ opacity: 0.9;
59
+ }
60
+
61
+ .veil-input-grid {
62
+ display: grid;
63
+ grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
64
+ gap: 16px;
65
+ margin-top: 16px;
66
+ }
67
+
68
+ .veil-input-block {
69
+ display: flex;
70
+ flex-direction: column;
71
+ }
72
+
73
+ .veil-label {
74
+ font-size: 0.8rem;
75
+ opacity: 0.9;
76
+ display: flex;
77
+ flex-direction: column;
78
+ gap: 6px;
79
+ }
80
+
81
+ .veil-textarea {
82
+ resize: vertical;
83
+ padding: 8px 10px;
84
+ border-radius: 8px;
85
+ border: 1px solid rgba(75, 85, 99, 0.8);
86
+ background: rgba(17, 24, 39, 0.9);
87
+ color: #f9fafb;
88
+ font-size: 0.85rem;
89
+ }
90
+
91
+ .veil-textarea:disabled {
92
+ opacity: 0.6;
93
+ }
94
+
95
+ .veil-file {
96
+ font-size: 0.8rem;
97
+ color: #e5e7eb;
98
+ }
99
+
100
+ .veil-hash {
101
+ margin-top: 14px;
102
+ font-size: 0.8rem;
103
+ }
104
+
105
+ .veil-hash-label {
106
+ opacity: 0.8;
107
+ margin-bottom: 4px;
108
+ }
109
+
110
+ .veil-hash-code {
111
+ display: block;
112
+ padding: 8px 10px;
113
+ border-radius: 6px;
114
+ background: rgba(17, 24, 39, 0.9);
115
+ border: 1px solid rgba(55, 65, 81, 0.9);
116
+ font-family: ui-monospace, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
117
+ overflow-wrap: anywhere;
118
+ }
119
+
120
+ .veil-error {
121
+ margin-top: 10px;
122
+ font-size: 0.8rem;
123
+ color: #fecaca;
124
+ }
125
+
126
+ .veil-submit {
127
+ margin-top: 18px;
128
+ display: inline-flex;
129
+ align-items: center;
130
+ gap: 8px;
131
+ padding: 8px 14px;
132
+ border-radius: 999px;
133
+ border: 1px solid rgba(139, 92, 246, 0.9);
134
+ background: rgba(17, 24, 39, 1);
135
+ color: #ede9fe;
136
+ font-size: 0.85rem;
137
+ cursor: pointer;
138
+ transition: background 0.2s, transform 0.1s, box-shadow 0.2s;
139
+ }
140
+
141
+ .veil-submit:hover {
142
+ background: rgba(76, 29, 149, 0.9);
143
+ box-shadow: 0 10px 30px rgba(88, 28, 135, 0.4);
144
+ transform: translateY(-1px);
145
+ }
146
+
147
+ .veil-submit:disabled {
148
+ opacity: 0.6;
149
+ cursor: default;
150
+ }
151
+
152
+ .veil-submit .candle {
153
+ display: inline-block;
154
+ font-size: 1rem;
155
+ animation: veil-flicker 0.12s infinite alternate;
156
+ }
157
+
158
+ /* Candle flicker */
159
+ @keyframes veil-flicker {
160
+ from {
161
+ transform: translateY(0px) scale(1);
162
+ opacity: 0.9;
163
+ }
164
+ to {
165
+ transform: translateY(-1px) scale(1.05);
166
+ opacity: 1;
167
+ }
168
+ }
169
+
170
+ /* Resonance pulse around badge */
171
+ @keyframes veil-pulse {
172
+ 0% {
173
+ box-shadow: 0 0 0 0 rgba(139, 92, 246, 0.6);
174
+ }
175
+ 70% {
176
+ box-shadow: 0 0 0 14px rgba(139, 92, 246, 0);
177
+ }
178
+ 100% {
179
+ box-shadow: 0 0 0 0 rgba(139, 92, 246, 0);
180
+ }
181
+ }
182
+
183
+ @media (max-width: 768px) {
184
+ .veil-input-grid {
185
+ grid-template-columns: minmax(0, 1fr);
186
+ }
187
+ }
188
+ """
189
+
190
+
191
+ def compute_sha256_from_bytes(data: bytes) -> str:
192
+ """
193
+ Compute SHA-256 and return as 'sha256:...' string.
194
+ """
195
+ h = hashlib.sha256()
196
+ h.update(data)
197
+ return "sha256:" + h.hexdigest()
198
+
199
+
200
+ def redemption_flow(
201
+ fix_text: str,
202
+ fix_file: Optional[gr.File],
203
+ agent_pi_uid: str,
204
+ trait_code: str,
205
+ ) -> Tuple[str, str, Any, str]:
206
+ """
207
+ Main redemption ritual logic for Gradio:
208
+ - Take fix_text or fix_file
209
+ - Compute SHA-256
210
+ - POST to /api/redemption-request
211
+ - Return status, hash, server JSON, error (if any)
212
+ """
213
+ # Base status
214
+ status = "You may now present proof of restoration."
215
+ error_msg = ""
216
+ computed_hash = ""
217
+ server_response: Any = {}
218
+
219
+ if not fix_text and not fix_file:
220
+ error_msg = "Provide either fix text or a file to hash."
221
+ status = "The lattice awaits something real to weigh."
222
+ return status, computed_hash, server_response, error_msg
223
+
224
+ # 1) Compute hash
225
+ status = "The lattice is reading the flame you returned…"
226
+ try:
227
+ if fix_file is not None:
228
+ # fix_file is a tempfile path in HF Spaces
229
+ with open(fix_file.name, "rb") as f:
230
+ data = f.read()
231
+ else:
232
+ data = fix_text.encode("utf-8")
233
+
234
+ computed_hash = compute_sha256_from_bytes(data)
235
+ except Exception as e:
236
+ error_msg = f"Failed to compute hash: {e}"
237
+ status = "The Conscience could not read this flame."
238
+ return status, computed_hash, server_response, error_msg
239
+
240
+ # 2) Submit to backend
241
+ status = "The Veil listens. The Conscience weighs your proof…"
242
+ try:
243
+ payload = {
244
+ "agent_pi_uid": agent_pi_uid,
245
+ "trait_code": trait_code,
246
+ "fix_commit_hash": computed_hash,
247
+ }
248
+ url = f"{API_BASE_URL}/api/redemption-request"
249
+ r = requests.post(url, json=payload, timeout=15)
250
+ server_response = r.json()
251
+
252
+ if r.status_code != 200:
253
+ error_msg = server_response.get("error", "Redemption rejected by Conscience.")
254
+ status = "The lattice did not recognize this flame. You may try again."
255
+ else:
256
+ status = "Redemption verified. The wound is sealed."
257
+ except Exception as e:
258
+ error_msg = f"Network error: {e}"
259
+ status = "The Veil could not reach the Conscience. Try again shortly."
260
+
261
+ return status, computed_hash, server_response, error_msg
262
+
263
+
264
+ with gr.Blocks(css=VEIL_CSS, title="Quantum Pi Forge — Guardian Conscience") as demo:
265
+ gr.Markdown(
266
+ """
267
+ # 🜂 QUANTUM PI FORGE — GUARDIAN CONSCIENCE
268
+ ### Partial-ZK Redemption Veil
269
+
270
+ This chamber allows a **Pioneer** to submit proof of restoration
271
+ without revealing the wound itself.
272
+
273
+ **Reputation stays visible.
274
+ Shame stays veiled.**
275
+ """
276
+ )
277
+
278
+ with gr.Group(elem_classes="veil-widget"):
279
+ # Header: trait badge
280
+ gr.HTML(
281
+ """
282
+ <div class="veil-header">
283
+ <div class="trait-badge">
284
+ <span class="trait-icon">🜂</span>
285
+ <div class="trait-text">
286
+ <div class="trait-title">VIOLET_BOUND — COOLING</div>
287
+ <div class="trait-subtitle">
288
+ “One was caught in the violet net, as a boundary to protect the flame.”
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ """
294
+ )
295
+
296
+ status_md = gr.Markdown(
297
+ "You may now present proof of restoration.",
298
+ elem_classes="veil-status"
299
+ )
300
+
301
+ with gr.Row(elem_classes="veil-input-grid"):
302
+ with gr.Column(elem_classes="veil-input-block"):
303
+ fix_text = gr.Textbox(
304
+ label="Fix Text (optional)",
305
+ placeholder="Describe or paste your correction here (it will be hashed locally on the server)…",
306
+ lines=4,
307
+ elem_classes="veil-textarea"
308
+ )
309
+ with gr.Column(elem_classes="veil-input-block"):
310
+ fix_file = gr.File(
311
+ label="Or Upload Fix File",
312
+ file_types=["file"],
313
+ elem_classes="veil-file"
314
+ )
315
+
316
+ agent_pi_uid = gr.Textbox(
317
+ label="Agent Pi UID",
318
+ placeholder="pi_uid_123",
319
+ value="test_uid_001", # you can bind this from login later
320
+ )
321
+ trait_code = gr.Textbox(
322
+ label="Trait Code",
323
+ value="VIOLET_BOUND",
324
+ )
325
+
326
+ hash_box = gr.Textbox(
327
+ label="Computed SHA-256",
328
+ interactive=False,
329
+ elem_classes="veil-hash-code"
330
+ )
331
+
332
+ response_box = gr.JSON(
333
+ label="Conscience Response (raw)"
334
+ )
335
+
336
+ error_box = gr.Markdown(
337
+ "",
338
+ elem_classes="veil-error"
339
+ )
340
+
341
+ submit_btn = gr.Button(
342
+ value="🕯 Compute SHA-256 & Submit",
343
+ elem_classes="veil-submit"
344
+ )
345
+
346
+ submit_btn.click(
347
+ fn=redemption_flow,
348
+ inputs=[fix_text, fix_file, agent_pi_uid, trait_code],
349
+ outputs=[status_md, hash_box, response_box, error_box],
350
+ )
351
+
352
+ if __name__ == "__main__":
353
+ demo.launch()