feat(ui): update node schema parsing

simplified logic thanks to backend changes
This commit is contained in:
psychedelicious 2023-08-20 20:24:43 +10:00
parent 56245a7406
commit ab76d54c10
4 changed files with 128 additions and 135 deletions

View File

@ -1,9 +0,0 @@
import { OpenAPIV3 } from 'openapi-types';
export const isReferenceObject = (
obj: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject
): obj is OpenAPIV3.ReferenceObject => '$ref' in obj;
export const isSchemaObject = (
obj: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject
): obj is OpenAPIV3.SchemaObject => !('$ref' in obj);

View File

@ -518,6 +518,18 @@ export type InvocationBaseSchemaObject = Omit<
> & > &
InvocationSchemaExtra; InvocationSchemaExtra;
export type InvocationOutputSchemaObject = Omit<
OpenAPIV3.SchemaObject,
'properties'
> &
OpenAPIV3.SchemaObject['properties'] & {
type: Omit<OpenAPIV3.SchemaObject, 'default'> & {
default: AnyInvocationType;
};
} & {
class: 'output';
};
export type InvocationFieldSchema = OpenAPIV3.SchemaObject & _InputField; export type InvocationFieldSchema = OpenAPIV3.SchemaObject & _InputField;
export interface ArraySchemaObject extends InvocationBaseSchemaObject { export interface ArraySchemaObject extends InvocationBaseSchemaObject {
@ -528,11 +540,30 @@ export interface NonArraySchemaObject extends InvocationBaseSchemaObject {
type?: OpenAPIV3.NonArraySchemaObjectType; type?: OpenAPIV3.NonArraySchemaObjectType;
} }
export type InvocationSchemaObject = ArraySchemaObject | NonArraySchemaObject; export type InvocationSchemaObject = (
| ArraySchemaObject
| NonArraySchemaObject
) & { class: 'invocation' };
export const isSchemaObject = (
obj: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject
): obj is OpenAPIV3.SchemaObject => !('$ref' in obj);
export const isInvocationSchemaObject = ( export const isInvocationSchemaObject = (
obj: OpenAPIV3.ReferenceObject | InvocationSchemaObject obj:
): obj is InvocationSchemaObject => !('$ref' in obj); | OpenAPIV3.ReferenceObject
| OpenAPIV3.SchemaObject
| InvocationSchemaObject
): obj is InvocationSchemaObject =>
'class' in obj && obj.class === 'invocation';
export const isInvocationOutputSchemaObject = (
obj:
| OpenAPIV3.ReferenceObject
| OpenAPIV3.SchemaObject
| InvocationOutputSchemaObject
): obj is InvocationOutputSchemaObject =>
'class' in obj && obj.class === 'output';
export const isInvocationFieldSchema = ( export const isInvocationFieldSchema = (
obj: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject obj: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject

View File

@ -1,8 +1,4 @@
import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { reduce } from 'lodash-es';
import { OpenAPIV3 } from 'openapi-types'; import { OpenAPIV3 } from 'openapi-types';
import { isSchemaObject } from '../types/typeGuards';
import { import {
BooleanInputFieldTemplate, BooleanInputFieldTemplate,
ClipInputFieldTemplate, ClipInputFieldTemplate,
@ -24,7 +20,6 @@ import {
LatentsInputFieldTemplate, LatentsInputFieldTemplate,
LoRAModelInputFieldTemplate, LoRAModelInputFieldTemplate,
MainModelInputFieldTemplate, MainModelInputFieldTemplate,
OutputFieldTemplate,
SDXLMainModelInputFieldTemplate, SDXLMainModelInputFieldTemplate,
SDXLRefinerModelInputFieldTemplate, SDXLRefinerModelInputFieldTemplate,
SchedulerInputFieldTemplate, SchedulerInputFieldTemplate,
@ -33,7 +28,6 @@ import {
VaeInputFieldTemplate, VaeInputFieldTemplate,
VaeModelInputFieldTemplate, VaeModelInputFieldTemplate,
isFieldType, isFieldType,
isInvocationFieldSchema,
} from '../types/types'; } from '../types/types';
export type BaseFieldProperties = 'name' | 'title' | 'description'; export type BaseFieldProperties = 'name' | 'title' | 'description';
@ -628,66 +622,3 @@ export const buildInputFieldTemplate = (
} }
return; return;
}; };
/**
* Builds invocation output fields from an invocation's output reference object.
* @param openAPI The OpenAPI schema
* @param refObject The output reference object
* @returns A record of outputs
*/
export const buildOutputFieldTemplates = (
refObject: OpenAPIV3.ReferenceObject,
openAPI: OpenAPIV3.Document
): Record<string, OutputFieldTemplate> => {
// extract output schema name from ref
const outputSchemaName = refObject.$ref.split('/').slice(-1)[0];
if (!outputSchemaName) {
logger('nodes').error(
{ refObject: parseify(refObject) },
'No output schema name found in ref object'
);
throw 'No output schema name found in ref object';
}
// get the output schema itself
const outputSchema = openAPI.components?.schemas?.[outputSchemaName];
if (!outputSchema) {
logger('nodes').error({ outputSchemaName }, 'Output schema not found');
throw 'Output schema not found';
}
// console.log('output', outputSchema);
if (isSchemaObject(outputSchema)) {
// console.log('isSchemaObject');
const outputFields = reduce(
outputSchema.properties as OpenAPIV3.SchemaObject,
(outputsAccumulator, property, propertyName) => {
if (
!['type', 'id'].includes(propertyName) &&
!['object'].includes(property.type) && // TODO: handle objects?
isInvocationFieldSchema(property)
) {
const fieldType = getFieldType(property);
// console.log('output fieldType', fieldType);
outputsAccumulator[propertyName] = {
fieldKind: 'output',
name: propertyName,
title: property.title ?? '',
description: property.description ?? '',
type: fieldType,
};
} else {
// console.warn('Unhandled OUTPUT property', property);
}
return outputsAccumulator;
},
{} as Record<string, OutputFieldTemplate>
);
return outputFields;
}
return {};
};

View File

@ -1,83 +1,123 @@
import { filter, reduce } from 'lodash-es'; import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { reduce } from 'lodash-es';
import { OpenAPIV3 } from 'openapi-types'; import { OpenAPIV3 } from 'openapi-types';
import { AnyInvocationType } from 'services/events/types';
import { import {
InputFieldTemplate, InputFieldTemplate,
InvocationSchemaObject, InvocationSchemaObject,
InvocationTemplate, InvocationTemplate,
OutputFieldTemplate,
isInvocationFieldSchema, isInvocationFieldSchema,
isInvocationOutputSchemaObject,
isInvocationSchemaObject, isInvocationSchemaObject,
} from '../types/types'; } from '../types/types';
import { import { buildInputFieldTemplate, getFieldType } from './fieldTemplateBuilders';
buildInputFieldTemplate,
buildOutputFieldTemplates,
} from './fieldTemplateBuilders';
const RESERVED_FIELD_NAMES = ['id', 'type', 'metadata']; const RESERVED_FIELD_NAMES = ['id', 'type', 'metadata'];
const invocationDenylist = [ const invocationDenylist: AnyInvocationType[] = [
'Graph', 'graph',
'InvocationMeta', 'metadata_accumulator',
'MetadataAccumulatorInvocation',
]; ];
const isNotInDenylist = (schema: InvocationSchemaObject) =>
!invocationDenylist.includes(schema.properties.type.default);
export const parseSchema = ( export const parseSchema = (
openAPI: OpenAPIV3.Document openAPI: OpenAPIV3.Document
): Record<string, InvocationTemplate> => { ): Record<string, InvocationTemplate> => {
const filteredSchemas = filter( const filteredSchemas = Object.values(openAPI.components?.schemas ?? {})
openAPI.components?.schemas, .filter(isInvocationSchemaObject)
(schema, key) => .filter(isNotInDenylist);
key.includes('Invocation') &&
!key.includes('InvocationOutput') &&
!invocationDenylist.some((denylistItem) => key.includes(denylistItem))
) as (OpenAPIV3.ReferenceObject | InvocationSchemaObject)[];
const invocations = filteredSchemas.reduce< const invocations = filteredSchemas.reduce<
Record<string, InvocationTemplate> Record<string, InvocationTemplate>
>((acc, schema) => { >((acc, schema) => {
if (isInvocationSchemaObject(schema)) { const type = schema.properties.type.default;
const type = schema.properties.type.default; const title = schema.title.replace('Invocation', '');
const title = schema.title.replace('Invocation', ''); const tags = schema.tags ?? [];
const tags = schema.tags ?? []; const description = schema.description ?? '';
const description = schema.description ?? '';
const inputs = reduce( const inputs = reduce(
schema.properties, schema.properties,
(inputsAccumulator, property, propertyName) => { (inputsAccumulator, property, propertyName) => {
if ( if (
!RESERVED_FIELD_NAMES.includes(propertyName) && !RESERVED_FIELD_NAMES.includes(propertyName) &&
isInvocationFieldSchema(property) && isInvocationFieldSchema(property) &&
!property.ui_hidden !property.ui_hidden
) { ) {
const field = buildInputFieldTemplate( const field = buildInputFieldTemplate(schema, property, propertyName);
schema,
property,
propertyName
);
if (field) { if (field) {
inputsAccumulator[propertyName] = field; inputsAccumulator[propertyName] = field;
}
} }
return inputsAccumulator; }
}, return inputsAccumulator;
{} as Record<string, InputFieldTemplate> },
{} as Record<string, InputFieldTemplate>
);
const outputSchemaName = schema.output.$ref.split('/').pop();
if (!outputSchemaName) {
logger('nodes').error(
{ outputRefObject: parseify(schema.output) },
'No output schema name found in ref object'
); );
throw 'No output schema name found in ref object';
const rawOutput = (schema as InvocationSchemaObject).output;
const outputs = buildOutputFieldTemplates(rawOutput, openAPI);
const invocation: InvocationTemplate = {
title,
type,
tags,
description,
inputs,
outputs,
};
Object.assign(acc, { [type]: invocation });
} }
const outputSchema = openAPI.components?.schemas?.[outputSchemaName];
if (!outputSchema) {
logger('nodes').error({ outputSchemaName }, 'Output schema not found');
throw 'Output schema not found';
}
if (!isInvocationOutputSchemaObject(outputSchema)) {
logger('nodes').error(
{ outputSchema: parseify(outputSchema) },
'Invalid output schema'
);
throw 'Invalid output schema';
}
const outputs = reduce(
outputSchema.properties as OpenAPIV3.SchemaObject,
(outputsAccumulator, property, propertyName) => {
if (
!['type', 'id'].includes(propertyName) &&
!['object'].includes(property.type) && // TODO: handle objects?
isInvocationFieldSchema(property)
) {
const fieldType = getFieldType(property);
outputsAccumulator[propertyName] = {
fieldKind: 'output',
name: propertyName,
title: property.title ?? '',
description: property.description ?? '',
type: fieldType,
};
} else {
logger('nodes').warn({ property }, 'Unhandled output property');
}
return outputsAccumulator;
},
{} as Record<string, OutputFieldTemplate>
);
const invocation: InvocationTemplate = {
title,
type,
tags,
description,
inputs,
outputs,
};
Object.assign(acc, { [type]: invocation });
return acc; return acc;
}, {}); }, {});