diff --git a/web/src/stores/sft.test.ts b/web/src/stores/sft.test.ts new file mode 100644 index 0000000..d2bec40 --- /dev/null +++ b/web/src/stores/sft.test.ts @@ -0,0 +1,75 @@ +import { setActivePinia, createPinia } from 'pinia' +import { useSftStore } from './sft' +import type { SftQueueItem } from './sft' +import { beforeEach, describe, it, expect } from 'vitest' + +const MOCK_ITEM: SftQueueItem = { + id: 'abc', + source: 'cf-orch-benchmark', + benchmark_run_id: 'run1', + timestamp: '2026-04-07T10:00:00Z', + status: 'needs_review', + prompt_messages: [ + { role: 'system', content: 'You are a coding assistant.' }, + { role: 'user', content: 'Write a Python add function.' }, + ], + model_response: 'def add(a, b): return a - b', + corrected_response: null, + quality_score: 0.2, + failure_reason: 'pattern_match: 0/2 matched', + task_id: 'code-fn', + task_type: 'code', + task_name: 'Code: Write a Python function', + model_id: 'Qwen/Qwen2.5-3B', + model_name: 'Qwen2.5-3B', + node_id: 'heimdall', + gpu_id: 0, + tokens_per_sec: 38.4, +} + +describe('useSftStore', () => { + beforeEach(() => setActivePinia(createPinia())) + + it('starts with empty queue', () => { + const store = useSftStore() + expect(store.queue).toEqual([]) + expect(store.current).toBeNull() + }) + + it('current returns first item', () => { + const store = useSftStore() + store.queue = [MOCK_ITEM] + expect(store.current?.id).toBe('abc') + }) + + it('removeCurrentFromQueue removes first item', () => { + const store = useSftStore() + const second = { ...MOCK_ITEM, id: 'def' } + store.queue = [MOCK_ITEM, second] + store.removeCurrentFromQueue() + expect(store.queue[0].id).toBe('def') + }) + + it('restoreItem adds to front of queue', () => { + const store = useSftStore() + const second = { ...MOCK_ITEM, id: 'def' } + store.queue = [second] + store.restoreItem(MOCK_ITEM) + expect(store.queue[0].id).toBe('abc') + expect(store.queue[1].id).toBe('def') + }) + + it('setLastAction records the action', () => { + const store = useSftStore() + store.setLastAction('discard', MOCK_ITEM) + expect(store.lastAction?.type).toBe('discard') + expect(store.lastAction?.item.id).toBe('abc') + }) + + it('clearLastAction nulls lastAction', () => { + const store = useSftStore() + store.setLastAction('flag', MOCK_ITEM) + store.clearLastAction() + expect(store.lastAction).toBeNull() + }) +}) diff --git a/web/src/stores/sft.ts b/web/src/stores/sft.ts new file mode 100644 index 0000000..f65d1a0 --- /dev/null +++ b/web/src/stores/sft.ts @@ -0,0 +1,58 @@ +// src/stores/sft.ts +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' + +export interface SftQueueItem { + id: string + source: 'cf-orch-benchmark' + benchmark_run_id: string + timestamp: string + status: 'needs_review' | 'approved' | 'discarded' | 'model_rejected' + prompt_messages: { role: string; content: string }[] + model_response: string + corrected_response: string | null + quality_score: number // 0.0 to 1.0 + failure_reason: string + task_id: string + task_type: string + task_name: string + model_id: string + model_name: string + node_id: string + gpu_id: number + tokens_per_sec: number +} + +export interface SftLastAction { + type: 'correct' | 'discard' | 'flag' + item: SftQueueItem +} + +export const useSftStore = defineStore('sft', () => { + const queue = ref([]) + const totalRemaining = ref(0) + const lastAction = ref(null) + + const current = computed(() => queue.value[0] ?? null) + + function removeCurrentFromQueue() { + queue.value.shift() + } + + function setLastAction(type: SftLastAction['type'], item: SftQueueItem) { + lastAction.value = { type, item } + } + + function clearLastAction() { + lastAction.value = null + } + + function restoreItem(item: SftQueueItem) { + queue.value.unshift(item) + } + + return { + queue, totalRemaining, lastAction, current, + removeCurrentFromQueue, setLastAction, clearLastAction, restoreItem, + } +})