mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): organize node utils
This commit is contained in:
@ -4,7 +4,7 @@ import { cloneDeep, omit, reduce } from 'lodash-es';
|
||||
import { Graph } from 'services/api/types';
|
||||
import { AnyInvocation } from 'services/events/types';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { buildWorkflow } from '../buildWorkflow';
|
||||
import { buildWorkflow } from '../workflow/buildWorkflow';
|
||||
import {
|
||||
FieldInputInstance,
|
||||
isColorFieldInputInstance,
|
@ -0,0 +1,23 @@
|
||||
import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
|
||||
import { CurrentImageNode } from 'features/nodes/types/invocation';
|
||||
import { XYPosition } from 'reactflow';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const buildCurrentImageNode = (
|
||||
position: XYPosition
|
||||
): CurrentImageNode => {
|
||||
const nodeId = uuidv4();
|
||||
const node: CurrentImageNode = {
|
||||
...SHARED_NODE_PROPERTIES,
|
||||
id: nodeId,
|
||||
type: 'current_image',
|
||||
position,
|
||||
data: {
|
||||
id: nodeId,
|
||||
type: 'current_image',
|
||||
isOpen: true,
|
||||
label: 'Current Image',
|
||||
},
|
||||
};
|
||||
return node;
|
||||
};
|
@ -0,0 +1,79 @@
|
||||
import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
|
||||
import {
|
||||
FieldInputInstance,
|
||||
FieldOutputInstance,
|
||||
} from 'features/nodes/types/field';
|
||||
import {
|
||||
InvocationNode,
|
||||
InvocationTemplate,
|
||||
} from 'features/nodes/types/invocation';
|
||||
import { buildFieldInputInstance } from 'features/nodes/util/schema/buildFieldInputInstance';
|
||||
import { reduce } from 'lodash-es';
|
||||
import { XYPosition } from 'reactflow';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const buildInvocationNode = (
|
||||
position: XYPosition,
|
||||
template: InvocationTemplate
|
||||
): InvocationNode => {
|
||||
const nodeId = uuidv4();
|
||||
const { type } = template;
|
||||
|
||||
const inputs = reduce(
|
||||
template.inputs,
|
||||
(inputsAccumulator, inputTemplate, inputName) => {
|
||||
const fieldId = uuidv4();
|
||||
|
||||
const inputFieldValue: FieldInputInstance = buildFieldInputInstance(
|
||||
fieldId,
|
||||
inputTemplate
|
||||
);
|
||||
|
||||
inputsAccumulator[inputName] = inputFieldValue;
|
||||
|
||||
return inputsAccumulator;
|
||||
},
|
||||
{} as Record<string, FieldInputInstance>
|
||||
);
|
||||
|
||||
const outputs = reduce(
|
||||
template.outputs,
|
||||
(outputsAccumulator, outputTemplate, outputName) => {
|
||||
const fieldId = uuidv4();
|
||||
|
||||
const outputFieldValue: FieldOutputInstance = {
|
||||
id: fieldId,
|
||||
name: outputName,
|
||||
type: outputTemplate.type,
|
||||
fieldKind: 'output',
|
||||
};
|
||||
|
||||
outputsAccumulator[outputName] = outputFieldValue;
|
||||
|
||||
return outputsAccumulator;
|
||||
},
|
||||
{} as Record<string, FieldOutputInstance>
|
||||
);
|
||||
|
||||
const node: InvocationNode = {
|
||||
...SHARED_NODE_PROPERTIES,
|
||||
id: nodeId,
|
||||
type: 'invocation',
|
||||
position,
|
||||
data: {
|
||||
id: nodeId,
|
||||
type,
|
||||
version: template.version,
|
||||
label: '',
|
||||
notes: '',
|
||||
isOpen: true,
|
||||
embedWorkflow: false,
|
||||
isIntermediate: type === 'save_image' ? false : true,
|
||||
useCache: template.useCache,
|
||||
inputs,
|
||||
outputs,
|
||||
},
|
||||
};
|
||||
|
||||
return node;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants';
|
||||
import { NotesNode } from 'features/nodes/types/invocation';
|
||||
import { XYPosition } from 'reactflow';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const buildNotesNode = (position: XYPosition): NotesNode => {
|
||||
const nodeId = uuidv4();
|
||||
const node: NotesNode = {
|
||||
...SHARED_NODE_PROPERTIES,
|
||||
id: nodeId,
|
||||
type: 'notes',
|
||||
position,
|
||||
data: {
|
||||
id: nodeId,
|
||||
isOpen: true,
|
||||
label: 'Notes',
|
||||
notes: '',
|
||||
type: 'notes',
|
||||
},
|
||||
};
|
||||
return node;
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { isNil } from 'lodash-es';
|
||||
import { FieldInputTemplate, FieldOutputTemplate } from '../types/field';
|
||||
import { FieldInputTemplate, FieldOutputTemplate } from '../../types/field';
|
||||
|
||||
export const getSortedFilteredFieldNames = (
|
||||
fields: FieldInputTemplate[] | FieldOutputTemplate[]
|
@ -0,0 +1,68 @@
|
||||
import { satisfies } from 'compare-versions';
|
||||
import { NodeUpdateError } from 'features/nodes/types/error';
|
||||
import {
|
||||
InvocationNodeData,
|
||||
InvocationTemplate,
|
||||
} from 'features/nodes/types/invocation';
|
||||
import { zParsedSemver } from 'features/nodes/types/semver';
|
||||
import { cloneDeep, defaultsDeep } from 'lodash-es';
|
||||
import { Node } from 'reactflow';
|
||||
import { buildInvocationNode } from './buildInvocationNode';
|
||||
|
||||
export const getNeedsUpdate = (
|
||||
node: Node<InvocationNodeData>,
|
||||
template: InvocationTemplate
|
||||
): boolean => {
|
||||
if (node.data.type !== template.type) {
|
||||
return true;
|
||||
}
|
||||
return node.data.version !== template.version;
|
||||
}; /**
|
||||
* Checks if a node may be updated by comparing its major version with the template's major version.
|
||||
* @param node The node to check.
|
||||
* @param template The invocation template to check against.
|
||||
*/
|
||||
|
||||
export const getMayUpdateNode = (
|
||||
node: Node<InvocationNodeData>,
|
||||
template: InvocationTemplate
|
||||
): boolean => {
|
||||
const needsUpdate = getNeedsUpdate(node, template);
|
||||
if (!needsUpdate || node.data.type !== template.type) {
|
||||
return false;
|
||||
}
|
||||
const templateMajor = zParsedSemver.parse(template.version).major;
|
||||
|
||||
return satisfies(node.data.version, `^${templateMajor}`);
|
||||
}; /**
|
||||
* Updates a node to the latest version of its template:
|
||||
* - Create a new node data object with the latest version of the template.
|
||||
* - Recursively merge new node data object into the node to be updated.
|
||||
*
|
||||
* @param node The node to updated.
|
||||
* @param template The invocation template to update to.
|
||||
* @throws {NodeUpdateError} If the node is not an invocation node.
|
||||
*/
|
||||
|
||||
export const updateNode = (
|
||||
node: Node<InvocationNodeData>,
|
||||
template: InvocationTemplate
|
||||
): Node<InvocationNodeData> => {
|
||||
const mayUpdate = getMayUpdateNode(node, template);
|
||||
|
||||
if (!mayUpdate || node.data.type !== template.type) {
|
||||
throw new NodeUpdateError(`Unable to update node ${node.id}`);
|
||||
}
|
||||
|
||||
// Start with a "fresh" node - just as if the user created a new node of this type
|
||||
const defaults = buildInvocationNode(node.position, template);
|
||||
|
||||
// The updateability of a node, via semver comparison, relies on the this kind of recursive merge
|
||||
// being valid. We rely on the template's major version to be majorly incremented if this kind of
|
||||
// merge would result in an invalid node.
|
||||
const clone = cloneDeep(node);
|
||||
clone.data.version = template.version;
|
||||
defaultsDeep(clone, defaults); // mutates!
|
||||
|
||||
return clone;
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { get } from 'lodash-es';
|
||||
import { FieldInputInstance, FieldInputTemplate } from '../types/field';
|
||||
import { FieldInputInstance, FieldInputTemplate } from '../../types/field';
|
||||
|
||||
const FIELD_VALUE_FALLBACK_MAP = {
|
||||
EnumField: '',
|
@ -22,8 +22,8 @@ import {
|
||||
T2IAdapterModelFieldInputTemplate,
|
||||
VAEModelFieldInputTemplate,
|
||||
isStatefulFieldType,
|
||||
} from '../types/field';
|
||||
import { InvocationFieldSchema } from '../types/openapi';
|
||||
} from '../../types/field';
|
||||
import { InvocationFieldSchema } from '../../types/openapi';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type FieldInputTemplateBuilder<T extends FieldInputTemplate = any> = // valid `any`!
|
@ -1,6 +1,6 @@
|
||||
import { startCase } from 'lodash-es';
|
||||
import { FieldOutputTemplate, FieldType } from '../types/field';
|
||||
import { InvocationFieldSchema } from '../types/openapi';
|
||||
import { FieldOutputTemplate, FieldType } from '../../types/field';
|
||||
import { InvocationFieldSchema } from '../../types/openapi';
|
||||
|
||||
export const buildFieldOutputTemplate = (
|
||||
fieldSchema: InvocationFieldSchema,
|
@ -1,8 +1,11 @@
|
||||
import { t } from 'i18next';
|
||||
import { isArray } from 'lodash-es';
|
||||
import { OpenAPIV3_1 } from 'openapi-types';
|
||||
import { FieldTypeParseError, UnsupportedFieldTypeError } from '../types/error';
|
||||
import { FieldType } from '../types/field';
|
||||
import {
|
||||
FieldTypeParseError,
|
||||
UnsupportedFieldTypeError,
|
||||
} from '../../types/error';
|
||||
import { FieldType } from '../../types/field';
|
||||
import {
|
||||
OpenAPIV3_1SchemaOrRef,
|
||||
isArraySchemaObject,
|
||||
@ -10,7 +13,7 @@ import {
|
||||
isNonArraySchemaObject,
|
||||
isRefObject,
|
||||
isSchemaObject,
|
||||
} from '../types/openapi';
|
||||
} from '../../types/openapi';
|
||||
|
||||
/**
|
||||
* Transforms an invocation output ref object to field type.
|
@ -3,15 +3,18 @@ import { parseify } from 'common/util/serialize';
|
||||
import { t } from 'i18next';
|
||||
import { reduce } from 'lodash-es';
|
||||
import { OpenAPIV3_1 } from 'openapi-types';
|
||||
import { FieldTypeParseError, UnsupportedFieldTypeError } from '../types/error';
|
||||
import { FieldInputTemplate, FieldOutputTemplate } from '../types/field';
|
||||
import { InvocationTemplate } from '../types/invocation';
|
||||
import {
|
||||
FieldTypeParseError,
|
||||
UnsupportedFieldTypeError,
|
||||
} from '../../types/error';
|
||||
import { FieldInputTemplate, FieldOutputTemplate } from '../../types/field';
|
||||
import { InvocationTemplate } from '../../types/invocation';
|
||||
import {
|
||||
InvocationSchemaObject,
|
||||
isInvocationFieldSchema,
|
||||
isInvocationOutputSchemaObject,
|
||||
isInvocationSchemaObject,
|
||||
} from '../types/openapi';
|
||||
} from '../../types/openapi';
|
||||
import { buildFieldInputTemplate } from './buildFieldInputTemplate';
|
||||
import { buildFieldOutputTemplate } from './buildFieldOutputTemplate';
|
||||
import { parseFieldType } from './parseFieldType';
|
@ -1,6 +1,6 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { NodesState } from '../store/types';
|
||||
import { WorkflowV2, zWorkflowEdge, zWorkflowNode } from '../types/workflow';
|
||||
import { NodesState } from '../../store/types';
|
||||
import { WorkflowV2, zWorkflowEdge, zWorkflowNode } from '../../types/workflow';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
import { parseify } from 'common/util/serialize';
|
||||
import i18n from 'i18next';
|
@ -2,10 +2,10 @@ import { parseify } from 'common/util/serialize';
|
||||
import { t } from 'i18next';
|
||||
import { keyBy } from 'lodash-es';
|
||||
import { JsonObject } from 'type-fest';
|
||||
import { getNeedsUpdate } from '../store/util/nodeUpdate';
|
||||
import { InvocationTemplate } from '../types/invocation';
|
||||
import { parseAndMigrateWorkflow } from '../types/migration/migrations';
|
||||
import { WorkflowV2, isWorkflowInvocationNode } from '../types/workflow';
|
||||
import { getNeedsUpdate } from '../node/nodeUpdate';
|
||||
import { InvocationTemplate } from '../../types/invocation';
|
||||
import { parseAndMigrateWorkflow } from '../../types/migration/migrations';
|
||||
import { WorkflowV2, isWorkflowInvocationNode } from '../../types/workflow';
|
||||
|
||||
type WorkflowWarning = {
|
||||
message: string;
|
Reference in New Issue
Block a user