Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions swarms/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -50,6 +49,5 @@
"LiteLLMException",
"exists",
"format_data_structure",
"format_dict_to_string",
"initialize_logger",
]
60 changes: 17 additions & 43 deletions swarms/utils/any_to_str.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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():
Expand All @@ -75,7 +49,7 @@ def any_to_str(data: Union[str, Dict, List, Tuple, Any]) -> str:
# }
# )
# )

#
# print("\nNested Dictionary:")
# print(
# any_to_str(
Expand All @@ -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()
86 changes: 45 additions & 41 deletions swarms/utils/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -145,11 +120,40 @@ 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:
lines.append(f"{indent} {attr}: {value}")
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)}"
115 changes: 115 additions & 0 deletions tests/utils/test_format_data_structure.py
Original file line number Diff line number Diff line change
@@ -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}"