package prediction import ( "encoding/json" "fmt" ) type NodeField struct { Node string `json:"node"` Field string `json:"field"` } type InputBinding map[string]*NodeField func ParseBinding(raw string) (InputBinding, error) { if raw == "" { return InputBinding{}, nil } var b InputBinding if err := json.Unmarshal([]byte(raw), &b); err != nil { return nil, err } return b, nil } func (b InputBinding) Validate(workflow map[string]any, required []string) error { for _, name := range required { f, ok := b[name] if !ok || f == nil || f.Node == "" || f.Field == "" { return fmt.Errorf("required binding missing: %s", name) } } for name, f := range b { if f == nil || f.Node == "" || f.Field == "" { continue } if err := validateNodeField(workflow, f.Node, f.Field); err != nil { return fmt.Errorf("binding %s: %w", name, err) } } return nil } func validateNodeField(workflow map[string]any, nodeID, field string) error { node, ok := workflow[nodeID].(map[string]any) if !ok { return fmt.Errorf("node %s not found", nodeID) } inputs, ok := node["inputs"].(map[string]any) if !ok { return fmt.Errorf("node %s has no inputs", nodeID) } if _, ok := inputs[field]; !ok { return fmt.Errorf("field %s not found on node %s", field, nodeID) } return nil }