Agent Template
ULTRON — Personal AI Command Center
A lightweight black-and-red personal AI dashboard with chat, configurable online API and local Ollama modes, browser voice controls, and local conversation history.
personal assistant
Ollama
offline chat
voice
dashboard
rithwik
Oct 20, 2026
1 use
Preview Mode
This is a preview with sample data. The template uses placeholders like
which will be replaced with actual agent data.
About This Template
ULTRON — Personal AI Command Center is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.
Topics
personal assistant
Ollama
offline chat
voice
dashboard
Template Preview
Template Metadata
- Slug
- ultron-personal-ai-command-center
- Created By
- rithwik
- Created
- Oct 20, 2026
- Usage Count
- 1
Tags
personal assistant
Ollama
offline chat
voice
dashboard
Code Statistics
- HTML Lines
- 8
- CSS Lines
- 1
- JS Lines
- 1
- Python Lines
- 90
Source Code
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>{{ agent_name }}</title><meta name="description" content="{{ description }}"><style>{{ css_code }}</style></head><body>
<div class="shell">
<header class="topbar"><div class="brand"><span class="brand-mark">◆</span><div><h1>ULTRON</h1><span>PERSONAL AI COMMAND CENTER</span></div></div><div class="header-actions"><span id="clock" class="clock">--:--:--</span><button id="settings-toggle" type="button" class="ghost">⚙️ Settings</button></div></header>
<div class="layout"><aside class="sidebar"><div class="section-label">CONVERSATIONS</div><button id="new-chat" type="button" class="outline full">+ New ULTRON chat</button><label for="conversation-select">Open conversation</label><select id="conversation-select"><option value="">No saved conversations</option></select><button id="delete-chat" type="button" class="ghost full">Delete current conversation</button><button id="clear-history" type="button" class="ghost danger full">Clear all stored history</button><div class="sidebar-note">🔒 Chats stay in this browser when storage is available. API keys are kept only in memory for this tab and are never saved with chats.</div></aside>
<main class="main"><section class="hero"><div class="orb-wrap" aria-hidden="true"><div class="orb"><div class="orb-core"></div></div></div><div class="hero-copy"><div class="eyebrow">SYSTEM INTERFACE · ACTIVE</div><h2>ULTRON systems online.</h2><p>A focused assistant for planning, writing, and answering questions. Responses come from your selected, working model—not a simulation.</p></div></section><section class="metrics" aria-label="System status"><div class="metric"><span>LOCAL TIME</span><strong id="time-status">--:--</strong></div><div class="metric"><span>AI MODE</span><strong id="mode-status">Not configured</strong></div><div class="metric"><span>CONNECTION</span><strong id="connection-status">Initializing</strong></div><div class="metric"><span>MODEL</span><strong id="model-status">Not selected</strong></div><div class="metric"><span>MICROPHONE</span><strong id="mic-status">Checking</strong></div></section>
<section class="chat-card"><div class="chat-head"><div><div class="section-label">ULTRON / CHAT</div><h3>Command channel</h3></div><button id="refresh-status" type="button" class="ghost">↻ Check connection</button></div><div id="results-container" class="messages" aria-live="polite"><div class="empty"><div class="empty-icon">◆</div><h3>Begin a conversation with ULTRON</h3><p>Configure a real model in Settings, then ask anything.</p><ul><li>💬 Keep conversations on this device</li><li>🔌 Connect an online API or local Ollama</li><li>🎙️ Dictate where browser speech input is supported</li><li>🔊 Hear replies using browser voices</li></ul></div></div><div id="chat-error" class="alert hidden" role="alert"></div><form id="chat-form" class="composer"><label class="sr-only" for="message-input">Message ULTRON</label><textarea id="message-input" rows="2" maxlength="6000" placeholder="Ask ULTRON a question or give an instruction…"></textarea><div class="composer-actions"><button id="mic-button" type="button" class="ghost" title="Dictate a message">🎙️ Dictate</button><button id="send-button" type="submit" class="primary">Send to ULTRON →</button></div></form></section></main>
<aside id="settings-panel" class="settings hidden"><div class="settings-header"><div><div class="section-label">CONFIGURATION</div><h3>ULTRON settings</h3></div></div><form id="settings-form"><label for="mode-select">AI mode</label><select id="mode-select"><option value="platform">AgentOp provider</option><option value="online">Online API (OpenAI-compatible)</option><option value="ollama">Offline Ollama</option></select><p class="hint">AgentOp uses the provider configured for this agent. Online API needs a compatible service; it may require an API key and payment. Ollama needs a running local server and a downloaded model.</p><label for="endpoint-input">Online API endpoint</label><input id="endpoint-input" type="url" placeholder="https://your-provider.example/v1" autocomplete="off"><label for="model-input">Model name</label><input id="model-input" type="text" placeholder="Enter the exact model ID" autocomplete="off"><label for="key-input">Online API key</label><input id="key-input" type="password" placeholder="Kept in this tab only" autocomplete="off"><label for="ollama-input">Local Ollama address</label><input id="ollama-input" type="url" placeholder="http://127.0.0.1:11434" autocomplete="off"><label for="ollama-models">Installed Ollama models</label><select id="ollama-models"><option value="">Check connection to list models</option></select><label for="voice-select">Answer voice</label><select id="voice-select"><option value="">Browser default voice</option></select><div class="voice-actions"><button id="mute-button" type="button" class="outline">🔊 Mute answers</button><button id="stop-speaking" type="button" class="outline">■ Stop speaking</button></div><label for="theme-select">Theme</label><select id="theme-select"><option value="red">Crimson black</option><option value="dim">Muted crimson</option></select><button id="save-settings" type="submit" class="primary full">Save ULTRON settings</button><div id="settings-message" class="hint" role="status"></div></form><div class="setup"><strong>Offline setup</strong><p>Install Ollama, download a model with <code>ollama pull <model></code>, and run <code>ollama serve</code>. Enter that model's exact name above and check the connection. Browser requests to localhost require Ollama to allow this page's origin (CORS). Only allow trusted origins; avoid a wildcard if possible.</p><p>Speech recognition may need browser permission and, in some browsers, internet. Spoken answers use installed browser/system voices. Initial AgentOp/Pyodide assets may also need to load or be cached before this page can operate fully offline.</p></div></aside></div></div><div id="loading-overlay" class="overlay hidden" role="status" aria-live="polite"><div class="loading-box"><div class="spinner"></div><span>ULTRON is processing…</span></div></div><script>{{ js_code }}</script></body></html>
:root{--bg:#08090c;--panel:#111318;--panel2:#17191e;--line:#303037;--text:#f2f0f0;--muted:#a7a1a3;--red:#eb3346;--red2:#a51228;--shadow:0 18px 54px #0008;--radius:16px;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color-scheme:dark}*{box-sizing:border-box}body{margin:0;min-height:100vh;background:radial-gradient(ellipse at 54% -15%,#43111d 0%,transparent 43%),var(--bg);color:var(--text)}body.dim{--red:#bd5361;--red2:#823645}button,input,select,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.43;cursor:not-allowed;box-shadow:none}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--red);outline-offset:3px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.shell{max-width:1600px;margin:auto;padding:24px}.topbar{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:6px 0 24px;border-bottom:1px solid var(--line)}.brand,.header-actions{display:flex;align-items:center;gap:18px}.brand-mark{color:var(--red);font-size:37px;text-shadow:0 0 24px var(--red)}h1,h2,h3,p{margin:0}h1{font-size:22px;letter-spacing:.33em}.brand span:not(.brand-mark),.section-label,.eyebrow{font-size:10px;letter-spacing:.22em;color:var(--red);font-weight:800}.clock{font-variant-numeric:tabular-nums;color:var(--muted)}.layout{display:grid;grid-template-columns:235px minmax(0,1fr) 330px;gap:22px;padding-top:24px;align-items:start}.sidebar,.settings,.chat-card,.metric{border:1px solid var(--line);background:linear-gradient(150deg,#17181d,#0f1014);border-radius:var(--radius);box-shadow:var(--shadow)}.sidebar,.settings{padding:20px}.sidebar{display:grid;gap:15px}.sidebar-note,.hint,.setup{font-size:12px;color:var(--muted);line-height:1.6}.sidebar-note{border-top:1px solid var(--line);padding-top:18px}label{display:block;font-size:12px;font-weight:700;color:#d3cacc}input,select,textarea{background:#0b0c10;color:var(--text);border:1px solid #414046;border-radius:10px;padding:11px 12px;width:100%;min-width:0}textarea{resize:vertical;min-height:56px;max-height:220px}input::placeholder,textarea::placeholder{color:#888287}button{border-radius:10px;padding:10px 13px;transition:background .18s ease,transform .18s ease}button:not(:disabled):hover{transform:translateY(-1px)}.full{width:100%}.primary{background:linear-gradient(130deg,var(--red),var(--red2));color:white;border:1px solid #fb6171;font-weight:750;box-shadow:0 6px 22px #bd15293b}.ghost{background:transparent;border:1px solid transparent;color:#d8d2d3}.ghost:hover{background:#ffffff10}.outline{background:#20171b;border:1px solid #60343c;color:#f4dfe1}.danger{color:#fa8996}.main{min-width:0}.hero{display:flex;gap:34px;align-items:center;min-height:230px;padding:22px 34px;border:1px solid #5b2530;border-radius:var(--radius);background:radial-gradient(circle at 18% 50%,#54162265,transparent 52%),linear-gradient(125deg,#1e131a,#101116);overflow:hidden}.orb-wrap{width:170px;height:170px;flex:none;display:grid;place-items:center}.orb{width:136px;height:136px;border:2px solid #ff5366;border-radius:50%;display:grid;place-items:center;box-shadow:0 0 15px #fc304b,0 0 45px #d9274866,inset 0 0 30px #d7244970;animation:breathe 4s ease-in-out infinite}.orb:before{content:"";position:absolute;width:103px;height:103px;border:1px dashed #fa6075;border-radius:50%;animation:turn 20s linear infinite}.orb-core{height:59px;width:59px;border-radius:50%;background:radial-gradient(circle,#ffd1d6,#f33c54 40%,#6b0a20 75%);box-shadow:0 0 25px #f33c54}.hero-copy h2{font-size:clamp(24px,3vw,38px);margin:12px 0}.hero-copy p{line-height:1.65;color:var(--muted);max-width:560px}.metrics{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:10px;margin:16px 0}.metric{padding:14px 10px;min-height:76px;box-shadow:none}.metric span{display:block;color:var(--muted);font-size:10px;letter-spacing:.08em;margin-bottom:8px}.metric strong{font-size:12px;overflow-wrap:anywhere}.chat-card{overflow:hidden}.chat-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:18px 22px;border-bottom:1px solid var(--line)}.chat-head h3,.settings h3{margin-top:6px;font-size:19px}.messages{height:min(44vh,460px);min-height:300px;overflow:auto;padding:21px;display:flex;flex-direction:column;gap:14px}.empty{margin:auto;max-width:420px;text-align:center;color:var(--muted);line-height:1.5}.empty-icon{font-size:27px;color:var(--red);margin-bottom:10px}.empty h3{color:var(--text);margin-bottom:7px}.empty ul{text-align:left;padding-left:30px;margin-top:16px;font-size:13px}.empty li{margin:7px 0}.message{max-width:86%;padding:11px 14px;border:1px solid var(--line);background:#1b1b21;border-radius:13px;white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.5;font-size:14px}.message.user{margin-left:auto;background:#35171f;border-color:#78313e}.message small{display:block;color:var(--red);font-size:10px;font-weight:800;letter-spacing:.13em;margin-bottom:5px}.alert{margin:0 20px 12px;padding:11px;border:1px solid #a44351;background:#4b1923;color:#ffced3;border-radius:9px;font-size:13px;overflow-wrap:anywhere}.composer{border-top:1px solid var(--line);padding:15px;display:grid;gap:10px}.composer-actions,.voice-actions{display:flex;justify-content:space-between;gap:8px}.settings-header{border-bottom:1px solid var(--line);padding-bottom:14px;margin-bottom:16px}.settings form{display:grid;gap:10px}.settings .hint{margin:0 0 8px}.settings .voice-actions button{flex:1;font-size:12px}.setup{border-top:1px solid var(--line);margin-top:20px;padding-top:18px}.setup p{margin-top:9px}.setup code{color:#ffa6b1;overflow-wrap:anywhere}.overlay{position:fixed;inset:0;background:#05050894;display:grid;place-items:center;z-index:20}.loading-box{background:#18151a;border:1px solid #9b3746;padding:22px 34px;border-radius:14px;display:flex;align-items:center;gap:16px;box-shadow:var(--shadow)}.spinner{width:20px;height:20px;border:2px solid #672e3a;border-top-color:var(--red);border-radius:50%;animation:turn .8s linear infinite}@keyframes turn{to{transform:rotate(360deg)}}@keyframes breathe{50%{transform:scale(1.045);box-shadow:0 0 22px #fc304b,0 0 65px #d9274899,inset 0 0 36px #d72449}}@media(prefers-reduced-motion:reduce){*,*:before{animation:none!important;transition:none!important}}@media(max-width:1200px){.layout{grid-template-columns:215px minmax(0,1fr)}.settings{grid-column:1/-1}.metrics{grid-template-columns:repeat(3,1fr)}}@media(max-width:768px){.shell{padding:13px}.layout{grid-template-columns:1fr;gap:15px}.sidebar,.settings{grid-column:auto}.hero{padding:20px;min-height:unset;gap:12px}.orb-wrap{width:85px;height:90px}.orb{width:72px;height:72px}.orb:before{width:55px;height:55px}.orb-core{width:29px;height:29px}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.brand span:not(.brand-mark),.clock{display:none}.chat-head{padding:12px}.chat-head button{font-size:11px}.messages{min-height:300px;height:42vh}}
(()=>{'use strict';const $=id=>document.getElementById(id);const defaults={mode:'platform',endpoint:'',model:'',ollama:'http://127.0.0.1:11434',voice:'',muted:false,theme:'red'};let cfg={...defaults},key='',chats=[],active='',ready=false,busy=false,recognition=null,voices=[],storageOK=true;const escapeHtml=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));function message(id,text){$(id).textContent=text||'';$(id).classList.toggle('hidden',!text)}function error(t){message('chat-error',t)}function persist(){try{localStorage.setItem('ultron-config-v1',JSON.stringify({...cfg,key:undefined}));localStorage.setItem('ultron-chats-v1',JSON.stringify(chats.slice(-30)))}catch(e){storageOK=false;$('settings-message').textContent='Local storage is unavailable; chats will be lost when this tab closes.'}}function sync(){try{cfg={...defaults,...JSON.parse(localStorage.getItem('ultron-config-v1')||'{}')};chats=JSON.parse(localStorage.getItem('ultron-chats-v1')||'[]');if(!Array.isArray(chats))chats=[];chats=chats.filter(x=>x&&typeof x.id==='string'&&Array.isArray(x.messages)).slice(-30);active=chats.at(-1)?.id||''}catch(e){cfg={...defaults};chats=[];storageOK=false}['mode','endpoint','model','ollama','voice','theme'].forEach(n=>{const el=$(n+(n==='mode'||n==='theme'||n==='voice'?'-select':'-input'));if(el)el.value=cfg[n]||''});document.body.classList.toggle('dim',cfg.theme==='dim');updateConversations();render();status()}function current(){return chats.find(x=>x.id===active)}function updateConversations(){const sel=$('conversation-select');sel.innerHTML=chats.length?chats.map(c=>'<option value="'+escapeHtml(c.id)+'">'+escapeHtml((c.title||'ULTRON chat').slice(0,48))+'</option>').join(''):'<option value="">No saved conversations</option>';sel.value=active||'';$('delete-chat').disabled=!active}function render(){const log=current()?.messages||[];const box=$('results-container');if(!log.length){box.innerHTML='<div class="empty"><div class="empty-icon">◆</div><h3>Begin a conversation with ULTRON</h3><p>Configure a real model in Settings, then ask anything.</p><ul><li>💬 Keep conversations on this device</li><li>🔌 Connect an online API or local Ollama</li><li>🎙️ Dictate where browser speech input is supported</li><li>🔊 Hear replies using browser voices</li></ul></div>';return}box.innerHTML=log.map(m=>'<div class="message '+(m.role==='user'?'user':'assistant')+'"><small>'+(m.role==='user'?'YOU':'ULTRON')+'</small>'+escapeHtml(m.content)+'</div>').join('');box.scrollTop=box.scrollHeight}window.addMessage=(type,content)=>{if(type==='assistant'&&typeof content==='string'&&content.trim()){const c=current();if(c){c.messages.push({role:'assistant',content});persist();render()}}};function status(){const local=window.PROVIDER==='local';$('mode-status').textContent=cfg.mode==='ollama'?'Offline Ollama':cfg.mode==='online'?'Online API':'AgentOp '+(local?'local':'provider');$('model-status').textContent=cfg.mode==='platform'?(local?(window.agentManager?.isLoaded?'Local model loaded':'Load model above'):'Provider-managed'):(cfg.model||'Model required');$('connection-status').textContent=!ready?'Initializing':cfg.mode==='ollama'?'Not checked':cfg.mode==='online'?(cfg.endpoint&&cfg.model?'Configured · unverified':'Configure API'):(local&&!window.agentManager?.isLoaded?'Model not loaded':'Ready · unverified');$('mic-status').textContent=recognition?'Listening':((window.SpeechRecognition||window.webkitSpeechRecognition)?'Permission on use':'Unsupported');$('mute-button').textContent=cfg.muted?'🔇 Unmute answers':'🔊 Mute answers'}function formToCfg(){cfg.mode=$('mode-select').value;cfg.endpoint=$('endpoint-input').value.trim();cfg.model=$('model-input').value.trim();cfg.ollama=$('ollama-input').value.trim()||defaults.ollama;cfg.voice=$('voice-select').value;cfg.theme=$('theme-select').value;key=$('key-input').value.trim();document.body.classList.toggle('dim',cfg.theme==='dim');persist();status()}function preflight(){if(!ready||!window.pyodide)throw Error('ULTRON is initializing. Wait for the runtime to load.');if(cfg.mode==='platform'&&window.PROVIDER==='local'&&!(window.agentManager&&window.agentManager.isLoaded))throw Error('Load a local model from the AgentOp model bar above before chatting.');if(cfg.mode==='online'&&(!cfg.endpoint||!cfg.model))throw Error('Set the online API endpoint and exact model name in Settings. Your service may also require an API key and payment.');if(cfg.mode==='ollama'&&!cfg.model)throw Error('Select or enter an installed Ollama model in Settings.')}function setPy(name,val){window.pyodide.globals.set(name,val)}function pyCfg(){return JSON.stringify({...cfg,key})}async function execute(source){return await window.pyodide.runPythonAsync(source)}function setBusy(v){busy=v;$('send-button').disabled=v;$('loading-overlay').classList.toggle('hidden',!v)}async function send(e){e.preventDefault();if(busy)return;const q=$('message-input').value.trim();error('');if(!q){error('Write a message for ULTRON before sending.');return}try{preflight()}catch(ex){error(ex.message);return}let c=current();if(!c){c={id:String(Date.now())+'-'+Math.random().toString(36).slice(2),title:q.slice(0,43),messages:[]};chats.push(c);active=c.id}const previous=c.messages.slice(-8);c.messages.push({role:'user',content:q});$('message-input').value='';updateConversations();render();persist();setBusy(true);$('connection-status').textContent='Processing';try{setPy('user_query',q);setPy('ultron_settings_json',pyCfg());setPy('ultron_history_json',JSON.stringify(previous));const raw=await execute('await process_user_query(user_query)');const text=String(raw??'').trim();if(!text)throw Error('The model returned an empty response. Check its status and try again.');if(!c.messages.some(m=>m.role==='assistant'&&m.content===text&&m===c.messages.at(-1)))c.messages.push({role:'assistant',content:text});persist();if(active===c.id)render();$('connection-status').textContent='Responding';speak(text)}catch(ex){error('ULTRON could not respond: '+(ex?.message||String(ex)));$('connection-status').textContent='Request failed'}finally{setBusy(false)}}async function refresh(){if(!ready){error('Runtime still initializing.');return}if(cfg.mode!=='ollama'){status();$('settings-message').textContent=cfg.mode==='online'?'Configured is not a verified connection. Send a message to test your API.':'Send a message to test the selected provider.';return}if(busy)return;setBusy(true);$('connection-status').textContent='Checking Ollama';try{setPy('ultron_settings_json',pyCfg());const raw=await execute('await _list_ollama_models()');const models=JSON.parse(String(raw));const sel=$('ollama-models');sel.innerHTML='<option value="">Select an installed model</option>'+models.map(n=>'<option value="'+escapeHtml(n)+'">'+escapeHtml(n)+'</option>').join('');sel.value=models.includes(cfg.model)?cfg.model:'';$('connection-status').textContent='Ollama reachable';$('settings-message').textContent=models.length?models.length+' installed Ollama model(s) found.':'Ollama is reachable, but no models are installed. Run ollama pull followed by a model name.';error('')}catch(ex){$('connection-status').textContent='Ollama unavailable';error('Cannot reach Ollama from this browser: '+(ex?.message||String(ex))+' Check ollama serve, the address, and Ollama CORS permissions.')}finally{setBusy(false)}}function listVoices(){if(!window.speechSynthesis)return;voices=window.speechSynthesis.getVoices();const sel=$('voice-select');sel.innerHTML='<option value="">Browser default voice</option>'+voices.map(v=>'<option value="'+escapeHtml(v.voiceURI)+'">'+escapeHtml(v.name+' ('+v.lang+')')+'</option>').join('');sel.value=cfg.voice;if(sel.value!==cfg.voice)sel.value=''}function speak(text){if(cfg.muted||!window.speechSynthesis)return;window.speechSynthesis.cancel();const utter=new SpeechSynthesisUtterance(text.slice(0,1800));utter.rate=1;utter.voice=voices.find(v=>v.voiceURI===cfg.voice)||null;window.speechSynthesis.speak(utter)}function toggleMic(){const Speech=window.SpeechRecognition||window.webkitSpeechRecognition;if(!Speech){error('This browser does not support speech recognition. Type your message instead. Some supported browsers require internet for dictation.');return}if(recognition){recognition.stop();return}try{recognition=new Speech();recognition.lang=navigator.language||'en-US';recognition.interimResults=false;recognition.onresult=e=>{$('message-input').value=($('message-input').value+' '+e.results[0][0].transcript).trim();$('message-input').focus()};recognition.onerror=e=>error('Microphone: '+e.error+'. Allow microphone access in browser settings; dictation may require internet.');recognition.onend=()=>{recognition=null;status()};recognition.start();status()}catch(ex){recognition=null;error('Microphone unavailable: '+(ex.message||String(ex)));status()}}function bind(){ $('chat-form').addEventListener('submit',send);$('message-input').addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();$('chat-form').requestSubmit()}});$('settings-toggle').addEventListener('click',()=>$('settings-panel').classList.toggle('hidden'));$('settings-form').addEventListener('submit',e=>{e.preventDefault();formToCfg();$('settings-message').textContent=storageOK?'Settings saved locally; API key remains only in this tab.':'Settings active in this tab; browser storage is unavailable.';error('')});$('mode-select').addEventListener('change',()=>{cfg.mode=$('mode-select').value;status()});$('refresh-status').addEventListener('click',refresh);$('ollama-models').addEventListener('change',e=>{if(e.target.value){$('model-input').value=e.target.value;cfg.model=e.target.value;persist();status()}});$('new-chat').addEventListener('click',()=>{active='';updateConversations();render();error('');$('message-input').focus()});$('conversation-select').addEventListener('change',e=>{active=e.target.value;render();error('')});$('delete-chat').addEventListener('click',()=>{if(!active||!confirm('Delete this ULTRON conversation?'))return;chats=chats.filter(c=>c.id!==active);active=chats.at(-1)?.id||'';persist();updateConversations();render()});$('clear-history').addEventListener('click',()=>{if(!confirm('Permanently clear all ULTRON conversations stored in this browser?'))return;chats=[];active='';persist();updateConversations();render();error('')});$('mic-button').addEventListener('click',toggleMic);$('voice-select').addEventListener('change',e=>{cfg.voice=e.target.value;persist()});$('mute-button').addEventListener('click',()=>{cfg.muted=!cfg.muted;if(cfg.muted)window.speechSynthesis?.cancel();persist();status()});$('stop-speaking').addEventListener('click',()=>window.speechSynthesis?.cancel());$('theme-select').addEventListener('change',e=>{cfg.theme=e.target.value;document.body.classList.toggle('dim',cfg.theme==='dim');persist()});window.speechSynthesis?.addEventListener?.('voiceschanged',listVoices);$('mic-button').disabled=!(window.SpeechRecognition||window.webkitSpeechRecognition);if($('mic-button').disabled)$('mic-button').title='Speech recognition is unsupported in this browser';if(!window.speechSynthesis){$('mute-button').disabled=true;$('stop-speaking').disabled=true;$('voice-select').disabled=true}}bind();sync();listVoices();function tick(){const now=new Date();$('clock').textContent=now.toLocaleTimeString();$('time-status').textContent=now.toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'})}tick();setInterval(tick,1000);if(window.pyodideReady){ready=true;status()}else document.addEventListener('pyodide-ready',()=>{ready=true;status()},{once:true});})();
import json
from urllib.parse import urlsplit
from pyodide.http import pyfetch
def _config():
return json.loads(globals().get('ultron_settings_json', '{}'))
def _base_ollama(cfg):
address = str(cfg.get('ollama') or 'http://127.0.0.1:11434').strip().rstrip('/')
parsed = urlsplit(address)
if parsed.scheme not in ('http', 'https') or parsed.hostname not in ('localhost', '127.0.0.1', '::1') or parsed.username or parsed.password or parsed.path not in ('', '/') or parsed.query or parsed.fragment:
raise ValueError('Ollama address must be a local http(s) address, such as http://127.0.0.1:11434.')
return address
async def _request_json(url, payload=None, key=''):
headers = {'Accept': 'application/json'}
if payload is not None:
headers['Content-Type'] = 'application/json'
if key:
headers['Authorization'] = 'Bearer ' + key
try:
kwargs = {'method': 'POST' if payload is not None else 'GET', 'headers': headers}
if payload is not None:
kwargs['body'] = json.dumps(payload)
response = await pyfetch(url, **kwargs)
if not response.ok:
raise ValueError('HTTP ' + str(response.status) + '. Check the address, model, credentials, service billing, and browser CORS permissions.')
return json.loads(await response.string())
except ValueError:
raise
except Exception as exc:
raise RuntimeError('Network request failed. Check that the server is running and allows requests from this page (CORS). Details: ' + str(exc)[:180]) from exc
async def _list_ollama_models():
data = await _request_json(_base_ollama(_config()) + '/api/tags')
return json.dumps([item['name'] for item in data.get('models', []) if isinstance(item, dict) and isinstance(item.get('name'), str)])
async def process_user_query(query: str) -> str:
"""Answer through the selected real AI model.
Args:
query: The user's latest chat message.
"""
cfg = _config()
mode = cfg.get('mode', 'platform')
question = query.strip()[:6000]
if not question:
raise ValueError('Write a message first.')
try:
history = json.loads(globals().get('ultron_history_json', '[]'))
except (ValueError, TypeError):
history = []
messages = []
for item in history[-8:] if isinstance(history, list) else []:
if isinstance(item, dict) and item.get('role') in ('user', 'assistant') and isinstance(item.get('content'), str):
messages.append({'role': item['role'], 'content': item['content'][:1200]})
messages.append({'role': 'user', 'content': question})
if mode == 'platform':
transcript = '\n'.join(('USER' if m['role'] == 'user' else 'ASSISTANT') + ': ' + m['content'] for m in messages)
prompt = 'Conversation so far:\n' + transcript[-6500:] + '\nASSISTANT:'
return str(await agentop_llm.generate(prompt, globals().get('TEMPLATE_SYSTEM_PROMPT', '')))
model = str(cfg.get('model') or '').strip()
if not model:
raise ValueError('Enter the exact model name in Settings.')
system = 'You are ULTRON, a concise, professional, confident futuristic assistant. Do not claim consciousness or abilities you do not have. Never fabricate system metrics or pretend a failed model call succeeded.'
payload_messages = [{'role': 'system', 'content': system}] + messages
if mode == 'ollama':
result = await _request_json(_base_ollama(cfg) + '/api/chat', {'model': model, 'messages': payload_messages, 'stream': False})
answer = (result.get('message') or {}).get('content', '')
elif mode == 'online':
endpoint = str(cfg.get('endpoint') or '').strip().rstrip('/')
parsed = urlsplit(endpoint)
if parsed.scheme != 'https' and not (parsed.scheme == 'http' and parsed.hostname in ('localhost', '127.0.0.1', '::1')):
raise ValueError('Online API endpoint must use HTTPS, except for localhost.')
if not parsed.netloc or parsed.username or parsed.password or parsed.query or parsed.fragment:
raise ValueError('Enter a valid online API endpoint without embedded credentials or query parameters.')
url = endpoint if endpoint.endswith('/chat/completions') else endpoint + ('/chat/completions' if endpoint.endswith('/v1') else '/v1/chat/completions')
result = await _request_json(url, {'model': model, 'messages': payload_messages, 'stream': False}, str(cfg.get('key') or ''))
choices = result.get('choices') or []
answer = ((choices[0].get('message') or {}).get('content') or '') if choices else ''
else:
raise ValueError('Select a supported AI mode in Settings.')
if not isinstance(answer, str) or not answer.strip():
raise ValueError('The selected model returned no text. Check its model name and service status.')
return answer.strip()
Name your agent
Based on . You'll get your own private copy to try and customize.