Skip to content

Commit e135b7f

Browse files
edwinjosechittilappillyCristhianzlogabrielluizautofix-ci[bot]
authored
fix: loop variable not accessible error (#7501)
* tests cases * update to loop * Update component.py * 📝 (LoopTemplate.json): update value of a configuration key from "OPENAI_API_KEY" to "ANTHROPIC_API_KEY" in order to reflect the correct API key being used * update json test loop * fix: update test URL in loop-component.spec.ts to reflect correct reference Changed the URL in the test case from "Artificial_intelligence" to "Human_intelligence" to ensure accurate testing of the loop component functionality. * update FE tests * [autofix.ci] apply automated fixes --------- Co-authored-by: cristhianzl <[email protected]> Co-authored-by: Gabriel Luiz Freitas Almeida <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent b5e33ce commit e135b7f

File tree

9 files changed

+1346
-1205
lines changed

9 files changed

+1346
-1205
lines changed

src/backend/base/langflow/components/logic/loop.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def aggregated_output(self) -> Data:
105105
aggregated = self.ctx.get(f"{self._id}_aggregated", [])
106106

107107
# Check if loop input is provided and append to aggregated list
108-
if self.data is not None and not isinstance(self.data, str) and len(aggregated) <= len(data_list):
109-
aggregated.append(self.data)
108+
if self.item is not None and not isinstance(self.item, str) and len(aggregated) <= len(data_list):
109+
aggregated.append(self.item)
110110
self.update_ctx({f"{self._id}_aggregated": aggregated})
111111
return aggregated

src/backend/base/langflow/custom/custom_component/component.py

+1
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,7 @@ async def _run(self):
720720

721721
def __getattr__(self, name: str) -> Any:
722722
if "_attributes" in self.__dict__ and name in self.__dict__["_attributes"]:
723+
# It is a dict of attributes that are not inputs or outputs all the raw data it should have the loop input.
723724
return self.__dict__["_attributes"][name]
724725
if "_inputs" in self.__dict__ and name in self.__dict__["_inputs"]:
725726
return self.__dict__["_inputs"][name].value

src/backend/base/langflow/graph/vertex/base.py

+2
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ def _set_params_from_normal_edge(self, params: dict, edge: Edge, template_dict:
307307
else:
308308
params[param_key] = self.graph.get_vertex(edge.source_id)
309309
elif param_key in self.output_names:
310+
# if the loop is run the param_key item will be set over here
311+
# validate the edge
310312
params[param_key] = self.graph.get_vertex(edge.source_id)
311313
return params
312314

src/backend/base/langflow/graph/vertex/param_handler.py

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ def _set_params_from_normal_edge(self, params: dict[str, Any], edge: CycleEdge)
7070
params[param_key].append(self.vertex.graph.get_vertex(edge.source_id))
7171
else:
7272
params[param_key] = self.process_non_list_edge_param(field, edge)
73+
elif param_key in self.vertex.output_names:
74+
# If the param_key is in the output_names, it means that the loop is run
75+
# if the loop is run the param_key item will be set over here
76+
# validate the edge
77+
params[param_key] = self.vertex.graph.get_vertex(edge.source_id)
7378
return params
7479

7580
def process_non_list_edge_param(self, field: dict, edge: CycleEdge) -> Any:

src/backend/base/langflow/initial_setup/starter_projects/LoopTemplate.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@
462462
"show": true,
463463
"title_case": false,
464464
"type": "code",
465-
"value": "from langflow.custom import Component\nfrom langflow.io import DataInput, Output\nfrom langflow.schema import Data\n\n\nclass LoopComponent(Component):\n display_name = \"Loop\"\n description = (\n \"Iterates over a list of Data objects, outputting one item at a time and aggregating results from loop inputs.\"\n )\n icon = \"infinity\"\n\n inputs = [\n DataInput(\n name=\"data\",\n display_name=\"Data\",\n info=\"The initial list of Data objects to iterate over.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Item\", name=\"item\", method=\"item_output\", allows_loop=True),\n Output(display_name=\"Done\", name=\"done\", method=\"done_output\"),\n ]\n\n def initialize_data(self) -> None:\n \"\"\"Initialize the data list, context index, and aggregated list.\"\"\"\n if self.ctx.get(f\"{self._id}_initialized\", False):\n return\n\n # Ensure data is a list of Data objects\n data_list = self._validate_data(self.data)\n\n # Store the initial data and context variables\n self.update_ctx(\n {\n f\"{self._id}_data\": data_list,\n f\"{self._id}_index\": 0,\n f\"{self._id}_aggregated\": [],\n f\"{self._id}_initialized\": True,\n }\n )\n\n def _validate_data(self, data):\n \"\"\"Validate and return a list of Data objects.\"\"\"\n if isinstance(data, Data):\n return [data]\n if isinstance(data, list) and all(isinstance(item, Data) for item in data):\n return data\n msg = \"The 'data' input must be a list of Data objects or a single Data object.\"\n raise TypeError(msg)\n\n def evaluate_stop_loop(self) -> bool:\n \"\"\"Evaluate whether to stop item or done output.\"\"\"\n current_index = self.ctx.get(f\"{self._id}_index\", 0)\n data_length = len(self.ctx.get(f\"{self._id}_data\", []))\n return current_index > data_length\n\n def item_output(self) -> Data:\n \"\"\"Output the next item in the list or stop if done.\"\"\"\n self.initialize_data()\n current_item = Data(text=\"\")\n\n if self.evaluate_stop_loop():\n self.stop(\"item\")\n return Data(text=\"\")\n\n # Get data list and current index\n data_list, current_index = self.loop_variables()\n if current_index < len(data_list):\n # Output current item and increment index\n try:\n current_item = data_list[current_index]\n except IndexError:\n current_item = Data(text=\"\")\n self.aggregated_output()\n self.update_ctx({f\"{self._id}_index\": current_index + 1})\n return current_item\n\n def done_output(self) -> Data:\n \"\"\"Trigger the done output when iteration is complete.\"\"\"\n self.initialize_data()\n\n if self.evaluate_stop_loop():\n self.stop(\"item\")\n self.start(\"done\")\n\n return self.ctx.get(f\"{self._id}_aggregated\", [])\n self.stop(\"done\")\n return Data(text=\"\")\n\n def loop_variables(self):\n \"\"\"Retrieve loop variables from context.\"\"\"\n return (\n self.ctx.get(f\"{self._id}_data\", []),\n self.ctx.get(f\"{self._id}_index\", 0),\n )\n\n def aggregated_output(self) -> Data:\n \"\"\"Return the aggregated list once all items are processed.\"\"\"\n self.initialize_data()\n\n # Get data list and aggregated list\n data_list = self.ctx.get(f\"{self._id}_data\", [])\n aggregated = self.ctx.get(f\"{self._id}_aggregated\", [])\n\n # Check if loop input is provided and append to aggregated list\n if self.data is not None and not isinstance(self.data, str) and len(aggregated) <= len(data_list):\n aggregated.append(self.data)\n self.update_ctx({f\"{self._id}_aggregated\": aggregated})\n return aggregated\n"
465+
"value": "from langflow.custom import Component\nfrom langflow.io import DataInput, Output\nfrom langflow.schema import Data\n\n\nclass LoopComponent(Component):\n display_name = \"Loop\"\n description = (\n \"Iterates over a list of Data objects, outputting one item at a time and aggregating results from loop inputs.\"\n )\n icon = \"infinity\"\n\n inputs = [\n DataInput(\n name=\"data\",\n display_name=\"Data\",\n info=\"The initial list of Data objects to iterate over.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Item\", name=\"item\", method=\"item_output\", allows_loop=True),\n Output(display_name=\"Done\", name=\"done\", method=\"done_output\"),\n ]\n\n def initialize_data(self) -> None:\n \"\"\"Initialize the data list, context index, and aggregated list.\"\"\"\n if self.ctx.get(f\"{self._id}_initialized\", False):\n return\n\n # Ensure data is a list of Data objects\n data_list = self._validate_data(self.data)\n\n # Store the initial data and context variables\n self.update_ctx(\n {\n f\"{self._id}_data\": data_list,\n f\"{self._id}_index\": 0,\n f\"{self._id}_aggregated\": [],\n f\"{self._id}_initialized\": True,\n }\n )\n\n def _validate_data(self, data):\n \"\"\"Validate and return a list of Data objects.\"\"\"\n if isinstance(data, Data):\n return [data]\n if isinstance(data, list) and all(isinstance(item, Data) for item in data):\n return data\n msg = \"The 'data' input must be a list of Data objects or a single Data object.\"\n raise TypeError(msg)\n\n def evaluate_stop_loop(self) -> bool:\n \"\"\"Evaluate whether to stop item or done output.\"\"\"\n current_index = self.ctx.get(f\"{self._id}_index\", 0)\n data_length = len(self.ctx.get(f\"{self._id}_data\", []))\n return current_index > data_length\n\n def item_output(self) -> Data:\n \"\"\"Output the next item in the list or stop if done.\"\"\"\n self.initialize_data()\n current_item = Data(text=\"\")\n\n if self.evaluate_stop_loop():\n self.stop(\"item\")\n return Data(text=\"\")\n\n # Get data list and current index\n data_list, current_index = self.loop_variables()\n if current_index < len(data_list):\n # Output current item and increment index\n try:\n current_item = data_list[current_index]\n except IndexError:\n current_item = Data(text=\"\")\n self.aggregated_output()\n self.update_ctx({f\"{self._id}_index\": current_index + 1})\n return current_item\n\n def done_output(self) -> Data:\n \"\"\"Trigger the done output when iteration is complete.\"\"\"\n self.initialize_data()\n\n if self.evaluate_stop_loop():\n self.stop(\"item\")\n self.start(\"done\")\n\n return self.ctx.get(f\"{self._id}_aggregated\", [])\n self.stop(\"done\")\n return Data(text=\"\")\n\n def loop_variables(self):\n \"\"\"Retrieve loop variables from context.\"\"\"\n return (\n self.ctx.get(f\"{self._id}_data\", []),\n self.ctx.get(f\"{self._id}_index\", 0),\n )\n\n def aggregated_output(self) -> Data:\n \"\"\"Return the aggregated list once all items are processed.\"\"\"\n self.initialize_data()\n\n # Get data list and aggregated list\n data_list = self.ctx.get(f\"{self._id}_data\", [])\n aggregated = self.ctx.get(f\"{self._id}_aggregated\", [])\n\n # Check if loop input is provided and append to aggregated list\n if self.item is not None and not isinstance(self.item, str) and len(aggregated) <= len(data_list):\n aggregated.append(self.item)\n self.update_ctx({f\"{self._id}_aggregated\": aggregated})\n return aggregated\n"
466466
},
467467
"data": {
468468
"_input_type": "DataInput",
@@ -595,7 +595,7 @@
595595
"show": true,
596596
"title_case": false,
597597
"type": "str",
598-
"value": "OPENAI_API_KEY"
598+
"value": "ANTHROPIC_API_KEY"
599599
},
600600
"base_url": {
601601
"_input_type": "MessageTextInput",

0 commit comments

Comments
 (0)