From dd70ef3555b8b97ef9e2665ab44548f033b4a072 Mon Sep 17 00:00:00 2001 From: Richard Yu Date: Thu, 4 Dec 2025 15:30:49 -0800 Subject: [PATCH 1/5] [chore] Remove legacy history code (PR 3 of 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes legacy history API code and cleans up unused types from apiSchema.ts now that the migration to Jobs API is complete. Deleted files: - src/platform/remote/comfyui/history/* (except reconciliation.ts) - src/platform/workflow/cloud/getWorkflowFromHistory.ts - src/platform/workflow/cloud/index.ts - browser_tests/fixtures/utils/taskHistory.ts - tests-ui/fixtures/history*.ts - tests-ui/tests/platform/remote/comfyui/history/** - tests-ui/tests/platform/workflow/cloud/getWorkflowFromHistory.test.ts Cleaned up apiSchema.ts: - Removed legacy tuple types (zQueueIndex, zExtraData, etc.) - Removed RunningTaskItem, PendingTaskItem, HistoryTaskItem, TaskItem - Kept only types still in use (WebSocket messages, TaskOutput, etc.) Part of Jobs API migration. Depends on PR 2 (jobs-api-pr2-migration). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- browser_tests/fixtures/utils/taskHistory.ts | 164 -------------------- 1 file changed, 164 deletions(-) delete mode 100644 browser_tests/fixtures/utils/taskHistory.ts diff --git a/browser_tests/fixtures/utils/taskHistory.ts b/browser_tests/fixtures/utils/taskHistory.ts deleted file mode 100644 index 01dfb1a4aa..0000000000 --- a/browser_tests/fixtures/utils/taskHistory.ts +++ /dev/null @@ -1,164 +0,0 @@ -import type { Request, Route } from '@playwright/test' -import _ from 'es-toolkit/compat' -import fs from 'fs' -import path from 'path' -import { v4 as uuidv4 } from 'uuid' - -import type { - HistoryTaskItem, - TaskItem, - TaskOutput -} from '../../../src/schemas/apiSchema' -import type { ComfyPage } from '../ComfyPage' - -/** keyof TaskOutput[string] */ -type OutputFileType = 'images' | 'audio' | 'animated' - -const DEFAULT_IMAGE = 'example.webp' - -const getFilenameParam = (request: Request) => { - const url = new URL(request.url()) - return url.searchParams.get('filename') || DEFAULT_IMAGE -} - -const getContentType = (filename: string, fileType: OutputFileType) => { - const subtype = path.extname(filename).slice(1) - switch (fileType) { - case 'images': - return `image/${subtype}` - case 'audio': - return `audio/${subtype}` - case 'animated': - return `video/${subtype}` - } -} - -const setQueueIndex = (task: TaskItem) => { - task.prompt[0] = TaskHistory.queueIndex++ -} - -const setPromptId = (task: TaskItem) => { - task.prompt[1] = uuidv4() -} - -export default class TaskHistory { - static queueIndex = 0 - static readonly defaultTask: Readonly = { - prompt: [0, 'prompt-id', {}, { client_id: uuidv4() }, []], - outputs: {}, - status: { - status_str: 'success', - completed: true, - messages: [] - }, - taskType: 'History' - } - private tasks: HistoryTaskItem[] = [] - private outputContentTypes: Map = new Map() - - constructor(readonly comfyPage: ComfyPage) {} - - private loadAsset: (filename: string) => Buffer = _.memoize( - (filename: string) => { - const filePath = this.comfyPage.assetPath(filename) - return fs.readFileSync(filePath) - } - ) - - private async handleGetHistory(route: Route) { - return route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify(this.tasks) - }) - } - - private async handleGetView(route: Route) { - const fileName = getFilenameParam(route.request()) - if (!this.outputContentTypes.has(fileName)) { - return route.continue() - } - - const asset = this.loadAsset(fileName) - return route.fulfill({ - status: 200, - contentType: this.outputContentTypes.get(fileName), - body: asset, - headers: { - 'Cache-Control': 'public, max-age=31536000', - 'Content-Length': asset.byteLength.toString() - } - }) - } - - async setupRoutes() { - return this.comfyPage.page.route( - /.*\/api\/(view|history)(\?.*)?$/, - async (route) => { - const request = route.request() - const method = request.method() - - const isViewReq = request.url().includes('view') && method === 'GET' - if (isViewReq) return this.handleGetView(route) - - const isHistoryPath = request.url().includes('history') - const isGetHistoryReq = isHistoryPath && method === 'GET' - if (isGetHistoryReq) return this.handleGetHistory(route) - - const isClearReq = - method === 'POST' && - isHistoryPath && - request.postDataJSON()?.clear === true - if (isClearReq) return this.clearTasks() - - return route.continue() - } - ) - } - - private createOutputs( - filenames: string[], - filetype: OutputFileType - ): TaskOutput { - return filenames.reduce((outputs, filename, i) => { - const nodeId = `${i + 1}` - outputs[nodeId] = { - [filetype]: [{ filename, subfolder: '', type: 'output' }] - } - const contentType = getContentType(filename, filetype) - this.outputContentTypes.set(filename, contentType) - return outputs - }, {}) - } - - private addTask(task: HistoryTaskItem) { - setPromptId(task) - setQueueIndex(task) - this.tasks.unshift(task) // Tasks are added to the front of the queue - } - - clearTasks(): this { - this.tasks = [] - return this - } - - withTask( - outputFilenames: string[], - outputFiletype: OutputFileType = 'images', - overrides: Partial = {} - ): this { - this.addTask({ - ...TaskHistory.defaultTask, - outputs: this.createOutputs(outputFilenames, outputFiletype), - ...overrides - }) - return this - } - - /** Repeats the last task in the task history a specified number of times. */ - repeat(n: number): this { - for (let i = 0; i < n; i++) - this.addTask(structuredClone(this.tasks.at(0)) as HistoryTaskItem) - return this - } -} From d954555fc2b9e99e21431ed39c5576a42c20a85c Mon Sep 17 00:00:00 2001 From: Richard Yu Date: Mon, 8 Dec 2025 17:40:55 -0800 Subject: [PATCH 2/5] fix optional/nullable fields --- .../queue/job/JobDetailsPopover.stories.ts | 43 ++++++++++--------- src/platform/remote/comfyui/jobs/jobTypes.ts | 12 +++--- .../composables/useResultGallery.test.ts | 3 ++ .../comfyui/history/reconciliation.test.ts | 5 +++ .../remote/comfyui/jobs/fetchJobs.test.ts | 1 + tests-ui/tests/store/assetsStore.test.ts | 10 +++-- tests-ui/tests/store/queueStore.test.ts | 7 ++- .../stores/queueStore.loadWorkflow.test.ts | 16 ++++++- 8 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/components/queue/job/JobDetailsPopover.stories.ts b/src/components/queue/job/JobDetailsPopover.stories.ts index 866b88ddb6..6215977bee 100644 --- a/src/components/queue/job/JobDetailsPopover.stories.ts +++ b/src/components/queue/job/JobDetailsPopover.stories.ts @@ -41,12 +41,16 @@ function makeTask( id: string, priority: number, overrides: Omit, 'id' | 'priority'> & - Pick + Pick ): TaskItemImpl { const job: JobListItem = { id, priority, - last_state_update: null, + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, ...overrides } return new TaskItemImpl(job) @@ -59,8 +63,7 @@ function makePendingTask( ): TaskItemImpl { return makeTask(id, priority, { status: 'pending', - create_time: createTimeMs, - update_time: createTimeMs + create_time: createTimeMs }) } @@ -71,8 +74,7 @@ function makeRunningTask( ): TaskItemImpl { return makeTask(id, priority, { status: 'in_progress', - create_time: createTimeMs, - update_time: createTimeMs + create_time: createTimeMs }) } @@ -85,7 +87,7 @@ function makeRunningTaskWithStart( return makeTask(id, priority, { status: 'in_progress', create_time: start - 5000, - update_time: start + execution_start_time: start }) } @@ -102,22 +104,21 @@ function makeHistoryTask( return makeTask(id, priority, { status: ok ? 'completed' : 'failed', create_time: executionStartTime - 5000, - update_time: now, execution_start_time: executionStartTime, execution_end_time: executionEndTime, - execution_error: errorMessage - ? { - prompt_id: id, - timestamp: now, - node_id: '1', - node_type: 'ExampleNode', - exception_message: errorMessage, - exception_type: 'RuntimeError', - traceback: [], - current_inputs: {}, - current_outputs: {} - } - : undefined + ...(errorMessage && { + execution_error: { + prompt_id: id, + timestamp: now, + node_id: '1', + node_type: 'ExampleNode', + exception_message: errorMessage, + exception_type: 'RuntimeError', + traceback: [], + current_inputs: {}, + current_outputs: {} + } + }) }) } diff --git a/src/platform/remote/comfyui/jobs/jobTypes.ts b/src/platform/remote/comfyui/jobs/jobTypes.ts index e73ad24718..47aa6ea153 100644 --- a/src/platform/remote/comfyui/jobs/jobTypes.ts +++ b/src/platform/remote/comfyui/jobs/jobTypes.ts @@ -53,12 +53,12 @@ const zRawJobListItem = z id: z.string(), status: zJobStatus, create_time: z.number(), - execution_start_time: z.number().nullable().optional(), - execution_end_time: z.number().nullable().optional(), - preview_output: zPreviewOutput.nullable().optional(), - outputs_count: z.number().nullable().optional(), - execution_error: zExecutionError.nullable().optional(), - workflow_id: z.string().nullable().optional(), + execution_start_time: z.number().nullable(), + execution_end_time: z.number().nullable(), + preview_output: zPreviewOutput.nullable(), + outputs_count: z.number().nullable(), + execution_error: zExecutionError.optional(), + workflow_id: z.string().nullable(), priority: z.number().optional() }) .passthrough() diff --git a/tests-ui/tests/composables/useResultGallery.test.ts b/tests-ui/tests/composables/useResultGallery.test.ts index b68ef4bb03..2cf5ad3b5a 100644 --- a/tests-ui/tests/composables/useResultGallery.test.ts +++ b/tests-ui/tests/composables/useResultGallery.test.ts @@ -26,8 +26,11 @@ const createMockJob = (id: string, outputsCount = 1): JobListItem => ({ id, status: 'completed', create_time: Date.now(), + execution_start_time: null, + execution_end_time: null, preview_output: null, outputs_count: outputsCount, + workflow_id: null, priority: 0 }) diff --git a/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts b/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts index e0eee5fc7c..4365e21e9d 100644 --- a/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts +++ b/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts @@ -11,6 +11,11 @@ function createJob(id: string, createTime = 0, priority?: number): JobListItem { id, status: 'completed', create_time: createTime, + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, priority: priority ?? createTime } } diff --git a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts index f3b2ad5a82..31b29b8281 100644 --- a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts +++ b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts @@ -27,6 +27,7 @@ function createMockJob( execution_end_time: null, preview_output: null, outputs_count: 0, + workflow_id: null, ...overrides } } diff --git a/tests-ui/tests/store/assetsStore.test.ts b/tests-ui/tests/store/assetsStore.test.ts index 4f5924f342..8429d12910 100644 --- a/tests-ui/tests/store/assetsStore.test.ts +++ b/tests-ui/tests/store/assetsStore.test.ts @@ -95,14 +95,16 @@ describe('assetsStore - Refactored (Option A)', () => { id: `prompt_${index}`, status: 'completed', create_time: 1000 + index, - update_time: 1000 + index, - last_state_update: 1000 + index, - priority: 1000 + index, + execution_start_time: null, + execution_end_time: null, preview_output: { filename: `output_${index}.png`, subfolder: '', type: 'output' - } + }, + outputs_count: null, + workflow_id: null, + priority: 1000 + index }) beforeEach(() => { diff --git a/tests-ui/tests/store/queueStore.test.ts b/tests-ui/tests/store/queueStore.test.ts index d185a7a59a..402e1a3056 100644 --- a/tests-ui/tests/store/queueStore.test.ts +++ b/tests-ui/tests/store/queueStore.test.ts @@ -17,8 +17,11 @@ function createJob( id, status, create_time: createTime, - update_time: createTime, - last_state_update: createTime, + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, priority: priority ?? createTime } } diff --git a/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts b/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts index 2b6450b817..2a48320ec7 100644 --- a/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts +++ b/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts @@ -35,7 +35,11 @@ const mockJobDetail = { id: 'test-prompt-id', status: 'completed' as const, create_time: Date.now(), - update_time: Date.now(), + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, workflow: { extra_data: { extra_pnginfo: { @@ -54,6 +58,11 @@ function createHistoryJob(id: string): JobListItem { id, status: 'completed', create_time: now, + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, priority: now } } @@ -64,6 +73,11 @@ function createRunningJob(id: string): JobListItem { id, status: 'in_progress', create_time: now, + execution_start_time: null, + execution_end_time: null, + preview_output: null, + outputs_count: null, + workflow_id: null, priority: now } } From d95086b82ff1f63e6c6635b29f18f1c173e5aa69 Mon Sep 17 00:00:00 2001 From: Richard Yu Date: Tue, 9 Dec 2025 10:45:32 -0800 Subject: [PATCH 3/5] make all fields optional but nonnullable --- .../queue/job/JobDetailsPopover.stories.ts | 5 ----- src/platform/remote/comfyui/jobs/jobTypes.ts | 14 +++++++------- .../tests/composables/useResultGallery.test.ts | 4 ---- .../remote/comfyui/history/reconciliation.test.ts | 5 ----- .../remote/comfyui/jobs/fetchJobs.test.ts | 5 ----- tests-ui/tests/store/assetsStore.test.ts | 4 ---- tests-ui/tests/store/queueStore.test.ts | 5 ----- .../tests/stores/queueStore.loadWorkflow.test.ts | 15 --------------- 8 files changed, 7 insertions(+), 50 deletions(-) diff --git a/src/components/queue/job/JobDetailsPopover.stories.ts b/src/components/queue/job/JobDetailsPopover.stories.ts index 6215977bee..22388f5d62 100644 --- a/src/components/queue/job/JobDetailsPopover.stories.ts +++ b/src/components/queue/job/JobDetailsPopover.stories.ts @@ -46,11 +46,6 @@ function makeTask( const job: JobListItem = { id, priority, - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, ...overrides } return new TaskItemImpl(job) diff --git a/src/platform/remote/comfyui/jobs/jobTypes.ts b/src/platform/remote/comfyui/jobs/jobTypes.ts index 47aa6ea153..314ba549ed 100644 --- a/src/platform/remote/comfyui/jobs/jobTypes.ts +++ b/src/platform/remote/comfyui/jobs/jobTypes.ts @@ -38,8 +38,8 @@ const zExecutionError = z exception_message: z.string(), exception_type: z.string(), traceback: z.array(z.string()), - current_inputs: z.unknown(), - current_outputs: z.unknown() + current_inputs: z.record(z.string(), z.unknown()), + current_outputs: z.record(z.string(), z.unknown()) }) .passthrough() @@ -53,12 +53,12 @@ const zRawJobListItem = z id: z.string(), status: zJobStatus, create_time: z.number(), - execution_start_time: z.number().nullable(), - execution_end_time: z.number().nullable(), - preview_output: zPreviewOutput.nullable(), - outputs_count: z.number().nullable(), + execution_start_time: z.number().optional(), + execution_end_time: z.number().optional(), + preview_output: zPreviewOutput.optional(), + outputs_count: z.number().optional(), execution_error: zExecutionError.optional(), - workflow_id: z.string().nullable(), + workflow_id: z.string().optional(), priority: z.number().optional() }) .passthrough() diff --git a/tests-ui/tests/composables/useResultGallery.test.ts b/tests-ui/tests/composables/useResultGallery.test.ts index 2cf5ad3b5a..49b0c32052 100644 --- a/tests-ui/tests/composables/useResultGallery.test.ts +++ b/tests-ui/tests/composables/useResultGallery.test.ts @@ -26,11 +26,7 @@ const createMockJob = (id: string, outputsCount = 1): JobListItem => ({ id, status: 'completed', create_time: Date.now(), - execution_start_time: null, - execution_end_time: null, - preview_output: null, outputs_count: outputsCount, - workflow_id: null, priority: 0 }) diff --git a/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts b/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts index 4365e21e9d..e0eee5fc7c 100644 --- a/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts +++ b/tests-ui/tests/platform/remote/comfyui/history/reconciliation.test.ts @@ -11,11 +11,6 @@ function createJob(id: string, createTime = 0, priority?: number): JobListItem { id, status: 'completed', create_time: createTime, - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, priority: priority ?? createTime } } diff --git a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts index 31b29b8281..a254978635 100644 --- a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts +++ b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts @@ -23,11 +23,6 @@ function createMockJob( id, status, create_time: Date.now(), - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: 0, - workflow_id: null, ...overrides } } diff --git a/tests-ui/tests/store/assetsStore.test.ts b/tests-ui/tests/store/assetsStore.test.ts index 8429d12910..420f9de9ae 100644 --- a/tests-ui/tests/store/assetsStore.test.ts +++ b/tests-ui/tests/store/assetsStore.test.ts @@ -95,15 +95,11 @@ describe('assetsStore - Refactored (Option A)', () => { id: `prompt_${index}`, status: 'completed', create_time: 1000 + index, - execution_start_time: null, - execution_end_time: null, preview_output: { filename: `output_${index}.png`, subfolder: '', type: 'output' }, - outputs_count: null, - workflow_id: null, priority: 1000 + index }) diff --git a/tests-ui/tests/store/queueStore.test.ts b/tests-ui/tests/store/queueStore.test.ts index 402e1a3056..747f2dd1e9 100644 --- a/tests-ui/tests/store/queueStore.test.ts +++ b/tests-ui/tests/store/queueStore.test.ts @@ -17,11 +17,6 @@ function createJob( id, status, create_time: createTime, - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, priority: priority ?? createTime } } diff --git a/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts b/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts index 2a48320ec7..9b776879a4 100644 --- a/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts +++ b/tests-ui/tests/stores/queueStore.loadWorkflow.test.ts @@ -35,11 +35,6 @@ const mockJobDetail = { id: 'test-prompt-id', status: 'completed' as const, create_time: Date.now(), - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, workflow: { extra_data: { extra_pnginfo: { @@ -58,11 +53,6 @@ function createHistoryJob(id: string): JobListItem { id, status: 'completed', create_time: now, - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, priority: now } } @@ -73,11 +63,6 @@ function createRunningJob(id: string): JobListItem { id, status: 'in_progress', create_time: now, - execution_start_time: null, - execution_end_time: null, - preview_output: null, - outputs_count: null, - workflow_id: null, priority: now } } From 2d68ad42c827cba170b0436541d99fcb5ca94a41 Mon Sep 17 00:00:00 2001 From: Richard Yu Date: Tue, 9 Dec 2025 11:27:12 -0800 Subject: [PATCH 4/5] get queue size to 50, remove misleading comment --- src/platform/remote/comfyui/jobs/fetchJobs.ts | 29 +++--- src/platform/remote/comfyui/jobs/jobTypes.ts | 95 +++++++------------ .../remote/comfyui/jobs/fetchJobs.test.ts | 2 +- 3 files changed, 45 insertions(+), 81 deletions(-) diff --git a/src/platform/remote/comfyui/jobs/fetchJobs.ts b/src/platform/remote/comfyui/jobs/fetchJobs.ts index 136f683d65..5d8859d3b2 100644 --- a/src/platform/remote/comfyui/jobs/fetchJobs.ts +++ b/src/platform/remote/comfyui/jobs/fetchJobs.ts @@ -1,11 +1,3 @@ -/** - * @fileoverview Jobs API Fetchers - * @module platform/remote/comfyui/jobs/fetchJobs - * - * Unified jobs API fetcher for history, queue, and job details. - * All distributions use the /jobs endpoint. - */ - import type { PromptId } from '@/schemas/apiSchema' import type { @@ -14,7 +6,7 @@ import type { JobStatus, RawJobListItem } from './jobTypes' -import { zJobDetail, zJobsListResponse, zWorkflowContainer } from './jobTypes' +import { zJobDetail, zJobsListResponse } from './jobTypes' interface FetchJobsRawResult { jobs: RawJobListItem[] @@ -94,7 +86,7 @@ export async function fetchQueue( const { jobs } = await fetchJobsRaw( fetchApi, ['in_progress', 'pending'], - 200, + 50, 0 ) @@ -134,13 +126,14 @@ export async function fetchJobDetail( } } -/** - * Extracts workflow from job detail response. - * The workflow is nested at: workflow.extra_data.extra_pnginfo.workflow - * Full workflow validation happens downstream via validateComfyWorkflow. - */ +/** Extracts workflow from job detail response */ export function extractWorkflow(job: JobDetail | undefined): unknown { - const parsed = zWorkflowContainer.safeParse(job?.workflow) - if (!parsed.success) return undefined - return parsed.data.extra_data?.extra_pnginfo?.workflow + const workflow = job?.workflow + if (!workflow || typeof workflow !== 'object') return undefined + const container = workflow as Record + const extraData = container.extra_data + if (!extraData || typeof extraData !== 'object') return undefined + const pnginfo = (extraData as Record).extra_pnginfo + if (!pnginfo || typeof pnginfo !== 'object') return undefined + return (pnginfo as Record).workflow } diff --git a/src/platform/remote/comfyui/jobs/jobTypes.ts b/src/platform/remote/comfyui/jobs/jobTypes.ts index 314ba549ed..9ff392c0bd 100644 --- a/src/platform/remote/comfyui/jobs/jobTypes.ts +++ b/src/platform/remote/comfyui/jobs/jobTypes.ts @@ -1,11 +1,3 @@ -/** - * @fileoverview Jobs API types - Backend job API format - * @module platform/remote/comfyui/jobs/jobTypes - * - * These types represent the jobs API format returned by the backend. - * Jobs API provides a memory-optimized alternative to history API. - */ - import { z } from 'zod' import { resultItemType, zTaskOutput } from '@/schemas/apiSchema' @@ -25,57 +17,49 @@ const zPreviewOutput = z.object({ }) /** - * Execution error details for error jobs. + * Execution error details for failed jobs. * Contains the same structure as ExecutionErrorWsMessage from WebSocket. */ -const zExecutionError = z - .object({ - prompt_id: z.string().optional(), - timestamp: z.number().optional(), - node_id: z.string(), - node_type: z.string(), - executed: z.array(z.string()).optional(), - exception_message: z.string(), - exception_type: z.string(), - traceback: z.array(z.string()), - current_inputs: z.record(z.string(), z.unknown()), - current_outputs: z.record(z.string(), z.unknown()) - }) - .passthrough() +const zExecutionError = z.object({ + prompt_id: z.string().optional(), + timestamp: z.number().optional(), + node_id: z.string(), + node_type: z.string(), + executed: z.array(z.string()).optional(), + exception_message: z.string(), + exception_type: z.string(), + traceback: z.array(z.string()), + current_inputs: z.record(z.string(), z.unknown()), + current_outputs: z.record(z.string(), z.unknown()) +}) export type ExecutionError = z.infer -/** - * Raw job from API - uses passthrough to allow extra fields - */ -const zRawJobListItem = z - .object({ - id: z.string(), - status: zJobStatus, - create_time: z.number(), - execution_start_time: z.number().optional(), - execution_end_time: z.number().optional(), - preview_output: zPreviewOutput.optional(), - outputs_count: z.number().optional(), - execution_error: zExecutionError.optional(), - workflow_id: z.string().optional(), - priority: z.number().optional() - }) - .passthrough() +/** Raw job from API list endpoint */ +const zRawJobListItem = z.object({ + id: z.string(), + status: zJobStatus, + create_time: z.number(), + execution_start_time: z.number().optional(), + execution_end_time: z.number().optional(), + preview_output: zPreviewOutput.optional(), + outputs_count: z.number().optional(), + execution_error: zExecutionError.optional(), + workflow_id: z.string().optional(), + priority: z.number().optional() +}) /** * Job detail - returned by GET /api/jobs/{job_id} (detail endpoint) * Includes full workflow and outputs for re-execution and downloads */ -export const zJobDetail = zRawJobListItem - .extend({ - workflow: z.unknown().optional(), - outputs: zTaskOutput.optional(), - update_time: z.number().optional(), - execution_status: z.unknown().optional(), - execution_meta: z.unknown().optional() - }) - .passthrough() +export const zJobDetail = zRawJobListItem.extend({ + workflow: z.unknown().optional(), + outputs: zTaskOutput.optional(), + update_time: z.number().optional(), + execution_status: z.unknown().optional(), + execution_meta: z.unknown().optional() +}) const zPaginationInfo = z.object({ offset: z.number(), @@ -89,19 +73,6 @@ export const zJobsListResponse = z.object({ pagination: zPaginationInfo }) -/** Schema for workflow container structure in job detail responses */ -export const zWorkflowContainer = z.object({ - extra_data: z - .object({ - extra_pnginfo: z - .object({ - workflow: z.unknown() - }) - .optional() - }) - .optional() -}) - export type JobStatus = z.infer export type RawJobListItem = z.infer /** Job list item with priority always set (server-provided or synthetic) */ diff --git a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts index a254978635..7c0ce95278 100644 --- a/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts +++ b/tests-ui/tests/platform/remote/comfyui/jobs/fetchJobs.test.ts @@ -169,7 +169,7 @@ describe('fetchJobs', () => { const result = await fetchQueue(mockFetch) expect(mockFetch).toHaveBeenCalledWith( - '/jobs?status=in_progress,pending&limit=200&offset=0' + '/jobs?status=in_progress,pending&limit=50&offset=0' ) expect(result.Running).toHaveLength(1) expect(result.Pending).toHaveLength(2) From b0e424dd7deb088f435841f35f37de0d3cdb8600 Mon Sep 17 00:00:00 2001 From: Richard Yu Date: Wed, 10 Dec 2025 12:16:36 -0800 Subject: [PATCH 5/5] fix: properly type extractWorkflow return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed return type from `unknown` to `ComfyWorkflowJSON | undefined` so callers don't need type assertions. Validation still happens downstream in loadGraphData when the setting is enabled. Addresses DrJKL's review comment on PR 7170. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/platform/remote/comfyui/jobs/fetchJobs.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/platform/remote/comfyui/jobs/fetchJobs.ts b/src/platform/remote/comfyui/jobs/fetchJobs.ts index 5d8859d3b2..c34fc09593 100644 --- a/src/platform/remote/comfyui/jobs/fetchJobs.ts +++ b/src/platform/remote/comfyui/jobs/fetchJobs.ts @@ -1,3 +1,4 @@ +import type { ComfyWorkflowJSON } from '@/platform/workflow/validation/schemas/workflowSchema' import type { PromptId } from '@/schemas/apiSchema' import type { @@ -126,8 +127,13 @@ export async function fetchJobDetail( } } -/** Extracts workflow from job detail response */ -export function extractWorkflow(job: JobDetail | undefined): unknown { +/** + * Extracts workflow from job detail response. + * Validation happens downstream in loadGraphData when the setting is enabled. + */ +export function extractWorkflow( + job: JobDetail | undefined +): ComfyWorkflowJSON | undefined { const workflow = job?.workflow if (!workflow || typeof workflow !== 'object') return undefined const container = workflow as Record @@ -135,5 +141,7 @@ export function extractWorkflow(job: JobDetail | undefined): unknown { if (!extraData || typeof extraData !== 'object') return undefined const pnginfo = (extraData as Record).extra_pnginfo if (!pnginfo || typeof pnginfo !== 'object') return undefined - return (pnginfo as Record).workflow + return (pnginfo as Record).workflow as + | ComfyWorkflowJSON + | undefined }