feat(@projects/@magic-civilization): 🖥️ sprite-gen worker preferences + operations/coverage GUI updates
Adds a GUI preferences module and refines the worker engine, operations panel, workers page, and coverage page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f3c1cb5564
commit
e12307b43d
5 changed files with 99 additions and 23 deletions
|
|
@ -236,17 +236,17 @@ class GenerationWorker:
|
|||
submitted_this_loop = 0
|
||||
if to_submit:
|
||||
self._log(f"\n[loop {loop_count}] Submitting {len(to_submit)} sprites x {variants} variants...")
|
||||
on_complete = _on_complete if config["backend"] == "model-boss" else None
|
||||
submit_cb = on_complete if config["backend"] == "model-boss" else None
|
||||
submitted_this_loop = await gen.submit_batch(
|
||||
sprite_ids=to_submit,
|
||||
variants_per=variants,
|
||||
priority="high",
|
||||
on_complete=on_complete,
|
||||
on_complete=submit_cb,
|
||||
)
|
||||
|
||||
collected = 0
|
||||
if config["backend"] == "model-boss":
|
||||
collected = await gen.collect_pending(on_complete=_on_complete)
|
||||
collected = await gen.collect_pending(on_complete=on_complete)
|
||||
if collected:
|
||||
self._log(f" Collected {collected} images")
|
||||
|
||||
|
|
@ -285,14 +285,7 @@ class GenerationWorker:
|
|||
)
|
||||
|
||||
if st["needed"] == 0 and st["queued_variants"] == 0:
|
||||
self._log("\nAll sprites processed. Worker idle.")
|
||||
for _ in range(15):
|
||||
if self._stop.is_set():
|
||||
break
|
||||
await asyncio.sleep(2)
|
||||
if self._stop.is_set():
|
||||
break
|
||||
self._log("\nAll sprites processed. Worker idle (polling).")
|
||||
await asyncio.sleep(10)
|
||||
else:
|
||||
await asyncio.sleep(2)
|
||||
|
||||
self._log("Worker stopped.")
|
||||
await asyncio.sleep(2)
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
stopWorker,
|
||||
type StarterStatus,
|
||||
} from '../api'
|
||||
import { loadStoredBackend, loadStoredVariants, saveBackend, saveVariants } from '../preferences'
|
||||
import { colors } from './theme'
|
||||
import { Tooltip } from '../components/Tooltip'
|
||||
|
||||
|
|
@ -33,13 +34,20 @@ export function OperationsPanel({
|
|||
onBackendChange?: (b: string) => void
|
||||
}): ReactNode {
|
||||
const [backends, setBackends] = useState<string[]>(['model-boss', 'grok'])
|
||||
const [internalBackend, setInternalBackend] = useState('model-boss')
|
||||
const [internalBackend, setInternalBackend] = useState(() =>
|
||||
loadStoredBackend(['model-boss', 'grok'], 'model-boss'),
|
||||
)
|
||||
const backend = controlledBackend ?? internalBackend
|
||||
const setBackend = (b: string): void => {
|
||||
saveBackend(b)
|
||||
setInternalBackend(b)
|
||||
onBackendChange?.(b)
|
||||
}
|
||||
const [variants, setVariants] = useState(3)
|
||||
const [variants, setVariantsState] = useState(() => loadStoredVariants(3))
|
||||
const setVariants = (n: number): void => {
|
||||
saveVariants(n)
|
||||
setVariantsState(n)
|
||||
}
|
||||
const [workerRunning, setWorkerRunning] = useState(false)
|
||||
const [workerLog, setWorkerLog] = useState('')
|
||||
const [workerStats, setWorkerStats] = useState({ queued: 0, needed: 0, review: 0 })
|
||||
|
|
@ -75,7 +83,10 @@ export function OperationsPanel({
|
|||
fetchConfig()
|
||||
.then((c) => {
|
||||
setBackends(c.backends)
|
||||
setBackend(c.default_backend)
|
||||
if (controlledBackend === undefined) {
|
||||
const preferred = loadStoredBackend(c.backends, c.default_backend)
|
||||
setBackend(preferred)
|
||||
}
|
||||
})
|
||||
.catch(() => { /* defaults */ })
|
||||
refreshWorker()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { useCallback, useEffect, useState, type CSSProperties, type ReactNode } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { FlashNumber, useFlashRow } from '@lilith/ui-animated'
|
||||
import { fetchConfig, fetchPipeline, fetchVariants, triggerGenerate, type PipelineState } from '../api'
|
||||
import { fetchPipeline, fetchVariants, triggerGenerate, type PipelineState } from '../api'
|
||||
import type { Variant } from '../types'
|
||||
import { colors, SCORER_COLORS, SCORER_ORDER, statusColors } from './theme'
|
||||
import { Tooltip } from '../components/Tooltip'
|
||||
import { OperationsPanel } from './OperationsPanel'
|
||||
import { loadStoredBackend, saveBackend } from '../preferences'
|
||||
|
||||
type CoverageRow = PipelineState['sprite_coverage'][number]
|
||||
type BtnState = 'idle' | 'loading' | 'queued' | 'error'
|
||||
|
|
@ -453,7 +454,13 @@ export function SpriteCoveragePage(): ReactNode {
|
|||
const [search, setSearch] = useState('')
|
||||
const [categoryFilter, setCategoryFilter] = useState('all')
|
||||
const [batchState, setBatchState] = useState<BtnState>('idle')
|
||||
const [backend, setBackend] = useState('model-boss')
|
||||
const [backend, setBackendState] = useState(() =>
|
||||
loadStoredBackend(['model-boss', 'grok'], 'model-boss'),
|
||||
)
|
||||
const setBackend = (b: string): void => {
|
||||
saveBackend(b)
|
||||
setBackendState(b)
|
||||
}
|
||||
|
||||
const load = useCallback((): void => {
|
||||
fetchPipeline()
|
||||
|
|
@ -470,7 +477,6 @@ export function SpriteCoveragePage(): ReactNode {
|
|||
|
||||
useEffect((): (() => void) => {
|
||||
load()
|
||||
fetchConfig().then((c) => setBackend(c.default_backend)).catch(() => { /* default */ })
|
||||
const interval = setInterval(load, 5000)
|
||||
return () => clearInterval(interval)
|
||||
}, [load])
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ import {
|
|||
type StarterStatus,
|
||||
type WorkersOverview,
|
||||
} from '../api'
|
||||
import {
|
||||
loadStoredBackend,
|
||||
loadStoredBatchSize,
|
||||
loadStoredVariants,
|
||||
saveBackend,
|
||||
saveBatchSize,
|
||||
saveVariants,
|
||||
} from '../preferences'
|
||||
import { colors, SCORER_COLORS, SCORER_ORDER, SCORER_TOOLTIPS, type ScorerName } from './theme'
|
||||
import { Tooltip } from '../components/Tooltip'
|
||||
|
||||
|
|
@ -213,9 +221,23 @@ const actionBtn = (color: string, disabled: boolean): CSSProperties => ({
|
|||
export function WorkersPage(): ReactNode {
|
||||
const [overview, setOverview] = useState<WorkersOverview | null>(null)
|
||||
const [starter, setStarter] = useState<StarterStatus | null>(null)
|
||||
const [backend, setBackend] = useState('model-boss')
|
||||
const [variants, setVariants] = useState(3)
|
||||
const [batchSize, setBatchSize] = useState(4)
|
||||
const [backend, setBackendState] = useState(() =>
|
||||
loadStoredBackend(['model-boss', 'grok'], 'model-boss'),
|
||||
)
|
||||
const [variants, setVariantsState] = useState(() => loadStoredVariants(3))
|
||||
const [batchSize, setBatchSizeState] = useState(() => loadStoredBatchSize(4))
|
||||
const setBackend = (b: string): void => {
|
||||
saveBackend(b)
|
||||
setBackendState(b)
|
||||
}
|
||||
const setVariants = (n: number): void => {
|
||||
saveVariants(n)
|
||||
setVariantsState(n)
|
||||
}
|
||||
const setBatchSize = (n: number): void => {
|
||||
saveBatchSize(n)
|
||||
setBatchSizeState(n)
|
||||
}
|
||||
const [selectedScorers, setSelectedScorers] = useState<Set<ScorerName>>(new Set(['qwen3']))
|
||||
const [busy, setBusy] = useState<string | null>(null)
|
||||
const [toast, setToast] = useState<string | null>(null)
|
||||
|
|
@ -230,7 +252,6 @@ export function WorkersPage(): ReactNode {
|
|||
fetchWorkersOverview()
|
||||
.then((data) => {
|
||||
setOverview(data)
|
||||
setBackend(data.config.default_backend)
|
||||
setError(null)
|
||||
})
|
||||
.catch((e: unknown) => {
|
||||
|
|
|
|||
45
tools/sprite-generation/gui/src/preferences.ts
Normal file
45
tools/sprite-generation/gui/src/preferences.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const BACKEND_KEY = 'spritegen.backend'
|
||||
const VARIANTS_KEY = 'spritegen.variants'
|
||||
const BATCH_SIZE_KEY = 'spritegen.batch_size'
|
||||
|
||||
export function loadStoredBackend(allowed: string[], fallback: string): string {
|
||||
try {
|
||||
const stored = localStorage.getItem(BACKEND_KEY)
|
||||
if (stored && allowed.includes(stored)) return stored
|
||||
} catch { /* private browsing */ }
|
||||
return fallback
|
||||
}
|
||||
|
||||
export function saveBackend(backend: string): void {
|
||||
try {
|
||||
localStorage.setItem(BACKEND_KEY, backend)
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
export function loadStoredVariants(fallback = 3): number {
|
||||
try {
|
||||
const n = parseInt(localStorage.getItem(VARIANTS_KEY) ?? '', 10)
|
||||
if (n >= 1 && n <= 12) return n
|
||||
} catch { /* ignore */ }
|
||||
return fallback
|
||||
}
|
||||
|
||||
export function saveVariants(n: number): void {
|
||||
try {
|
||||
localStorage.setItem(VARIANTS_KEY, String(Math.max(1, Math.min(12, n))))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
export function loadStoredBatchSize(fallback = 4): number {
|
||||
try {
|
||||
const n = parseInt(localStorage.getItem(BATCH_SIZE_KEY) ?? '', 10)
|
||||
if (n >= 1 && n <= 20) return n
|
||||
} catch { /* ignore */ }
|
||||
return fallback
|
||||
}
|
||||
|
||||
export function saveBatchSize(n: number): void {
|
||||
try {
|
||||
localStorage.setItem(BATCH_SIZE_KEY, String(Math.max(1, Math.min(20, n))))
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue