feat(blocks): Add depends_on support for input fields (#8852)

- 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>
This commit is contained in:
Abhimanyu Yadav 2024-12-17 08:50:19 +05:30 committed by GitHub
parent 2fe6eb1df1
commit 569222e9cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 84 additions and 1 deletions

View File

@ -257,7 +257,7 @@ class GraphModel(Graph):
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:
@ -278,6 +278,38 @@ class GraphModel(Graph):
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:

View File

@ -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 = {
@ -147,6 +148,7 @@ def SchemaField(
"secret": secret,
"advanced": advanced,
"hidden": hidden,
"depends_on": depends_on,
}.items()
if v is not None
}

View File

@ -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) => {

View File

@ -56,6 +56,7 @@ export type BlockIOSubSchemaMeta = {
description?: string;
placeholder?: string;
advanced?: boolean;
depends_on?: string[];
hidden?: boolean;
};