feat(validation): Enhance schema validation with 'depends_on' support

- 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.
This commit is contained in:
abhi1992002
2024-12-02 11:02:00 +05:30
parent 2121ffd06b
commit bd1fa8f6c2
4 changed files with 81 additions and 1 deletions

View File

@@ -0,0 +1,49 @@
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
from backend.data.model import SchemaField
class InputValidationBlock(Block):
"""
TEMPORARY BLOCK FOR TESTING
"""
class Input(BlockSchema):
required_field: str = SchemaField(
description="parent of dependent_field", default=""
)
required_field_2: str = SchemaField(
description="parent of dependent_field", default=""
)
optional_field: str = SchemaField(
description="This field is optional", default=""
)
dependent_field: str = SchemaField(
description="This field depends on required_field being set",
depends_on=["required_field", "required_field_2"],
default="",
)
class Output(BlockSchema):
is_valid: bool = SchemaField(description="Whether the input validation passed")
def __init__(self):
super().__init__(
id="a1b2c3d4-e5f6-4a5b-9c8d-7e6f5d4c3b2a",
description="Validates input fields based on requirements and dependencies",
categories={BlockCategory.BASIC},
input_schema=InputValidationBlock.Input,
output_schema=InputValidationBlock.Output,
)
def run(self, input_data: Input, **kwargs) -> BlockOutput:
if not input_data.required_field or not input_data.required_field_2:
yield "is_valid", False
return
if input_data.dependent_field and (not input_data.required_field or not input_data.required_field_2):
yield "is_valid", False
return
yield "is_valid", True

View File

@@ -124,6 +124,7 @@ def SchemaField(
secret: bool = False,
exclude: bool = False,
hidden: Optional[bool] = None,
depends_on: list[str] | None = None,
**kwargs,
) -> T:
json_extra = {
@@ -133,6 +134,7 @@ def SchemaField(
"secret": secret,
"advanced": advanced,
"hidden": hidden,
"depends_on": depends_on,
}.items()
if v is not None
}

View File

@@ -366,6 +366,7 @@ export default function useAgentGraph(
nodes.forEach((node) => {
const validate = ajv.compile(node.data.inputSchema);
console.log("Validating node", node.id, node.data.inputSchema);
const errors = {} as { [key: string]: string };
// Validate values against schema using AJV
@@ -405,7 +406,34 @@ export default function useAgentGraph(
setNestedProperty(errors, key, "This field is required");
}
});
}
}
Object.entries(node.data.inputSchema.properties || {}).forEach(([key, schema]) => {
if ('depends_on' in schema) {
const dependencies = Array.isArray(schema.depends_on) ? schema.depends_on : [schema.depends_on];
// Check if dependent field is set while required fields are empty
const hasValue = inputData[key as keyof typeof inputData] && String(inputData[key as keyof typeof inputData]).length > 0;
const missingDependencies = dependencies.filter(dep => !inputData[dep as keyof typeof inputData] || String(inputData[dep as keyof typeof inputData]).length === 0);
if (hasValue && missingDependencies.length > 0) {
errors[key] = `Requires ${missingDependencies.join(', ')} to be set`;
errorMessage = `Field ${key} requires ${missingDependencies.join(', ')} to be set`;
}
// Check if required fields are set but dependent field is empty
const hasAllDependencies = dependencies.every(dep =>
inputData[dep as keyof typeof inputData] &&
String(inputData[dep as keyof typeof inputData]).length > 0
);
if (hasAllDependencies && (!inputData[key as keyof typeof inputData] || String(inputData[key as keyof typeof inputData]).length === 0)) {
errors[key] = `This field 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;
};