diff options
Diffstat (limited to 'htroot')
| -rw-r--r-- | htroot/AILab.html | 16 | ||||
| -rw-r--r-- | htroot/AIShield_p.html | 204 | ||||
| -rw-r--r-- | htroot/ConfigSearchPage_p.html | 2 | ||||
| -rw-r--r-- | htroot/Status.html | 2 | ||||
| -rw-r--r-- | htroot/env/templates/header.template | 30 | ||||
| -rw-r--r-- | htroot/env/templates/simpleSearchHeader.template | 1 | ||||
| -rw-r--r-- | htroot/env/templates/simpleheader.template | 1 | ||||
| -rw-r--r-- | htroot/env/templates/submenuAI.template | 1 | ||||
| -rw-r--r-- | htroot/yacychat.html | 13 |
9 files changed, 250 insertions, 20 deletions
diff --git a/htroot/AILab.html b/htroot/AILab.html index cbf330393..caa89d83b 100644 --- a/htroot/AILab.html +++ b/htroot/AILab.html @@ -313,18 +313,18 @@ <span class="status-pill">Needs setup</span> </div> <h3>Define a shield</h3> - <p>Add guardrails: moderation prompts, content filters, or safety rules that wrap every request.</p> + <p>Add guardrails: access rates, grant or deny non-localhost access. Activate the front page link for chat to complete this quest.</p> <div class="quest-body"> - <div class="quest-visual"> - <img src="env/grafics/AILab_shield.png" alt="Shield definition" width="128" height="128" /> - </div> - <div class="quest-actions"> - <a class="btn btn-info btn-sm" href="ConfigProperties_p.html">Open shield settings</a><br /> - <span class="quest-note">Store your shield directives (system prompts, stop words) as properties, then exercise them in chat.</span> - </div> + <div class="quest-visual"> + <img src="env/grafics/AILab_shield.png" alt="Shield definition" width="128" height="128" /> + </div> + <div class="quest-actions"> + <a class="btn btn-info btn-sm" href="AIShield_p.html">Open shield settings</a><br /> + <span class="quest-note">Store your shield directives (system prompts, stop words) as properties, then exercise them in chat.</span> </div> </div> </div> + </div> </div> <script type="text/javascript"> diff --git a/htroot/AIShield_p.html b/htroot/AIShield_p.html new file mode 100644 index 000000000..dcc7d8ecb --- /dev/null +++ b/htroot/AIShield_p.html @@ -0,0 +1,204 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>YaCy '#[clientname]#': AI Shield</title> + #%env/templates/metas.template%# + <style type="text/css"> + .shield-card { + border: 1px solid #e6e9ef; + border-left: 6px solid #5bc0de; + padding: 16px; + margin-bottom: 14px; + background: #fff; + box-shadow: 0 3px 10px rgba(0,0,0,0.06); + } + .shield-card h3 { + margin-top: 0; + margin-bottom: 6px; + color: #0f2b46; + } + .shield-card p { + margin: 0 0 8px 0; + color: #3b4a5e; + } + .shield-inline { + display: flex; + gap: 16px; + flex-wrap: wrap; + align-items: center; + } + .shield-inline label { + margin: 0; + } + .shield-rate { + max-width: 120px; + } + + .telemetry-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 12px; + margin-top: 10px; + } + + .telemetry-item { + border: 1px solid #e6e9ef; + border-radius: 6px; + padding: 10px; + background: #f9fbff; + box-shadow: 0 2px 6px rgba(0,0,0,0.04); + } + + .telemetry-label { + font-weight: 700; + color: #0f2b46; + margin-bottom: 4px; + } + + .gauge { + position: relative; + height: 12px; + background: #e9edf5; + border-radius: 999px; + overflow: hidden; + } + + .gauge-fill { + height: 100%; + width: 0%; + border-radius: 999px; + transition: width 0.3s ease; + } + + .gauge-meta { + margin-top: 4px; + font-size: 0.9em; + color: #3b4a5e; + display: flex; + justify-content: space-between; + } + + .gauge-green { background: #5cb85c; } + .gauge-amber { background: #f0ad4e; } + .gauge-red { background: #d9534f; } + .gauge-neutral { background: #5bc0de; } + </style> + </head> + <body id="IndexControl"> + #%env/templates/header.template%# + #%env/templates/submenuAI.template%# + + <h2>Wire RAG Retrieval Shield</h2> + <p>Control who can access the chat interface and rate-limit non-localhost clients to protect your peer and LLM backends from overload.</p> + + <form method="post" action="AIShield_p.html" id="shieldForm"> + <input type="hidden" name="submit" value="1" /> + <div class="shield-card"> + <h3>Overall Load Protection</h3> + <p>Recent access volume across all clients (localhost included). You can enforce global limits here to protect the host.</p> + <div class="telemetry-grid"> + <div class="telemetry-item"> + <div class="telemetry-label">Requests / minute</div> + <div class="gauge"><div class="gauge-fill" id="gaugeMinute"></div></div> + <div class="gauge-meta"> + <span id="gaugeMinuteValue">#[telemetry.per-minute]#</span> + <span id="gaugeMinuteLimit">#[ai.shield.all.per-minute]#</span> + </div> + </div> + <div class="telemetry-item"> + <div class="telemetry-label">Requests / hour</div> + <div class="gauge"><div class="gauge-fill" id="gaugeHour"></div></div> + <div class="gauge-meta"> + <span id="gaugeHourValue">#[telemetry.per-hour]#</span> + <span id="gaugeHourLimit">#[ai.shield.all.per-hour]#</span> + </div> + </div> + <div class="telemetry-item"> + <div class="telemetry-label">Requests / day</div> + <div class="gauge"><div class="gauge-fill" id="gaugeDay"></div></div> + <div class="gauge-meta"> + <span id="gaugeDayValue">#[telemetry.per-day]#</span> + <span id="gaugeDayLimit">#[ai.shield.all.per-day]#</span> + </div> + </div> + </div> + <p style="margin-top:8px;"> + <label> + <input type="checkbox" name="ai.shield.limit-all" id="limitAll" #(ai.shield.limit-all)#::checked="checked"#(/ai.shield.limit-all)# /> + Limit for all requests, including localhost + </label> + </p> + <div class="shield-inline"> + <label>Per minute: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.all.per-minute" id="allMinute" value="#[ai.shield.all.per-minute]#" /></label> + <label>Per hour: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.all.per-hour" id="allHour" value="#[ai.shield.all.per-hour]#" /></label> + <label>Per day: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.all.per-day" id="allDay" value="#[ai.shield.all.per-day]#" /></label> + </div> + </div> + + <div class="shield-card"> + <h3>Guest Access Control & Rate Limits</h3> + <p>By default only localhost may reach the chat UI. Enable non-localhost access and throttle requests to reduce abuse.</p> + <label> + <input type="checkbox" name="ai.shield.allow-nonlocalhost" id="allowNonLocal" #(ai.shield.allow-nonlocalhost)#::checked="checked"#(/ai.shield.allow-nonlocalhost)# /> + Allow non-localhost clients to access the chat interface + </label> + <p style="margin-top:8px;">Requests from non-localhost will be throttled using these caps:</p> + <div class="shield-inline"> + <label>Per minute: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.rate.per-minute" id="rateMinute" value="#[ai.shield.rate.per-minute]#" /></label> + <label>Per hour: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.rate.per-hour" id="rateHour" value="#[ai.shield.rate.per-hour]#" /></label> + <label>Per day: <input type="number" class="form-control shield-rate" min="0" name="ai.shield.rate.per-day" id="rateDay" value="#[ai.shield.rate.per-day]#" /></label> + </div> + </div> + + <div class="shield-card"> + <h3>Front Page Link</h3> + <p>Expose a shortcut to the chat UI on the search front page if you want users to discover it.</p> + <label> + <input type="checkbox" name="ai.shield.show-chat-link" id="showLink" #(ai.shield.show-chat-link)#::checked="checked"#(/ai.shield.show-chat-link)# /> + Show a link to yacychat.html on the search front page + </label> + </div> + + <button type="submit" class="btn btn-primary">Save Shield Settings</button> + </form> + + <script type="text/javascript"> + (function() { + const allowBox = document.getElementById("allowNonLocal"); + const rateInputs = [document.getElementById("rateMinute"), document.getElementById("rateHour"), document.getElementById("rateDay")]; + const limitAllBox = document.getElementById("limitAll"); + const allRates = [document.getElementById("allMinute"), document.getElementById("allHour"), document.getElementById("allDay")]; + function syncRates() { + const enabled = allowBox && allowBox.checked; + rateInputs.forEach(inp => { if (inp) inp.disabled = !enabled; }); + const allEnabled = limitAllBox && limitAllBox.checked; + allRates.forEach(inp => { if (inp) inp.disabled = !allEnabled; }); + } + if (allowBox) { + allowBox.addEventListener("change", syncRates); + } + if (limitAllBox) { + limitAllBox.addEventListener("change", syncRates); + } + syncRates(); + + function updateGauge(id, valueId, limitId) { + const fill = document.getElementById(id); + const valEl = document.getElementById(valueId); + const limitEl = document.getElementById(limitId); + if (!fill || !valEl || !limitEl) return; + const current = parseInt(valEl.textContent, 10) || 0; + const limit = parseInt(limitEl.textContent.replace('/', '').trim(), 10) || 0; + let pct = limit > 0 ? Math.min(100, Math.round((current / limit) * 100)) : 0; + fill.style.width = (limit > 0 ? pct : 100) + "%"; + fill.className = "gauge-fill " + (limit > 0 ? (pct < 60 ? "gauge-green" : pct < 90 ? "gauge-amber" : "gauge-red") : "gauge-neutral"); + } + updateGauge("gaugeMinute", "gaugeMinuteValue", "gaugeMinuteLimit"); + updateGauge("gaugeHour", "gaugeHourValue", "gaugeHourLimit"); + updateGauge("gaugeDay", "gaugeDayValue", "gaugeDayLimit"); + })(); + </script> + + #%env/templates/footer.template%# + </body> +</html> diff --git a/htroot/ConfigSearchPage_p.html b/htroot/ConfigSearchPage_p.html index 381006e24..a8e69849c 100644 --- a/htroot/ConfigSearchPage_p.html +++ b/htroot/ConfigSearchPage_p.html @@ -186,7 +186,7 @@ <div style="float: left;"> <fieldset class="yacys"> <div class="input-group"> - <input type="text" class="form-control searchinput typeahead" size="40" maxlength="80" placeholder="Web Search by the People, for the People" name="query" /> + <input type="text" class="form-control searchinput typeahead" size="40" maxlength="80" placeholder="search..." name="query" /> <div class="input-group-btn"> <button id="Enter" class="btn btn-default" type="submit">search</button> </div> diff --git a/htroot/Status.html b/htroot/Status.html index e2933c49f..3a7d16e4f 100644 --- a/htroot/Status.html +++ b/htroot/Status.html @@ -224,6 +224,8 @@ <!--[if IE]> </div> <![endif]--> + + <iframe src="/env/donate.html" style="border:none;"></iframe> #%env/templates/footer.template%# </body> diff --git a/htroot/env/templates/header.template b/htroot/env/templates/header.template index b42d9a2c2..360e94e52 100644 --- a/htroot/env/templates/header.template +++ b/htroot/env/templates/header.template @@ -106,7 +106,7 @@ <li> </li> <li id="header_community"> <form action="https://community.searchlab.eu" target="_blank" method="get"> - <button accesskey="f" type="submit" class="btn btn-inverse navbar-btn label-info" title="Community"> + <button accesskey="f" type="submit" class="btn btn-inverse navbar-btn label-primary" title="Community"> <span class="glyphicon glyphicon-user"></span> <span class="hidden-sm"> Forum</span> </button> @@ -149,13 +149,24 @@ <li><a href="#">Please help! We need financial help to move on with the development!</a></li> </ul> </li> + #(navigation-showChatLink)#:: <li> </li> - <li id="header_administration"> + <li id="header_chat"> + <form action="yacychat.html" method="get"> + <button accesskey="c" type="submit" class="btn btn-inverse navbar-btn label-info" title="Chat"> + <span class="glyphicon glyphicon-console"></span> + <span class="hidden-sm"> Chat</span> + </button> + </form> + </li> + #(/navigation-showChatLink)# + <li> </li> + <li id="header_search"> <form action="index.html" method="get"> #(authorized)#:: <input type="hidden" name="auth" value=""/> #(/authorized)# - <button accesskey="s" type="submit" class="btn btn-inverse navbar-btn label-primary" title="Search"> + <button accesskey="s" type="submit" class="btn btn-inverse navbar-btn label-info" title="Search"> <span class="glyphicon glyphicon-search"></span> <span class="hidden-sm"> Search</span> </button> @@ -178,8 +189,7 @@ > <li><h3>First Steps</h3></li> <li><a href="ConfigBasic.html" class="MenuItemLink">Use Case & Account</a></li> - <li><a href="CrawlStartSite.html" class="MenuItemLink">Load Web Pages, Crawler</a></li> - <li><a href="Performance_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">RAM/Disk Usage & Updates</a></li> + <li><a href="CrawlStartSite.html" class="MenuItemLink">Grab a whole site</a></li> </ul> #(navigation-advanced)#<script>$("#first-steps").popover();</script>::#(/navigation-advanced)# @@ -201,17 +211,19 @@ #(navigation-advanced)#:: <ul class="nav nav-sidebar menugroup"> <li><h3>Production</h3></li> - <li><a href="CrawlStartExpert.html" class="MenuItemLink">Advanced Crawler</a></li> - <li><a href="IndexPackGenerator_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Index Export/Import</a></li> + <li><a href="CrawlStartExpert.html" class="MenuItemLink">Crawler</a></li> + <li><a href="AILab.html" class="MenuItemLink">AI Lab</a></li> + <li><a href="IndexPackGenerator_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">YaCy Packs & Import/Export</a></li> + <li><a href="Table_API_p.html?sort=-date_recording" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Process Scheduler</a></li> <li><a href="Vocabulary_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Content Semantic</a></li> - <li><a href="CrawlCheck_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Target Analysis</a></li> + <li><a href="CrawlCheck_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Target Analysis</a></li> </ul> <ul class="nav nav-sidebar menugroup"> <li><h3>Administration</h3></li> <li><a href="IndexControlURLs_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Index Administration</a></li> <li><a href="Settings_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">System Administration</a></li> <li><a href="Blacklist_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Filter & Blacklists</a></li> - <li><a href="Table_API_p.html?sort=-date_recording" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">Process Scheduler</a></li> + <li><a href="Performance_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">RAM/Disk Usage & Updates</a></li> </ul> <ul class="nav nav-sidebar menugroup"> <li><h3>Search Portal Integration</h3></li> diff --git a/htroot/env/templates/simpleSearchHeader.template b/htroot/env/templates/simpleSearchHeader.template index 35fdf067c..5637778ca 100644 --- a/htroot/env/templates/simpleSearchHeader.template +++ b/htroot/env/templates/simpleSearchHeader.template @@ -40,6 +40,7 @@ <li id="header_websearch"><a href="index.html#(authSearch)#::?auth#(/authSearch)#" onclick="this.href='index.html?#(authSearch)#::auth&#(/authSearch)#former='+encodeURIComponent(document.searchform.search.value)">Web Search</a></li> <li id="header_filesearch"><a href="yacyinteractive.html" onclick="this.href='yacyinteractive.html?handover='+document.searchform.search.value">File Search</a></li> <li id="header_comparesearch"><a href="compare_yacy.html?display=0">Compare Search</a></li> + #(navigation-showChatLink)#::<li id="header_chat"><a href="yacychat.html">Chat</a></li>#(/navigation-showChatLink)# <li id="header_urlviewer"><a href="ViewFile.html">URL Viewer</a></li> <!--<li><a href="yacysearch_location.html">Location Search</a></li>--> <li class="divider" role="separator"></li> diff --git a/htroot/env/templates/simpleheader.template b/htroot/env/templates/simpleheader.template index 3112f4601..f58a66265 100644 --- a/htroot/env/templates/simpleheader.template +++ b/htroot/env/templates/simpleheader.template @@ -20,6 +20,7 @@ <li id="header_websearch"><a href="index.html#(authorized)#::?auth#(/authorized)#" onclick="this.href='index.html?#(authorized)#::auth&#(/authorized)#former='+encodeURIComponent(document.searchform.search.value)">Web Search</a></li> <li id="header_filesearch"><a href="yacyinteractive.html" onclick="this.href='yacyinteractive.html?handover='+document.searchform.search.value">File Search</a></li> <li id="header_comparesearch"><a href="compare_yacy.html?display=0">Compare Search</a></li> + #(navigation-showChatLink)#::<li id="header_chat"><a href="yacychat.html">Chat</a></li>#(/navigation-showChatLink)# <li id="header_urlviewer"><a href="ViewFile.html">URL Viewer</a></li> <!--<li><a href="yacysearch_location.html">Location Search</a></li>--> <li class="divider" role="separator"></li> diff --git a/htroot/env/templates/submenuAI.template b/htroot/env/templates/submenuAI.template index 614dff4a1..10a72c935 100644 --- a/htroot/env/templates/submenuAI.template +++ b/htroot/env/templates/submenuAI.template @@ -4,6 +4,7 @@ <li><a href="AILab.html" class="MenuItemLink">AI Lab</a></li> <li><a href="LLMSelection_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">LLM Selection</a></li> <li><a href="RAGConfig_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">RAG Config</a></li> + <li><a href="AIShield_p.html" class="MenuItemLink #(authorized)#lock::unlock#(/authorized)#">AI Shield</a></li> <li><a href="yacychat.html" class="MenuItemLink">Chat</a></li> </ul> </div> diff --git a/htroot/yacychat.html b/htroot/yacychat.html index 26ee71cd1..ff68f18cf 100644 --- a/htroot/yacychat.html +++ b/htroot/yacychat.html @@ -542,8 +542,14 @@ </style> </head> <body id="IndexControl"> - #%env/templates/header.template%# - #%env/templates/submenuAI.template%# + #(topmenu)# + #%env/templates/embeddedheader.template%# + :: + #%env/templates/simpleheader.template%# + :: + #%env/templates/header.template%# + #%env/templates/submenuAI.template%# + #(/topmenu)# <h2>YaCy Chat</h2> <p>Chat with your configured LLM using the local YaCy settings. Requests are sent to the same host that served this page.</p> @@ -1103,6 +1109,9 @@ body: JSON.stringify(payload) }); if (!response.ok) { + if (response.status === 429) { + throw new Error('Rate limit reached: please wait and try again. The server is protecting itself from overload.'); + } throw new Error(`Request failed: ${response.status} ${response.statusText}`); } if (!response.body) { |
