ARandAI / app.py
1qwsd's picture
Rename app_v3 (1).py to app.py
a08647e verified
Raw
History Blame Contribute Delete
22.5 kB
import gradio as gr
from huggingface_hub import InferenceClient
import os
HF_TOKEN = os.environ.get("HF_TOKEN", "")
client = InferenceClient(
model="mistralai/Mistral-7B-Instruct-v0.3",
token=HF_TOKEN,
)
OCEANUS_SYSTEM = """You are OCEANUS β€” the AI Oracle of the Deep, trained on all marine science, oceanography, and underwater exploration data.
Your knowledge spans:
🌊 All 5 ocean zones: Sunlit (0-200m), Twilight (200-1000m), Midnight (1000-4000m), Abyssal (4000-6000m), Hadal (6000-11000m)
πŸ™ Marine biology: species taxonomy, bioluminescence, deep-sea adaptations, food webs
βš—οΈ Ocean chemistry: salinity, thermoclines, oxygen minimum zones, hydrothermal vent chemistry
πŸ—ΊοΈ Geology: mid-ocean ridges, trenches (Mariana, Puerto Rico, Java), underwater volcanoes
🌑️ Physics: pressure (1 atm per 10m), temperature inversions, thermohaline circulation
πŸ”¬ Historic missions: Alvin, Deepsea Challenger, Nereus, Kaiko
Respond with scientific precision but a dramatic explorer's tone. Include measurements, species names, Latin taxonomy. Use emojis. Always end with one 🀯 MIND-BLOWING FACT. Keep under 280 words."""
INITIAL_MSG = [{"role": "assistant", "content": "🌊 **OCEANUS ONLINE** β€” AI Oracle of the Deep, powered by Mistral-7B (free). Select a zone in the 3D viewer above, then ask me anything about the ocean.\n\n🀿 Where shall we dive first?"}]
def build_prompt(message: str, history: list, zone: str, ocean: str) -> str:
context = ""
if zone: context += f" [Exploring: {zone}]"
if ocean: context += f" [Ocean: {ocean}]"
prompt = f"<s>[INST] <<SYS>>\n{OCEANUS_SYSTEM}\n<</SYS>>\n\n"
for msg in history[-8:]:
role = msg["role"]
content = msg["content"]
if role == "user":
prompt += f"{content} [/INST] "
else:
prompt += f"{content} </s><s>[INST] "
prompt += f"{message}{context} [/INST]"
return prompt
def chat_with_oceanus(message: str, history: list, zone: str, ocean: str):
if not message.strip():
return "", history
prompt = build_prompt(message, history, zone, ocean)
try:
response = client.text_generation(
prompt,
max_new_tokens=400,
temperature=0.7,
repetition_penalty=1.1,
do_sample=True,
stop_sequences=["</s>", "[INST]"],
)
reply = response.strip() or "⚠️ Signal lost. Please retry."
except Exception as e:
reply = f"⚠️ OCEANUS disrupted: {str(e)}\n\nEnsure HF_TOKEN is set in Space secrets."
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": reply})
return "", history
def quick_ask(question: str, history: list, zone: str, ocean: str):
return chat_with_oceanus(question, history, zone, ocean)
def get_zone_info(zone: str) -> str:
data = {
"Sunlit Zone (0-200m)": "β˜€οΈ **SUNLIT ZONE** | 0–200m | 4–30Β°C | 1–20 atm\nHome to 90% of all marine life. Photosynthesis drives the base of all ocean food chains.",
"Twilight Zone (200-1000m)": "πŸŒ’ **TWILIGHT ZONE** | 200–1,000m | 4–20Β°C | 20–100 atm\nOnly 1% sunlight here. Creatures perform daily vertical migrations of hundreds of meters.",
"Midnight Zone (1000-4000m)": "πŸŒ‘ **MIDNIGHT ZONE** | 1,000–4,000m | 2–4Β°C | 100–400 atm\nAbsolute darkness. 76% of deep-sea fish produce their own bioluminescent light.",
"Abyssal Zone (4000-6000m)": "⚫ **ABYSSAL ZONE** | 4,000–6,000m | 0–3Β°C | 400–600 atm\nCovers 60% of Earth's surface. Home to hydrothermal vents with chemosynthetic life.",
"Hadal Zone (6000-11000m)": "πŸ’€ **HADAL ZONE** | 6,000–11,000m | 1–4Β°C | 600–1,086 atm\nMariana Trench (10,935m) β€” pressure is 1,086Γ— surface. Life still thrives here.",
}
return data.get(zone, "Select a zone to begin your descent...")
THREE_JS_HTML = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
* { margin:0; padding:0; box-sizing:border-box; }
body { background:#000408; font-family:'Share Tech Mono',monospace; overflow:hidden; color:#00ffcc; }
#cc { position:relative; width:100%; height:560px; overflow:hidden; }
#tc { width:100%!important; height:100%!important; display:block; }
.hud { position:absolute; pointer-events:none; font-family:'Share Tech Mono',monospace; font-size:11px; letter-spacing:1px; text-transform:uppercase; }
#htl { top:16px; left:16px; color:#00ffcc; }
#htr { top:16px; right:16px; text-align:right; color:#00d4ff; }
#hbl { bottom:64px; left:16px; color:#7fff00; }
#hbr { bottom:64px; right:16px; text-align:right; color:#ff6b35; }
.hv { font-size:16px; font-family:'Orbitron',sans-serif; font-weight:700; display:block; }
.hl { font-size:9px; opacity:.6; display:block; margin-bottom:5px; }
#sl { position:absolute; top:0; left:0; right:0; bottom:0; background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,255,204,.012) 2px,rgba(0,255,204,.012) 4px); pointer-events:none; z-index:10; }
.corner { position:absolute; width:36px; height:36px; pointer-events:none; z-index:5; }
.c1 { top:8px; left:8px; border-top:2px solid #00ffcc; border-left:2px solid #00ffcc; }
.c2 { top:8px; right:8px; border-top:2px solid #00ffcc; border-right:2px solid #00ffcc; }
.c3 { bottom:8px; left:8px; border-bottom:2px solid #00ffcc; border-left:2px solid #00ffcc; }
.c4 { bottom:8px; right:8px; border-bottom:2px solid #00ffcc; border-right:2px solid #00ffcc; }
#th { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center; pointer-events:none; z-index:15; transition:opacity 1.2s; }
#th h1 { font-family:'Orbitron',sans-serif; font-size:clamp(22px,4vw,46px); font-weight:900; color:#00ffcc; text-shadow:0 0 40px #00ffcc; letter-spacing:8px; animation:glow 3s ease-in-out infinite; }
#th p { font-size:10px; color:rgba(0,255,204,.55); letter-spacing:4px; margin-top:6px; }
@keyframes glow { 0%,100%{text-shadow:0 0 40px #00ffcc} 50%{text-shadow:0 0 80px #00ffcc,0 0 160px rgba(0,255,204,.4)} }
#zc { position:absolute; left:50%; bottom:12px; transform:translateX(-50%); display:flex; gap:7px; z-index:20; pointer-events:all; }
.zb { background:rgba(0,20,40,.85); border:1px solid; color:inherit; font-family:'Share Tech Mono',monospace; font-size:9px; letter-spacing:1px; padding:5px 9px; cursor:pointer; text-transform:uppercase; transition:all .3s; backdrop-filter:blur(4px); }
.zb:hover,.zb.active { background:rgba(0,255,204,.18); box-shadow:0 0 10px currentColor; }
.z0{color:#fff176;border-color:#fff176} .z1{color:#80cbc4;border-color:#80cbc4} .z2{color:#5c6bc0;border-color:#5c6bc0} .z3{color:#00ffcc;border-color:#00ffcc} .z4{color:#ff6b35;border-color:#ff6b35}
#sb { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#00ffcc; font-family:'Orbitron',sans-serif; font-size:11px; letter-spacing:3px; text-align:center; pointer-events:none; opacity:0; transition:opacity .5s; text-shadow:0 0 20px #00ffcc; z-index:20; }
#sb.show { opacity:1; }
#db { position:absolute; right:14px; top:50%; transform:translateY(-50%); width:5px; height:180px; background:rgba(0,255,204,.1); border:1px solid rgba(0,255,204,.3); pointer-events:none; z-index:10; }
#df { position:absolute; bottom:0; width:100%; background:linear-gradient(to top,#ff6b35,#5c6bc0,#00ffcc); transition:height 1s ease; }
#ld { position:absolute; top:0; left:0; right:0; bottom:0; display:flex; align-items:center; justify-content:center; background:#000408; z-index:100; flex-direction:column; gap:14px; transition:opacity 1s; }
#ld.fade { opacity:0; pointer-events:none; }
.lt { font-family:'Orbitron',sans-serif; color:#00ffcc; font-size:13px; letter-spacing:5px; animation:blink .9s step-end infinite; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }
.lb { width:180px; height:2px; background:rgba(0,255,204,.15); overflow:hidden; }
.lf { height:100%; background:#00ffcc; animation:lp 2.2s ease-in-out forwards; box-shadow:0 0 8px #00ffcc; }
@keyframes lp { 0%{width:0%} 100%{width:100%} }
</style>
</head>
<body>
<div id="ld"><div class="lt">INITIALIZING OCEANUS ARRAY</div><div class="lb"><div class="lf"></div></div></div>
<div id="cc">
<canvas id="tc"></canvas>
<div id="sl"></div>
<div class="corner c1"></div><div class="corner c2"></div><div class="corner c3"></div><div class="corner c4"></div>
<div id="th"><h1>OCEANUS</h1><p>GOD'S EYE β€” AR DEEP OCEAN EXPLORER</p></div>
<div class="hud" id="htl">
<span class="hl">SYS STATUS</span><span class="hv" style="font-size:12px;color:#7fff00">● ONLINE</span><br>
<span class="hl">SONAR</span><span class="hv" style="font-size:12px" id="sv">ACTIVE</span><br>
<span class="hl">ZONE</span><span class="hv" style="font-size:12px" id="zd">SURFACE</span>
</div>
<div class="hud" id="htr">
<span class="hl">DEPTH</span><span class="hv" id="dv">0 m</span><br>
<span class="hl">PRESSURE</span><span class="hv" id="pv">1 atm</span><br>
<span class="hl">TEMP</span><span class="hv" id="tv">28Β°C</span>
</div>
<div class="hud" id="hbl">
<span class="hl">SPECIES DETECTED</span><span class="hv" id="spv" style="font-size:11px">SCANNING...</span><br>
<span class="hl">BIOLUMINESCENCE</span><span class="hv" id="bv">0%</span>
</div>
<div class="hud" id="hbr">
<span class="hl">OCEAN COVERAGE</span><span class="hv">70.9%</span><br>
<span class="hl">UNMAPPED</span><span class="hv" style="color:#ff6b35">~80%</span>
</div>
<div id="db"><div id="df" style="height:5%"></div></div>
<div id="sb">DIVING...</div>
<div id="zc">
<button class="zb z0 active" onclick="setZone(0)">β˜€ SUNLIT</button>
<button class="zb z1" onclick="setZone(1)">πŸŒ’ TWILIGHT</button>
<button class="zb z2" onclick="setZone(2)">πŸŒ‘ MIDNIGHT</button>
<button class="zb z3" onclick="setZone(3)">⚫ ABYSSAL</button>
<button class="zb z4" onclick="setZone(4)">πŸ’€ HADAL</button>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
const ZONES=[
{name:'SUNLIT', depth:100, pressure:10, temp:24, color:0x006994,fog:0x0099cc,fogD:.05,bio:5, sp:'Blue Whale Β· Manta Ray Β· Tuna'},
{name:'TWILIGHT',depth:600, pressure:60, temp:12, color:0x003366,fog:0x001a4d,fogD:.08,bio:30,sp:'Giant Squid Β· Lanternfish Β· Hatchetfish'},
{name:'MIDNIGHT',depth:2500, pressure:250, temp:3, color:0x000d1a,fog:0x000814,fogD:.13,bio:76,sp:'Anglerfish Β· Fangtooth Β· Viperfish'},
{name:'ABYSSAL', depth:5000, pressure:500, temp:2, color:0x000408,fog:0x000204,fogD:.17,bio:60,sp:'Sea Cucumber Β· Tripod Fish Β· Xenophyophore'},
{name:'HADAL', depth:10935,pressure:1086,temp:1.5,color:0x000000,fog:0x000000,fogD:.23,bio:20,sp:'Amphipods Β· Snailfish Β· Polychaete Worms'},
];
let scene,camera,renderer,ocean,particles,bubbles,creatures=[],clock,titleFaded=false;
function init(){
const canvas=document.getElementById('tc'),cont=document.getElementById('cc');
renderer=new THREE.WebGLRenderer({canvas,antialias:true});
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.setSize(cont.clientWidth,cont.clientHeight);
renderer.setClearColor(0x001a3d,1);
scene=new THREE.Scene();
scene.fog=new THREE.FogExp2(0x001a3d,.05);
clock=new THREE.Clock();
camera=new THREE.PerspectiveCamera(60,cont.clientWidth/cont.clientHeight,.1,1000);
camera.position.set(0,8,16); camera.lookAt(0,0,0);
buildAll(); addLights();
window.addEventListener('resize',onResize);
setTimeout(()=>{document.getElementById('ld').classList.add('fade');setTimeout(()=>{const l=document.getElementById('ld');if(l)l.remove();},1000);},2400);
animate();
}
function buildAll(){
// Ocean surface
const og=new THREE.PlaneGeometry(60,60,80,80);
const om=new THREE.MeshPhongMaterial({color:0x006994,emissive:0x001133,transparent:true,opacity:.85,shininess:120});
ocean=new THREE.Mesh(og,om); ocean.rotation.x=-Math.PI/2; scene.add(ocean);
// Floor
const fg=new THREE.PlaneGeometry(60,60,20,20);
const fm=new THREE.MeshPhongMaterial({color:0x1a0a00,emissive:0x050200});
const fl=new THREE.Mesh(fg,fm); fl.rotation.x=-Math.PI/2; fl.position.y=-10; scene.add(fl);
// Grid
const gg=new THREE.PlaneGeometry(60,60,40,40);
const gm=new THREE.MeshBasicMaterial({color:0x00ffcc,wireframe:true,transparent:true,opacity:.06});
const gr2=new THREE.Mesh(gg,gm); gr2.rotation.x=-Math.PI/2; gr2.position.y=-4.9; scene.add(gr2);
// Vents
for(let i=0;i<6;i++){
const v=new THREE.Mesh(new THREE.CylinderGeometry(.05,.22,4,8),new THREE.MeshPhongMaterial({color:0x333333,emissive:0x110500}));
v.position.set((Math.random()-.5)*22,-8,(Math.random()-.5)*22); scene.add(v);
const s=new THREE.Mesh(new THREE.SphereGeometry(.3,8,8),new THREE.MeshBasicMaterial({color:0xff4400,transparent:true,opacity:.65}));
s.position.copy(v.position); s.position.y=-6.4; scene.add(s);
}
// Coral
const cc=[0x00ff80,0xff6060,0xffaa00,0x8080ff,0xff80ff];
for(let i=0;i<24;i++){
const h=.3+Math.random()*1.8;
const c=new THREE.Mesh(new THREE.ConeGeometry(.08+Math.random()*.22,h,6),new THREE.MeshPhongMaterial({color:cc[Math.floor(Math.random()*cc.length)],emissive:0x110011,transparent:true,opacity:.9}));
c.position.set((Math.random()-.5)*28,-9.5+h/2,(Math.random()-.5)*28);
c.rotation.y=Math.random()*Math.PI; c.rotation.z=(Math.random()-.5)*.3; scene.add(c);
}
// Particles
const N=3000,pg=new THREE.BufferGeometry(),pp=new Float32Array(N*3),pc=new Float32Array(N*3);
for(let i=0;i<N;i++){pp[i*3]=(Math.random()-.5)*40;pp[i*3+1]=-10+Math.random()*14;pp[i*3+2]=(Math.random()-.5)*40;const c=Math.random();pc[i*3]=c*.1;pc[i*3+1]=c*.8+.2;pc[i*3+2]=c*.6+.4;}
pg.setAttribute('position',new THREE.BufferAttribute(pp,3)); pg.setAttribute('color',new THREE.BufferAttribute(pc,3));
particles=new THREE.Points(pg,new THREE.PointsMaterial({size:.06,vertexColors:true,transparent:true,opacity:.8})); scene.add(particles);
// Bubbles
const BN=220,bg=new THREE.BufferGeometry(),bp=new Float32Array(BN*3);
for(let i=0;i<BN;i++){bp[i*3]=(Math.random()-.5)*28;bp[i*3+1]=-10+Math.random()*13;bp[i*3+2]=(Math.random()-.5)*28;}
bg.setAttribute('position',new THREE.BufferAttribute(bp,3));
bubbles=new THREE.Points(bg,new THREE.PointsMaterial({size:.13,color:0xaaddff,transparent:true,opacity:.4})); scene.add(bubbles);
// Creatures
for(let i=0;i<32;i++){
const m=new THREE.Mesh(new THREE.OctahedronGeometry(.15+Math.random()*.3,0),new THREE.MeshBasicMaterial({color:Math.random()>.5?0x00ffcc:0x7fff00,wireframe:true,transparent:true,opacity:.6}));
m.position.set((Math.random()-.5)*18,-8+Math.random()*9,(Math.random()-.5)*18);
m.userData={spd:.01+Math.random()*.03,r:2+Math.random()*5,a:Math.random()*Math.PI*2,sy:m.position.y};
scene.add(m); creatures.push(m);
}
}
function addLights(){
scene.add(new THREE.AmbientLight(0x001a2e,2));
const sun=new THREE.DirectionalLight(0x4488cc,1.5); sun.position.set(10,20,10); scene.add(sun);
[[0x00ffcc,0],[0x0088ff,1],[0x00ff44,2]].forEach(([c,i])=>{
const l=new THREE.PointLight(c,2,12); l.position.set(Math.cos(i*2.1)*8,-5+i*2,Math.sin(i*2.1)*8); scene.add(l);
});
}
function animate(){
requestAnimationFrame(animate);
const t=clock.getElapsedTime();
if(ocean){const p=ocean.geometry.attributes.position;for(let i=0;i<p.count;i++)p.setZ(i,Math.sin(p.getX(i)*.5+t)*.22+Math.cos(p.getY(i)*.5+t*.8)*.16);p.needsUpdate=true;}
if(particles){const p=particles.geometry.attributes.position;for(let i=0;i<p.count;i++){let y=p.getY(i)+.002;if(y>2)y=-10;p.setY(i,y);p.setX(i,p.getX(i)+Math.sin(t+i)*.001);}p.needsUpdate=true;particles.rotation.y=t*.005;}
if(bubbles){const p=bubbles.geometry.attributes.position;for(let i=0;i<p.count;i++){let y=p.getY(i)+.014+Math.random()*.01;if(y>2)y=-10;p.setY(i,y);}p.needsUpdate=true;}
creatures.forEach(c=>{c.userData.a+=c.userData.spd;c.position.x=Math.cos(c.userData.a)*c.userData.r;c.position.z=Math.sin(c.userData.a)*c.userData.r;c.position.y=c.userData.sy+Math.sin(t*.5+c.userData.a)*.5;c.rotation.x=t*.5;c.rotation.y=c.userData.a;});
camera.position.x=Math.sin(t*.05)*18; camera.position.z=Math.cos(t*.05)*18; camera.position.y=6+Math.sin(t*.03)*2; camera.lookAt(0,-2,0);
renderer.render(scene,camera);
}
function setZone(idx){
const z=ZONES[idx];
document.getElementById('dv').textContent=z.depth.toLocaleString()+' m';
document.getElementById('pv').textContent=z.pressure.toLocaleString()+' atm';
document.getElementById('tv').textContent=z.temp+'Β°C';
document.getElementById('spv').textContent=z.sp;
document.getElementById('bv').textContent=z.bio+'%';
document.getElementById('zd').textContent=z.name;
document.getElementById('df').style.height=((idx+1)*18)+'%';
scene.fog.color.setHex(z.fog); scene.fog.density=z.fogD;
ocean.material.color.setHex(z.color); ocean.material.opacity=Math.max(.3,.85-idx*.12);
renderer.setClearColor(z.fog,1);
const s=document.getElementById('sb');
s.textContent=`DIVING TO ${z.name} β€” ${z.depth.toLocaleString()}m`;
s.classList.add('show'); setTimeout(()=>s.classList.remove('show'),2500);
if(!titleFaded){document.getElementById('th').style.opacity='0';titleFaded=true;}
document.querySelectorAll('.zb').forEach((b,i)=>b.classList.toggle('active',i===idx));
}
function onResize(){const c=document.getElementById('cc');camera.aspect=c.clientWidth/c.clientHeight;camera.updateProjectionMatrix();renderer.setSize(c.clientWidth,c.clientHeight);}
window.addEventListener('load',init);
setInterval(()=>{const e=document.getElementById('sv');if(e)e.textContent=new Date().toUTCString().slice(17,25)+' UTC';},1000);
</script>
</body>
</html>
"""
CSS = """
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
body, .gradio-container { background:#000408!important; color:#00ffcc!important; font-family:'Share Tech Mono',monospace!important; }
.oceanus-header { background:linear-gradient(135deg,#000408,#001a2e); border-bottom:1px solid #00ffcc33; padding:14px 24px; text-align:center; }
.oceanus-title { font-family:'Orbitron',sans-serif; font-size:26px; font-weight:900; color:#00ffcc; text-shadow:0 0 30px #00ffcc; letter-spacing:7px; margin:0; }
.oceanus-sub { font-size:10px; color:rgba(0,255,204,.5); letter-spacing:4px; margin-top:4px; }
.gr-panel { background:#000408!important; border:1px solid #00ffcc22!important; border-radius:0!important; }
textarea, input { background:#000d1a!important; border:1px solid #00ffcc44!important; color:#00ffcc!important; font-family:'Share Tech Mono',monospace!important; border-radius:0!important; }
label, .label-wrap { color:#00ffcc88!important; font-size:10px!important; letter-spacing:2px!important; text-transform:uppercase!important; }
.gr-button { border-radius:0!important; font-family:'Share Tech Mono',monospace!important; letter-spacing:2px!important; }
.gr-button-primary { background:linear-gradient(135deg,#00ffcc22,#00ffcc44)!important; border:1px solid #00ffcc!important; color:#00ffcc!important; }
.gr-button-primary:hover { background:rgba(0,255,204,.3)!important; box-shadow:0 0 14px #00ffcc88!important; }
.gr-button-secondary { background:#000d1a!important; border:1px solid #00ffcc33!important; color:#00ffcc99!important; font-size:11px!important; }
.gr-button-secondary:hover { border-color:#00ffcc!important; color:#00ffcc!important; }
footer { display:none!important; }
"""
ZONE_CHOICES = ["Sunlit Zone (0-200m)","Twilight Zone (200-1000m)","Midnight Zone (1000-4000m)","Abyssal Zone (4000-6000m)","Hadal Zone (6000-11000m)"]
OCEAN_CHOICES = ["Pacific Ocean β€” Largest & Deepest","Atlantic Ocean β€” Mid-Atlantic Ridge","Indian Ocean β€” Warm Currents","Arctic Ocean β€” Permanent Ice Cap","Southern Ocean β€” Circumpolar Current"]
QUICK_Q = ["πŸ¦‘ What lives in the Twilight Zone?","πŸ’‘ How does bioluminescence work?","πŸŒ‹ What lives near hydrothermal vents?","πŸ”οΈ Describe the Mariana Trench","πŸ‹ How do whales dive so deep?","πŸ”¬ Most bizarre Midnight Zone creature?"]
with gr.Blocks(title="OCEANUS β€” AR Ocean Explorer") as demo:
gr.HTML('<div class="oceanus-header"><p class="oceanus-title">βš“ OCEANUS</p><p class="oceanus-sub">GOD\'S EYE β€” AR DEEP OCEAN EXPLORER Β· FREE Β· POWERED BY MISTRAL AI</p></div>')
gr.HTML(THREE_JS_HTML)
gr.HTML("<div style='height:14px'></div>")
with gr.Row():
with gr.Column(scale=3):
gr.HTML("<p style='font-family:Orbitron,sans-serif;color:#00ffcc;font-size:13px;letter-spacing:3px;margin-bottom:8px;'>🧠 OCEANUS AI ORACLE</p>")
# Gradio 6: type="messages" + list of dicts with role/content
chatbot = gr.Chatbot(
label="",
height=400,
show_label=False,
value=INITIAL_MSG,
)
with gr.Row():
msg = gr.Textbox(placeholder="Ask OCEANUS... e.g. 'What lives at 10,000 meters?'", show_label=False, scale=5, lines=1)
btn = gr.Button("β–Ά TRANSMIT", scale=1, variant="primary")
with gr.Column(scale=1):
gr.HTML("<p style='font-family:Orbitron,sans-serif;color:#00ffcc;font-size:11px;letter-spacing:3px;margin-bottom:8px;'>πŸ“‘ DIVE CONTROLS</p>")
zone_dd = gr.Dropdown(ZONE_CHOICES, value=ZONE_CHOICES[0], label="ACTIVE ZONE")
ocean_dd = gr.Dropdown(OCEAN_CHOICES, value=OCEAN_CHOICES[0], label="OCEAN REGION")
zone_md = gr.Markdown(get_zone_info(ZONE_CHOICES[0]))
gr.HTML("<p style='font-family:Orbitron,sans-serif;color:#00ffcc88;font-size:10px;letter-spacing:2px;margin:12px 0 8px;'>QUICK TRANSMISSIONS</p>")
for q in QUICK_Q:
gr.Button(q, size="sm", variant="secondary").click(
fn=lambda h, z, o, _q=q: quick_ask(_q, h, z, o),
inputs=[chatbot, zone_dd, ocean_dd],
outputs=[msg, chatbot],
)
zone_dd.change(fn=get_zone_info, inputs=zone_dd, outputs=zone_md)
btn.click(fn=chat_with_oceanus, inputs=[msg, chatbot, zone_dd, ocean_dd], outputs=[msg, chatbot])
msg.submit(fn=chat_with_oceanus, inputs=[msg, chatbot, zone_dd, ocean_dd], outputs=[msg, chatbot])
demo.launch(css=CSS)