Compare commits

...

2 Commits

Author SHA1 Message Date
Benjamin Lu
8a83e9fae1 fix: normalize queue estimate durations from ms to seconds 2026-02-22 00:58:50 -08:00
Benjamin Lu
81b3cda6cf fix: standardize execution time metadata to milliseconds 2026-02-22 00:49:44 -08:00
7 changed files with 35 additions and 20 deletions

View File

@@ -11,10 +11,10 @@ type QueueStore = UseQueueEstimatesOptions['queueStore']
type ExecutionStore = UseQueueEstimatesOptions['executionStore']
const makeHistoryTask = (
executionTimeInSeconds: number | string | undefined
executionTimeInMilliseconds: number | string | undefined
): TaskItemImpl =>
({
executionTimeInSeconds
executionTimeInSeconds: executionTimeInMilliseconds
}) as TaskItemImpl
const makeRunningTask = (executionStartTimestamp?: number): TaskItemImpl =>
@@ -116,13 +116,13 @@ describe('useQueueEstimates', () => {
})
it('uses the last 20 valid durations to estimate queued batches', () => {
const durations = Array.from({ length: 25 }, (_, idx) => idx + 1)
const durationsMs = Array.from({ length: 25 }, (_, idx) => (idx + 1) * 1000)
const queueStore = createQueueStore({
historyTasks: [
...durations.slice(0, 5).map((value) => makeHistoryTask(value)),
...durationsMs.slice(0, 5).map((value) => makeHistoryTask(value)),
makeHistoryTask('not-a-number'),
makeHistoryTask(undefined),
...durations.slice(5).map((value) => makeHistoryTask(value))
...durationsMs.slice(5).map((value) => makeHistoryTask(value))
]
})
@@ -144,7 +144,7 @@ describe('useQueueEstimates', () => {
const missingAhead = createHarness({
queueStore: createQueueStore({
historyTasks: [makeHistoryTask(10)]
historyTasks: [makeHistoryTask(10_000)]
})
})
expect(missingAhead.estimateRangeSeconds.value).toBeNull()
@@ -153,7 +153,9 @@ describe('useQueueEstimates', () => {
it('falls back to the running remaining range when there are no jobs ahead', () => {
const now = 20000
const queueStore = createQueueStore({
historyTasks: [10, 20, 30].map((value) => makeHistoryTask(value)),
historyTasks: [10_000, 20_000, 30_000].map((value) =>
makeHistoryTask(value)
),
runningTasks: [
makeRunningTask(now - 5000),
makeRunningTask(now - 15000),
@@ -173,7 +175,9 @@ describe('useQueueEstimates', () => {
it('subtracts elapsed time when estimating a running job', () => {
const now = 25000
const queueStore = createQueueStore({
historyTasks: [10, 20, 30].map((value) => makeHistoryTask(value))
historyTasks: [10_000, 20_000, 30_000].map((value) =>
makeHistoryTask(value)
)
})
const { estimateRemainingRangeSeconds } = createHarness({
@@ -189,7 +193,9 @@ describe('useQueueEstimates', () => {
it('uses the first-seen timestamp for pending jobs and clamps negatives to zero', () => {
const queueStore = createQueueStore({
historyTasks: [10, 20, 30].map((value) => makeHistoryTask(value))
historyTasks: [10_000, 20_000, 30_000].map((value) =>
makeHistoryTask(value)
)
})
const harness = createHarness({
@@ -207,6 +213,19 @@ describe('useQueueEstimates', () => {
expect(harness.estimateRemainingRangeSeconds.value).toEqual([0, 0])
})
it('converts execution durations from milliseconds to seconds', () => {
const queueStore = createQueueStore({
historyTasks: [2_000, 4_000].map((value) => makeHistoryTask(value))
})
const { estimateRangeSeconds } = createHarness({
queueStore,
jobsAhead: 1
})
expect(estimateRangeSeconds.value).toEqual([3, 4])
})
it('computes the elapsed label using execution start, then first-seen timestamp', () => {
const harness = createHarness()

View File

@@ -30,10 +30,8 @@ export const formatElapsedTime = (ms: number): string => {
const pickRecentDurations = (queueStore: QueueStore) =>
queueStore.historyTasks
.map((task: TaskItemImpl) => Number(task.executionTimeInSeconds))
.filter(
(value: number | undefined) =>
typeof value === 'number' && !Number.isNaN(value)
) as number[]
.filter((value: number) => Number.isFinite(value) && value >= 0)
.map((durationMilliseconds: number) => durationMilliseconds / 1000)
export const useQueueEstimates = ({
queueStore,

View File

@@ -153,7 +153,7 @@ function getAssetPreviewUrl(asset: AssetItem): string {
function getAssetSecondaryText(asset: AssetItem): string {
const metadata = getOutputAssetMetadata(asset.user_metadata)
if (typeof metadata?.executionTimeInSeconds === 'number') {
return `${metadata.executionTimeInSeconds.toFixed(2)}s`
return formatDuration(metadata.executionTimeInSeconds)
}
const duration = asset.user_metadata?.duration

View File

@@ -293,7 +293,7 @@ const shouldShowOutputCount = (item: AssetItem): boolean => {
const formattedExecutionTime = computed(() => {
if (!folderExecutionTime.value) return ''
return formatDuration(folderExecutionTime.value * 1000)
return formatDuration(folderExecutionTime.value)
})
const toast = useToast()

View File

@@ -255,7 +255,7 @@ const formattedDuration = computed(() => {
// Check for execution time first (from history API)
const executionTime = asset?.user_metadata?.executionTimeInSeconds
if (executionTime !== undefined && executionTime !== null) {
return `${Number(executionTime).toFixed(2)}s`
return formatDuration(Number(executionTime))
}
// Fall back to duration for media files

View File

@@ -391,9 +391,7 @@ export class TaskItemImpl {
}
get executionTimeInSeconds() {
return this.executionTime !== undefined
? this.executionTime / 1000
: undefined
return this.executionTime
}
/**

View File

@@ -139,7 +139,7 @@ export const buildJobDisplay = (
iconName: iconForJobState(state),
iconImageUrl,
primary,
secondary: time !== undefined ? `${time.toFixed(2)}s` : '',
secondary: time !== undefined ? formatDuration(time) : '',
showClear: false
}
}