diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 450801580..85ae306aa 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -19,7 +19,6 @@ from swarms.utils.index import ( exists, format_data_structure, - format_dict_to_string, ) from swarms.utils.litellm_tokenizer import count_tokens from swarms.utils.litellm_wrapper import ( @@ -50,6 +49,5 @@ "LiteLLMException", "exists", "format_data_structure", - "format_dict_to_string", "initialize_logger", ] diff --git a/swarms/utils/any_to_str.py b/swarms/utils/any_to_str.py index 2b0e38095..641456a79 100644 --- a/swarms/utils/any_to_str.py +++ b/swarms/utils/any_to_str.py @@ -4,8 +4,9 @@ def any_to_str(data: Union[str, Dict, List, Tuple, Any]) -> str: """Convert any input data type to a nicely formatted string. - This function handles conversion of various Python data types into a clean string representation. - It recursively processes nested data structures and handles None values gracefully. + This is a thin alias for ``format_data_structure(data, style="compact")``. + It recursively processes nested data structures and handles None values + gracefully. Args: data: Input data of any type to convert to string. Can be: @@ -17,50 +18,23 @@ def any_to_str(data: Union[str, Dict, List, Tuple, Any]) -> str: Returns: str: A formatted string representation of the input data. - - Dictionaries are formatted as "key: value" pairs separated by commas - - Lists/tuples are comma-separated - - None returns empty string + - Dictionaries are formatted as "key: value" pairs separated by newlines + - Lists/tuples are bracket-enclosed and comma-separated + - None returns the string "None" + - Strings are wrapped in double quotes - Other types are converted using str() Examples: >>> any_to_str({'a': 1, 'b': 2}) - 'a: 1, b: 2' + 'a: 1\\nb: 2' >>> any_to_str([1, 2, 3]) - '1, 2, 3' + '["1", "2", "3"]' >>> any_to_str(None) - '' + 'None' """ - try: - if isinstance(data, dict): - # Format dictionary with newlines and indentation - items = [] - for k, v in data.items(): - value = any_to_str(v) - items.append(f"{k}: {value}") - return "\n".join(items) + from swarms.utils.index import format_data_structure - elif isinstance(data, (list, tuple)): - # Format sequences with brackets and proper spacing - items = [any_to_str(x) for x in data] - if len(items) == 0: - return "[]" if isinstance(data, list) else "()" - return ( - f"[{', '.join(items)}]" - if isinstance(data, list) - else f"({', '.join(items)})" - ) - - elif data is None: - return "None" - - else: - # Handle strings and other types - if isinstance(data, str): - return f'"{data}"' - return str(data) - - except Exception as e: - return f"Error converting data: {str(e)}" + return format_data_structure(data, style="compact") # def main(): @@ -75,7 +49,7 @@ def any_to_str(data: Union[str, Dict, List, Tuple, Any]) -> str: # } # ) # ) - +# # print("\nNested Dictionary:") # print( # any_to_str( @@ -88,15 +62,15 @@ def any_to_str(data: Union[str, Dict, List, Tuple, Any]) -> str: # } # ) # ) - +# # print("\nList and Tuple:") # print(any_to_str([1, "text", None, (1, 2)])) # print(any_to_str((True, False, None))) - +# # print("\nEmpty Collections:") # print(any_to_str([])) # print(any_to_str({})) - - +# +# # if __name__ == "__main__": # main() diff --git a/swarms/utils/index.py b/swarms/utils/index.py index fcd48a7e7..3a8de938c 100644 --- a/swarms/utils/index.py +++ b/swarms/utils/index.py @@ -10,53 +10,28 @@ def exists(val): return val is not None -def format_dict_to_string(data: dict, indent_level=0, use_colon=True): - """ - Recursively format a dictionary into a multi-line string. - - Args: - data (dict): The dictionary to format. - indent_level (int, optional): The current indentation level for nested structures. - use_colon (bool, optional): If True, use "key: value" formatting; - if False, use "key value" formatting. - - Returns: - str: Multi-line readable string representing the structure of the input dictionary. - """ - if not isinstance(data, dict): - return str(data) - - lines = [] - indent = " " * indent_level - separator = ": " if use_colon else " " - - for key, value in data.items(): - if isinstance(value, dict): - lines.append(f"{indent}{key}:") - nested_string = format_dict_to_string( - value, indent_level + 1, use_colon - ) - lines.append(nested_string) - else: - lines.append(f"{indent}{key}{separator}{value}") - - return "\n".join(lines) - - def format_data_structure( - data: any, indent_level: int = 0, max_depth: int = 10 + data: any, + indent_level: int = 0, + max_depth: int = 10, + style: str = "indented", ) -> str: """ - Format any Python data structure into a readable, indented, multi-line string. + Format any Python data structure into a readable, multi-line string. Args: data: The data structure to format. indent_level (int, optional): The current indentation level. Default is 0. max_depth (int, optional): The maximum depth to recurse. Defaults to 10. + style (str, optional): Output style. "indented" (default) for indented + multi-line format, "compact" for a flat format matching any_to_str. Returns: - str: Readable multi-line string representation of the input structure. + str: Readable string representation of the input structure. """ + if style == "compact": + return _compact_format(data, max_depth) + if indent_level >= max_depth: return f"{' ' * indent_level}... (max depth reached)" @@ -72,7 +47,7 @@ def format_data_structure( lines.append(f"{indent}{key}:") lines.append( format_data_structure( - value, indent_level + 1, max_depth + value, indent_level + 1, max_depth, style ) ) else: @@ -88,7 +63,7 @@ def format_data_structure( lines.append(f"{indent}[{i}]:") lines.append( format_data_structure( - item, indent_level + 1, max_depth + item, indent_level + 1, max_depth, style ) ) else: @@ -104,7 +79,7 @@ def format_data_structure( lines.append(f"{indent}({i}):") lines.append( format_data_structure( - item, indent_level + 1, max_depth + item, indent_level + 1, max_depth, style ) ) else: @@ -120,7 +95,7 @@ def format_data_structure( lines.append(f"{indent}set item:") lines.append( format_data_structure( - item, indent_level + 1, max_depth + item, indent_level + 1, max_depth, style ) ) else: @@ -145,7 +120,7 @@ def format_data_structure( lines.append(f"{indent} {attr}:") lines.append( format_data_structure( - value, indent_level + 2, max_depth + value, indent_level + 2, max_depth, style ) ) else: @@ -153,3 +128,32 @@ def format_data_structure( return "\n".join(lines) else: return f"{indent}{data} ({data_type.__name__})" + + +def _compact_format(data: any, max_depth: int = 10) -> str: + try: + if isinstance(data, dict): + items = [] + for k, v in data.items(): + value = _compact_format(v, max_depth) + items.append(f"{k}: {value}") + return "\n".join(items) + + elif isinstance(data, (list, tuple)): + items = [_compact_format(x, max_depth) for x in data] + if len(items) == 0: + return "[]" if isinstance(data, list) else "()" + if isinstance(data, list): + return f"[{', '.join(items)}]" + return f"({', '.join(items)})" + + elif data is None: + return "None" + + else: + if isinstance(data, str): + return f'"{data}"' + return str(data) + + except Exception as e: + return f"Error converting data: {str(e)}" diff --git a/tests/utils/test_format_data_structure.py b/tests/utils/test_format_data_structure.py new file mode 100644 index 000000000..cfa360901 --- /dev/null +++ b/tests/utils/test_format_data_structure.py @@ -0,0 +1,115 @@ +import pytest +from swarms.utils.index import format_data_structure + + +class TestFormatDataStructure: + """Table tests for format_data_structure with both style modes.""" + + @pytest.mark.parametrize( + "data,style,expected_lines", + [ + ( + {"a": 1, "b": 2}, + "indented", + ["a: 1", "b: 2"], + ), + ( + {"a": 1, "b": 2}, + "compact", + ["a: 1", "b: 2"], + ), + ( + [1, 2, 3], + "indented", + ["1", "2", "3"], + ), + ( + [1, 2, 3], + "compact", + ["[1, 2, 3]"], + ), + ( + None, + "compact", + ["None"], + ), + ( + None, + "indented", + ["None"], + ), + ( + "hello", + "compact", + ['"hello"'], + ), + ( + "hello", + "indented", + ["hello"], + ), + ( + 42, + "compact", + ["42"], + ), + ( + [], + "compact", + ["[]"], + ), + ( + [], + "indented", + ["[] (empty list)"], + ), + ( + {}, + "indented", + ["{} (empty dict)"], + ), + ( + (True, False), + "compact", + ["(True, False)"], + ), + ( + {"user": {"id": 123, "active": True}, "data": [1, 2, 3]}, + "compact", + ["user:", "id: 123", "active: True", "data: [1, 2, 3]"], + ), + ( + {"user": {"id": 123, "active": True}, "data": [1, 2, 3]}, + "indented", + ["user:", "id: 123", "active: True", "data:", "1", "2", "3"], + ), + ], + ) + def test_parametrized(self, data, style, expected_lines): + result = format_data_structure(data, style=style) + for line in expected_lines: + assert line in result, f"Expected {line!r} in {result!r}" + + +class TestAnyToStrAlias: + """Verify any_to_str produces the same output as compact style.""" + + def test_any_to_str_matches_compact(self): + from swarms.utils.any_to_str import any_to_str + + fixtures = [ + {"a": 1, "b": 2}, + [1, 2, 3], + None, + "hello", + 42, + [], + {}, + (True, False, None), + [1, "text", None, 2.5], + {"user": {"id": 123, "details": {"city": "New York"}}}, + ] + for data in fixtures: + a = any_to_str(data) + b = format_data_structure(data, style="compact") + assert a == b, f"Mismatch for {data!r}: {a!r} != {b!r}"