Spaces:
Sleeping
Sleeping
Upload app (2).py
Browse files- dvnc_ai_v2_hf/app (2).py +817 -0
dvnc_ai_v2_hf/app (2).py
ADDED
|
@@ -0,0 +1,817 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import math
|
| 3 |
+
import random
|
| 4 |
+
import html
|
| 5 |
+
import urllib.parse
|
| 6 |
+
import xml.etree.ElementTree as ET
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
import gradio as gr
|
| 10 |
+
import requests
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
MODELS = [
|
| 14 |
+
{"name": "DVNC Sovereign", "tag": "flagship", "desc": "Maximum depth orchestration for frontier discovery"},
|
| 15 |
+
{"name": "DVNC Atlas", "tag": "research", "desc": "Balanced reasoning, graph traversal, and synthesis"},
|
| 16 |
+
{"name": "DVNC Curie", "tag": "lab", "desc": "Experimental hypothesis generation for anomalous signals"},
|
| 17 |
+
]
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
AGENTS = [
|
| 21 |
+
"Query Interpreter",
|
| 22 |
+
"Graph Divergence Mapper",
|
| 23 |
+
"Evidence Harvester",
|
| 24 |
+
"Analogy Engine",
|
| 25 |
+
"Hypothesis Composer",
|
| 26 |
+
"Adversarial Critic",
|
| 27 |
+
"Experimental Program Designer",
|
| 28 |
+
]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
NODES = [
|
| 32 |
+
{"id": "seed", "label": "Seed Query", "group": "core", "x": 10, "y": 0, "z": 0},
|
| 33 |
+
{"id": "bio", "label": "Biomaterials", "group": "domain", "x": 24, "y": 12, "z": -8},
|
| 34 |
+
{"id": "card", "label": "Cardiac Repair", "group": "domain", "x": 38, "y": 3, "z": 14},
|
| 35 |
+
{"id": "nano", "label": "Nanostructure", "group": "bridge", "x": 24, "y": -18, "z": 16},
|
| 36 |
+
{"id": "selfasm", "label": "Self-Assembly", "group": "bridge", "x": 40, "y": -16, "z": -16},
|
| 37 |
+
{"id": "electro", "label": "Electro-signalling", "group": "mechanism", "x": 58, "y": 10, "z": -10},
|
| 38 |
+
{"id": "immune", "label": "Immune Modulation", "group": "mechanism", "x": 64, "y": -8, "z": 10},
|
| 39 |
+
{"id": "trial", "label": "Validation Path", "group": "outcome", "x": 80, "y": 0, "z": 0},
|
| 40 |
+
{"id": "alt1", "label": "Piezoelectric Scaffold", "group": "candidate", "x": 56, "y": 26, "z": 14},
|
| 41 |
+
{"id": "alt2", "label": "Peptide Mesh", "group": "candidate", "x": 54, "y": -27, "z": -14},
|
| 42 |
+
]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
EDGES = [
|
| 46 |
+
("seed", "bio"), ("seed", "nano"), ("bio", "card"), ("nano", "selfasm"),
|
| 47 |
+
("selfasm", "electro"), ("card", "immune"), ("electro", "trial"), ("immune", "trial"),
|
| 48 |
+
("card", "alt1"), ("selfasm", "alt2"), ("alt1", "trial"), ("alt2", "trial")
|
| 49 |
+
]
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
DEFAULT_PATH = ["seed", "nano", "selfasm", "electro", "trial"]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
CANDIDATES = [
|
| 56 |
+
{
|
| 57 |
+
"title": "Piezoelectric Scaffold Cascade",
|
| 58 |
+
"front": "Use mechano-electric scaffolds to convert cardiac strain into micro-current signalling.",
|
| 59 |
+
"back": "Discovery path: anomalous healing signal -> piezoelectric analog -> ion-channel entrainment -> tissue regeneration. Risk: power density and fibrosis coupling.",
|
| 60 |
+
"score": 92,
|
| 61 |
+
"novelty": "High",
|
| 62 |
+
"agent": "Hypothesis Composer"
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"title": "Peptide Self-Assembly Mesh",
|
| 66 |
+
"front": "Deploy dynamic peptide meshes that self-assemble around damaged myocardium and guide repair.",
|
| 67 |
+
"back": "Discovery path: self-assembly -> local immune choreography -> regenerative substrate formation. Risk: degradation timing and targeting specificity.",
|
| 68 |
+
"score": 88,
|
| 69 |
+
"novelty": "High",
|
| 70 |
+
"agent": "Analogy Engine"
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"title": "Immune-Tuned Conductive Hydrogel",
|
| 74 |
+
"front": "Blend conductivity with macrophage-state modulation to reduce scarring and restore conduction.",
|
| 75 |
+
"back": "Discovery path: inflammation mismatch -> conductive medium -> macrophage polarization -> synchronized healing. Risk: persistence and biocompatibility.",
|
| 76 |
+
"score": 85,
|
| 77 |
+
"novelty": "Medium-High",
|
| 78 |
+
"agent": "Adversarial Critic"
|
| 79 |
+
}
|
| 80 |
+
]
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
JOURNALS = [
|
| 84 |
+
{"name": "Nature", "url": "https://www.nature.com/search", "desc": "Flagship multidisciplinary research journal."},
|
| 85 |
+
{"name": "Science", "url": "https://www.science.org/search", "desc": "High-impact science journal and family."},
|
| 86 |
+
{"name": "Cell", "url": "https://www.cell.com/search", "desc": "Life sciences and translational biology."},
|
| 87 |
+
{"name": "The Lancet", "url": "https://www.thelancet.com/search", "desc": "Clinical and medical research."},
|
| 88 |
+
{"name": "IEEE Xplore", "url": "https://ieeexplore.ieee.org/search/searchresult.jsp", "desc": "Engineering, AI, signal processing, and systems."},
|
| 89 |
+
]
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def build_connectome_html(path_ids):
|
| 93 |
+
active = set(path_ids)
|
| 94 |
+
node_map = {n["id"]: n for n in NODES}
|
| 95 |
+
path_pairs = set()
|
| 96 |
+
for i in range(len(path_ids) - 1):
|
| 97 |
+
path_pairs.add((path_ids[i], path_ids[i + 1]))
|
| 98 |
+
path_pairs.add((path_ids[i + 1], path_ids[i]))
|
| 99 |
+
|
| 100 |
+
lines = []
|
| 101 |
+
active_lines = []
|
| 102 |
+
for a, b in EDGES:
|
| 103 |
+
na, nb = node_map[a], node_map[b]
|
| 104 |
+
x1, y1 = na["x"] * 8 + 80, na["y"] * 6 + 280
|
| 105 |
+
x2, y2 = nb["x"] * 8 + 80, nb["y"] * 6 + 280
|
| 106 |
+
lines.append(f'<line class="edge" x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" />')
|
| 107 |
+
if (a, b) in path_pairs:
|
| 108 |
+
active_lines.append(f'<line class="edge active" x1="{x1:.1f}" y1="{y1:.1f}" x2="{x2:.1f}" y2="{y2:.1f}" />')
|
| 109 |
+
|
| 110 |
+
circles = []
|
| 111 |
+
labels = []
|
| 112 |
+
for n in NODES:
|
| 113 |
+
cx = n["x"] * 8 + 80
|
| 114 |
+
cy = n["y"] * 6 + 280
|
| 115 |
+
is_active = n["id"] in active
|
| 116 |
+
cls = f'node {n["group"]} {'chosen' if is_active else 'idle'}'
|
| 117 |
+
halo_cls = f'halo {'active' if is_active else ''}'
|
| 118 |
+
circles.append(
|
| 119 |
+
f'<g class="node-wrap">'
|
| 120 |
+
f'<circle class="{halo_cls}" cx="{cx:.1f}" cy="{cy:.1f}" r="{30 if is_active else 0}" />'
|
| 121 |
+
f'<circle class="{cls}" cx="{cx:.1f}" cy="{cy:.1f}" r="{18 if is_active else 13}" />'
|
| 122 |
+
f'</g>'
|
| 123 |
+
)
|
| 124 |
+
labels.append(f'<text class="label {'active' if is_active else ''}" x="{cx + 18:.1f}" y="{cy - 16:.1f}">{n["label"]}</text>')
|
| 125 |
+
|
| 126 |
+
return f"""
|
| 127 |
+
<div class="panel brain-shell">
|
| 128 |
+
<div class="brain-header">
|
| 129 |
+
<div>
|
| 130 |
+
<p class="eyebrow">Connectome</p>
|
| 131 |
+
<h3>3D Connectome</h3>
|
| 132 |
+
</div>
|
| 133 |
+
<div class="brain-legend">
|
| 134 |
+
<span><i class="dot dot-live"></i> lit path</span>
|
| 135 |
+
<span><i class="dot dot-chosen"></i> chosen node</span>
|
| 136 |
+
<span><i class="dot dot-idle"></i> available node</span>
|
| 137 |
+
</div>
|
| 138 |
+
</div>
|
| 139 |
+
<div class="brain-stage">
|
| 140 |
+
<svg viewBox="0 0 780 560" class="brain-svg" role="img" aria-label="DVNC 3D connectome visualisation">
|
| 141 |
+
{''.join(lines)}
|
| 142 |
+
{''.join(active_lines)}
|
| 143 |
+
{''.join(circles)}
|
| 144 |
+
{''.join(labels)}
|
| 145 |
+
</svg>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
"""
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def build_learning_graph_html(nodes, edges, title="Self-Learning Knowledge Graph"):
|
| 152 |
+
if not nodes:
|
| 153 |
+
return """
|
| 154 |
+
<div class="panel brain-shell">
|
| 155 |
+
<div class="brain-header">
|
| 156 |
+
<div>
|
| 157 |
+
<p class="eyebrow">Learning Graph</p>
|
| 158 |
+
<h3>Self-Learning Knowledge Graph</h3>
|
| 159 |
+
</div>
|
| 160 |
+
</div>
|
| 161 |
+
<div class="brain-stage learning-empty">
|
| 162 |
+
<div class="empty-graph-copy">
|
| 163 |
+
<h4>No papers mapped yet</h4>
|
| 164 |
+
<p>Search papers, pick a topic, or upload a PDF to grow the graph in real time.</p>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
</div>
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
node_items = []
|
| 171 |
+
label_items = []
|
| 172 |
+
edge_items = []
|
| 173 |
+
coords = [(110, 110), (320, 80), (540, 130), (660, 270), (550, 410), (300, 450), (110, 340), (380, 260)]
|
| 174 |
+
|
| 175 |
+
for i, node in enumerate(nodes[:8]):
|
| 176 |
+
x, y = coords[i]
|
| 177 |
+
node["sx"] = x
|
| 178 |
+
node["sy"] = y
|
| 179 |
+
|
| 180 |
+
node_map = {n["id"]: n for n in nodes[:8]}
|
| 181 |
+
for a, b in edges:
|
| 182 |
+
if a in node_map and b in node_map:
|
| 183 |
+
na = node_map[a]
|
| 184 |
+
nb = node_map[b]
|
| 185 |
+
edge_items.append(
|
| 186 |
+
f'<line class="learn-edge" x1="{na["sx"]}" y1="{na["sy"]}" x2="{nb["sx"]}" y2="{nb["sy"]}" />'
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
for node in nodes[:8]:
|
| 190 |
+
klass = f'learn-node {node.get("kind", "paper")}'
|
| 191 |
+
node_items.append(
|
| 192 |
+
f'<circle class="{klass}" cx="{node["sx"]}" cy="{node["sy"]}" r="{24 if node.get("kind") == "query" else 20}" />'
|
| 193 |
+
)
|
| 194 |
+
label_items.append(
|
| 195 |
+
f'<text class="learn-label" x="{node["sx"] + 28}" y="{node["sy"] - 10}">{html.escape(node["label"][:44])}</text>'
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
return f"""
|
| 199 |
+
<div class="panel brain-shell">
|
| 200 |
+
<div class="brain-header">
|
| 201 |
+
<div>
|
| 202 |
+
<p class="eyebrow">Learning Graph</p>
|
| 203 |
+
<h3>{html.escape(title)}</h3>
|
| 204 |
+
</div>
|
| 205 |
+
<div class="brain-legend">
|
| 206 |
+
<span><i class="dot dot-query"></i> query</span>
|
| 207 |
+
<span><i class="dot dot-paper"></i> paper</span>
|
| 208 |
+
<span><i class="dot dot-upload"></i> uploaded PDF</span>
|
| 209 |
+
</div>
|
| 210 |
+
</div>
|
| 211 |
+
<div class="brain-stage">
|
| 212 |
+
<svg viewBox="0 0 760 520" class="brain-svg" role="img" aria-label="Self-learning knowledge graph">
|
| 213 |
+
{''.join(edge_items)}
|
| 214 |
+
{''.join(node_items)}
|
| 215 |
+
{''.join(label_items)}
|
| 216 |
+
</svg>
|
| 217 |
+
</div>
|
| 218 |
+
</div>
|
| 219 |
+
"""
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def build_cards_html(cards):
|
| 223 |
+
items = []
|
| 224 |
+
for c in cards:
|
| 225 |
+
items.append(
|
| 226 |
+
f"""
|
| 227 |
+
<article class="candidate-card" tabindex="0">
|
| 228 |
+
<div class="candidate-card-inner">
|
| 229 |
+
<div class="candidate-face candidate-front">
|
| 230 |
+
<div class="candidate-top">
|
| 231 |
+
<span class="chip">{c['agent']}</span>
|
| 232 |
+
<span class="score">{c['score']}</span>
|
| 233 |
+
</div>
|
| 234 |
+
<h4>{c['title']}</h4>
|
| 235 |
+
<p>{c['front']}</p>
|
| 236 |
+
<div class="meta-row"><span>Novelty</span><strong>{c['novelty']}</strong></div>
|
| 237 |
+
<button class="mini" type="button">Flip insight</button>
|
| 238 |
+
</div>
|
| 239 |
+
<div class="candidate-face candidate-back">
|
| 240 |
+
<div class="candidate-top">
|
| 241 |
+
<span class="chip alt">Alternative path</span>
|
| 242 |
+
<span class="score">{c['score']}</span>
|
| 243 |
+
</div>
|
| 244 |
+
<h4>{c['title']}</h4>
|
| 245 |
+
<p>{c['back']}</p>
|
| 246 |
+
<div class="meta-row"><span>Swap into route</span><strong>Enabled</strong></div>
|
| 247 |
+
<button class="mini" type="button">Return</button>
|
| 248 |
+
</div>
|
| 249 |
+
</div>
|
| 250 |
+
</article>
|
| 251 |
+
"""
|
| 252 |
+
)
|
| 253 |
+
return '<div class="panel" style="padding:20px;"><div class="candidate-grid">' + ''.join(items) + '</div></div>'
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def build_agent_timeline(reasoning):
|
| 257 |
+
rows = []
|
| 258 |
+
for r in reasoning:
|
| 259 |
+
rows.append(
|
| 260 |
+
f"""
|
| 261 |
+
<details class="agent-step" {'open' if r['step'] == 1 else ''}>
|
| 262 |
+
<summary class="agent-summary">
|
| 263 |
+
<div class="agent-index">{r['step']}</div>
|
| 264 |
+
<div class="agent-head">
|
| 265 |
+
<h4>{r['agent']}</h4>
|
| 266 |
+
<span>{r['tag']}</span>
|
| 267 |
+
</div>
|
| 268 |
+
</summary>
|
| 269 |
+
<div class="agent-copy">
|
| 270 |
+
<p>{r['summary']}</p>
|
| 271 |
+
</div>
|
| 272 |
+
</details>
|
| 273 |
+
"""
|
| 274 |
+
)
|
| 275 |
+
return '<div class="panel" style="padding:18px;"><div class="timeline">' + ''.join(rows) + '</div></div>'
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def build_chat_html(query, result):
|
| 279 |
+
return f"""
|
| 280 |
+
<div class="panel chat-panel">
|
| 281 |
+
<div class="chat-thread">
|
| 282 |
+
<div class="bubble bubble-user">
|
| 283 |
+
<span class="role">You</span>
|
| 284 |
+
<p>{query}</p>
|
| 285 |
+
</div>
|
| 286 |
+
<div class="bubble bubble-ai">
|
| 287 |
+
<span class="role">DVNC Sovereign</span>
|
| 288 |
+
<p>{result['summary']}</p>
|
| 289 |
+
</div>
|
| 290 |
+
<div class="bubble bubble-system">
|
| 291 |
+
<span class="role">Discovery Signal</span>
|
| 292 |
+
<p><strong>Primary hypothesis:</strong> {result['primary_hypothesis']}</p>
|
| 293 |
+
</div>
|
| 294 |
+
</div>
|
| 295 |
+
</div>
|
| 296 |
+
"""
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def build_models_html(selected):
|
| 300 |
+
items = []
|
| 301 |
+
for m in MODELS:
|
| 302 |
+
active = "active" if m["name"] == selected else ""
|
| 303 |
+
items.append(
|
| 304 |
+
f"""
|
| 305 |
+
<div class="model-pill {active}">
|
| 306 |
+
<span class="model-name">{m['name']}</span>
|
| 307 |
+
<span class="model-tag">{m['tag']}</span>
|
| 308 |
+
<small>{m['desc']}</small>
|
| 309 |
+
</div>
|
| 310 |
+
"""
|
| 311 |
+
)
|
| 312 |
+
return '<div class="panel" style="padding:18px;"><div class="model-switcher">' + ''.join(items) + '</div></div>'
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def build_journal_html(query):
|
| 316 |
+
q = urllib.parse.quote_plus(query or "biomaterials cardiac repair")
|
| 317 |
+
rows = []
|
| 318 |
+
for journal in JOURNALS:
|
| 319 |
+
url = f"{journal['url']}?q={q}" if "?" not in journal["url"] else f"{journal['url']}&q={q}"
|
| 320 |
+
if "ieeexplore" in journal["url"]:
|
| 321 |
+
url = f"https://ieeexplore.ieee.org/search/searchresult.jsp?queryText={q}"
|
| 322 |
+
rows.append(
|
| 323 |
+
f"""
|
| 324 |
+
<a class="journal-card" href="{url}" target="_blank" rel="noopener noreferrer">
|
| 325 |
+
<div>
|
| 326 |
+
<h4>{journal['name']}</h4>
|
| 327 |
+
<p>{journal['desc']}</p>
|
| 328 |
+
</div>
|
| 329 |
+
<span>Open</span>
|
| 330 |
+
</a>
|
| 331 |
+
"""
|
| 332 |
+
)
|
| 333 |
+
return '<div class="journal-grid">' + ''.join(rows) + '</div>'
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def search_arxiv(query, max_results=5):
|
| 337 |
+
encoded = urllib.parse.quote(query)
|
| 338 |
+
url = (
|
| 339 |
+
"http://export.arxiv.org/api/query?search_query=all:"
|
| 340 |
+
f"{encoded}&start=0&max_results={max_results}&sortBy=relevance&sortOrder=descending"
|
| 341 |
+
)
|
| 342 |
+
response = requests.get(url, timeout=20)
|
| 343 |
+
response.raise_for_status()
|
| 344 |
+
root = ET.fromstring(response.text)
|
| 345 |
+
ns = {"atom": "http://www.w3.org/2005/Atom"}
|
| 346 |
+
papers = []
|
| 347 |
+
for entry in root.findall("atom:entry", ns):
|
| 348 |
+
title = " ".join((entry.findtext("atom:title", default="", namespaces=ns) or "").split())
|
| 349 |
+
summary = " ".join((entry.findtext("atom:summary", default="", namespaces=ns) or "").split())
|
| 350 |
+
published = entry.findtext("atom:published", default="", namespaces=ns)
|
| 351 |
+
paper_id = entry.findtext("atom:id", default="", namespaces=ns)
|
| 352 |
+
authors = [a.findtext("atom:name", default="", namespaces=ns) for a in entry.findall("atom:author", ns)]
|
| 353 |
+
pdf_url = ""
|
| 354 |
+
for link in entry.findall("atom:link", ns):
|
| 355 |
+
if link.attrib.get("title") == "pdf":
|
| 356 |
+
pdf_url = link.attrib.get("href", "")
|
| 357 |
+
break
|
| 358 |
+
papers.append(
|
| 359 |
+
{
|
| 360 |
+
"id": paper_id or title,
|
| 361 |
+
"title": title,
|
| 362 |
+
"summary": summary,
|
| 363 |
+
"published": published[:10],
|
| 364 |
+
"authors": ", ".join([a for a in authors[:4] if a]),
|
| 365 |
+
"url": paper_id,
|
| 366 |
+
"pdf": pdf_url,
|
| 367 |
+
}
|
| 368 |
+
)
|
| 369 |
+
return papers
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
def format_papers_html(papers):
|
| 373 |
+
if not papers:
|
| 374 |
+
return '<div class="panel papers-panel"><p>No papers found yet.</p></div>'
|
| 375 |
+
|
| 376 |
+
items = []
|
| 377 |
+
for paper in papers:
|
| 378 |
+
summary = html.escape((paper.get("summary") or "")[:280])
|
| 379 |
+
items.append(
|
| 380 |
+
f"""
|
| 381 |
+
<article class="paper-card">
|
| 382 |
+
<div class="paper-topline">
|
| 383 |
+
<span class="paper-badge">{html.escape(paper.get('published', '')) or 'Paper'}</span>
|
| 384 |
+
<span class="paper-badge alt">{html.escape(paper.get('authors', 'Unknown authors'))}</span>
|
| 385 |
+
</div>
|
| 386 |
+
<h4>{html.escape(paper.get('title', 'Untitled'))}</h4>
|
| 387 |
+
<p>{summary}</p>
|
| 388 |
+
<div class="paper-links">
|
| 389 |
+
<a href="{html.escape(paper.get('url', '#'))}" target="_blank" rel="noopener noreferrer">Abstract</a>
|
| 390 |
+
<a href="{html.escape(paper.get('pdf', '#'))}" target="_blank" rel="noopener noreferrer">PDF</a>
|
| 391 |
+
</div>
|
| 392 |
+
</article>
|
| 393 |
+
"""
|
| 394 |
+
)
|
| 395 |
+
return '<div class="papers-grid">' + ''.join(items) + '</div>'
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
def uploaded_pdf_summary(file_obj):
|
| 399 |
+
if not file_obj:
|
| 400 |
+
return "No PDF uploaded yet."
|
| 401 |
+
path = getattr(file_obj, "name", None) or str(file_obj)
|
| 402 |
+
p = Path(path)
|
| 403 |
+
return f"Uploaded PDF ready for ingestion: {p.name}. You can parse this next with a paper reader, chunker, or citation extractor."
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
def build_learning_graph_state(query, papers, uploaded_name=None):
|
| 407 |
+
nodes = [{"id": "query", "label": query or "Research topic", "kind": "query"}]
|
| 408 |
+
edges = []
|
| 409 |
+
for i, paper in enumerate(papers[:5], start=1):
|
| 410 |
+
pid = f"paper_{i}"
|
| 411 |
+
nodes.append({"id": pid, "label": paper["title"], "kind": "paper"})
|
| 412 |
+
edges.append(("query", pid))
|
| 413 |
+
if uploaded_name:
|
| 414 |
+
nodes.append({"id": "upload", "label": uploaded_name, "kind": "upload"})
|
| 415 |
+
edges.append(("query", "upload"))
|
| 416 |
+
if len(nodes) > 2:
|
| 417 |
+
edges.append(("upload", "paper_1"))
|
| 418 |
+
return nodes, edges
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def run_paper_search(query, pdf_file):
|
| 422 |
+
if not query.strip() and not pdf_file:
|
| 423 |
+
empty_graph = build_learning_graph_html([], [], "Self-Learning Knowledge Graph")
|
| 424 |
+
return (
|
| 425 |
+
empty_graph,
|
| 426 |
+
'<div class="panel papers-panel"><p>Enter a topic or upload a PDF to start learning.</p></div>',
|
| 427 |
+
build_journal_html("biomaterials cardiac repair"),
|
| 428 |
+
"No PDF uploaded yet.",
|
| 429 |
+
)
|
| 430 |
+
|
| 431 |
+
papers = []
|
| 432 |
+
if query.strip():
|
| 433 |
+
try:
|
| 434 |
+
papers = search_arxiv(query.strip(), max_results=5)
|
| 435 |
+
except Exception as e:
|
| 436 |
+
papers = []
|
| 437 |
+
error_html = f'<div class="panel papers-panel"><p>Paper search failed: {html.escape(str(e))}</p></div>'
|
| 438 |
+
graph_nodes, graph_edges = build_learning_graph_state(query.strip(), [], Path(getattr(pdf_file, "name", "uploaded.pdf")).name if pdf_file else None)
|
| 439 |
+
return (
|
| 440 |
+
build_learning_graph_html(graph_nodes, graph_edges),
|
| 441 |
+
error_html,
|
| 442 |
+
build_journal_html(query.strip()),
|
| 443 |
+
uploaded_pdf_summary(pdf_file),
|
| 444 |
+
)
|
| 445 |
+
|
| 446 |
+
uploaded_name = None
|
| 447 |
+
if pdf_file:
|
| 448 |
+
uploaded_name = Path(getattr(pdf_file, "name", str(pdf_file))).name
|
| 449 |
+
|
| 450 |
+
graph_nodes, graph_edges = build_learning_graph_state(query.strip(), papers, uploaded_name)
|
| 451 |
+
graph_html = build_learning_graph_html(graph_nodes, graph_edges)
|
| 452 |
+
papers_html = format_papers_html(papers)
|
| 453 |
+
journals_html = build_journal_html(query.strip())
|
| 454 |
+
pdf_summary = uploaded_pdf_summary(pdf_file)
|
| 455 |
+
return graph_html, papers_html, journals_html, pdf_summary
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def run_discovery(query, model_name):
|
| 459 |
+
random.seed(len(query) + len(model_name))
|
| 460 |
+
|
| 461 |
+
if "curie" in query.lower() or "einstein" in query.lower():
|
| 462 |
+
primary = "Map the anomaly first, then force a distant analogy before composing the experimental programme."
|
| 463 |
+
path = ["seed", "bio", "card", "immune", "trial"]
|
| 464 |
+
else:
|
| 465 |
+
primary = "Use a self-assembling conductive scaffold that transforms mechanical strain into local regenerative signalling."
|
| 466 |
+
path = DEFAULT_PATH
|
| 467 |
+
|
| 468 |
+
summaries = [
|
| 469 |
+
"Normalises the user prompt into a graph-searchable seed and isolates the tension inside the question.",
|
| 470 |
+
"Finds remote conceptual bridges instead of staying near the starting domain cluster.",
|
| 471 |
+
"Pulls evidence packets and conflict signals required for grounded hypothesis formation.",
|
| 472 |
+
"Generates cross-domain analogies with a bias toward mechanism transfer rather than keyword similarity.",
|
| 473 |
+
"Composes the lead hypothesis and two structurally different variants.",
|
| 474 |
+
"Attacks weak assumptions, hidden confounders, and feasibility gaps.",
|
| 475 |
+
"Produces a staged validation plan with measurable falsification criteria."
|
| 476 |
+
]
|
| 477 |
+
|
| 478 |
+
reasoning = [
|
| 479 |
+
{
|
| 480 |
+
"step": i + 1,
|
| 481 |
+
"agent": AGENTS[i],
|
| 482 |
+
"tag": ["input", "graph", "evidence", "analogy", "compose", "critique", "experiment"][i],
|
| 483 |
+
"summary": summaries[i],
|
| 484 |
+
}
|
| 485 |
+
for i in range(7)
|
| 486 |
+
]
|
| 487 |
+
|
| 488 |
+
result = {
|
| 489 |
+
"summary": "A deeper route was chosen through the connectome, with live alternatives preserved as swappable cards so the reasoning path can be inspected rather than hidden.",
|
| 490 |
+
"primary_hypothesis": primary,
|
| 491 |
+
"reasoning": reasoning,
|
| 492 |
+
"cards": CANDIDATES,
|
| 493 |
+
"path": path,
|
| 494 |
+
"metrics": {
|
| 495 |
+
"Novelty": 93,
|
| 496 |
+
"Mechanistic clarity": 89,
|
| 497 |
+
"Experimental tractability": 82,
|
| 498 |
+
"Cross-domain distance": 91,
|
| 499 |
+
},
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
chat_html = build_chat_html(query, result)
|
| 503 |
+
connectome_html = build_connectome_html(path)
|
| 504 |
+
cards_html = build_cards_html(CANDIDATES)
|
| 505 |
+
timeline_html = build_agent_timeline(reasoning)
|
| 506 |
+
|
| 507 |
+
metrics = "\n".join([f"- {k}: {v}/100" for k, v in result["metrics"].items()])
|
| 508 |
+
hypothesis = (
|
| 509 |
+
"# Discovery Output\n\n"
|
| 510 |
+
f"**Model:** {model_name}\n\n"
|
| 511 |
+
f"**Primary hypothesis:** {result['primary_hypothesis']}\n\n"
|
| 512 |
+
"## Scoring\n"
|
| 513 |
+
f"{metrics}\n\n"
|
| 514 |
+
"## Experimental outline\n"
|
| 515 |
+
"1. Construct the candidate material or protocol.\n"
|
| 516 |
+
"2. Test mechanistic signal expression under controlled conditions.\n"
|
| 517 |
+
"3. Compare against baseline and nearest-neighbour alternatives.\n"
|
| 518 |
+
"4. Falsify using the adversarial risk criteria surfaced in the reasoning path.\n"
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
return chat_html, connectome_html, timeline_html, cards_html, hypothesis, build_models_html(model_name)
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
CSS = r"""
|
| 525 |
+
:root {
|
| 526 |
+
--bg: #ffffff;
|
| 527 |
+
--panel: #ffffff;
|
| 528 |
+
--line: rgba(0, 0, 0, 0.12);
|
| 529 |
+
--text: #111111;
|
| 530 |
+
--muted: #5b5b5b;
|
| 531 |
+
--soft: rgba(0, 0, 0, 0.62);
|
| 532 |
+
--gold: #ff6600;
|
| 533 |
+
--teal: #17b8a6;
|
| 534 |
+
--blue: #628dff;
|
| 535 |
+
--chosen: #ff7a1a;
|
| 536 |
+
--idle: #b8d8ff;
|
| 537 |
+
--idle-stroke: #5e8fe6;
|
| 538 |
+
--query-node: #ffd8b3;
|
| 539 |
+
--paper-node: #d7f6f2;
|
| 540 |
+
--upload-node: #e7defe;
|
| 541 |
+
--shadow: 0 16px 40px rgba(0, 0, 0, 0.12);
|
| 542 |
+
}
|
| 543 |
+
html, body, .gradio-container {
|
| 544 |
+
background: #ffffff !important;
|
| 545 |
+
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
| 546 |
+
}
|
| 547 |
+
.gradio-container {
|
| 548 |
+
max-width: 1640px !important;
|
| 549 |
+
padding: 20px !important;
|
| 550 |
+
}
|
| 551 |
+
#dvnc-shell {
|
| 552 |
+
border: 1px solid var(--line);
|
| 553 |
+
border-radius: 28px;
|
| 554 |
+
overflow: hidden;
|
| 555 |
+
background: #ffffff;
|
| 556 |
+
box-shadow: var(--shadow);
|
| 557 |
+
padding: 20px 22px 22px;
|
| 558 |
+
}
|
| 559 |
+
.hero-bar {
|
| 560 |
+
display: flex;
|
| 561 |
+
justify-content: space-between;
|
| 562 |
+
align-items: center;
|
| 563 |
+
gap: 16px;
|
| 564 |
+
padding-bottom: 12px;
|
| 565 |
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
| 566 |
+
margin-bottom: 16px;
|
| 567 |
+
}
|
| 568 |
+
.brand { display: flex; align-items: center; gap: 14px; }
|
| 569 |
+
.logo {
|
| 570 |
+
width: 42px; height: 42px; border-radius: 14px; display: grid; place-items: center; color: var(--gold);
|
| 571 |
+
background: linear-gradient(135deg, rgba(255, 122, 26, 0.12), rgba(23, 184, 166, 0.10));
|
| 572 |
+
border: 1px solid rgba(0, 0, 0, 0.08);
|
| 573 |
+
}
|
| 574 |
+
.logo svg { width: 24px; height: 24px; }
|
| 575 |
+
.brand h1 { font-size: 1.05rem; margin: 0; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
|
| 576 |
+
.brand p { margin: 3px 0 0; color: var(--muted); font-size: .84rem; }
|
| 577 |
+
.status { display: flex; gap: 10px; align-items: center; color: var(--soft); font-size: .85rem; }
|
| 578 |
+
.status-dot {
|
| 579 |
+
width: 10px; height: 10px; border-radius: 50%; background: var(--teal);
|
| 580 |
+
box-shadow: 0 0 0 6px rgba(23, 184, 166, 0.10), 0 0 14px rgba(23, 184, 166, 0.25);
|
| 581 |
+
}
|
| 582 |
+
.panel {
|
| 583 |
+
background: #ffffff; border: 1px solid var(--line); border-radius: 22px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
| 584 |
+
}
|
| 585 |
+
.querybox textarea, .querybox input { background: transparent !important; color: var(--text) !important; }
|
| 586 |
+
.querybox, .querybox > div { background: #ffffff !important; border-radius: 18px !important; border-color: var(--line) !important; }
|
| 587 |
+
.chat-panel { padding: 18px; min-height: 280px; }
|
| 588 |
+
.chat-thread { display: flex; flex-direction: column; gap: 14px; }
|
| 589 |
+
.bubble { max-width: 88%; padding: 16px 18px; border-radius: 22px; border: 1px solid var(--line); }
|
| 590 |
+
.bubble p { margin: 8px 0 0; line-height: 1.6; font-size: .96rem; color: var(--text); }
|
| 591 |
+
.bubble .role { font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
|
| 592 |
+
.bubble-user { align-self: flex-end; background: linear-gradient(135deg, rgba(98, 141, 255, 0.16), rgba(98, 141, 255, 0.08)); }
|
| 593 |
+
.bubble-ai { align-self: flex-start; background: #ffffff; }
|
| 594 |
+
.bubble-system { align-self: flex-start; background: linear-gradient(135deg, rgba(255, 122, 26, 0.10), rgba(255, 122, 26, 0.04)); }
|
| 595 |
+
.model-switcher { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
| 596 |
+
.model-pill {
|
| 597 |
+
padding: 14px; border: 1px solid var(--line); border-radius: 18px; display: flex; flex-direction: column; gap: 4px; min-height: 98px; background: #ffffff;
|
| 598 |
+
}
|
| 599 |
+
.model-pill.active {
|
| 600 |
+
border-color: rgba(255, 122, 26, 0.40);
|
| 601 |
+
background: linear-gradient(135deg, rgba(255, 122, 26, 0.10), rgba(255, 255, 255, 0.96));
|
| 602 |
+
}
|
| 603 |
+
.model-name { font-weight: 650; color: var(--text); }
|
| 604 |
+
.model-tag { font-size: .76rem; text-transform: uppercase; letter-spacing: .12em; color: var(--gold); }
|
| 605 |
+
.model-pill small { color: var(--muted); line-height: 1.45; }
|
| 606 |
+
.brain-shell { padding: 18px; }
|
| 607 |
+
.brain-header { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; margin-bottom: 10px; }
|
| 608 |
+
.eyebrow { font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--gold); margin: 0 0 4px; }
|
| 609 |
+
.brain-header h3 { margin: 0; font-size: 1.12rem; color: var(--text); }
|
| 610 |
+
.brain-legend { display: flex; gap: 14px; color: var(--muted); font-size: .8rem; flex-wrap: wrap; }
|
| 611 |
+
.dot { width: 10px; height: 10px; display: inline-block; border-radius: 50%; margin-right: 6px; }
|
| 612 |
+
.dot-live { background: var(--chosen); box-shadow: 0 0 10px rgba(255, 122, 26, 0.35); }
|
| 613 |
+
.dot-chosen { background: var(--chosen); }
|
| 614 |
+
.dot-idle { background: var(--idle); border: 1px solid var(--idle-stroke); }
|
| 615 |
+
.dot-query { background: var(--query-node); border: 1px solid #de9e58; }
|
| 616 |
+
.dot-paper { background: var(--paper-node); border: 1px solid #4fb3a5; }
|
| 617 |
+
.dot-upload { background: var(--upload-node); border: 1px solid #8f73d9; }
|
| 618 |
+
.brain-stage {
|
| 619 |
+
position: relative; min-height: 420px; overflow: hidden; background: linear-gradient(180deg, rgba(250, 250, 250, 1), rgba(255, 255, 255, 1));
|
| 620 |
+
border: 1px solid rgba(0, 0, 0, 0.05); border-radius: 20px;
|
| 621 |
+
}
|
| 622 |
+
.brain-svg { width: 100%; height: 520px; display: block; }
|
| 623 |
+
.edge { stroke: rgba(0, 0, 0, 0.12); stroke-width: 2.4; }
|
| 624 |
+
.edge.active {
|
| 625 |
+
stroke: var(--chosen); stroke-width: 4.2; stroke-linecap: round; filter: drop-shadow(0 0 6px rgba(255, 122, 26, 0.45)); stroke-dasharray: 8 12; animation: pulseEdge 1.5s linear infinite;
|
| 626 |
+
}
|
| 627 |
+
.node { stroke-width: 2.2; transition: all .25s ease; }
|
| 628 |
+
.node.idle { fill: var(--idle); stroke: var(--idle-stroke); }
|
| 629 |
+
.node.chosen { fill: var(--chosen); stroke: #ffb16d; }
|
| 630 |
+
.halo { fill: none; }
|
| 631 |
+
.halo.active { stroke: rgba(255, 122, 26, 0.18); stroke-width: 12; }
|
| 632 |
+
.label { fill: #2c2c2c; font-size: 13px; font-weight: 500; letter-spacing: .01em; }
|
| 633 |
+
.label.active { fill: #111111; font-weight: 700; }
|
| 634 |
+
.learn-edge { stroke: rgba(0, 0, 0, 0.18); stroke-width: 2.2; stroke-linecap: round; }
|
| 635 |
+
.learn-node { stroke-width: 2.2; }
|
| 636 |
+
.learn-node.query { fill: var(--query-node); stroke: #de9e58; }
|
| 637 |
+
.learn-node.paper { fill: var(--paper-node); stroke: #36a091; }
|
| 638 |
+
.learn-node.upload { fill: var(--upload-node); stroke: #7e63cb; }
|
| 639 |
+
.learn-label { fill: #1e1e1e; font-size: 12px; font-weight: 600; }
|
| 640 |
+
.learning-empty { display: grid; place-items: center; }
|
| 641 |
+
.empty-graph-copy { text-align: center; max-width: 440px; padding: 40px 20px; }
|
| 642 |
+
.empty-graph-copy h4 { margin: 0 0 10px; font-size: 1.05rem; }
|
| 643 |
+
.empty-graph-copy p { margin: 0; color: var(--muted); line-height: 1.6; }
|
| 644 |
+
.timeline { display: flex; flex-direction: column; gap: 10px; }
|
| 645 |
+
.agent-step { border: 1px solid var(--line); border-radius: 18px; background: #ffffff; overflow: hidden; }
|
| 646 |
+
.agent-summary { list-style: none; display: grid; grid-template-columns: 42px 1fr; gap: 12px; align-items: center; padding: 12px; cursor: pointer; }
|
| 647 |
+
.agent-summary::-webkit-details-marker { display: none; }
|
| 648 |
+
.agent-index {
|
| 649 |
+
width: 42px; height: 42px; border-radius: 14px; display: grid; place-items: center; font-weight: 700; color: var(--gold);
|
| 650 |
+
background: rgba(255, 122, 26, 0.08); border: 1px solid rgba(255, 122, 26, 0.18);
|
| 651 |
+
}
|
| 652 |
+
.agent-head { display: flex; justify-content: space-between; gap: 12px; align-items: center; }
|
| 653 |
+
.agent-head h4 { margin: 0; font-size: .98rem; color: var(--text); }
|
| 654 |
+
.agent-head span { font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
|
| 655 |
+
.agent-copy { padding: 0 14px 16px 66px; }
|
| 656 |
+
.agent-copy p { margin: 0; color: #2d2d2d; font-size: .93rem; line-height: 1.6; }
|
| 657 |
+
.candidate-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
|
| 658 |
+
.candidate-card { background: none; perspective: 1400px; min-height: 330px; }
|
| 659 |
+
.candidate-card-inner { position: relative; width: 100%; min-height: 330px; transition: transform .8s cubic-bezier(.2,.7,.1,1); transform-style: preserve-3d; }
|
| 660 |
+
.candidate-card:hover .candidate-card-inner, .candidate-card:focus .candidate-card-inner, .candidate-card:focus-within .candidate-card-inner { transform: rotateY(180deg); }
|
| 661 |
+
.candidate-face {
|
| 662 |
+
position: absolute; inset: 0; padding: 20px; border-radius: 22px; border: 1px solid var(--line); background: #ffffff; color: var(--text);
|
| 663 |
+
backface-visibility: hidden; box-shadow: 0 12px 24px rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; gap: 14px;
|
| 664 |
+
}
|
| 665 |
+
.candidate-back { transform: rotateY(180deg); background: #ffffff; }
|
| 666 |
+
.candidate-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
|
| 667 |
+
.chip {
|
| 668 |
+
font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: #0b6f66; padding: 7px 10px; border-radius: 999px;
|
| 669 |
+
background: rgba(23, 184, 166, 0.08); border: 1px solid rgba(23, 184, 166, 0.18);
|
| 670 |
+
}
|
| 671 |
+
.chip.alt { color: var(--gold); background: rgba(255, 122, 26, 0.08); border-color: rgba(255, 122, 26, 0.18); }
|
| 672 |
+
.score { font-weight: 700; color: var(--gold); }
|
| 673 |
+
.candidate-face h4 { margin: 0; font-size: 1.08rem; line-height: 1.35; color: var(--text); }
|
| 674 |
+
.candidate-face p { margin: 0; color: #1e1e1e; line-height: 1.65; font-size: .96rem; overflow-wrap: anywhere; }
|
| 675 |
+
.meta-row { margin-top: auto; display: flex; justify-content: space-between; color: var(--muted); font-size: .88rem; gap: 14px; }
|
| 676 |
+
.mini { margin-top: 8px; align-self: flex-start; color: var(--text); padding: 10px 12px; border-radius: 14px; border: 1px solid var(--line); background: #ffffff; }
|
| 677 |
+
.papers-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
| 678 |
+
.paper-card { border: 1px solid var(--line); border-radius: 18px; padding: 16px; background: #ffffff; }
|
| 679 |
+
.paper-topline { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
|
| 680 |
+
.paper-badge { font-size: .72rem; padding: 6px 10px; border-radius: 999px; background: rgba(98, 141, 255, 0.08); color: #3456b5; border: 1px solid rgba(98, 141, 255, 0.18); }
|
| 681 |
+
.paper-badge.alt { background: rgba(0, 0, 0, 0.04); color: #444; border-color: rgba(0, 0, 0, 0.08); }
|
| 682 |
+
.paper-card h4 { margin: 0 0 10px; line-height: 1.35; font-size: 1rem; }
|
| 683 |
+
.paper-card p { margin: 0 0 12px; line-height: 1.6; color: #222; }
|
| 684 |
+
.paper-links { display: flex; gap: 12px; flex-wrap: wrap; }
|
| 685 |
+
.paper-links a, .journal-card, .upload-note a { color: #0b63ce; text-decoration: none; }
|
| 686 |
+
.journal-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
|
| 687 |
+
.journal-card {
|
| 688 |
+
border: 1px solid var(--line); border-radius: 18px; padding: 16px; display: flex; justify-content: space-between; gap: 14px; align-items: center; background: #ffffff;
|
| 689 |
+
}
|
| 690 |
+
.journal-card h4 { margin: 0 0 6px; color: var(--text); }
|
| 691 |
+
.journal-card p { margin: 0; color: var(--muted); line-height: 1.5; }
|
| 692 |
+
.upload-note {
|
| 693 |
+
border: 1px dashed rgba(0,0,0,.16); border-radius: 18px; padding: 16px; background: rgba(0,0,0,.015); color: #1f1f1f; line-height: 1.6;
|
| 694 |
+
}
|
| 695 |
+
.prosebox { padding: 18px; white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; line-height: 1.55; color: #1b1b1b; }
|
| 696 |
+
.gr-button-primary { background: linear-gradient(135deg, rgba(255, 122, 26, 0.92), rgba(240, 108, 22, 0.92)) !important; color: #ffffff !important; border: none !important; }
|
| 697 |
+
.gr-button-secondary { background: #ffffff !important; color: var(--text) !important; border: 1px solid var(--line) !important; }
|
| 698 |
+
footer { display: none !important; }
|
| 699 |
+
@keyframes pulseEdge { to { stroke-dashoffset: -40; } }
|
| 700 |
+
@media (max-width: 1180px) {
|
| 701 |
+
.model-switcher, .candidate-grid, .papers-grid, .journal-grid { grid-template-columns: 1fr; }
|
| 702 |
+
.brain-svg { height: 460px; }
|
| 703 |
+
}
|
| 704 |
+
"""
|
| 705 |
+
|
| 706 |
+
|
| 707 |
+
HEAD = """
|
| 708 |
+
<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">
|
| 709 |
+
<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>
|
| 710 |
+
<link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">
|
| 711 |
+
"""
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
with gr.Blocks(css=CSS, head=HEAD, theme=gr.themes.Base(), fill_height=True) as demo:
|
| 715 |
+
gr.HTML(
|
| 716 |
+
"""
|
| 717 |
+
<div id=\"dvnc-shell\">
|
| 718 |
+
<div class=\"hero-bar\">
|
| 719 |
+
<div class=\"brand\">
|
| 720 |
+
<div class=\"logo\" aria-hidden=\"true\">
|
| 721 |
+
<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\">
|
| 722 |
+
<path d=\"M5 17L12 4l7 13\"/>
|
| 723 |
+
<path d=\"M8.5 12.5h7\"/>
|
| 724 |
+
<circle cx=\"12\" cy=\"12\" r=\"1.8\" fill=\"currentColor\" stroke=\"none\"/>
|
| 725 |
+
</svg>
|
| 726 |
+
</div>
|
| 727 |
+
<div>
|
| 728 |
+
<h1>DVNC.AI</h1>
|
| 729 |
+
<p>Sovereign discovery instrument · connectome-native reasoning</p>
|
| 730 |
+
</div>
|
| 731 |
+
</div>
|
| 732 |
+
<div class=\"status\"><span class=\"status-dot\"></span><span>Live orchestration</span></div>
|
| 733 |
+
</div>
|
| 734 |
+
</div>
|
| 735 |
+
"""
|
| 736 |
+
)
|
| 737 |
+
|
| 738 |
+
with gr.Tabs():
|
| 739 |
+
with gr.Tab("Discovery Engine"):
|
| 740 |
+
model_html = gr.HTML(build_models_html("DVNC Sovereign"))
|
| 741 |
+
with gr.Row():
|
| 742 |
+
with gr.Column(scale=2):
|
| 743 |
+
model = gr.Dropdown(
|
| 744 |
+
choices=[m["name"] for m in MODELS],
|
| 745 |
+
value="DVNC Sovereign",
|
| 746 |
+
label="Model tier",
|
| 747 |
+
)
|
| 748 |
+
query = gr.Textbox(
|
| 749 |
+
label="Discovery query",
|
| 750 |
+
elem_classes=["querybox"],
|
| 751 |
+
placeholder="Enter a scientific question, anomaly, or breakthrough direction…",
|
| 752 |
+
lines=4,
|
| 753 |
+
)
|
| 754 |
+
with gr.Row():
|
| 755 |
+
run_btn = gr.Button("Run discovery", variant="primary")
|
| 756 |
+
example_btn = gr.Button("Load example", variant="secondary")
|
| 757 |
+
chat = gr.HTML(
|
| 758 |
+
"""
|
| 759 |
+
<div class=\"panel chat-panel\">
|
| 760 |
+
<div class=\"chat-thread\">
|
| 761 |
+
<div class=\"bubble bubble-ai\">
|
| 762 |
+
<span class=\"role\">DVNC</span>
|
| 763 |
+
<p>Enter a query to activate the 7-agent discovery stack and illuminate the chosen path through the 3D connectome.</p>
|
| 764 |
+
</div>
|
| 765 |
+
</div>
|
| 766 |
+
</div>
|
| 767 |
+
"""
|
| 768 |
+
)
|
| 769 |
+
timeline = gr.HTML('<div class="panel" style="padding:18px"><div class="timeline"></div></div>')
|
| 770 |
+
|
| 771 |
+
with gr.Column(scale=3):
|
| 772 |
+
connectome = gr.HTML(build_connectome_html(DEFAULT_PATH))
|
| 773 |
+
cards = gr.HTML('<div class="panel" style="padding:20px"><div class="candidate-grid"></div></div>')
|
| 774 |
+
|
| 775 |
+
output = gr.Markdown("# Discovery Output\n\nAwaiting query.")
|
| 776 |
+
|
| 777 |
+
with gr.Tab("Self-Learning Graph"):
|
| 778 |
+
with gr.Row():
|
| 779 |
+
with gr.Column(scale=2):
|
| 780 |
+
paper_query = gr.Textbox(
|
| 781 |
+
label="Research topic",
|
| 782 |
+
elem_classes=["querybox"],
|
| 783 |
+
placeholder="e.g. self-assembling conductive biomaterials for cardiac repair",
|
| 784 |
+
lines=3,
|
| 785 |
+
)
|
| 786 |
+
pdf_upload = gr.File(label="Upload PDF papers", file_types=[".pdf"], file_count="single")
|
| 787 |
+
with gr.Row():
|
| 788 |
+
learn_btn = gr.Button("Grow knowledge graph", variant="primary")
|
| 789 |
+
load_topic_btn = gr.Button("Load example topic", variant="secondary")
|
| 790 |
+
upload_status = gr.Markdown("No PDF uploaded yet.")
|
| 791 |
+
journal_panel = gr.HTML(build_journal_html("biomaterials cardiac repair"))
|
| 792 |
+
with gr.Column(scale=3):
|
| 793 |
+
learning_graph = gr.HTML(build_learning_graph_html([], []))
|
| 794 |
+
papers_panel = gr.HTML('<div class="panel papers-panel" style="padding:18px"><p>Search arXiv, pull linked PDFs, and grow the graph from retrieved papers.</p></div>')
|
| 795 |
+
|
| 796 |
+
def load_example():
|
| 797 |
+
return "How could a self-assembling conductive biomaterial improve cardiac tissue regeneration by converting mechanical strain into repair signalling?"
|
| 798 |
+
|
| 799 |
+
def load_paper_topic():
|
| 800 |
+
return "self-assembling conductive biomaterials for cardiac repair"
|
| 801 |
+
|
| 802 |
+
example_btn.click(fn=load_example, outputs=query)
|
| 803 |
+
load_topic_btn.click(fn=load_paper_topic, outputs=paper_query)
|
| 804 |
+
run_btn.click(
|
| 805 |
+
fn=run_discovery,
|
| 806 |
+
inputs=[query, model],
|
| 807 |
+
outputs=[chat, connectome, timeline, cards, output, model_html],
|
| 808 |
+
)
|
| 809 |
+
learn_btn.click(
|
| 810 |
+
fn=run_paper_search,
|
| 811 |
+
inputs=[paper_query, pdf_upload],
|
| 812 |
+
outputs=[learning_graph, papers_panel, journal_panel, upload_status],
|
| 813 |
+
)
|
| 814 |
+
|
| 815 |
+
|
| 816 |
+
if __name__ == "__main__":
|
| 817 |
+
demo.launch()
|