lionagi 0.12.4__py3-none-any.whl → 0.12.5__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.
@@ -5,6 +5,7 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  import asyncio
8
+ import json
8
9
  import threading
9
10
  from collections import deque
10
11
  from collections.abc import (
@@ -21,18 +22,13 @@ from typing import Any, ClassVar, Generic, TypeVar
21
22
  import pandas as pd
22
23
  from pydantic import Field
23
24
  from pydantic.fields import FieldInfo
25
+ from pydapter import Adapter, AdapterRegistry
26
+ from pydapter.adapters import CsvAdapter, JsonAdapter
27
+ from pydapter.extras.excel_ import ExcelAdapter
28
+ from pydapter.extras.pandas_ import DataFrameAdapter
24
29
  from typing_extensions import Self, override
25
30
 
26
31
  from lionagi._errors import ItemExistsError, ItemNotFoundError
27
- from lionagi.adapters.types import (
28
- Adapter,
29
- AdapterRegistry,
30
- CSVFileAdapter,
31
- ExcelFileAdapter,
32
- JsonAdapter,
33
- JsonFileAdapter,
34
- PandasDataFrameAdapter,
35
- )
36
32
  from lionagi.utils import UNDEFINED, is_same_dtype, to_list
37
33
 
38
34
  from .._concepts import Observable
@@ -45,10 +41,9 @@ T = TypeVar("T", bound=E)
45
41
 
46
42
  PILE_DEFAULT_ADAPTERS = (
47
43
  JsonAdapter,
48
- JsonFileAdapter,
49
- CSVFileAdapter,
50
- ExcelFileAdapter,
51
- PandasDataFrameAdapter,
44
+ CsvAdapter,
45
+ ExcelAdapter,
46
+ DataFrameAdapter,
52
47
  )
53
48
 
54
49
 
@@ -56,8 +51,9 @@ class PileAdapterRegistry(AdapterRegistry):
56
51
  pass
57
52
 
58
53
 
54
+ pile_adapter_registry = PileAdapterRegistry()
59
55
  for i in PILE_DEFAULT_ADAPTERS:
60
- PileAdapterRegistry.register(i)
56
+ pile_adapter_registry.register(i)
61
57
 
62
58
 
63
59
  __all__ = (
@@ -117,7 +113,7 @@ class Pile(Element, Collective[E], Generic[E]):
117
113
  frozen=True,
118
114
  )
119
115
 
120
- _adapter_registry: ClassVar[AdapterRegistry] = PileAdapterRegistry
116
+ _adapter_registry: ClassVar[AdapterRegistry] = pile_adapter_registry
121
117
 
122
118
  def __pydantic_extra__(self) -> dict[str, FieldInfo]:
123
119
  return {
@@ -966,12 +962,25 @@ class Pile(Element, Collective[E], Generic[E]):
966
962
 
967
963
  def adapt_to(self, obj_key: str, /, **kwargs: Any) -> Any:
968
964
  """Convert to another format."""
969
- return self._get_adapter_registry().adapt_to(self, obj_key, **kwargs)
965
+ # For JSON adapter, we need to pass the dict representation
966
+ if obj_key in ["json", "csv", "toml"]:
967
+ data = self.to_dict()
970
968
 
971
- @classmethod
972
- def list_adapters(cls):
973
- """List available adapters."""
974
- return cls._get_adapter_registry().list_adapters()
969
+ # Create a simple object that has model_dump method
970
+ class _Wrapper:
971
+ def __init__(self, data):
972
+ self._data = data
973
+
974
+ def model_dump(self):
975
+ return self._data
976
+
977
+ wrapper = _Wrapper(data)
978
+ return self._get_adapter_registry().adapt_to(
979
+ wrapper, obj_key=obj_key, **kwargs
980
+ )
981
+ return self._get_adapter_registry().adapt_to(
982
+ self, obj_key=obj_key, **kwargs
983
+ )
975
984
 
976
985
  @classmethod
977
986
  def register_adapter(cls, adapter: type[Adapter]):
@@ -988,7 +997,7 @@ class Pile(Element, Collective[E], Generic[E]):
988
997
  def adapt_from(cls, obj: Any, obj_key: str, /, **kwargs: Any):
989
998
  """Create from another format."""
990
999
  dict_ = cls._get_adapter_registry().adapt_from(
991
- cls, obj, obj_key, **kwargs
1000
+ cls, obj, obj_key=obj_key, **kwargs
992
1001
  )
993
1002
  if isinstance(dict_, list):
994
1003
  dict_ = {"collections": dict_}
@@ -1000,11 +1009,31 @@ class Pile(Element, Collective[E], Generic[E]):
1000
1009
  **kwargs: Any,
1001
1010
  ) -> pd.DataFrame:
1002
1011
  """Convert to DataFrame."""
1003
- return self.adapt_to("pd_dataframe", columns=columns, **kwargs)
1012
+ # For DataFrame, we need to pass a list of dicts
1013
+ data = [item.to_dict() for item in self.collections.values()]
1014
+
1015
+ # Create wrapper objects for each item
1016
+ class _ItemWrapper:
1017
+ def __init__(self, data):
1018
+ self._data = data
1019
+
1020
+ def model_dump(self):
1021
+ return self._data
1022
+
1023
+ wrappers = [_ItemWrapper(d) for d in data]
1024
+ df = self._get_adapter_registry().adapt_to(
1025
+ wrappers, obj_key="pd.DataFrame", many=True, **kwargs
1026
+ )
1027
+
1028
+ if columns:
1029
+ return df[columns]
1030
+ return df
1004
1031
 
1005
1032
  def to_csv_file(self, fp: str | Path, **kwargs: Any) -> None:
1006
1033
  """Save to CSV file."""
1007
- self.adapt_to(".csv", fp=fp, **kwargs)
1034
+ # Convert to DataFrame first, then save as CSV
1035
+ df = self.to_df()
1036
+ df.to_csv(fp, index=False, **kwargs)
1008
1037
 
1009
1038
  def to_json_file(
1010
1039
  self,
@@ -1025,8 +1054,14 @@ class Pile(Element, Collective[E], Generic[E]):
1025
1054
  **kwargs: Additional arguments for json.dump() or DataFrame.to_json().
1026
1055
  """
1027
1056
  if use_pd:
1028
- return self.to_df().to_json(mode=mode, **kwargs)
1029
- return self.adapt_to(".json", fp=path_or_buf, mode=mode, many=many)
1057
+ return self.to_df().to_json(path_or_buf, mode=mode, **kwargs)
1058
+
1059
+ # Get JSON string from adapter
1060
+ json_str = self.adapt_to("json", many=many, **kwargs)
1061
+
1062
+ # Write to file
1063
+ with open(path_or_buf, mode, encoding="utf-8") as f:
1064
+ f.write(json_str)
1030
1065
 
1031
1066
 
1032
1067
  def pile(
@@ -1052,16 +1087,28 @@ def pile(
1052
1087
  """
1053
1088
 
1054
1089
  if df:
1055
- return Pile.adapt_from(df, "pd_dataframe", **kwargs)
1090
+ return Pile.adapt_from(df, "pd.DataFrame", **kwargs)
1056
1091
 
1057
1092
  if fp:
1058
1093
  fp = Path(fp)
1059
1094
  if fp.suffix == ".csv":
1060
- return Pile.adapt_from(fp, ".csv", **kwargs)
1095
+ # Read CSV to DataFrame first
1096
+ df = pd.read_csv(fp, **kwargs)
1097
+ return Pile.adapt_from(df, "pd.DataFrame")
1061
1098
  if fp.suffix == ".xlsx":
1062
- return Pile.adapt_from(fp, ".xlsx", **kwargs)
1099
+ # Read Excel to DataFrame first
1100
+ df = pd.read_excel(fp, **kwargs)
1101
+ return Pile.adapt_from(df, "pd.DataFrame")
1063
1102
  if fp.suffix in [".json", ".jsonl"]:
1064
- return Pile.adapt_from(fp, ".json", **kwargs)
1103
+ # Read JSON file
1104
+ with open(fp, encoding="utf-8") as f:
1105
+ data = json.load(f)
1106
+ if isinstance(data, dict):
1107
+ return Pile.from_dict(data)
1108
+ elif isinstance(data, list):
1109
+ return Pile.from_dict({"collections": data})
1110
+ else:
1111
+ raise ValueError(f"Invalid JSON data structure in {fp}")
1065
1112
 
1066
1113
  return Pile(
1067
1114
  collections,
@@ -6,26 +6,19 @@ import json
6
6
  from typing import Any, ClassVar
7
7
 
8
8
  from pydantic import field_validator
9
+ from pydapter import AdapterRegistry
10
+ from pydapter.adapters import JsonAdapter, TomlAdapter
11
+ from pydapter.extras.pandas_ import SeriesAdapter
9
12
 
10
13
  from lionagi._class_registry import LION_CLASS_REGISTRY
11
- from lionagi.adapters.types import (
12
- AdapterRegistry,
13
- JsonAdapter,
14
- JsonFileAdapter,
15
- PandasSeriesAdapter,
16
- TomlAdapter,
17
- TomlFileAdapter,
18
- )
19
14
 
20
15
  from .._concepts import Relational
21
16
  from ..generic.element import Element
22
17
 
23
18
  NODE_DEFAULT_ADAPTERS = (
24
19
  JsonAdapter,
25
- JsonFileAdapter,
26
- PandasSeriesAdapter,
20
+ SeriesAdapter,
27
21
  TomlAdapter,
28
- TomlFileAdapter,
29
22
  )
30
23
 
31
24
 
@@ -33,8 +26,9 @@ class NodeAdapterRegistry(AdapterRegistry):
33
26
  pass
34
27
 
35
28
 
29
+ node_adapter_registry = NodeAdapterRegistry()
36
30
  for i in NODE_DEFAULT_ADAPTERS:
37
- NodeAdapterRegistry.register(i)
31
+ node_adapter_registry.register(i)
38
32
 
39
33
  __all__ = ("Node",)
40
34
 
@@ -48,7 +42,7 @@ class Node(Element, Relational):
48
42
  - Automatic subclass registration
49
43
  """
50
44
 
51
- _adapter_registry: ClassVar[AdapterRegistry] = NodeAdapterRegistry
45
+ _adapter_registry: ClassVar[AdapterRegistry] = node_adapter_registry
52
46
 
53
47
  content: Any = None
54
48
  embedding: list[float] | None = None
@@ -88,8 +82,24 @@ class Node(Element, Relational):
88
82
  """
89
83
  Convert this Node to another format using a registered adapter.
90
84
  """
85
+ # For JSON/TOML adapters, we need to pass the dict representation
86
+ if obj_key in ["json", "toml"]:
87
+ data = self.to_dict()
88
+
89
+ # Create a simple object that has model_dump method
90
+ class _Wrapper:
91
+ def __init__(self, data):
92
+ self._data = data
93
+
94
+ def model_dump(self):
95
+ return self._data
96
+
97
+ wrapper = _Wrapper(data)
98
+ return self._get_adapter_registry().adapt_to(
99
+ wrapper, obj_key=obj_key, many=many, **kwargs
100
+ )
91
101
  return self._get_adapter_registry().adapt_to(
92
- self, obj_key, many=many, **kwargs
102
+ self, obj_key=obj_key, many=many, **kwargs
93
103
  )
94
104
 
95
105
  @classmethod
@@ -107,7 +117,7 @@ class Node(Element, Relational):
107
117
  auto-delegate to the correct subclass via from_dict.
108
118
  """
109
119
  result = cls._get_adapter_registry().adapt_from(
110
- cls, obj, obj_key, many=many, **kwargs
120
+ cls, obj, obj_key=obj_key, many=many, **kwargs
111
121
  )
112
122
  # If adapter returned multiple items, choose the first or handle as needed.
113
123
  if isinstance(result, list):
@@ -124,9 +134,5 @@ class Node(Element, Relational):
124
134
  def register_adapter(cls, adapter: Any) -> None:
125
135
  cls._get_adapter_registry().register(adapter)
126
136
 
127
- @classmethod
128
- def list_adapters(cls) -> list[str]:
129
- return cls._get_adapter_registry().list_adapters()
130
-
131
137
 
132
138
  # File: lionagi/protocols/graph/node.py
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.12.4"
1
+ __version__ = "0.12.5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.12.4
3
+ Version: 0.12.5
4
4
  Summary: An Intelligence Operating System.
5
5
  Author-email: HaiyangLi <quantocean.li@gmail.com>, Liangbingyan Luo <llby_luo@outlook.com>
6
6
  License: Apache License
@@ -223,10 +223,9 @@ Requires-Dist: aiocache>=0.12.0
223
223
  Requires-Dist: aiohttp>=3.12.0
224
224
  Requires-Dist: backoff>=2.2.1
225
225
  Requires-Dist: jinja2>=3.1.0
226
- Requires-Dist: pandas>=2.0.0
227
226
  Requires-Dist: pillow>=11.0.0
228
227
  Requires-Dist: pydantic-settings>=2.8.0
229
- Requires-Dist: pydantic>2.0.0
228
+ Requires-Dist: pydapter[pandas]>=0.3.0
230
229
  Requires-Dist: python-dotenv>=1.1.0
231
230
  Requires-Dist: tiktoken>=0.8.0
232
231
  Requires-Dist: toml>=0.9.0
@@ -6,17 +6,7 @@ lionagi/config.py,sha256=dAhDFKtaaSfn6WT9dwX9Vd4TWWs6-Su1FgYIrFgYcgc,3709
6
6
  lionagi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  lionagi/settings.py,sha256=W52mM34E6jXF3GyqCFzVREKZrmnUqtZm_BVDsUiDI_s,1627
8
8
  lionagi/utils.py,sha256=uLTJKl7aTnFXV6ehA6zwiwEB7G2nQYKsO2pZ6mqFzUk,78908
9
- lionagi/version.py,sha256=DoMS9KOhsApLyuLYhLEsd5nmoLFQ_IvVkEs_jKRzFk8,23
10
- lionagi/adapters/__init__.py,sha256=FJBV1Fb7GR9mcRApEB9bNP3IRMQ9Qjg5aVTouZFyTBU,45
11
- lionagi/adapters/adapter.py,sha256=aW7s1OKAdxHd8HBv2UcThn-r2Q08EyArssNyFobMLuA,3357
12
- lionagi/adapters/json_adapter.py,sha256=EJj0Jev46ZhU3ZMnlYwyzN2rLxjLCVrMDpHkEuggBvk,4561
13
- lionagi/adapters/toml_adapter.py,sha256=XOx0Q41g9FoNVuGrce96ck3gxPo4G-3mwqSin5kGq9s,5441
14
- lionagi/adapters/types.py,sha256=CHfB39BSeyU11SDkXXkU_vzqy4v7kaR-2r0y6DYkhXc,660
15
- lionagi/adapters/pandas_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- lionagi/adapters/pandas_/csv_adapter.py,sha256=HWie6Jlt8nR-EVJC_zmCFilaWszLNuk7EWUp8Xvr-H8,2358
17
- lionagi/adapters/pandas_/excel_adapter.py,sha256=ZqRT2xF93NLKNyMp16ePNzwUN3ntNkUy1dO3nbsrfak,2287
18
- lionagi/adapters/pandas_/pd_dataframe_adapter.py,sha256=ULGZVhK5aaOuTrmFq4x5SiuDScYetyYYUHeL8Hh13Eg,2279
19
- lionagi/adapters/pandas_/pd_series_adapter.py,sha256=TX3cqFtgEip8JqVqkjdJYOu4PQGpW1yYU6POhvz8Jeg,1388
9
+ lionagi/version.py,sha256=QFQfu3CUVe9Ncr8kv3aaBY3oWrZmv8xboen_Uwy7eXU,23
20
10
  lionagi/fields/__init__.py,sha256=8oU7Vfk-fKiULFKqhM6VpJMqdZcVXPTM7twVfNDN_SQ,603
21
11
  lionagi/fields/action.py,sha256=iWSApCM77jS0Oc28lb7G601Etkp-yjx5U1hfI_FQgfA,5792
22
12
  lionagi/fields/base.py,sha256=5CJc7j8kTTWzXwpYzkSAFzx4BglABfx3AElIATKB7bg,3857
@@ -140,13 +130,13 @@ lionagi/protocols/generic/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGv
140
130
  lionagi/protocols/generic/element.py,sha256=Eaij2YpTWsGk28Tqjazmjmc_tOnalH7_iGFZrL6QJb4,14420
141
131
  lionagi/protocols/generic/event.py,sha256=InjBd2K9aSYxgai1c20d4jaJOkEx5VGFfb7iZbiMiNA,5200
142
132
  lionagi/protocols/generic/log.py,sha256=vepclOaY3fdR1QgFDj9usOffsx9T-9PbgwXjTvm6twQ,7441
143
- lionagi/protocols/generic/pile.py,sha256=0nAfmCb_0RjiSQPvW6tkzuHUk9aPHFvCySlL-TkokFI,31803
133
+ lionagi/protocols/generic/pile.py,sha256=L965LgVKhaxpEe_5dOwjYFZ1UAGeWmUtZ4Tf0MRznHw,33562
144
134
  lionagi/protocols/generic/processor.py,sha256=LTF9Enb-9jj5Wuy_jLanAhiesc5-Gm6WiSntZoAAm8w,10382
145
135
  lionagi/protocols/generic/progression.py,sha256=qlITq1qzV119iR5qR__fBAzV489S7d4t20E8uDRicEw,15189
146
136
  lionagi/protocols/graph/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
147
137
  lionagi/protocols/graph/edge.py,sha256=lgxeceoazM4Jl2Un_owLpJOanqw2m4XPk3-dXGp1u8g,5229
148
138
  lionagi/protocols/graph/graph.py,sha256=ks3zJWdwycUW_3Q4dz3f_ZLYhOALpg76t5J3HEzDcy4,10237
149
- lionagi/protocols/graph/node.py,sha256=cklw0PJcWPmFW8jGaZGT5enJDEkqulxRHgdMjRDKCl4,3799
139
+ lionagi/protocols/graph/node.py,sha256=_bQ9h_s_X6dVyV4pueEfiKiFapOkDL-HuH2MboUekYI,4250
150
140
  lionagi/protocols/mail/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
151
141
  lionagi/protocols/mail/exchange.py,sha256=P1PcrFylIBeiQa8kox9H1qyJ4kjhUlbLiTUT8rs1OXg,7041
152
142
  lionagi/protocols/mail/mail.py,sha256=RB5CUft_4J85H9nM9g6aRXomTaqKwF5xVjJacPAhoa8,1356
@@ -207,7 +197,7 @@ lionagi/tools/types.py,sha256=XtJLY0m-Yi_ZLWhm0KycayvqMCZd--HxfQ0x9vFUYDE,230
207
197
  lionagi/tools/file/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
208
198
  lionagi/tools/file/reader.py,sha256=0TdnfVGVCKuM58MmGM-NyVjhU9BFoitkNYEepdc0z_Y,9529
209
199
  lionagi/tools/memory/tools.py,sha256=zTGBenVsF8Wuh303kWntmQSGlAFKonHNdh5ePuQ26KE,15948
210
- lionagi-0.12.4.dist-info/METADATA,sha256=qWskGXtFjTqx7LFfsjeVdiFFSgV4CerJiJTrhyhaxgk,18980
211
- lionagi-0.12.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
212
- lionagi-0.12.4.dist-info/licenses/LICENSE,sha256=VXFWsdoN5AAknBCgFqQNgPWYx7OPp-PFEP961zGdOjc,11288
213
- lionagi-0.12.4.dist-info/RECORD,,
200
+ lionagi-0.12.5.dist-info/METADATA,sha256=0vHIkYR3nyKPKxuivRY8XjNiVBk0UP0KvEbY2cxscEY,18960
201
+ lionagi-0.12.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
202
+ lionagi-0.12.5.dist-info/licenses/LICENSE,sha256=VXFWsdoN5AAknBCgFqQNgPWYx7OPp-PFEP961zGdOjc,11288
203
+ lionagi-0.12.5.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- """deprecated, will be removed in v0.13.0"""
@@ -1,120 +0,0 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
3
- # SPDX-License-Identifier: Apache-2.0
4
-
5
- """
6
- Defines the `Adapter` protocol (a formal interface), along with the
7
- `AdapterRegistry` that maps string/file extensions or object keys to
8
- specific adapter implementations.
9
- """
10
-
11
- import logging
12
- from typing import Any, Protocol, TypeVar, runtime_checkable
13
-
14
- from typing_extensions import get_protocol_members
15
-
16
- T = TypeVar("T")
17
-
18
- __all__ = (
19
- "Adapter",
20
- "ADAPTER_MEMBERS",
21
- "AdapterRegistry",
22
- )
23
-
24
-
25
- @runtime_checkable
26
- class Adapter(Protocol):
27
- """
28
- Describes a two-way converter that knows how to transform an object
29
- from an external representation to an internal format, and vice versa.
30
-
31
- Attributes
32
- ----------
33
- obj_key : str
34
- A unique key or extension that identifies what format this
35
- adapter supports (e.g. ".csv", "json", "pd_dataframe").
36
-
37
- Methods
38
- -------
39
- from_obj(subj_cls: type[T], obj: Any, /, many: bool, **kwargs) -> dict|list[dict]
40
- Converts a raw external object (file contents, JSON string, etc.)
41
- into a dictionary or list of dictionaries.
42
- to_obj(subj: T, /, many: bool, **kwargs) -> Any
43
- Converts an internal object (e.g., a Pydantic-based model)
44
- into the target format (file, JSON, DataFrame, etc.).
45
- """
46
-
47
- obj_key: str
48
-
49
- @classmethod
50
- def from_obj(
51
- cls,
52
- subj_cls: type[T],
53
- obj: Any,
54
- /,
55
- *,
56
- many: bool,
57
- **kwargs,
58
- ) -> dict | list[dict]: ...
59
-
60
- @classmethod
61
- def to_obj(
62
- cls,
63
- subj: T,
64
- /,
65
- *,
66
- many: bool,
67
- **kwargs,
68
- ) -> Any: ...
69
-
70
-
71
- ADAPTER_MEMBERS = get_protocol_members(Adapter) # duck typing
72
-
73
-
74
- class AdapterRegistry:
75
-
76
- _adapters: dict[str, Adapter] = {}
77
-
78
- @classmethod
79
- def list_adapters(cls) -> list[tuple[str | type, ...]]:
80
- return list(cls._adapters.keys())
81
-
82
- @classmethod
83
- def register(cls, adapter: type[Adapter]) -> None:
84
- for member in ADAPTER_MEMBERS:
85
- if not hasattr(adapter, member):
86
- _str = getattr(adapter, "obj_key", None) or repr(adapter)
87
- _str = _str[:50] if len(_str) > 50 else _str
88
- raise AttributeError(
89
- f"Adapter {_str} missing required methods."
90
- )
91
-
92
- if isinstance(adapter, type):
93
- cls._adapters[adapter.obj_key] = adapter()
94
- else:
95
- cls._adapters[adapter.obj_key] = adapter
96
-
97
- @classmethod
98
- def get(cls, obj_key: type | str) -> Adapter:
99
- try:
100
- return cls._adapters[obj_key]
101
- except Exception as e:
102
- logging.error(f"Error getting adapter for {obj_key}. Error: {e}")
103
-
104
- @classmethod
105
- def adapt_from(
106
- cls, subj_cls: type[T], obj: Any, obj_key: type | str, **kwargs
107
- ) -> dict | list[dict]:
108
- try:
109
- return cls.get(obj_key).from_obj(subj_cls, obj, **kwargs)
110
- except Exception as e:
111
- logging.error(f"Error adapting data from {obj_key}. Error: {e}")
112
- raise e
113
-
114
- @classmethod
115
- def adapt_to(cls, subj: T, obj_key: type | str, **kwargs) -> Any:
116
- try:
117
- return cls.get(obj_key).to_obj(subj, **kwargs)
118
- except Exception as e:
119
- logging.error(f"Error adapting data to {obj_key}. Error: {e}")
120
- raise e
@@ -1,181 +0,0 @@
1
- """
2
- Implements two adapters:
3
- - `JsonAdapter` for in-memory JSON strings
4
- - `JsonFileAdapter` for reading/writing JSON files
5
- """
6
-
7
- import json
8
- import logging
9
- from pathlib import Path
10
-
11
- from lionagi.protocols._concepts import Collective
12
-
13
- from .adapter import Adapter, T
14
-
15
-
16
- class JsonAdapter(Adapter):
17
- """
18
- Adapter that converts to/from JSON **strings** in memory.
19
- Example usage: taking a Python dictionary and making JSON,
20
- or parsing JSON string to a dict.
21
- """
22
-
23
- obj_key = "json"
24
-
25
- @classmethod
26
- def from_obj(
27
- cls,
28
- subj_cls: type[T],
29
- obj: str,
30
- /,
31
- *,
32
- many: bool = False,
33
- **kwargs,
34
- ) -> dict | list[dict]:
35
- """
36
- Convert a JSON string into a dict or list of dicts.
37
-
38
- Parameters
39
- ----------
40
- subj_cls : type[T]
41
- The target class for context (not always used).
42
- obj : str
43
- The JSON string.
44
- many : bool, optional
45
- If True, expects a JSON array (returns list[dict]).
46
- Otherwise returns a single dict or the first element.
47
- **kwargs
48
- Extra arguments for json.loads().
49
-
50
- Returns
51
- -------
52
- dict | list[dict]
53
- The loaded JSON data.
54
- """
55
- result = json.loads(obj, **kwargs)
56
- if many:
57
- return result if isinstance(result, list) else [result]
58
- if isinstance(result, list) and len(result) > 0:
59
- return result[0]
60
- return result
61
-
62
- @classmethod
63
- def to_obj(
64
- cls,
65
- subj: T,
66
- *,
67
- many: bool = False,
68
- **kwargs,
69
- ) -> str:
70
- """
71
- Convert an object (or collection) to a JSON string.
72
-
73
- Parameters
74
- ----------
75
- subj : T
76
- The object to serialize.
77
- many : bool, optional
78
- If True, convert multiple items to a JSON array.
79
- **kwargs
80
- Extra arguments for json.dumps().
81
-
82
- Returns
83
- -------
84
- str
85
- The resulting JSON string.
86
- """
87
- if many:
88
- if isinstance(subj, Collective):
89
- data = [i.to_dict() for i in subj]
90
- else:
91
- data = [subj.to_dict()]
92
- return json.dumps(data, **kwargs)
93
- return json.dumps(subj.to_dict(), **kwargs)
94
-
95
-
96
- class JsonFileAdapter(Adapter):
97
- """
98
- Adapter that reads/writes JSON data to/from a file on disk.
99
- The file extension key is ".json".
100
- """
101
-
102
- obj_key = ".json"
103
-
104
- @classmethod
105
- def from_obj(
106
- cls,
107
- subj_cls: type[T],
108
- obj: str | Path,
109
- /,
110
- *,
111
- many: bool = False,
112
- **kwargs,
113
- ) -> dict | list[dict]:
114
- """
115
- Read a JSON file from disk and return a dict or list of dicts.
116
-
117
- Parameters
118
- ----------
119
- subj_cls : type[T]
120
- The target class for context.
121
- obj : str | Path
122
- The JSON file path.
123
- many : bool
124
- If True, expects a list. Otherwise single dict or first element.
125
- **kwargs
126
- Extra arguments for json.load().
127
-
128
- Returns
129
- -------
130
- dict | list[dict]
131
- The loaded data from file.
132
- """
133
- with open(obj, encoding="utf-8") as f:
134
- result = json.load(f, **kwargs)
135
- if many:
136
- return result if isinstance(result, list) else [result]
137
- if isinstance(result, list) and len(result) > 0:
138
- return result[0]
139
- return result
140
-
141
- @classmethod
142
- def to_obj(
143
- cls,
144
- subj: T,
145
- /,
146
- *,
147
- fp: str | Path,
148
- many: bool = False,
149
- mode: str = "w",
150
- **kwargs,
151
- ) -> None:
152
- """
153
- Write a dict (or list) to a JSON file.
154
-
155
- Parameters
156
- ----------
157
- subj : T
158
- The object/collection to serialize.
159
- fp : str | Path
160
- The file path to write.
161
- many : bool
162
- If True, write as a JSON array of multiple items.
163
- **kwargs
164
- Extra arguments for json.dump().
165
-
166
- Returns
167
- -------
168
- None
169
- """
170
- with open(fp, mode, encoding="utf-8") as f:
171
- if many:
172
- if isinstance(subj, Collective):
173
- json.dump([i.to_dict() for i in subj], f, **kwargs)
174
- else:
175
- json.dump([subj.to_dict()], f, **kwargs)
176
- else:
177
- json.dump(subj.to_dict(), f, **kwargs)
178
- logging.info(f"JSON data saved to {fp}")
179
-
180
-
181
- # File: lionagi/protocols/adapters/json_adapter.py
File without changes
@@ -1,94 +0,0 @@
1
- import logging
2
- from pathlib import Path
3
-
4
- import pandas as pd
5
-
6
- from lionagi.protocols._concepts import Collective
7
-
8
- from ..adapter import Adapter, T
9
-
10
-
11
- class CSVFileAdapter(Adapter):
12
- """
13
- Reads/writes CSV files to a list of dicts or vice versa,
14
- using `pandas`.
15
- """
16
-
17
- obj_key = ".csv"
18
-
19
- @classmethod
20
- def from_obj(
21
- cls,
22
- subj_cls: type[T],
23
- obj: str | Path,
24
- /,
25
- *,
26
- many: bool = False,
27
- **kwargs,
28
- ) -> list[dict]:
29
- """
30
- Read a CSV file into a list of dictionaries.
31
-
32
- Parameters
33
- ----------
34
- subj_cls : type[T]
35
- The target class for context (not used).
36
- obj : str | Path
37
- The CSV file path.
38
- many : bool, optional
39
- If True, returns list[dict]; if False, returns only
40
- the first dict.
41
- **kwargs
42
- Additional options for `pd.read_csv`.
43
-
44
- Returns
45
- -------
46
- list[dict]
47
- The parsed CSV data as a list of row dictionaries.
48
- """
49
- df: pd.DataFrame = pd.read_csv(obj, **kwargs)
50
- dicts_ = df.to_dict(orient="records")
51
- if many:
52
- return dicts_
53
- return dicts_[0] if len(dicts_) > 0 else {}
54
-
55
- @classmethod
56
- def to_obj(
57
- cls,
58
- subj: T,
59
- /,
60
- *,
61
- fp: str | Path,
62
- many: bool = False,
63
- **kwargs,
64
- ) -> None:
65
- """
66
- Write an object's data to a CSV file.
67
-
68
- Parameters
69
- ----------
70
- subj : T
71
- The item(s) to convert. If `many=True`, can be a Collective.
72
- fp : str | Path
73
- File path to write the CSV.
74
- many : bool
75
- If True, we assume a collection of items, else a single item.
76
- **kwargs
77
- Extra params for `DataFrame.to_csv`.
78
-
79
- Returns
80
- -------
81
- None
82
- """
83
- kwargs["index"] = False # By default, do not save index
84
- if many:
85
- if isinstance(subj, Collective):
86
- pd.DataFrame([i.to_dict() for i in subj]).to_csv(fp, **kwargs)
87
- else:
88
- pd.DataFrame([subj.to_dict()]).to_csv(fp, **kwargs)
89
- else:
90
- pd.DataFrame([subj.to_dict()]).to_csv(fp, **kwargs)
91
- logging.info(f"CSV data saved to {fp}")
92
-
93
-
94
- # File: lionagi/protocols/adapters/pandas_/csv_adapter.py
@@ -1,94 +0,0 @@
1
- """
2
- Provides an ExcelFileAdapter for reading/writing Excel (.xlsx) files
3
- via pandas.
4
- """
5
-
6
- import logging
7
- from pathlib import Path
8
-
9
- import pandas as pd
10
-
11
- from lionagi.protocols._concepts import Collective
12
-
13
- from ..adapter import Adapter, T
14
-
15
-
16
- class ExcelFileAdapter(Adapter):
17
- """
18
- Reads/writes Excel (XLSX) files, using `pandas`.
19
- """
20
-
21
- obj_key = ".xlsx"
22
-
23
- @classmethod
24
- def from_obj(
25
- cls,
26
- subj_cls: type[T],
27
- obj: str | Path,
28
- /,
29
- *,
30
- many: bool = False,
31
- **kwargs,
32
- ) -> list[dict]:
33
- """
34
- Read an Excel file into a list of dictionaries.
35
-
36
- Parameters
37
- ----------
38
- subj_cls : type[T]
39
- Target class for context.
40
- obj : str | Path
41
- The Excel file path.
42
- many : bool, optional
43
- If True, returns list[dict]. If False, returns single dict or first element.
44
- **kwargs
45
- Additional options for `pd.read_excel`.
46
-
47
- Returns
48
- -------
49
- list[dict]
50
- """
51
- df: pd.DataFrame = pd.read_excel(obj, **kwargs)
52
- dicts_ = df.to_dict(orient="records")
53
- if many:
54
- return dicts_
55
- return dicts_[0] if len(dicts_) > 0 else {}
56
-
57
- @classmethod
58
- def to_obj(
59
- cls,
60
- subj: T,
61
- /,
62
- *,
63
- fp: str | Path,
64
- many: bool = False,
65
- **kwargs,
66
- ) -> None:
67
- """
68
- Write data to an Excel file.
69
-
70
- Parameters
71
- ----------
72
- subj : T
73
- The object(s) to convert to Excel rows.
74
- fp : str | Path
75
- Path to save the XLSX file.
76
- many : bool
77
- If True, writes multiple items (e.g., a Collective).
78
- **kwargs
79
- Extra parameters for `DataFrame.to_excel`.
80
- """
81
- kwargs["index"] = False
82
- if many:
83
- if isinstance(subj, Collective):
84
- pd.DataFrame([i.to_dict() for i in subj]).to_excel(
85
- fp, **kwargs
86
- )
87
- else:
88
- pd.DataFrame([subj.to_dict()]).to_excel(fp, **kwargs)
89
- else:
90
- pd.DataFrame([subj.to_dict()]).to_excel(fp, **kwargs)
91
- logging.info(f"Excel data saved to {fp}")
92
-
93
-
94
- # File: lionagi/protocols/adapters/pandas_/excel_adapter.py
@@ -1,81 +0,0 @@
1
- """
2
- Defines a `PandasDataFrameAdapter` that converts between
3
- a DataFrame and a list of dictionary-based elements.
4
- """
5
-
6
- from datetime import datetime
7
-
8
- import pandas as pd
9
-
10
- from ..adapter import Adapter, T
11
-
12
-
13
- class PandasDataFrameAdapter(Adapter):
14
- """
15
- Converts a set of objects to a single `pd.DataFrame`, or
16
- a DataFrame to a list of dictionaries. Typically used in memory,
17
- not for saving to file.
18
- """
19
-
20
- obj_key = "pd_dataframe"
21
- alias = ("pandas_dataframe", "pd.DataFrame", "pd_dataframe")
22
-
23
- @classmethod
24
- def from_obj(
25
- cls, subj_cls: type[T], obj: pd.DataFrame, /, **kwargs
26
- ) -> list[dict]:
27
- """
28
- Convert an existing DataFrame into a list of dicts.
29
-
30
- Parameters
31
- ----------
32
- subj_cls : type[T]
33
- The internal class to which we might parse.
34
- obj : pd.DataFrame
35
- The DataFrame to convert.
36
- **kwargs
37
- Additional args for DataFrame.to_dict (like `orient`).
38
-
39
- Returns
40
- -------
41
- list[dict]
42
- Each row as a dictionary.
43
- """
44
- return obj.to_dict(orient="records", **kwargs)
45
-
46
- @classmethod
47
- def to_obj(cls, subj: list[T], /, **kwargs) -> pd.DataFrame:
48
- """
49
- Convert multiple items into a DataFrame, adjusting `created_at` to datetime.
50
-
51
- Parameters
52
- ----------
53
- subj : list[T]
54
- The items to convert. Each item must have `to_dict()`.
55
- **kwargs
56
- Additional arguments for `pd.DataFrame(...)`.
57
-
58
- Returns
59
- -------
60
- pd.DataFrame
61
- The resulting DataFrame.
62
- """
63
- out_ = []
64
- for i in subj:
65
- _dict = i.to_dict()
66
- # Attempt to parse timestamps
67
- if "created_at" in _dict:
68
- try:
69
- _dict["created_at"] = datetime.fromtimestamp(
70
- _dict["created_at"]
71
- )
72
- except Exception:
73
- pass
74
- out_.append(_dict)
75
- df = pd.DataFrame(out_, **kwargs)
76
- # Convert created_at to datetime if present
77
- if "created_at" in df.columns:
78
- df["created_at"] = pd.to_datetime(
79
- df["created_at"], errors="coerce"
80
- )
81
- return df
@@ -1,57 +0,0 @@
1
- """
2
- Defines a `PandasSeriesAdapter` that converts a single object
3
- to/from a `pd.Series`.
4
- """
5
-
6
- import pandas as pd
7
-
8
- from ..adapter import Adapter, T
9
-
10
-
11
- class PandasSeriesAdapter(Adapter):
12
- """
13
- Converts a single item to a Pandas Series and vice versa.
14
- Great for 1-row data or simpler key-value pairs.
15
- """
16
-
17
- obj_key = "pd_series"
18
- alias = ("pandas_series", "pd.series", "pd_series")
19
-
20
- @classmethod
21
- def from_obj(cls, subj_cls: type[T], obj: pd.Series, /, **kwargs) -> dict:
22
- """
23
- Convert a Pandas Series into a dictionary.
24
-
25
- Parameters
26
- ----------
27
- subj_cls : type[T]
28
- Possibly the class we might use to rehydrate the item.
29
- obj : pd.Series
30
- The series to interpret.
31
- **kwargs
32
- Additional arguments for `Series.to_dict`.
33
-
34
- Returns
35
- -------
36
- dict
37
- The data from the Series as a dictionary.
38
- """
39
- return obj.to_dict(**kwargs)
40
-
41
- @classmethod
42
- def to_obj(cls, subj: T, /, **kwargs) -> pd.Series:
43
- """
44
- Convert a single item to a Series.
45
-
46
- Parameters
47
- ----------
48
- subj : T
49
- The item, which must have `to_dict()`.
50
- **kwargs
51
- Extra args passed to `pd.Series`.
52
-
53
- Returns
54
- -------
55
- pd.Series
56
- """
57
- return pd.Series(subj.to_dict(), **kwargs)
@@ -1,204 +0,0 @@
1
- """
2
- Implements two adapters:
3
- - `TomlAdapter` for in-memory TOML strings
4
- - `TomlFileAdapter` for reading/writing TOML files
5
- """
6
-
7
- import logging
8
- from pathlib import Path
9
-
10
- import toml
11
-
12
- from lionagi.protocols._concepts import Collective
13
-
14
- from .adapter import Adapter, T
15
-
16
-
17
- class TomlAdapter(Adapter):
18
- """
19
- Adapter that converts to/from TOML **strings** in memory.
20
- Example usage: taking a Python dictionary and making TOML,
21
- or parsing TOML string to a dict.
22
- """
23
-
24
- obj_key = "toml"
25
-
26
- @classmethod
27
- def from_obj(
28
- cls,
29
- subj_cls: type[T],
30
- obj: str,
31
- /,
32
- *,
33
- many: bool = False,
34
- **kwargs,
35
- ) -> dict | list[dict]:
36
- """
37
- Convert a TOML string into a dict or list of dicts.
38
-
39
- Parameters
40
- ----------
41
- subj_cls : type[T]
42
- The target class for context (not always used).
43
- obj : str
44
- The TOML string.
45
- many : bool, optional
46
- If True, expects a TOML array of tables (returns list[dict]).
47
- Otherwise returns a single dict.
48
- **kwargs
49
- Extra arguments for toml.loads().
50
-
51
- Returns
52
- -------
53
- dict | list[dict]
54
- The loaded TOML data.
55
- """
56
- result = toml.loads(obj, **kwargs)
57
-
58
- # Handle array of tables in TOML for "many" case
59
- if many:
60
- # Check if there's a top-level array key that might hold multiple items
61
- for key, value in result.items():
62
- if isinstance(value, list) and all(
63
- isinstance(item, dict) for item in value
64
- ):
65
- return value
66
- # If no array of tables found, wrap the result in a list
67
- return [result]
68
-
69
- return result
70
-
71
- @classmethod
72
- def to_obj(
73
- cls,
74
- subj: T,
75
- *,
76
- many: bool = False,
77
- **kwargs,
78
- ) -> str:
79
- """
80
- Convert an object (or collection) to a TOML string.
81
-
82
- Parameters
83
- ----------
84
- subj : T
85
- The object to serialize.
86
- many : bool, optional
87
- If True, convert multiple items to a TOML array of tables.
88
- **kwargs
89
- Extra arguments for toml.dumps().
90
-
91
- Returns
92
- -------
93
- str
94
- The resulting TOML string.
95
- """
96
- if many:
97
- if isinstance(subj, Collective):
98
- # For multiple items, create a wrapper dict with an array of items
99
- data = {"items": [i.to_dict() for i in subj]}
100
- else:
101
- data = {"items": [subj.to_dict()]}
102
- return toml.dumps(data, **kwargs)
103
-
104
- return toml.dumps(subj.to_dict(), **kwargs)
105
-
106
-
107
- class TomlFileAdapter(Adapter):
108
- """
109
- Adapter that reads/writes TOML data to/from a file on disk.
110
- The file extension key is ".toml".
111
- """
112
-
113
- obj_key = ".toml"
114
-
115
- @classmethod
116
- def from_obj(
117
- cls,
118
- subj_cls: type[T],
119
- obj: str | Path,
120
- /,
121
- *,
122
- many: bool = False,
123
- **kwargs,
124
- ) -> dict | list[dict]:
125
- """
126
- Read a TOML file from disk and return a dict or list of dicts.
127
-
128
- Parameters
129
- ----------
130
- subj_cls : type[T]
131
- The target class for context.
132
- obj : str | Path
133
- The TOML file path.
134
- many : bool
135
- If True, expects an array of tables. Otherwise single dict.
136
- **kwargs
137
- Extra arguments for toml.load().
138
-
139
- Returns
140
- -------
141
- dict | list[dict]
142
- The loaded data from file.
143
- """
144
- with open(obj, encoding="utf-8") as f:
145
- result = toml.load(f, **kwargs)
146
-
147
- # Handle array of tables in TOML for "many" case
148
- if many:
149
- # Check if there's a top-level array key that might hold multiple items
150
- for key, value in result.items():
151
- if isinstance(value, list) and all(
152
- isinstance(item, dict) for item in value
153
- ):
154
- return value
155
- # If no array of tables found, wrap the result in a list
156
- return [result]
157
-
158
- return result
159
-
160
- @classmethod
161
- def to_obj(
162
- cls,
163
- subj: T,
164
- /,
165
- *,
166
- fp: str | Path,
167
- many: bool = False,
168
- mode: str = "w",
169
- **kwargs,
170
- ) -> None:
171
- """
172
- Write a dict (or list) to a TOML file.
173
-
174
- Parameters
175
- ----------
176
- subj : T
177
- The object/collection to serialize.
178
- fp : str | Path
179
- The file path to write.
180
- many : bool
181
- If True, write as a TOML array of tables of multiple items.
182
- mode : str
183
- File open mode, defaults to write ("w").
184
- **kwargs
185
- Extra arguments for toml.dump().
186
-
187
- Returns
188
- -------
189
- None
190
- """
191
- with open(fp, mode, encoding="utf-8") as f:
192
- if many:
193
- if isinstance(subj, Collective):
194
- # TOML requires arrays of tables to be in a table
195
- data = {"items": [i.to_dict() for i in subj]}
196
- else:
197
- data = {"items": [subj.to_dict()]}
198
- toml.dump(data, f, **kwargs)
199
- else:
200
- toml.dump(subj.to_dict(), f, **kwargs)
201
- logging.info(f"TOML data saved to {fp}")
202
-
203
-
204
- # File: lionagi/protocols/adapters/toml_adapter.py
lionagi/adapters/types.py DELETED
@@ -1,21 +0,0 @@
1
- from .adapter import ADAPTER_MEMBERS, Adapter, AdapterRegistry
2
- from .json_adapter import JsonAdapter, JsonFileAdapter
3
- from .pandas_.csv_adapter import CSVFileAdapter
4
- from .pandas_.excel_adapter import ExcelFileAdapter
5
- from .pandas_.pd_dataframe_adapter import PandasDataFrameAdapter
6
- from .pandas_.pd_series_adapter import PandasSeriesAdapter
7
- from .toml_adapter import TomlAdapter, TomlFileAdapter
8
-
9
- __all__ = (
10
- "Adapter",
11
- "AdapterRegistry",
12
- "ADAPTER_MEMBERS",
13
- "JsonAdapter",
14
- "JsonFileAdapter",
15
- "CSVFileAdapter",
16
- "PandasSeriesAdapter",
17
- "PandasDataFrameAdapter",
18
- "ExcelFileAdapter",
19
- "TomlAdapter",
20
- "TomlFileAdapter",
21
- )