Helikon

Von $1

    Inhaltsverzeichnis
    keine Gliederung

    Version seit 15:23, 11 Aug 2026

    zu dieser Version.

    Zurück zu Versionshistorie.

    Zeige aktuelle Version

    /* === HELIKON CHAT v3.2 === */ #serverBuddy { display: none !important; } #buddySpeech { display: none !important; } #buddyChat { display: flex; gap: 24px; align-items: flex-start; width: 100%; font-family: 'Press Start 2P', 'Courier New', Courier, monospace; font-size: 8px; line-height: 2; -webkit-font-smoothing: none; -moz-osx-font-smoothing: unset; font-smooth: never; text-rendering: optimizeSpeed; -webkit-backface-visibility: hidden; backface-visibility: hidden; color: #333; } #buddyChatMain { flex: 1; min-width: 0; } #buddyChatSide { width: 280px; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; position: sticky; top: 40px; } #buddyChatSide img { width: 280px; height: 280px; image-rendering: pixelated; image-rendering: crisp-edges; display: block; border: none; cursor: pointer; transition: transform 0.2s ease; } #buddyChatSide img:hover { transform: scale(1.03); } #buddyBubble { transform: scale(0); background: #fff; color: #333; padding: 12px 18px; border-radius: 14px; border: 2px solid #ccc; font-family: 'Press Start 2P', 'Courier New', Courier, monospace; font-size: 8px; line-height: 2; -webkit-font-smoothing: none; -moz-osx-font-smoothing: unset; font-smooth: never; text-rendering: optimizeSpeed; max-width: 280px; text-align: center; white-space: pre-wrap; word-wrap: break-word; opacity: 0; transition: opacity 0.3s ease, transform 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 12px; } #buddyBubble.visible { opacity: 1; transform: scale(1); } #buddyBubble::after { content: ''; position: absolute; bottom: -10px; left: 50%; transform: translateX(-50%); width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-top: 10px solid #ccc; } /* === CHAT LOG === */ #chatLog { min-height: 400px; max-height: 600px; overflow-y: auto; padding: 20px; margin-bottom: 16px; border: 2px solid #ccc; background: #f8f8f8; } .chatMsg { margin-bottom: 14px; padding: 14px 18px; border-radius: 8px; font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; font-size: 16px; line-height: 1.65; max-width: 90%; word-wrap: break-word; white-space: pre-wrap; -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; font-smooth: auto; text-rendering: auto; } .chatMsg.user { background: #fff; color: #222; margin-left: auto; text-align: right; border: 1px solid #ccc; border-radius: 8px 8px 2px 8px; } .chatMsg.buddy { background: #4caf50; color: #fff; margin-right: auto; border: 1px solid #43a047; border-radius: 8px 8px 8px 2px; } .chatMsg.buddy.claude { background: #d97706; border-color: #b45309; } .chatMsg.buddy .provTag { display: block; font-size: 9px; opacity: 0.85; margin-bottom: 4px; font-family: 'Segoe UI', sans-serif; font-weight: 600; letter-spacing: 0.5px; } .chatMsg.buddy a { color: #fff; text-decoration: underline; text-decoration-color: rgba(255,255,255,0.5); } .chatMsg.buddy a:hover { color: #fff; text-decoration-color: #fff; } .chatMsg.system { color: #999; text-align: center; margin: 8px auto; max-width: 100%; font-family: 'Press Start 2P', 'Courier New', Courier, monospace; font-size: 8px; line-height: 2; -webkit-font-smoothing: none; font-smooth: never; text-rendering: optimizeSpeed; } .chatMsg.typing { color: #999; opacity: 0.6; background: #f0f0f0 !important; border-color: #ddd !important; } .chatMsg a { color: #0077aa; text-decoration: underline; } .chatMsg a:hover { color: #005580; } /* === INPUT === */ #chatInputWrap { display: flex; gap: 8px; border: 2px solid #ccc; background: #f8f8f8; padding: 10px; } #chatInput { flex: 1; background: #fff; border: 1px solid #ccc; color: #333; font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; font-size: 16px; line-height: 1.6; padding: 12px 14px; outline: none; } #chatInput::placeholder { color: #aaa; } #chatInput:focus { border-color: #0077aa; } #chatSend { background: #fff; color: #0077aa; border: 1px solid #0077aa; padding: 12px 24px; font-family: 'Press Start 2P', 'Courier New', Courier, monospace; font-size: 8px; -webkit-font-smoothing: none; font-smooth: never; cursor: pointer; } #chatSend:hover { background: #0077aa; color: #fff; } #chatSend:disabled { opacity: 0.3; cursor: default; } /* === FOOTER === */ #chatFooter { display: flex; justify-content: space-between; align-items: center; margin-top: 8px; flex-wrap: wrap; gap: 8px; } #chatStatus { font-size: 8px; color: #999; } .chatBtn { background: none; border: 1px solid #ccc; color: #888; padding: 6px 14px; font-family: 'Press Start 2P', 'Courier New', Courier, monospace; font-size: 8px; -webkit-font-smoothing: none; font-smooth: never; cursor: pointer; border-radius: 4px; } .chatBtn:hover { background: #f0f0f0; color: #333; border-color: #999; } .chatBtn.active { background: #0077aa; color: #fff; border-color: #0077aa; } .chatBtn:disabled { opacity: 0.3; cursor: default; } #llmSwitch { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; } /* === AI STATS PANEL === */ #aiStats { display: none; margin-top: 16px; padding: 16px; border: 2px solid #0077aa; background: #f0f8ff; border-radius: 8px; font-size: 8px; line-height: 2.2; color: #333; width: 100%; } #aiStats.visible { display: block; } .statsTitle { font-size: 8px; color: #0077aa; margin: 0 0 8px 0; padding-bottom: 6px; border-bottom: 1px solid #ccc; } .statRow { display: flex; justify-content: space-between; padding: 3px 0; } .statLabel { color: #666; } .statValue { color: #333; text-align: right; } .statSection { margin-top: 10px; padding-top: 6px; border-top: 1px solid #e0e0e0; } .energyBar { display: inline-block; height: 6px; background: #4caf50; margin-left: 6px; vertical-align: middle; border-radius: 3px; } .scoreBox { text-align: center; padding: 14px; margin-bottom: 10px; border: 2px solid #0077aa; border-radius: 8px; background: #e8f4ff; } .scoreNum { font-size: 24px; color: #0077aa; display: block; margin: 6px 0; } .scoreLabel { font-size: 8px; color: #666; } .scoreGrade { font-size: 8px; margin-top: 4px; } .statIcon { display: inline-block; width: 16px; text-align: center; margin-right: 4px; font-style: normal; } /* === PROMPT VIEWER === */ #promptViewer { display: none; margin-top: 16px; padding: 16px; border: 2px solid #d97706; background: #fffbf0; border-radius: 8px; font-size: 8px; line-height: 2; color: #333; max-height: 300px; overflow-y: auto; width: 100%; white-space: pre-wrap; word-wrap: break-word; } #promptViewer.visible { display: block; } .promptTitle { font-size: 8px; color: #d97706; margin: 0 0 12px 0; padding-bottom: 8px; border-bottom: 1px solid #ddd; } /* === COPY CONTEXT MENU === */ #chatCopyMenu { display: none; position: fixed; z-index: 9999; background: #fff; border: 1px solid #ccc; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.15); padding: 4px 0; font-family: 'Segoe UI', sans-serif; font-size: 13px; min-width: 160px; } #chatCopyMenu.visible { display: block; } .copyMenuItem { padding: 8px 16px; cursor: pointer; color: #333; display: flex; align-items: center; gap: 8px; } .copyMenuItem:hover { background: #f0f0f0; } .copyMenuItem .cmIcon { width: 16px; text-align: center; } /* === MOBILE === */ @media (max-width: 768px) { #buddyChat { flex-direction: column-reverse; } #buddyChatSide { width: 100%; flex-direction: row; justify-content: center; position: static; } #buddyChatSide img { width: 120px; height: 120px; } .chatMsg { font-size: 15px; } #chatInput { font-size: 15px; } } Lade... Send initialisiere...Chat loeschen Export (function() { var fontLink = document.createElement('link'); fontLink.rel = 'stylesheet'; fontLink.href = 'https://fonts.googleapis.com/css2?fa...Press+Start+2P'; document.head.appendChild(fontLink); /* === CONFIG === */ var SHORT_LEN = 100; var ENERGY_PER_TOKEN = 0.00004; var LOG_URL = '/helikon-logger.php'; /* Runtime tag builder */ var LT = String.fromCharCode(60); var GT = String.fromCharCode(62); function tag(name, cls, content) { var o = LT + name; if (cls) o += ' class="' + cls + '"'; o += GT; return o + (content || '') + LT + '/' + name + GT; } function tagS(name, cls, style, content) { var o = LT + name; if (cls) o += ' class="' + cls + '"'; if (style) o += ' style="' + style + '"'; o += GT; return o + (content || '') + LT + '/' + name + GT; } /* === DOM === */ var log = document.getElementById('chatLog'); var input = document.getElementById('chatInput'); var btn = document.getElementById('chatSend'); var status = document.getElementById('chatStatus'); var avatar = document.getElementById('buddyChatImg'); var bubble = document.getElementById('buddyBubble'); var clearBtn = document.getElementById('chatClear'); var exportBtn = document.getElementById('chatExport'); var switchDiv = document.getElementById('llmSwitch'); var statsDiv = document.getElementById('aiStats'); var promptDiv = document.getElementById('promptViewer'); if (!log || !input || !btn) return; log.innerHTML = ''; var GIFS = new Object(); GIFS.idle = '/@api/deki/files/403/=buddy_idle.gif'; GIFS.sleep = '/@api/deki/files/402/=buddy_sleeping.gif'; GIFS.happy = '/@api/deki/files/401/=buddy_happy.gif'; GIFS.error = '/@api/deki/files/400/=buddy_error.gif'; GIFS.loading = '/@api/deki/files/399/=buddy_loading.gif'; /* === STATE === */ var history = []; var busy = false; var sysMsg = ''; var staticInfo = ''; var bubbleTimer = null; var statsVisible = false; var promptVisible = false; /* === SESSION STATS === */ var ss = new Object(); ss.tokIn = 0; ss.tokOut = 0; ss.chIn = 0; ss.chOut = 0; ss.reqs = 0; ss.timeMs = 0; ss.lastMs = 0; ss.start = Date.now(); ss.model = ''; ss.provider = ''; ss.errors = 0; /* === PROVIDERS === */ var providers = new Object(); providers.p1 = { name: 'Local', url: '', key: '', model: 'default', ready: false, type: 'openai' }; providers.p2 = { name: 'Claude', url: '', key: '', model: 'claude-sonnet-4-20250514', ready: false, type: 'anthropic' }; var activeProvider = 'p1'; var dualMode = false; /* === HELPERS === */ function setGif(name) { if (avatar) avatar.src = GIFS[name] || GIFS.idle; } function showBubble(txt, duration) { if (!bubble) return; if (bubbleTimer) clearTimeout(bubbleTimer); bubble.textContent = txt; bubble.className = 'visible'; bubbleTimer = setTimeout(function() { bubble.className = ''; }, duration || 5000); } function addMsg(text, cls, raw) { var div = document.createElement('div'); div.className = 'chatMsg ' + cls; if (raw) { div.innerHTML = text; } else { div.innerHTML = linkify(text); } log.appendChild(div); log.scrollTop = log.scrollHeight; return div; } /* === COPY CONTEXT MENU === */ var copyMenu = document.getElementById('chatCopyMenu'); var copyTarget = null; function hideCopyMenu() { if (copyMenu) copyMenu.className = ''; } if (copyMenu) { document.addEventListener('click', hideCopyMenu); document.addEventListener('scroll', hideCopyMenu, true); log.addEventListener('contextmenu', function(e) { var el = e.target; while (el && el !== log) { if (el.className && el.className.indexOf('chatMsg') !== -1) { e.preventDefault(); copyTarget = el; var items = ''; items += LT + 'div class="copyMenuItem" data-action="text"' + GT; items += LT + 'span class="cmIcon"' + GT + '\u2398' + LT + '/span' + GT + 'Text kopieren'; items += LT + '/div' + GT; if (el.textContent.indexOf('```') !== -1 || el.textContent.indexOf('function') !== -1 || el.textContent.indexOf('var ') !== -1) { items += LT + 'div class="copyMenuItem" data-action="code"' + GT; items += LT + 'span class="cmIcon"' + GT + '\u2699' + LT + '/span' + GT + 'Code kopieren'; items += LT + '/div' + GT; } copyMenu.innerHTML = items; copyMenu.style.left = e.clientX + 'px'; copyMenu.style.top = e.clientY + 'px'; copyMenu.className = 'visible'; return; } el = el.parentNode; } }); copyMenu.addEventListener('click', function(e) { var el = e.target; while (el && el !== copyMenu) { if (el.getAttribute && el.getAttribute('data-action')) { var action = el.getAttribute('data-action'); if (copyTarget) { var txt = ''; if (action === 'code') { var raw = copyTarget.textContent || ''; var lines = raw.split('\n'); var inCode = false; var codeLines = []; var li; for (li = 0; li < lines.length; li++) { if (lines[li].indexOf('```') !== -1) { inCode = !inCode; } else if (inCode) { codeLines.push(lines[li]); } } txt = codeLines.length > 0 ? codeLines.join('\n') : raw; } else { txt = copyTarget.textContent || ''; var provTag = copyTarget.querySelector('.provTag'); if (provTag) { txt = txt.replace(provTag.textContent, '').replace(/^\s+/, ''); } } if (navigator.clipboard) { navigator.clipboard.writeText(txt); showBubble('Kopiert!', 2000); } else { var ta = document.createElement('textarea'); ta.value = txt; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); showBubble('Kopiert!', 2000); } } hideCopyMenu(); return; } el = el.parentNode; } }); } function linkify(text) { var amp = String.fromCharCode(38); var escaped = text.replace(/&/g, amp + 'amp;') .replace(new RegExp(LT, 'g'), amp + 'lt;') .replace(new RegExp(GT, 'g'), amp + 'gt;'); return escaped.replace(/(\/[A-Za-z0-9_\-\/\.\?\=\+\:\#\%]+)/g, LT + 'a href="$1"' + GT + '$1' + LT + '/a' + GT); } function setStatus(txt) { if (status) status.textContent = txt || ''; } function estimateTokens(text) { return Math.ceil(text.length / 4); } /* === USERNAME === */ var currentUser = 'anon'; if (typeof Deki !== 'undefined') { if (Deki.UserName) currentUser = Deki.UserName; } var currentPage = window.location.pathname || '-'; /* === LOGGING === */ function logEvent(type, extra) { if (!LOG_URL) return; var payload = { type: type, user: currentUser, page: currentPage }; if (extra) { var k; for (k in extra) { if (extra.hasOwnProperty(k)) payload[k] = extra[k]; } } try { var xhr = new XMLHttpRequest(); xhr.open('POST', LOG_URL, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(payload)); } catch (e) {} } /* === FETCH HELPERS === */ function fetchText(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function() { callback(xhr.status === 200 ? xhr.responseText.replace(/^\s+|\s+$/g, '') : null); }; xhr.onerror = function() { callback(null); }; xhr.send(); } function fetchPageProps(callback) { var pid = 0; if (typeof Deki !== 'undefined') { if (Deki.PageId) pid = Deki.PageId; } if (!pid) { callback({}); return; } var url = '/@api/deki/pages/' + pid + '/properties'; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function() { var props = {}; if (xhr.status !== 200) { callback(props); return; } try { var parser = new DOMParser(); var xml = parser.parseFromString(xhr.responseText, 'text/xml'); var nodes = xml.getElementsByTagName('property'); var i; for (i = 0; i < nodes.length; i++) { var name = nodes[i].getAttribute('name') || ''; var contents = nodes[i].getElementsByTagName('contents'); if (contents.length > 0) { var val = (contents[0].textContent || '').replace(/^\s+|\s+$/g, ''); if (name.indexOf('buddy.systemprompt') !== -1) { props.systemprompt = val; } else if (name.indexOf('buddy.staticinfo') !== -1) { props.staticinfo = val; } else if (name.indexOf('buddy.apiurl2') !== -1) { props.apiurl2 = val; } else if (name.indexOf('buddy.apiurl') !== -1) { props.apiurl = val; } else if (name.indexOf('buddy.apikey2') !== -1) { props.apikey2 = val; } else if (name.indexOf('buddy.apikey') !== -1) { props.apikey = val; } else if (name.indexOf('buddy.model2') !== -1) { props.model2 = val; } else if (name.indexOf('buddy.name1') !== -1) { props.name1 = val; } else if (name.indexOf('buddy.name2') !== -1) { props.name2 = val; } } } } catch (e) {} callback(props); }; xhr.onerror = function() { callback({}); }; xhr.send(); } /* === CHAT CLEAR === */ if (clearBtn) { clearBtn.addEventListener('click', function() { if (busy) return; history = []; log.innerHTML = ''; ss.tokIn = 0; ss.tokOut = 0; ss.chIn = 0; ss.chOut = 0; ss.reqs = 0; ss.timeMs = 0; ss.lastMs = 0; ss.start = Date.now(); ss.errors = 0; setGif('happy'); showBubble('Chat geloescht!', 3000); logEvent('clear'); if (statsVisible) updateStats(); setTimeout(function() { setGif('idle'); }, 2000); }); } /* === EXPORT === */ if (exportBtn) { exportBtn.addEventListener('click', function() { if (history.length === 0) { showBubble('Nichts zum Exportieren!', 3000); return; } var text = '=== Helikon Chat Export ===\nDatum: ' + new Date().toLocaleString() + '\n\n'; var i; for (i = 0; i < history.length; i++) { var role = history[i].role === 'user' ? 'Du' : 'Helikon'; text += role + ': ' + history[i].content + '\n\n'; } var blob = new Blob([text], { type: 'text/plain;charset=utf-8' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'helikon-chat-' + Date.now() + '.txt'; a.click(); URL.revokeObjectURL(url); showBubble('Exportiert!', 3000); }); } /* === AI STATS PANEL === */ function sr(label, value) { return tag('div', 'statRow', tag('span', 'statLabel', label) + tag('span', 'statValue', value)); } function calcScore() { if (ss.reqs === 0) return { score: 0, grade: '-', color: '#999' }; var successRate = ss.reqs > 0 ? ((ss.reqs - ss.errors) / ss.reqs) : 0; var avgLatency = ss.timeMs / ss.reqs; var tokPerReq = (ss.tokIn + ss.tokOut) / ss.reqs; var latScore = avgLatency < 2000 ? 100 : (avgLatency < 5000 ? 70 : (avgLatency < 10000 ? 40 : 10)); var effScore = tokPerReq < 500 ? 100 : (tokPerReq < 1000 ? 70 : (tokPerReq < 2000 ? 40 : 10)); var raw = Math.round(successRate * 40 + latScore * 0.3 + effScore * 0.3); var grade, color; if (raw >= 90) { grade = '\u2B50 Prompt-Meister'; color = '#4caf50'; } else if (raw >= 75) { grade = '\u26A1 Speed-Runner'; color = '#8bc34a'; } else if (raw >= 60) { grade = '\u2705 Effizient'; color = '#ffc107'; } else if (raw >= 40) { grade = '\u23F3 Gemuetlich'; color = '#ff9800'; } else { grade = '\u2618 Energie-Verschwender'; color = '#f44336'; } return { score: raw, grade: grade, color: color }; } function updateStats() { if (!statsDiv) return; var elapsed = Math.floor((Date.now() - ss.start) / 1000); var mins = Math.floor(elapsed / 60); var secs = elapsed % 60; var totTok = ss.tokIn + ss.tokOut; var kwh = totTok * ENERGY_PER_TOKEN; var avgMs = ss.reqs > 0 ? Math.round(ss.timeMs / ss.reqs) : 0; var sc = calcScore(); var ico = function(i) { return tagS('em', 'statIcon', '', i); }; /* Score Box */ var h = tag('div', 'scoreBox', tag('span', 'scoreLabel', 'EFFIZIENZ-SCORE') + tagS('span', 'scoreNum', 'color:' + sc.color, '' + sc.score) + tagS('span', 'scoreGrade', 'color:' + sc.color, sc.grade) ); /* Session kompakt */ h += tag('div', 'statsTitle', ico('\u2699') + 'Session'); h += sr(ico('\u26A1') + 'Provider', ss.provider || '-'); h += sr(ico('\u23F1') + 'Dauer', mins + 'm ' + secs + 's'); /* Anfragen kompakt */ h += tag('div', 'statsTitle', ico('\u2709') + 'Anfragen'); h += sr(ico('\u2197') + 'Gesendet', '' + ss.reqs); h += sr(ico('\u23F3') + 'Avg Antwort', avgMs + ' ms'); /* Tokens mit Balken */ h += tag('div', 'statsTitle', ico('\u2B50') + 'Tokens'); h += sr(ico('\u25B2') + 'Rein', '' + ss.tokIn); h += sr(ico('\u25BC') + 'Raus', '' + ss.tokOut); h += sr(ico('\u2211') + 'Total', totTok + ' ' + tagS('span', 'energyBar', 'width:' + Math.min(totTok / 50, 150) + 'px', '')); /* Energie - eine Zeile */ h += tag('div', 'statsTitle', ico('\u2618') + 'Energie'); h += sr(ico('\u26A1') + 'Verbrauch', kwh.toFixed(6) + ' kWh'); var comparison = ''; if (kwh > 0) { var ledH = kwh / 0.01; var phoneX = kwh / 0.015; if (ledH < 0.01) { comparison = 'weniger als 1 Sek LED'; } else if (ledH < 1) { comparison = (ledH * 60).toFixed(0) + ' Min LED'; } else { comparison = ledH.toFixed(1) + ' Std LED'; } } else { comparison = 'noch nix verbraucht'; } h += sr(ico('\u2600') + 'Vergleich', comparison); statsDiv.innerHTML = h; } if (avatar) { avatar.addEventListener('click', function() { statsVisible = !statsVisible; if (statsVisible) { updateStats(); statsDiv.className = 'visible'; showBubble('Nerd-Modus!', 3000); } else { statsDiv.className = ''; } }); } /* === PROMPT VIEWER === */ function showPromptViewer() { if (!promptDiv) return; promptVisible = !promptVisible; if (promptVisible) { var full = sysMsg; if (staticInfo) full += '\n\n--- STATIC INFO ---\n' + staticInfo; var amp = String.fromCharCode(38); var escaped = full.replace(/&/g, amp + 'amp;').replace(new RegExp(LT, 'g'), amp + 'lt;').replace(new RegExp(GT, 'g'), amp + 'gt;'); promptDiv.innerHTML = tag('div', 'promptTitle', 'System Prompt (nur fuer Admins)') + escaped; promptDiv.className = 'visible'; showBubble('Prompt sichtbar!', 3000); logEvent('prompt_view'); } else { promptDiv.className = ''; } } /* === LLM SWITCH UI === */ function updateSwitchUI() { if (!switchDiv) return; switchDiv.innerHTML = ''; function makeBtn(id, label, isActive) { var b = document.createElement('button'); b.className = 'chatBtn' + (isActive ? ' active' : ''); b.id = id; b.textContent = label; return b; } if (providers.p1.ready) { var b1 = makeBtn('swP1', providers.p1.name, activeProvider === 'p1' && !dualMode); b1.addEventListener('click', function() { activeProvider = 'p1'; dualMode = false; updateSwitchUI(); showBubble(providers.p1.name + ' aktiv', 2000); logEvent('provider_switch', { message: providers.p1.name }); }); switchDiv.appendChild(b1); } if (providers.p2.ready) { var b2 = makeBtn('swP2', providers.p2.name, activeProvider === 'p2' && !dualMode); b2.addEventListener('click', function() { activeProvider = 'p2'; dualMode = false; updateSwitchUI(); showBubble(providers.p2.name + ' aktiv', 2000); logEvent('provider_switch', { message: providers.p2.name }); }); switchDiv.appendChild(b2); } if (providers.p1.ready && providers.p2.ready) { var bd = makeBtn('swDual', 'Dual', dualMode); bd.addEventListener('click', function() { dualMode = !dualMode; updateSwitchUI(); showBubble(dualMode ? 'Dual-Modus!' : providers[activeProvider].name + ' aktiv', 2000); logEvent('provider_switch', { message: dualMode ? 'Dual' : providers[activeProvider].name }); }); switchDiv.appendChild(bd); } } /* === SEND TO LLM === */ function buildFullPrompt() { var full = sysMsg || 'Du bist Helikon, ein freundlicher Dokumentations-Assistent.'; if (staticInfo) full += '\n\n' + staticInfo; return full; } function handleReply(reply, provName, provType) { history.push({ role: 'assistant', content: reply }); var cls = 'buddy'; if (provType === 'anthropic') cls += ' claude'; var provTag = LT + 'span class="provTag"' + GT + provName + LT + '/span' + GT; if (reply.length <= SHORT_LEN && !dualMode) { showBubble(reply, 6000); addMsg(provTag + linkify(reply), cls, true); } else { addMsg(provTag + linkify(reply), cls, true); var dot = reply.indexOf('.'); if (dot > 0 && dot < 80) { showBubble(reply.substring(0, dot + 1), 5000); } } } function callProvider(prov, userMsg, callback) { var fullPrompt = buildFullPrompt(); var startTime = Date.now(); ss.chIn += userMsg.length; ss.tokIn += estimateTokens(fullPrompt + userMsg); ss.provider = prov.name; ss.model = prov.model; var xhr = new XMLHttpRequest(); xhr.timeout = 30000; xhr.open('POST', prov.url, true); xhr.setRequestHeader('Content-Type', 'application/json'); var body; if (prov.type === 'anthropic') { xhr.setRequestHeader('x-api-key', prov.key); xhr.setRequestHeader('anthropic-version', '2023-06-01'); xhr.setRequestHeader('anthropic-dangerous-direct-browser-access', 'true'); var msgs = []; var mi; for (mi = 0; mi < history.length; mi++) { msgs.push(history[mi]); } body = JSON.stringify({ model: prov.model, system: fullPrompt, messages: msgs, max_tokens: 1000 }); } else { if (prov.key) xhr.setRequestHeader('Authorization', 'Bearer ' + prov.key); var messages = [{ role: 'system', content: fullPrompt }]; var hi; for (hi = 0; hi < history.length; hi++) { messages.push(history[hi]); } body = JSON.stringify({ model: prov.model, messages: messages, max_tokens: 1000 }); } xhr.onload = function() { var latency = Date.now() - startTime; ss.lastMs = latency; ss.timeMs += latency; ss.reqs++; if (xhr.status === 200) { try { var data = JSON.parse(xhr.responseText); var reply = ''; if (prov.type === 'anthropic') { if (data.content && data.content.length > 0) { reply = data.content[0].text || ''; } } else { if (data.choices && data.choices.length > 0) { reply = data.choices[0].message.content || ''; } } if (!reply) { ss.errors++; logEvent('error', { provider: prov.name, message: 'Leere Antwort' }); callback(prov.name + ': Leere Antwort vom Model erhalten.'); return; } ss.chOut += reply.length; ss.tokOut += estimateTokens(reply); if (statsVisible) updateStats(); logEvent('bot_reply', { provider: prov.name, model: prov.model, tokens: estimateTokens(reply), latency: latency, message: reply }); callback(null, reply); } catch (e) { ss.errors++; var snippet = (xhr.responseText || '').substring(0, 150); logEvent('error', { provider: prov.name, message: 'Parse: ' + e.message }); callback(prov.name + ' Parse-Fehler: ' + e.message + '\nAntwort: ' + snippet); } } else { ss.errors++; var errBody = ''; try { var ed = JSON.parse(xhr.responseText); errBody = ed.error ? (ed.error.message || ed.error) : xhr.responseText.substring(0, 200); } catch (ex) { errBody = (xhr.responseText || '').substring(0, 200); } var errMsg = prov.name + ' [' + prov.model + '] HTTP ' + xhr.status; if (xhr.status === 401) { errMsg += ' - API Key ungueltig'; } else if (xhr.status === 403) { errMsg += ' - Zugriff verweigert'; } else if (xhr.status === 404) { errMsg += ' - Endpoint nicht gefunden'; } else if (xhr.status === 429) { errMsg += ' - Rate Limit, bitte warten'; } else if (xhr.status === 500) { errMsg += ' - Server-Fehler'; } else if (xhr.status === 529) { errMsg += ' - Provider ueberlastet'; } if (errBody) errMsg += '\n' + errBody; logEvent('error', { provider: prov.name, message: 'HTTP ' + xhr.status, latency: latency }); callback(errMsg); } }; xhr.onerror = function() { var latency = Date.now() - startTime; ss.lastMs = latency; ss.timeMs += latency; ss.reqs++; ss.errors++; logEvent('error', { provider: prov.name, message: 'Verbindungsfehler', latency: latency }); callback(prov.name + ': Verbindungsfehler - nicht erreichbar (' + prov.url + ')'); }; xhr.ontimeout = function() { var latency = Date.now() - startTime; ss.lastMs = latency; ss.timeMs += latency; ss.reqs++; ss.errors++; logEvent('error', { provider: prov.name, message: 'Timeout', latency: latency }); callback(prov.name + ': Timeout - keine Antwort nach 30s'); }; xhr.send(body); } function send() { var msg = input.value.replace(/^\s+|\s+$/g, ''); if (!msg || busy) return; /* === SPECIAL COMMANDS === */ if (msg === '/showprompt') { input.value = ''; showPromptViewer(); return; } if (msg === '/stats') { input.value = ''; statsVisible = !statsVisible; if (statsVisible) { updateStats(); statsDiv.className = 'visible'; } else { statsDiv.className = ''; } return; } if (msg === '/clear') { input.value = ''; clearBtn.click(); return; } if (msg === '/debug') { input.value = ''; var d = '=== HELIKON DEBUG ===\n'; d += 'Provider 1: ' + providers.p1.name + '\n'; d += ' URL: ' + (providers.p1.url || 'NICHT GESETZT') + '\n'; d += ' Key: ' + (providers.p1.key ? providers.p1.key.substring(0, 8) + '...' : 'NICHT GESETZT') + '\n'; d += ' Model: ' + providers.p1.model + '\n'; d += ' Ready: ' + providers.p1.ready + '\n\n'; d += 'Provider 2: ' + providers.p2.name + '\n'; d += ' URL: ' + (providers.p2.url || 'NICHT GESETZT') + '\n'; d += ' Key: ' + (providers.p2.key ? providers.p2.key.substring(0, 8) + '...' : 'NICHT GESETZT') + '\n'; d += ' Model: ' + providers.p2.model + '\n'; d += ' Ready: ' + providers.p2.ready + '\n\n'; d += 'Aktiv: ' + providers[activeProvider].name + (dualMode ? ' (DUAL)' : '') + '\n'; d += 'System Prompt: ' + (sysMsg ? sysMsg.length + ' Zeichen' : 'FEHLT') + '\n'; d += 'Static Info: ' + (staticInfo ? staticInfo.length + ' Zeichen' : 'FEHLT') + '\n'; d += 'History: ' + history.length + ' Eintraege\n'; d += 'User: ' + currentUser; addMsg(d, 'system'); return; } if (msg === '/help') { input.value = ''; addMsg('/clear - Chat loeschen\n/stats - AI Stats Panel\n/showprompt - System Prompt\n/debug - Config anzeigen\n/help - Diese Hilfe', 'system'); return; } addMsg(msg, 'user'); input.value = ''; history.push({ role: 'user', content: msg }); logEvent('user_msg', { message: msg }); busy = true; btn.disabled = true; setGif('loading'); setStatus('denkt nach...'); var typing = addMsg('...', 'buddy typing'); if (dualMode) { var done = 0; var total = 0; if (providers.p1.ready) total++; if (providers.p2.ready) total++; log.removeChild(typing); function checkDone() { done++; if (done >= total) { setGif('happy'); setTimeout(function() { setGif('idle'); }, 3000); setStatus(''); busy = false; btn.disabled = false; input.focus(); } } if (providers.p1.ready) { callProvider(providers.p1, msg, function(err, reply) { if (err) { addMsg(err, 'system'); } else { handleReply(reply, providers.p1.name, providers.p1.type); } checkDone(); }); } if (providers.p2.ready) { callProvider(providers.p2, msg, function(err, reply) { if (err) { addMsg(err, 'system'); } else { handleReply(reply, providers.p2.name, providers.p2.type); } checkDone(); }); } } else { var prov = providers[activeProvider]; if (!prov || !prov.ready) { log.removeChild(typing); history.pop(); addMsg('Kein Provider aktiv! Waehle einen Provider unten.', 'system'); setGif('error'); busy = false; btn.disabled = false; return; } callProvider(prov, msg, function(err, reply) { log.removeChild(typing); if (err) { history.pop(); addMsg(err, 'system'); showBubble('Fehler!', 3000); setGif('error'); setTimeout(function() { setGif('idle'); }, 4000); setStatus(err.substring(0, 80)); } else { handleReply(reply, prov.name, prov.type); setGif('happy'); setTimeout(function() { setGif('idle'); }, 3000); setStatus(''); } busy = false; btn.disabled = false; input.focus(); }); } } btn.addEventListener('click', send); input.addEventListener('keydown', function(e) { if (e.keyCode === 13) send(); }); /* === INIT === */ setGif('loading'); setStatus('lade konfiguration...'); fetchPageProps(function(props) { if (props.apiurl) { providers.p1.url = props.apiurl; providers.p1.key = props.apikey || ''; providers.p1.ready = true; } if (props.name1) providers.p1.name = props.name1; if (props.apiurl2) { providers.p2.url = props.apiurl2; providers.p2.key = props.apikey2 || ''; providers.p2.model = props.model2 || 'claude-sonnet-4-20250514'; providers.p2.ready = true; } if (props.name2) providers.p2.name = props.name2; var sysPromptUrl = props.systemprompt || ''; var staticInfoUrl = props.staticinfo || ''; /* Auto-select: prefer p1, fallback p2 */ if (providers.p1.ready) { activeProvider = 'p1'; } else if (providers.p2.ready) { activeProvider = 'p2'; } function ready() { setGif('idle'); input.disabled = false; btn.disabled = false; input.placeholder = 'Nachricht eingeben...'; updateSwitchUI(); if (providers.p1.ready || providers.p2.ready) { var ap = providers[activeProvider]; setStatus('\u2713 ' + ap.name + ' bereit'); showBubble('Hallo! Frag mich was!', 5000); logEvent('session_start', { provider: ap.name, model: ap.model }); } else { showBubble('Kein LLM konfiguriert!', 5000); setGif('error'); setStatus('buddy.apiurl fehlt'); addMsg('Keine AI konfiguriert. Bitte buddy.apiurl in Page Properties setzen.', 'system'); } } function loadPrompt(callback) { if (sysPromptUrl) { fetchText(sysPromptUrl, function(val) { sysMsg = val || 'Du bist Helikon, ein freundlicher Dokumentations-Assistent.'; callback(); }); } else { sysMsg = 'Du bist Helikon, ein freundlicher Dokumentations-Assistent.'; callback(); } } function loadStatic(callback) { if (staticInfoUrl) { fetchText(staticInfoUrl, function(val) { if (val) staticInfo = val; callback(); }); } else { callback(); } } loadPrompt(function() { loadStatic(function() { ready(); }); }); }); })();

     

    Nach oben Nach unten

     

     

    Nach unten

     

    Lade Daten...
     
     
     
     
     
     
     

     
    Use the language selection below for English Utilise le sélecteur de langue ci-dessous pour français Per l'italiano, utilizzare la selezione della lingua qui sotto Schön sprichst du deutsch :-)