jundot--omlx
532 行
42 KiB
HTML
532 行
42 KiB
HTML
{% macro bench_th(label_key, tooltip_key, align='right') %}
|
|
<th class="text-{{ align }} px-4 py-3 font-semibold text-neutral-600">
|
|
<div class="inline-flex items-center gap-1 {% if align == 'right' %}justify-end{% endif %}">
|
|
<span>{{ t(label_key) }}</span>
|
|
<i data-lucide="info"
|
|
class="w-3 h-3 text-neutral-400 flex-shrink-0 cursor-help"
|
|
@mouseenter="benchTip = { text: `{{ t(tooltip_key) }}`, el: $event.target }; $nextTick(() => { const r = $event.target.getBoundingClientRect(); const tip = $refs.benchTipEl; const tw = tip.offsetWidth; let x = r.left + r.width / 2 - tw / 2; if (x + tw > window.innerWidth - 8) x = window.innerWidth - 8 - tw; if (x < 8) x = 8; tip.style.left = x + 'px'; tip.style.top = (r.top - tip.offsetHeight - 8) + 'px'; })"
|
|
@mouseleave="benchTip = null"></i>
|
|
</div>
|
|
</th>
|
|
{% endmacro %}
|
|
<div x-show="mainTab === 'bench'" x-cloak>
|
|
<!-- Sub-tab navigation -->
|
|
<div class="flex flex-wrap items-center gap-2 mb-10 animate-fade-in-up">
|
|
<button
|
|
@click="setBenchTab('throughput')"
|
|
:class="benchTab === 'throughput'
|
|
? 'bg-neutral-900 text-white'
|
|
: 'text-neutral-600 hover:text-neutral-900 hover:bg-neutral-100'"
|
|
class="px-5 py-2.5 rounded-full text-sm font-medium transition-all duration-200">
|
|
{{ t('navbar.dropdown.performance') }}
|
|
</button>
|
|
<button
|
|
@click="setBenchTab('accuracy')"
|
|
:class="benchTab === 'accuracy'
|
|
? 'bg-neutral-900 text-white'
|
|
: 'text-neutral-600 hover:text-neutral-900 hover:bg-neutral-100'"
|
|
class="px-5 py-2.5 rounded-full text-sm font-medium transition-all duration-200">
|
|
{{ t('navbar.dropdown.accuracy') }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Throughput tab -->
|
|
<div x-show="benchTab === 'throughput'" x-cloak>
|
|
<div class="animate-fade-in-up">
|
|
<!-- Header -->
|
|
<div class="mb-8">
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
|
<div>
|
|
<h2 class="text-xs font-bold uppercase tracking-widest text-neutral-400 mb-2">{{ t('bench.section_label') }}</h2>
|
|
<h3 class="text-2xl font-bold tracking-tight text-neutral-900">{{ t('bench.heading') }}</h3>
|
|
</div>
|
|
<a x-show="benchDeviceInfo?.owner_hash"
|
|
x-cloak
|
|
:href="'https://omlx.ai/my/' + benchDeviceInfo.owner_hash"
|
|
target="_blank"
|
|
class="flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-neutral-600 bg-neutral-100 rounded-lg hover:bg-neutral-200 hover:text-neutral-900 transition-all">
|
|
<i data-lucide="external-link" class="w-3.5 h-3.5"></i>
|
|
My Submissions
|
|
</a>
|
|
</div>
|
|
<p class="text-sm text-neutral-500 mt-2">{{ t('bench.description') }}</p>
|
|
</div>
|
|
|
|
<!-- "Another bench is running" banner. Shown when the
|
|
server reports a running run that this tab isn't
|
|
attached to (e.g. another tab started one while this
|
|
one was viewing a previous result). Lets the user
|
|
keep their previous result on screen until they
|
|
explicitly choose to view the new run. -->
|
|
<div x-show="benchOtherActive" x-cloak
|
|
class="bg-amber-50 border border-amber-200 rounded-2xl px-5 py-4 mb-6 flex items-center justify-between gap-4">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="info" class="w-5 h-5 text-amber-600 flex-shrink-0"></i>
|
|
<div class="text-sm text-amber-900">
|
|
Another throughput benchmark is running on
|
|
<span class="font-medium" x-text="benchOtherActive?.model_id"></span>.
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2 flex-shrink-0">
|
|
<button @click="acceptOtherBench()"
|
|
class="px-3 py-1.5 bg-amber-600 text-white text-xs font-medium rounded-lg hover:bg-amber-700 transition-all">
|
|
View live
|
|
</button>
|
|
<button @click="dismissOtherBench()"
|
|
class="px-3 py-1.5 text-amber-700 text-xs font-medium hover:text-amber-900 transition-all">
|
|
Dismiss
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Configuration Card -->
|
|
<div class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="settings-2" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">{{ t('bench.config.section_label') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="px-6 py-5 space-y-5">
|
|
<!-- Model Selector -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-neutral-700 mb-2">{{ t('bench.config.model') }}</label>
|
|
<select x-model="benchModelId"
|
|
:disabled="benchRunning || benchExternalEnabled"
|
|
class="w-full max-w-md px-3 py-2 text-sm border border-neutral-200 rounded-lg focus:ring-2 focus:ring-neutral-900 focus:border-transparent bg-white disabled:opacity-50">
|
|
<option value="">{{ t('bench.config.model_placeholder') }}</option>
|
|
<template x-for="m in models.filter(m => m.model_type === 'llm' || m.model_type === 'vlm' || !m.model_type)" :key="m.id">
|
|
<option :value="m.id" x-text="m.id + ' (' + (m.estimated_size ? (m.estimated_size / 1073741824).toFixed(1) + ' GB' : 'unknown') + ')'"></option>
|
|
</template>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Single Request Config -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-neutral-700 mb-2">{{ t('bench.config.single_request') }}</label>
|
|
<div class="flex flex-wrap items-center gap-4">
|
|
<template x-for="pp in [1024, 4096, 8192, 16384, 32768, 65536, 131072, 200000]" :key="pp">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="checkbox"
|
|
x-model="benchPromptLengths[pp]"
|
|
:disabled="benchRunning"
|
|
class="w-4 h-4 rounded border-neutral-300 text-neutral-900 focus:ring-neutral-900">
|
|
<span class="text-sm text-neutral-600" x-text="'pp' + pp"></span>
|
|
</label>
|
|
</template>
|
|
</div>
|
|
<p class="text-xs text-neutral-400 mt-1.5">{{ t('bench.config.generation_hint') }}</p>
|
|
</div>
|
|
|
|
<!-- Batch Config -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-neutral-700 mb-2">{{ t('bench.config.batch_tests') }}</label>
|
|
<div class="flex flex-wrap items-center gap-4">
|
|
<template x-for="bs in [2, 4, 8]" :key="bs">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="checkbox"
|
|
x-model="benchBatchSizes[bs]"
|
|
:disabled="benchRunning"
|
|
class="w-4 h-4 rounded border-neutral-300 text-neutral-900 focus:ring-neutral-900">
|
|
<span class="text-sm text-neutral-600" x-text="bs + 'x batch'"></span>
|
|
</label>
|
|
</template>
|
|
</div>
|
|
<p class="text-xs text-neutral-400 mt-1.5">{{ t('bench.config.batch_hint') }}</p>
|
|
</div>
|
|
|
|
<!-- Advanced Options -->
|
|
<div class="pt-2 border-t border-neutral-100">
|
|
<button type="button"
|
|
@click="benchAdvancedOptionsOpen = !benchAdvancedOptionsOpen"
|
|
:disabled="benchRunning"
|
|
:aria-expanded="benchAdvancedOptionsOpen.toString()"
|
|
class="flex items-center gap-2 text-sm font-medium text-neutral-600 hover:text-neutral-900 transition-colors disabled:opacity-50">
|
|
<i data-lucide="sliders-horizontal" class="w-4 h-4"></i>
|
|
<span>{{ t('bench.config.advanced_options') }}</span>
|
|
<i data-lucide="chevron-down"
|
|
class="w-4 h-4 text-neutral-400 transition-transform"
|
|
:class="benchAdvancedOptionsOpen && 'rotate-180'"></i>
|
|
</button>
|
|
<div x-show="benchAdvancedOptionsOpen" x-collapse x-cloak class="mt-3 space-y-3">
|
|
<label class="inline-flex items-start gap-2 cursor-pointer">
|
|
<input type="checkbox"
|
|
x-model="benchForceLmEngine"
|
|
:disabled="benchRunning || benchExternalEnabled"
|
|
class="mt-0.5 w-4 h-4 rounded border-neutral-300 text-neutral-900 focus:ring-neutral-900 disabled:opacity-50">
|
|
<span>
|
|
<span class="block text-sm font-medium text-neutral-700">{{ t('bench.config.force_lm_engine') }}</span>
|
|
<span class="block text-xs text-neutral-400 mt-1">{{ t('bench.config.force_lm_engine_hint') }}</span>
|
|
</span>
|
|
</label>
|
|
<!-- External OpenAI-compatible endpoint -->
|
|
<div>
|
|
<label class="inline-flex items-start gap-2 cursor-pointer">
|
|
<input type="checkbox"
|
|
x-model="benchExternalEnabled"
|
|
:disabled="benchRunning"
|
|
class="mt-0.5 w-4 h-4 rounded border-neutral-300 text-neutral-900 focus:ring-neutral-900 disabled:opacity-50">
|
|
<span>
|
|
<span class="block text-sm font-medium text-neutral-700">{{ t('bench.config.external') }}</span>
|
|
<span class="block text-xs text-neutral-400 mt-1">{{ t('bench.config.external_hint') }}</span>
|
|
</span>
|
|
</label>
|
|
<div x-show="benchExternalEnabled" x-collapse x-cloak class="mt-3 space-y-3 pl-6">
|
|
<div>
|
|
<label class="block text-xs font-medium text-neutral-600 mb-1">{{ t('bench.config.external_base_url') }}</label>
|
|
<input type="url"
|
|
x-model="externalBaseUrl"
|
|
@change="saveExternalEndpoint()"
|
|
:disabled="benchRunning"
|
|
placeholder="https://api.openai.com/v1"
|
|
class="w-full max-w-md px-3 py-2 text-sm border border-neutral-200 rounded-lg focus:ring-2 focus:ring-neutral-900 focus:border-transparent bg-white disabled:opacity-50">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium text-neutral-600 mb-1">{{ t('bench.config.external_api_key') }}</label>
|
|
<input type="password"
|
|
autocomplete="off"
|
|
x-model="externalApiKey"
|
|
@change="saveExternalEndpoint()"
|
|
:disabled="benchRunning"
|
|
class="w-full max-w-md px-3 py-2 text-sm border border-neutral-200 rounded-lg focus:ring-2 focus:ring-neutral-900 focus:border-transparent bg-white disabled:opacity-50">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-medium text-neutral-600 mb-1">{{ t('bench.config.external_model') }}</label>
|
|
<input type="text"
|
|
x-model="externalModel"
|
|
@change="saveExternalEndpoint()"
|
|
:disabled="benchRunning"
|
|
placeholder="gpt-4o-mini"
|
|
class="w-full max-w-md px-3 py-2 text-sm border border-neutral-200 rounded-lg focus:ring-2 focus:ring-neutral-900 focus:border-transparent bg-white disabled:opacity-50">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Run / Cancel Button -->
|
|
<div class="flex items-center gap-3 pt-2">
|
|
<button x-show="!benchRunning"
|
|
@click="startBenchmark()"
|
|
:disabled="(benchExternalEnabled ? !externalConfigValid() : !benchModelId) || Object.values(benchPromptLengths).every(v => !v) || benchOtherActive !== null"
|
|
:title="benchOtherActive ? 'Another throughput benchmark is already running in this server.' : ''"
|
|
class="px-6 py-2.5 bg-neutral-900 text-white text-sm font-medium rounded-lg hover:bg-neutral-800 transition-all disabled:opacity-40 disabled:cursor-not-allowed flex items-center gap-2">
|
|
<i data-lucide="play" class="w-4 h-4"></i>
|
|
{{ t('bench.config.run_button') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Progress Section -->
|
|
<div x-show="benchRunning && benchProgress" x-cloak class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="px-4 sm:px-6 py-4">
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-3">
|
|
<span class="text-sm font-medium text-neutral-700" x-text="benchProgress?.message || window.t('bench.progress.preparing')"></span>
|
|
<div class="flex items-center gap-3">
|
|
<span class="text-sm text-neutral-500" x-text="benchProgress?.current + ' / ' + benchProgress?.total"></span>
|
|
<button @click="cancelBenchmark()"
|
|
class="px-3 py-1 bg-neutral-900 text-white text-xs font-medium rounded-lg hover:bg-neutral-800 transition-all flex items-center gap-1">
|
|
<i data-lucide="square" class="w-3 h-3"></i>
|
|
{{ t('bench.progress.cancel') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="w-full bg-neutral-200 rounded-full h-2">
|
|
<div class="bg-neutral-900 h-2 rounded-full transition-all duration-500"
|
|
:style="'width: ' + (benchProgress?.total > 0 ? (benchProgress.current / benchProgress.total * 100) : 0) + '%'"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Running indicator (no progress yet) -->
|
|
<div x-show="benchRunning && !benchProgress" x-cloak class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="px-6 py-4 flex items-center gap-3">
|
|
<div class="w-5 h-5 border-2 border-neutral-300 border-t-neutral-900 rounded-full animate-spin"></div>
|
|
<span class="text-sm text-neutral-600">{{ t('bench.progress.starting') }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error -->
|
|
<div x-show="benchError" x-cloak class="bg-red-50 border border-red-200 rounded-2xl px-6 py-4 mb-6">
|
|
<div class="flex items-center gap-2 text-red-700">
|
|
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
<span class="text-sm font-medium" x-text="benchError"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Single Request Results -->
|
|
<div x-show="benchSingleResults.length > 0" x-cloak class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="zap" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">{{ t('bench.results.single.section_label') }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-sm">
|
|
<thead>
|
|
<tr class="border-b border-neutral-200 bg-neutral-50">
|
|
{{ bench_th('bench.results.single.test', 'bench.metrics.test.tooltip', align='left') }}
|
|
{{ bench_th('bench.results.single.ttft', 'bench.metrics.ttft.tooltip') }}
|
|
{{ bench_th('bench.results.single.tpot', 'bench.metrics.tpot.tooltip') }}
|
|
{{ bench_th('bench.results.single.pp_tps', 'bench.metrics.pp_tps.tooltip') }}
|
|
{{ bench_th('bench.results.single.tg_tps', 'bench.metrics.tg_tps.tooltip') }}
|
|
{{ bench_th('bench.results.single.e2e_latency', 'bench.metrics.e2e.tooltip') }}
|
|
{{ bench_th('bench.results.single.throughput', 'bench.metrics.throughput.tooltip') }}
|
|
{{ bench_th('bench.results.single.peak_mem', 'bench.metrics.peak_mem.tooltip') }}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<template x-for="r in benchSingleResults" :key="r.pp">
|
|
<tr class="border-b border-neutral-100 hover:bg-neutral-50">
|
|
<td class="px-4 py-3 font-medium text-neutral-900" x-text="'pp' + r.pp + '/tg' + r.tg"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.ttft_ms.toFixed(1)"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.tpot_ms.toFixed(2)"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.processing_tps.toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums font-medium text-neutral-900" x-text="r.gen_tps.toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.e2e_latency_s.toFixed(3) + 's'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.total_throughput.toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="benchFormatMemory(r.peak_memory_bytes)"></td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Batch Results: Continuous Batching -->
|
|
<div x-show="benchBatchResults.length > 0" x-cloak class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="layers" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">{{ t('bench.results.batch.section_label') }}</span>
|
|
</div>
|
|
<span class="text-xs text-neutral-400">{{ t('bench.results.batch.subtitle') }}</span>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-sm">
|
|
<thead>
|
|
<tr class="border-b border-neutral-200 bg-neutral-50">
|
|
{{ bench_th('bench.results.batch.batch_size', 'bench.metrics.batch_size.tooltip', align='left') }}
|
|
{{ bench_th('bench.results.batch.tg_tps', 'bench.metrics.tg_tps.tooltip') }}
|
|
{{ bench_th('bench.results.batch.speedup', 'bench.metrics.speedup.tooltip') }}
|
|
{{ bench_th('bench.results.batch.pp_tps', 'bench.metrics.pp_tps.tooltip') }}
|
|
{{ bench_th('bench.results.batch.pp_tps_req', 'bench.metrics.pp_tps_req.tooltip') }}
|
|
{{ bench_th('bench.results.batch.avg_ttft', 'bench.metrics.avg_ttft.tooltip') }}
|
|
{{ bench_th('bench.results.batch.e2e_latency', 'bench.metrics.e2e.tooltip') }}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- 1x baseline from single request -->
|
|
<tr x-show="benchSingleResults.some(r => r.pp === 1024)" class="border-b border-neutral-100 bg-neutral-50/50">
|
|
<td class="px-4 py-3 font-medium text-neutral-500">{{ t('bench.results.batch.baseline') }}</td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="(benchSingleResults.find(r => r.pp === 1024)?.gen_tps?.toFixed(1) || '-') + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500">1.00x</td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="(benchSingleResults.find(r => r.pp === 1024)?.processing_tps?.toFixed(1) || '-') + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="(benchSingleResults.find(r => r.pp === 1024)?.processing_tps?.toFixed(1) || '-') + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="benchSingleResults.find(r => r.pp === 1024)?.ttft_ms?.toFixed(1) || '-'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="(benchSingleResults.find(r => r.pp === 1024)?.e2e_latency_s?.toFixed(3) || '-') + 's'"></td>
|
|
</tr>
|
|
<template x-for="r in benchBatchResults" :key="r.batch_size">
|
|
<tr class="border-b border-neutral-100 hover:bg-neutral-50">
|
|
<td class="px-4 py-3 font-medium text-neutral-900" x-text="r.batch_size + 'x'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums font-medium text-neutral-900" x-text="r.tg_tps.toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums font-medium" :class="benchGetSpeedup(r) >= 1.5 ? 'text-green-600' : 'text-neutral-700'" x-text="benchGetSpeedup(r).toFixed(2) + 'x'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.pp_tps.toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-500" x-text="(r.pp_tps / r.batch_size).toFixed(1) + ' tok/s'"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.avg_ttft_ms.toFixed(1)"></td>
|
|
<td class="px-4 py-3 text-right tabular-nums text-neutral-700" x-text="r.e2e_latency_s.toFixed(3) + 's'"></td>
|
|
</tr>
|
|
</template>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Benchmark Results (Text Export) -->
|
|
<div x-show="benchSingleResults.length > 0 || benchBatchResults.length > 0" x-cloak
|
|
class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<button @click="benchShowText = !benchShowText"
|
|
class="w-full flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200 hover:bg-neutral-150 transition-colors">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="clipboard-copy" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">{{ t('bench.results.text_export.section_label') }}</span>
|
|
</div>
|
|
<i data-lucide="chevron-down" class="w-4 h-4 text-neutral-400 transition-transform" :class="benchShowText && 'rotate-180'"></i>
|
|
</button>
|
|
<div x-show="benchShowText" x-collapse x-cloak>
|
|
<div class="px-4 py-3 flex justify-end border-b border-neutral-200">
|
|
<button @click="benchCopyText()"
|
|
class="px-4 py-1.5 bg-neutral-900 text-white text-xs font-medium rounded-lg hover:bg-neutral-800 transition-all flex items-center gap-1.5">
|
|
<i data-lucide="copy" class="w-3.5 h-3.5"></i>
|
|
<span x-text="benchCopied ? window.t('bench.results.text_export.copied') : window.t('bench.results.text_export.copy')"></span>
|
|
</button>
|
|
</div>
|
|
<div class="bg-neutral-900 rounded-b-2xl">
|
|
<textarea
|
|
x-ref="benchTextarea"
|
|
:value="benchBuildText()"
|
|
readonly
|
|
class="w-full h-[400px] px-4 py-4 bg-neutral-900 text-green-400 font-mono text-xs leading-relaxed resize-none focus:outline-none"
|
|
></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Community Benchmark Upload Results -->
|
|
<div x-show="benchUploadResults.length > 0 || benchUploading" x-cloak
|
|
class="bg-white rounded-2xl border border-neutral-200 overflow-hidden mb-6">
|
|
<div class="flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="upload-cloud" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">Community Benchmark Upload</span>
|
|
</div>
|
|
<div x-show="benchUploading && benchUploadResults.length === 0" class="flex items-center gap-2">
|
|
<div class="w-4 h-4 border-2 border-neutral-300 border-t-neutral-900 rounded-full animate-spin"></div>
|
|
<span class="text-xs text-neutral-500">Uploading...</span>
|
|
</div>
|
|
</div>
|
|
<div class="px-6 py-4 space-y-2">
|
|
<template x-for="r in benchUploadResults" :key="r.context_length">
|
|
<div class="flex items-center justify-between py-2 border-b border-neutral-100 last:border-0">
|
|
<span class="text-sm font-medium text-neutral-700" x-text="'pp' + r.context_length"></span>
|
|
<div class="flex items-center gap-2">
|
|
<!-- Success -->
|
|
<template x-if="r.url && !r.duplicate">
|
|
<a :href="r.url" target="_blank"
|
|
class="flex items-center gap-1.5 text-sm text-green-600 hover:text-green-700">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
|
<span x-text="r.id"></span>
|
|
<i data-lucide="external-link" class="w-3 h-3"></i>
|
|
</a>
|
|
</template>
|
|
<!-- Duplicate -->
|
|
<template x-if="r.url && r.duplicate">
|
|
<a :href="r.url" target="_blank"
|
|
class="flex items-center gap-1.5 text-sm text-amber-600 hover:text-amber-700">
|
|
<i data-lucide="copy" class="w-4 h-4"></i>
|
|
<span>Already exists</span>
|
|
<i data-lucide="external-link" class="w-3 h-3"></i>
|
|
</a>
|
|
</template>
|
|
<!-- Error -->
|
|
<template x-if="r.error">
|
|
<span class="flex items-center gap-1.5 text-sm text-red-500">
|
|
<i data-lucide="x-circle" class="w-4 h-4"></i>
|
|
<span x-text="r.error"></span>
|
|
</span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<!-- Upload complete summary -->
|
|
<div x-show="benchUploadDone" x-cloak class="pt-3 flex items-center justify-between">
|
|
<span class="text-xs text-neutral-500"
|
|
x-text="(benchUploadDone?.success || 0) + '/' + (benchUploadDone?.total || 0) + ' uploaded'"></span>
|
|
<a x-show="benchUploadDone?.owner_hash"
|
|
:href="'https://omlx.ai/my/' + benchUploadDone?.owner_hash"
|
|
target="_blank"
|
|
class="flex items-center gap-1.5 text-sm font-medium text-neutral-600 hover:text-neutral-900 transition-colors">
|
|
Manage on omlx.ai
|
|
<i data-lucide="external-link" class="w-3.5 h-3.5"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Community Benchmark Upload Skipped (experimental features) -->
|
|
<div x-show="benchUploadSkipped" x-cloak
|
|
class="bg-amber-50 rounded-2xl border border-amber-200 overflow-hidden mb-6">
|
|
<div class="flex items-center gap-3 px-6 py-4 border-b border-amber-200">
|
|
<i data-lucide="flask-conical" class="w-4 h-4 text-amber-600"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-amber-600"
|
|
x-text="t('bench.upload_skipped.title')"></span>
|
|
</div>
|
|
<div class="px-6 py-4 space-y-2">
|
|
<p class="text-sm text-amber-600"
|
|
x-text="benchUploadSkipped?.reason === 'external_endpoint' ? t('bench.upload_skipped.reason_external') : t('bench.upload_skipped.reason')"></p>
|
|
<ul class="text-sm text-amber-600 list-disc list-inside">
|
|
<template x-for="f in (benchUploadSkipped?.features || [])" :key="f">
|
|
<li x-text="t('bench.upload_skipped.features.' + f)"></li>
|
|
</template>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Metrics Reference -->
|
|
<div class="bg-white rounded-2xl border border-neutral-200 overflow-hidden">
|
|
<button @click="benchShowMetrics = !benchShowMetrics"
|
|
class="w-full flex items-center justify-between px-6 py-4 bg-neutral-100 border-b border-neutral-200 hover:bg-neutral-150 transition-colors">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="book-open" class="w-4 h-4 text-neutral-500"></i>
|
|
<span class="text-xs font-bold uppercase tracking-wider text-neutral-600">{{ t('bench.metrics.section_label') }}</span>
|
|
</div>
|
|
<i data-lucide="chevron-down" class="w-4 h-4 text-neutral-400 transition-transform" :class="benchShowMetrics && 'rotate-180'"></i>
|
|
</button>
|
|
<div x-show="benchShowMetrics" x-collapse x-cloak class="px-6 py-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.ttft.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.ttft.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.ttft.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.tpot.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.tpot.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.tpot.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.tg_tps.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.tg_tps.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.tg_tps.description') }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-3">
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.pp_tps.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.pp_tps.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.pp_tps.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.e2e.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.e2e.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.e2e.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.throughput.name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.throughput.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.batch_size.name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.batch_size.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.speedup.name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.speedup.description') }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-semibold text-neutral-900">{{ t('bench.metrics.pp_tps_req.name') }}</span>
|
|
<span class="text-neutral-400 ml-1">{{ t('bench.metrics.pp_tps_req.full_name') }}</span>
|
|
<p class="text-neutral-500 mt-0.5">{{ t('bench.metrics.pp_tps_req.description') }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Global fixed tooltip for bench metrics -->
|
|
<div x-ref="benchTipEl"
|
|
x-show="benchTip"
|
|
x-cloak
|
|
x-text="benchTip?.text"
|
|
class="bench-tip-fixed"></div>
|
|
</div>
|
|
|
|
<!-- Accuracy tab -->
|
|
{% include "dashboard/_bench_accuracy.html" %}
|
|
</div>
|