This commit is contained in:
team 1
2026-05-12 09:16:09 +02:00
parent 0d55c0a439
commit feaec9bbaf
7 changed files with 405 additions and 17 deletions

View File

@@ -146,18 +146,23 @@
</div>
<div class="mb-3">
<label class="form-label">Optional: Case-ID</label>
<input name="case_id"
list="evalCaseIds"
class="form-control bg-dark text-light border-secondary"
placeholder="z. B. followup_indicator_price_001">
<datalist id="evalCaseIds">
<label class="form-label">Optional: Case</label>
<select name="case_id"
class="form-select bg-dark text-light border-secondary js-admin-eval-case-select">
<option value="">Alle Cases des ausgewählten Typs</option>
{% for type, cases in cases_by_type %}
{% for case in cases %}
<option value="{{ case.id }}">{{ type }} - {{ case.prompt }}</option>
<option value="{{ case.id }}"
data-eval-type="{{ type }}"
{% if type != selected_type %}hidden disabled{% endif %}>
{{ case.id }}{{ case.prompt }}
</option>
{% endfor %}
{% endfor %}
</datalist>
</select>
<div class="form-text text-secondary">
Die Case-Liste wird passend zum Eval-Typ gefiltert. Leer lassen, um alle Cases des Typs auszuführen.
</div>
</div>
<button type="submit" class="btn btn-outline-warning js-admin-eval-run-button">
@@ -294,9 +299,94 @@
<div class="small text-success mb-2">Keine Fehler.</div>
{% endif %}
{% set documentRefs = result.details.document_refs|default([]) %}
{% if documentRefs is not empty %}
<div class="mb-2">
<div class="small text-secondary mb-1">Gefundene Dokumente</div>
<div class="table-responsive">
<table class="table table-dark table-sm table-bordered border-secondary align-middle mb-2">
<thead>
<tr class="small text-secondary">
<th style="width: 90px;">Ranks</th>
<th>Titel / Datei</th>
<th style="width: 170px;">Doc-ID</th>
<th style="width: 220px;">Chunks</th>
</tr>
</thead>
<tbody>
{% for doc in documentRefs %}
<tr>
<td class="small">{{ doc.ranks|default([])|join(', ') }}</td>
<td>
<div class="fw-semibold">{{ doc.title|default('Ohne Titel') }}</div>
{% if doc.file_path|default('') %}
<div class="small text-secondary" style="word-break: break-all;">
{{ doc.file_path }}
</div>
{% endif %}
{% if doc.version_number|default('') %}
<div class="small text-secondary">Version: {{ doc.version_number }}</div>
{% endif %}
</td>
<td><code class="small">{{ doc.id|default('') }}</code></td>
<td class="small" style="word-break: break-all;">
{% for chunkId in doc.chunk_ids|default([]) %}
<code>{{ chunkId }}</code>{% if not loop.last %}<br>{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% set resultRows = result.details.result_rows|default([]) %}
{% if resultRows is not empty %}
<details class="mb-2">
<summary class="small text-info" style="cursor:pointer;">
Treffer / Chunks anzeigen
</summary>
<div class="table-responsive mt-2">
<table class="table table-dark table-sm table-bordered border-secondary align-middle mb-0">
<thead>
<tr class="small text-secondary">
<th style="width: 60px;">Rank</th>
<th>Titel / Datei</th>
<th style="width: 180px;">Chunk</th>
<th>Preview</th>
</tr>
</thead>
<tbody>
{% for row in resultRows %}
<tr>
<td>{{ row.rank|default('') }}</td>
<td>
<div class="fw-semibold">{{ row.document_title|default('Ohne Titel') }}</div>
{% if row.file_path|default('') %}
<div class="small text-secondary" style="word-break: break-all;">{{ row.file_path }}</div>
{% endif %}
<div class="small text-secondary">Doc-ID: <code>{{ row.document_id|default('') }}</code></div>
</td>
<td class="small" style="word-break: break-all;">
<code>{{ row.chunk_id|default('') }}</code>
{% if row.chunk_index is defined and row.chunk_index is not same as(null) %}
<div class="text-secondary">Index: {{ row.chunk_index }}</div>
{% endif %}
</td>
<td class="small text-secondary">{{ row.text_preview|default('') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</details>
{% endif %}
<details>
<summary class="small text-info" style="cursor:pointer;">
Details anzeigen
JSON-Details anzeigen
</summary>
<pre class="bg-dark border border-secondary rounded p-2 mt-2 small text-light" style="white-space: pre-wrap; max-height: 260px; overflow: auto;">{{ result.details|default({})|json_encode(constant('JSON_PRETTY_PRINT')) }}</pre>
</details>
@@ -337,6 +427,33 @@
return (form.dataset.evalTypeLabel || 'Eval').trim();
}
function syncCaseSelect(form) {
const typeSelect = form.querySelector('.js-admin-eval-type-select');
const caseSelect = form.querySelector('.js-admin-eval-case-select');
if (!typeSelect || !caseSelect) {
return;
}
const selectedType = typeSelect.value;
Array.from(caseSelect.options).forEach(function (option) {
if (option.value === '') {
option.hidden = false;
option.disabled = false;
return;
}
const matchesType = option.dataset.evalType === selectedType;
option.hidden = !matchesType;
option.disabled = !matchesType;
if (!matchesType && option.selected) {
caseSelect.value = '';
}
});
}
function setAllRunButtonsDisabled() {
document.querySelectorAll('.js-admin-eval-run-button').forEach(function (button) {
button.disabled = true;
@@ -345,6 +462,15 @@
}
forms.forEach(function (form) {
syncCaseSelect(form);
const typeSelect = form.querySelector('.js-admin-eval-type-select');
if (typeSelect) {
typeSelect.addEventListener('change', function () {
syncCaseSelect(form);
});
}
form.addEventListener('submit', function (event) {
const button = event.submitter && event.submitter.classList.contains('js-admin-eval-run-button')
? event.submitter