helikon

Von $1

    Inhaltsverzeichnis
    keine Gliederung

    Version seit 17:06, 6 Nov 2026

    zu dieser Version.

    Zurück zu Versionshistorie.

    Zeige aktuelle Version

     
    LLM:
     
     
    (function() {
    
        /* ============================================================
           HELIKON CHAT - MULTI-LLM TEMPLATE
           ============================================================
           Page Properties needed:
             buddy.apiurl    = Local/BWO LLM endpoint (OpenAI-compatible)
             buddy.apikey    = Local/BWO API key
             buddy.apiurl2   = Claude API endpoint (https://api.anthropic.com/v1/messages)
             buddy.apikey2   = Claude/Anthropic API key
             buddy.model     = Local model name (default: 'default')
             buddy.model2    = Claude model name (default: 'claude-sonnet-4-20250514')
           Files needed:
             /@api/deki/files/404/=system-prompt.txt
           ============================================================ */
    
        /* === CONFIG (loaded from page properties) === */
        var PROMPT_URL  = '/@api/deki/files/404/=system-prompt.txt';
        var SHORT_LEN   = 100;
        var SEARCH_FIRST = true;
    
        /* === PROVIDER STATE === */
        var providers = {
            p1: { url: '', key: '', model: 'default', name: 'Local', type: 'openai', ready: false },
            p2: { url: '', key: '', model: 'claude-sonnet-4-20250514', name: 'Claude', type: 'anthropic', ready: false }
        };
        var activeProvider = 'p1';
        var dualMode = false;
    
        /* === 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 exportBtn = document.getElementById('chatExport');
        var llmBtn1   = document.getElementById('llmBtn1');
        var llmBtn2   = document.getElementById('llmBtn2');
        var llmBtnDual = document.getElementById('llmBtnDual');
        if (!log || !input || !btn) return;
    
        log.innerHTML = '';
    
        /* === INJECT CSS === */
        var T  = String.fromCharCode(60);
        var TC = String.fromCharCode(60, 47);
        var G  = String.fromCharCode(62);
    
        var css = ''
        + '#serverBuddy { display: none !important; }'
        + '#buddySpeech { display: none !important; }'
        + '#buddyChat { display:flex; gap:24px; align-items:flex-start; width:100%; font-family:"Courier New",Courier,monospace; 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; }'
        + '#buddyBubble { transform:scale(0); background:#fff; color:#333; padding:12px 18px; border-radius:14px; border:2px solid #ccc; font-size:15px; line-height:1.4; 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; }'
        + '#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:12px 16px; border-radius:8px; font-size:16px; line-height:1.6; max-width:90%; word-wrap:break-word; }'
        + '.chatMsg.user { background:#e8e8e8; color:#555; margin-left:auto; text-align:right; border:1px solid #ddd; }'
        + '.chatMsg.buddy { background:#fff; color:#333; margin-right:auto; border:1px solid #ccc; }'
        + '.chatMsg.buddy::before { content:"> "; color:#0077aa; margin-right:4px; opacity:0.6; }'
        + '.chatMsg.buddy.provider-p2 { border-left:3px solid #d97706; }'
        + '.chatMsg.buddy.provider-p2::before { content:"> "; color:#d97706; margin-right:4px; opacity:0.6; }'
        + '.chatMsg .provider-tag { font-size:10px; opacity:0.5; display:block; margin-bottom:4px; text-transform:uppercase; letter-spacing:1px; }'
        + '.chatMsg.system { color:#999; font-size:13px; text-align:center; margin:8px auto; max-width:100%; }'
        + '.chatMsg.typing { color:#999; opacity:0.6; }'
        + '.chatMsg a { color:#0077aa; text-decoration:underline; }'
        + '.chatMsg a:hover { color:#005580; }'
        + '#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:"Courier New",Courier,monospace; font-size:16px; padding:12px 14px; outline:none; }'
        + '#chatInput::placeholder { color:#aaa; }'
        + '#chatInput:focus { border-color:#0077aa; }'
        + '#chatSend { background:#fff; color:#0077aa; border:1px solid #0077aa; padding:10px 20px; font-family:"Courier New",Courier,monospace; font-size:14px; cursor:pointer; }'
        + '#chatSend:hover { background:#0077aa; color:#fff; }'
        + '#chatSend:disabled { opacity:0.3; cursor:default; }'
        + '#llmSwitch { display:flex; align-items:center; gap:6px; margin-top:8px; padding:6px 0; }'
        + '#llmLabel { font-size:11px; color:#999; margin-right:4px; }'
        + '.llmBtn { background:#f0f0f0; color:#666; border:1px solid #ccc; padding:4px 12px; font-family:"Courier New",Courier,monospace; font-size:11px; cursor:pointer; border-radius:3px; transition:all 0.2s ease; }'
        + '.llmBtn:hover { border-color:#0077aa; color:#0077aa; }'
        + '.llmBtn.active { background:#0077aa; color:#fff; border-color:#0077aa; }'
        + '.llmBtn.active-dual { background:#d97706; color:#fff; border-color:#d97706; }'
        + '.llmBtn:disabled { opacity:0.3; cursor:default; }'
        + '#chatFooter { display:flex; justify-content:space-between; align-items:center; margin-top:4px; }'
        + '#chatStatus { font-size:11px; color:#999; }'
        + '#chatExport { background:none; border:1px solid #ccc; color:#999; padding:3px 10px; font-size:11px; cursor:pointer; font-family:"Courier New",Courier,monospace; }'
        + '#chatExport:hover { border-color:#0077aa; color:#0077aa; }'
        + '@media (max-width:768px) {'
        + '  #buddyChat { flex-direction:column-reverse; }'
        + '  #buddyChatSide { width:100%; flex-direction:row; justify-content:center; position:static; gap:12px; }'
        + '  #buddyChatSide img { width:96px; height:96px; }'
        + '  #buddyBubble { max-width:200px; font-size:12px; margin-bottom:0; }'
        + '}';
    
        var styleEl = document.createElement('style');
        styleEl.textContent = css;
        document.head.appendChild(styleEl);
    
        /* === GIFs === */
        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 bubbleTimer = null;
        var pendingDual = 0;
    
        /* === GET PAGE ID === */
        var pageId = 0;
        if (typeof Deki !== 'undefined' && Deki.PageId) {
            pageId = Deki.PageId;
        }
    
        /* === HELPERS === */
        function setGif(name) { if (avatar) avatar.src = GIFS[name] || GIFS.idle; }
        function setStatus(txt) { if (status) status.textContent = txt; }
    
        function showBubble(txt, dur) {
            if (!bubble) return;
            bubble.textContent = txt;
            bubble.className = 'visible';
            if (bubbleTimer) clearTimeout(bubbleTimer);
            bubbleTimer = setTimeout(function() { bubble.className = ''; }, dur || 4000);
        }
    
        function addMsg(txt, cls, providerKey) {
            var div = document.createElement('div');
            div.className = 'chatMsg ' + cls;
            if (providerKey) {
                div.className += ' provider-' + providerKey;
            }
            if (dualMode && providerKey && cls === 'buddy') {
                var tag = document.createElement('span');
                tag.className = 'provider-tag';
                tag.textContent = providers[providerKey] ? providers[providerKey].name : providerKey;
                div.appendChild(tag);
            }
            var textNode = document.createTextNode(txt);
            div.appendChild(textNode);
            log.appendChild(div);
            log.scrollTop = log.scrollHeight;
        }
    
        function addTyping(providerKey) {
            var div = document.createElement('div');
            div.className = 'chatMsg buddy typing';
            if (providerKey) {
                div.className += ' typing-' + providerKey;
            }
            div.textContent = '. . .';
            log.appendChild(div);
            log.scrollTop = log.scrollHeight;
        }
    
        function removeTyping(providerKey) {
            var selector = providerKey ? '.typing-' + providerKey : '.typing';
            var els = log.querySelectorAll(selector);
            var i;
            for (i = 0; i < els.length; i++) {
                log.removeChild(els[i]);
            }
        }
    
        /* === 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, '') : '');
            };
            xhr.onerror = function() { callback(''); };
            xhr.send();
        }
    
        function fetchAllProperties(callback) {
            if (!pageId) { callback({}); return; }
            var url = '/@api/deki/pages/' + pageId + '/properties';
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.onload = function() {
                var props = {};
                if (xhr.status === 200) {
                    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 nameAttr = nodes[i].getAttribute('name') || '';
                            var shortName = nameAttr;
                            var dotPos = nameAttr.lastIndexOf('#');
                            if (dotPos !== -1) shortName = nameAttr.substring(dotPos + 1);
                            var contents = nodes[i].getElementsByTagName('contents');
                            if (contents.length > 0 && contents[0].textContent) {
                                props[shortName] = contents[0].textContent.replace(/^\s+|\s+$/g, '');
                            }
                        }
                    } catch (e) {}
                }
                callback(props);
            };
            xhr.onerror = function() { callback({}); };
            xhr.send();
        }
    
        /* === MINDTOUCH SEARCH === */
        function searchMindTouch(query, callback) {
            if (!SEARCH_FIRST) { callback(null); return; }
            var url = '/@api/deki/site/search?q=' + encodeURIComponent(query) + String.fromCharCode(38) + 'limit=5';
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.onload = function() {
                if (xhr.status !== 200) { callback(null); return; }
                try {
                    var parser = new DOMParser();
                    var xml = parser.parseFromString(xhr.responseText, 'text/xml');
                    var pages = xml.getElementsByTagName('page');
                    if (pages.length === 0) { callback(null); return; }
                    var results = [];
                    var i;
                    for (i = 0; i < pages.length && i < 5; i++) {
                        var titleEl = pages[i].getElementsByTagName('title');
                        var pathEl  = pages[i].getElementsByTagName('path');
                        if (titleEl.length > 0 && pathEl.length > 0) {
                            results.push({ title: titleEl[0].textContent, path: '/' + pathEl[0].textContent });
                        }
                    }
                    if (results.length === 0) { callback(null); return; }
                    var ctx = '\n\n--- Suchergebnisse aus der Dokumentation ---\n';
                    var ri;
                    for (ri = 0; ri < results.length; ri++) {
                        ctx += (ri + 1) + '. ' + results[ri].title + ' (' + results[ri].path + ')\n';
                    }
                    ctx += '---\nBitte verwende NUR diese tatsaechlichen Seiten als Referenz. Erfinde KEINE Pfade.\n';
                    callback(ctx);
                } catch (e) { callback(null); }
            };
            xhr.onerror = function() { callback(null); };
            xhr.send();
        }
    
        /* === LLM CALL: OpenAI-compatible (BWO/Local) === */
        function callOpenAI(prov, messages, callback) {
            var headers = { 'Content-Type': 'application/json' };
            if (prov.key) headers['Authorization'] = 'Bearer ' + prov.key;
    
            var body = JSON.stringify({
                model: prov.model,
                messages: messages,
                max_tokens: 500
            });
    
            var xhr = new XMLHttpRequest();
            xhr.open('POST', prov.url, true);
            var key;
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    xhr.setRequestHeader(key, headers[key]);
                }
            }
            xhr.onload = function() {
                if (xhr.status === 200) {
                    try {
                        var data = JSON.parse(xhr.responseText);
                        callback(null, data.choices[0].message.content);
                    } catch (e) {
                        callback('Parse-Fehler');
                    }
                } else {
                    callback('API-Fehler ' + xhr.status);
                }
            };
            xhr.onerror = function() { callback('Nicht erreichbar'); };
            xhr.send(body);
        }
    
        /* === LLM CALL: Anthropic (Claude) === */
        function callAnthropic(prov, sysPrompt, messages, callback) {
            var anthropicMessages = [];
            var i;
            for (i = 0; i < messages.length; i++) {
                if (messages[i].role !== 'system') {
                    anthropicMessages.push({ role: messages[i].role, content: messages[i].content });
                }
            }
    
            var body = JSON.stringify({
                model: prov.model,
                max_tokens: 500,
                system: sysPrompt,
                messages: anthropicMessages
            });
    
            var xhr = new XMLHttpRequest();
            xhr.open('POST', prov.url, true);
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.setRequestHeader('x-api-key', prov.key);
            xhr.setRequestHeader('anthropic-version', '2023-06-01');
            xhr.setRequestHeader('anthropic-dangerous-direct-browser-access', 'true');
    
            xhr.onload = function() {
                if (xhr.status === 200) {
                    try {
                        var data = JSON.parse(xhr.responseText);
                        var reply = '';
                        var ci;
                        for (ci = 0; ci < data.content.length; ci++) {
                            if (data.content[ci].type === 'text') {
                                reply += data.content[ci].text;
                            }
                        }
                        callback(null, reply);
                    } catch (e) {
                        callback('Parse-Fehler');
                    }
                } else {
                    callback('API-Fehler ' + xhr.status);
                }
            };
            xhr.onerror = function() { callback('Nicht erreichbar'); };
            xhr.send(body);
        }
    
        /* === UNIFIED LLM DISPATCH === */
        function callProvider(providerKey, searchContext, callback) {
            var prov = providers[providerKey];
            if (!prov || !prov.ready) {
                callback('Provider nicht konfiguriert');
                return;
            }
    
            var augmentedSys = sysMsg;
            if (searchContext) augmentedSys = sysMsg + searchContext;
    
            var messages = [{ role: 'system', content: augmentedSys }];
            var i;
            for (i = 0; i < history.length; i++) {
                messages.push(history[i]);
            }
    
            if (prov.type === 'anthropic') {
                callAnthropic(prov, augmentedSys, messages, callback);
            } else {
                callOpenAI(prov, messages, callback);
            }
        }
    
        /* === HANDLE REPLY === */
        function handleReply(reply, providerKey) {
            history.push({ role: 'assistant', content: reply });
            addMsg(reply, 'buddy', providerKey);
    
            var dot = reply.indexOf('.');
            var summary;
            if (dot > 0 && dot < 80) {
                summary = reply.substring(0, dot + 1);
            } else if (reply.length > 60) {
                summary = reply.substring(0, 57) + '...';
            } else {
                summary = reply;
            }
            showBubble(summary, 6000);
            setGif('happy');
            setTimeout(function() { setGif('idle'); }, 3000);
        }
    
        function handleError(errMsg, providerKey) {
            addMsg('Fehler (' + providers[providerKey].name + '): ' + errMsg, 'system');
            showBubble('Fehler!', 3000);
            setGif('error');
            setTimeout(function() { setGif('idle'); }, 4000);
            setStatus(providers[providerKey].name + ': ' + errMsg);
        }
    
        /* === SEND MESSAGE === */
        function send() {
            var msg = input.value.replace(/^\s+|\s+$/g, '');
            if (!msg || busy) return;
    
            addMsg(msg, 'user');
            history.push({ role: 'user', content: msg });
            input.value = '';
            busy = true;
            btn.disabled = true;
            setGif('loading');
            setStatus('denkt nach...');
    
            searchMindTouch(msg, function(searchContext) {
    
                if (dualMode) {
                    /* --- DUAL MODE: both providers answer --- */
                    pendingDual = 0;
                    var targets = ['p1', 'p2'];
                    var ti;
                    for (ti = 0; ti < targets.length; ti++) {
                        if (providers[targets[ti]].ready) {
                            pendingDual++;
                            addTyping(targets[ti]);
                        }
                    }
                    if (pendingDual === 0) {
                        addMsg('Kein Provider verfuegbar.', 'system');
                        busy = false;
                        btn.disabled = false;
                        setGif('error');
                        return;
                    }
    
                    var dualDone = function() {
                        pendingDual--;
                        if (pendingDual <= 0) {
                            busy = false;
                            btn.disabled = false;
                            input.focus();
                            setStatus('bereit');
                        }
                    };
    
                    for (ti = 0; ti < targets.length; ti++) {
                        (function(pk) {
                            if (!providers[pk].ready) return;
                            callProvider(pk, searchContext, function(err, reply) {
                                removeTyping(pk);
                                if (err) {
                                    handleError(err, pk);
                                } else {
                                    addMsg(reply, 'buddy', pk);
                                    showBubble(providers[pk].name + ' hat geantwortet', 3000);
                                }
                                dualDone();
                            });
                        })(targets[ti]);
                    }
    
                    /* In dual mode, only store one reply in history (last one) */
    
                } else {
                    /* --- SINGLE MODE --- */
                    addTyping(activeProvider);
                    callProvider(activeProvider, searchContext, function(err, reply) {
                        removeTyping(activeProvider);
                        if (err) {
                            handleError(err, activeProvider);
                        } else {
                            handleReply(reply, activeProvider);
                            setStatus(providers[activeProvider].name + ' | bereit');
                        }
                        busy = false;
                        btn.disabled = false;
                        input.focus();
                    });
                }
            });
        }
    
        /* === LLM SWITCH UI === */
        function updateSwitchUI() {
            llmBtn1.className = 'llmBtn';
            llmBtn2.className = 'llmBtn';
            llmBtnDual.className = 'llmBtn';
    
            if (!providers.p1.ready) llmBtn1.disabled = true;
            if (!providers.p2.ready) llmBtn2.disabled = true;
            if (!providers.p1.ready || !providers.p2.ready) llmBtnDual.disabled = true;
    
            if (dualMode) {
                llmBtnDual.className = 'llmBtn active-dual';
            } else if (activeProvider === 'p1') {
                llmBtn1.className = 'llmBtn active';
            } else {
                llmBtn2.className = 'llmBtn active';
            }
    
            if (!dualMode && providers[activeProvider]) {
                setStatus(providers[activeProvider].name + ' | bereit');
            } else if (dualMode) {
                setStatus('Dual Mode | bereit');
            }
        }
    
        llmBtn1.addEventListener('click', function() {
            if (!providers.p1.ready) return;
            dualMode = false;
            activeProvider = 'p1';
            updateSwitchUI();
        });
        llmBtn2.addEventListener('click', function() {
            if (!providers.p2.ready) return;
            dualMode = false;
            activeProvider = 'p2';
            updateSwitchUI();
        });
        llmBtnDual.addEventListener('click', function() {
            if (!providers.p1.ready || !providers.p2.ready) return;
            dualMode = !dualMode;
            updateSwitchUI();
        });
    
        /* === INPUT HANDLERS === */
        btn.addEventListener('click', send);
        input.addEventListener('keydown', function(e) {
            if (e.keyCode === 13) send();
        });
    
        /* === PDF EXPORT === */
        if (exportBtn) {
            exportBtn.addEventListener('click', function() {
                var T  = String.fromCharCode(60);
                var TC = String.fromCharCode(60, 47);
                var G  = String.fromCharCode(62);
                var html = T + 'html' + G + T + 'head' + G;
                html += T + 'style' + G;
                html += 'body{font-family:"Courier New",monospace;padding:20px;color:#333}';
                html += '.msg{margin:8px 0;padding:8px;border-radius:4px}';
                html += '.user{background:#e8e8e8;text-align:right}';
                html += '.buddy{background:#fff;border:1px solid #ccc}';
                html += '.system{color:#999;text-align:center;font-size:11px}';
                html += '.tag{font-size:9px;color:#999;text-transform:uppercase}';
                html += TC + 'style' + G + TC + 'head' + G + T + 'body' + G;
                html += T + 'h2' + G + 'Helikon Chat Export' + TC + 'h2' + G;
                html += T + 'p' + G + new Date().toLocaleString() + TC + 'p' + G;
    
                var msgs = log.getElementsByClassName('chatMsg');
                var mi;
                for (mi = 0; mi < msgs.length; mi++) {
                    var m = msgs[mi];
                    if (m.className.indexOf('typing') !== -1) continue;
                    var cls = 'buddy';
                    if (m.className.indexOf('user') !== -1) cls = 'user';
                    if (m.className.indexOf('system') !== -1) cls = 'system';
                    html += T + 'div class="msg ' + cls + '"' + G;
                    html += m.textContent;
                    html += TC + 'div' + G;
                }
    
                html += TC + 'body' + G + TC + 'html' + G;
                var w = window.open('', '_blank');
                if (w) {
                    w.document.write(html);
                    w.document.close();
                    w.print();
                }
            });
        }
    
        /* === INIT === */
        setGif('loading');
        setStatus('lade konfiguration...');
        input.disabled = true;
        btn.disabled = true;
        llmBtn1.disabled = true;
        llmBtn2.disabled = true;
        llmBtnDual.disabled = true;
    
        fetchAllProperties(function(props) {
            /* Provider 1: Local/BWO */
            providers.p1.url   = props['buddy.apiurl'] || '';
            providers.p1.key   = props['buddy.apikey'] || '';
            providers.p1.model = props['buddy.model']  || 'default';
            providers.p1.ready = !!providers.p1.url;
            if (props['buddy.name1']) providers.p1.name = props['buddy.name1'];
    
            /* Provider 2: Claude */
            providers.p2.url   = props['buddy.apiurl2'] || '';
            providers.p2.key   = props['buddy.apikey2'] || '';
            providers.p2.model = props['buddy.model2']  || 'claude-sonnet-4-20250514';
            providers.p2.ready = !!providers.p2.url;
            if (props['buddy.name2']) providers.p2.name = props['buddy.name2'];
    
            /* Update button labels from config */
            llmBtn1.textContent = providers.p1.name;
            llmBtn2.textContent = providers.p2.name;
    
            /* Set default active provider */
            if (providers.p1.ready) {
                activeProvider = 'p1';
            } else if (providers.p2.ready) {
                activeProvider = 'p2';
            }
    
            var statusParts = [];
            statusParts.push(providers.p1.name + ': ' + (providers.p1.ready ? 'OK' : 'FEHLT'));
            statusParts.push(providers.p2.name + ': ' + (providers.p2.ready ? 'OK' : 'FEHLT'));
            setStatus(statusParts.join(' | '));
    
            /* Load system prompt */
            fetchText(PROMPT_URL, function(val) {
                sysMsg = val || 'Du bist Helikon, ein freundlicher Dokumentations-Assistent.';
                setGif('idle');
                input.disabled = false;
                btn.disabled = false;
                updateSwitchUI();
    
                if (providers.p1.ready || providers.p2.ready) {
                    showBubble('Hallo! Wie kann ich helfen?', 5000);
                } else {
                    showBubble('Kein LLM konfiguriert!', 5000);
                    setGif('error');
                    setStatus('buddy.apiurl und buddy.apiurl2 fehlen');
                }
            });
        });
    
    })();

     
    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 :-)