diff --git a/agent/canvas.py b/agent/canvas.py index 3e15814aa..9e95a5611 100644 --- a/agent/canvas.py +++ b/agent/canvas.py @@ -281,6 +281,7 @@ class Canvas(Graph): "sys.conversation_turns": 0, "sys.files": [] } + self.variables = {} super().__init__(dsl, tenant_id, task_id) def load(self): @@ -295,6 +296,10 @@ class Canvas(Graph): "sys.conversation_turns": 0, "sys.files": [] } + if "variables" in self.dsl: + self.variables = self.dsl["variables"] + else: + self.variables = {} self.retrieval = self.dsl["retrieval"] self.memory = self.dsl.get("memory", []) @@ -311,8 +316,9 @@ class Canvas(Graph): self.history = [] self.retrieval = [] self.memory = [] + print(self.variables) for k in self.globals.keys(): - if k.startswith("sys.") or k.startswith("env."): + if k.startswith("sys."): if isinstance(self.globals[k], str): self.globals[k] = "" elif isinstance(self.globals[k], int): @@ -325,6 +331,29 @@ class Canvas(Graph): self.globals[k] = {} else: self.globals[k] = None + if k.startswith("env."): + key = k[4:] + if key in self.variables: + variable = self.variables[key] + if variable["value"]: + self.globals[k] = variable["value"] + else: + if variable["type"] == "string": + self.globals[k] = "" + elif variable["type"] == "number": + self.globals[k] = 0 + elif variable["type"] == "boolean": + self.globals[k] = False + elif variable["type"] == "object": + self.globals[k] = {} + elif variable["type"].startswith("array"): + self.globals[k] = [] + else: + self.globals[k] = "" + else: + self.globals[k] = "" + print(self.globals) + async def run(self, **kwargs): st = time.perf_counter() @@ -473,7 +502,7 @@ class Canvas(Graph): else: self.error = cpn_obj.error() - if cpn_obj.component_name.lower() != "iteration": + if cpn_obj.component_name.lower() not in ("iteration","loop"): if isinstance(cpn_obj.output("content"), partial): if self.error: cpn_obj.set_output("content", None) @@ -498,14 +527,16 @@ class Canvas(Graph): for cpn_id in cpn_ids: _append_path(cpn_id) - if cpn_obj.component_name.lower() == "iterationitem" and cpn_obj.end(): + if cpn_obj.component_name.lower() in ("iterationitem","loopitem") and cpn_obj.end(): iter = cpn_obj.get_parent() yield _node_finished(iter) _extend_path(self.get_component(cpn["parent_id"])["downstream"]) elif cpn_obj.component_name.lower() in ["categorize", "switch"]: _extend_path(cpn_obj.output("_next")) - elif cpn_obj.component_name.lower() == "iteration": + elif cpn_obj.component_name.lower() in ("iteration", "loop"): _append_path(cpn_obj.get_start()) + elif cpn_obj.component_name.lower() == "exitloop" and cpn_obj.get_parent().component_name.lower() == "loop": + _extend_path(self.get_component(cpn["parent_id"])["downstream"]) elif not cpn["downstream"] and cpn_obj.get_parent(): _append_path(cpn_obj.get_parent().get_start()) else: diff --git a/agent/component/exit_loop.py b/agent/component/exit_loop.py new file mode 100644 index 000000000..9dc044912 --- /dev/null +++ b/agent/component/exit_loop.py @@ -0,0 +1,32 @@ +# +# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from abc import ABC +from agent.component.base import ComponentBase, ComponentParamBase + + +class ExitLoopParam(ComponentParamBase, ABC): + def check(self): + return True + + +class ExitLoop(ComponentBase, ABC): + component_name = "ExitLoop" + + def _invoke(self, **kwargs): + pass + + def thoughts(self) -> str: + return "" \ No newline at end of file diff --git a/agent/component/loop.py b/agent/component/loop.py new file mode 100644 index 000000000..484dfae82 --- /dev/null +++ b/agent/component/loop.py @@ -0,0 +1,80 @@ +# +# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from abc import ABC +from agent.component.base import ComponentBase, ComponentParamBase + + +class LoopParam(ComponentParamBase): + """ + Define the Loop component parameters. + """ + + def __init__(self): + super().__init__() + self.loop_variables = [] + self.loop_termination_condition=[] + self.maximum_loop_count = 0 + + def get_input_form(self) -> dict[str, dict]: + return { + "items": { + "type": "json", + "name": "Items" + } + } + + def check(self): + return True + + +class Loop(ComponentBase, ABC): + component_name = "Loop" + + def get_start(self): + for cid in self._canvas.components.keys(): + if self._canvas.get_component(cid)["obj"].component_name.lower() != "loopitem": + continue + if self._canvas.get_component(cid)["parent_id"] == self._id: + return cid + + def _invoke(self, **kwargs): + if self.check_if_canceled("Loop processing"): + return + + for item in self._param.loop_variables: + if any([not item.get("variable"), not item.get("input_mode"), not item.get("value"),not item.get("type")]): + assert "Loop Variable is not complete." + if item["input_mode"]=="variable": + self.set_output(item["variable"],self._canvas.get_variable_value(item["value"])) + elif item["input_mode"]=="constant": + self.set_output(item["variable"],item["value"]) + else: + if item["type"] == "number": + self.set_output(item["variable"], 0) + elif item["type"] == "string": + self.set_output(item["variable"], "") + elif item["type"] == "boolean": + self.set_output(item["variable"], False) + elif item["type"].startswith("object"): + self.set_output(item["variable"], {}) + elif item["type"].startswith("array"): + self.set_output(item["variable"], []) + else: + self.set_output(item["variable"], "") + + + def thoughts(self) -> str: + return "Loop from canvas." \ No newline at end of file diff --git a/agent/component/loopitem.py b/agent/component/loopitem.py new file mode 100644 index 000000000..71b91c810 --- /dev/null +++ b/agent/component/loopitem.py @@ -0,0 +1,163 @@ +# +# Copyright 2024 The InfiniFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from abc import ABC +from agent.component.base import ComponentBase, ComponentParamBase + + +class LoopItemParam(ComponentParamBase): + """ + Define the LoopItem component parameters. + """ + def check(self): + return True + +class LoopItem(ComponentBase, ABC): + component_name = "LoopItem" + + def __init__(self, canvas, id, param: ComponentParamBase): + super().__init__(canvas, id, param) + self._idx = 0 + + + def _invoke(self, **kwargs): + if self.check_if_canceled("LoopItem processing"): + return + parent = self.get_parent() + maximum_loop_count = parent._param.maximum_loop_count + if self._idx >= maximum_loop_count: + self._idx = -1 + return + if self._idx > 0: + if self.check_if_canceled("LoopItem processing"): + return + self._idx += 1 + + def evaluate_condition(self,var, operator, value): + if isinstance(var, str): + if operator == "contains": + return value in var + elif operator == "not contains": + return value not in var + elif operator == "start with": + return var.startswith(value) + elif operator == "end with": + return var.endswith(value) + elif operator == "is": + return var == value + elif operator == "is not": + return var != value + elif operator == "empty": + return var == "" + elif operator == "not empty": + return var != "" + + elif isinstance(var, (int, float)): + if operator == "=": + return var == value + elif operator == "≠": + return var != value + elif operator == ">": + return var > value + elif operator == "<": + return var < value + elif operator == "≥": + return var >= value + elif operator == "≤": + return var <= value + elif operator == "empty": + return var is None + elif operator == "not empty": + return var is not None + + elif isinstance(var, bool): + if operator == "is": + return var is value + elif operator == "is not": + return var is not value + elif operator == "empty": + return var is None + elif operator == "not empty": + return var is not None + + elif isinstance(var, dict): + if operator == "empty": + return len(var) == 0 + elif operator == "not empty": + return len(var) > 0 + + elif isinstance(var, list): + if operator == "contains": + return value in var + elif operator == "not contains": + return value not in var + + elif operator == "is": + return var == value + elif operator == "is not": + return var != value + + elif operator == "empty": + return len(var) == 0 + elif operator == "not empty": + return len(var) > 0 + + raise Exception(f"Invalid operator: {operator}") + + def end(self): + if self._idx == -1: + return True + parent = self.get_parent() + logical_operator = parent._param.logical_operator if hasattr(parent._param, "logical_operator") else "and" + conditions = [] + for item in parent._param.loop_termination_condition: + if not item.get("variable") or not item.get("operator"): + raise ValueError("Loop condition is incomplete.") + var = self._canvas.get_variable_value(item["variable"]) + operator = item["operator"] + input_mode = item.get("input_mode", "constant") + + if input_mode == "variable": + value = self._canvas.get_variable_value(item.get("value", "")) + elif input_mode == "constant": + value = item.get("value", "") + else: + raise ValueError("Invalid input mode.") + conditions.append(self.evaluate_condition(var, operator, value)) + should_end = ( + all(conditions) if logical_operator == "and" + else any(conditions) if logical_operator == "or" + else None + ) + if should_end is None: + raise ValueError("Invalid logical operator,should be 'and' or 'or'.") + + if should_end: + self._idx = -1 + return True + + return False + + def next(self): + if self._idx == -1: + self._idx = 0 + else: + self._idx += 1 + if self._idx >= len(self._items): + self._idx = -1 + return False + + def thoughts(self) -> str: + return "Next turn..." \ No newline at end of file diff --git a/deepdoc/parser/pdf_parser.py b/deepdoc/parser/pdf_parser.py index f6613c2f5..a6c370a7e 100644 --- a/deepdoc/parser/pdf_parser.py +++ b/deepdoc/parser/pdf_parser.py @@ -402,7 +402,6 @@ class RAGFlowPdfParser: continue else: score = 0 - print(f"{k=},{score=}",flush=True) if score > best_score: best_score = score best_k = k