lionagi 0.0.315__py3-none-any.whl → 0.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- lionagi/core/__init__.py +19 -8
- lionagi/core/agent/__init__.py +0 -3
- lionagi/core/agent/base_agent.py +26 -30
- lionagi/core/branch/__init__.py +0 -4
- lionagi/core/branch/{base_branch.py → base.py} +13 -14
- lionagi/core/branch/branch.py +22 -20
- lionagi/core/branch/executable_branch.py +0 -347
- lionagi/core/branch/{branch_flow_mixin.py → flow_mixin.py} +6 -6
- lionagi/core/branch/util.py +1 -1
- lionagi/core/direct/__init__.py +13 -1
- lionagi/core/direct/cot.py +123 -1
- lionagi/core/direct/plan.py +164 -0
- lionagi/core/direct/predict.py +13 -9
- lionagi/core/direct/react.py +12 -8
- lionagi/core/direct/score.py +4 -4
- lionagi/core/direct/select.py +4 -4
- lionagi/core/direct/utils.py +23 -0
- lionagi/core/direct/vote.py +2 -2
- lionagi/core/execute/base_executor.py +50 -0
- lionagi/core/execute/branch_executor.py +233 -0
- lionagi/core/execute/instruction_map_executor.py +131 -0
- lionagi/core/execute/structure_executor.py +218 -0
- lionagi/core/flow/monoflow/ReAct.py +4 -4
- lionagi/core/flow/monoflow/chat.py +6 -6
- lionagi/core/flow/monoflow/chat_mixin.py +24 -34
- lionagi/core/flow/monoflow/followup.py +4 -4
- lionagi/core/flow/polyflow/__init__.py +1 -1
- lionagi/core/flow/polyflow/chat.py +15 -12
- lionagi/core/{prompt/action_template.py → form/action_form.py} +2 -2
- lionagi/core/{prompt → form}/field_validator.py +40 -31
- lionagi/core/form/form.py +302 -0
- lionagi/core/form/mixin.py +214 -0
- lionagi/core/{prompt/scored_template.py → form/scored_form.py} +2 -2
- lionagi/core/generic/__init__.py +37 -0
- lionagi/core/generic/action.py +26 -0
- lionagi/core/generic/component.py +457 -0
- lionagi/core/generic/condition.py +44 -0
- lionagi/core/generic/data_logger.py +305 -0
- lionagi/core/generic/edge.py +110 -0
- lionagi/core/generic/mail.py +90 -0
- lionagi/core/generic/mailbox.py +36 -0
- lionagi/core/generic/node.py +285 -0
- lionagi/core/generic/relation.py +70 -0
- lionagi/core/generic/signal.py +22 -0
- lionagi/core/generic/structure.py +362 -0
- lionagi/core/generic/transfer.py +20 -0
- lionagi/core/generic/work.py +40 -0
- lionagi/core/graph/graph.py +126 -0
- lionagi/core/graph/tree.py +190 -0
- lionagi/core/mail/__init__.py +0 -8
- lionagi/core/mail/mail_manager.py +12 -10
- lionagi/core/mail/schema.py +9 -2
- lionagi/core/messages/__init__.py +0 -3
- lionagi/core/messages/schema.py +17 -225
- lionagi/core/session/__init__.py +0 -3
- lionagi/core/session/session.py +25 -23
- lionagi/core/tool/__init__.py +3 -1
- lionagi/core/tool/tool.py +28 -0
- lionagi/core/tool/tool_manager.py +75 -75
- lionagi/integrations/chunker/chunk.py +7 -7
- lionagi/integrations/config/oai_configs.py +4 -4
- lionagi/integrations/loader/load.py +6 -6
- lionagi/integrations/loader/load_util.py +8 -8
- lionagi/libs/ln_api.py +3 -3
- lionagi/libs/ln_parse.py +43 -6
- lionagi/libs/ln_validate.py +288 -0
- lionagi/libs/sys_util.py +28 -6
- lionagi/tests/libs/test_async.py +0 -0
- lionagi/tests/libs/test_field_validators.py +353 -0
- lionagi/tests/test_core/test_base_branch.py +0 -1
- lionagi/tests/test_core/test_branch.py +3 -0
- lionagi/tests/test_core/test_session_base_util.py +1 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/METADATA +1 -1
- lionagi-0.1.0.dist-info/RECORD +136 -0
- lionagi/core/prompt/prompt_template.py +0 -312
- lionagi/core/schema/__init__.py +0 -22
- lionagi/core/schema/action_node.py +0 -29
- lionagi/core/schema/base_mixin.py +0 -296
- lionagi/core/schema/base_node.py +0 -199
- lionagi/core/schema/condition.py +0 -24
- lionagi/core/schema/data_logger.py +0 -354
- lionagi/core/schema/data_node.py +0 -93
- lionagi/core/schema/prompt_template.py +0 -67
- lionagi/core/schema/structure.py +0 -912
- lionagi/core/tool/manual.py +0 -1
- lionagi-0.0.315.dist-info/RECORD +0 -121
- /lionagi/core/{branch/base → execute}/__init__.py +0 -0
- /lionagi/core/flow/{base/baseflow.py → baseflow.py} +0 -0
- /lionagi/core/flow/{base/__init__.py → mono_chat_mixin.py} +0 -0
- /lionagi/core/{prompt → form}/__init__.py +0 -0
- /lionagi/{tests/test_integrations → core/graph}/__init__.py +0 -0
- /lionagi/tests/{test_libs → integrations}/__init__.py +0 -0
- /lionagi/tests/{test_libs/test_async.py → libs/__init__.py} +0 -0
- /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_func_call.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_nested.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_parse.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
- {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/LICENSE +0 -0
- {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/WHEEL +0 -0
- {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/top_level.txt +0 -0
@@ -1,312 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
This module defines the PromptTemplate and ScoredTemplate classes for creating and managing prompt templates.
|
3
|
-
|
4
|
-
The PromptTemplate class is a base class for creating prompt templates with input and output fields, validation,
|
5
|
-
and processing. The ScoredTemplate class extends the PromptTemplate class and adds fields for confidence score and reason.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from typing import Any
|
9
|
-
from lionagi.libs import convert, func_call
|
10
|
-
from lionagi.core.schema.base_node import BaseComponent
|
11
|
-
|
12
|
-
from pydantic import Field
|
13
|
-
from .field_validator import validation_funcs
|
14
|
-
|
15
|
-
|
16
|
-
_non_prompt_words = [
|
17
|
-
"id_",
|
18
|
-
"node_id",
|
19
|
-
"meta",
|
20
|
-
"timestamp",
|
21
|
-
"metadata",
|
22
|
-
"signature",
|
23
|
-
"task",
|
24
|
-
"template_name",
|
25
|
-
]
|
26
|
-
|
27
|
-
|
28
|
-
class PromptTemplate(BaseComponent):
|
29
|
-
"""
|
30
|
-
A base class for creating and managing prompt templates.
|
31
|
-
|
32
|
-
Attributes:
|
33
|
-
signature (str): The signature indicating the input and output fields.
|
34
|
-
choices (dict[str, list[str]]): The choices to select from for each field.
|
35
|
-
out_validation_kwargs (dict[str, Any]): The validation keyword arguments for output fields.
|
36
|
-
in_validation_kwargs (dict[str, Any]): The validation keyword arguments for input fields.
|
37
|
-
task (str | dict[str, Any] | None): The task to follow.
|
38
|
-
fix_input (bool): Whether to fix input fields.
|
39
|
-
fix_output (bool): Whether to fix output fields.
|
40
|
-
template_name (str): The name of the prompt template.
|
41
|
-
version (str | float | int): The version of the prompt template.
|
42
|
-
description (dict | str | None): The description of the prompt template.
|
43
|
-
input_fields (list[str]): The input fields of the prompt template.
|
44
|
-
output_fields (list[str]): The output fields of the prompt template.
|
45
|
-
|
46
|
-
Methods:
|
47
|
-
__init__(self, template_name="default_prompt_template", version_=None, description_=None, task=None, **kwargs):
|
48
|
-
Initializes a new instance of the PromptTemplate class.
|
49
|
-
_validate_input_choices(self, fix_=fix_input):
|
50
|
-
Validates the input choices based on the defined choices.
|
51
|
-
_validate_output_choices(self, fix_=fix_output):
|
52
|
-
Validates the output choices based on the defined choices.
|
53
|
-
_get_input_output_fields(str_) -> tuple[list[str], list[str]]:
|
54
|
-
Extracts the input and output fields from the signature string.
|
55
|
-
instruction_context(self) -> str:
|
56
|
-
Returns the instruction context based on the input fields.
|
57
|
-
instruction(self) -> str:
|
58
|
-
Returns the instruction based on the task and input/output fields.
|
59
|
-
instruction_output_fields(self) -> dict[str, str]:
|
60
|
-
Returns a dictionary mapping output fields to their descriptions.
|
61
|
-
prompt_fields_annotation(self) -> dict[str, list[str]]:
|
62
|
-
Returns a dictionary mapping prompt fields to their annotated types.
|
63
|
-
_validate_field(self, k, v, choices=None, fix_=False, **kwargs) -> bool:
|
64
|
-
Validates a single field based on its annotated type and choices.
|
65
|
-
_process_input(self, fix_=False):
|
66
|
-
Processes and validates the input fields.
|
67
|
-
_process_response(self, out_, fix_=True):
|
68
|
-
Processes and validates the output fields.
|
69
|
-
in_(self) -> dict[str, Any]:
|
70
|
-
Returns a dictionary mapping input fields to their values.
|
71
|
-
out(self) -> dict[str, Any]:
|
72
|
-
Returns a dictionary mapping output fields to their values.
|
73
|
-
process(self, in_=None, out_=None):
|
74
|
-
Processes and validates the input and output fields.
|
75
|
-
"""
|
76
|
-
|
77
|
-
signature: str = Field("null", description="signature indicating inputs, outputs")
|
78
|
-
choices: dict[str, list[str]] = Field(
|
79
|
-
default_factory=dict, description="choices to select from"
|
80
|
-
)
|
81
|
-
out_validation_kwargs: dict[str, Any] = Field(
|
82
|
-
default_factory=dict, description="validation kwargs for output"
|
83
|
-
)
|
84
|
-
in_validation_kwargs: dict[str, Any] = Field(
|
85
|
-
default_factory=dict, description="validation kwargs for input"
|
86
|
-
)
|
87
|
-
task: str | dict[str, Any] | None = Field(None, description="task to follow")
|
88
|
-
fix_input: bool = Field(True, description="whether to fix input")
|
89
|
-
fix_output: bool = Field(True, description="whether to fix output")
|
90
|
-
|
91
|
-
def __init__(
|
92
|
-
self,
|
93
|
-
template_name: str = "default_prompt_template",
|
94
|
-
version_: str | float | int = None,
|
95
|
-
description_: dict | str | None = None,
|
96
|
-
task: str | None = None,
|
97
|
-
**kwargs,
|
98
|
-
):
|
99
|
-
super().__init__(**kwargs)
|
100
|
-
self.template_name = template_name
|
101
|
-
self.meta_insert(["version"], version_)
|
102
|
-
self.meta_insert(["description"], description_ or "")
|
103
|
-
self.task = task
|
104
|
-
self.input_fields, self.output_fields = self._get_input_output_fields(
|
105
|
-
self.signature
|
106
|
-
)
|
107
|
-
self.process(in_=True)
|
108
|
-
|
109
|
-
def _validate_input_choices(self, fix_=fix_input):
|
110
|
-
if len(self.choices) >= 1:
|
111
|
-
for k, choices in self.choices.items():
|
112
|
-
if k in self.input_fields and not self._validate_field(
|
113
|
-
k, getattr(self, k), choices, fix_
|
114
|
-
):
|
115
|
-
raise ValueError(
|
116
|
-
f"Invalid choice for field {k}: {getattr(self, k)} is not in {choices}"
|
117
|
-
)
|
118
|
-
|
119
|
-
def _validate_output_choices(self, fix_=fix_output):
|
120
|
-
if len(self.choices) >= 1:
|
121
|
-
for k, choices in self.choices.items():
|
122
|
-
if k in self.output_fields and not self._validate_field(
|
123
|
-
k, getattr(self, k), choices, fix_
|
124
|
-
):
|
125
|
-
raise ValueError(
|
126
|
-
f"Invalid choice for field {k}: {getattr(self, k)} is not in {choices}"
|
127
|
-
)
|
128
|
-
|
129
|
-
@property
|
130
|
-
def version(self):
|
131
|
-
return self.metadata["version"]
|
132
|
-
|
133
|
-
@version.setter
|
134
|
-
def version(self, value):
|
135
|
-
self.metadata["version"] = value
|
136
|
-
|
137
|
-
@property
|
138
|
-
def description(self):
|
139
|
-
return self.metadata["description"]
|
140
|
-
|
141
|
-
@description.setter
|
142
|
-
def description(self, value):
|
143
|
-
self.metadata["description"] = value
|
144
|
-
|
145
|
-
@property
|
146
|
-
def prompt_fields(self):
|
147
|
-
return [
|
148
|
-
_field for _field in self.property_keys if _field not in _non_prompt_words
|
149
|
-
]
|
150
|
-
|
151
|
-
@staticmethod
|
152
|
-
def _get_input_output_fields(str_):
|
153
|
-
_inputs, _outputs = str_.split("->")
|
154
|
-
|
155
|
-
_inputs = [convert.strip_lower(i) for i in _inputs.split(",")]
|
156
|
-
_outputs = [convert.strip_lower(o) for o in _outputs.split(",")]
|
157
|
-
|
158
|
-
return _inputs, _outputs
|
159
|
-
|
160
|
-
@property
|
161
|
-
def instruction_context(self):
|
162
|
-
a = "".join(
|
163
|
-
f"""
|
164
|
-
## input: {i}:
|
165
|
-
- description: {self.model_fields[i].description}
|
166
|
-
- value: {str(self.__getattribute__(self.input_fields[idx]))}
|
167
|
-
"""
|
168
|
-
for idx, i in enumerate(self.input_fields)
|
169
|
-
)
|
170
|
-
return a.replace(" ", "")
|
171
|
-
|
172
|
-
@property
|
173
|
-
def instruction(self):
|
174
|
-
ccc = f"""
|
175
|
-
0. Your task is {self.task},
|
176
|
-
1. provided: {self.input_fields},
|
177
|
-
2. requested: {self.output_fields}
|
178
|
-
----------
|
179
|
-
"""
|
180
|
-
return ccc.replace(" ", "")
|
181
|
-
|
182
|
-
@property
|
183
|
-
def instruction_output_fields(self):
|
184
|
-
return {i: self.model_fields[i].description for i in self.output_fields}
|
185
|
-
|
186
|
-
@property
|
187
|
-
def prompt_fields_annotation(self):
|
188
|
-
dict_ = {i: self.model_fields[i].annotation for i in self.prompt_fields}
|
189
|
-
for k, v in dict_.items():
|
190
|
-
if "|" in str(v):
|
191
|
-
v = str(v)
|
192
|
-
v = v.split("|")
|
193
|
-
dict_[k] = func_call.lcall(v, convert.strip_lower)
|
194
|
-
else:
|
195
|
-
dict_[k] = [v.__name__]
|
196
|
-
|
197
|
-
return dict_
|
198
|
-
|
199
|
-
def _validate_field(self, k, v, choices=None, fix_=False, **kwargs):
|
200
|
-
|
201
|
-
str_ = self.prompt_fields_annotation[k]
|
202
|
-
|
203
|
-
if choices:
|
204
|
-
v_ = validation_funcs["enum"](v, choices=choices, fix_=fix_, **kwargs)
|
205
|
-
if v_ not in choices:
|
206
|
-
raise ValueError(f"{v} is not in chocies {choices}")
|
207
|
-
setattr(self, k, v_)
|
208
|
-
return True
|
209
|
-
|
210
|
-
if "lionagi.core.prompt.action_template.actionrequest" in str_:
|
211
|
-
self.__setattr__(k, validation_funcs["action"](v))
|
212
|
-
return True
|
213
|
-
|
214
|
-
elif "bool" in str_:
|
215
|
-
self.__setattr__(k, validation_funcs["bool"](v, fix_=fix_, **kwargs))
|
216
|
-
return True
|
217
|
-
|
218
|
-
elif "int" in str_ or "float" in str_:
|
219
|
-
self.__setattr__(k, validation_funcs["number"](v, fix_=fix_, **kwargs))
|
220
|
-
return True
|
221
|
-
|
222
|
-
elif "str" in str_:
|
223
|
-
self.__setattr__(k, validation_funcs["str"](v, fix_=fix_, **kwargs))
|
224
|
-
return True
|
225
|
-
|
226
|
-
return False
|
227
|
-
|
228
|
-
def _process_input(self, fix_=False):
|
229
|
-
kwargs = self.in_validation_kwargs.copy()
|
230
|
-
for k, v in self.in_.items():
|
231
|
-
if k not in kwargs:
|
232
|
-
kwargs = {k: {}}
|
233
|
-
|
234
|
-
if self._field_has_choices(k):
|
235
|
-
self.choices[k] = self.model_fields[k].json_schema_extra["choices"]
|
236
|
-
if self._validate_field(
|
237
|
-
k, v, choices=self.choices[k], fix_=fix_, **kwargs[k]
|
238
|
-
):
|
239
|
-
continue
|
240
|
-
else:
|
241
|
-
raise ValueError(f"{k} has no choices")
|
242
|
-
|
243
|
-
elif self._validate_field(k, v, fix_=fix_, **kwargs[k]):
|
244
|
-
continue
|
245
|
-
else:
|
246
|
-
raise ValueError(f"failed to validate field {k}")
|
247
|
-
|
248
|
-
def _field_has_choices(self, k):
|
249
|
-
try:
|
250
|
-
a = (
|
251
|
-
self.model_fields[k].json_schema_extra["choices"] is not None
|
252
|
-
and "choices" in self.model_fields[k].json_schema_extra
|
253
|
-
)
|
254
|
-
return a if isinstance(a, bool) else False
|
255
|
-
except Exception:
|
256
|
-
return False
|
257
|
-
|
258
|
-
def _process_response(self, out_, fix_=True):
|
259
|
-
kwargs = self.out_validation_kwargs.copy()
|
260
|
-
for k, v in out_.items():
|
261
|
-
if k not in kwargs:
|
262
|
-
kwargs = {k: {}}
|
263
|
-
|
264
|
-
if self._field_has_choices(k):
|
265
|
-
self.choices[k] = self.model_fields[k].json_schema_extra["choices"]
|
266
|
-
if self._validate_field(
|
267
|
-
k, v, choices=self.choices[k], fix_=fix_, **kwargs[k]
|
268
|
-
):
|
269
|
-
continue
|
270
|
-
else:
|
271
|
-
raise ValueError(f"{k} has no choices")
|
272
|
-
|
273
|
-
elif self._validate_field(k, v, fix_=fix_, **kwargs[k]):
|
274
|
-
continue
|
275
|
-
|
276
|
-
else:
|
277
|
-
raise ValueError(f"failed to validate field {k} with value {v}")
|
278
|
-
|
279
|
-
@property
|
280
|
-
def in_(self):
|
281
|
-
return {i: self.__getattribute__(i) for i in self.input_fields}
|
282
|
-
|
283
|
-
@property
|
284
|
-
def out(self):
|
285
|
-
return {i: self.__getattribute__(i) for i in self.output_fields}
|
286
|
-
|
287
|
-
def process(self, in_=None, out_=None):
|
288
|
-
if in_:
|
289
|
-
self._process_input(fix_=self.fix_input)
|
290
|
-
self._validate_input_choices(fix_=self.fix_input)
|
291
|
-
if out_:
|
292
|
-
self._process_response(out_, fix_=self.fix_output)
|
293
|
-
self._validate_output_choices(fix_=self.fix_output)
|
294
|
-
return self
|
295
|
-
|
296
|
-
|
297
|
-
# class Weather(PromptTemplate):
|
298
|
-
# sunny: bool = Field(True, description="true if the weather is sunny outside else false")
|
299
|
-
# rainy: bool = Field(False, description="true if it is raining outside else false")
|
300
|
-
# play1: bool = Field(True, description="conduct play1")
|
301
|
-
# play2: bool = Field(False, description="conduct play2")
|
302
|
-
# signature: str = Field("sunny, rainy -> play1, play2")
|
303
|
-
|
304
|
-
# predictor = Weather(
|
305
|
-
# template_name="predictor",
|
306
|
-
# task_ = "decides to conduct one of play1 and play2",
|
307
|
-
# version_=0.1,
|
308
|
-
# description_="predicts the weather and decides play",
|
309
|
-
# signature = "sunny, play1 -> play2"
|
310
|
-
# )
|
311
|
-
|
312
|
-
# predictor.to_instruction()
|
lionagi/core/schema/__init__.py
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
from .base_node import BaseNode, BaseRelatableNode, Tool, TOOL_TYPE
|
2
|
-
from .data_node import DataNode
|
3
|
-
from .data_logger import DLog, DataLogger
|
4
|
-
from .structure import Relationship, Graph, Structure
|
5
|
-
from .action_node import ActionNode, ActionSelection
|
6
|
-
from .condition import Condition
|
7
|
-
|
8
|
-
__all__ = [
|
9
|
-
"BaseNode",
|
10
|
-
"BaseRelatableNode",
|
11
|
-
"Tool",
|
12
|
-
"DataNode",
|
13
|
-
"DLog",
|
14
|
-
"DataLogger",
|
15
|
-
"Relationship",
|
16
|
-
"Graph",
|
17
|
-
"Structure",
|
18
|
-
"ActionNode",
|
19
|
-
"TOOL_TYPE",
|
20
|
-
"ActionSelection",
|
21
|
-
"Condition",
|
22
|
-
]
|
@@ -1,29 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
|
3
|
-
from .base_node import BaseNode
|
4
|
-
|
5
|
-
|
6
|
-
class ActionSelection(BaseNode):
|
7
|
-
|
8
|
-
def __init__(self, action: str = "chat", action_kwargs=None):
|
9
|
-
if action_kwargs is None:
|
10
|
-
action_kwargs = {}
|
11
|
-
super().__init__()
|
12
|
-
self.action = action
|
13
|
-
self.action_kwargs = action_kwargs
|
14
|
-
|
15
|
-
|
16
|
-
class ActionNode(BaseNode):
|
17
|
-
|
18
|
-
def __init__(
|
19
|
-
self, instruction, action: str = "chat", tools=None, action_kwargs=None
|
20
|
-
):
|
21
|
-
if tools is None:
|
22
|
-
tools = []
|
23
|
-
if action_kwargs is None:
|
24
|
-
action_kwargs = {}
|
25
|
-
super().__init__()
|
26
|
-
self.instruction = instruction
|
27
|
-
self.action = action
|
28
|
-
self.tools = tools
|
29
|
-
self.action_kwargs = action_kwargs
|
@@ -1,296 +0,0 @@
|
|
1
|
-
from abc import ABC
|
2
|
-
from functools import singledispatchmethod
|
3
|
-
from typing import Any, TypeVar, Type, Callable
|
4
|
-
|
5
|
-
import pandas as pd
|
6
|
-
from pydantic import BaseModel, ValidationError
|
7
|
-
|
8
|
-
from lionagi.libs import nested, convert, ParseUtil, SysUtil
|
9
|
-
|
10
|
-
T = TypeVar("T") # Generic type for return type of from_obj method
|
11
|
-
|
12
|
-
|
13
|
-
class BaseToObjectMixin(ABC, BaseModel):
|
14
|
-
|
15
|
-
def to_json_str(self, *args, **kwargs) -> str:
|
16
|
-
"""
|
17
|
-
Serializes the model instance into a JSON string.
|
18
|
-
|
19
|
-
This method utilizes the model's `model_dump_json` method, typically available in Pydantic
|
20
|
-
models or models with similar serialization mechanisms, to convert the instance into a JSON
|
21
|
-
string. It supports passing arbitrary arguments to the underlying `model_dump_json` method.
|
22
|
-
|
23
|
-
Args:
|
24
|
-
*args: Variable-length argument list to be passed to `model_dump_json`.
|
25
|
-
**kwargs: Arbitrary keyword arguments, with `by_alias=True` set by default to use
|
26
|
-
model field aliases in the output JSON, if any.
|
27
|
-
|
28
|
-
Returns:
|
29
|
-
str: A JSON string representation of the model instance.
|
30
|
-
"""
|
31
|
-
return self.model_dump_json(*args, by_alias=True, **kwargs)
|
32
|
-
|
33
|
-
def to_dict(self, *args, **kwargs) -> dict[str, Any]:
|
34
|
-
"""
|
35
|
-
Converts the model instance into a dictionary.
|
36
|
-
|
37
|
-
Leveraging the model's `model_dump` method, this function produces a dictionary representation
|
38
|
-
of the model. The dictionary keys correspond to the model's field names, with an option to use
|
39
|
-
aliases instead of the original field names.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
*args: Variable-length argument list for the `model_dump` method.
|
43
|
-
**kwargs: Arbitrary keyword arguments. By default, `by_alias=True` is applied, indicating
|
44
|
-
that field aliases should be used as keys in the resulting dictionary.
|
45
|
-
|
46
|
-
Returns:
|
47
|
-
dict[str, Any]: The dictionary representation of the model instance.
|
48
|
-
"""
|
49
|
-
return self.model_dump(*args, by_alias=True, **kwargs)
|
50
|
-
|
51
|
-
def to_xml(self) -> str:
|
52
|
-
"""
|
53
|
-
Serializes the model instance into an XML string.
|
54
|
-
|
55
|
-
This method converts the model instance into an XML format. It first transforms the instance
|
56
|
-
into a dictionary and then recursively constructs an XML tree representing the model's data.
|
57
|
-
The root element of the XML tree is named after the class of the model instance.
|
58
|
-
|
59
|
-
Returns:
|
60
|
-
str: An XML string representation of the model instance.
|
61
|
-
"""
|
62
|
-
|
63
|
-
import xml.etree.ElementTree as ET
|
64
|
-
|
65
|
-
root = ET.Element(self.__class__.__name__)
|
66
|
-
|
67
|
-
def convert(dict_obj, parent):
|
68
|
-
for key, val in dict_obj.items():
|
69
|
-
if isinstance(val, dict):
|
70
|
-
element = ET.SubElement(parent, key)
|
71
|
-
convert(val, element)
|
72
|
-
else:
|
73
|
-
element = ET.SubElement(parent, key)
|
74
|
-
element.text = str(val)
|
75
|
-
|
76
|
-
convert(self.to_dict(), root)
|
77
|
-
return ET.tostring(root, encoding="unicode")
|
78
|
-
|
79
|
-
def to_pd_series(self, *args, pd_kwargs: dict | None = None, **kwargs) -> pd.Series:
|
80
|
-
"""
|
81
|
-
Converts the model instance into a pandas Series.
|
82
|
-
|
83
|
-
This method first transforms the model instance into a dictionary and then constructs a pandas
|
84
|
-
Series from it. The Series' index corresponds to the model's field names, with an option to
|
85
|
-
customize the Series creation through `pd_kwargs`.
|
86
|
-
|
87
|
-
Args:
|
88
|
-
*args: Variable-length argument list for the `to_dict` method.
|
89
|
-
pd_kwargs (dict | None): Optional dictionary of keyword arguments to pass to the pandas
|
90
|
-
Series constructor. Defaults to None, in which case an empty
|
91
|
-
dictionary is used.
|
92
|
-
**kwargs: Arbitrary keyword arguments for the `to_dict` method, influencing the dictionary
|
93
|
-
representation used for Series creation.
|
94
|
-
|
95
|
-
Returns:
|
96
|
-
pd.Series: A pandas Series representation of the model instance.
|
97
|
-
"""
|
98
|
-
pd_kwargs = {} if pd_kwargs is None else pd_kwargs
|
99
|
-
dict_ = self.to_dict(*args, **kwargs)
|
100
|
-
return pd.Series(dict_, **pd_kwargs)
|
101
|
-
|
102
|
-
|
103
|
-
class BaseFromObjectMixin(ABC, BaseModel):
|
104
|
-
|
105
|
-
@singledispatchmethod
|
106
|
-
@classmethod
|
107
|
-
def from_obj(cls: Type[T], obj: Any, *args, **kwargs) -> T:
|
108
|
-
raise NotImplementedError(f"Unsupported type: {type(obj)}")
|
109
|
-
|
110
|
-
@from_obj.register(dict)
|
111
|
-
@classmethod
|
112
|
-
def _from_dict(cls, obj: dict, *args, **kwargs) -> T:
|
113
|
-
return cls.model_validate(obj, *args, **kwargs)
|
114
|
-
|
115
|
-
@from_obj.register(str)
|
116
|
-
@classmethod
|
117
|
-
def _from_str(cls, obj: str, *args, fuzzy_parse=False, **kwargs) -> T:
|
118
|
-
obj = ParseUtil.fuzzy_parse_json(obj) if fuzzy_parse else convert.to_dict(obj)
|
119
|
-
try:
|
120
|
-
return cls.from_obj(obj, *args, **kwargs)
|
121
|
-
except ValidationError as e:
|
122
|
-
raise ValueError(f"Invalid JSON for deserialization: {e}") from e
|
123
|
-
|
124
|
-
@from_obj.register(list)
|
125
|
-
@classmethod
|
126
|
-
def _from_list(cls, obj: list[Any], *args, **kwargs) -> list[T]:
|
127
|
-
return [cls.from_obj(item, *args, **kwargs) for item in obj]
|
128
|
-
|
129
|
-
@from_obj.register(pd.Series)
|
130
|
-
@classmethod
|
131
|
-
def _from_pd_series(cls, obj: pd.Series, *args, pd_kwargs=None, **kwargs) -> T:
|
132
|
-
if pd_kwargs is None:
|
133
|
-
pd_kwargs = {}
|
134
|
-
return cls.from_obj(obj.to_dict(**pd_kwargs), *args, **kwargs)
|
135
|
-
|
136
|
-
@from_obj.register(pd.DataFrame)
|
137
|
-
@classmethod
|
138
|
-
def _from_pd_dataframe(
|
139
|
-
cls, obj: pd.DataFrame, *args, pd_kwargs=None, **kwargs
|
140
|
-
) -> list[T]:
|
141
|
-
if pd_kwargs is None:
|
142
|
-
pd_kwargs = {}
|
143
|
-
return [
|
144
|
-
cls.from_obj(row, *args, **pd_kwargs, **kwargs) for _, row in obj.iterrows()
|
145
|
-
]
|
146
|
-
|
147
|
-
@from_obj.register(BaseModel)
|
148
|
-
@classmethod
|
149
|
-
def _from_base_model(cls, obj: BaseModel, pydantic_kwargs=None, **kwargs) -> T:
|
150
|
-
if pydantic_kwargs is None:
|
151
|
-
pydantic_kwargs = {"by_alias": True}
|
152
|
-
config_ = {**obj.model_dump(**pydantic_kwargs), **kwargs}
|
153
|
-
return cls.from_obj(**config_)
|
154
|
-
|
155
|
-
|
156
|
-
class BaseMetaManageMixin(ABC, BaseModel):
|
157
|
-
|
158
|
-
def meta_keys(self, flattened: bool = False, **kwargs) -> list[str]:
|
159
|
-
"""
|
160
|
-
Retrieves a list of metadata keys.
|
161
|
-
|
162
|
-
Args:
|
163
|
-
flattened (bool): If True, returns keys from a flattened metadata structure.
|
164
|
-
**kwargs: Additional keyword arguments passed to the flattening function.
|
165
|
-
|
166
|
-
Returns:
|
167
|
-
list[str]: List of metadata keys.
|
168
|
-
"""
|
169
|
-
if flattened:
|
170
|
-
return nested.get_flattened_keys(self.metadata, **kwargs)
|
171
|
-
return list(self.metadata.keys())
|
172
|
-
|
173
|
-
def meta_has_key(self, key: str, flattened: bool = False, **kwargs) -> bool:
|
174
|
-
"""
|
175
|
-
Checks if a specified key exists in the metadata.
|
176
|
-
|
177
|
-
Args:
|
178
|
-
key (str): The key to check.
|
179
|
-
flattened (bool): If True, checks within a flattened metadata structure.
|
180
|
-
**kwargs: Additional keyword arguments for flattening.
|
181
|
-
|
182
|
-
Returns:
|
183
|
-
bool: True if key exists, False otherwise.
|
184
|
-
"""
|
185
|
-
if flattened:
|
186
|
-
return key in nested.get_flattened_keys(self.metadata, **kwargs)
|
187
|
-
return key in self.metadata
|
188
|
-
|
189
|
-
def meta_get(
|
190
|
-
self, key: str, indices: list[str | int] = None, default: Any = None
|
191
|
-
) -> Any:
|
192
|
-
"""
|
193
|
-
Retrieves the value associated with a given key from the metadata.
|
194
|
-
|
195
|
-
Args:
|
196
|
-
key (str): The key for the desired value.
|
197
|
-
indices: Optional indices for nested retrieval.
|
198
|
-
default (Any): The default value to return if the key is not found.
|
199
|
-
|
200
|
-
Returns:
|
201
|
-
Any: The value associated with the key or the default value.
|
202
|
-
"""
|
203
|
-
if indices:
|
204
|
-
return nested.nget(self.metadata, key, indices, default)
|
205
|
-
return self.metadata.get(key, default)
|
206
|
-
|
207
|
-
def meta_change_key(self, old_key: str, new_key: str) -> bool:
|
208
|
-
"""
|
209
|
-
Renames a key in the metadata.
|
210
|
-
|
211
|
-
Args:
|
212
|
-
old_key (str): The current key name.
|
213
|
-
new_key (str): The new key name.
|
214
|
-
|
215
|
-
Returns:
|
216
|
-
bool: True if the key was changed, False otherwise.
|
217
|
-
"""
|
218
|
-
if old_key in self.metadata:
|
219
|
-
SysUtil.change_dict_key(self.metadata, old_key, new_key)
|
220
|
-
return True
|
221
|
-
return False
|
222
|
-
|
223
|
-
def meta_insert(self, indices: str | list, value: Any, **kwargs) -> bool:
|
224
|
-
"""
|
225
|
-
Inserts a value into the metadata at specified indices.
|
226
|
-
|
227
|
-
Args:
|
228
|
-
indices (str | list): The indices where the value should be inserted.
|
229
|
-
value (Any): The value to insert.
|
230
|
-
**kwargs: Additional keyword arguments.
|
231
|
-
|
232
|
-
Returns:
|
233
|
-
bool: True if the insertion was successful, False otherwise.
|
234
|
-
"""
|
235
|
-
return nested.ninsert(self.metadata, indices, value, **kwargs)
|
236
|
-
|
237
|
-
# ToDo: do a nested pop
|
238
|
-
def meta_pop(self, key: str, default: Any = None) -> Any:
|
239
|
-
"""
|
240
|
-
Removes a key from the metadata and returns its value.
|
241
|
-
|
242
|
-
Args:
|
243
|
-
key (str): The key to remove.
|
244
|
-
default (Any): The default value to return if the key is not found.
|
245
|
-
|
246
|
-
Returns:
|
247
|
-
Any: The value of the removed key or the default value.
|
248
|
-
"""
|
249
|
-
return self.metadata.pop(key, default)
|
250
|
-
|
251
|
-
def meta_merge(
|
252
|
-
self, additional_metadata: dict[str, Any], overwrite: bool = False, **kwargs
|
253
|
-
) -> None:
|
254
|
-
"""
|
255
|
-
Merges additional metadata into the existing metadata.
|
256
|
-
|
257
|
-
Args:
|
258
|
-
additional_metadata (dict[str, Any]): The metadata to merge in.
|
259
|
-
overwrite (bool): If True, existing keys will be overwritten by those in additional_metadata.
|
260
|
-
**kwargs: Additional keyword arguments for the merge.
|
261
|
-
|
262
|
-
Returns:
|
263
|
-
None
|
264
|
-
"""
|
265
|
-
nested.nmerge(
|
266
|
-
[self.metadata, additional_metadata], overwrite=overwrite, **kwargs
|
267
|
-
)
|
268
|
-
|
269
|
-
for key, value in additional_metadata.items():
|
270
|
-
if overwrite or key not in self.metadata:
|
271
|
-
self.metadata[key] = value
|
272
|
-
|
273
|
-
def meta_clear(self) -> None:
|
274
|
-
"""
|
275
|
-
Clears all metadata.
|
276
|
-
|
277
|
-
Returns:
|
278
|
-
None
|
279
|
-
"""
|
280
|
-
self.metadata.clear()
|
281
|
-
|
282
|
-
def meta_filter(self, condition: Callable[[Any, Any], bool]) -> dict[str, Any]:
|
283
|
-
"""
|
284
|
-
Filters the metadata based on a condition.
|
285
|
-
|
286
|
-
Args:
|
287
|
-
condition (Callable[[Any, Any], bool]): The condition function to apply.
|
288
|
-
|
289
|
-
Returns:
|
290
|
-
dict[str, Any]: The filtered metadata.
|
291
|
-
"""
|
292
|
-
return nested.nfilter(self.metadata, condition)
|
293
|
-
|
294
|
-
|
295
|
-
class BaseComponentMixin(BaseFromObjectMixin, BaseToObjectMixin, BaseMetaManageMixin):
|
296
|
-
pass
|