lionagi 0.16.1__py3-none-any.whl → 0.16.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. lionagi/adapters/_utils.py +0 -14
  2. lionagi/libs/file/save.py +8 -1
  3. lionagi/ln/__init__.py +10 -0
  4. lionagi/ln/_json_dump.py +322 -49
  5. lionagi/ln/fuzzy/__init__.py +4 -1
  6. lionagi/ln/fuzzy/_fuzzy_validate.py +109 -0
  7. lionagi/ln/fuzzy/_to_dict.py +388 -0
  8. lionagi/models/__init__.py +0 -2
  9. lionagi/operations/brainstorm/brainstorm.py +10 -10
  10. lionagi/operations/communicate/communicate.py +1 -1
  11. lionagi/operations/parse/parse.py +1 -1
  12. lionagi/protocols/generic/element.py +5 -14
  13. lionagi/protocols/generic/log.py +2 -2
  14. lionagi/protocols/generic/pile.py +1 -1
  15. lionagi/protocols/messages/message.py +8 -1
  16. lionagi/protocols/operatives/operative.py +2 -2
  17. lionagi/service/connections/endpoint.py +7 -0
  18. lionagi/service/connections/match_endpoint.py +2 -10
  19. lionagi/service/connections/providers/types.py +1 -3
  20. lionagi/service/hooks/hook_event.py +1 -1
  21. lionagi/service/hooks/hook_registry.py +1 -1
  22. lionagi/service/rate_limited_processor.py +1 -1
  23. lionagi/session/branch.py +1 -101
  24. lionagi/session/session.py +9 -14
  25. lionagi/utils.py +3 -334
  26. lionagi/version.py +1 -1
  27. {lionagi-0.16.1.dist-info → lionagi-0.16.3.dist-info}/METADATA +3 -13
  28. {lionagi-0.16.1.dist-info → lionagi-0.16.3.dist-info}/RECORD +30 -78
  29. lionagi/adapters/postgres_model_adapter.py +0 -131
  30. lionagi/libs/concurrency.py +0 -1
  31. lionagi/libs/file/params.py +0 -175
  32. lionagi/libs/nested/__init__.py +0 -3
  33. lionagi/libs/nested/flatten.py +0 -172
  34. lionagi/libs/nested/nfilter.py +0 -59
  35. lionagi/libs/nested/nget.py +0 -45
  36. lionagi/libs/nested/ninsert.py +0 -104
  37. lionagi/libs/nested/nmerge.py +0 -158
  38. lionagi/libs/nested/npop.py +0 -69
  39. lionagi/libs/nested/nset.py +0 -94
  40. lionagi/libs/nested/unflatten.py +0 -83
  41. lionagi/libs/nested/utils.py +0 -189
  42. lionagi/libs/parse.py +0 -31
  43. lionagi/libs/schema/json_schema.py +0 -231
  44. lionagi/libs/token_transform/__init__.py +0 -0
  45. lionagi/libs/token_transform/base.py +0 -54
  46. lionagi/libs/token_transform/llmlingua.py +0 -1
  47. lionagi/libs/token_transform/perplexity.py +0 -450
  48. lionagi/libs/token_transform/symbolic_compress_context.py +0 -152
  49. lionagi/libs/token_transform/synthlang.py +0 -9
  50. lionagi/libs/token_transform/synthlang_/base.py +0 -128
  51. lionagi/libs/token_transform/synthlang_/resources/frameworks/abstract_algebra.toml +0 -11
  52. lionagi/libs/token_transform/synthlang_/resources/frameworks/category_theory.toml +0 -11
  53. lionagi/libs/token_transform/synthlang_/resources/frameworks/complex_analysis.toml +0 -11
  54. lionagi/libs/token_transform/synthlang_/resources/frameworks/framework_options.json +0 -52
  55. lionagi/libs/token_transform/synthlang_/resources/frameworks/group_theory.toml +0 -11
  56. lionagi/libs/token_transform/synthlang_/resources/frameworks/math_logic.toml +0 -11
  57. lionagi/libs/token_transform/synthlang_/resources/frameworks/reflective_patterns.toml +0 -11
  58. lionagi/libs/token_transform/synthlang_/resources/frameworks/set_theory.toml +0 -11
  59. lionagi/libs/token_transform/synthlang_/resources/frameworks/topology_fundamentals.toml +0 -11
  60. lionagi/libs/token_transform/synthlang_/resources/mapping/lion_emoji_mapping.toml +0 -61
  61. lionagi/libs/token_transform/synthlang_/resources/mapping/python_math_mapping.toml +0 -41
  62. lionagi/libs/token_transform/synthlang_/resources/mapping/rust_chinese_mapping.toml +0 -60
  63. lionagi/libs/token_transform/synthlang_/resources/utility/base_synthlang_system_prompt.toml +0 -11
  64. lionagi/libs/token_transform/synthlang_/translate_to_synthlang.py +0 -140
  65. lionagi/libs/token_transform/types.py +0 -15
  66. lionagi/libs/unstructured/__init__.py +0 -0
  67. lionagi/libs/unstructured/pdf_to_image.py +0 -45
  68. lionagi/libs/unstructured/read_image_to_base64.py +0 -33
  69. lionagi/libs/validate/fuzzy_match_keys.py +0 -7
  70. lionagi/libs/validate/fuzzy_validate_mapping.py +0 -144
  71. lionagi/libs/validate/string_similarity.py +0 -7
  72. lionagi/libs/validate/xml_parser.py +0 -203
  73. lionagi/models/note.py +0 -383
  74. lionagi/operations/translate/__init__.py +0 -0
  75. lionagi/operations/translate/translate.py +0 -47
  76. lionagi/service/connections/providers/claude_code_.py +0 -294
  77. lionagi/tools/memory/tools.py +0 -495
  78. {lionagi-0.16.1.dist-info → lionagi-0.16.3.dist-info}/WHEEL +0 -0
  79. {lionagi-0.16.1.dist-info → lionagi-0.16.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,172 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from collections import deque
6
- from collections.abc import Mapping, Sequence
7
- from typing import Any, Literal, TypeVar, overload
8
-
9
- T = TypeVar("T")
10
-
11
-
12
- @overload
13
- def flatten(
14
- nested_structure: T,
15
- /,
16
- *,
17
- parent_key: tuple = (),
18
- sep: str = "|",
19
- coerce_keys: Literal[True] = True,
20
- dynamic: bool = True,
21
- coerce_sequence: Literal["dict", None] = None,
22
- max_depth: int | None = None,
23
- ) -> dict[str, Any] | None: ...
24
-
25
-
26
- @overload
27
- def flatten(
28
- nested_structure: T,
29
- /,
30
- *,
31
- parent_key: tuple = (),
32
- sep: str = "|",
33
- coerce_keys: Literal[False],
34
- dynamic: bool = True,
35
- coerce_sequence: Literal["dict", "list", None] = None,
36
- max_depth: int | None = None,
37
- ) -> dict[tuple, Any] | None: ...
38
-
39
-
40
- def flatten(
41
- nested_structure: Any,
42
- /,
43
- *,
44
- parent_key: tuple = (),
45
- sep: str = "|",
46
- coerce_keys: bool = True,
47
- dynamic: bool = True,
48
- coerce_sequence: Literal["dict", "list"] | None = None,
49
- max_depth: int | None = None,
50
- ) -> dict[tuple | str, Any] | None:
51
- """Flatten a nested structure into a single-level dictionary.
52
-
53
- Recursively traverses the input, creating keys that represent the path
54
- to each value in the flattened result.
55
-
56
- Args:
57
- nested_structure: The nested structure to flatten.
58
- parent_key: Base key for the current recursion level. Default: ().
59
- sep: Separator for joining keys. Default: "|".
60
- coerce_keys: Join keys into strings if True, keep as tuples if False.
61
- Default: True.
62
- dynamic: Handle sequences (except strings) dynamically if True.
63
- Default: True.
64
- coerce_sequence: Force sequences to be treated as dicts or lists.
65
- Options: "dict", "list", or None. Default: None.
66
- max_depth: Maximum depth to flatten. None for complete flattening.
67
- Default: None.
68
-
69
- Returns:
70
- A flattened dictionary with keys as tuples or strings (based on
71
- coerce_keys) representing the path to each value.
72
-
73
- Raises:
74
- ValueError: If coerce_sequence is "list" and coerce_keys is True.
75
-
76
- Example:
77
- >>> nested = {"a": 1, "b": {"c": 2, "d": [3, 4]}}
78
- >>> flatten(nested)
79
- {'a': 1, 'b|c': 2, 'b|d|0': 3, 'b|d|1': 4}
80
-
81
- Note:
82
- - Preserves order of keys in dicts and indices in sequences.
83
- - With dynamic=True, treats sequences (except strings) as nestable.
84
- - coerce_sequence allows forcing sequence handling for homogeneity.
85
- """
86
-
87
- if coerce_keys and coerce_sequence == "list":
88
- raise ValueError(
89
- "coerce_sequence cannot be 'list' when coerce_keys is True"
90
- )
91
-
92
- coerce_sequence_to_list = None
93
- coerce_sequence_to_dict = None
94
-
95
- if dynamic and coerce_sequence:
96
- if coerce_sequence == "dict":
97
- coerce_sequence_to_dict = True
98
- elif coerce_sequence == "list":
99
- coerce_sequence_to_list = True
100
-
101
- return _flatten_iterative(
102
- obj=nested_structure,
103
- parent_key=parent_key,
104
- sep=sep,
105
- coerce_keys=coerce_keys,
106
- dynamic=dynamic,
107
- coerce_sequence_to_list=coerce_sequence_to_list,
108
- coerce_sequence_to_dict=coerce_sequence_to_dict,
109
- max_depth=max_depth,
110
- )
111
-
112
-
113
- def _flatten_iterative(
114
- obj: Any,
115
- parent_key: tuple,
116
- sep: str,
117
- coerce_keys: bool,
118
- dynamic: bool,
119
- coerce_sequence_to_list: bool = False,
120
- coerce_sequence_to_dict: bool = False,
121
- max_depth: int | None = None,
122
- ) -> dict[tuple | str, Any]:
123
- stack = deque([(obj, parent_key, 0)])
124
- result = {}
125
-
126
- while stack:
127
- current_obj, current_key, depth = stack.pop()
128
-
129
- if max_depth is not None and depth >= max_depth:
130
- result[_format_key(current_key, sep, coerce_keys)] = current_obj
131
- continue
132
-
133
- if isinstance(current_obj, Mapping):
134
- for k, v in current_obj.items():
135
- new_key = current_key + (k,)
136
- if (
137
- v
138
- and isinstance(v, (Mapping, Sequence))
139
- and not isinstance(v, (str, bytes, bytearray))
140
- ):
141
- stack.appendleft((v, new_key, depth + 1))
142
- else:
143
- result[_format_key(new_key, sep, coerce_keys)] = v
144
-
145
- elif (
146
- dynamic
147
- and isinstance(current_obj, Sequence)
148
- and not isinstance(current_obj, (str, bytes, bytearray))
149
- ):
150
- if coerce_sequence_to_dict:
151
- dict_obj = {str(i): v for i, v in enumerate(current_obj)}
152
- for k, v in dict_obj.items():
153
- new_key = current_key + (k,)
154
- stack.appendleft((v, new_key, depth + 1))
155
- elif coerce_sequence_to_list:
156
- for i, v in enumerate(current_obj):
157
- new_key = current_key + (i,)
158
- stack.appendleft((v, new_key, depth + 1))
159
- else:
160
- for i, v in enumerate(current_obj):
161
- new_key = current_key + (str(i),)
162
- stack.appendleft((v, new_key, depth + 1))
163
- else:
164
- result[_format_key(current_key, sep, coerce_keys)] = current_obj
165
-
166
- return result
167
-
168
-
169
- def _format_key(key: tuple, sep: str, coerce_keys: bool, /) -> tuple | str:
170
- if not key:
171
- return key
172
- return sep.join(map(str, key)) if coerce_keys else key
@@ -1,59 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from collections.abc import Callable
6
- from typing import Any
7
-
8
-
9
- def nfilter(
10
- nested_structure: dict[Any, Any] | list[Any],
11
- /,
12
- condition: Callable[[Any], bool],
13
- ) -> dict[Any, Any] | list[Any]:
14
- """Filter elements in a nested structure based on a condition.
15
-
16
- Args:
17
- nested_structure: The nested structure (dict or list) to filter.
18
- condition: Function returning True for elements to keep, False to
19
- discard.
20
-
21
- Returns:
22
- The filtered nested structure.
23
-
24
- Raises:
25
- TypeError: If nested_structure is not a dict or list.
26
-
27
- Example:
28
- >>> data = {"a": 1, "b": {"c": 2, "d": 3}, "e": [4, 5, 6]}
29
- >>> nfilter(data, lambda x: isinstance(x, int) and x > 2)
30
- {'b': {'d': 3}, 'e': [4, 5, 6]}
31
- """
32
- if isinstance(nested_structure, dict):
33
- return _filter_dict(nested_structure, condition)
34
- elif isinstance(nested_structure, list):
35
- return _filter_list(nested_structure, condition)
36
- else:
37
- raise TypeError(
38
- "The nested_structure must be either a dict or a list."
39
- )
40
-
41
-
42
- def _filter_dict(
43
- dictionary: dict[Any, Any], condition: Callable[[tuple[Any, Any]], bool]
44
- ) -> dict[Any, Any]:
45
- return {
46
- k: nfilter(v, condition) if isinstance(v, dict | list) else v
47
- for k, v in dictionary.items()
48
- if condition(v) or isinstance(v, dict | list)
49
- }
50
-
51
-
52
- def _filter_list(
53
- lst: list[Any], condition: Callable[[Any], bool]
54
- ) -> list[Any]:
55
- return [
56
- nfilter(item, condition) if isinstance(item, dict | list) else item
57
- for item in lst
58
- if condition(item) or isinstance(item, dict | list)
59
- ]
@@ -1,45 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from typing import Any
6
-
7
- from lionagi.utils import UNDEFINED
8
-
9
- from .utils import get_target_container
10
-
11
-
12
- def nget(
13
- nested_structure: dict[Any, Any] | list[Any],
14
- /,
15
- indices: list[int | str],
16
- default: Any = UNDEFINED,
17
- ) -> Any:
18
- try:
19
- target_container = get_target_container(nested_structure, indices[:-1])
20
- last_index = indices[-1]
21
-
22
- if (
23
- isinstance(target_container, list)
24
- and isinstance(last_index, int)
25
- and last_index < len(target_container)
26
- ):
27
- return target_container[last_index]
28
- elif (
29
- isinstance(target_container, dict)
30
- and last_index in target_container
31
- ):
32
- return target_container[last_index]
33
- elif default is not UNDEFINED:
34
- return default
35
- else:
36
- raise LookupError(
37
- "Target not found and no default value provided."
38
- )
39
- except (IndexError, KeyError, TypeError):
40
- if default is not UNDEFINED:
41
- return default
42
- else:
43
- raise LookupError(
44
- "Target not found and no default value provided."
45
- )
@@ -1,104 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from typing import Any
6
-
7
- from lionagi.utils import to_list
8
-
9
-
10
- def ninsert(
11
- nested_structure: dict[Any, Any] | list[Any],
12
- /,
13
- indices: list[str | int],
14
- value: Any,
15
- *,
16
- current_depth: int = 0,
17
- ) -> None:
18
- """
19
- Inserts a value into a nested structure at a specified path.
20
-
21
- Navigates a nested dictionary or list based on a sequence of indices or
22
- keys and inserts `value` at the final location. This method can create
23
- intermediate dictionaries or lists as needed.
24
-
25
- Args:
26
- nested_structure: The nested structure to modify.
27
- indices: The sequence of keys or indices defining the insertion path.
28
- value: The value to insert at the specified location.
29
- current_depth: Internal use only; tracks the current depth during
30
- recursive calls.
31
-
32
- Raises:
33
- ValueError: If the indices list is empty.
34
- TypeError: If an invalid key or container type is encountered.
35
-
36
- Examples:
37
- >>> subject_ = {'a': {'b': [1, 2]}}
38
- >>> ninsert(subject_, ['a', 'b', 2], 3)
39
- >>> assert subject_ == {'a': {'b': [1, 2, 3]}}
40
-
41
- >>> subject_ = []
42
- >>> ninsert(subject_, [0, 'a'], 1)
43
- >>> assert subject_ == [{'a': 1}]
44
- """
45
- if not indices:
46
- raise ValueError("Indices list cannot be empty")
47
-
48
- indices = to_list(indices)
49
- for i, part in enumerate(indices[:-1]):
50
- if isinstance(part, int):
51
- if isinstance(nested_structure, dict):
52
- raise TypeError(
53
- f"Unsupported key type: {type(part).__name__}.Only string keys are acceptable.",
54
- )
55
- while len(nested_structure) <= part:
56
- nested_structure.append(None)
57
- if nested_structure[part] is None or not isinstance(
58
- nested_structure[part], (dict, list)
59
- ):
60
- next_part = indices[i + 1]
61
- nested_structure[part] = (
62
- [] if isinstance(next_part, int) else {}
63
- )
64
- elif isinstance(nested_structure, dict):
65
- if part is None:
66
- raise TypeError("Cannot use NoneType as a key in a dictionary")
67
- if isinstance(part, (float, complex)):
68
- raise TypeError(
69
- f"Unsupported key type: {type(part).__name__}.Only string keys are acceptable.",
70
- )
71
- if part not in nested_structure:
72
- next_part = indices[i + 1]
73
- nested_structure[part] = (
74
- [] if isinstance(next_part, int) else {}
75
- )
76
- else:
77
- raise TypeError(
78
- f"Invalid container type: {type(nested_structure)} encountered during insertion"
79
- )
80
-
81
- nested_structure = nested_structure[part]
82
- current_depth += 1
83
-
84
- last_part = indices[-1]
85
- if isinstance(last_part, int):
86
- if isinstance(nested_structure, dict):
87
- raise TypeError(
88
- f"Unsupported key type: {type(last_part).__name__}."
89
- "Only string keys are acceptable.",
90
- )
91
- while len(nested_structure) <= last_part:
92
- nested_structure.append(None)
93
- nested_structure[last_part] = value
94
- elif isinstance(nested_structure, list):
95
- raise TypeError("Cannot use non-integer index on a list")
96
- else:
97
- if last_part is None:
98
- raise TypeError("Cannot use NoneType as a key in a dictionary")
99
- if isinstance(last_part, (float, complex)):
100
- raise TypeError(
101
- f"Unsupported key type: {type(last_part).__name__}."
102
- "Only string keys are acceptable.",
103
- )
104
- nested_structure[last_part] = value
@@ -1,158 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from collections import defaultdict
6
- from collections.abc import Callable, Sequence
7
- from itertools import chain
8
- from typing import Any
9
-
10
- from .utils import is_homogeneous
11
-
12
-
13
- def nmerge(
14
- nested_structure: Sequence[dict[str, Any] | list[Any]],
15
- /,
16
- *,
17
- overwrite: bool = False,
18
- dict_sequence: bool = False,
19
- sort_list: bool = False,
20
- custom_sort: Callable[[Any], Any] | None = None,
21
- ) -> dict[str, Any] | list[Any]:
22
- """
23
- Merge multiple dictionaries, lists, or sequences into a unified structure.
24
-
25
- Args:
26
- nested_structure: A sequence containing dictionaries, lists, or other
27
- iterable objects to merge.
28
- overwrite: If True, overwrite existing keys in dictionaries with
29
- those from subsequent dictionaries.
30
- dict_sequence: Enables unique key generation for duplicate keys by
31
- appending a sequence number. Applicable only if `overwrite` is
32
- False.
33
- sort_list: When True, sort the resulting list after merging. It does
34
- not affect dictionaries.
35
- custom_sort: An optional callable that defines custom sorting logic
36
- for the merged list.
37
-
38
- Returns:
39
- A merged dictionary or list, depending on the types present in
40
- `nested_structure`.
41
-
42
- Raises:
43
- TypeError: If `nested_structure` contains objects of incompatible
44
- types that cannot be merged.
45
- """
46
- if not isinstance(nested_structure, list):
47
- raise TypeError("Please input a list")
48
- if is_homogeneous(nested_structure, dict):
49
- return _merge_dicts(nested_structure, overwrite, dict_sequence)
50
- elif is_homogeneous(nested_structure, list):
51
- return _merge_sequences(nested_structure, sort_list, custom_sort)
52
- else:
53
- raise TypeError(
54
- "All items in the input list must be of the same type, either dict, list, or Iterable."
55
- )
56
-
57
-
58
- def _deep_merge_dicts(
59
- dict1: dict[str, Any], dict2: dict[str, Any]
60
- ) -> dict[str, Any]:
61
- """
62
- Recursively merges two dictionaries, combining values where keys overlap.
63
-
64
- Args:
65
- dict1: The first dictionary.
66
- dict2: The second dictionary.
67
-
68
- Returns:
69
- The merged dictionary.
70
- """
71
- for key in dict2:
72
- if key in dict1:
73
- if isinstance(dict1[key], dict) and isinstance(dict2[key], dict):
74
- _deep_merge_dicts(dict1[key], dict2[key])
75
- else:
76
- if not isinstance(dict1[key], list):
77
- dict1[key] = [dict1[key]]
78
- dict1[key].append(dict2[key])
79
- else:
80
- dict1[key] = dict2[key]
81
- return dict1
82
-
83
-
84
- def _merge_dicts(
85
- iterables: list[dict[str, Any]],
86
- dict_update: bool,
87
- dict_sequence: bool,
88
- ) -> dict[str, Any]:
89
- """
90
- Merges a list of dictionaries into a single dictionary, with options for
91
- handling duplicate keys and sequences.
92
-
93
- Args:
94
- iterables: A list of dictionaries to merge.
95
- dict_update: If True, overwrite existing keys in dictionaries
96
- with those from subsequent dictionaries.
97
- dict_sequence: Enables unique key generation for duplicate keys
98
- by appending a sequence number
99
-
100
- Returns:
101
- The merged dictionary.
102
- """
103
- merged_dict = {} # {'a': [1, 2]}
104
- sequence_counters = defaultdict(int)
105
- list_values = {}
106
-
107
- for d in iterables: # [{'a': [1, 2]}, {'a': [3, 4]}]
108
- for key, value in d.items(): # {'a': [3, 4]}
109
- if key not in merged_dict or dict_update:
110
- if (
111
- key in merged_dict
112
- and isinstance(merged_dict[key], dict)
113
- and isinstance(value, dict)
114
- ):
115
- _deep_merge_dicts(merged_dict[key], value)
116
- else:
117
- merged_dict[key] = value # {'a': [1, 2]}
118
- if isinstance(value, list):
119
- list_values[key] = True
120
- elif dict_sequence:
121
- sequence_counters[key] += 1
122
- new_key = f"{key}{sequence_counters[key]}"
123
- merged_dict[new_key] = value
124
- else:
125
- if not isinstance(merged_dict[key], list) or list_values.get(
126
- key, False
127
- ):
128
- merged_dict[key] = [merged_dict[key]]
129
- merged_dict[key].append(value)
130
-
131
- return merged_dict
132
-
133
-
134
- def _merge_sequences(
135
- iterables: list[list[Any]],
136
- sort_list: bool,
137
- custom_sort: Callable[[Any], Any] | None = None,
138
- ) -> list[Any]:
139
- """
140
- Merges a list of lists into a single list, with options for sorting and
141
- custom sorting logic.
142
-
143
- Args:
144
- iterables: A list of lists to merge.
145
- sort_list: When True, sort the resulting list after merging.
146
- custom_sort: An optional callable that defines custom sorting logic
147
- for the merged list.
148
-
149
- Returns:
150
- The merged list.
151
- """
152
- merged_list = list(chain(*iterables))
153
- if sort_list:
154
- if custom_sort:
155
- return sorted(merged_list, key=custom_sort)
156
- else:
157
- return sorted(merged_list, key=lambda x: (isinstance(x, str), x))
158
- return merged_list
@@ -1,69 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from collections.abc import Sequence
6
- from typing import Any
7
-
8
- from lionagi.utils import UNDEFINED, to_list
9
-
10
-
11
- def npop(
12
- input_: dict[str, Any] | list[Any],
13
- /,
14
- indices: str | int | Sequence[str | int],
15
- default: Any = UNDEFINED,
16
- ) -> Any:
17
- """
18
- Perform a nested pop operation on the input structure.
19
-
20
- This function navigates through the nested structure using the provided
21
- indices and removes and returns the value at the final location.
22
-
23
- Args:
24
- input_: The input nested structure (dict or list) to pop from.
25
- indices: A single index or a sequence of indices to navigate the
26
- nested structure.
27
- default: The value to return if the key is not found. If not
28
- provided, a KeyError will be raised.
29
-
30
- Returns:
31
- The value at the specified nested location.
32
-
33
- Raises:
34
- ValueError: If the indices list is empty.
35
- KeyError: If a key is not found in a dictionary.
36
- IndexError: If an index is out of range for a list.
37
- TypeError: If an operation is not supported on the current data type.
38
- """
39
- if not indices:
40
- raise ValueError("Indices list cannot be empty")
41
-
42
- indices = to_list(indices)
43
-
44
- current = input_
45
- for key in indices[:-1]:
46
- if isinstance(current, dict):
47
- if current.get(key):
48
- current = current[key]
49
- else:
50
- raise KeyError(f"{key} is not found in {current}")
51
- elif isinstance(current, list) and isinstance(key, int):
52
- if key >= len(current):
53
- raise KeyError(
54
- f"{key} exceeds the length of the list {current}"
55
- )
56
- elif key < 0:
57
- raise ValueError("list index cannot be negative")
58
- current = current[key]
59
-
60
- last_key = indices[-1]
61
- try:
62
- return current.pop(
63
- last_key,
64
- )
65
- except Exception as e:
66
- if default is not UNDEFINED:
67
- return default
68
- else:
69
- raise KeyError(f"Invalid npop. Error: {e}")
@@ -1,94 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- from collections.abc import Sequence
6
- from typing import Any
7
-
8
- from lionagi.utils import to_list
9
-
10
- from .utils import ensure_list_index
11
-
12
-
13
- def nset(
14
- nested_structure: dict[str, Any] | list[Any],
15
- /,
16
- indices: str | int | Sequence[str | int],
17
- value: Any,
18
- ) -> None:
19
- """Set a value within a nested structure at the specified path.
20
-
21
- This method allows setting a value deep within a nested dictionary or list
22
- by specifying a path to the target location using a sequence of indices.
23
- Each index in the sequence represents a level in the nested structure,
24
- with integers used for list indices and strings for dictionary keys.
25
-
26
- Args:
27
- nested_structure: The nested structure to modify.
28
- indices: The path of indices leading to the target location.
29
- value: The value to set at the specified location.
30
-
31
- Raises:
32
- ValueError: If the indices sequence is empty.
33
- TypeError: If the target container is not a list or dictionary,
34
- or if the index type is incorrect.
35
-
36
- Examples:
37
- >>> data = {'a': {'b': [10, 20]}}
38
- >>> nset(data, ['a', 'b', 1], 99)
39
- >>> assert data == {'a': {'b': [10, 99]}}
40
-
41
- >>> data = [0, [1, 2], 3]
42
- >>> nset(data, [1, 1], 99)
43
- >>> assert data == [0, [1, 99], 3]
44
- """
45
-
46
- if not indices:
47
- raise ValueError(
48
- "Indices list is empty, cannot determine target container"
49
- )
50
-
51
- _indices = to_list(indices)
52
- target_container = nested_structure
53
-
54
- for i, index in enumerate(_indices[:-1]):
55
- if isinstance(target_container, list):
56
- if not isinstance(index, int):
57
- raise TypeError("Cannot use non-integer index on a list")
58
- ensure_list_index(target_container, index)
59
- if target_container[index] is None:
60
- next_index = _indices[i + 1]
61
- target_container[index] = (
62
- [] if isinstance(next_index, int) else {}
63
- )
64
- elif isinstance(target_container, dict):
65
- if isinstance(index, int):
66
- raise TypeError(
67
- f"Unsupported key type: {type(index).__name__}. "
68
- "Only string keys are acceptable."
69
- )
70
- if index not in target_container:
71
- next_index = _indices[i + 1]
72
- target_container[index] = (
73
- [] if isinstance(next_index, int) else {}
74
- )
75
- else:
76
- raise TypeError("Target container is not a list or dictionary")
77
-
78
- target_container = target_container[index]
79
-
80
- last_index = _indices[-1]
81
- if isinstance(target_container, list):
82
- if not isinstance(last_index, int):
83
- raise TypeError("Cannot use non-integer index on a list")
84
- ensure_list_index(target_container, last_index)
85
- target_container[last_index] = value
86
- elif isinstance(target_container, dict):
87
- if not isinstance(last_index, str):
88
- raise TypeError(
89
- f"Unsupported key type: {type(last_index).__name__}. "
90
- "Only string keys are acceptable."
91
- )
92
- target_container[last_index] = value
93
- else:
94
- raise TypeError("Cannot set value on non-list/dict element")