feat(frontend): Add field extraction handle for block with object output (#8900)
This addresses https://github.com/Significant-Gravitas/AutoGPT/issues/8741 We have quite a few blocks with (object) outputs. The only way to really use these is to use a "Find In Dictionary" block to pick out that property. If the structure of the output object is known, we should expose the properties of the object directly as sub-outputs. This will make a huge difference in UX and make using these blocks much much easier. ### Changes 🏗️ Recursively flatten object fields into output node handles <img width="637" alt="image" src="https://github.com/user-attachments/assets/dac1f691-9866-4bb7-96b7-20fa6ddbb616"> <img width="773" alt="image" src="https://github.com/user-attachments/assets/f8e7f97c-b245-40bd-b84f-2c044f5f9e23"> ### Checklist 📋 #### For code changes: - [ ] I have clearly listed my changes in the PR description - [ ] I have made a test plan - [ ] I have tested my changes according to the test plan: <!-- Put your test plan here: --> - [ ] ... <details> <summary>Example test plan</summary> - [ ] Create from scratch and execute an agent with at least 3 blocks - [ ] Import an agent from file upload, and confirm it executes correctly - [ ] Upload agent to marketplace - [ ] Import an agent from marketplace and confirm it executes correctly - [ ] Edit an agent from monitor, and confirm it executes correctly </details> #### For configuration changes: - [ ] `.env.example` is updated or already compatible with my changes - [ ] `docker-compose.yml` is updated or already compatible with my changes - [ ] I have included a list of my configuration changes in the PR description (under **Changes**) <details> <summary>Examples of configuration changes</summary> - Changing ports - Adding new services that need to communicate with each other - Secrets or environment variable changes - New or infrastructure changes such as databases </details> Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
This commit is contained in:
parent
e6d728b081
commit
79c0c314e2
|
@ -13,6 +13,7 @@ import InputModalComponent from "./InputModalComponent";
|
||||||
import OutputModalComponent from "./OutputModalComponent";
|
import OutputModalComponent from "./OutputModalComponent";
|
||||||
import {
|
import {
|
||||||
BlockIORootSchema,
|
BlockIORootSchema,
|
||||||
|
BlockIOSubSchema,
|
||||||
BlockIOStringSubSchema,
|
BlockIOStringSubSchema,
|
||||||
Category,
|
Category,
|
||||||
NodeExecutionResult,
|
NodeExecutionResult,
|
||||||
|
@ -167,17 +168,38 @@ export function CustomNode({
|
||||||
nodeType === BlockUIType.NOTE
|
nodeType === BlockUIType.NOTE
|
||||||
)
|
)
|
||||||
return null;
|
return null;
|
||||||
const keys = Object.keys(schema.properties);
|
|
||||||
return keys.map((key) => (
|
const renderHandles = (
|
||||||
<div key={key}>
|
propSchema: { [key: string]: BlockIOSubSchema },
|
||||||
<NodeHandle
|
keyPrefix = "",
|
||||||
keyName={key}
|
titlePrefix = "",
|
||||||
isConnected={isOutputHandleConnected(key)}
|
) => {
|
||||||
schema={schema.properties[key]}
|
return Object.keys(propSchema).map((propKey) => {
|
||||||
side="right"
|
const fieldSchema = propSchema[propKey];
|
||||||
/>
|
const fieldTitle =
|
||||||
</div>
|
titlePrefix + (fieldSchema.title || beautifyString(propKey));
|
||||||
));
|
|
||||||
|
return (
|
||||||
|
<div key={propKey}>
|
||||||
|
<NodeHandle
|
||||||
|
title={fieldTitle}
|
||||||
|
keyName={`${keyPrefix}${propKey}`}
|
||||||
|
isConnected={isOutputHandleConnected(propKey)}
|
||||||
|
schema={fieldSchema}
|
||||||
|
side="right"
|
||||||
|
/>
|
||||||
|
{"properties" in fieldSchema &&
|
||||||
|
renderHandles(
|
||||||
|
fieldSchema.properties,
|
||||||
|
`${keyPrefix}${propKey}_#_`,
|
||||||
|
`${fieldTitle}.`,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return renderHandles(schema.properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateInputHandles = (
|
const generateInputHandles = (
|
||||||
|
|
|
@ -10,6 +10,7 @@ type HandleProps = {
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
isRequired?: boolean;
|
isRequired?: boolean;
|
||||||
side: "left" | "right";
|
side: "left" | "right";
|
||||||
|
title?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NodeHandle: FC<HandleProps> = ({
|
const NodeHandle: FC<HandleProps> = ({
|
||||||
|
@ -18,6 +19,7 @@ const NodeHandle: FC<HandleProps> = ({
|
||||||
isConnected,
|
isConnected,
|
||||||
isRequired,
|
isRequired,
|
||||||
side,
|
side,
|
||||||
|
title,
|
||||||
}) => {
|
}) => {
|
||||||
const typeName: Record<string, string> = {
|
const typeName: Record<string, string> = {
|
||||||
string: "text",
|
string: "text",
|
||||||
|
@ -34,7 +36,7 @@ const NodeHandle: FC<HandleProps> = ({
|
||||||
const label = (
|
const label = (
|
||||||
<div className="flex flex-grow flex-row">
|
<div className="flex flex-grow flex-row">
|
||||||
<span className="text-m green flex items-end pr-2 text-gray-900">
|
<span className="text-m green flex items-end pr-2 text-gray-900">
|
||||||
{schema.title || beautifyString(keyName.toLowerCase())}
|
{title || schema.title || beautifyString(keyName.toLowerCase())}
|
||||||
{isRequired ? "*" : ""}
|
{isRequired ? "*" : ""}
|
||||||
</span>
|
</span>
|
||||||
<span className={`${typeClass} flex items-end`}>
|
<span className={`${typeClass} flex items-end`}>
|
||||||
|
|
Loading…
Reference in New Issue