refactor: extract missing node type parsing to utility

Amp-Thread-ID: https://ampcode.com/threads/T-019c1662-2ba6-740f-b4d7-4d5902850034
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
bymyself
2026-01-31 15:42:23 -08:00
parent 556f9ee00a
commit da1ea116c0
3 changed files with 141 additions and 22 deletions

View File

@@ -70,6 +70,10 @@ import type { ComfyExtension, MissingNodeType } from '@/types/comfy'
import { type ExtensionManager } from '@/types/extensionTypes'
import type { NodeExecutionId } from '@/types/nodeIdentification'
import { graphToPrompt } from '@/utils/executionUtil'
import {
createMissingNodeTypeFromError,
type MissingNodeTypeExtraInfo
} from '@/utils/missingNodeUtil'
import { anyItemOverlapsRect } from '@/utils/mathUtil'
import { collectAllNodes, forEachNode } from '@/utils/graphTraversalUtil'
import {
@@ -1399,28 +1403,9 @@ export class ComfyApp {
typeof error.response.error === 'object' &&
error.response.error?.type === 'missing_node_type'
) {
const extraInfo = (error.response.error.extra_info ?? {}) as {
class_type?: string | null
node_title?: string | null
node_id?: string
}
const classType = extraInfo.class_type ?? 'Unknown'
const nodeTitle = extraInfo.node_title ?? classType
const nodeId = extraInfo.node_id
const hint = (() => {
if (nodeTitle !== classType && nodeId) {
return `"${nodeTitle}" (Node ID #${nodeId})`
} else if (nodeTitle !== classType) {
return `"${nodeTitle}"`
} else if (nodeId) {
return `Node ID #${nodeId}`
}
return undefined
})()
const missingNodeType: MissingNodeType = {
type: classType,
...(hint && { hint })
}
const extraInfo = (error.response.error.extra_info ??
{}) as MissingNodeTypeExtraInfo
const missingNodeType = createMissingNodeTypeFromError(extraInfo)
this.showMissingNodesError([missingNodeType])
} else {
useDialogService().showErrorDialog(error, {

View File

@@ -0,0 +1,90 @@
import { describe, expect, it } from 'vitest'
import {
buildMissingNodeHint,
createMissingNodeTypeFromError
} from './missingNodeUtil'
describe('buildMissingNodeHint', () => {
it('returns hint with title and node ID when both available', () => {
expect(buildMissingNodeHint('My Node', 'MyNodeClass', '42')).toBe(
'"My Node" (Node ID #42)'
)
})
it('returns hint with title only when no node ID', () => {
expect(buildMissingNodeHint('My Node', 'MyNodeClass', undefined)).toBe(
'"My Node"'
)
})
it('returns hint with node ID only when title matches class type', () => {
expect(buildMissingNodeHint('MyNodeClass', 'MyNodeClass', '42')).toBe(
'Node ID #42'
)
})
it('returns undefined when title matches class type and no node ID', () => {
expect(
buildMissingNodeHint('MyNodeClass', 'MyNodeClass', undefined)
).toBeUndefined()
})
it('returns undefined when title is null and no node ID', () => {
expect(buildMissingNodeHint(null, 'MyNodeClass', undefined)).toBeUndefined()
})
it('returns node ID hint when title is null but node ID exists', () => {
expect(buildMissingNodeHint(null, 'MyNodeClass', '42')).toBe('Node ID #42')
})
})
describe('createMissingNodeTypeFromError', () => {
it('returns string type when no hint is generated', () => {
const result = createMissingNodeTypeFromError({
class_type: 'MyNodeClass',
node_title: 'MyNodeClass'
})
expect(result).toBe('MyNodeClass')
})
it('returns object with hint when title differs from class type', () => {
const result = createMissingNodeTypeFromError({
class_type: 'MyNodeClass',
node_title: 'My Custom Title',
node_id: '42'
})
expect(result).toEqual({
type: 'MyNodeClass',
hint: '"My Custom Title" (Node ID #42)'
})
})
it('handles null class_type by defaulting to Unknown', () => {
const result = createMissingNodeTypeFromError({
class_type: null,
node_title: 'Some Title',
node_id: '42'
})
expect(result).toEqual({
type: 'Unknown',
hint: '"Some Title" (Node ID #42)'
})
})
it('handles empty extra_info', () => {
const result = createMissingNodeTypeFromError({})
expect(result).toBe('Unknown')
})
it('returns object with node ID hint when only node_id is available', () => {
const result = createMissingNodeTypeFromError({
class_type: 'MyNodeClass',
node_id: '123'
})
expect(result).toEqual({
type: 'MyNodeClass',
hint: 'Node ID #123'
})
})
})

View File

@@ -0,0 +1,44 @@
import type { MissingNodeType } from '@/types/comfy'
/**
* Extra info returned by the backend for missing_node_type errors
*/
export interface MissingNodeTypeExtraInfo {
class_type?: string | null
node_title?: string | null
node_id?: string
}
/**
* Builds a hint string from missing node metadata.
* Provides context about which node is missing (title, ID) when available.
*/
export function buildMissingNodeHint(
nodeTitle: string | null | undefined,
classType: string,
nodeId: string | undefined
): string | undefined {
const hasTitle = nodeTitle && nodeTitle !== classType
if (hasTitle && nodeId) {
return `"${nodeTitle}" (Node ID #${nodeId})`
} else if (hasTitle) {
return `"${nodeTitle}"`
} else if (nodeId) {
return `Node ID #${nodeId}`
}
return undefined
}
/**
* Creates a MissingNodeType from backend error extra_info.
* Used when the /prompt endpoint returns a missing_node_type error.
*/
export function createMissingNodeTypeFromError(
extraInfo: MissingNodeTypeExtraInfo
): MissingNodeType {
const classType = extraInfo.class_type ?? 'Unknown'
const nodeTitle = extraInfo.node_title ?? classType
const hint = buildMissingNodeHint(nodeTitle, classType, extraInfo.node_id)
return hint ? { type: classType, hint } : classType
}