Files
changedetection.io/changedetectionio/blueprint/settings/templates/settings_llm_tab.html
T
2026-04-16 11:06:30 +02:00

86 lines
4.7 KiB
HTML

{% from '_helpers.html' import render_field %}
{#
AI/LLM settings tab content — included from settings.html.
Requires template context: form, llm_config, llm_env_configured
#}
<div class="tab-pane-inner" id="ai">
<fieldset>
<p>{{ _('Configure an AI/LLM provider to enable intent-based change filtering. Each watch or tag can have a plain-English intent — the AI evaluates every detected change against it and only notifies you when it matches.') }}</p>
{% if llm_env_configured %}
{# Config is coming from environment variables — hide the form #}
<div class="inline-warning" style="margin-bottom: 1em;">
<img class="inline-warning-icon" src="{{url_for('static_content', group='images', filename='notice.svg')}}" alt="{{ _('Note') }}">
{{ _('AI/LLM is configured via environment variables') }}
(<code>LLM_MODEL={{ llm_config.get('model', '') }}</code>{% if llm_config.get('api_base') %}, <code>LLM_API_BASE={{ llm_config.get('api_base') }}</code>{% endif %}).
{{ _('Remove the') }} <code>LLM_MODEL</code> {{ _('environment variable to configure via this form instead.') }}
</div>
{% else %}
<div class="pure-control-group">
<label>{{ _('Quick preset') }}</label>
<select id="llm-preset" onchange="applyLLMPreset(this.value)" class="pure-input-1-3">
<option value="">— {{ _('choose a preset or type your own below') }} —</option>
<optgroup label="OpenAI">
<option value="gpt-4o-mini|">gpt-4o-mini (fast, affordable)</option>
<option value="gpt-4o|">gpt-4o (most capable)</option>
</optgroup>
<optgroup label="Anthropic">
<option value="claude-3-5-haiku-20251001|">claude-3-5-haiku (fast)</option>
<option value="claude-sonnet-4-5|">claude-sonnet-4-5 (balanced)</option>
</optgroup>
<optgroup label="Google">
<option value="gemini/gemini-2.0-flash|">gemini-2.0-flash</option>
<option value="gemini/gemini-2.0-flash-lite|">gemini-2.0-flash-lite (free tier)</option>
</optgroup>
<optgroup label="OpenRouter (access 200+ models)">
<option value="openrouter/google/gemma-3-12b-it:free|">gemma-3-12b (free)</option>
<option value="openrouter/meta-llama/llama-4-scout:free|">llama-4-scout (free)</option>
<option value="openrouter/mistralai/mistral-small-3.1-24b-instruct:free|">mistral-small (free)</option>
</optgroup>
<optgroup label="{{ _('Local / Self-hosted') }}">
<option value="ollama/llama3.2|http://localhost:11434">ollama/llama3.2 (local)</option>
<option value="ollama/mistral|http://localhost:11434">ollama/mistral (local)</option>
</optgroup>
</select>
</div>
<div class="pure-control-group">
{{ render_field(form.llm.form.llm_model) }}
<span class="pure-form-message-inline">
{{ _('The model string encodes both provider and model — litellm routes automatically. Any') }}
<a href="https://docs.litellm.ai/docs/providers" target="_blank">{{ _('litellm-supported model') }}</a> {{ _('works here.') }}
</span>
</div>
<div class="pure-control-group">
{{ render_field(form.llm.form.llm_api_key) }}
</div>
<div class="pure-control-group">
{{ render_field(form.llm.form.llm_api_base) }}
<span class="pure-form-message-inline">{{ _('Only needed for Ollama or custom/self-hosted endpoints. Leave blank for cloud providers.') }}</span>
</div>
{% if llm_config and llm_config.get('model') %}
<div class="pure-control-group">
<span style="color: #4a7c59; font-weight: bold;">
&#10003; {{ _('AI configured:') }} {{ llm_config.get('model') }}
</span>
</div>
{% endif %}
<p class="pure-form-message-inline">
{{ _("Your API key is stored locally and sent only to your chosen provider. On each detected change, the watch's diff and extracted text are sent to the LLM — no full page HTML.") }}
</p>
{% endif %}{# llm_env_configured #}
</fieldset>
</div>
<script>
function applyLLMPreset(value) {
if (!value) return;
var parts = value.split('|');
var modelField = document.querySelector('[name="llm-llm_model"]');
var baseField = document.querySelector('[name="llm-llm_api_base"]');
if (modelField) modelField.value = parts[0] || '';
if (baseField) baseField.value = parts[1] || '';
}
</script>