Files
searxng/searx/templates/simple/preferences.html
T
jasonwitty 8edc368752 [mod] plugin: AI tab only when activated, user API key, grounding on
Three changes to the ai_summary plugin:

- The *AI Summary* preferences tab is only rendered when the plugin is
  activated in settings.yml.  An instance that does not offer AI
  summaries no longer shows an AI tab at all.  The gate is the
  administrator setting, not the user opt-out, because the per user
  on/off switch lives inside that tab -- hiding it on opt-out would
  leave no way to opt back in.

- Users can configure an API key for their own LLM server
  (ai_summary_api_key).  The administrator key is still only sent to
  base_url and the user key only to a server the user configured, so
  neither key can be captured through the other.  The setting is marked
  secret: credentials are excluded from the preferences URL, which users
  copy around to transfer or share their preferences.

- Grounding summaries on the search results is now the default; the
  extra cost of the longer prompt is moderate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 12:45:50 -07:00

277 lines
10 KiB
HTML

{%- from 'simple/icons.html' import icon_small, icon_big -%}
{%- extends "simple/page_with_header.html" -%}
{%- macro tabs_open() -%}
<div class="tabs" role="tablist">
{%- endmacro -%}
{%- macro tab_header(name, id, label, checked) -%}
<input type="radio" name="{{ name }}" id="tab-{{ id }}" {% if checked is sameas true %}checked="checked"{% endif %}>
<label id="tab-label-{{ id }}" for="tab-{{ id }}" role="tab" aria-controls="tab-content-{{ id }}">{{ label }}</label>
<section id="tab-content-{{ id }}" role="tabpanel" aria-hidden="false">
{%- endmacro -%}
{%- macro tab_footer() -%}
</section>
{%- endmacro -%}
{%- macro tabs_close() -%}
</div>
{%- endmacro -%}
{%- macro checkbox(name, checked, disabled) -%}
{%- if checked == '?' -%}
{{- icon_small('alert') -}}
{%- else -%}
<input type="checkbox" {%- if name %} name="{{ name }}" {%- endif %} value="None" {%- if checked %} checked {%- endif -%}{%- if disabled %} disabled {%- endif -%}>
{%- endif -%}
{%- endmacro -%}
{%- macro checkbox_onoff_reversed(name, checked, labelledby) -%}
<input type="checkbox" {{- ' ' -}}
name="{{ name }}" {{- ' ' -}}
id="{{ name }}" {{- ' ' -}}
{%- if labelledby -%} aria-labelledby="{{ labelledby }}"{{- ' ' -}}{%- endif -%}
class="checkbox-onoff reversed-checkbox"{{- ' ' -}}
{%- if checked -%} checked{%- endif -%}>
{%- endmacro -%}
{%- macro plugin_preferences(section) -%}
{%- for plugin in plugins_storage -%}
{%- if plugin.preference_section == section -%}
<fieldset>{{- '' -}}
<legend>{{ _(plugin.name) }}</legend>{{- '' -}}
<div class="value">
{{- checkbox_onoff_reversed('plugin_' + plugin.id, plugin.id not in allowed_plugins, 'plugin_labelledby' + plugin.id) -}}
</div>{{- '' -}}
<div class="description" id="{{ 'plugin_labelledby' + plugin.id }}">
{{- _(plugin.description) -}}
</div>{{- '' -}}
</fieldset>
{%- endif -%}
{%- endfor -%}
{%- endmacro -%}
{%- macro engine_about(search_engine) -%}
{%- if search_engine.about is defined -%}
{%- set about = search_engine.about -%}
<div class="engine-tooltip" role="tooltip">{{- '' -}}
<p class="engine-description"></p>{{- '' -}}
<p><a href="{{about.website}}" rel="noreferrer">{{about.website}}</a></p>{{- '' -}}
{%- if about.wikidata_id -%}
<p><a href="https://www.wikidata.org/wiki/{{about.wikidata_id}}" rel="noreferrer">wikidata.org/wiki/{{about.wikidata_id}}</a></p>
{%- endif -%}
{%- if search_engine.enable_http -%}
<p>{{- icon_big('exclamation-sign', 'No HTTPS') -}}{{- _('No HTTPS')-}}</p>
{% endif -%}
{%- if reliabilities.get(search_engine.name, {}).errors -%}
<a href="{{ url_for('stats', engine=search_engine.name|e) }}" {{- ' ' -}}
title="{{ _('View error logs and submit a bug report') }}">
{{- _('View error logs and submit a bug report') -}}
</a>
{%- endif -%}
<p>{{- '' -}}
<span class="right">{{ _("!bang for this engine") }}</span>{{- '' -}}
{%- for bang in [search_engine.name] + [search_engine.shortcut] -%}
<span class="bang"> {{ '!' + bang.replace(' ', '_') }}</span>
{%- endfor -%}
</p>{{- '' -}}
<p>{{- '' -}}
<span class="right">{{ _("!bang for its categories") }}</span>
{%- for bang in search_engine.categories -%}
<span class="bang">{{ '!' + bang.replace(' ', '_') }}</span>
{%- endfor -%}
</p>{{- '' -}}
</div>
{%- endif -%}
{%- endmacro -%}
{%- macro engine_time(engine_name) -%}
<td class="{{ label }}">{{- '' -}}
{%- if stats[engine_name].time != None -%}
<span class="stacked-bar-chart-value">{{- stats[engine_name].time -}}</span>{{- '' -}}
<span class="stacked-bar-chart" aria-hidden="true">
{%- if max_rate95 is not none and max_rate95 > 0 -%}
<div class="stacked-bar-chart-median bar{{ (100 * (stats[engine_name].time / max_rate95))|round }}"></div>{{- '' -}}
<div class="stacked-bar-chart-rate80 bar{{ (100 * ((stats[engine_name].rate80 - stats[engine_name].time) / max_rate95))|round }}"></div>{{- '' -}}
<div class="stacked-bar-chart-rate95 bar{{ (100 * ((stats[engine_name].rate95 - stats[engine_name].rate80) / max_rate95))|round }}"></div>{{- '' -}}
<span class="stacked-bar-chart-rate100"></span>
{%- endif -%}
</span>{{- '' -}}
<div class="engine-tooltip text-left" role="tooltip" id="{{engine_name}}_graph">{{- '' -}}
<p>{{ _('Median') }}: {{ stats[engine_name].time }}</p>{{- '' -}}
<p>{{ _('P80') }}: {{ stats[engine_name].rate80 }}</p>{{- '' -}}
<p>{{ _('P95') }}: {{ stats[engine_name].rate95 }}</p>{{- '' -}}
</div>
{%- endif -%}
</td>
{%- endmacro -%}
{%- macro engine_reliability(engine_name) -%}
{%- set r = reliabilities.get(engine_name, {}).get('reliability', None) -%}
{%- set errors = reliabilities.get(engine_name, {}).get('errors', []) -%}
{%- if r != None -%}
{%- if r <= 50 -%}
{% set label = 'danger' -%}
{%- elif r < 80 -%}
{%- set label = 'warning' -%}
{%- elif r < 90 %}
{%- set label = '' -%}
{%- else -%}
{%- set label = 'success' -%}
{%- endif -%}
{% else %}
{%- set r = '' -%}
{%- endif -%}
{%- if errors -%}
<td class="{{ label }} column-reliability">{{- '' -}}
<a href="{{ url_for('stats', engine=engine_name|e) }}">{{- '' -}}
<span>
{{- icon_big('alert', 'The engine is not reliabled') }} {{ r -}}
</span>{{- '' -}}
</a>{{- '' -}}
<div class="engine-tooltip" role="tooltip" id="{{engine_name}}_reliability">
{%- if errors -%}<p>{{ _('Errors:') }}</p>{%- endif -%}
{%- for error in errors -%}
<p>{{ error }}</p>{{- '' -}}
{%- endfor -%}
</div>{{- '' -}}
</td>
{%- else -%}
<td class="{{ label }}">{% if r %}<span>{{ r }}</span>
{%- endif -%}
</td>
{%- endif -%}
{%- endmacro -%}
{%- block head -%}{%- endblock -%}
{%- block linkto_preferences -%}{%- endblock -%}
{%- block content -%}
<h1>{{ _('Preferences') }}</h1>
{%- if request.args.get('preferences_preview_only') == 'true' -%}
<div class="dialog-warning-block">
<p>{{ _("This is a preview of the settings used by the 'Search URL' you used to get here.") }}</p>
<ul>
<li>{{ _('Press save to copy these preferences to your browser.') }}</li>
<li>{{ _('Click here to view your browser preferences instead:') }} <a href="{{ url_for('preferences') }}">/preferences</a></li>
</ul>
</div>
{%- endif -%}
<form id="search_form" method="post" action="{{ url_for('preferences') }}" autocomplete="off">
{{- tabs_open() -}}
{# tab: general #}
{{- tab_header('maintab', 'general', _('General'), True) -}}
{%- if 'categories' not in locked_preferences -%}
<fieldset>
<legend>{{- _('Default categories') -}}</legend>
{% set display_tooltip = false %}
{% include 'simple/categories.html' %}
</fieldset>
{%- endif -%}
{%- if 'language' not in locked_preferences -%}
{%- include 'simple/preferences/language.html' -%}
{% endif %}
{%- if 'autocomplete' not in locked_preferences -%}
{%- include 'simple/preferences/autocomplete.html' -%}
{%- endif -%}
{%- if 'favicon_resolver' not in locked_preferences -%}
{%- include 'simple/preferences/favicon.html' -%}
{%- endif -%}
{% if 'safesearch' not in locked_preferences %}
{%- include 'simple/preferences/safesearch.html' -%}
{%- endif -%}
{%- include 'simple/preferences/tokens.html' -%}
{{- plugin_preferences('general') -}}
{%- if 'doi_resolver' not in locked_preferences %}
{%- include 'simple/preferences/doi_resolver.html' -%}
{%- endif -%}
{{- tab_footer() -}}
{# tab: ui #}
{{- tab_header('maintab', 'ui', _('User interface')) -}}
{%- if 'locale' not in locked_preferences -%}
{%- include 'simple/preferences/ui_locale.html' -%}
{%- endif -%}
{%- if 'theme' not in locked_preferences -%}
{%- include 'simple/preferences/theme.html' -%}
{%- endif -%}
{%- if 'results_on_new_tab' not in locked_preferences -%}
{%- include 'simple/preferences/results_on_new_tab.html' -%}
{%- endif -%}
{%- if 'search_on_category_select' not in locked_preferences -%}
{%- include 'simple/preferences/search_on_category_select.html' -%}
{%- endif -%}
{%- include 'simple/preferences/hotkeys.html' -%}
{%- include 'simple/preferences/urlformatting.html' -%}
{{- plugin_preferences('ui') -}}
{{- tab_footer() -}}
{# tab: privacy #}
{{- tab_header('maintab', 'privacy', _('Privacy')) -}}
{%- if 'method' not in locked_preferences -%}
{%- include 'simple/preferences/method.html' -%}
{%- endif -%}
{%- if 'image_proxy' not in locked_preferences -%}
{%- include 'simple/preferences/image_proxy.html' -%}
{%- endif -%}
{%- if 'query_in_title' not in locked_preferences -%}
{%- include 'simple/preferences/query_in_title.html' -%}
{%- endif -%}
{{- plugin_preferences('privacy') -}}
{{- tab_footer() -}}
{# tab: enignes #}
{{- tab_header('maintab', 'engines', _('Engines')) -}}
<p>
{{- _('Currently used search engines') -}}
</p>
{{- tabs_open() -}}
{%- include 'simple/preferences/engines.html' -%}
{{- tabs_close() -}}
{{- tab_footer() -}}
{# tab: query #}
{{- tab_header('maintab', 'query', _('Special Queries')) -}}
{%- if answer_storage -%}
{%- include 'simple/preferences/answerers.html' -%}
{%- endif -%}
{{- tab_footer() -}}
{# tab: ai #}
{#- the tab is only shown when the administrator activated the plugin in
settings.yml, an instance without an AI summary has no AI tab -#}
{%- if 'ai_summary' in plugins_active_by_default
and plugins_storage | selectattr('preference_section', 'equalto', 'ai') | list -%}
{{- tab_header('maintab', 'ai', _('AI Summary')) -}}
{{- plugin_preferences('ai') -}}
{%- include 'simple/preferences/ai_summary.html' -%}
{{- tab_footer() -}}
{%- endif -%}
{# tab: cookies #}
{{- tab_header('maintab', 'cookies', _('Cookies')) -}}
{%- include 'simple/preferences/cookies.html' -%}
{{- tab_footer() -}}
{{- tabs_close() -}}
{# footer with save & reset buttons #}
{%- include 'simple/preferences/footer.html' -%}
</form>{{- '' -}}
{%- endblock -%}