feat: add LLM provider settings to web UI with encrypted API key storage

Add full LLM configuration section to the setup page with provider
dropdown, model/API key fields for cloud providers (Anthropic, OpenAI,
Gemini), and endpoint/model fields for Ollama. API keys are encrypted
with AES-256-GCM and stored in the database.
This commit is contained in:
2026-02-09 11:41:16 +01:00
parent c63e70cab0
commit a89720fded
8 changed files with 291 additions and 15 deletions

View File

@@ -37,6 +37,7 @@
<a href="#ac" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600">AC Units</a>
<a href="#toggles" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600">Toggles</a>
<a href="#forecast" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600">Forecast</a>
<a href="#llm" class="px-3 py-1 bg-gray-200 dark:bg-gray-700 rounded hover:bg-gray-300 dark:hover:bg-gray-600">LLM</a>
</div>
{{template "profiles" .}}
@@ -46,6 +47,7 @@
{{template "ac_units" .}}
{{template "toggles" .}}
{{template "forecast" .}}
{{template "llm" .}}
<footer class="mt-8 text-center text-xs text-gray-500 dark:text-gray-500 py-4">
<p>Heatwave Autopilot</p>
@@ -540,3 +542,47 @@
{{end}}
</section>
{{end}}
{{define "llm"}}
<section id="llm" class="mb-8">
<h2 class="text-xl font-semibold mb-3">LLM Provider</h2>
<form method="POST" action="/setup/llm/save" class="p-4 bg-white dark:bg-gray-800 rounded-lg shadow dark:shadow-gray-700">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="sm:col-span-2">
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Provider</label>
<select name="provider" id="llm-provider" class="w-full px-3 py-2 border dark:border-gray-600 rounded bg-white dark:bg-gray-700"
onchange="document.getElementById('llm-cloud').style.display=(this.value==='anthropic'||this.value==='openai'||this.value==='gemini')?'':'none';document.getElementById('llm-local').style.display=(this.value==='ollama')?'':'none';">
<option value="none" {{if eq .LLMProvider "none"}}selected{{end}}>None</option>
<option value="anthropic" {{if eq .LLMProvider "anthropic"}}selected{{end}}>Anthropic</option>
<option value="openai" {{if eq .LLMProvider "openai"}}selected{{end}}>OpenAI</option>
<option value="gemini" {{if eq .LLMProvider "gemini"}}selected{{end}}>Gemini</option>
<option value="ollama" {{if eq .LLMProvider "ollama"}}selected{{end}}>Ollama (local)</option>
</select>
</div>
<div id="llm-cloud" style="{{if or (eq .LLMProvider "anthropic") (eq .LLMProvider "openai") (eq .LLMProvider "gemini")}}{{else}}display:none{{end}}" class="sm:col-span-2 grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Model</label>
<input type="text" name="cloud_model" value="{{.LLMModel}}" placeholder="e.g. claude-sonnet-4-5-20250929" class="w-full px-3 py-2 border dark:border-gray-600 rounded bg-white dark:bg-gray-700">
</div>
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">API Key</label>
<input type="password" name="api_key" placeholder="Leave blank to keep current key" class="w-full px-3 py-2 border dark:border-gray-600 rounded bg-white dark:bg-gray-700">
</div>
</div>
<div id="llm-local" style="{{if eq .LLMProvider "ollama"}}{{else}}display:none{{end}}" class="sm:col-span-2 grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Endpoint URL</label>
<input type="text" name="endpoint" value="{{.LLMEndpoint}}" placeholder="http://localhost:11434" class="w-full px-3 py-2 border dark:border-gray-600 rounded bg-white dark:bg-gray-700">
</div>
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Model</label>
<input type="text" name="local_model" value="{{.LLMModel}}" placeholder="e.g. llama3" class="w-full px-3 py-2 border dark:border-gray-600 rounded bg-white dark:bg-gray-700">
</div>
</div>
</div>
<button type="submit" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Save LLM Settings</button>
</form>
</section>
{{end}}