diff --git a/agent/component/switch.py b/agent/component/switch.py index 21b78efd0..72cfaccf2 100644 --- a/agent/component/switch.py +++ b/agent/component/switch.py @@ -14,50 +14,36 @@ # limitations under the License. # from abc import ABC - -import pandas as pd from agent.component.base import ComponentBase, ComponentParamBase class SwitchParam(ComponentParamBase): - """ Define the Switch component parameters. """ + def __init__(self): super().__init__() """ { - "cpn_id": "categorize:0", - "not": False, - "operator": "gt/gte/lt/lte/eq/in", - "value": "", + "logical_operator" : "and | or" + "items" : [ + {"cpn_id": "categorize:0", "operator": "contains", "value": ""}, + {"cpn_id": "categorize:0", "operator": "contains", "value": ""},...], "to": "" } """ self.conditions = [] - self.default = "" + self.end_cpn_id = "answer:0" + self.operators = ['contains', 'not contains', 'start with', 'end with', 'empty', 'not empty', '=', '≠', '>', + '<', '≥', '≤'] def check(self): self.check_empty(self.conditions, "[Switch] conditions") - self.check_empty(self.default, "[Switch] Default path") for cond in self.conditions: if not cond["to"]: raise ValueError(f"[Switch] 'To' can not be empty!") - - def operators(self, field, op, value): - if op == "gt": - return float(field) > float(value) - if op == "gte": - return float(field) >= float(value) - if op == "lt": - return float(field) < float(value) - if op == "lte": - return float(field) <= float(value) - if op == "eq": - return str(field) == str(value) - if op == "in": - return str(field).find(str(value)) >= 0 - return False + if cond["logical_operator"] not in ["and", "or"] and len(cond["items"]) > 1: + raise ValueError(f"[Switch] Please set logical_operator correctly!") class Switch(ComponentBase, ABC): @@ -65,13 +51,77 @@ class Switch(ComponentBase, ABC): def _run(self, history, **kwargs): for cond in self._param.conditions: - input = self._canvas.get_component(cond["cpn_id"])["obj"].output()[1] - if self._param.operators(input.iloc[0, 0], cond["operator"], cond["value"]): - if not cond["not"]: - return pd.DataFrame([{"content": cond["to"]}]) - return pd.DataFrame([{"content": self._param.default}]) + if len(cond["items"]) == 1: + out = self._canvas.get_component(cond["items"][0]["cpn_id"])["obj"].output()[1] + cpn_input = "" if "content" not in out.columns else " ".join(out["content"]) + if self.process_operator(cpn_input, cond["items"][0]["operator"], cond["items"][0]["value"]): + return Switch.be_output(cond["to"]) + continue + if cond["logical_operator"] == "and": + res = True + for item in cond["items"]: + out = self._canvas.get_component(item["cpn_id"])["obj"].output()[1] + cpn_input = "" if "content" not in out.columns else " ".join(out["content"]) + if not self.process_operator(cpn_input, item["operator"], item["value"]): + res = False + break + if res: + return Switch.be_output(cond["to"]) + continue + res = False + for item in cond["items"]: + out = self._canvas.get_component(item["cpn_id"])["obj"].output()[1] + cpn_input = "" if "content" not in out.columns else " ".join(out["content"]) + if self.process_operator(cpn_input, item["operator"], item["value"]): + res = True + break + if res: + return Switch.be_output(cond["to"]) + return Switch.be_output(self._param.end_cpn_id) + def process_operator(self, input: str, operator: str, value: str) -> bool: + if not isinstance(input, str) or not isinstance(value, str): + raise ValueError('Invalid input or value type: string') + + if operator == "contains": + return True if value.lower() in input.lower() else False + elif operator == "not contains": + return True if value.lower() not in input.lower() else False + elif operator == "start with": + return True if input.lower().startswith(value.lower()) else False + elif operator == "end with": + return True if input.lower().endswith(value.lower()) else False + elif operator == "empty": + return True if not input else False + elif operator == "not empty": + return True if input else False + elif operator == "=": + return True if input == value else False + elif operator == "≠": + return True if input != value else False + elif operator == ">": + try: + return True if float(input) > float(value) else False + except Exception as e: + return True if input > value else False + elif operator == "<": + try: + return True if float(input) < float(value) else False + except Exception as e: + return True if input < value else False + elif operator == "≥": + try: + return True if float(input) >= float(value) else False + except Exception as e: + return True if input >= value else False + elif operator == "≤": + try: + return True if float(input) <= float(value) else False + except Exception as e: + return True if input <= value else False + + raise ValueError('Not supported operator' + operator) diff --git a/agent/test/dsl_examples/baidu_generate_and_switch.json b/agent/test/dsl_examples/baidu_generate_and_switch.json new file mode 100644 index 000000000..90069cfaf --- /dev/null +++ b/agent/test/dsl_examples/baidu_generate_and_switch.json @@ -0,0 +1,129 @@ +{ + "components": { + "begin": { + "obj":{ + "component_name": "Begin", + "params": { + "prologue": "Hi there!" + } + }, + "downstream": ["answer:0"], + "upstream": [] + }, + "answer:0": { + "obj": { + "component_name": "Answer", + "params": {} + }, + "downstream": ["baidu:0"], + "upstream": ["begin", "message:0","message:1"] + }, + "baidu:0": { + "obj": { + "component_name": "Baidu", + "params": {} + }, + "downstream": ["generate:0"], + "upstream": ["answer:0"] + }, + "generate:0": { + "obj": { + "component_name": "Generate", + "params": { + "llm_id": "deepseek-chat", + "prompt": "You are an intelligent assistant. Please answer the user's question based on what Baidu searched. First, please output the user's question and the content searched by Baidu, and then answer yes, no, or i don't know.Here is the user's question:{user_input}The above is the user's question.Here is what Baidu searched for:{baidu}The above is the content searched by Baidu.", + "temperature": 0.2 + }, + "parameters": [ + { + "component_id": "answer:0", + "id": "69415446-49bf-4d4b-8ec9-ac86066f7709", + "key": "user_input" + }, + { + "component_id": "baidu:0", + "id": "83363c2a-00a8-402f-a45c-ddc4097d7d8b", + "key": "baidu" + } + ] + }, + "downstream": ["switch:0"], + "upstream": ["baidu:0"] + }, + "switch:0": { + "obj": { + "component_name": "Switch", + "params": { + "conditions": [ + { + "logical_operator" : "or", + "items" : [ + {"cpn_id": "generate:0", "operator": "contains", "value": "yes"}, + {"cpn_id": "generate:0", "operator": "contains", "value": "yeah"} + ], + "to": "message:0" + }, + { + "logical_operator" : "and", + "items" : [ + {"cpn_id": "generate:0", "operator": "contains", "value": "no"}, + {"cpn_id": "generate:0", "operator": "not contains", "value": "yes"}, + {"cpn_id": "generate:0", "operator": "not contains", "value": "know"} + ], + "to": "message:1" + }, + { + "logical_operator" : "", + "items" : [ + {"cpn_id": "generate:0", "operator": "contains", "value": "know"} + ], + "to": "message:2" + } + ], + "end_cpn_id": "answer:0" + + } + }, + "downstream": ["message:0","message:1"], + "upstream": ["generate:0"] + }, + "message:0": { + "obj": { + "component_name": "Message", + "params": { + "messages": ["YES YES YES YES YES YES YES YES YES YES YES YES"] + } + }, + + "upstream": ["switch:0"], + "downstream": ["answer:0"] + }, + "message:1": { + "obj": { + "component_name": "Message", + "params": { + "messages": ["NO NO NO NO NO NO NO NO NO NO NO NO NO NO"] + } + }, + + "upstream": ["switch:0"], + "downstream": ["answer:0"] + }, + "message:2": { + "obj": { + "component_name": "Message", + "params": { + "messages": ["I DON'T KNOW---------------------------"] + } + }, + + "upstream": ["switch:0"], + "downstream": ["answer:0"] + } + }, + "history": [], + "messages": [], + "reference": {}, + "path": [], + "answer": [] +}