Instruction: Create an AUTARCH web template for the 'Llm Settings' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: llm_settings.html
Sections: 6
Tabs: 2
Forms/API calls: 12
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}LLM Settings - AUTARCH{% endblock %}

{% block content %}
<div class="page-header" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
    <h1>LLM Settings</h1>
    <a href="{{ url_for('settings.index') }}" class="btn btn-sm" style="margin-left:auto">&larr; Back to Settings</a>
</div>

<!-- Backend Selector + Load Status -->
<div class="section">
    <h2>Active Backend</h2>
    <div class="tab-bar" id="llm-tab-bar">
        <button class="tab {% if llm_backend == 'local' or llm_backend == 'transformers' %}active{% endif %}"
                onclick="llmTab('local')">Local Model</button>
        <button class="tab {% if llm_backend == 'claude' %}active{% endif %}"
                onclick="llmTab('claude')">Claude</button>
        <button class="tab {% if llm_backend == 'openai' %}active{% endif %}"
                onclick="llmTab('openai')">OpenAI</button>
        <button class="tab {% if llm_backend == 'huggingface' %}active{% endif %}"
                onclick="llmTab('huggingface')">HuggingFace</button>
    </div>
    <p style="font-size:0.8rem;color:var(--text-secondary);margin-top:0.5rem">
        Configured backend: <strong style="color:var(--accent)">{{ llm_backend }}</strong>
        — select a tab, fill in settings, and click <em>Save &amp; Activate</em>, then <em>Load Model</em> to initialise.
    </p>

    <!-- Load / Status bar -->
    <div style="display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;margin-top:0.9rem;
                padding:0.65rem 0.9rem;border-radius:var(--radius);border:1px solid var(--border);
                background:var(--bg-card)">
        <div id="llm-status-dot" style="width:10px;height:10px;border-radius:50%;
             background:var(--text-muted);flex-shrink:0" title="Not loaded"></div>
        <span id="llm-status-text" style="font-size:0.83rem;color:var(--text-secondary);flex:1">
            Not loaded — click <strong>Load Model</strong> to initialise the current backend.
        </span>
        <button id="btn-llm-load" class="btn btn-primary btn-sm" onclick="loadLLM()">
            Load Model
        </button>
        <button class="btn btn-sm" onclick="debugOpen()"
                title="Open debug console to see detailed load output">
            Debug Log
        </button>
    </div>
    <p style="font-size:0.75rem;color:var(--text-muted);margin-top:0.4rem">
        Local GGUF models may take 10–60 s to load depending on size.
        The page will wait — check the Debug Log for live output.
    </p>
</div>

<!-- ══════════════════════════════════════════════════════════════════ -->
<!-- LOCAL MODEL TAB                                                    -->
<!-- ══════════════════════════════════════════════════════════════════ -->
<div id="tab-local" class="llm-tab-panel {% if llm_backend != 'local' and llm_backend != 'transformers' %}hidden{% endif %}">

    <!-- Model Folder + File Picker -->
    <div class="section">
        <h2>Model Selection</h2>
        <div class="form-group">
            <label for="model-dir">Models Folder</label>
            <div style="display:flex;gap:0.5rem">
                <input type="text" id="model-dir" value="{{ default_models_dir }}" style="flex:1" placeholder="/path/to/models">
                <button class="btn btn-sm btn-primary" onclick="scanModels()">Scan</button>
            </div>
            <p style="font-size:0.78rem;color:var(--text-secondary);margin-top:0.3rem">
                Scans for .gguf, .ggml, .bin files and SafeTensors model directories.
            </p>
        </div>

        <div id="model-list-wrap" style="display:none">
            <label>Available Models</label>
            <div id="model-list" style="margin-top:0.4rem;max-height:240px;overflow-y:auto;border:1px solid var(--border);border-radius:4px"></div>
        </div>

        <div class="form-group" style="margin-top:0.75rem">
            <label for="selected-model-path">Selected Model Path</label>
            <input type="text" id="selected-model-path"
                   value="{{ llama.model_path if llm_backend == 'local' else transformers.model_path }}"
                   placeholder="Click a model above or enter path manually" style="font-family:monospace;font-size:0.82rem">
        </div>

        <div class="form-group">
            <label class="checkbox-label">
                <input type="checkbox" id="safetensors-chk"
                       {% if llm_backend == 'transformers' %}checked{% endif %}
                       onchange="llmToggleSafetensors(this.checked)">
                SafeTensors / Transformers mode (uncheck for llama.cpp GGUF)
            </label>
        </div>
    </div>

    <!-- llama.cpp Parameters -->
    <div id="llamacpp-params" class="section {% if llm_backend == 'transformers' %}hidden{% endif %}">
        <h2>llama.cpp Parameters</h2>
        <form id="form-local" method="POST" action="{{ url_for('settings.update_llm') }}" class="settings-form">
            <input type="hidden" name="backend" value="local">
            <input type="hidden" id="llama-model-hidden" name="model_path" value="{{ llama.model_path }}">
            <input type="hidden" id="gpu-backend-input" name="gpu_backend" value="{{ llama.get('gpu_backend', 'cpu') }}">

            <!-- ── GPU / Compute Backend Selector ─────────────────────────── -->
            <h3 style="margin-top:0;font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">GPU / Compute Backend</h3>
            <div class="gpu-presets" id="gpu-presets">

                <div class="gpu-card {% if llama.get('gpu_backend','cpu') == 'cuda' %}gpu-selected{% endif %}"
                     data-backend="cuda" onclick="selectGpuPreset('cuda')">
                    <div class="gpu-card-icon" style="color:#76b900">&#x26A1;</div>
                    <div class="gpu-card-name">CUDA</div>
                    <div class="gpu-card-ram" style="color:#76b900">7.5 GB VRAM</div>
                    <div class="gpu-card-chip">NVIDIA GPU</div>
                    <div class="gpu-card-badge" style="background:rgba(118,185,0,0.12);border-color:rgba(118,185,0,0.3);color:#76b900">n_gpu_layers = -1</div>
                </div>

                <div class="gpu-card {% if llama.get('gpu_backend','cpu') == 'vulkan' %}gpu-selected{% endif %}"
                     data-backend="vulkan" onclick="selectGpuPreset('vulkan')">
                    <div class="gpu-card-icon" style="color:#0071c5">&#x25C6;</div>
                    <div class="gpu-card-name">Intel · Vulkan</div>
                    <div class="gpu-card-ram" style="color:#0071c5">15 GB RAM</div>
                    <div class="gpu-card-chip">Intel GPU / iGPU</div>
                    <div class="gpu-card-badge" style="background:rgba(0,113,197,0.12);border-color:rgba(0,113,197,0.3);color:#5ba4e8">n_gpu_layers = -1</div>
                </div>

                <div class="gpu-card {% if llama.get('gpu_backend','cpu') != 'cuda' and llama.get('gpu_backend','cpu') != 'vulkan' %}gpu-selected{% endif %}"
                     data-backend="cpu" onclick="selectGpuPreset('cpu')">
                    <div class="gpu-card-icon" style="color:var(--text-secondary)">&#x1F5A5;</div>
                    <div class="gpu-card-name">CPU Only</div>
                    <div class="gpu-card-ram" style="color:var(--text-secondary)">24 GB RAM</div>
                    <div class="gpu-card-chip">Any CPU</div>
                    <div class="gpu-card-badge" style="background:rgba(128,128,128,0.1);border-color:rgba(128,128,128,0.25);color:#888">n_gpu_layers = 0</div>
                </div>

            </div>

            <!-- Install hint shown after selecting CUDA/Vulkan -->
            <div id="gpu-hint" class="gpu-hint" style="display:none"></div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em;margin-top:1.25rem">Context &amp; Threading</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="llama-n-ctx">Context Size (n_ctx)</label>
                    <input type="number" id="llama-n-ctx" name="n_ctx" value="{{ llama.n_ctx }}" min="128" max="131072" step="128">
                    <small>Token window. Higher = more memory.</small>
                </div>
                <div class="form-group">
                    <label for="llama-n-threads">CPU Threads</label>
                    <input type="number" id="llama-n-threads" name="n_threads" value="{{ llama.n_threads }}" min="1" max="128">
                    <small>Match physical cores.</small>
                </div>
                <div class="form-group">
                    <label for="llama-n-gpu">GPU Layers</label>
                    <input type="number" id="llama-n-gpu" name="n_gpu_layers" value="{{ llama.n_gpu_layers }}" min="-1">
                    <small>-1 = all, 0 = CPU only.</small>
                </div>
                <div class="form-group">
                    <label for="llama-n-batch">Batch Size (n_batch)</label>
                    <input type="number" id="llama-n-batch" name="n_batch" value="{{ llama.get('n_batch', 512) }}" min="1" max="4096">
                    <small>Prompt processing batch.</small>
                </div>
            </div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">Sampling / Generation</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="llama-temp">Temperature</label>
                    <input type="number" id="llama-temp" name="temperature" value="{{ llama.temperature }}" step="0.05" min="0" max="2">
                    <small>0 = deterministic, 1 = creative.</small>
                </div>
                <div class="form-group">
                    <label for="llama-top-p">Top-P</label>
                    <input type="number" id="llama-top-p" name="top_p" value="{{ llama.top_p }}" step="0.05" min="0" max="1">
                    <small>Nucleus sampling threshold.</small>
                </div>
                <div class="form-group">
                    <label for="llama-top-k">Top-K</label>
                    <input type="number" id="llama-top-k" name="top_k" value="{{ llama.top_k }}" min="0" max="200">
                    <small>0 = disabled.</small>
                </div>
                <div class="form-group">
                    <label for="llama-repeat">Repeat Penalty</label>
                    <input type="number" id="llama-repeat" name="repeat_penalty" value="{{ llama.repeat_penalty }}" step="0.05" min="1" max="2">
                    <small>Penalises repeated tokens.</small>
                </div>
                <div class="form-group">
                    <label for="llama-max-tok">Max Tokens</label>
                    <input type="number" id="llama-max-tok" name="max_tokens" value="{{ llama.max_tokens }}" min="1" max="131072">
                    <small>Max new tokens generated.</small>
                </div>
                <div class="form-group">
                    <label for="llama-seed">Seed</label>
                    <input type="number" id="llama-seed" name="seed" value="{{ llama.seed }}">
                    <small>-1 = random.</small>
                </div>
            </div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">RoPE &amp; Mirostat</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="llama-rope">RoPE Scaling</label>
                    <select id="llama-rope" name="rope_scaling_type">
                        <option value="0" {% if llama.get('rope_scaling_type', '0') == '0' %}selected{% endif %}>None (0)</option>
                        <option value="1" {% if llama.get('rope_scaling_type', '0') == '1' %}selected{% endif %}>Linear (1)</option>
                        <option value="2" {% if llama.get('rope_scaling_type', '0') == '2' %}selected{% endif %}>YaRN (2)</option>
                    </select>
                    <small>Extends context via RoPE.</small>
                </div>
                <div class="form-group">
                    <label for="llama-mirostat">Mirostat Mode</label>
                    <select id="llama-mirostat" name="mirostat_mode">
                        <option value="0" {% if llama.get('mirostat_mode', '0') == '0' %}selected{% endif %}>Off (0)</option>
                        <option value="1" {% if llama.get('mirostat_mode', '0') == '1' %}selected{% endif %}>v1 (1)</option>
                        <option value="2" {% if llama.get('mirostat_mode', '0') == '2' %}selected{% endif %}>v2 (2)</option>
                    </select>
                    <small>Replaces top-p/k sampling.</small>
                </div>
                <div class="form-group">
                    <label for="llama-m-tau">Mirostat Tau</label>
                    <input type="number" id="llama-m-tau" name="mirostat_tau" value="{{ llama.get('mirostat_tau', 5.0) }}" step="0.5" min="0">
                    <small>Target entropy (5.0 default).</small>
                </div>
                <div class="form-group">
                    <label for="llama-m-eta">Mirostat Eta</label>
                    <input type="number" id="llama-m-eta" name="mirostat_eta" value="{{ llama.get('mirostat_eta', 0.1) }}" step="0.01" min="0">
                    <small>Learning rate (0.1 default).</small>
                </div>
            </div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">Performance Flags</h3>
            <div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:1rem">
                <label class="checkbox-label">
                    <input type="checkbox" name="flash_attn" {% if llama.get('flash_attn', False) %}checked{% endif %}>
                    Flash Attention (faster on supported hardware)
                </label>
            </div>

            <button type="submit" class="btn btn-primary" onclick="syncLocalPath('llama')">Save &amp; Activate llama.cpp</button>
        </form>
    </div>

    <!-- Transformers / SafeTensors Parameters -->
    <div id="transformers-params" class="section {% if llm_backend != 'transformers' %}hidden{% endif %}">
        <h2>Transformers / SafeTensors Parameters</h2>
        <form id="form-transformers" method="POST" action="{{ url_for('settings.update_llm') }}" class="settings-form">
            <input type="hidden" name="backend" value="transformers">
            <input type="hidden" id="tf-model-hidden" name="model_path" value="{{ transformers.model_path }}">

            <h3 style="margin-top:0;font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">Device &amp; Precision</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="tf-device">Device</label>
                    <select id="tf-device" name="device">
                        <option value="auto" {% if transformers.device == 'auto' %}selected{% endif %}>auto</option>
                        <option value="cpu" {% if transformers.device == 'cpu' %}selected{% endif %}>cpu</option>
                        <option value="cuda" {% if transformers.device == 'cuda' %}selected{% endif %}>cuda</option>
                        <option value="cuda:0" {% if transformers.device == 'cuda:0' %}selected{% endif %}>cuda:0</option>
                        <option value="cuda:1" {% if transformers.device == 'cuda:1' %}selected{% endif %}>cuda:1</option>
                        <option value="mps" {% if transformers.device == 'mps' %}selected{% endif %}>mps (Apple)</option>
                        <option value="xpu" {% if transformers.device == 'xpu' %}selected{% endif %}>xpu (Intel)</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="tf-dtype">Torch Dtype</label>
                    <select id="tf-dtype" name="torch_dtype">
                        <option value="auto" {% if transformers.torch_dtype == 'auto' %}selected{% endif %}>auto</option>
                        <option value="float16" {% if transformers.torch_dtype == 'float16' %}selected{% endif %}>float16</option>
                        <option value="bfloat16" {% if transformers.torch_dtype == 'bfloat16' %}selected{% endif %}>bfloat16</option>
                        <option value="float32" {% if transformers.torch_dtype == 'float32' %}selected{% endif %}>float32</option>
                    </select>
                </div>
            </div>
            <div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:0.75rem">
                <label class="checkbox-label">
                    <input type="checkbox" name="load_in_8bit" {% if transformers.load_in_8bit %}checked{% endif %}>
                    8-bit quantization (bitsandbytes)
                </label>
                <label class="checkbox-label">
                    <input type="checkbox" name="load_in_4bit" {% if transformers.load_in_4bit %}checked{% endif %}>
                    4-bit quantization (bitsandbytes)
                </label>
                <label class="checkbox-label">
                    <input type="checkbox" name="llm_int8_enable_fp32_cpu_offload"
                           {% if transformers.llm_int8_enable_fp32_cpu_offload %}checked{% endif %}>
                    FP32 CPU Offload
                    <small style="color:var(--text-muted);margin-left:0.3rem">(enables partial GPU+CPU loading for 8-bit)</small>
                </label>
                <label class="checkbox-label">
                    <input type="checkbox" name="trust_remote_code" {% if transformers.trust_remote_code %}checked{% endif %}>
                    Trust Remote Code
                </label>
            </div>
            <div class="form-group" style="max-width:340px;margin-bottom:0.75rem">
                <label for="tf-device-map">Device Map</label>
                <input type="text" id="tf-device-map" name="device_map"
                       value="{{ transformers.get('device_map', 'auto') }}"
                       placeholder="auto">
                <small>
                    <code>auto</code> (default) — let transformers decide &nbsp;|&nbsp;
                    <code>cpu</code> — CPU only &nbsp;|&nbsp;
                    <code>cuda:0</code> — single GPU. Enable FP32 CPU Offload above if 8-bit gives a "dispatched on CPU" error.
                </small>
            </div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">Tokenizer</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="tf-pad-side">Padding Side</label>
                    <select id="tf-pad-side" name="padding_side">
                        <option value="left" {% if transformers.get('padding_side', 'left') == 'left' %}selected{% endif %}>left</option>
                        <option value="right" {% if transformers.get('padding_side', 'left') == 'right' %}selected{% endif %}>right</option>
                    </select>
                    <small>left = causal LM standard.</small>
                </div>
            </div>
            <div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:0.75rem">
                <label class="checkbox-label">
                    <input type="checkbox" name="use_fast_tokenizer"
                           {% if transformers.get('use_fast_tokenizer', True) %}checked{% endif %}>
                    Use Fast Tokenizer (Rust-backed)
                </label>
            </div>

            <h3 style="font-size:0.9rem;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.05em">Generation</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="tf-max-tok">Max New Tokens</label>
                    <input type="number" id="tf-max-tok" name="max_tokens" value="{{ transformers.max_tokens }}" min="1" max="131072">
                </div>
                <div class="form-group">
                    <label for="tf-temp">Temperature</label>
                    <input type="number" id="tf-temp" name="temperature" value="{{ transformers.temperature }}" step="0.05" min="0" max="2">
                </div>
                <div class="form-group">
                    <label for="tf-top-p">Top-P</label>
                    <input type="number" id="tf-top-p" name="top_p" value="{{ transformers.top_p }}" step="0.05" min="0" max="1">
                </div>
                <div class="form-group">
                    <label for="tf-top-k">Top-K</label>
                    <input type="number" id="tf-top-k" name="top_k" value="{{ transformers.top_k }}" min="0" max="200">
                    <small>0 = disabled.</small>
                </div>
                <div class="form-group">
                    <label for="tf-rep-pen">Repetition Penalty</label>
                    <input type="number" id="tf-rep-pen" name="repetition_penalty" value="{{ transformers.repetition_penalty }}" step="0.05" min="1" max="2">
                </div>
                <div class="form-group">
                    <label for="tf-beams">Num Beams</label>
                    <input type="number" id="tf-beams" name="num_beams" value="{{ transformers.get('num_beams', 1) }}" min="1" max="16">
                    <small>1 = greedy/sampling.</small>
                </div>
            </div>
            <div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:1rem">
                <label class="checkbox-label">
                    <input type="checkbox" name="do_sample"
                           {% if transformers.get('do_sample', True) %}checked{% endif %}>
                    Do Sample (enables temp/top-p/top-k)
                </label>
            </div>

            <button type="submit" class="btn btn-primary" onclick="syncLocalPath('tf')">Save &amp; Activate Transformers</button>
        </form>
    </div>

</div><!-- end tab-local -->

<!-- ══════════════════════════════════════════════════════════════════ -->
<!-- CLAUDE TAB                                                         -->
<!-- ══════════════════════════════════════════════════════════════════ -->
<div id="tab-claude" class="llm-tab-panel {% if llm_backend != 'claude' %}hidden{% endif %}">
    <div class="section">
        <h2>Claude API</h2>
        <p style="font-size:0.82rem;color:var(--text-secondary)">
            Requires an <a href="https://console.anthropic.com" target="_blank" rel="noopener">Anthropic API</a> key.
            AUTARCH calls Claude directly for chat, agent, and analysis tasks.
        </p>

        <div class="form-group">
            <label for="claude-key">API Key</label>
            <input type="password" id="claude-key" value="{{ claude.api_key }}" placeholder="sk-ant-api03-...">
            <small>Stored in autarch_settings.conf — keep it safe.</small>
        </div>

        <div class="form-group">
            <label for="claude-model">Model</label>
            <div style="display:flex;gap:0.5rem;align-items:center">
                <select id="claude-model" style="flex:1">
                    <option value="{{ claude.model }}" selected>{{ claude.model }}</option>
                </select>
                <button class="btn btn-sm" onclick="claudeFetchModels()" id="btn-claude-refresh" title="Fetch available models from API">Refresh</button>
            </div>
            <small id="claude-model-hint">Click <strong>Refresh</strong> to fetch available models from the API.</small>
        </div>

        <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
            <div class="form-group">
                <label for="claude-max-tok">Max Tokens</label>
                <input type="number" id="claude-max-tok" value="{{ claude.max_tokens }}" min="1" max="200000">
            </div>
            <div class="form-group">
                <label for="claude-temp">Temperature</label>
                <input type="number" id="claude-temp" value="{{ claude.temperature }}" step="0.05" min="0" max="1">
                <small>0–1. Claude default is 1.</small>
            </div>
            <div class="form-group">
                <label for="claude-top-p">Top-P</label>
                <input type="number" id="claude-top-p" value="{{ claude.get('top_p', 1.0) }}" step="0.05" min="0" max="1">
                <small>Use with lower temp.</small>
            </div>
            <div class="form-group">
                <label for="claude-top-k">Top-K</label>
                <input type="number" id="claude-top-k" value="{{ claude.get('top_k', 0) }}" min="0">
                <small>0 = disabled.</small>
            </div>
        </div>

        <!-- Activate Claude button + inline status -->
        <div style="display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap;margin-top:0.5rem">
            <button class="btn btn-primary" onclick="claudeActivate()" id="btn-claude-activate">
                Activate Claude
            </button>
            <div id="claude-status-dot" style="width:10px;height:10px;border-radius:50%;
                 background:{% if llm_backend == 'claude' %}var(--success, #34c759){% else %}var(--text-muted){% endif %};flex-shrink:0"></div>
            <span id="claude-status-text" style="font-size:0.83rem;color:var(--text-secondary)">
                {% if llm_backend == 'claude' %}Active — {{ claude.model }}{% else %}Not active{% endif %}
            </span>
        </div>
    </div>
</div><!-- end tab-claude -->

<!-- ══════════════════════════════════════════════════════════════════ -->
<!-- OPENAI TAB                                                         -->
<!-- ══════════════════════════════════════════════════════════════════ -->
<div id="tab-openai" class="llm-tab-panel {% if llm_backend != 'openai' %}hidden{% endif %}">
    <div class="section">
        <h2>OpenAI API</h2>
        <p style="font-size:0.82rem;color:var(--text-secondary)">
            Also compatible with any OpenAI-format endpoint: LiteLLM, Ollama (<code>/v1</code>), vLLM, LocalAI, etc.
            Just set the Base URL to your local server.
        </p>
        <form method="POST" action="{{ url_for('settings.update_llm') }}" class="settings-form">
            <input type="hidden" name="backend" value="openai">

            <div class="form-group">
                <label for="oai-key">API Key</label>
                <input type="password" id="oai-key" name="api_key" value="{{ openai.api_key }}" placeholder="sk-...">
                <small>Leave blank for local servers (Ollama, vLLM, etc.).</small>
            </div>

            <div class="form-group">
                <label for="oai-base-url">Base URL</label>
                <input type="text" id="oai-base-url" name="base_url" value="{{ openai.base_url }}" placeholder="https://api.openai.com/v1">
                <small>Change to point to local LLM servers.</small>
            </div>

            <div class="form-group">
                <label for="oai-model">Model</label>
                <input type="text" id="oai-model" name="model" value="{{ openai.model }}" placeholder="gpt-4o" list="oai-model-list">
                <datalist id="oai-model-list">
                    <option value="gpt-4o">
                    <option value="gpt-4o-mini">
                    <option value="gpt-4-turbo">
                    <option value="gpt-3.5-turbo">
                    <option value="o1-preview">
                    <option value="o1-mini">
                    <option value="o3-mini">
                </datalist>
                <small>Type any model ID, or pick from suggestions.</small>
            </div>

            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="oai-max-tok">Max Tokens</label>
                    <input type="number" id="oai-max-tok" name="max_tokens" value="{{ openai.max_tokens }}" min="1" max="128000">
                </div>
                <div class="form-group">
                    <label for="oai-temp">Temperature</label>
                    <input type="number" id="oai-temp" name="temperature" value="{{ openai.temperature }}" step="0.1" min="0" max="2">
                    <small>0–2. 1 = default.</small>
                </div>
                <div class="form-group">
                    <label for="oai-top-p">Top-P</label>
                    <input type="number" id="oai-top-p" name="top_p" value="{{ openai.top_p }}" step="0.05" min="0" max="1">
                </div>
                <div class="form-group">
                    <label for="oai-freq-pen">Frequency Penalty</label>
                    <input type="number" id="oai-freq-pen" name="frequency_penalty" value="{{ openai.frequency_penalty }}" step="0.1" min="-2" max="2">
                    <small>Reduce repetition.</small>
                </div>
                <div class="form-group">
                    <label for="oai-pres-pen">Presence Penalty</label>
                    <input type="number" id="oai-pres-pen" name="presence_penalty" value="{{ openai.presence_penalty }}" step="0.1" min="-2" max="2">
                    <small>Encourage new topics.</small>
                </div>
            </div>

            <button type="submit" class="btn btn-primary">Save &amp; Activate OpenAI</button>
        </form>
    </div>
</div><!-- end tab-openai -->

<!-- ══════════════════════════════════════════════════════════════════ -->
<!-- HUGGINGFACE TAB                                                    -->
<!-- ══════════════════════════════════════════════════════════════════ -->
<div id="tab-huggingface" class="llm-tab-panel {% if llm_backend != 'huggingface' %}hidden{% endif %}">
    <div class="section">
        <h2>HuggingFace Inference API</h2>

        <!-- Token / Login -->
        <h3>Account</h3>
        <div class="form-group">
            <label for="hf-token">HuggingFace Token</label>
            <div style="display:flex;gap:0.5rem">
                <input type="password" id="hf-token" value="{{ huggingface.api_key }}" placeholder="hf_..." style="flex:1">
                <button class="btn btn-sm btn-primary" type="button" onclick="hfVerifyToken()">Verify Token</button>
            </div>
            <small>Get yours at <a href="https://huggingface.co/settings/tokens" target="_blank" rel="noopener">huggingface.co/settings/tokens</a></small>
        </div>
        <div id="hf-account-info" style="display:none;padding:0.5rem 0.75rem;border-radius:4px;background:rgba(0,255,65,0.08);border:1px solid var(--success);font-size:0.82rem;margin-bottom:0.75rem"></div>

        <form id="form-hf" method="POST" action="{{ url_for('settings.update_llm') }}" class="settings-form">
            <input type="hidden" name="backend" value="huggingface">
            <input type="hidden" id="hf-api-key-hidden" name="api_key" value="{{ huggingface.api_key }}">

            <!-- Model -->
            <h3>Model</h3>
            <div class="form-group">
                <label for="hf-model">Model ID</label>
                <div style="display:flex;gap:0.5rem">
                    <input type="text" id="hf-model" name="model" value="{{ huggingface.model }}"
                           placeholder="mistralai/Mistral-7B-Instruct-v0.3" style="flex:1">
                    <a href="https://huggingface.co/models?pipeline_tag=text-generation&sort=trending" target="_blank"
                       rel="noopener" class="btn btn-sm">Browse HF</a>
                </div>
                <small>Full model repo ID (e.g. <code>meta-llama/Llama-3.1-8B-Instruct</code>).</small>
            </div>

            <div class="form-group">
                <label for="hf-provider">Inference Provider</label>
                <select id="hf-provider" name="provider">
                    <option value="auto" {% if huggingface.provider == 'auto' %}selected{% endif %}>auto (HF selects best)</option>
                    <option value="cerebras" {% if huggingface.provider == 'cerebras' %}selected{% endif %}>Cerebras</option>
                    <option value="together-ai" {% if huggingface.provider == 'together-ai' %}selected{% endif %}>Together AI</option>
                    <option value="sambanova" {% if huggingface.provider == 'sambanova' %}selected{% endif %}>SambaNova</option>
                    <option value="novita" {% if huggingface.provider == 'novita' %}selected{% endif %}>Novita</option>
                    <option value="cohere" {% if huggingface.provider == 'cohere' %}selected{% endif %}>Cohere</option>
                    <option value="fireworks-ai" {% if huggingface.provider == 'fireworks-ai' %}selected{% endif %}>Fireworks AI</option>
                    <option value="hf-inference" {% if huggingface.provider == 'hf-inference' %}selected{% endif %}>HF Inference (default)</option>
                </select>
                <small>Provider used for serverless inference. Some require separate accounts.</small>
            </div>

            <div class="form-group">
                <label for="hf-endpoint">Custom Endpoint URL (optional)</label>
                <input type="text" id="hf-endpoint" name="endpoint" value="{{ huggingface.endpoint }}"
                       placeholder="https://your-endpoint.endpoints.huggingface.cloud">
                <small>Overrides provider. Use for Dedicated Inference Endpoints.</small>
            </div>

            <!-- Generation Parameters -->
            <h3>Generation Parameters</h3>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="hf-max-tok">Max New Tokens</label>
                    <input type="number" id="hf-max-tok" name="max_tokens" value="{{ huggingface.max_tokens }}" min="1" max="32768">
                </div>
                <div class="form-group">
                    <label for="hf-temp">Temperature</label>
                    <input type="number" id="hf-temp" name="temperature" value="{{ huggingface.temperature }}" step="0.05" min="0.01" max="2">
                </div>
                <div class="form-group">
                    <label for="hf-top-p">Top-P</label>
                    <input type="number" id="hf-top-p" name="top_p" value="{{ huggingface.top_p }}" step="0.05" min="0" max="1">
                </div>
                <div class="form-group">
                    <label for="hf-top-k">Top-K</label>
                    <input type="number" id="hf-top-k" name="top_k" value="{{ huggingface.top_k }}" min="0" max="200">
                    <small>0 = disabled.</small>
                </div>
                <div class="form-group">
                    <label for="hf-rep-pen">Repetition Penalty</label>
                    <input type="number" id="hf-rep-pen" name="repetition_penalty" value="{{ huggingface.repetition_penalty }}" step="0.05" min="1" max="2">
                </div>
                <div class="form-group">
                    <label for="hf-seed">Seed</label>
                    <input type="number" id="hf-seed" name="seed" value="{{ huggingface.seed }}">
                    <small>-1 = random.</small>
                </div>
            </div>

            <div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-bottom:0.75rem">
                <label class="checkbox-label">
                    <input type="checkbox" name="do_sample" {% if huggingface.do_sample %}checked{% endif %}>
                    Do Sample
                </label>
            </div>

            <div class="form-group">
                <label for="hf-stop">Stop Sequences (comma-separated)</label>
                <input type="text" id="hf-stop" name="stop_sequences" value="{{ huggingface.stop_sequences }}"
                       placeholder="&lt;|endoftext|&gt;, &lt;|im_end|&gt;">
                <small>Tokens that will stop generation.</small>
            </div>

            <button type="submit" class="btn btn-primary">Save &amp; Activate HuggingFace</button>
        </form>
    </div>
</div><!-- end tab-huggingface -->

<!-- ══════════════════════════════════════════════════════════════════ -->
<!-- AGENTS SECTION                                                      -->
<!-- ══════════════════════════════════════════════════════════════════ -->
<div class="section" style="margin-top:2rem">
    <h2>Agent Configuration</h2>
    <p style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:0.75rem">
        Configure the AI agent backends used by Agent Hal and Autonomy.
        Agents use the THOUGHT/ACTION/PARAMS loop to accomplish tasks with tools.
    </p>

    <!-- Agent sub-tabs -->
    <div class="tab-bar" id="agent-tab-bar">
        <button class="tab active" onclick="agentTab('local')">Local Agent</button>
        <button class="tab" onclick="agentTab('claude')">Claude Agent</button>
        <button class="tab" onclick="agentTab('openai')">OpenAI Agent</button>
    </div>

    <!-- ── Local Agent Sub-Tab ──────────────────────────────────────── -->
    <div id="agent-tab-local" class="agent-tab-panel">
        <div style="padding:1rem 0">
            <p style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:1rem">
                Uses the currently loaded LLM backend (configured above) for agent operations.
                Best for offline or privacy-sensitive work.
            </p>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="agent-local-steps">Max Steps</label>
                    <input type="number" id="agent-local-steps" value="{{ agents.local_max_steps }}" min="1" max="100">
                    <small>Maximum tool-use steps per task.</small>
                </div>
                <div class="form-group">
                    <label for="agent-local-verbose">
                        <input type="checkbox" id="agent-local-verbose" {% if agents.local_verbose %}checked{% endif %}
                               style="margin-right:0.4rem">
                        Verbose Output
                    </label>
                    <small>Show step-by-step agent reasoning.</small>
                </div>
            </div>
            <div style="display:flex;align-items:center;gap:0.75rem;margin-top:0.5rem">
                <button class="btn btn-primary btn-sm" onclick="agentSave('local')">Save Local Agent Settings</button>
                <span id="agent-local-status" style="font-size:0.82rem;color:var(--text-secondary)"></span>
            </div>
        </div>
    </div>

    <!-- ── Claude Agent Sub-Tab ─────────────────────────────────────── -->
    <div id="agent-tab-claude" class="agent-tab-panel hidden">
        <div style="padding:1rem 0">
            <p style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:1rem">
                Uses the Anthropic Claude API with native tool use for agent operations.
                Requires a Claude API key (configured in the Claude tab above).
                Supports extended thinking and structured tool calls.
            </p>
            <div class="form-group">
                <label for="agent-claude-model">Agent Model</label>
                <div style="display:flex;gap:0.5rem;align-items:center">
                    <select id="agent-claude-model" style="flex:1">
                        <option value="{{ agents.claude_model }}" selected>{{ agents.claude_model }}</option>
                    </select>
                    <button class="btn btn-sm" onclick="agentClaudeFetchModels()" id="btn-agent-claude-refresh">Refresh</button>
                </div>
                <small id="agent-claude-model-hint">Click Refresh to fetch models from the API. Uses the API key from the Claude tab.</small>
            </div>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="agent-claude-tokens">Max Tokens</label>
                    <input type="number" id="agent-claude-tokens" value="{{ agents.claude_max_tokens }}" min="1" max="200000">
                </div>
                <div class="form-group">
                    <label for="agent-claude-steps">Max Steps</label>
                    <input type="number" id="agent-claude-steps" value="{{ agents.claude_max_steps }}" min="1" max="200">
                </div>
                <div class="form-group">
                    <label for="agent-claude-enabled">
                        <input type="checkbox" id="agent-claude-enabled" {% if agents.claude_enabled %}checked{% endif %}
                               style="margin-right:0.4rem">
                        Enable Claude Agent
                    </label>
                    <small>Allow agent tasks to use Claude.</small>
                </div>
            </div>
            <div style="display:flex;align-items:center;gap:0.75rem;margin-top:0.5rem">
                <button class="btn btn-primary btn-sm" onclick="agentSave('claude')">Save Claude Agent Settings</button>
                <span id="agent-claude-status" style="font-size:0.82rem;color:var(--text-secondary)"></span>
            </div>
        </div>
    </div>

    <!-- ── OpenAI Agent Sub-Tab ─────────────────────────────────────── -->
    <div id="agent-tab-openai" class="agent-tab-panel hidden">
        <div style="padding:1rem 0">
            <p style="font-size:0.82rem;color:var(--text-secondary);margin-bottom:1rem">
                Uses the OpenAI API (or any compatible endpoint: Ollama, vLLM, LiteLLM) with function calling for agent operations.
                Requires an API key configured in the OpenAI tab above.
            </p>
            <div class="form-group">
                <label for="agent-openai-model">Agent Model</label>
                <input type="text" id="agent-openai-model" value="{{ agents.openai_model }}" placeholder="gpt-4o"
                       list="agent-openai-model-list">
                <datalist id="agent-openai-model-list">
                    <option value="gpt-4o">
                    <option value="gpt-4o-mini">
                    <option value="gpt-4.1">
                    <option value="gpt-4.1-mini">
                    <option value="gpt-4.1-nano">
                    <option value="o3">
                    <option value="o4-mini">
                </datalist>
                <small>Model ID. For local servers (Ollama, etc.) use the name you pulled.</small>
            </div>
            <div class="form-group">
                <label for="agent-openai-base-url">Base URL</label>
                <input type="text" id="agent-openai-base-url" value="{{ agents.openai_base_url }}" placeholder="https://api.openai.com/v1">
                <small>Change for Ollama (<code>http://localhost:11434/v1</code>), vLLM, LiteLLM, etc.</small>
            </div>
            <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0.75rem 1rem">
                <div class="form-group">
                    <label for="agent-openai-tokens">Max Tokens</label>
                    <input type="number" id="agent-openai-tokens" value="{{ agents.openai_max_tokens }}" min="1" max="200000">
                </div>
                <div class="form-group">
                    <label for="agent-openai-steps">Max Steps</label>
                    <input type="number" id="agent-openai-steps" value="{{ agents.openai_max_steps }}" min="1" max="200">
                </div>
                <div class="form-group">
                    <label for="agent-openai-enabled">
                        <input type="checkbox" id="agent-openai-enabled" {% if agents.openai_enabled %}checked{% endif %}
                               style="margin-right:0.4rem">
                        Enable OpenAI Agent
                    </label>
                    <small>Allow agent tasks to use OpenAI.</small>
                </div>
            </div>
            <div style="display:flex;align-items:center;gap:0.75rem;margin-top:0.5rem">
                <button class="btn btn-primary btn-sm" onclick="agentSave('openai')">Save OpenAI Agent Settings</button>
                <span id="agent-openai-status" style="font-size:0.82rem;color:var(--text-secondary)"></span>
            </div>
        </div>
    </div>

    <!-- Active agent backend indicator -->
    <div style="margin-top:0.75rem;padding:0.5rem 0.75rem;border-radius:var(--radius);border:1px solid var(--border);
                background:var(--bg-card);font-size:0.82rem;color:var(--text-secondary)">
        Active agent backend: <strong id="agent-active-backend" style="color:var(--accent)">{{ agents.backend }}</strong>
        — The agent will use this backend when processing tasks.
    </div>
</div>

<style>
.hidden { display: none !important; }
.form-group small { display:block; font-size:0.72rem; color:var(--text-muted); margin-top:0.2rem; }
.agent-tab-panel.hidden { display: none !important; }

/* ── GPU Preset Cards ───────────────────────────────────────────────────── */
.gpu-presets {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
@media (max-width: 600px) { .gpu-presets { grid-template-columns: 1fr; } }

.gpu-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.85rem 1rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 0.18rem;
    transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
    user-select: none;
    background: var(--bg-card);
}
.gpu-card:hover {
    border-color: var(--accent);
    box-shadow: 0 0 12px rgba(0,255,65,0.07);
    background: rgba(0,255,65,0.03);
}
.gpu-card.gpu-selected {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px rgba(0,255,65,0.18);
    background: rgba(0,255,65,0.04);
}
.gpu-card-icon   { font-size: 1.3rem; line-height: 1; margin-bottom: 0.15rem; }
.gpu-card-name   { font-size: 0.95rem; font-weight: 700; color: var(--text-primary); }
.gpu-card-ram    { font-size: 0.88rem; font-weight: 700; }
.gpu-card-chip   { font-size: 0.72rem; color: var(--text-muted); }
.gpu-card-badge  {
    display: inline-block;
    margin-top: 0.35rem;
    padding: 1px 7px;
    border-radius: 3px;
    font-size: 0.68rem;
    font-family: monospace;
    border: 1px solid;
    font-weight: 600;
    align-self: flex-start;
}
.gpu-hint {
    font-size: 0.78rem;
    color: var(--text-secondary);
    padding: 0.45rem 0.75rem;
    border-left: 3px solid var(--accent);
    background: rgba(0,255,65,0.04);
    border-radius: 0 4px 4px 0;
    margin-bottom: 0.75rem;
}
</style>

<script>
// ── Load Model ────────────────────────────────────────────────────────────────
function loadLLM() {
    var btn  = document.getElementById('btn-llm-load');
    var dot  = document.getElementById('llm-status-dot');
    var text = document.getElementById('llm-status-text');

    btn.disabled  = true;
    btn.textContent = 'Loading…';
    dot.style.background  = '#f59e0b';  // amber = in-progress
    dot.title = 'Loading…';
    text.innerHTML = '<em>Initialising — please wait. Check the Debug Log window for live output…</em>';

    fetch('/settings/llm/load', {method: 'POST'})
        .then(function(r) { return r.json(); })
        .then(function(d) {
            btn.disabled    = false;
            btn.textContent = 'Load Model';
            if (d.ok) {
                dot.style.background = 'var(--success, #34c759)';
                dot.title = 'Loaded';
                text.innerHTML = '&#x2713; <strong style="color:var(--success,#34c759)">'
                    + escapeHtml(d.backend) + '</strong> ready &mdash; '
                    + escapeHtml(d.model_name);
            } else {
                dot.style.background = 'var(--danger, #ff3b30)';
                dot.title = 'Error';
                text.innerHTML = '&#x2715; <strong style="color:var(--danger,#ff3b30)">Load failed:</strong> '
                    + escapeHtml(d.error || 'Unknown error')
                    + ' &mdash; <em>check Debug Log for details</em>';
            }
        })
        .catch(function(e) {
            btn.disabled    = false;
            btn.textContent = 'Load Model';
            dot.style.background = 'var(--danger, #ff3b30)';
            dot.title = 'Error';
            text.textContent = 'Request failed: ' + e.message;
        });
}

// ── Tab switching ─────────────────────────────────────────────────────────────
function llmTab(name) {
    ['local','claude','openai','huggingface'].forEach(function(t) {
        document.getElementById('tab-' + t).classList.toggle('hidden', t !== name);
    });
    document.querySelectorAll('#llm-tab-bar .tab').forEach(function(btn, i) {
        btn.classList.toggle('active', ['local','claude','openai','huggingface'][i] === name);
    });
}

// SafeTensors toggle
function llmToggleSafetensors(isST) {
    document.getElementById('llamacpp-params').classList.toggle('hidden', isST);
    document.getElementById('transformers-params').classList.toggle('hidden', !isST);
}

// Sync the hidden model_path input from the selected-model-path field
function syncLocalPath(target) {
    var val = document.getElementById('selected-model-path').value;
    if (target === 'llama') {
        document.getElementById('llama-model-hidden').value = val;
    } else {
        document.getElementById('tf-model-hidden').value = val;
    }
}

// Scan models folder
function scanModels() {
    var folder = document.getElementById('model-dir').value.trim();
    if (!folder) { alert('Enter a folder path first.'); return; }
    var btn = event.target;
    btn.textContent = 'Scanning...';
    btn.disabled = true;

    fetch('/settings/llm/scan-models', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({folder: folder})
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        btn.textContent = 'Scan';
        btn.disabled = false;
        if (!d.ok) { alert('Scan error: ' + d.error); return; }
        var wrap = document.getElementById('model-list-wrap');
        var list = document.getElementById('model-list');
        list.innerHTML = '';
        if (!d.models.length) {
            list.innerHTML = '<div style="padding:0.75rem;color:var(--text-secondary);font-size:0.82rem">No supported model files found.</div>';
        } else {
            d.models.forEach(function(m) {
                var row = document.createElement('div');
                row.style.cssText = 'display:flex;align-items:center;gap:0.75rem;padding:0.4rem 0.75rem;cursor:pointer;border-bottom:1px solid var(--border);font-size:0.82rem';
                row.onmouseenter = function() { this.style.background = 'var(--hover)'; };
                row.onmouseleave = function() { this.style.background = ''; };
                var badge = m.type === 'safetensors' ? '<span style="background:#5a3f9a;color:#fff;border-radius:3px;padding:1px 5px;font-size:0.7rem">ST</span>' :
                            '<span style="background:#1a6e3c;color:#fff;border-radius:3px;padding:1px 5px;font-size:0.7rem">GGUF</span>';
                row.innerHTML = badge + '<span style="flex:1;font-family:monospace">' + escapeHtml(m.name) + '</span>'
                    + '<span style="color:var(--text-secondary)">' + m.size_mb + ' MB</span>';
                row.onclick = function() {
                    document.getElementById('selected-model-path').value = m.path;
                    // Auto-toggle SafeTensors checkbox
                    var isST = m.type === 'safetensors';
                    document.getElementById('safetensors-chk').checked = isST;
                    llmToggleSafetensors(isST);
                    // Highlight selected
                    list.querySelectorAll('div').forEach(function(r2) { r2.style.background = ''; r2.style.outline = ''; });
                    this.style.background = 'var(--hover)';
                    this.style.outline = '1px solid var(--accent)';
                };
                list.appendChild(row);
            });
        }
        wrap.style.display = '';
    })
    .catch(function(e) { btn.textContent = 'Scan'; btn.disabled = false; alert('Request failed: ' + e.message); });
}

// ── GPU Preset Selector ───────────────────────────────────────────────────────
const _GPU_PRESETS = {
    cuda:   {
        n_gpu_layers: -1, n_batch: 512,
        hint: '⚡ CUDA selected — all layers offloaded to NVIDIA GPU (n_gpu_layers = -1). ' +
              'Requires llama-cpp-python compiled with CUDA: ' +
              'CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python --force-reinstall',
    },
    vulkan: {
        n_gpu_layers: -1, n_batch: 512,
        hint: '◆ Vulkan (Intel) selected — all layers offloaded via Vulkan API (n_gpu_layers = -1). ' +
              'Requires llama-cpp-python compiled with Vulkan: ' +
              'CMAKE_ARGS="-DGGML_VULKAN=on" pip install llama-cpp-python --force-reinstall',
    },
    cpu:    {
        n_gpu_layers: 0, n_batch: 256,
        hint: '',   // no hint needed — always works
    },
};

function selectGpuPreset(backend) {
    // Update card highlight
    document.querySelectorAll('.gpu-card').forEach(function(c) {
        c.classList.toggle('gpu-selected', c.dataset.backend === backend);
    });
    // Store selection
    document.getElementById('gpu-backend-input').value = backend;
    // Apply preset values
    var p = _GPU_PRESETS[backend];
    document.getElementById('llama-n-gpu').value   = p.n_gpu_layers;
    document.getElementById('llama-n-batch').value = p.n_batch;
    // Show / hide hint
    var hint = document.getElementById('gpu-hint');
    if (p.hint) {
        hint.textContent  = p.hint;
        hint.style.display = '';
    } else {
        hint.style.display = 'none';
    }
}

// Initialise hint on page load if a non-CPU preset is already saved
(function() {
    var saved = document.getElementById('gpu-backend-input').value;
    if (saved && saved !== 'cpu') {
        var h = document.getElementById('gpu-hint');
        var p = _GPU_PRESETS[saved];
        if (p && p.hint) { h.textContent = p.hint; h.style.display = ''; }
    }
})();

// ── HuggingFace token verify
function hfVerifyToken() {
    var token = document.getElementById('hf-token').value.trim();
    if (!token) { alert('Enter a token first.'); return; }
    document.getElementById('hf-api-key-hidden').value = token;
    var btn = event.target;
    btn.textContent = 'Verifying...';
    btn.disabled = true;
    fetch('/settings/llm/hf-verify', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({token: token})
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        btn.textContent = 'Verify Token';
        btn.disabled = false;
        var info = document.getElementById('hf-account-info');
        if (d.ok) {
            info.style.display = '';
            info.textContent = '✓ Logged in as: ' + d.username + (d.email ? ' (' + d.email + ')' : '');
        } else {
            info.style.display = '';
            info.style.borderColor = 'var(--danger)';
            info.style.background = 'rgba(255,59,48,0.08)';
            info.textContent = '✕ Invalid token: ' + d.error;
        }
    })
    .catch(function(e) { btn.textContent = 'Verify Token'; btn.disabled = false; alert('Request failed: ' + e.message); });
}

// ── Claude: Activate (save + load in one click) ──────────────────────────────
function claudeActivate() {
    var btn  = document.getElementById('btn-claude-activate');
    var dot  = document.getElementById('claude-status-dot');
    var text = document.getElementById('claude-status-text');

    btn.disabled = true;
    btn.textContent = 'Saving…';
    dot.style.background = '#f59e0b';
    text.textContent = 'Saving settings…';

    // Build form data from the Claude tab fields
    var fd = new FormData();
    fd.append('backend', 'claude');
    fd.append('api_key', document.getElementById('claude-key').value);
    fd.append('model', document.getElementById('claude-model').value);
    fd.append('max_tokens', document.getElementById('claude-max-tok').value);
    fd.append('temperature', document.getElementById('claude-temp').value);

    // Step 1: Save settings via the existing update_llm route (no redirect — use fetch)
    fetch('/settings/llm', {method: 'POST', body: fd, redirect: 'manual'})
        .then(function() {
            // Step 2: Now load the backend
            btn.textContent = 'Loading…';
            text.innerHTML = '<em>Initialising Claude — please wait…</em>';
            return fetch('/settings/llm/load', {method: 'POST'});
        })
        .then(function(r) { return r.json(); })
        .then(function(d) {
            btn.disabled = false;
            btn.textContent = 'Activate Claude';
            if (d.ok) {
                dot.style.background = 'var(--success, #34c759)';
                text.innerHTML = '&#x2713; <strong style="color:var(--success,#34c759)">Claude active</strong> — '
                    + escapeHtml(d.model_name);
                // Also update the top-level status bar
                var topDot  = document.getElementById('llm-status-dot');
                var topText = document.getElementById('llm-status-text');
                if (topDot) { topDot.style.background = 'var(--success, #34c759)'; topDot.title = 'Loaded'; }
                if (topText) { topText.innerHTML = '&#x2713; <strong style="color:var(--success,#34c759)">claude</strong> ready — ' + escapeHtml(d.model_name); }
            } else {
                dot.style.background = 'var(--danger, #ff3b30)';
                text.innerHTML = '&#x2715; <strong style="color:var(--danger,#ff3b30)">Load failed:</strong> '
                    + escapeHtml(d.error || 'Unknown error')
                    + ' — <em>check Debug Log for details</em>';
            }
        })
        .catch(function(e) {
            btn.disabled = false;
            btn.textContent = 'Activate Claude';
            dot.style.background = 'var(--danger, #ff3b30)';
            text.textContent = 'Request failed: ' + e.message;
        });
}

// ── Agent tab switching ───────────────────────────────────────────────────────
function agentTab(name) {
    // Hide all agent panels
    var panels = document.querySelectorAll('.agent-tab-panel');
    for (var i = 0; i < panels.length; i++) panels[i].classList.add('hidden');
    // Deactivate all agent tabs
    var tabs = document.querySelectorAll('#agent-tab-bar .tab');
    for (var i = 0; i < tabs.length; i++) tabs[i].classList.remove('active');
    // Show selected
    var panel = document.getElementById('agent-tab-' + name);
    if (panel) panel.classList.remove('hidden');
    // Activate tab button (find by text match)
    var labels = {local: 'Local Agent', claude: 'Claude Agent', openai: 'OpenAI Agent'};
    for (var i = 0; i < tabs.length; i++) {
        if (tabs[i].textContent.trim() === labels[name]) tabs[i].classList.add('active');
    }
}

// ── Agent save ───────────────────────────────────────────────────────────────
function agentSave(tab) {
    var status = document.getElementById('agent-' + tab + '-status');
    var data = {backend: tab};

    if (tab === 'local') {
        data.local_max_steps = parseInt(document.getElementById('agent-local-steps').value) || 20;
        data.local_verbose = document.getElementById('agent-local-verbose').checked;
    } else if (tab === 'claude') {
        data.claude_enabled = document.getElementById('agent-claude-enabled').checked;
        data.claude_model = document.getElementById('agent-claude-model').value;
        data.claude_max_tokens = parseInt(document.getElementById('agent-claude-tokens').value) || 16384;
        data.claude_max_steps = parseInt(document.getElementById('agent-claude-steps').value) || 30;
    } else if (tab === 'openai') {
        data.openai_enabled = document.getElementById('agent-openai-enabled').checked;
        data.openai_model = document.getElementById('agent-openai-model').value;
        data.openai_base_url = document.getElementById('agent-openai-base-url').value;
        data.openai_max_tokens = parseInt(document.getElementById('agent-openai-tokens').value) || 16384;
        data.openai_max_steps = parseInt(document.getElementById('agent-openai-steps').value) || 30;
    }

    status.textContent = 'Saving…';
    fetch('/settings/agents/save', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(data)
    })
    .then(function(r) { return r.json(); })
    .then(function(d) {
        if (d.ok) {
            status.innerHTML = '<span style="color:var(--success,#34c759)">&#x2713; Saved</span>';
            document.getElementById('agent-active-backend').textContent = tab;
        } else {
            status.innerHTML = '<span style="color:var(--danger,#ff3b30)">&#x2715; ' + escapeHtml(d.error || 'Error') + '</span>';
        }
        setTimeout(function() { status.textContent = ''; }, 4000);
    })
    .catch(function(e) {
        status.textContent = 'Failed: ' + e.message;
    });
}

// ── Agent Claude: Fetch models (reuses the same API endpoint) ────────────────
function agentClaudeFetchModels() {
    var btn  = document.getElementById('btn-agent-claude-refresh');
    var sel  = document.getElementById('agent-claude-model');
    var hint = document.getElementById('agent-claude-model-hint');
    var curVal = sel.value;

    btn.disabled = true;
    btn.textContent = 'Fetching…';
    hint.textContent = 'Querying Anthropic API…';

    fetch('/settings/llm/claude-models', {method: 'POST'})
        .then(function(r) { return r.json(); })
        .then(function(d) {
            btn.disabled = false;
            btn.textContent = 'Refresh';
            if (!d.ok) {
                hint.textContent = 'Error: ' + (d.error || 'Unknown');
                return;
            }
            sel.innerHTML = '';
            var models = d.models || [];
            if (models.length === 0) {
                hint.textContent = 'No models returned.';
                var opt = document.createElement('option');
                opt.value = curVal; opt.textContent = curVal; opt.selected = true;
                sel.appendChild(opt);
                return;
            }
            var foundCurrent = false;
            for (var i = 0; i < models.length; i++) {
                var m = models[i];
                var opt = document.createElement('option');
                opt.value = m.id;
                opt.textContent = m.name || m.id;
                if (m.id === curVal) { opt.selected = true; foundCurrent = true; }
                sel.appendChild(opt);
            }
            if (!foundCurrent && curVal) {
                var opt = document.createElement('option');
                opt.value = curVal; opt.textContent = curVal + ' (current)'; opt.selected = true;
                sel.insertBefore(opt, sel.firstChild);
            }
            hint.textContent = models.length + ' models available.';
        })
        .catch(function(e) {
            btn.disabled = false;
            btn.textContent = 'Refresh';
            hint.textContent = 'Failed: ' + e.message;
        });
}

// ── Claude: Fetch models from API ────────────────────────────────────────────
function claudeFetchModels() {
    var btn    = document.getElementById('btn-claude-refresh');
    var sel    = document.getElementById('claude-model');
    var hint   = document.getElementById('claude-model-hint');
    var curVal = sel.value;

    btn.disabled = true;
    btn.textContent = 'Fetching…';
    hint.textContent = 'Querying Anthropic API…';

    fetch('/settings/llm/claude-models', {method: 'POST'})
        .then(function(r) { return r.json(); })
        .then(function(d) {
            btn.disabled = false;
            btn.textContent = 'Refresh';
            if (!d.ok) {
                hint.textContent = 'Error: ' + (d.error || 'Unknown error');
                return;
            }
            // Rebuild the dropdown with live models
            sel.innerHTML = '';
            var models = d.models || [];
            if (models.length === 0) {
                hint.textContent = 'No models returned — check API key permissions.';
                var opt = document.createElement('option');
                opt.value = curVal; opt.textContent = curVal; opt.selected = true;
                sel.appendChild(opt);
                return;
            }
            var foundCurrent = false;
            for (var i = 0; i < models.length; i++) {
                var m = models[i];
                var opt = document.createElement('option');
                opt.value = m.id;
                opt.textContent = m.name || m.id;
                if (m.id === curVal) { opt.selected = true; foundCurrent = true; }
                sel.appendChild(opt);
            }
            // Keep current value selected even if not in list
            if (!foundCurrent && curVal) {
                var opt = document.createElement('option');
                opt.value = curVal; opt.textContent = curVal + ' (current)'; opt.selected = true;
                sel.insertBefore(opt, sel.firstChild);
            }
            hint.textContent = models.length + ' models available.';
        })
        .catch(function(e) {
            btn.disabled = false;
            btn.textContent = 'Refresh';
            hint.textContent = 'Request failed: ' + e.message;
        });
}
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Llm Trainer' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: llm_trainer.html
Sections: 6
Tabs: 3
Forms/API calls: 0
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}LLM Trainer - AUTARCH{% endblock %}

{% block content %}
<div class="page-header" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
    <div>
        <h1>LLM Trainer</h1>
        <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
            Fine-tune language models on the AUTARCH codebase. Generate datasets, train LoRA adapters, convert to GGUF.
        </p>
    </div>
    <a href="{{ url_for('analyze.index') }}" class="btn btn-sm" style="margin-left:auto">&larr; Analyze</a>
</div>

<!-- Tab Bar -->
<div class="tab-bar">
    <button class="tab active" data-tab-group="trainer" data-tab="dataset" onclick="showTab('trainer','dataset')">Dataset</button>
    <button class="tab" data-tab-group="trainer" data-tab="training" onclick="showTab('trainer','training')">Training</button>
    <button class="tab" data-tab-group="trainer" data-tab="convert" onclick="showTab('trainer','convert')">Convert</button>
    <button class="tab" data-tab-group="trainer" data-tab="evaluate" onclick="showTab('trainer','evaluate')">Evaluate</button>
</div>

<!-- ==================== DATASET TAB ==================== -->
<div class="tab-content active" data-tab-group="trainer" data-tab="dataset">
<div class="section">
    <h2>Codebase Scan</h2>
    <div class="tool-actions">
        <button id="btn-scan" class="btn btn-primary" onclick="trScanCodebase()">Scan Codebase</button>
    </div>
    <div id="scan-result" style="margin-top:12px"></div>
</div>

<div class="section">
    <h2>Generate Training Dataset</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Extracts code, architecture Q&A, and module creation examples from the AUTARCH codebase into JSONL training data.
    </p>
    <div class="form-row" style="margin-bottom:12px">
        <div class="form-group">
            <label>Format</label>
            <select id="ds-format">
                <option value="sharegpt">ShareGPT (conversations)</option>
                <option value="instruction">Alpaca (instruction/output)</option>
            </select>
        </div>
    </div>
    <div style="margin-bottom:12px">
        <label><input type="checkbox" id="ds-source" checked> Include source code understanding</label><br>
        <label><input type="checkbox" id="ds-qa" checked> Include architecture Q&A</label><br>
        <label><input type="checkbox" id="ds-modules" checked> Include module creation examples</label>
    </div>
    <div class="tool-actions">
        <button id="btn-generate" class="btn btn-primary" onclick="trGenerateDataset()">Generate Dataset</button>
    </div>
    <pre class="output-panel" id="gen-result" style="margin-top:12px;min-height:0"></pre>
</div>

<div class="section">
    <h2>Saved Datasets</h2>
    <div class="tool-actions">
        <button class="btn btn-small" onclick="trListDatasets()">Refresh</button>
    </div>
    <div id="datasets-list" style="margin-top:12px">
        <p class="empty-state">Click "Refresh" to load datasets.</p>
    </div>
</div>
</div>

<!-- ==================== TRAINING TAB ==================== -->
<div class="tab-content" data-tab-group="trainer" data-tab="training">
<div class="section">
    <h2>LoRA Fine-Tuning</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Fine-tune a base model with LoRA adapters on your generated dataset. Requires a HuggingFace model ID or local path.
    </p>

    <div class="form-row" style="margin-bottom:8px">
        <div class="form-group" style="flex:2">
            <label>Base Model (local path)</label>
            <div class="input-row">
                <input type="text" id="tr-base-model" placeholder="Click Browse or paste a local model path...">
                <button class="btn btn-small" onclick="trOpenBrowser()">Browse</button>
            </div>
        </div>
        <div class="form-group" style="flex:1">
            <label>Dataset</label>
            <select id="tr-dataset">
                <option value="">Select dataset...</option>
            </select>
        </div>
    </div>

    <!-- File Browser Modal -->
    <div id="model-browser-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:1000;display:none;align-items:center;justify-content:center">
        <div style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;width:90%;max-width:700px;max-height:80vh;display:flex;flex-direction:column;padding:16px">
            <div style="display:flex;align-items:center;gap:8px;margin-bottom:12px">
                <h3 style="margin:0;flex:1">Select Base Model</h3>
                <button class="btn btn-small" onclick="trCloseBrowser()">Close</button>
            </div>
            <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
                <button class="btn btn-small" onclick="trBrowseUp()" title="Parent directory">&uarr; Up</button>
                <code id="browser-path" style="flex:1;font-size:0.78rem;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap"></code>
            </div>
            <div id="browser-entries" style="flex:1;overflow-y:auto;border:1px solid var(--border);border-radius:4px;font-size:0.82rem"></div>
            <p style="font-size:0.72rem;color:var(--text-muted);margin:8px 0 0">Select a directory containing config.json + model weights, or navigate to find your model.</p>
        </div>
    </div>

    <details style="margin-bottom:12px">
        <summary style="cursor:pointer;font-size:0.85rem;color:var(--accent)">Advanced Settings</summary>
        <div class="form-row" style="margin-top:8px">
            <div class="form-group">
                <label>LoRA Rank (r)</label>
                <input type="number" id="tr-lora-r" value="16" min="4" max="128" style="max-width:80px">
            </div>
            <div class="form-group">
                <label>LoRA Alpha</label>
                <input type="number" id="tr-lora-alpha" value="32" min="8" max="256" style="max-width:80px">
            </div>
            <div class="form-group">
                <label>Dropout</label>
                <input type="number" id="tr-dropout" value="0.05" step="0.01" min="0" max="0.5" style="max-width:80px">
            </div>
            <div class="form-group">
                <label>Epochs</label>
                <input type="number" id="tr-epochs" value="3" min="1" max="20" style="max-width:80px">
            </div>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label>Batch Size</label>
                <input type="number" id="tr-batch" value="4" min="1" max="32" style="max-width:80px">
            </div>
            <div class="form-group">
                <label>Grad Accum Steps</label>
                <input type="number" id="tr-grad-accum" value="4" min="1" max="32" style="max-width:80px">
            </div>
            <div class="form-group">
                <label>Learning Rate</label>
                <input type="text" id="tr-lr" value="2e-4" style="max-width:100px">
            </div>
            <div class="form-group">
                <label>Max Seq Length</label>
                <input type="number" id="tr-seq-len" value="2048" min="512" max="8192" style="max-width:100px">
            </div>
        </div>
        <div style="margin-top:4px">
            <label><input type="checkbox" id="tr-4bit" checked> Use 4-bit quantization (QLoRA)</label><br>
            <label><input type="checkbox" id="tr-unsloth"> Use Unsloth (faster, if installed)</label>
        </div>
    </details>

    <div class="tool-actions">
        <button id="btn-train-start" class="btn btn-primary" onclick="trStartTraining()">Start Training</button>
        <button id="btn-train-stop" class="btn btn-danger" onclick="trStopTraining()" style="display:none">Stop Training</button>
    </div>

    <div id="training-status" style="margin-top:12px">
        <div class="stats-grid" style="grid-template-columns:repeat(auto-fit,minmax(120px,1fr))">
            <div class="stat-card">
                <div class="stat-label">Phase</div>
                <div class="stat-value small" id="tr-phase">Idle</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Progress</div>
                <div class="stat-value small" id="tr-progress">0%</div>
            </div>
            <div class="stat-card">
                <div class="stat-label">Status</div>
                <div class="stat-value small" id="tr-message">Ready</div>
            </div>
        </div>
    </div>

    <h4 style="margin-top:16px">Training Log</h4>
    <pre class="output-panel scrollable" id="training-log" style="max-height:300px;font-size:0.75rem">Training log will appear here...</pre>
</div>
</div>

<!-- ==================== CONVERT TAB ==================== -->
<div class="tab-content" data-tab-group="trainer" data-tab="convert">
<div class="section">
    <h2>GGUF Conversion</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Merge a trained LoRA adapter with its base model and convert to GGUF format for local inference.
    </p>

    <h4>Saved LoRA Adapters</h4>
    <div class="tool-actions">
        <button class="btn btn-small" onclick="trListAdapters()">Refresh Adapters</button>
    </div>
    <div id="adapters-list" style="margin-top:8px">
        <p class="empty-state">Click "Refresh" to load adapters.</p>
    </div>

    <h4 style="margin-top:16px">Convert to GGUF</h4>
    <div class="form-row" style="margin-bottom:12px">
        <div class="form-group" style="flex:2">
            <label>Adapter Path</label>
            <input type="text" id="cv-adapter" placeholder="Path to LoRA adapter directory">
        </div>
        <div class="form-group">
            <label>Output Name</label>
            <input type="text" id="cv-output" placeholder="e.g., Hal_v3" value="Hal_v3">
        </div>
        <div class="form-group">
            <label>Quantization</label>
            <select id="cv-quant">
                <option value="Q4_K_M">Q4_K_M (small, fast)</option>
                <option value="Q5_K_M" selected>Q5_K_M (balanced)</option>
                <option value="Q6_K">Q6_K (quality)</option>
                <option value="Q8_0">Q8_0 (high quality)</option>
                <option value="F16">F16 (unquantized)</option>
            </select>
        </div>
    </div>
    <div class="tool-actions">
        <button id="btn-convert" class="btn btn-primary" onclick="trConvert()">Merge & Convert</button>
    </div>
    <pre class="output-panel" id="convert-result" style="margin-top:12px;min-height:0"></pre>

    <h4 style="margin-top:16px">Available GGUF Models</h4>
    <div class="tool-actions">
        <button class="btn btn-small" onclick="trListModels()">Refresh Models</button>
    </div>
    <div id="models-list" style="margin-top:8px">
        <p class="empty-state">Click "Refresh" to load models.</p>
    </div>
</div>
</div>

<!-- ==================== EVALUATE TAB ==================== -->
<div class="tab-content" data-tab-group="trainer" data-tab="evaluate">
<div class="section">
    <h2>Model Evaluation</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Test a GGUF model with sample prompts to verify it learned the AUTARCH codebase.
    </p>

    <div class="form-row" style="margin-bottom:12px">
        <div class="form-group" style="flex:2">
            <label>Model Path</label>
            <select id="ev-model">
                <option value="">Select model...</option>
            </select>
        </div>
    </div>

    <h4>Test Prompts</h4>
    <div id="eval-prompts">
        <div class="input-row" style="margin-bottom:4px">
            <input type="text" class="eval-prompt" value="What is AUTARCH?" style="flex:1">
        </div>
        <div class="input-row" style="margin-bottom:4px">
            <input type="text" class="eval-prompt" value="How do I create a new defense module?" style="flex:1">
        </div>
        <div class="input-row" style="margin-bottom:4px">
            <input type="text" class="eval-prompt" value="What module categories does AUTARCH support?" style="flex:1">
        </div>
        <div class="input-row" style="margin-bottom:4px">
            <input type="text" class="eval-prompt" value="Create a module that scans for open ports on localhost." style="flex:1">
        </div>
    </div>
    <button class="btn btn-small" onclick="trAddPrompt()" style="margin-top:4px">+ Add Prompt</button>

    <div class="tool-actions" style="margin-top:12px">
        <button id="btn-evaluate" class="btn btn-primary" onclick="trEvaluate()">Run Evaluation</button>
    </div>

    <div id="eval-results" style="margin-top:12px"></div>
</div>
</div>

<script>
/* ── LLM Trainer ── */
var _trPollInterval = null;

function trScanCodebase() {
    var btn = document.getElementById('btn-scan');
    setLoading(btn, true);
    postJSON('/llm-trainer/scan', {}).then(function(data) {
        setLoading(btn, false);
        var inv = data.inventory || {};
        var html = '<table class="data-table" style="font-size:0.82rem"><thead><tr>'
            + '<th>Category</th><th>Files</th><th>Lines</th></tr></thead><tbody>';
        var cats = ['modules','core','routes','templates','configs','other'];
        cats.forEach(function(cat) {
            var files = inv[cat] || [];
            var lines = files.reduce(function(s,f){return s+f.lines},0);
            html += '<tr><td>' + cat + '</td><td>' + files.length + '</td><td>' + lines.toLocaleString() + '</td></tr>';
        });
        html += '<tr style="font-weight:bold"><td>Total</td><td>' + data.total_files
            + '</td><td>' + data.total_lines.toLocaleString() + '</td></tr>';
        html += '</tbody></table>';
        document.getElementById('scan-result').innerHTML = html;
    }).catch(function() { setLoading(btn, false); });
}

function trGenerateDataset() {
    var btn = document.getElementById('btn-generate');
    setLoading(btn, true);
    postJSON('/llm-trainer/dataset/generate', {
        format: document.getElementById('ds-format').value,
        include_source: document.getElementById('ds-source').checked,
        include_qa: document.getElementById('ds-qa').checked,
        include_module_creation: document.getElementById('ds-modules').checked,
    }).then(function(data) {
        setLoading(btn, false);
        if (data.error) {
            renderOutput('gen-result', 'Error: ' + data.error);
            return;
        }
        var lines = [
            'Dataset Generated!',
            '  File: ' + data.filename,
            '  Samples: ' + data.sample_count,
            '  Format: ' + data.format,
            '  Size: ' + (data.size_bytes / 1024).toFixed(1) + ' KB',
            '',
            'Preview (first sample):',
        ];
        if (data.preview && data.preview.length) {
            lines.push(JSON.stringify(data.preview[0], null, 2));
        }
        renderOutput('gen-result', lines.join('\n'));
        trListDatasets();
        trRefreshDatasetSelect();
    }).catch(function() { setLoading(btn, false); });
}

function trListDatasets() {
    fetchJSON('/llm-trainer/dataset/list').then(function(data) {
        var ds = data.datasets || [];
        var container = document.getElementById('datasets-list');
        if (!ds.length) {
            container.innerHTML = '<p class="empty-state">No datasets generated yet.</p>';
            return;
        }
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>'
            + '<th>File</th><th>Samples</th><th>Size</th><th>Created</th><th>Actions</th></tr></thead><tbody>';
        ds.forEach(function(d) {
            html += '<tr><td>' + escapeHtml(d.filename) + '</td>'
                + '<td>' + d.sample_count + '</td>'
                + '<td>' + (d.size_bytes/1024).toFixed(1) + ' KB</td>'
                + '<td>' + d.created.substring(0,16).replace('T',' ') + '</td>'
                + '<td>'
                + '<button class="btn btn-small" style="font-size:0.65rem;padding:2px 6px;margin-right:4px" '
                + 'onclick="trPreviewDataset(\'' + escapeHtml(d.filename) + '\')">Preview</button>'
                + '<button class="btn btn-danger btn-small" style="font-size:0.65rem;padding:2px 6px" '
                + 'onclick="trDeleteDataset(\'' + escapeHtml(d.filename) + '\')">Delete</button>'
                + '</td></tr>';
        });
        html += '</tbody></table>';
        container.innerHTML = html;
    });
}

function trPreviewDataset(filename) {
    postJSON('/llm-trainer/dataset/preview', {filename: filename, limit: 5}).then(function(data) {
        if (data.error) { alert(data.error); return; }
        var html = '<h4>Preview: ' + escapeHtml(filename) + '</h4>';
        (data.samples || []).forEach(function(s, i) {
            html += '<pre class="output-panel" style="font-size:0.75rem;margin-bottom:4px;max-height:150px;overflow-y:auto">'
                + escapeHtml(JSON.stringify(s, null, 2)) + '</pre>';
        });
        document.getElementById('datasets-list').innerHTML += html;
    });
}

function trDeleteDataset(filename) {
    if (!confirm('Delete ' + filename + '?')) return;
    postJSON('/llm-trainer/dataset/delete', {filename: filename}).then(function() {
        trListDatasets();
        trRefreshDatasetSelect();
    });
}

function trRefreshDatasetSelect() {
    fetchJSON('/llm-trainer/dataset/list').then(function(data) {
        var sel = document.getElementById('tr-dataset');
        sel.innerHTML = '<option value="">Select dataset...</option>';
        (data.datasets || []).forEach(function(d) {
            sel.innerHTML += '<option value="' + escapeHtml(d.path) + '">' + escapeHtml(d.filename)
                + ' (' + d.sample_count + ' samples)</option>';
        });
    });
}

/* ── Model Browser ── */
var _browserCurrentDir = '';
var _browserParentDir = '';

function trOpenBrowser() {
    document.getElementById('model-browser-overlay').style.display = 'flex';
    trBrowseDir('');  // starts at models/ directory
}

function trCloseBrowser() {
    document.getElementById('model-browser-overlay').style.display = 'none';
}

function trBrowseUp() {
    if (_browserParentDir) trBrowseDir(_browserParentDir);
}

function trBrowseDir(dir) {
    postJSON('/llm-trainer/browse', {directory: dir}).then(function(data) {
        if (data.error) {
            document.getElementById('browser-entries').innerHTML = '<p style="padding:8px;color:var(--danger)">' + escapeHtml(data.error) + '</p>';
            return;
        }
        _browserCurrentDir = data.current_dir || '';
        _browserParentDir = data.parent_dir || '';
        document.getElementById('browser-path').textContent = _browserCurrentDir;

        var entries = data.entries || [];
        if (!entries.length) {
            document.getElementById('browser-entries').innerHTML = '<p style="padding:8px;color:var(--text-muted)">Empty directory</p>';
            return;
        }
        var html = '<table class="data-table" style="font-size:0.8rem;margin:0"><tbody>';
        entries.forEach(function(e) {
            if (e.is_dir) {
                var icon = e.is_model ? '&#x1F4E6;' : '&#x1F4C1;';
                var label = e.is_model
                    ? '<span style="color:var(--success);font-weight:bold">' + escapeHtml(e.name) + '</span> <span style="font-size:0.7rem;color:var(--text-muted)">(HF model)</span>'
                    : escapeHtml(e.name);
                html += '<tr><td style="cursor:pointer" onclick="trBrowseDir(\'' + escapeHtml(e.path) + '\')">' + icon + ' ' + label + '</td>';
                if (e.is_model) {
                    html += '<td><button class="btn btn-small" style="font-size:0.65rem;padding:2px 8px" onclick="trSelectModel(\'' + escapeHtml(e.path) + '\')">Select</button></td>';
                } else {
                    html += '<td></td>';
                }
                html += '</tr>';
            } else {
                var size = e.size_gb !== undefined ? e.size_gb + ' GB' : '';
                html += '<tr><td>&#x1F4C4; ' + escapeHtml(e.name) + '</td><td style="font-size:0.7rem;color:var(--text-muted)">' + size + '</td></tr>';
            }
        });
        html += '</tbody></table>';
        document.getElementById('browser-entries').innerHTML = html;
    });
}

function trSelectModel(path) {
    document.getElementById('tr-base-model').value = path;
    trCloseBrowser();
}

/* ── Training ── */
function trStartTraining() {
    var baseModel = document.getElementById('tr-base-model').value.trim();
    var dataset = document.getElementById('tr-dataset').value;
    if (!baseModel) { alert('Click Browse to select a base model'); return; }
    if (!dataset) { alert('Please select a dataset'); return; }

    var config = {
        base_model: baseModel,
        dataset: dataset,
        lora_r: parseInt(document.getElementById('tr-lora-r').value),
        lora_alpha: parseInt(document.getElementById('tr-lora-alpha').value),
        lora_dropout: parseFloat(document.getElementById('tr-dropout').value),
        num_epochs: parseInt(document.getElementById('tr-epochs').value),
        batch_size: parseInt(document.getElementById('tr-batch').value),
        gradient_accumulation_steps: parseInt(document.getElementById('tr-grad-accum').value),
        learning_rate: parseFloat(document.getElementById('tr-lr').value),
        max_seq_length: parseInt(document.getElementById('tr-seq-len').value),
        use_4bit: document.getElementById('tr-4bit').checked,
        use_unsloth: document.getElementById('tr-unsloth').checked,
    };

    var btn = document.getElementById('btn-train-start');
    setLoading(btn, true);
    postJSON('/llm-trainer/train/start', config).then(function(data) {
        setLoading(btn, false);
        if (data.error) {
            alert('Training failed: ' + data.error);
            return;
        }
        document.getElementById('btn-train-start').style.display = 'none';
        document.getElementById('btn-train-stop').style.display = '';
        trStartPolling();
    }).catch(function() { setLoading(btn, false); });
}

function trStopTraining() {
    postJSON('/llm-trainer/train/stop', {}).then(function() {
        trStopPolling();
        document.getElementById('btn-train-start').style.display = '';
        document.getElementById('btn-train-stop').style.display = 'none';
        trUpdateStatus();
    });
}

function trStartPolling() {
    if (_trPollInterval) clearInterval(_trPollInterval);
    _trPollInterval = setInterval(trUpdateStatus, 3000);
    trUpdateStatus();
}

function trStopPolling() {
    if (_trPollInterval) { clearInterval(_trPollInterval); _trPollInterval = null; }
}

function trUpdateStatus() {
    fetchJSON('/llm-trainer/train/status').then(function(data) {
        document.getElementById('tr-phase').textContent = data.phase || 'idle';
        document.getElementById('tr-progress').textContent = (data.progress || 0) + '%';
        document.getElementById('tr-message').textContent = data.message || '';
        if (data.training_log) {
            document.getElementById('training-log').textContent = data.training_log;
            var log = document.getElementById('training-log');
            log.scrollTop = log.scrollHeight;
        }
        if (!data.training_running && _trPollInterval) {
            trStopPolling();
            document.getElementById('btn-train-start').style.display = '';
            document.getElementById('btn-train-stop').style.display = 'none';
        }
    });
}

/* ── Conversion ── */
function trListAdapters() {
    fetchJSON('/llm-trainer/adapters').then(function(data) {
        var adapters = data.adapters || [];
        var container = document.getElementById('adapters-list');
        if (!adapters.length) {
            container.innerHTML = '<p class="empty-state">No LoRA adapters found. Train a model first.</p>';
            return;
        }
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>'
            + '<th>Name</th><th>Base Model</th><th>LoRA r</th><th>Action</th></tr></thead><tbody>';
        adapters.forEach(function(a) {
            html += '<tr><td>' + escapeHtml(a.name) + '</td>'
                + '<td>' + escapeHtml(a.base_model || '—') + '</td>'
                + '<td>' + (a.r || '—') + '</td>'
                + '<td><button class="btn btn-small" style="font-size:0.65rem;padding:2px 6px" '
                + 'onclick="document.getElementById(\'cv-adapter\').value=\'' + escapeHtml(a.path) + '\'">Select</button></td></tr>';
        });
        html += '</tbody></table>';
        container.innerHTML = html;
    });
}

function trConvert() {
    var adapter = document.getElementById('cv-adapter').value.trim();
    var output = document.getElementById('cv-output').value.trim();
    var quant = document.getElementById('cv-quant').value;
    if (!adapter) { alert('Enter or select an adapter path'); return; }
    if (!output) { alert('Enter an output name'); return; }

    var btn = document.getElementById('btn-convert');
    setLoading(btn, true);
    renderOutput('convert-result', 'Merging and converting — this may take a while...');
    postJSON('/llm-trainer/convert', {
        adapter_path: adapter,
        output_name: output,
        quantization: quant,
    }).then(function(data) {
        setLoading(btn, false);
        if (data.error) {
            renderOutput('convert-result', 'Error: ' + data.error);
        } else if (data.partial) {
            renderOutput('convert-result', data.message);
        } else {
            renderOutput('convert-result', 'GGUF model saved!\n  Path: ' + data.output_path
                + '\n  Size: ' + (data.size_bytes / (1024*1024*1024)).toFixed(2) + ' GB');
            trListModels();
        }
    }).catch(function() { setLoading(btn, false); });
}

function trListModels() {
    fetchJSON('/llm-trainer/models').then(function(data) {
        var models = data.models || [];
        var container = document.getElementById('models-list');
        if (!models.length) {
            container.innerHTML = '<p class="empty-state">No GGUF models found.</p>';
            return;
        }
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>'
            + '<th>Name</th><th>Size</th><th>Modified</th></tr></thead><tbody>';
        models.forEach(function(m) {
            html += '<tr><td>' + escapeHtml(m.filename) + '</td>'
                + '<td>' + m.size_gb + ' GB</td>'
                + '<td>' + m.modified.substring(0,16).replace('T',' ') + '</td></tr>';
        });
        html += '</tbody></table>';
        container.innerHTML = html;

        // Update evaluate model select
        var sel = document.getElementById('ev-model');
        sel.innerHTML = '<option value="">Select model...</option>';
        models.forEach(function(m) {
            sel.innerHTML += '<option value="' + escapeHtml(m.path) + '">' + escapeHtml(m.filename)
                + ' (' + m.size_gb + ' GB)</option>';
        });
    });
}

/* ── Evaluate ── */
function trAddPrompt() {
    var div = document.createElement('div');
    div.className = 'input-row';
    div.style.marginBottom = '4px';
    div.innerHTML = '<input type="text" class="eval-prompt" placeholder="Enter test prompt..." style="flex:1">'
        + '<button class="btn btn-small" style="font-size:0.65rem;padding:2px 6px;margin-left:4px" '
        + 'onclick="this.parentElement.remove()">X</button>';
    document.getElementById('eval-prompts').appendChild(div);
}

function trEvaluate() {
    var model = document.getElementById('ev-model').value;
    if (!model) { alert('Select a model to evaluate'); return; }

    var prompts = [];
    document.querySelectorAll('.eval-prompt').forEach(function(el) {
        if (el.value.trim()) prompts.push(el.value.trim());
    });
    if (!prompts.length) { alert('Add at least one test prompt'); return; }

    var btn = document.getElementById('btn-evaluate');
    setLoading(btn, true);
    document.getElementById('eval-results').innerHTML = '<p style="color:var(--text-muted)">Running evaluation — loading model and generating responses...</p>';

    postJSON('/llm-trainer/evaluate', {model_path: model, prompts: prompts}).then(function(data) {
        setLoading(btn, false);
        if (data.error) {
            document.getElementById('eval-results').innerHTML = '<p style="color:var(--danger)">Error: ' + escapeHtml(data.error) + '</p>';
            return;
        }
        var html = '';
        (data.results || []).forEach(function(r, i) {
            html += '<div style="margin-bottom:12px;border:1px solid var(--border);border-radius:6px;padding:12px">'
                + '<div style="font-weight:bold;margin-bottom:6px;color:var(--accent)">Prompt: ' + escapeHtml(r.prompt) + '</div>'
                + '<pre class="output-panel scrollable" style="max-height:200px;font-size:0.78rem;margin:0">'
                + escapeHtml(r.response) + '</pre>'
                + '<div style="font-size:0.7rem;color:var(--text-muted);margin-top:4px">Response length: ' + r.length + ' chars</div>'
                + '</div>';
        });
        document.getElementById('eval-results').innerHTML = html;
    }).catch(function() { setLoading(btn, false); });
}

/* ── Init ── */
document.addEventListener('DOMContentLoaded', function() {
    trRefreshDatasetSelect();
    trListModels();
});
</script>
{% endblock %}