diff --git a/autogpt_platform/backend/backend/data/block.py b/autogpt_platform/backend/backend/data/block.py index 096195e2c..2a89e28d5 100644 --- a/autogpt_platform/backend/backend/data/block.py +++ b/autogpt_platform/backend/backend/data/block.py @@ -90,6 +90,7 @@ class BlockSchema(BaseModel): } elif isinstance(obj, list): return [ref_to_dict(item) for item in obj] + return obj cls.cached_jsonschema = cast(dict[str, Any], ref_to_dict(model)) diff --git a/autogpt_platform/frontend/src/components/node-input-components.tsx b/autogpt_platform/frontend/src/components/node-input-components.tsx index fb3d34d9c..f92ea1dcb 100644 --- a/autogpt_platform/frontend/src/components/node-input-components.tsx +++ b/autogpt_platform/frontend/src/components/node-input-components.tsx @@ -18,7 +18,7 @@ import { BlockIONumberSubSchema, BlockIOBooleanSubSchema, } from "@/lib/autogpt-server-api/types"; -import React, { FC, useCallback, useEffect, useState } from "react"; +import React, { FC, useCallback, useEffect, useMemo, useState } from "react"; import { Button } from "./ui/button"; import { Switch } from "./ui/switch"; import { @@ -326,13 +326,26 @@ export const NodeGenericInputField: FC<{ } } - if ("oneOf" in propSchema) { - // At the time of writing, this isn't used in the backend -> no impl. needed - console.error( - `Unsupported 'oneOf' in schema for '${propKey}'!`, - propSchema, + if ( + "oneOf" in propSchema && + propSchema.oneOf && + "discriminator" in propSchema && + propSchema.discriminator + ) { + return ( + ); - return null; } if (!("type" in propSchema)) { @@ -451,6 +464,132 @@ export const NodeGenericInputField: FC<{ } }; +const NodeOneOfDiscriminatorField: FC<{ + nodeId: string; + propKey: string; + propSchema: any; + currentValue?: any; + errors: { [key: string]: string | undefined }; + connections: any; + handleInputChange: (key: string, value: any) => void; + handleInputClick: (key: string) => void; + className?: string; + displayName?: string; +}> = ({ + nodeId, + propKey, + propSchema, + currentValue, + errors, + connections, + handleInputChange, + handleInputClick, + className, + displayName, +}) => { + const discriminator = propSchema.discriminator; + + const discriminatorProperty = discriminator.propertyName; + + const variantOptions = useMemo(() => { + const oneOfVariants = propSchema.oneOf || []; + + return oneOfVariants + .map((variant: any) => { + const variantDiscValue = + variant.properties?.[discriminatorProperty]?.const; + + return { + value: variantDiscValue, + schema: variant, + }; + }) + .filter((v: any) => v.value != null); + }, [discriminatorProperty, propSchema.oneOf]); + + const currentVariant = variantOptions.find( + (opt: any) => currentValue?.[discriminatorProperty] === opt.value, + ); + + const [chosenType, setChosenType] = useState( + currentVariant?.value || "", + ); + + const handleVariantChange = (newType: string) => { + setChosenType(newType); + const chosenVariant = variantOptions.find( + (opt: any) => opt.value === newType, + ); + if (chosenVariant) { + const initialValue = { + [discriminatorProperty]: newType, + }; + handleInputChange(propKey, initialValue); + } + }; + + const chosenVariantSchema = variantOptions.find( + (opt: any) => opt.value === chosenType, + )?.schema; + + return ( +
+ + + {chosenVariantSchema && ( +
+ {Object.entries(chosenVariantSchema.properties).map( + ([someKey, childSchema]) => { + if (someKey === "discriminator") { + return null; + } + const childKey = propKey ? `${propKey}.${someKey}` : someKey; + return ( +
+ + {(childSchema as BlockIOSubSchema).title || + beautifyString(someKey)} + + +
+ ); + }, + )} +
+ )} +
+ ); +}; + const NodeCredentialsInput: FC<{ selfKey: string; value: any; @@ -849,7 +988,7 @@ const NodeStringInput: FC<{ placeholder={ schema?.placeholder || `Enter ${beautifyString(displayName)}` } - className="pr-8 read-only:cursor-pointer read-only:text-gray-500 dark:text-white" + className="pr-8 read-only:cursor-pointer read-only:text-gray-500" />