lionagi 0.16.2__py3-none-any.whl → 0.17.0__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.
- lionagi/adapters/_utils.py +10 -23
- lionagi/adapters/async_postgres_adapter.py +83 -79
- lionagi/ln/__init__.py +4 -4
- lionagi/ln/_json_dump.py +0 -6
- lionagi/ln/fuzzy/__init__.py +4 -1
- lionagi/ln/fuzzy/_fuzzy_validate.py +109 -0
- lionagi/ln/fuzzy/_to_dict.py +388 -0
- lionagi/models/__init__.py +0 -2
- lionagi/operations/__init__.py +0 -6
- lionagi/operations/_visualize_graph.py +285 -0
- lionagi/operations/brainstorm/brainstorm.py +14 -12
- lionagi/operations/builder.py +23 -302
- lionagi/operations/communicate/communicate.py +1 -1
- lionagi/operations/flow.py +14 -11
- lionagi/operations/node.py +14 -3
- lionagi/operations/operate/operate.py +5 -11
- lionagi/operations/parse/parse.py +2 -3
- lionagi/operations/types.py +0 -2
- lionagi/operations/utils.py +11 -5
- lionagi/protocols/generic/pile.py +3 -7
- lionagi/protocols/graph/graph.py +23 -6
- lionagi/protocols/graph/node.py +0 -2
- lionagi/protocols/messages/message.py +0 -1
- lionagi/protocols/operatives/operative.py +2 -2
- lionagi/protocols/types.py +0 -15
- lionagi/service/connections/endpoint.py +11 -5
- lionagi/service/connections/match_endpoint.py +2 -10
- lionagi/service/connections/providers/types.py +1 -3
- lionagi/service/hooks/hook_event.py +1 -1
- lionagi/service/hooks/hook_registry.py +1 -1
- lionagi/service/rate_limited_processor.py +1 -1
- lionagi/session/branch.py +24 -18
- lionagi/session/session.py +2 -18
- lionagi/utils.py +3 -335
- lionagi/version.py +1 -1
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/METADATA +4 -13
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/RECORD +39 -61
- lionagi/adapters/postgres_model_adapter.py +0 -131
- lionagi/libs/concurrency.py +0 -1
- lionagi/libs/nested/__init__.py +0 -3
- lionagi/libs/nested/flatten.py +0 -172
- lionagi/libs/nested/nfilter.py +0 -59
- lionagi/libs/nested/nget.py +0 -45
- lionagi/libs/nested/ninsert.py +0 -104
- lionagi/libs/nested/nmerge.py +0 -158
- lionagi/libs/nested/npop.py +0 -69
- lionagi/libs/nested/nset.py +0 -94
- lionagi/libs/nested/unflatten.py +0 -83
- lionagi/libs/nested/utils.py +0 -189
- lionagi/libs/parse.py +0 -31
- lionagi/libs/schema/json_schema.py +0 -231
- lionagi/libs/unstructured/__init__.py +0 -0
- lionagi/libs/unstructured/pdf_to_image.py +0 -45
- lionagi/libs/unstructured/read_image_to_base64.py +0 -33
- lionagi/libs/validate/fuzzy_match_keys.py +0 -7
- lionagi/libs/validate/fuzzy_validate_mapping.py +0 -144
- lionagi/libs/validate/string_similarity.py +0 -7
- lionagi/libs/validate/xml_parser.py +0 -203
- lionagi/models/note.py +0 -387
- lionagi/protocols/graph/_utils.py +0 -22
- lionagi/service/connections/providers/claude_code_.py +0 -299
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/WHEEL +0 -0
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,203 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
import xml.etree.ElementTree as ET
|
5
|
-
from typing import Any
|
6
|
-
|
7
|
-
|
8
|
-
def to_xml(
|
9
|
-
obj: dict | list | str | int | float | bool | None,
|
10
|
-
root_name: str = "root",
|
11
|
-
) -> str:
|
12
|
-
"""
|
13
|
-
Convert a dictionary into an XML formatted string.
|
14
|
-
|
15
|
-
Rules:
|
16
|
-
- A dictionary key becomes an XML tag.
|
17
|
-
- If the dictionary value is:
|
18
|
-
- A primitive type (str, int, float, bool, None): it becomes the text content of the tag.
|
19
|
-
- A list: each element of the list will repeat the same tag.
|
20
|
-
- Another dictionary: it is recursively converted to nested XML.
|
21
|
-
- root_name sets the top-level XML element name.
|
22
|
-
|
23
|
-
Args:
|
24
|
-
obj: The Python object to convert (typically a dictionary).
|
25
|
-
root_name: The name of the root XML element.
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
A string representing the XML.
|
29
|
-
|
30
|
-
Examples:
|
31
|
-
>>> to_xml({"a": 1, "b": {"c": "hello", "d": [10, 20]}}, root_name="data")
|
32
|
-
'<data><a>1</a><b><c>hello</c><d>10</d><d>20</d></b></data>'
|
33
|
-
"""
|
34
|
-
|
35
|
-
def _convert(value: Any, tag_name: str) -> str:
|
36
|
-
# If value is a dict, recursively convert its keys
|
37
|
-
if isinstance(value, dict):
|
38
|
-
inner = "".join(_convert(v, k) for k, v in value.items())
|
39
|
-
return f"<{tag_name}>{inner}</{tag_name}>"
|
40
|
-
# If value is a list, repeat the same tag for each element
|
41
|
-
elif isinstance(value, list):
|
42
|
-
return "".join(_convert(item, tag_name) for item in value)
|
43
|
-
# If value is a primitive, convert to string and place inside tag
|
44
|
-
else:
|
45
|
-
text = "" if value is None else str(value)
|
46
|
-
# Escape special XML characters if needed (minimal)
|
47
|
-
text = (
|
48
|
-
text.replace("&", "&")
|
49
|
-
.replace("<", "<")
|
50
|
-
.replace(">", ">")
|
51
|
-
.replace('"', """)
|
52
|
-
.replace("'", "'")
|
53
|
-
)
|
54
|
-
return f"<{tag_name}>{text}</{tag_name}>"
|
55
|
-
|
56
|
-
# If top-level obj is not a dict, wrap it in one
|
57
|
-
if not isinstance(obj, dict):
|
58
|
-
obj = {root_name: obj}
|
59
|
-
|
60
|
-
inner_xml = "".join(_convert(v, k) for k, v in obj.items())
|
61
|
-
return f"<{root_name}>{inner_xml}</{root_name}>"
|
62
|
-
|
63
|
-
|
64
|
-
class XMLParser:
|
65
|
-
def __init__(self, xml_string: str):
|
66
|
-
self.xml_string = xml_string.strip()
|
67
|
-
self.index = 0
|
68
|
-
|
69
|
-
def parse(self) -> dict[str, Any]:
|
70
|
-
"""Parse the XML string and return the root element as a dictionary."""
|
71
|
-
return self._parse_element()
|
72
|
-
|
73
|
-
def _parse_element(self) -> dict[str, Any]:
|
74
|
-
"""Parse a single XML element and its children."""
|
75
|
-
self._skip_whitespace()
|
76
|
-
if self.xml_string[self.index] != "<":
|
77
|
-
raise ValueError(
|
78
|
-
f"Expected '<', found '{self.xml_string[self.index]}'"
|
79
|
-
)
|
80
|
-
|
81
|
-
tag, attributes = self._parse_opening_tag()
|
82
|
-
children: dict[str, str | list | dict] = {}
|
83
|
-
text = ""
|
84
|
-
|
85
|
-
while self.index < len(self.xml_string):
|
86
|
-
self._skip_whitespace()
|
87
|
-
if self.xml_string.startswith("</", self.index):
|
88
|
-
closing_tag = self._parse_closing_tag()
|
89
|
-
if closing_tag != tag:
|
90
|
-
raise ValueError(
|
91
|
-
f"Mismatched tags: '{tag}' and '{closing_tag}'"
|
92
|
-
)
|
93
|
-
break
|
94
|
-
elif self.xml_string.startswith("<", self.index):
|
95
|
-
child = self._parse_element()
|
96
|
-
child_tag, child_data = next(iter(child.items()))
|
97
|
-
if child_tag in children:
|
98
|
-
if not isinstance(children[child_tag], list):
|
99
|
-
children[child_tag] = [children[child_tag]]
|
100
|
-
children[child_tag].append(child_data)
|
101
|
-
else:
|
102
|
-
children[child_tag] = child_data
|
103
|
-
else:
|
104
|
-
text += self._parse_text()
|
105
|
-
|
106
|
-
result: dict[str, Any] = {}
|
107
|
-
if attributes:
|
108
|
-
result["@attributes"] = attributes
|
109
|
-
if children:
|
110
|
-
result.update(children)
|
111
|
-
elif text.strip():
|
112
|
-
result = text.strip()
|
113
|
-
|
114
|
-
return {tag: result}
|
115
|
-
|
116
|
-
def _parse_opening_tag(self) -> tuple[str, dict[str, str]]:
|
117
|
-
"""Parse an opening XML tag and its attributes."""
|
118
|
-
match = re.match(
|
119
|
-
r'<(\w+)((?:\s+\w+="[^"]*")*)\s*/?>',
|
120
|
-
self.xml_string[self.index :], # noqa
|
121
|
-
)
|
122
|
-
if not match:
|
123
|
-
raise ValueError("Invalid opening tag")
|
124
|
-
self.index += match.end()
|
125
|
-
tag = match.group(1)
|
126
|
-
attributes = dict(re.findall(r'(\w+)="([^"]*)"', match.group(2)))
|
127
|
-
return tag, attributes
|
128
|
-
|
129
|
-
def _parse_closing_tag(self) -> str:
|
130
|
-
"""Parse a closing XML tag."""
|
131
|
-
match = re.match(r"</(\w+)>", self.xml_string[self.index :]) # noqa
|
132
|
-
if not match:
|
133
|
-
raise ValueError("Invalid closing tag")
|
134
|
-
self.index += match.end()
|
135
|
-
return match.group(1)
|
136
|
-
|
137
|
-
def _parse_text(self) -> str:
|
138
|
-
"""Parse text content between XML tags."""
|
139
|
-
start = self.index
|
140
|
-
while (
|
141
|
-
self.index < len(self.xml_string)
|
142
|
-
and self.xml_string[self.index] != "<"
|
143
|
-
):
|
144
|
-
self.index += 1
|
145
|
-
return self.xml_string[start : self.index] # noqa
|
146
|
-
|
147
|
-
def _skip_whitespace(self) -> None:
|
148
|
-
"""Skip any whitespace characters at the current parsing position."""
|
149
|
-
p_ = len(self.xml_string[self.index :]) # noqa
|
150
|
-
m_ = len(self.xml_string[self.index :].lstrip()) # noqa
|
151
|
-
|
152
|
-
self.index += p_ - m_
|
153
|
-
|
154
|
-
|
155
|
-
def xml_to_dict(
|
156
|
-
xml_string: str,
|
157
|
-
/,
|
158
|
-
suppress=False,
|
159
|
-
remove_root: bool = True,
|
160
|
-
root_tag: str = None,
|
161
|
-
) -> dict[str, Any]:
|
162
|
-
"""
|
163
|
-
Parse an XML string into a nested dictionary structure.
|
164
|
-
|
165
|
-
This function converts an XML string into a dictionary where:
|
166
|
-
- Element tags become dictionary keys
|
167
|
-
- Text content is assigned directly to the tag key if there are no children
|
168
|
-
- Attributes are stored in a '@attributes' key
|
169
|
-
- Multiple child elements with the same tag are stored as lists
|
170
|
-
|
171
|
-
Args:
|
172
|
-
xml_string: The XML string to parse.
|
173
|
-
|
174
|
-
Returns:
|
175
|
-
A dictionary representation of the XML structure.
|
176
|
-
|
177
|
-
Raises:
|
178
|
-
ValueError: If the XML is malformed or parsing fails.
|
179
|
-
"""
|
180
|
-
try:
|
181
|
-
a = XMLParser(xml_string).parse()
|
182
|
-
if remove_root and (root_tag or "root") in a:
|
183
|
-
a = a[root_tag or "root"]
|
184
|
-
return a
|
185
|
-
except ValueError as e:
|
186
|
-
if not suppress:
|
187
|
-
raise e
|
188
|
-
|
189
|
-
|
190
|
-
def dict_to_xml(data: dict, /, root_tag: str = "root") -> str:
|
191
|
-
root = ET.Element(root_tag)
|
192
|
-
|
193
|
-
def convert(dict_obj: dict, parent: Any) -> None:
|
194
|
-
for key, val in dict_obj.items():
|
195
|
-
if isinstance(val, dict):
|
196
|
-
element = ET.SubElement(parent, key)
|
197
|
-
convert(dict_obj=val, parent=element)
|
198
|
-
else:
|
199
|
-
element = ET.SubElement(parent, key)
|
200
|
-
element.text = str(object=val)
|
201
|
-
|
202
|
-
convert(dict_obj=data, parent=root)
|
203
|
-
return ET.tostring(root, encoding="unicode")
|
lionagi/models/note.py
DELETED
@@ -1,387 +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 ItemsView, Iterator, ValuesView
|
6
|
-
from typing import Any, TypeAlias
|
7
|
-
|
8
|
-
from pydantic import BaseModel, ConfigDict, Field, field_serializer
|
9
|
-
from typing_extensions import override
|
10
|
-
|
11
|
-
from lionagi.libs.nested.flatten import flatten
|
12
|
-
from lionagi.utils import UNDEFINED, copy, to_list
|
13
|
-
|
14
|
-
IndiceType: TypeAlias = str | list[str | int]
|
15
|
-
|
16
|
-
|
17
|
-
class Note(BaseModel):
|
18
|
-
"""Container for managing nested dictionary data structures.
|
19
|
-
|
20
|
-
A flexible container that provides deep nested data access, dictionary-like
|
21
|
-
interface, flattening capabilities, and update operations for managing
|
22
|
-
complex nested data structures.
|
23
|
-
|
24
|
-
Args:
|
25
|
-
**kwargs: Key-value pairs for initial content.
|
26
|
-
|
27
|
-
Attributes:
|
28
|
-
content: Nested dictionary structure.
|
29
|
-
model_config: Configuration allowing arbitrary types.
|
30
|
-
|
31
|
-
Examples:
|
32
|
-
>>> note = Note(
|
33
|
-
... user={
|
34
|
-
... "name": "John",
|
35
|
-
... "settings": {
|
36
|
-
... "theme": "dark"
|
37
|
-
... }
|
38
|
-
... }
|
39
|
-
... )
|
40
|
-
>>>
|
41
|
-
>>> # Access nested data
|
42
|
-
>>> name = note.get(["user", "name"])
|
43
|
-
>>> theme = note["user"]["settings"]["theme"]
|
44
|
-
>>>
|
45
|
-
>>> # Update nested structure
|
46
|
-
>>> note.update(["user", "settings"], {"language": "en"})
|
47
|
-
"""
|
48
|
-
|
49
|
-
content: dict[str, Any] = Field(
|
50
|
-
default_factory=dict
|
51
|
-
) # Nested data structure
|
52
|
-
|
53
|
-
model_config = ConfigDict(
|
54
|
-
arbitrary_types_allowed=True,
|
55
|
-
use_enum_values=True,
|
56
|
-
populate_by_name=True,
|
57
|
-
)
|
58
|
-
|
59
|
-
def __init__(self, **kwargs: Any) -> None:
|
60
|
-
"""Initialize Note with dictionary data.
|
61
|
-
|
62
|
-
Args:
|
63
|
-
**kwargs: Key-value pairs that will form the initial nested
|
64
|
-
dictionary structure.
|
65
|
-
"""
|
66
|
-
super().__init__()
|
67
|
-
self.content = kwargs
|
68
|
-
|
69
|
-
@field_serializer("content")
|
70
|
-
def _serialize_content(self, value: Any) -> dict[str, Any]:
|
71
|
-
"""Serialize content to dictionary format.
|
72
|
-
|
73
|
-
Args:
|
74
|
-
value: Content to serialize
|
75
|
-
|
76
|
-
Returns:
|
77
|
-
Deep copy of content dictionary
|
78
|
-
"""
|
79
|
-
output_dict = copy(value, deep=True)
|
80
|
-
return output_dict
|
81
|
-
|
82
|
-
def to_dict(self) -> dict[str, Any]:
|
83
|
-
"""Convert Note to dictionary, excluding undefined values.
|
84
|
-
|
85
|
-
Returns:
|
86
|
-
Dictionary representation with UNDEFINED values removed
|
87
|
-
"""
|
88
|
-
out = copy(self.content)
|
89
|
-
for k, v in self.content.items():
|
90
|
-
if v is UNDEFINED:
|
91
|
-
out.pop(k)
|
92
|
-
return out
|
93
|
-
|
94
|
-
def pop(
|
95
|
-
self,
|
96
|
-
indices: IndiceType,
|
97
|
-
/,
|
98
|
-
default: Any = UNDEFINED,
|
99
|
-
) -> Any:
|
100
|
-
"""Remove and return item from nested structure.
|
101
|
-
|
102
|
-
Removes and returns the value at the specified path in the nested
|
103
|
-
structure. If the path doesn't exist and no default is provided,
|
104
|
-
raises KeyError.
|
105
|
-
|
106
|
-
Args:
|
107
|
-
indices: Path to the item to remove, can be a string for top-level
|
108
|
-
keys or a list for nested access.
|
109
|
-
default: Value to return if the path is not found. If not provided
|
110
|
-
and path is not found, raises KeyError.
|
111
|
-
|
112
|
-
Returns:
|
113
|
-
The value that was removed, or the default value if provided and
|
114
|
-
path not found.
|
115
|
-
|
116
|
-
Raises:
|
117
|
-
KeyError: If the path is not found and no default value is provided.
|
118
|
-
"""
|
119
|
-
from lionagi.libs.nested.npop import npop
|
120
|
-
|
121
|
-
indices = to_list(indices, flatten=True, dropna=True)
|
122
|
-
return npop(self.content, indices, default)
|
123
|
-
|
124
|
-
def insert(self, indices: IndiceType, value: Any, /) -> None:
|
125
|
-
"""Insert value into nested structure at specified indices.
|
126
|
-
|
127
|
-
Args:
|
128
|
-
indices: Path where to insert
|
129
|
-
value: Value to insert
|
130
|
-
"""
|
131
|
-
from lionagi.libs.nested.ninsert import ninsert
|
132
|
-
|
133
|
-
indices = to_list(indices, flatten=True, dropna=True)
|
134
|
-
ninsert(self.content, indices, value)
|
135
|
-
|
136
|
-
def set(self, indices: IndiceType, value: Any, /) -> None:
|
137
|
-
"""Set value in nested structure at specified indices.
|
138
|
-
|
139
|
-
Args:
|
140
|
-
indices: Path where to set
|
141
|
-
value: Value to set
|
142
|
-
"""
|
143
|
-
indices = to_list(indices, flatten=True, dropna=True)
|
144
|
-
if self.get(indices, None) is None:
|
145
|
-
self.insert(indices, value)
|
146
|
-
else:
|
147
|
-
from lionagi.libs.nested.nset import nset
|
148
|
-
|
149
|
-
nset(self.content, indices, value)
|
150
|
-
|
151
|
-
def get(
|
152
|
-
self,
|
153
|
-
indices: IndiceType,
|
154
|
-
/,
|
155
|
-
default: Any = UNDEFINED,
|
156
|
-
) -> Any:
|
157
|
-
"""Get value from nested structure at specified indices.
|
158
|
-
|
159
|
-
Retrieves the value at the specified path in the nested structure.
|
160
|
-
If the path doesn't exist and no default is provided, raises KeyError.
|
161
|
-
|
162
|
-
Args:
|
163
|
-
indices: Path to the value, can be a string for top-level keys
|
164
|
-
or a list for nested access.
|
165
|
-
default: Value to return if the path is not found. If not provided
|
166
|
-
and path is not found, raises KeyError.
|
167
|
-
|
168
|
-
Returns:
|
169
|
-
The value at the specified path, or the default value if provided
|
170
|
-
and path not found.
|
171
|
-
|
172
|
-
Raises:
|
173
|
-
KeyError: If the path is not found and no default value is provided.
|
174
|
-
"""
|
175
|
-
from lionagi.libs.nested.nget import nget
|
176
|
-
|
177
|
-
indices = to_list(indices, flatten=True, dropna=True)
|
178
|
-
return nget(self.content, indices, default)
|
179
|
-
|
180
|
-
def keys(self, /, flat: bool = False, **kwargs: Any) -> list:
|
181
|
-
"""Get keys of the Note.
|
182
|
-
|
183
|
-
Args:
|
184
|
-
flat: If True, return flattened keys
|
185
|
-
kwargs: Additional flattening options
|
186
|
-
|
187
|
-
Returns:
|
188
|
-
List of keys, optionally flattened
|
189
|
-
"""
|
190
|
-
if flat:
|
191
|
-
kwargs["coerce_keys"] = kwargs.get("coerce_keys", False)
|
192
|
-
kwargs["coerce_sequence"] = kwargs.get("coerce_sequence", "list")
|
193
|
-
return flatten(self.content, **kwargs).keys()
|
194
|
-
return list(self.content.keys())
|
195
|
-
|
196
|
-
def values(self, /, flat: bool = False, **kwargs: Any) -> ValuesView:
|
197
|
-
"""Get values of the Note.
|
198
|
-
|
199
|
-
Returns either a view of top-level values or, if flat=True, a view
|
200
|
-
of all values in the flattened nested structure.
|
201
|
-
|
202
|
-
Args:
|
203
|
-
flat: If True, returns values from all levels of the nested
|
204
|
-
structure. If False, returns only top-level values.
|
205
|
-
kwargs: Additional options for flattening behavior when flat=True.
|
206
|
-
Common options include coerce_keys and coerce_sequence.
|
207
|
-
|
208
|
-
Returns:
|
209
|
-
A ValuesView object containing either top-level values or all
|
210
|
-
values from the flattened structure if flat=True.
|
211
|
-
"""
|
212
|
-
if flat:
|
213
|
-
kwargs["coerce_keys"] = kwargs.get("coerce_keys", False)
|
214
|
-
kwargs["coerce_sequence"] = kwargs.get("coerce_sequence", "list")
|
215
|
-
return flatten(self.content, **kwargs).values()
|
216
|
-
return self.content.values()
|
217
|
-
|
218
|
-
def items(self, /, flat: bool = False, **kwargs: Any) -> ItemsView:
|
219
|
-
"""Get items of the Note.
|
220
|
-
|
221
|
-
Args:
|
222
|
-
flat: If True, return flattened items
|
223
|
-
kwargs: Additional flattening options
|
224
|
-
|
225
|
-
Returns:
|
226
|
-
View of items, optionally flattened
|
227
|
-
"""
|
228
|
-
if flat:
|
229
|
-
kwargs["coerce_keys"] = kwargs.get("coerce_keys", False)
|
230
|
-
kwargs["coerce_sequence"] = kwargs.get("coerce_sequence", "list")
|
231
|
-
return flatten(self.content, **kwargs).items()
|
232
|
-
return self.content.items()
|
233
|
-
|
234
|
-
def clear(self) -> None:
|
235
|
-
"""Clear all content."""
|
236
|
-
self.content.clear()
|
237
|
-
|
238
|
-
def update(
|
239
|
-
self,
|
240
|
-
indices: IndiceType,
|
241
|
-
value: Any,
|
242
|
-
) -> None:
|
243
|
-
"""Update nested structure at specified indices.
|
244
|
-
|
245
|
-
Updates the value at the specified path in the nested structure.
|
246
|
-
The behavior depends on the existing value and the update value:
|
247
|
-
- If path doesn't exist: creates it with value (wrapped in list if scalar)
|
248
|
-
- If existing is list: extends with value if list, appends if scalar
|
249
|
-
- If existing is dict: updates with value if dict, raises error if not
|
250
|
-
|
251
|
-
Args:
|
252
|
-
indices: Path to the location to update, can be a string for
|
253
|
-
top-level keys or a list for nested access.
|
254
|
-
value: The new value to set. Must be compatible with existing
|
255
|
-
value type (dict for dict, any value for list).
|
256
|
-
|
257
|
-
Raises:
|
258
|
-
ValueError: If trying to update a dictionary with a non-dictionary
|
259
|
-
value.
|
260
|
-
"""
|
261
|
-
existing = None
|
262
|
-
if not indices:
|
263
|
-
existing = self.content
|
264
|
-
else:
|
265
|
-
existing = self.get(indices, None)
|
266
|
-
|
267
|
-
if existing is None:
|
268
|
-
if not isinstance(value, (list, dict)):
|
269
|
-
value = [value]
|
270
|
-
self.set(indices, value)
|
271
|
-
|
272
|
-
if isinstance(existing, list):
|
273
|
-
if isinstance(value, list):
|
274
|
-
existing.extend(value)
|
275
|
-
else:
|
276
|
-
existing.append(value)
|
277
|
-
|
278
|
-
elif isinstance(existing, dict):
|
279
|
-
if isinstance(value, self.__class__):
|
280
|
-
value = value.content
|
281
|
-
|
282
|
-
if isinstance(value, dict):
|
283
|
-
existing.update(value)
|
284
|
-
else:
|
285
|
-
raise ValueError(
|
286
|
-
"Cannot update a dictionary with a non-dictionary value."
|
287
|
-
)
|
288
|
-
|
289
|
-
@classmethod
|
290
|
-
def from_dict(cls, kwargs: Any) -> "Note":
|
291
|
-
"""Create Note instance from dictionary.
|
292
|
-
|
293
|
-
Args:
|
294
|
-
kwargs: Dictionary to initialize with
|
295
|
-
|
296
|
-
Returns:
|
297
|
-
New Note instance
|
298
|
-
"""
|
299
|
-
return cls(**kwargs)
|
300
|
-
|
301
|
-
def __contains__(self, indices: IndiceType) -> bool:
|
302
|
-
"""Check if indices exist in content.
|
303
|
-
|
304
|
-
Implements the 'in' operator for checking path existence in the nested
|
305
|
-
structure.
|
306
|
-
|
307
|
-
Args:
|
308
|
-
indices: Path to check, can be a string for top-level keys or a
|
309
|
-
list for nested access.
|
310
|
-
|
311
|
-
Returns:
|
312
|
-
True if the path exists in the nested structure, False otherwise.
|
313
|
-
"""
|
314
|
-
return self.content.get(indices, UNDEFINED) is not UNDEFINED
|
315
|
-
|
316
|
-
def __len__(self) -> int:
|
317
|
-
"""Get length of content.
|
318
|
-
|
319
|
-
Implements len() function to return the number of top-level keys in
|
320
|
-
the nested structure.
|
321
|
-
|
322
|
-
Returns:
|
323
|
-
The number of top-level keys in the content dictionary.
|
324
|
-
"""
|
325
|
-
return len(self.content)
|
326
|
-
|
327
|
-
def __iter__(self) -> Iterator[str]:
|
328
|
-
"""Get iterator over content.
|
329
|
-
|
330
|
-
Returns:
|
331
|
-
Iterator over top-level keys
|
332
|
-
"""
|
333
|
-
return iter(self.content)
|
334
|
-
|
335
|
-
def __next__(self) -> str:
|
336
|
-
"""Get next item from content iterator.
|
337
|
-
|
338
|
-
Returns:
|
339
|
-
Next key in iteration
|
340
|
-
"""
|
341
|
-
return next(iter(self.content))
|
342
|
-
|
343
|
-
@override
|
344
|
-
def __str__(self) -> str:
|
345
|
-
"""Get string representation of content.
|
346
|
-
|
347
|
-
Implements str() function to provide a simple string representation
|
348
|
-
of the Note's content. Uses the standard dictionary string format.
|
349
|
-
|
350
|
-
Returns:
|
351
|
-
A string representation of the content dictionary, showing its
|
352
|
-
current state.
|
353
|
-
"""
|
354
|
-
return str(self.content)
|
355
|
-
|
356
|
-
@override
|
357
|
-
def __repr__(self) -> str:
|
358
|
-
"""Get detailed string representation of content.
|
359
|
-
|
360
|
-
Returns:
|
361
|
-
Detailed string representation of content dict
|
362
|
-
"""
|
363
|
-
return repr(self.content)
|
364
|
-
|
365
|
-
def __getitem__(self, indices: IndiceType) -> Any:
|
366
|
-
"""Get item using index notation.
|
367
|
-
|
368
|
-
Args:
|
369
|
-
indices: Path to value
|
370
|
-
|
371
|
-
Returns:
|
372
|
-
Value at path
|
373
|
-
|
374
|
-
Raises:
|
375
|
-
KeyError: If path not found
|
376
|
-
"""
|
377
|
-
indices = to_list(indices, flatten=True, dropna=True)
|
378
|
-
return self.get(indices)
|
379
|
-
|
380
|
-
def __setitem__(self, indices: IndiceType, value: Any) -> None:
|
381
|
-
"""Set item using index notation.
|
382
|
-
|
383
|
-
Args:
|
384
|
-
indices: Path where to set
|
385
|
-
value: Value to set
|
386
|
-
"""
|
387
|
-
self.set(indices, value)
|
@@ -1,22 +0,0 @@
|
|
1
|
-
def check_networkx_available():
|
2
|
-
try:
|
3
|
-
from networkx import DiGraph # noqa: F401
|
4
|
-
|
5
|
-
return True
|
6
|
-
except Exception:
|
7
|
-
return ImportError(
|
8
|
-
"The 'networkx' package is required for this feature. "
|
9
|
-
"Please install `networkx` or `'lionagi[graph]'`."
|
10
|
-
)
|
11
|
-
|
12
|
-
|
13
|
-
def check_matplotlib_available():
|
14
|
-
try:
|
15
|
-
import matplotlib.pyplot as plt
|
16
|
-
|
17
|
-
return True
|
18
|
-
except Exception:
|
19
|
-
return ImportError(
|
20
|
-
"The 'matplotlib' package is required for this feature. "
|
21
|
-
"Please install `matplotlib` or `'lionagi[graph]'`."
|
22
|
-
)
|