def _check_condition(node, condition): if "type" not in condition: reason = "condition_group字段中每个判断条件必须有type字段" raise DialogueStaticCheckException("condition_group", reason, node.node_name) if "operator" not in condition: reason = "condition_group字段中每个判断条件必须有operator字段" raise DialogueStaticCheckException("condition_group", reason, node.node_name) operator_options = ["==", "!=", ">", "<", "<=", ">=", "like", "isNull", "isNotNull"] if condition["operator"] not in operator_options: reason = "condition_group字段中operator字段必须是{}其中之一。而现在是{}".format( operator_options, condition["operator"]) type_options = ["intent", "entity", "global", "params"] if condition["type"] not in type_options: reason = "condition_group字段中type字段必须是{}其中之一,而现在是{}".format( type_options, condition["type"]) raise DialogueStaticCheckException("condition_group", reason, node.node_name) if condition["type"] in type_options[1:] and "value" not in condition: reason = "condition_group字段中如果条件判断类型为{},则必须指定value字段".format( condition["type"])
def build_graph(self, graph): """ 将对话流程配置构造成节点图 """ nodes_mapping = {} for node_meta in graph["nodes"]: node_type = node_meta["node_type"] node_id = node_meta["node_id"] if node_type not in TYPE_NODE_MAPPING: raise DialogueStaticCheckException( "node_type", "没有这种类型的节点: {}".format(node_type), node_id) node_class = TYPE_NODE_MAPPING[node_type] nodes_mapping[node_id] = node_class(node_meta) # 静态检查节点 try: nodes_mapping[node_id].static_check() except DialogueStaticCheckException as e: e.update_optional_params(robot_code=self.robot_code, graph_id=graph.get("id", "unknown")) raise e for conn in graph["connections"]: if "source_id" not in conn or "target_id" not in conn: raise DialogueStaticCheckException( conn.get("line_id", "unkown"), reason="连接线必须有source_id和target_id字段", robot_code=self.robot_code, graph_id=graph.get("graph_id", "unknown"), node_id=conn.get("line_id", "未知(连接线没有line_id字段)")) source_node = nodes_mapping[conn["source_id"]] target_node = nodes_mapping[conn["target_id"]] line_id = conn["line_id"] branch_id = conn.get("branch_id", None) intent_id = conn.get("intent_id", None) option_id = conn.get("options", None) if branch_id and intent_id: raise DialogueStaticCheckException( conn.get("line_id", "unkown"), "连接线不能同时指定branch_id, intent_id参数", robot_code=self.robot_code, graph_id=graph.get("graph_id", "unknown"), node_id=conn.get("line_id", "未知(连接线没有line_id字段)")) source_node.add_child(target_node, line_id, branch_id, intent_id, option_id) start_nodes = [ nodes_mapping[node_id] for node_id in graph["start_nodes"] ] for node in start_nodes: if not isinstance(node, nodes.StartNode): raise DialogueStaticCheckException( "node_type", "对话流程根节点的类型必须是开始节点", node.config.get("node_id", "未知")) return start_nodes
def fill_slot_node_slots_checker(node, slots): if not isinstance(slots, list): reason = "填槽节点的slots字段必须是list类型,现在是{}类型".format(type(slots)) raise DialogueStaticCheckException("slots", reason, node.node_name) for slot in slots: if "slot_name" not in slot or not isinstance(slot["slot_name"], str): reason = "填槽节点的slots字段中每个元素必须有slot_name字段,并且必须是str类型" raise DialogueStaticCheckException("slots", reason, node.node_name) if "multi" not in slot or not isinstance(slot["multi"], bool): reason = "填槽节点的slots字段中每个元素必须有multi字段,并且必须是bool类型" raise DialogueStaticCheckException("slots", reason, node.node_name) if "rounds" not in slot or not isinstance(slot["rounds"], int): reason = "填槽节点的slots字段每个元素必须有rounds字段,并且必须是int类型" raise DialogueStaticCheckException("slots", reason, node.node_name) if "reask_words" not in slot or not isinstance(slot["reask_words"], list): reason = "填槽节点的slots字段中每个元素必须有reask_words字段,并且必须是list类型" raise DialogueStaticCheckException("slots", reason, node.node_name) if "callback_words" not in slot or not isinstance( slot["callback_words"], list): reason = "填槽节点的slots字段中每个元素必须有callback_words字段,并且必须是list类型" raise DialogueStaticCheckException("slots", reason, node.node_name) if "is_necessary" not in slot or not isinstance( slot["is_necessary"], bool): reason = "填槽节点的slots字段中每个元素必须有is_necessary字段,并且必须是bool类型" raise DialogueStaticCheckException("slots", reason, node.node_name)
def start_node_conditional_checker(node, value): if not isinstance(value, list): reason = "slots字段的类型必须是list,而现在是{}".format(type(value)) raise DialogueStaticCheckException("slots", reason, node.node_name) for group in value: if not isinstance(group, list): reason = "condition_group字段中每个group的类型必须是list,目前是{}".format( type(group)) for condition in group: _check_condition(node, condition)
def judge_node_conditional_checker(node, branchs): if not isinstance(branchs, list): reason = "branchs字段的类型必须是list,而现在是{}".format(type(branchs)) raise DialogueStaticCheckException("branchs", reason=reason, node_id=node.node_name) for branch in branchs: if not isinstance(branch, dict): reason = "branchs字段中每个branch的类型必须是dict,目前是{}".format(type(branch)) raise DialogueStaticCheckException("branchs", reason, node_id=node.node_name) if "branch_id" not in branch: reason = "branchs字段中的每个branch中必须有branch_id字段" raise DialogueStaticCheckException("branchs", reason, node_id=node.node_name) if "conditions" not in branch or not isinstance( branch["conditions"], list): reason = "branchs字段中的每个group必须有conditions字段,并且是list类型" raise DialogueStaticCheckException("branchs", reason, node_id=node.node_name) for condition in branch["conditions"]: if not isinstance(condition, list): reason = "branchs字段中每个group的conditions字段必须是list,而现在是{}".format( type(condition)) raise DialogueStaticCheckException("slots", reason=reason, node_id=node.node_name) for group in condition: _check_condition(node, group)
def check(node, _): callback_in = "callback_words" in node.config life_cycle_in = "life_cycle" in node.config if life_cycle_in and not callback_in or (not life_cycle_in and callback_in): reason = "节点类型{}的配置中必须同时包含或者同时不包含callback_words和life_cycle两个字段。".format( node.NODE_NAME) raise DialogueStaticCheckException("callback_words, life_cycle", reason, node.node_name) if life_cycle_in and callback_in: simple_type_checker("callback_words", list)(node, node.config["callback_words"]) simple_type_checker("life_cycle", int)(node, node.config["life_cycle"])
def static_check(self): """ 静态检查配置的数据结构 """ required_checkers = chain(self.base_checkers.items(), self.required_checkers.items()) for key, func in required_checkers: if key in self.config: func(self, self.config[key]) else: raise DialogueStaticCheckException(key, "该节点缺少此必填字段", self.node_name) for key, func in self.optional_checkers.items(): if key in self.config: func(self, self.config[key])
def check(node, value): if value not in ref_values: reason = "字段key的值必须是{}中的值之一," \ " 而不是{}。".format(ref_values, value) raise DialogueStaticCheckException(key, reason, node.node_name)
def check(node, value): if not isinstance(value, dtype): reason = "字段{}的值必须是类型{}," \ "但是配置的类型是{}。".format(key, get_class_name( dtype), get_class_name(type(value))) raise DialogueStaticCheckException(key, reason, node.node_name)