Spaces:
Running on Zero
Running on Zero
| import os | |
| import json | |
| import html | |
| import requests | |
| import gradio as gr | |
| import spaces | |
| import torch | |
| AWS_API_URL = os.getenv("API_URL", "https://betaprecision.com/api/v1/chain-of-custody/analyze") | |
| # --- ZeroGPU Accelerated Analysis Function --- | |
| def analyze_on_zerogpu(contract_text, doc_name="Master_Agreement_ZeroGPU.txt"): | |
| if not contract_text or len(contract_text.strip()) < 10: | |
| return "<div style='color:#ef4444;padding:12px;'>⚠️ Error: Please enter at least 10 characters of contract text.</div>", "{}" | |
| if torch.cuda.is_available(): | |
| _ = torch.eye(9, device="cuda") | |
| payload = { | |
| "docName": doc_name, | |
| "text": contract_text | |
| } | |
| try: | |
| res = requests.post(AWS_API_URL, json=payload, headers={"Content-Type": "application/json"}, timeout=30) | |
| res.raise_for_status() | |
| data = res.json() | |
| except Exception as e: | |
| return f"<div style='color:#ef4444;padding:12px;'>❌ API Error: {str(e)}</div>", json.dumps({"error": str(e)}) | |
| # Generate isolated 3D Three.js + CoC Docket Application | |
| iframe_html = build_isolated_viewer(data) | |
| return iframe_html, json.dumps(data, indent=2) | |
| def build_isolated_viewer(data): | |
| # Safe JSON encoding for embedding in HTML | |
| json_payload = json.dumps(data) | |
| # Standalone HTML application that runs inside the iframe | |
| inner_html = f"""<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"/> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet" /> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
| <style> | |
| * {{ box-sizing: border-box; margin: 0; padding: 0; }} | |
| body {{ | |
| background: radial-gradient(circle at 50% 0%, #172436 0%, #0e141f 100%); | |
| color: #f8fafc; | |
| font-family: 'Inter', -apple-system, sans-serif; | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| }} | |
| .top-banner {{ | |
| padding: 8px 16px; | |
| background: #0f1622; | |
| border-bottom: 1px solid rgba(71, 85, 105, 0.5); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| font-family: 'IBM Plex Mono', monospace; | |
| font-size: 11px; | |
| }} | |
| .glow {{ color: #00e5ff; text-shadow: 0 0 8px #00e5ff; }} | |
| .fracture-count {{ color: #ef4444; font-weight: bold; }} | |
| .timeline-wrap {{ | |
| overflow-x: auto; | |
| background: #141d2a; | |
| border-bottom: 1px solid rgba(71, 85, 105, 0.5); | |
| padding: 6px 12px; | |
| }} | |
| .workspace {{ | |
| flex: 1; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| overflow: hidden; | |
| }} | |
| .docket-panel {{ | |
| background: #141e2c; | |
| border-right: 1px solid rgba(71, 85, 105, 0.5); | |
| display: flex; | |
| flex-direction: column; | |
| height: 100%; | |
| }} | |
| .docket-head {{ | |
| padding: 10px 14px; | |
| background: #111a26; | |
| border-bottom: 1px solid rgba(71, 85, 105, 0.45); | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: #fff; | |
| }} | |
| .docket-list {{ | |
| flex: 1; | |
| overflow-y: auto; | |
| }} | |
| .docket-item {{ | |
| display: flex; | |
| padding: 10px 12px; | |
| border-bottom: 1px solid rgba(71, 85, 105, 0.3); | |
| cursor: pointer; | |
| transition: background 0.15s; | |
| }} | |
| .docket-item:hover {{ background: #1a2738; }} | |
| .docket-item.selected {{ background: #1c2b3e; box-shadow: inset 3px 0 0 #00e5ff; }} | |
| .bar {{ width: 4px; margin-right: 10px; border-radius: 2px; flex-shrink: 0; }} | |
| .bar.risk-diverge {{ background: #ef4444; }} | |
| .bar.risk-match {{ background: #00e5ff; }} | |
| .bar.risk-review {{ background: #f59e0b; }} | |
| .meta-row {{ | |
| display: flex; | |
| justify-content: space-between; | |
| font-family: 'IBM Plex Mono', monospace; | |
| font-size: 9px; | |
| margin-bottom: 3px; | |
| }} | |
| .tag.broken {{ color: #f87171; font-weight: bold; }} | |
| .tag.intact {{ color: #38bdf8; }} | |
| .heading {{ font-size: 12px; font-weight: 600; color: #fff; margin-bottom: 2px; }} | |
| .category {{ font-family: 'IBM Plex Mono', monospace; font-size: 9.5px; color: #94a3b8; }} | |
| .legal-disclaimer {{ | |
| padding: 8px 12px; | |
| background: #101824; | |
| border-top: 1px solid rgba(71, 85, 105, 0.45); | |
| font-size: 8.5px; | |
| color: #7c8ba1; | |
| line-height: 1.4; | |
| }} | |
| .stage-panel {{ | |
| display: flex; | |
| flex-direction: column; | |
| background: radial-gradient(ellipse at 50% 35%, #20344d 0%, #152336 60%, #0f1a29 100%); | |
| position: relative; | |
| }} | |
| .stage-head {{ | |
| padding: 10px 14px; | |
| background: rgba(17, 26, 38, 0.9); | |
| border-bottom: 1px solid rgba(0, 229, 255, 0.2); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| }} | |
| .stage-title {{ font-size: 13.5px; font-weight: 600; color: #fff; }} | |
| .stage-sub {{ font-family: 'IBM Plex Mono', monospace; font-size: 9px; color: #38bdf8; }} | |
| .mode-btn {{ | |
| font-family: 'IBM Plex Mono', monospace; | |
| font-size: 9.5px; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| border: 1px solid rgba(71, 85, 105, 0.7); | |
| background: #1c2b3e; | |
| color: #94a3b8; | |
| cursor: pointer; | |
| }} | |
| .mode-btn.active {{ | |
| background: #00e5ff; | |
| color: #0c1118; | |
| font-weight: bold; | |
| border-color: #00e5ff; | |
| }} | |
| #canvas-mount {{ | |
| flex: 1; | |
| position: relative; | |
| }} | |
| .stage-foot {{ | |
| padding: 10px 14px; | |
| background: rgba(17, 26, 38, 0.92); | |
| border-top: 1px solid rgba(0, 229, 255, 0.2); | |
| display: flex; | |
| justify-content: space-between; | |
| font-family: 'IBM Plex Mono', monospace; | |
| font-size: 11px; | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="top-banner"> | |
| <div><span class="glow">⟠</span> SPATIOTEMPORAL CHAIN-OF-CUSTODY (CoC)</div> | |
| <div>Nodes: <b style="color:#00e5ff;" id="stat-nodes">0</b> | Fractures: <span class="fracture-count" id="stat-breaks">0</span></div> | |
| </div> | |
| <div class="timeline-wrap" id="timeline-mount"></div> | |
| <div class="workspace"> | |
| <div class="docket-panel"> | |
| <div class="docket-head" id="doc-name-head">Document Docket</div> | |
| <div class="docket-list" id="docket-mount"></div> | |
| <div class="legal-disclaimer"> | |
| <b>LEGAL NOTICE:</b> Algorithmic spatiotemporal provenance & geometric tensor analysis under Beta Precision HSO protocols. | |
| </div> | |
| </div> | |
| <div class="stage-panel"> | |
| <div class="stage-head"> | |
| <div> | |
| <div class="stage-title" id="active-title">Hilbert-Schmidt Operator</div> | |
| <div class="stage-sub" id="active-sub">DIM 9 HSO TENSOR</div> | |
| </div> | |
| <div style="display:flex;gap:4px;"> | |
| <button class="mode-btn active" data-mode="state" onclick="setMode('state')">Density (ρ)</button> | |
| <button class="mode-btn" data-mode="transition" onclick="setMode('transition')">Transition (Δρ)</button> | |
| <button class="mode-btn" data-mode="tension" onclick="setMode('tension')">Contradiction (K)</button> | |
| </div> | |
| </div> | |
| <div id="canvas-mount"></div> | |
| <div class="stage-foot"> | |
| <div>Fidelity: <b style="color:#00e5ff;" id="hud-f">—</b> | CoC Γ: <b style="color:#00e5ff;" id="hud-g">—</b> | Tension: <b style="color:#ef4444;" id="hud-t">—</b></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const data = {json_payload}; | |
| const clauses = data.clauses || []; | |
| const edges = data.cocEdges || []; | |
| let selectedId = clauses.length ? clauses[0].id : null; | |
| let viewMode = 'state'; | |
| document.getElementById('doc-name-head').textContent = '📄 ' + (data.docName || 'Document'); | |
| document.getElementById('stat-nodes').textContent = clauses.length; | |
| document.getElementById('stat-breaks').textContent = edges.filter(e => e.status === 'broken').length; | |
| // Render SVG Timeline | |
| function renderTimeline() {{ | |
| const mount = document.getElementById('timeline-mount'); | |
| if (!clauses.length) return; | |
| const boxW = 140, gap = 45, y = 28; | |
| const totalW = Math.max(700, clauses.length * (boxW + gap)); | |
| let svg = `<svg width="${{totalW}}" height="56" viewBox="0 0 ${{totalW}} 56">`; | |
| edges.forEach((e, i) => {{ | |
| const x1 = 15 + i * (boxW + gap) + boxW; | |
| const x2 = 15 + (i + 1) * (boxW + gap); | |
| const isBroken = e.status === 'broken'; | |
| const col = isBroken ? '#ef4444' : '#00e5ff'; | |
| svg += `<line x1="${{x1}}" y1="${{y}}" x2="${{x2}}" y2="${{y}}" stroke="${{col}}" stroke-width="1.5" stroke-dasharray="${{isBroken ? '3 3' : 'none'}}" />`; | |
| svg += `<text x="${{(x1+x2)/2}}" y="${{y-5}}" fill="${{col}}" font-size="8" font-family="monospace" text-anchor="middle">${{isBroken ? '⚠ BREAK' : 'Γ='+e.gamma.toFixed(2)}}</text>`; | |
| }}); | |
| clauses.forEach((c, idx) => {{ | |
| const x = 15 + idx * (boxW + gap); | |
| const isSel = c.id === selectedId; | |
| svg += `<g style="cursor:pointer" onclick="selectClause('${{c.id}}')"> | |
| <rect x="${{x}}" y="8" width="${{boxW}}" height="40" rx="4" fill="${{isSel ? '#1e2c3f' : '#141e2c'}}" stroke="${{isSel ? '#00e5ff' : 'rgba(71,85,105,0.6)'}}" stroke-width="${{isSel ? 2 : 1}}" /> | |
| <text x="${{x+8}}" y="22" fill="#fff" font-size="8.5" font-family="monospace" font-weight="bold">CLAUSE ${{c.index}}</text> | |
| <text x="${{x+8}}" y="38" fill="#94a3b8" font-size="8" font-family="monospace">${{(c.topCategory || 'Unanchored').slice(0,18)}}</text> | |
| </g>`; | |
| }}); | |
| svg += '</svg>'; | |
| mount.innerHTML = svg; | |
| }} | |
| // Render Docket List | |
| function renderDocket() {{ | |
| const mount = document.getElementById('docket-mount'); | |
| mount.innerHTML = ''; | |
| clauses.forEach(c => {{ | |
| const isSel = c.id === selectedId; | |
| const item = document.createElement('div'); | |
| item.className = 'docket-item' + (isSel ? ' selected' : ''); | |
| item.innerHTML = ` | |
| <div class="bar ${{c.risk?.key || 'risk-match'}}"></div> | |
| <div style="flex:1;"> | |
| <div class="meta-row"> | |
| <span>CLAUSE ${{c.index}}</span> | |
| ${{c.incomingEdge?.status === 'broken' ? '<span class="tag broken">⚠ Fracture</span>' : '<span class="tag intact">Intact</span>'}} | |
| </div> | |
| <div class="heading">${{c.heading.slice(0,34)}}</div> | |
| <div class="category">${{c.topCategory || 'Unanchored'}} · ${{Math.round((c.topScore || 0)*100)}}%</div> | |
| </div> | |
| `; | |
| item.onclick = () => selectClause(c.id); | |
| mount.appendChild(item); | |
| }}); | |
| }} | |
| // 3D Three.js Stage | |
| let scene, camera, renderer, hsoGroup; | |
| function initThree() {{ | |
| const container = document.getElementById('canvas-mount'); | |
| renderer = new THREE.WebGLRenderer({{ antialias: true, alpha: true }}); | |
| renderer.setSize(container.clientWidth || 360, container.clientHeight || 300); | |
| container.appendChild(renderer.domElement); | |
| scene = new THREE.Scene(); | |
| camera = new THREE.PerspectiveCamera(40, (container.clientWidth||360)/(container.clientHeight||300), 0.1, 100); | |
| camera.position.set(7, 8, 10); | |
| camera.lookAt(0, 0.3, 0); | |
| scene.add(new THREE.AmbientLight(0xfffaec, 1.4)); | |
| const dir = new THREE.DirectionalLight(0xfff0d0, 1.1); | |
| dir.position.set(6, 12, 8); | |
| scene.add(dir); | |
| hsoGroup = new THREE.Group(); | |
| scene.add(hsoGroup); | |
| function loop() {{ | |
| requestAnimationFrame(loop); | |
| hsoGroup.rotation.y += 0.003; | |
| renderer.render(scene, camera); | |
| }} | |
| loop(); | |
| }} | |
| function updateMatrix3D(matrix) {{ | |
| while(hsoGroup.children.length) hsoGroup.remove(hsoGroup.children[0]); | |
| if (!matrix) return; | |
| const n = matrix.length, spacing = 0.48, offset = (n - 1) / 2; | |
| for (let i = 0; i < n; i++) {{ | |
| for (let j = 0; j < n; j++) {{ | |
| const v = matrix[i][j], h = Math.max(Math.abs(v) * 2.8, 0.05); | |
| const color = v < -0.01 ? 0xef4444 : 0x00e5ff; | |
| const mesh = new THREE.Mesh( | |
| new THREE.BoxGeometry(0.38, 1, 0.38), | |
| new THREE.MeshStandardMaterial({{ color, roughness: 0.3, emissive: color, emissiveIntensity: 0.25 }}) | |
| ); | |
| mesh.scale.y = h; | |
| mesh.position.set((i - offset) * spacing, h / 2, (j - offset) * spacing); | |
| hsoGroup.add(mesh); | |
| }} | |
| }} | |
| }} | |
| function selectClause(id) {{ | |
| selectedId = id; | |
| const c = clauses.find(x => x.id === id); | |
| if (!c) return; | |
| document.getElementById('active-title').textContent = 'Clause ' + c.index + ': ' + c.heading.slice(0, 26); | |
| document.getElementById('active-sub').textContent = 'ANCHOR: ' + (c.topCategory || 'UNANCHORED').toUpperCase(); | |
| document.getElementById('hud-f').textContent = c.incomingEdge ? (c.incomingEdge.fidelity * 100).toFixed(1) + '%' : '100%'; | |
| document.getElementById('hud-g').textContent = c.incomingEdge ? c.incomingEdge.gamma.toFixed(3) : '1.000'; | |
| document.getElementById('hud-t').textContent = c.incomingEdge ? 'T=' + c.incomingEdge.tension.toFixed(3) : 'T=0.000'; | |
| let mat = c.matrix; | |
| if (viewMode === 'transition' && c.transitionMatrix) mat = c.transitionMatrix; | |
| if (viewMode === 'tension' && c.tensionMatrix) mat = c.tensionMatrix; | |
| updateMatrix3D(mat); | |
| renderDocket(); | |
| renderTimeline(); | |
| }} | |
| function setMode(mode) {{ | |
| viewMode = mode; | |
| document.querySelectorAll('.mode-btn').forEach(b => b.classList.toggle('active', b.dataset.mode === mode)); | |
| selectClause(selectedId); | |
| }} | |
| window.addEventListener('load', () => {{ | |
| initThree(); | |
| renderTimeline(); | |
| renderDocket(); | |
| if (clauses.length) selectClause(clauses[0].id); | |
| }}); | |
| </script> | |
| </body> | |
| </html>""" | |
| # Escape HTML to safely embed in iframe srcdoc | |
| escaped_srcdoc = html.escape(inner_html, quote=True) | |
| return f'<iframe style="width:100%;height:680px;border:1px solid rgba(0,229,255,0.25);border-radius:8px;background:#0e141f;" srcdoc="{escaped_srcdoc}"></iframe>' | |
| DEFAULT_CONTRACT = """ARTICLE 1. APPOINTMENT & SCOPE | |
| Provider shall perform professional technology consulting services as set forth in statements of work executed by both parties. | |
| ARTICLE 2. UNLIMITED SPECIAL LIABILITY | |
| Notwithstanding any other provision herein, Provider's liability under this Agreement is completely uncapped, and Provider expressly waives all exclusions for incidental, indirect, punitive, or consequential commercial damages. | |
| ARTICLE 3. SUDDEN UNILATERAL INDEMNIFICATION | |
| Provider shall defend, indemnify, and hold harmless Customer against any and all losses arising from market fluctuations or Customer's contributory negligence.""" | |
| # --- Gradio Interface --- | |
| with gr.Blocks(title="Beta Precision CoC (ZeroGPU)") as demo: | |
| gr.Markdown("# ⟠ Beta Precision: Spatiotemporal Chain-of-Custody (ZeroGPU)") | |
| gr.Markdown("GPU-accelerated geometric tensor evaluation against high-dimensional Hilbert space anchors.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| doc_name_in = gr.Textbox(label="Document Name", value="Master_Agreement_ZeroGPU.txt") | |
| text_in = gr.Textbox(label="Contract Provision Text", value=DEFAULT_CONTRACT, lines=9) | |
| run_btn = gr.Button("⚡ Run ZeroGPU CoC Analysis", variant="primary") | |
| viewer_output = gr.HTML() | |
| with gr.Accordion("📦 Raw JSON Operator Tensors", open=False): | |
| json_output = gr.Code(language="json") | |
| run_btn.click( | |
| fn=analyze_on_zerogpu, | |
| inputs=[text_in, doc_name_in], | |
| outputs=[viewer_output, json_output] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |