mirror of
https://github.com/perstarkse/minne.git
synced 2026-04-26 10:48:37 +02:00
design: improved admin page, new structure
This commit is contained in:
130
html-router/templates/admin/sections/models.html
Normal file
130
html-router/templates/admin/sections/models.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<section class="nb-panel p-4 sm:p-5 flex flex-col gap-4">
|
||||
<div class="flex items-start justify-between flex-col sm:flex-row gap-3">
|
||||
<div>
|
||||
<div class="text-sm uppercase tracking-wide opacity-60 mb-1">AI Models</div>
|
||||
<h2 class="text-lg font-semibold">Model configuration</h2>
|
||||
<p class="text-xs opacity-70 max-w-2xl">
|
||||
Choose which models power conversational search, ingestion analysis, and embeddings. Adjusting embeddings may trigger a full reprocess.
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/admin?section=overview"
|
||||
class="nb-btn btn-sm btn-ghost"
|
||||
hx-boost="true"
|
||||
hx-target="#admin-shell"
|
||||
hx-select="#admin-shell"
|
||||
hx-swap="outerHTML"
|
||||
hx-push-url="true"
|
||||
>
|
||||
← Back to Admin
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if available_models %}
|
||||
{% block model_settings_form %}
|
||||
<form hx-patch="/update-model-settings" hx-swap="outerHTML" class="grid grid-cols-1 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1">Query Model</div>
|
||||
<select name="query_model" class="nb-select w-full">
|
||||
{% for model in available_models.data %}
|
||||
<option value="{{ model.id }}" {% if settings.query_model == model.id %}selected{% endif %}>{{ model.id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="text-xs opacity-70 mt-1">Current: <span class="font-mono">{{ settings.query_model }}</span></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1">Processing Model</div>
|
||||
<select name="processing_model" class="nb-select w-full">
|
||||
{% for model in available_models.data %}
|
||||
<option value="{{ model.id }}" {% if settings.processing_model == model.id %}selected{% endif %}>{{ model.id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="text-xs opacity-70 mt-1">Current: <span class="font-mono">{{ settings.processing_model }}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1">Image Processing Model</div>
|
||||
<select name="image_processing_model" class="nb-select w-full">
|
||||
{% for model in available_models.data %}
|
||||
<option value="{{ model.id }}" {% if settings.image_processing_model == model.id %}selected{% endif %}>{{ model.id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="text-xs opacity-70 mt-1">Current: <span class="font-mono">{{ settings.image_processing_model }}</span></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1">Voice Processing Model</div>
|
||||
<select name="voice_processing_model" class="nb-select w-full">
|
||||
{% for model in available_models.data %}
|
||||
<option value="{{ model.id }}" {% if settings.voice_processing_model == model.id %}selected{% endif %}>{{ model.id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="text-xs opacity-70 mt-1">Current: <span class="font-mono">{{ settings.voice_processing_model }}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1">Embedding Model</div>
|
||||
<select name="embedding_model" class="nb-select w-full">
|
||||
{% for model in available_models.data %}
|
||||
<option value="{{ model.id }}" {% if settings.embedding_model == model.id %}selected{% endif %}>{{ model.id }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="text-xs opacity-70 mt-1">Current: <span class="font-mono">{{ settings.embedding_model }}</span></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-sm opacity-80 mb-1" for="embedding_dimensions">Embedding Dimensions</div>
|
||||
<input
|
||||
type="number"
|
||||
id="embedding_dimensions"
|
||||
name="embedding_dimensions"
|
||||
class="nb-input w-full"
|
||||
value="{{ settings.embedding_dimensions }}"
|
||||
required
|
||||
min="1"
|
||||
/>
|
||||
<p class="text-xs opacity-70 mt-1">Changing dimensions will trigger a background re-embedding.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="embedding-change-alert" class="nb-panel p-3 bg-warning/20 hidden">
|
||||
<div class="text-sm">
|
||||
<strong>Warning:</strong> Changing dimensions recreates embeddings for text chunks and knowledge entities. Confirm the target model requires the new value.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<button type="submit" class="nb-btn nb-cta btn-sm">Save Model Settings</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const dimensionInput = document.getElementById('embedding_dimensions');
|
||||
const alertElement = document.getElementById('embedding-change-alert');
|
||||
const initialDimensions = '{{ settings.embedding_dimensions }}';
|
||||
if (dimensionInput && alertElement) {
|
||||
dimensionInput.addEventListener('input', (event) => {
|
||||
if (String(event.target.value) !== String(initialDimensions)) {
|
||||
alertElement.classList.remove('hidden');
|
||||
} else {
|
||||
alertElement.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% else %}
|
||||
<div class="nb-panel p-4 bg-warning/10 border border-warning/40">
|
||||
<div class="text-sm font-semibold mb-1">Unable to load models</div>
|
||||
<p class="text-xs opacity-70">We could not reach the model provider. Check the API key and retry.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
Reference in New Issue
Block a user