feat: Enhance ConditionalRouterComponent with Regex Matching and UI Improvements (#5217)

* fix: revert response return type to Message for compatibility

* Change the max_iterations and case_sensitive advanced options

* feat: add regex matching and real-time refresh functionality

* Refactor evaluate_condition method to use elif statements for clarity

- Changed if statements to elif in the evaluate_condition method to improve readability and indicate mutually exclusive conditions.

- Removed redundant advanced=True line from BoolInput for case sensitivity.

* [autofix.ci] apply automated fixes

* Format code using make format

- Applied code formatting to ensure consistency and adherence to style guidelines.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Eric Hare <ericrhare@gmail.com>
This commit is contained in:
VICTOR CORREA GOMES 2024-12-16 15:13:25 -03:00 • committed by GitHub
commit ef6226d25d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,3 +1,5 @@
import re
from langflow.custom import Component from langflow.custom import Component
from langflow.io import BoolInput, DropdownInput, IntInput, MessageInput, MessageTextInput, Output from langflow.io import BoolInput, DropdownInput, IntInput, MessageInput, MessageTextInput, Output
from langflow.schema.message import Message from langflow.schema.message import Message
@ -27,16 +29,16 @@ class ConditionalRouterComponent(Component):
DropdownInput( DropdownInput(
name="operator", name="operator",
display_name="Operator", display_name="Operator",
options=["equals", "not equals", "contains", "starts with", "ends with"], options=["equals", "not equals", "contains", "starts with", "ends with", "matches regex"],
info="The operator to apply for comparing the texts.", info="The operator to apply for comparing the texts.",
value="equals", value="equals",
real_time_refresh=True,
), ),
BoolInput( BoolInput(
name="case_sensitive", name="case_sensitive",
display_name="Case Sensitive", display_name="Case Sensitive",
info="If true, the comparison will be case sensitive.", info="If true, the comparison will be case sensitive.",
value=False, value=False,
advanced=True,
), ),
MessageInput( MessageInput(
name="message", name="message",
@ -49,6 +51,7 @@ class ConditionalRouterComponent(Component):
display_name="Max Iterations", display_name="Max Iterations",
info="The maximum number of iterations for the conditional router.", info="The maximum number of iterations for the conditional router.",
value=10, value=10,
advanced=True,
), ),
DropdownInput( DropdownInput(
name="default_route", name="default_route",
@ -69,7 +72,7 @@ class ConditionalRouterComponent(Component):
self.__iteration_updated = False self.__iteration_updated = False
def evaluate_condition(self, input_text: str, match_text: str, operator: str, *, case_sensitive: bool) -> bool: def evaluate_condition(self, input_text: str, match_text: str, operator: str, *, case_sensitive: bool) -> bool:
if not case_sensitive: if not case_sensitive and operator != "matches regex":
input_text = input_text.lower() input_text = input_text.lower()
match_text = match_text.lower() match_text = match_text.lower()
@ -83,6 +86,11 @@ class ConditionalRouterComponent(Component):
return input_text.startswith(match_text) return input_text.startswith(match_text)
if operator == "ends with": if operator == "ends with":
return input_text.endswith(match_text) return input_text.endswith(match_text)
if operator == "matches regex":
try:
return bool(re.match(match_text, input_text))
except re.error:
return False # Return False if the regex is invalid
return False return False
def iterate_and_stop_once(self, route_to_stop: str): def iterate_and_stop_once(self, route_to_stop: str):
@ -90,11 +98,10 @@ class ConditionalRouterComponent(Component):
self.update_ctx({f"{self._id}_iteration": self.ctx.get(f"{self._id}_iteration", 0) + 1}) self.update_ctx({f"{self._id}_iteration": self.ctx.get(f"{self._id}_iteration", 0) + 1})
self.__iteration_updated = True self.__iteration_updated = True
if self.ctx.get(f"{self._id}_iteration", 0) >= self.max_iterations and route_to_stop == self.default_route: if self.ctx.get(f"{self._id}_iteration", 0) >= self.max_iterations and route_to_stop == self.default_route:
# We need to stop the other route
route_to_stop = "true_result" if route_to_stop == "false_result" else "false_result" route_to_stop = "true_result" if route_to_stop == "false_result" else "false_result"
self.stop(route_to_stop) self.stop(route_to_stop)
def true_response(self) -> Message | str: def true_response(self) -> Message:
result = self.evaluate_condition( result = self.evaluate_condition(
self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive
) )
@ -103,9 +110,9 @@ class ConditionalRouterComponent(Component):
self.iterate_and_stop_once("false_result") self.iterate_and_stop_once("false_result")
return self.message return self.message
self.iterate_and_stop_once("true_result") self.iterate_and_stop_once("true_result")
return "" return Message(content="")
def false_response(self) -> Message | str: def false_response(self) -> Message:
result = self.evaluate_condition( result = self.evaluate_condition(
self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive
) )
@ -114,4 +121,18 @@ class ConditionalRouterComponent(Component):
self.iterate_and_stop_once("true_result") self.iterate_and_stop_once("true_result")
return self.message return self.message
self.iterate_and_stop_once("false_result") self.iterate_and_stop_once("false_result")
return "" return Message(content="")
def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:
if field_name == "operator":
if field_value == "matches regex":
if "case_sensitive" in build_config:
del build_config["case_sensitive"]
# Ensure case_sensitive is present for all other operators
elif "case_sensitive" not in build_config:
case_sensitive_input = next(
(input_field for input_field in self.inputs if input_field.name == "case_sensitive"), None
)
if case_sensitive_input:
build_config["case_sensitive"] = case_sensitive_input.to_dict()
return build_config