feat(avocet): add cancel buttons for benchmark and fine-tune runs

This commit is contained in:
pyr0ball 2026-03-15 18:15:35 -07:00
parent 8e016d7fe6
commit 1fa5b9e2b0

View file

@ -14,6 +14,13 @@
> >
{{ running ? '⏳ Running…' : results ? '🔄 Re-run' : '▶ Run Benchmark' }} {{ running ? '⏳ Running…' : results ? '🔄 Re-run' : '▶ Run Benchmark' }}
</button> </button>
<button
v-if="running"
class="btn-cancel"
@click="cancelBenchmark"
>
Cancel
</button>
</div> </div>
</header> </header>
@ -36,8 +43,8 @@
<!-- Progress log --> <!-- Progress log -->
<div v-if="running || runLog.length" class="run-log"> <div v-if="running || runLog.length" class="run-log">
<div class="run-log-title"> <div class="run-log-title">
<span>{{ running ? '⏳ Running benchmark…' : runError ? '❌ Failed' : '✅ Done' }}</span> <span>{{ running ? '⏳ Running benchmark…' : runCancelled ? '⏹ Cancelled' : runError ? '❌ Failed' : '✅ Done' }}</span>
<button class="btn-ghost" @click="runLog = []; runError = ''">Clear</button> <button class="btn-ghost" @click="runLog = []; runError = ''; runCancelled = false">Clear</button>
</div> </div>
<div class="log-lines" ref="logEl"> <div class="log-lines" ref="logEl">
<div <div
@ -169,12 +176,19 @@
> >
{{ ftRunning ? '⏳ Training…' : '▶ Run fine-tune' }} {{ ftRunning ? '⏳ Training…' : '▶ Run fine-tune' }}
</button> </button>
<button
v-if="ftRunning"
class="btn-cancel"
@click="cancelFinetune"
>
Cancel
</button>
</div> </div>
<div v-if="ftRunning || ftLog.length || ftError" class="run-log ft-log"> <div v-if="ftRunning || ftLog.length || ftError" class="run-log ft-log">
<div class="run-log-title"> <div class="run-log-title">
<span>{{ ftRunning ? '⏳ Training…' : ftError ? '❌ Failed' : '✅ Done' }}</span> <span>{{ ftRunning ? '⏳ Training…' : ftCancelled ? '⏹ Cancelled' : ftError ? '❌ Failed' : '✅ Done' }}</span>
<button class="btn-ghost" @click="ftLog = []; ftError = ''">Clear</button> <button class="btn-ghost" @click="ftLog = []; ftError = ''; ftCancelled = false">Clear</button>
</div> </div>
<div class="log-lines" ref="ftLogEl"> <div class="log-lines" ref="ftLogEl">
<div <div
@ -249,6 +263,17 @@ const ftLog = ref<string[]>([])
const ftError = ref('') const ftError = ref('')
const ftLogEl = ref<HTMLElement | null>(null) const ftLogEl = ref<HTMLElement | null>(null)
const runCancelled = ref(false)
const ftCancelled = ref(false)
async function cancelBenchmark() {
await fetch('/api/benchmark/cancel', { method: 'POST' }).catch(() => {})
}
async function cancelFinetune() {
await fetch('/api/finetune/cancel', { method: 'POST' }).catch(() => {})
}
// Derived // Derived
const modelNames = computed(() => Object.keys(results.value?.models ?? {})) const modelNames = computed(() => Object.keys(results.value?.models ?? {}))
const modelCount = computed(() => modelNames.value.length) const modelCount = computed(() => modelNames.value.length)
@ -328,6 +353,7 @@ function startBenchmark() {
running.value = true running.value = true
runLog.value = [] runLog.value = []
runError.value = '' runError.value = ''
runCancelled.value = false
const url = `/api/benchmark/run${includeSlow.value ? '?include_slow=true' : ''}` const url = `/api/benchmark/run${includeSlow.value ? '?include_slow=true' : ''}`
useApiSSE( useApiSSE(
@ -341,6 +367,10 @@ function startBenchmark() {
if (event.type === 'error' && typeof event.message === 'string') { if (event.type === 'error' && typeof event.message === 'string') {
runError.value = event.message runError.value = event.message
} }
if (event.type === 'cancelled') {
running.value = false
runCancelled.value = true
}
}, },
async () => { async () => {
running.value = false running.value = false
@ -363,6 +393,7 @@ function startFinetune() {
ftRunning.value = true ftRunning.value = true
ftLog.value = [] ftLog.value = []
ftError.value = '' ftError.value = ''
ftCancelled.value = false
const params = new URLSearchParams({ model: ftModel.value, epochs: String(ftEpochs.value) }) const params = new URLSearchParams({ model: ftModel.value, epochs: String(ftEpochs.value) })
useApiSSE( useApiSSE(
@ -376,6 +407,10 @@ function startFinetune() {
if (event.type === 'error' && typeof event.message === 'string') { if (event.type === 'error' && typeof event.message === 'string') {
ftError.value = event.message ftError.value = event.message
} }
if (event.type === 'cancelled') {
ftRunning.value = false
ftCancelled.value = true
}
}, },
async () => { async () => {
ftRunning.value = false ftRunning.value = false
@ -453,6 +488,22 @@ onMounted(() => {
.btn-run:disabled { opacity: 0.5; cursor: not-allowed; } .btn-run:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-run:not(:disabled):hover { opacity: 0.85; } .btn-run:not(:disabled):hover { opacity: 0.85; }
.btn-cancel {
padding: 0.45rem 0.9rem;
background: transparent;
border: 1px solid var(--color-text-secondary, #6b7a99);
color: var(--color-text-secondary, #6b7a99);
border-radius: 0.4rem;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s;
}
.btn-cancel:hover {
background: color-mix(in srgb, var(--color-text-secondary, #6b7a99) 12%, transparent);
}
/* ── Run log ────────────────────────────────────────────── */ /* ── Run log ────────────────────────────────────────────── */
.run-log { .run-log {
border: 1px solid var(--color-border, #d0d7e8); border: 1px solid var(--color-border, #d0d7e8);