From 598a833b756f915a74188739a62410b1cd0398fe Mon Sep 17 00:00:00 2001 From: gustavoschaedler Date: Tue, 18 Jul 2023 18:47:04 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20import=20o?= =?UTF-8?q?rder=20to=20follow=20PEP8=20guidelines=20for=20better=20readabi?= =?UTF-8?q?lity=20and=20maintainability=20=E2=9C=A8=20feat(util.py):=20add?= =?UTF-8?q?=20sync=5Fto=5Fasync=20decorator=20to=20convert=20sync=20functi?= =?UTF-8?q?ons=20to=20async=20functions=20for=20better=20performance=20and?= =?UTF-8?q?=20compatibility=20=F0=9F=90=9B=20fix(util.py):=20fix=20formatt?= =?UTF-8?q?ing=20of=20dictionary=20by=20removing=20certain=20keys=20and=20?= =?UTF-8?q?modifying=20the=20values=20of=20other=20keys=20to=20improve=20c?= =?UTF-8?q?onsistency=20and=20clarity=20=F0=9F=90=9B=20fix(util.py):=20fix?= =?UTF-8?q?=20update=5Fverbose=20function=20to=20recursively=20update=20th?= =?UTF-8?q?e=20value=20of=20the=20'verbose'=20key=20in=20a=20dictionary=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20update=5Fverbose=20functio?= =?UTF-8?q?n=20to=20correctly=20handle=20nested=20dictionaries=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20update=5Fverbose=20functio?= =?UTF-8?q?n=20to=20correctly=20handle=20non-dictionary=20values=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20format=5Fdict=20function?= =?UTF-8?q?=20to=20correctly=20handle=20the=20'type'=20value=20when=20it?= =?UTF-8?q?=20is=20a=20type=20object=20=F0=9F=90=9B=20fix(util.py):=20fix?= =?UTF-8?q?=20format=5Fdict=20function=20to=20correctly=20handle=20the=20'?= =?UTF-8?q?type'=20value=20when=20it=20is=20a=20list=20type=20=F0=9F=90=9B?= =?UTF-8?q?=20fix(util.py):=20fix=20format=5Fdict=20function=20to=20correc?= =?UTF-8?q?tly=20handle=20the=20'type'=20value=20when=20it=20is=20a=20Mapp?= =?UTF-8?q?ing=20type=20=F0=9F=90=9B=20fix(util.py):=20fix=20format=5Fdict?= =?UTF-8?q?=20function=20to=20correctly=20handle=20the=20'type'=20value=20?= =?UTF-8?q?when=20it=20is=20a=20dict=20type=20=F0=9F=90=9B=20fix(util.py):?= =?UTF-8?q?=20fix=20format=5Fdict=20function=20to=20correctly=20handle=20t?= =?UTF-8?q?he=20'type'=20value=20when=20it=20is=20a=20Tool=20type=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20format=5Fdict=20function?= =?UTF-8?q?=20to=20correctly=20handle=20the=20'type'=20value=20when=20it?= =?UTF-8?q?=20is=20an=20int=20type=20=F0=9F=90=9B=20fix(util.py):=20fix=20?= =?UTF-8?q?format=5Fdict=20function=20to=20correctly=20handle=20the=20'sho?= =?UTF-8?q?w'=20value=20based=20on=20field=20requirements=20and=20naming?= =?UTF-8?q?=20conventions=20=F0=9F=90=9B=20fix(util.py):=20fix=20format=5F?= =?UTF-8?q?dict=20function=20to=20correctly=20handle=20the=20'password'=20?= =?UTF-8?q?value=20based=20on=20field=20naming=20conventions=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20format=5Fdict=20function?= =?UTF-8?q?=20to=20correctly=20handle=20the=20'multiline'=20value=20based?= =?UTF-8?q?=20on=20field=20naming=20conventions=20=F0=9F=90=9B=20fix(util.?= =?UTF-8?q?py):=20fix=20format=5Fdict=20function=20to=20correctly=20handle?= =?UTF-8?q?=20the=20'type'=20value=20when=20it=20is=20a=20dict=20type=20fo?= =?UTF-8?q?r=20the=20'dict=5F'=20key=20=F0=9F=90=9B=20fix(util.py):=20fix?= =?UTF-8?q?=20format=5Fdict=20function=20to=20correctly=20handle=20the=20'?= =?UTF-8?q?value'=20field=20when=20it=20is=20a=20default=20value=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(util.py):=20fix=20format=5Fdict=20function?= =?UTF-8?q?=20to=20correctly=20handle=20the=20'value'=20field=20for=20the?= =?UTF-8?q?=20'headers'=20key=20=F0=9F=90=9B=20fix(util.py):=20fix=20forma?= =?UTF-8?q?t=5Fdict=20function=20to=20correctly=20handle=20the=20'options'?= =?UTF-8?q?=20field=20for=20specific=20class=20and=20key=20combinations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/utils/util.py | 318 +++++++++++++++++++---------- src/frontend/package-lock.json | 3 +- 2 files changed, 211 insertions(+), 110 deletions(-) diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index c5db6052e..0ee39b337 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -1,8 +1,8 @@ -import importlib -import inspect import re +import inspect +import importlib from functools import wraps -from typing import Dict, Optional +from typing import Optional, Dict, Any, Union from docstring_parser import parse # type: ignore @@ -214,111 +214,6 @@ def get_default_factory(module: str, function: str): return None -def format_dict(d, name: Optional[str] = None): - """ - Formats a dictionary by removing certain keys and modifying the - values of other keys. - - Args: - d: the dictionary to format - name: the name of the class to format - - Returns: - A new dictionary with the desired modifications applied. - """ - - # Process remaining keys - for key, value in d.items(): - if key == "_type": - continue - - _type = value["type"] - - if not isinstance(_type, str): - _type = _type.__name__ - - # Remove 'Optional' wrapper - if "Optional" in _type: - _type = _type.replace("Optional[", "")[:-1] - - # Check for list type - if "List" in _type or "Sequence" in _type or "Set" in _type: - _type = ( - _type.replace("List[", "") - .replace("Sequence[", "") - .replace("Set[", "")[:-1] - ) - value["list"] = True - else: - value["list"] = False - - # Replace 'Mapping' with 'dict' - if "Mapping" in _type: - _type = _type.replace("Mapping", "dict") - - # Change type from str to Tool - value["type"] = "Tool" if key in ["allowed_tools"] else _type - - value["type"] = "int" if key in ["max_value_length"] else value["type"] - - # Show or not field - value["show"] = bool( - (value["required"] and key not in ["input_variables"]) - or key in FORCE_SHOW_FIELDS - or "api_key" in key - ) - - # Add password field - value["password"] = any( - text in key.lower() for text in ["password", "token", "api", "key"] - ) - - # Add multline - value["multiline"] = key in [ - "suffix", - "prefix", - "template", - "examples", - "code", - "headers", - "format_instructions", - ] - - # Replace dict type with str - if "dict" in value["type"].lower(): - value["type"] = "code" - - if key == "dict_": - value["type"] = "file" - value["suffixes"] = [".json", ".yaml", ".yml"] - value["fileTypes"] = ["json", "yaml", "yml"] - - # Replace default value with actual value - if "default" in value: - value["value"] = value["default"] - value.pop("default") - - if key == "headers": - value[ - "value" - ] = """{'Authorization': - 'Bearer '}""" - # Add options to openai - if name == "OpenAI" and key == "model_name": - value["options"] = constants.OPENAI_MODELS - value["list"] = True - value["value"] = constants.OPENAI_MODELS[0] - elif name == "ChatOpenAI" and key == "model_name": - value["options"] = constants.CHAT_OPENAI_MODELS - value["list"] = True - value["value"] = constants.CHAT_OPENAI_MODELS[0] - elif (name == "Anthropic" or name == "ChatAnthropic") and key == "model_name": - value["options"] = constants.ANTHROPIC_MODELS - value["list"] = True - value["value"] = constants.ANTHROPIC_MODELS[0] - return d - - def update_verbose(d: dict, new_value: bool) -> dict: """ Recursively updates the value of the 'verbose' key in a dictionary. @@ -349,3 +244,210 @@ def sync_to_async(func): return func(*args, **kwargs) return async_wrapper + + +def format_dict( + dictionary: Dict[str, Any], class_name: Optional[str] = None +) -> Dict[str, Any]: + """ + Formats a dictionary by removing certain keys and modifying the + values of other keys. + + Returns: + A new dictionary with the desired modifications applied. + """ + + for key, value in dictionary.items(): + if key == "_type": + continue + + _type: Union[str, type] = get_type(value) + + _type = remove_optional_wrapper(_type) + _type = check_list_type(_type, value) + _type = replace_mapping_with_dict(_type) + + value["type"] = get_formatted_type(key, _type) + value["show"] = should_show_field(value, key) + value["password"] = is_password_field(key) + value["multiline"] = is_multiline_field(key) + + replace_dict_type_with_code(value) + + if key == "dict_": + set_dict_file_attributes(value) + + replace_default_value_with_actual(value) + + if key == "headers": + set_headers_value(value) + + add_options_to_field(value, class_name, key) + + return dictionary + + +def get_type(value: Any) -> Union[str, type]: + """ + Retrieves the type value from the dictionary. + + Returns: + The type value. + """ + _type = value["type"] + + return _type if isinstance(_type, str) else _type.__name__ + + +def remove_optional_wrapper(_type: str) -> str: + """ + Removes the 'Optional' wrapper from the type string. + + Returns: + The type string with the 'Optional' wrapper removed. + """ + if "Optional" in _type: + _type = _type.replace("Optional[", "")[:-1] + + return _type + + +def check_list_type(_type: str, value: Dict[str, Any]) -> str: + """ + Checks if the type is a list type and modifies the value accordingly. + + Returns: + The modified type string. + """ + if any(list_type in _type for list_type in ["List", "Sequence", "Set"]): + _type = ( + _type.replace("List[", "").replace("Sequence[", "").replace("Set[", "")[:-1] + ) + value["list"] = True + else: + value["list"] = False + + return _type + + +def replace_mapping_with_dict(_type: str) -> str: + """ + Replaces 'Mapping' with 'dict' in the type string. + + Returns: + The modified type string. + """ + if "Mapping" in _type: + _type = _type.replace("Mapping", "dict") + + return _type + + +def get_formatted_type(key: str, _type: str) -> str: + """ + Formats the type value based on the given key. + + Returns: + The formatted type value. + """ + if key == "allowed_tools": + return "Tool" + + elif key == "max_value_length": + return "int" + + return _type + + +def should_show_field(value: Dict[str, Any], key: str) -> bool: + """ + Determines if the field should be shown or not. + + Returns: + True if the field should be shown, False otherwise. + """ + return ( + (value["required"] and key != "input_variables") + or key in FORCE_SHOW_FIELDS + or any(text in key.lower() for text in ["password", "token", "api", "key"]) + ) + + +def is_password_field(key: str) -> bool: + """ + Determines if the field is a password field. + + Returns: + True if the field is a password field, False otherwise. + """ + return any(text in key.lower() for text in ["password", "token", "api", "key"]) + + +def is_multiline_field(key: str) -> bool: + """ + Determines if the field is a multiline field. + + Returns: + True if the field is a multiline field, False otherwise. + """ + return key in { + "suffix", + "prefix", + "template", + "examples", + "code", + "headers", + "format_instructions", + } + + +def replace_dict_type_with_code(value: Dict[str, Any]) -> None: + """ + Replaces the type value with 'code' if the type is a dict. + """ + if "dict" in value["type"].lower(): + value["type"] = "code" + + +def set_dict_file_attributes(value: Dict[str, Any]) -> None: + """ + Sets the file attributes for the 'dict_' key. + """ + value["type"] = "file" + value["suffixes"] = [".json", ".yaml", ".yml"] + value["fileTypes"] = ["json", "yaml", "yml"] + + +def replace_default_value_with_actual(value: Dict[str, Any]) -> None: + """ + Replaces the default value with the actual value. + """ + if "default" in value: + value["value"] = value["default"] + value.pop("default") + + +def set_headers_value(value: Dict[str, Any]) -> None: + """ + Sets the value for the 'headers' key. + """ + value["value"] = """{'Authorization': 'Bearer '}""" + + +def add_options_to_field( + value: Dict[str, Any], class_name: Optional[str], key: str +) -> None: + """ + Adds options to the field based on the class name and key. + """ + options_map = { + "OpenAI": constants.OPENAI_MODELS, + "ChatOpenAI": constants.CHAT_OPENAI_MODELS, + "Anthropic": constants.ANTHROPIC_MODELS, + "ChatAnthropic": constants.ANTHROPIC_MODELS, + } + + if class_name in options_map and key == "model_name": + value["options"] = options_map[class_name] + value["list"] = True + value["value"] = options_map[class_name][0] diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 04abc4875..68c508e40 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -3577,7 +3577,7 @@ "version": "16.18.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", - "dev": true + "devOptional": true }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -5623,7 +5623,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [