Skip to content

Commit

Permalink
feat(blocks): Add depends_on support for input fields (#8852)
Browse files Browse the repository at this point in the history
- Resolves part of #8731 

### Changes
- Added `depends_on` parameter to SchemaField in `model.py` to specify
field dependencies.
- Updated `useAgentGraph` hook to validate input fields based on their
dependencies, ensuring required fields are set when dependent fields are
filled.
- Modified `BlockIOSubSchemaMeta` to include `depends_on` as an optional
property.



https://github.com/user-attachments/assets/64fd47b3-34dc-48fa-ad90-1c9c5cd4c4a3

---------

Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
  • Loading branch information
3 people authored Dec 17, 2024
1 parent 2fe6eb1 commit 569222e
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
34 changes: 33 additions & 1 deletion autogpt_platform/backend/backend/data/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def sanitize(name):
for link in self.links:
input_links[link.sink_id].append(link)

# Nodes: required fields are filled or connected
# Nodes: required fields are filled or connected and dependencies are satisfied
for node in self.nodes:
block = get_block(node.block_id)
if block is None:
Expand All @@ -278,6 +278,38 @@ def sanitize(name):
f"Node {block.name} #{node.id} required input missing: `{name}`"
)

# Get input schema properties and check dependencies
input_schema = block.input_schema.model_fields
required_fields = block.input_schema.get_required_fields()

def has_value(name):
return (
node is not None
and name in node.input_default
and node.input_default[name] is not None
and str(node.input_default[name]).strip() != ""
) or (name in input_schema and input_schema[name].default is not None)

# Validate dependencies between fields
for field_name, field_info in input_schema.items():

# Apply input dependency validation only on run & field with depends_on
json_schema_extra = field_info.json_schema_extra or {}
dependencies = json_schema_extra.get("depends_on", [])
if not for_run or not dependencies:
continue

# Check if dependent field has value in input_default
field_has_value = has_value(field_name)
field_is_required = field_name in required_fields

# Check for missing dependencies when dependent field is present
missing_deps = [dep for dep in dependencies if not has_value(dep)]
if missing_deps and (field_has_value or field_is_required):
raise ValueError(
f"Node {block.name} #{node.id}: Field `{field_name}` requires [{', '.join(missing_deps)}] to be set"
)

node_map = {v.id: v for v in self.nodes}

def is_static_output_block(nid: str) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions autogpt_platform/backend/backend/data/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def SchemaField(
secret: bool = False,
exclude: bool = False,
hidden: Optional[bool] = None,
depends_on: list[str] | None = None,
**kwargs,
) -> T:
json_extra = {
Expand All @@ -147,6 +148,7 @@ def SchemaField(
"secret": secret,
"advanced": advanced,
"hidden": hidden,
"depends_on": depends_on,
}.items()
if v is not None
}
Expand Down
48 changes: 48 additions & 0 deletions autogpt_platform/frontend/src/hooks/useAgentGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,54 @@ export default function useAgentGraph(
}
});
}

Object.entries(node.data.inputSchema.properties || {}).forEach(
([key, schema]) => {
if (schema.depends_on) {
const dependencies = schema.depends_on;

// Check if dependent field has value
const hasValue =
inputData[key] != null ||
("default" in schema && schema.default != null);

const mustHaveValue = node.data.inputSchema.required?.includes(key);

// Check for missing dependencies when dependent field is present
const missingDependencies = dependencies.filter(
(dep) =>
!inputData[dep as keyof typeof inputData] ||
String(inputData[dep as keyof typeof inputData]).trim() === "",
);

if ((hasValue || mustHaveValue) && missingDependencies.length > 0) {
setNestedProperty(
errors,
key,
`Requires ${missingDependencies.join(", ")} to be set`,
);
errorMessage = `Field ${key} requires ${missingDependencies.join(", ")} to be set`;
}

// Check if field is required when dependencies are present
const hasAllDependencies = dependencies.every(
(dep) =>
inputData[dep as keyof typeof inputData] &&
String(inputData[dep as keyof typeof inputData]).trim() !== "",
);

if (hasAllDependencies && !hasValue) {
setNestedProperty(
errors,
key,
`${key} is required when ${dependencies.join(", ")} are set`,
);
errorMessage = `${key} is required when ${dependencies.join(", ")} are set`;
}
}
},
);

// Set errors
setNodes((nodes) => {
return nodes.map((n) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type BlockIOSubSchemaMeta = {
description?: string;
placeholder?: string;
advanced?: boolean;
depends_on?: string[];
hidden?: boolean;
};

Expand Down

0 comments on commit 569222e

Please sign in to comment.