lionagi 0.15.8__py3-none-any.whl → 0.15.11__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/__init__.py +4 -6
- lionagi/adapters/async_postgres_adapter.py +55 -319
- lionagi/libs/file/_utils.py +10 -0
- lionagi/libs/file/process.py +16 -13
- lionagi/libs/file/save.py +3 -2
- lionagi/libs/schema/load_pydantic_model_from_schema.py +2 -1
- lionagi/libs/unstructured/pdf_to_image.py +2 -2
- lionagi/libs/validate/string_similarity.py +4 -4
- lionagi/ln/__init__.py +38 -0
- lionagi/ln/_extract_json.py +60 -0
- lionagi/ln/_fuzzy_json.py +116 -0
- lionagi/ln/_json_dump.py +75 -0
- lionagi/ln/_models.py +0 -1
- lionagi/models/field_model.py +8 -6
- lionagi/operations/__init__.py +3 -0
- lionagi/operations/builder.py +10 -0
- lionagi/protocols/generic/element.py +56 -53
- lionagi/protocols/generic/event.py +46 -67
- lionagi/protocols/generic/pile.py +56 -1
- lionagi/protocols/generic/progression.py +11 -11
- lionagi/protocols/graph/_utils.py +22 -0
- lionagi/protocols/graph/graph.py +17 -21
- lionagi/protocols/graph/node.py +23 -5
- lionagi/protocols/messages/manager.py +41 -45
- lionagi/protocols/messages/message.py +3 -1
- lionagi/protocols/operatives/step.py +2 -19
- lionagi/protocols/types.py +1 -2
- lionagi/service/connections/providers/claude_code_.py +9 -7
- lionagi/service/third_party/claude_code.py +3 -2
- lionagi/session/session.py +14 -2
- lionagi/tools/file/reader.py +5 -6
- lionagi/utils.py +8 -385
- lionagi/version.py +1 -1
- {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/METADATA +2 -2
- {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/RECORD +37 -37
- lionagi/libs/package/__init__.py +0 -3
- lionagi/libs/package/imports.py +0 -21
- lionagi/libs/package/management.py +0 -62
- lionagi/libs/package/params.py +0 -30
- lionagi/libs/package/system.py +0 -22
- {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/WHEEL +0 -0
- {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/licenses/LICENSE +0 -0
@@ -2,14 +2,15 @@
|
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
|
5
|
+
from __future__ import annotations
|
6
|
+
|
5
7
|
import contextlib
|
6
|
-
import json
|
7
8
|
from enum import Enum
|
8
9
|
from typing import Any
|
9
10
|
|
10
11
|
from pydantic import Field, field_serializer
|
11
12
|
|
12
|
-
from lionagi
|
13
|
+
from lionagi import ln
|
13
14
|
from lionagi.utils import to_dict
|
14
15
|
|
15
16
|
from .element import Element
|
@@ -24,7 +25,7 @@ __all__ = (
|
|
24
25
|
_SIMPLE_TYPE = (str, bytes, bytearray, int, float, type(None), Enum)
|
25
26
|
|
26
27
|
|
27
|
-
class EventStatus(str, Enum):
|
28
|
+
class EventStatus(str, ln.Enum):
|
28
29
|
"""Status states for tracking action execution progress.
|
29
30
|
|
30
31
|
Attributes:
|
@@ -95,29 +96,30 @@ class Execution:
|
|
95
96
|
Returns:
|
96
97
|
dict: A dictionary representation of the execution state.
|
97
98
|
"""
|
98
|
-
res_ = Unset
|
99
|
+
res_ = ln.Unset
|
99
100
|
json_serializable = True
|
100
101
|
|
101
102
|
if not isinstance(self.response, _SIMPLE_TYPE):
|
102
103
|
json_serializable = False
|
103
104
|
try:
|
104
105
|
# check whether response is JSON serializable
|
105
|
-
|
106
|
+
ln.json_dumps(self.response)
|
106
107
|
res_ = self.response
|
107
108
|
json_serializable = True
|
108
109
|
except Exception:
|
109
110
|
with contextlib.suppress(Exception):
|
110
|
-
# attempt to convert to dict
|
111
|
+
# attempt to force convert to dict
|
111
112
|
d_ = to_dict(
|
112
113
|
self.response,
|
113
114
|
recursive=True,
|
114
115
|
recursive_python_only=False,
|
116
|
+
use_enum_values=True,
|
115
117
|
)
|
116
|
-
|
118
|
+
ln.json_dumps(d_)
|
117
119
|
res_ = d_
|
118
120
|
json_serializable = True
|
119
121
|
|
120
|
-
if res_ is Unset and not json_serializable:
|
122
|
+
if res_ is ln.Unset and not json_serializable:
|
121
123
|
res_ = "<unserializable>"
|
122
124
|
|
123
125
|
return {
|
@@ -140,93 +142,70 @@ class Event(Element):
|
|
140
142
|
|
141
143
|
@field_serializer("execution")
|
142
144
|
def _serialize_execution(self, val: Execution) -> dict:
|
143
|
-
"""Serializes the Execution object into a dictionary.
|
144
|
-
|
145
|
-
Args:
|
146
|
-
val (Execution): The Execution object to serialize.
|
147
|
-
|
148
|
-
Returns:
|
149
|
-
dict: The serialized data containing status, duration, response,
|
150
|
-
and error fields.
|
151
|
-
"""
|
145
|
+
"""Serializes the Execution object into a dictionary."""
|
152
146
|
return val.to_dict()
|
153
147
|
|
154
148
|
@property
|
155
149
|
def response(self) -> Any:
|
156
|
-
"""Gets or sets the execution response.
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
Any: The current response for this event.
|
160
|
-
"""
|
150
|
+
"""Gets or sets the execution response."""
|
161
151
|
return self.execution.response
|
162
152
|
|
163
153
|
@response.setter
|
164
154
|
def response(self, val: Any) -> None:
|
165
|
-
"""Sets the execution response.
|
166
|
-
|
167
|
-
Args:
|
168
|
-
val (Any): The new response value for this event.
|
169
|
-
"""
|
155
|
+
"""Sets the execution response."""
|
170
156
|
self.execution.response = val
|
171
157
|
|
172
158
|
@property
|
173
159
|
def status(self) -> EventStatus:
|
174
|
-
"""Gets or sets the event status.
|
175
|
-
|
176
|
-
Returns:
|
177
|
-
EventStatus: The current status of this event.
|
178
|
-
"""
|
160
|
+
"""Gets or sets the event status."""
|
179
161
|
return self.execution.status
|
180
162
|
|
181
163
|
@status.setter
|
182
|
-
def status(self, val: EventStatus) -> None:
|
183
|
-
"""Sets the event status.
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
164
|
+
def status(self, val: EventStatus | str) -> None:
|
165
|
+
"""Sets the event status."""
|
166
|
+
if isinstance(val, str):
|
167
|
+
if val not in EventStatus.allowed():
|
168
|
+
raise ValueError(f"Invalid status: {val}")
|
169
|
+
val = EventStatus(val)
|
170
|
+
if isinstance(val, EventStatus):
|
171
|
+
self.execution.status = val
|
172
|
+
else:
|
173
|
+
raise ValueError(
|
174
|
+
f"Invalid status type: {type(val)}. Expected EventStatus or str."
|
175
|
+
)
|
189
176
|
|
190
177
|
@property
|
191
178
|
def request(self) -> dict:
|
192
|
-
"""Gets the request for this event.
|
193
|
-
|
194
|
-
Returns:
|
195
|
-
dict: An empty dictionary by default. Override in subclasses
|
196
|
-
if needed.
|
197
|
-
"""
|
179
|
+
"""Gets the request for this event. Override in subclasses"""
|
198
180
|
return {}
|
199
181
|
|
200
182
|
async def invoke(self) -> None:
|
201
|
-
"""Performs the event action asynchronously.
|
202
|
-
|
203
|
-
Raises:
|
204
|
-
NotImplementedError: This base method must be overridden by
|
205
|
-
subclasses.
|
206
|
-
"""
|
183
|
+
"""Performs the event action asynchronously."""
|
207
184
|
raise NotImplementedError("Override in subclass.")
|
208
185
|
|
209
186
|
async def stream(self) -> None:
|
210
|
-
"""Performs the event action asynchronously, streaming results.
|
211
|
-
|
212
|
-
Raises:
|
213
|
-
NotImplementedError: This base method must be overridden by
|
214
|
-
subclasses.
|
215
|
-
"""
|
187
|
+
"""Performs the event action asynchronously, streaming results."""
|
216
188
|
raise NotImplementedError("Override in subclass.")
|
217
189
|
|
218
190
|
@classmethod
|
219
|
-
def from_dict(cls, data: dict) ->
|
220
|
-
"""Not implemented. Events cannot be fully recreated once done.
|
221
|
-
|
222
|
-
Args:
|
223
|
-
data (dict): Event data (unused).
|
224
|
-
|
225
|
-
Raises:
|
226
|
-
NotImplementedError: Always, because recreating an event is
|
227
|
-
disallowed.
|
228
|
-
"""
|
191
|
+
def from_dict(cls, data: dict) -> Event:
|
192
|
+
"""Not implemented. Events cannot be fully recreated once done."""
|
229
193
|
raise NotImplementedError("Cannot recreate an event once it's done.")
|
230
194
|
|
195
|
+
def as_fresh_event(self, copy_meta: bool = False) -> Event:
|
196
|
+
"""Creates a clone of this event with the same execution state."""
|
197
|
+
d_ = self.to_dict()
|
198
|
+
for i in ["execution", "created_at", "id", "metadata"]:
|
199
|
+
d_.pop(i, None)
|
200
|
+
fresh = self.__class__(**d_)
|
201
|
+
if copy_meta:
|
202
|
+
meta = self.metadata.copy()
|
203
|
+
fresh.metadata = meta
|
204
|
+
fresh.metadata["original"] = {
|
205
|
+
"id": str(self.id),
|
206
|
+
"created_at": self.created_at,
|
207
|
+
}
|
208
|
+
return fresh
|
209
|
+
|
231
210
|
|
232
211
|
# File: lionagi/protocols/generic/event.py
|
@@ -19,7 +19,7 @@ from pathlib import Path
|
|
19
19
|
from typing import Any, ClassVar, Generic, Literal, TypeVar
|
20
20
|
|
21
21
|
import pandas as pd
|
22
|
-
from pydantic import Field, field_serializer
|
22
|
+
from pydantic import Field, field_serializer
|
23
23
|
from pydantic.fields import FieldInfo
|
24
24
|
from pydapter import Adaptable, AsyncAdaptable
|
25
25
|
from typing_extensions import Self, deprecated, override
|
@@ -988,6 +988,12 @@ class Pile(Element, Collective[T], Generic[T], Adaptable, AsyncAdaptable):
|
|
988
988
|
is_same_dtype(self.collections.values())
|
989
989
|
)
|
990
990
|
|
991
|
+
@classmethod
|
992
|
+
def list_adapters(cls) -> list[str]:
|
993
|
+
syn_ = cls._adapter_registry._reg.keys()
|
994
|
+
asy_ = cls._async_registry._reg.keys()
|
995
|
+
return list(set(syn_) | set(asy_))
|
996
|
+
|
991
997
|
def adapt_to(self, obj_key: str, many=False, **kw: Any) -> Any:
|
992
998
|
"""Adapt to another format.
|
993
999
|
|
@@ -1132,6 +1138,55 @@ class Pile(Element, Collective[T], Generic[T], Adaptable, AsyncAdaptable):
|
|
1132
1138
|
) -> None:
|
1133
1139
|
return self.dump(fp, obj_key=obj_key, mode=mode, clear=clear, **kw)
|
1134
1140
|
|
1141
|
+
def filter_by_type(
|
1142
|
+
self,
|
1143
|
+
item_type: type[T] | list | set,
|
1144
|
+
strict_type: bool = False,
|
1145
|
+
as_pile: bool = False,
|
1146
|
+
reverse: bool = False,
|
1147
|
+
num_items: int | None = None,
|
1148
|
+
) -> list[T]:
|
1149
|
+
if isinstance(item_type, type):
|
1150
|
+
if is_union_type(item_type):
|
1151
|
+
item_type = set(union_members(item_type))
|
1152
|
+
else:
|
1153
|
+
item_type = {item_type}
|
1154
|
+
|
1155
|
+
if isinstance(item_type, list | tuple):
|
1156
|
+
item_type = set(item_type)
|
1157
|
+
|
1158
|
+
if not isinstance(item_type, set):
|
1159
|
+
raise TypeError("item_type must be a type or a list/set of types")
|
1160
|
+
|
1161
|
+
meth = None
|
1162
|
+
|
1163
|
+
if strict_type:
|
1164
|
+
meth = lambda item: type(item) in item_type
|
1165
|
+
else:
|
1166
|
+
meth = (
|
1167
|
+
lambda item: any(isinstance(item, t) for t in item_type)
|
1168
|
+
is True
|
1169
|
+
)
|
1170
|
+
|
1171
|
+
out = []
|
1172
|
+
prog = (
|
1173
|
+
list(self.progression)
|
1174
|
+
if not reverse
|
1175
|
+
else reversed(list(self.progression))
|
1176
|
+
)
|
1177
|
+
for i in prog:
|
1178
|
+
item = self.collections[i]
|
1179
|
+
if meth(item):
|
1180
|
+
out.append(item)
|
1181
|
+
if num_items is not None and len(out) == num_items:
|
1182
|
+
break
|
1183
|
+
|
1184
|
+
if as_pile:
|
1185
|
+
return self.__class__(
|
1186
|
+
collections=out, item_type=item_type, strict_type=strict_type
|
1187
|
+
)
|
1188
|
+
return out
|
1189
|
+
|
1135
1190
|
|
1136
1191
|
def to_list_type(value: Any, /) -> list[Any]:
|
1137
1192
|
"""Convert input to a list format"""
|
@@ -14,7 +14,7 @@ from lionagi._errors import ItemNotFoundError
|
|
14
14
|
from .._concepts import Ordering
|
15
15
|
from .element import ID, Element, IDError, IDType, validate_order
|
16
16
|
|
17
|
-
|
17
|
+
T = TypeVar("T", bound=Element)
|
18
18
|
|
19
19
|
|
20
20
|
__all__ = (
|
@@ -23,7 +23,7 @@ __all__ = (
|
|
23
23
|
)
|
24
24
|
|
25
25
|
|
26
|
-
class Progression(Element, Ordering[
|
26
|
+
class Progression(Element, Ordering[T], Generic[T]):
|
27
27
|
"""Tracks an ordered sequence of item IDs, with optional naming.
|
28
28
|
|
29
29
|
This class extends `Element` and implements `Ordering`, providing
|
@@ -39,7 +39,7 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
39
39
|
An optional human-readable identifier for the progression.
|
40
40
|
"""
|
41
41
|
|
42
|
-
order: list[ID[
|
42
|
+
order: list[ID[T].ID] = Field(
|
43
43
|
default_factory=list,
|
44
44
|
title="Order",
|
45
45
|
description="A sequence of IDs representing the progression.",
|
@@ -358,7 +358,7 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
358
358
|
raise ValueError("Can only extend with another Progression.")
|
359
359
|
self.order.extend(other.order)
|
360
360
|
|
361
|
-
def __add__(self, other: Any) -> Progression[
|
361
|
+
def __add__(self, other: Any) -> Progression[T]:
|
362
362
|
"""Returns a new Progression with IDs from both this and `other`.
|
363
363
|
|
364
364
|
Args:
|
@@ -371,7 +371,7 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
371
371
|
new_refs = validate_order(other)
|
372
372
|
return Progression(order=self.order + new_refs)
|
373
373
|
|
374
|
-
def __radd__(self, other: Any) -> Progression[
|
374
|
+
def __radd__(self, other: Any) -> Progression[T]:
|
375
375
|
"""Returns a new Progression with IDs from `other` + this.
|
376
376
|
|
377
377
|
Args:
|
@@ -396,7 +396,7 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
396
396
|
self.append(other)
|
397
397
|
return self
|
398
398
|
|
399
|
-
def __sub__(self, other: Any) -> Progression[
|
399
|
+
def __sub__(self, other: Any) -> Progression[T]:
|
400
400
|
"""Returns a new Progression excluding specified IDs.
|
401
401
|
|
402
402
|
Args:
|
@@ -435,7 +435,7 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
435
435
|
for i in reversed(item_):
|
436
436
|
self.order.insert(index, ID.get_id(i))
|
437
437
|
|
438
|
-
def
|
438
|
+
def __reversed__(self) -> Progression[T]:
|
439
439
|
"""Returns a new reversed Progression.
|
440
440
|
|
441
441
|
Returns:
|
@@ -456,19 +456,19 @@ class Progression(Element, Ordering[E], Generic[E]):
|
|
456
456
|
return NotImplemented
|
457
457
|
return (self.order == other.order) and (self.name == other.name)
|
458
458
|
|
459
|
-
def __gt__(self, other: Progression[
|
459
|
+
def __gt__(self, other: Progression[T]) -> bool:
|
460
460
|
"""Compares if this progression is "greater" by ID order."""
|
461
461
|
return self.order > other.order
|
462
462
|
|
463
|
-
def __lt__(self, other: Progression[
|
463
|
+
def __lt__(self, other: Progression[T]) -> bool:
|
464
464
|
"""Compares if this progression is "less" by ID order."""
|
465
465
|
return self.order < other.order
|
466
466
|
|
467
|
-
def __ge__(self, other: Progression[
|
467
|
+
def __ge__(self, other: Progression[T]) -> bool:
|
468
468
|
"""Compares if this progression is >= the other by ID order."""
|
469
469
|
return self.order >= other.order
|
470
470
|
|
471
|
-
def __le__(self, other: Progression[
|
471
|
+
def __le__(self, other: Progression[T]) -> bool:
|
472
472
|
"""Compares if this progression is <= the other by ID order."""
|
473
473
|
return self.order <= other.order
|
474
474
|
|
@@ -0,0 +1,22 @@
|
|
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
|
+
)
|
lionagi/protocols/graph/graph.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
4
|
|
5
5
|
from collections import deque
|
6
|
-
from typing import Any, Literal
|
6
|
+
from typing import Any, Generic, Literal, TypeVar
|
7
7
|
|
8
8
|
from pydantic import Field, field_serializer, field_validator, model_validator
|
9
9
|
from typing_extensions import Self
|
@@ -17,11 +17,17 @@ from ..generic.pile import Pile
|
|
17
17
|
from .edge import Edge
|
18
18
|
from .node import Node
|
19
19
|
|
20
|
+
T = TypeVar("T", bound=Node)
|
21
|
+
|
22
|
+
from ._utils import check_matplotlib_available, check_networkx_available
|
23
|
+
|
24
|
+
_NETWORKX_AVAILABLE = check_networkx_available()
|
25
|
+
_MATPLIB_AVAILABLE = check_matplotlib_available()
|
20
26
|
__all__ = ("Graph",)
|
21
27
|
|
22
28
|
|
23
|
-
class Graph(Element, Relational):
|
24
|
-
internal_nodes: Pile[
|
29
|
+
class Graph(Element, Relational, Generic[T]):
|
30
|
+
internal_nodes: Pile[T] = Field(
|
25
31
|
default_factory=lambda: Pile(item_type={Node}, strict_type=False),
|
26
32
|
title="Internal Nodes",
|
27
33
|
description="A collection of nodes in the graph.",
|
@@ -214,13 +220,10 @@ class Graph(Element, Relational):
|
|
214
220
|
|
215
221
|
def to_networkx(self, **kwargs) -> Any:
|
216
222
|
"""Convert the graph to a NetworkX graph object."""
|
217
|
-
|
218
|
-
|
223
|
+
if _NETWORKX_AVAILABLE is not True:
|
224
|
+
raise _NETWORKX_AVAILABLE
|
219
225
|
|
220
|
-
|
221
|
-
from lionagi.libs.package.imports import check_import
|
222
|
-
|
223
|
-
DiGraph = check_import("networkx", import_name="DiGraph")
|
226
|
+
from networkx import DiGraph # type: ignore
|
224
227
|
|
225
228
|
g = DiGraph(**kwargs)
|
226
229
|
for node in self.internal_nodes:
|
@@ -245,20 +248,13 @@ class Graph(Element, Relational):
|
|
245
248
|
**kwargs,
|
246
249
|
):
|
247
250
|
"""Display the graph using NetworkX and Matplotlib."""
|
251
|
+
g = self.to_networkx(**kwargs)
|
252
|
+
if _MATPLIB_AVAILABLE is not True:
|
253
|
+
raise _MATPLIB_AVAILABLE
|
248
254
|
|
249
|
-
|
250
|
-
|
251
|
-
import networkx as nx # type: ignore
|
252
|
-
except ImportError:
|
253
|
-
from lionagi.libs.package.imports import check_import
|
254
|
-
|
255
|
-
check_import("matplotlib")
|
256
|
-
check_import("networkx")
|
257
|
-
|
258
|
-
import matplotlib.pyplot as plt # type: ignore
|
259
|
-
import networkx as nx # type: ignore
|
255
|
+
import matplotlib.pyplot as plt # type: ignore
|
256
|
+
import networkx as nx # type: ignore
|
260
257
|
|
261
|
-
g = self.to_networkx(**kwargs)
|
262
258
|
pos = nx.spring_layout(g)
|
263
259
|
nx.draw(
|
264
260
|
g,
|
lionagi/protocols/graph/node.py
CHANGED
@@ -4,10 +4,10 @@
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
import json
|
8
7
|
from typing import Any
|
9
8
|
|
10
|
-
|
9
|
+
import orjson
|
10
|
+
from pydantic import BaseModel, field_serializer, field_validator
|
11
11
|
from pydapter import Adaptable, AsyncAdaptable
|
12
12
|
|
13
13
|
from lionagi._class_registry import LION_CLASS_REGISTRY
|
@@ -44,7 +44,7 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
|
|
44
44
|
return None
|
45
45
|
if isinstance(value, str):
|
46
46
|
try:
|
47
|
-
loaded =
|
47
|
+
loaded = orjson.loads(value)
|
48
48
|
if not isinstance(loaded, list):
|
49
49
|
raise ValueError
|
50
50
|
return [float(x) for x in loaded]
|
@@ -62,7 +62,8 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
|
|
62
62
|
async def adapt_to_async(
|
63
63
|
self, obj_key: str, many=False, **kwargs: Any
|
64
64
|
) -> Any:
|
65
|
-
kwargs["adapt_meth"] = "
|
65
|
+
kwargs["adapt_meth"] = "as_jsonable"
|
66
|
+
kwargs["mode"] = "json"
|
66
67
|
return await super().adapt_to_async(
|
67
68
|
obj_key=obj_key, many=many, **kwargs
|
68
69
|
)
|
@@ -84,7 +85,8 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
|
|
84
85
|
"""
|
85
86
|
Convert this Node to another format using a registered adapter.
|
86
87
|
"""
|
87
|
-
kwargs["adapt_meth"] = "
|
88
|
+
kwargs["adapt_meth"] = "as_jsonable"
|
89
|
+
kwargs["mode"] = "json"
|
88
90
|
return super().adapt_to(obj_key=obj_key, many=many, **kwargs)
|
89
91
|
|
90
92
|
@classmethod
|
@@ -103,6 +105,22 @@ class Node(Element, Relational, AsyncAdaptable, Adaptable):
|
|
103
105
|
kwargs["adapt_meth"] = "from_dict"
|
104
106
|
return super().adapt_from(obj, obj_key=obj_key, many=many, **kwargs)
|
105
107
|
|
108
|
+
@field_serializer("content")
|
109
|
+
def _serialize_content(self, value: Any) -> Any:
|
110
|
+
if isinstance(value, Element):
|
111
|
+
return value.to_dict()
|
112
|
+
if isinstance(value, BaseModel):
|
113
|
+
return value.model_dump()
|
114
|
+
return value
|
115
|
+
|
116
|
+
@field_validator("content", mode="before")
|
117
|
+
def _validate_content(cls, value: Any) -> Any:
|
118
|
+
if isinstance(value, dict) and "lion_class" in value.get(
|
119
|
+
"metadata", {}
|
120
|
+
):
|
121
|
+
return Element.from_dict(value)
|
122
|
+
return value
|
123
|
+
|
106
124
|
|
107
125
|
if not _ADAPATER_REGISTERED:
|
108
126
|
from pydapter.adapters import JsonAdapter, TomlAdapter
|
@@ -392,79 +392,75 @@ class MessageManager(Manager):
|
|
392
392
|
|
393
393
|
@property
|
394
394
|
def last_response(self) -> AssistantResponse | None:
|
395
|
-
"""
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
395
|
+
"""Retrieve the most recent `AssistantResponse`."""
|
396
|
+
res = self.messages.filter_by_type(
|
397
|
+
item_type=AssistantResponse,
|
398
|
+
strict_type=True,
|
399
|
+
as_pile=False,
|
400
|
+
reverse=True,
|
401
|
+
num_items=1,
|
402
|
+
)
|
403
|
+
if len(res) == 1:
|
404
|
+
return res[0]
|
401
405
|
return None
|
402
406
|
|
403
407
|
@property
|
404
408
|
def last_instruction(self) -> Instruction | None:
|
405
|
-
"""
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
409
|
+
"""Retrieve the most recent `Instruction`."""
|
410
|
+
res = self.messages.filter_by_type(
|
411
|
+
item_type=Instruction,
|
412
|
+
strict_type=True,
|
413
|
+
as_pile=False,
|
414
|
+
reverse=True,
|
415
|
+
num_items=1,
|
416
|
+
)
|
417
|
+
if len(res) == 1:
|
418
|
+
return res[0]
|
411
419
|
return None
|
412
420
|
|
413
421
|
@property
|
414
422
|
def assistant_responses(self) -> Pile[AssistantResponse]:
|
415
423
|
"""All `AssistantResponse` messages in the manager."""
|
416
|
-
return
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
if isinstance(self.messages[mid], AssistantResponse)
|
421
|
-
]
|
424
|
+
return self.messages.filter_by_type(
|
425
|
+
item_type=AssistantResponse,
|
426
|
+
strict_type=True,
|
427
|
+
as_pile=True,
|
422
428
|
)
|
423
429
|
|
424
430
|
@property
|
425
431
|
def actions(self) -> Pile[ActionRequest | ActionResponse]:
|
426
432
|
"""All action messages in the manager."""
|
427
|
-
return
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
if isinstance(
|
432
|
-
self.messages[mid], (ActionRequest, ActionResponse)
|
433
|
-
)
|
434
|
-
]
|
433
|
+
return self.messages.filter_by_type(
|
434
|
+
item_type={ActionRequest, ActionResponse},
|
435
|
+
strict_type=True,
|
436
|
+
as_pile=True,
|
435
437
|
)
|
436
438
|
|
437
439
|
@property
|
438
440
|
def action_requests(self) -> Pile[ActionRequest]:
|
439
441
|
"""All `ActionRequest` messages in the manager."""
|
440
|
-
return
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
if isinstance(self.messages[mid], ActionRequest)
|
445
|
-
]
|
442
|
+
return self.messages.filter_by_type(
|
443
|
+
item_type=ActionRequest,
|
444
|
+
strict_type=True,
|
445
|
+
as_pile=True,
|
446
446
|
)
|
447
447
|
|
448
448
|
@property
|
449
449
|
def action_responses(self) -> Pile[ActionResponse]:
|
450
450
|
"""All `ActionResponse` messages in the manager."""
|
451
|
-
return
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
if isinstance(self.messages[mid], ActionResponse)
|
456
|
-
]
|
451
|
+
return self.messages.filter_by_type(
|
452
|
+
item_type=ActionResponse,
|
453
|
+
strict_type=True,
|
454
|
+
as_pile=True,
|
457
455
|
)
|
458
456
|
|
459
457
|
@property
|
460
458
|
def instructions(self) -> Pile[Instruction]:
|
461
459
|
"""All `Instruction` messages in the manager."""
|
462
|
-
return
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
if isinstance(self.messages[mid], Instruction)
|
467
|
-
]
|
460
|
+
return self.messages.filter_by_type(
|
461
|
+
item_type=Instruction,
|
462
|
+
strict_type=True,
|
463
|
+
as_pile=True,
|
468
464
|
)
|
469
465
|
|
470
466
|
def remove_last_instruction_tool_schemas(self) -> None:
|
@@ -481,7 +477,7 @@ class MessageManager(Manager):
|
|
481
477
|
Example method to merge the content of recent ActionResponses
|
482
478
|
into an instruction's context.
|
483
479
|
"""
|
484
|
-
for i in reversed(self.messages.progression):
|
480
|
+
for i in reversed(list(self.messages.progression)):
|
485
481
|
if isinstance(self.messages[i], ActionResponse):
|
486
482
|
instruction.context.append(self.messages[i].content)
|
487
483
|
else:
|
@@ -11,6 +11,8 @@ from typing import Any
|
|
11
11
|
from jinja2 import Environment, FileSystemLoader, Template
|
12
12
|
from pydantic import Field, PrivateAttr, field_serializer
|
13
13
|
|
14
|
+
from lionagi import ln
|
15
|
+
|
14
16
|
from .._concepts import Sendable
|
15
17
|
from ..generic.element import Element, IDType
|
16
18
|
from ..generic.log import Log
|
@@ -114,7 +116,7 @@ class RoledMessage(Node, Sendable):
|
|
114
116
|
if isinstance(self.template, Template):
|
115
117
|
return self.template.render(**self.content)
|
116
118
|
except Exception:
|
117
|
-
return
|
119
|
+
return ln.json_dumps(self.content)
|
118
120
|
|
119
121
|
@classmethod
|
120
122
|
def create(cls, **kwargs):
|