lionagi 0.0.316__py3-none-any.whl → 0.1.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. lionagi/core/__init__.py +19 -8
  2. lionagi/core/agent/__init__.py +0 -3
  3. lionagi/core/agent/base_agent.py +25 -30
  4. lionagi/core/branch/__init__.py +0 -4
  5. lionagi/core/branch/{base_branch.py → base.py} +12 -13
  6. lionagi/core/branch/branch.py +22 -19
  7. lionagi/core/branch/executable_branch.py +0 -347
  8. lionagi/core/branch/{branch_flow_mixin.py → flow_mixin.py} +5 -5
  9. lionagi/core/direct/__init__.py +10 -1
  10. lionagi/core/direct/cot.py +61 -26
  11. lionagi/core/direct/plan.py +10 -8
  12. lionagi/core/direct/predict.py +5 -5
  13. lionagi/core/direct/react.py +8 -8
  14. lionagi/core/direct/score.py +4 -4
  15. lionagi/core/direct/select.py +4 -4
  16. lionagi/core/direct/utils.py +7 -4
  17. lionagi/core/direct/vote.py +2 -2
  18. lionagi/core/execute/base_executor.py +47 -0
  19. lionagi/core/execute/branch_executor.py +296 -0
  20. lionagi/core/execute/instruction_map_executor.py +179 -0
  21. lionagi/core/execute/neo4j_executor.py +381 -0
  22. lionagi/core/execute/structure_executor.py +314 -0
  23. lionagi/core/flow/monoflow/ReAct.py +20 -20
  24. lionagi/core/flow/monoflow/chat.py +6 -6
  25. lionagi/core/flow/monoflow/chat_mixin.py +23 -33
  26. lionagi/core/flow/monoflow/followup.py +14 -15
  27. lionagi/core/flow/polyflow/chat.py +15 -12
  28. lionagi/core/{prompt/action_template.py → form/action_form.py} +2 -2
  29. lionagi/core/{prompt → form}/field_validator.py +40 -31
  30. lionagi/core/form/form.py +302 -0
  31. lionagi/core/form/mixin.py +214 -0
  32. lionagi/core/{prompt/scored_template.py → form/scored_form.py} +2 -2
  33. lionagi/core/generic/__init__.py +37 -0
  34. lionagi/core/generic/action.py +26 -0
  35. lionagi/core/generic/component.py +455 -0
  36. lionagi/core/generic/condition.py +44 -0
  37. lionagi/core/generic/data_logger.py +305 -0
  38. lionagi/core/generic/edge.py +162 -0
  39. lionagi/core/generic/mail.py +90 -0
  40. lionagi/core/generic/mailbox.py +36 -0
  41. lionagi/core/generic/node.py +285 -0
  42. lionagi/core/generic/relation.py +70 -0
  43. lionagi/core/generic/signal.py +22 -0
  44. lionagi/core/generic/structure.py +362 -0
  45. lionagi/core/generic/transfer.py +20 -0
  46. lionagi/core/generic/work.py +40 -0
  47. lionagi/core/graph/graph.py +126 -0
  48. lionagi/core/graph/tree.py +190 -0
  49. lionagi/core/mail/__init__.py +0 -8
  50. lionagi/core/mail/mail_manager.py +15 -12
  51. lionagi/core/mail/schema.py +9 -2
  52. lionagi/core/messages/__init__.py +0 -3
  53. lionagi/core/messages/schema.py +17 -225
  54. lionagi/core/session/__init__.py +0 -3
  55. lionagi/core/session/session.py +24 -22
  56. lionagi/core/tool/__init__.py +3 -1
  57. lionagi/core/tool/tool.py +28 -0
  58. lionagi/core/tool/tool_manager.py +75 -75
  59. lionagi/experimental/directive/evaluator/__init__.py +0 -0
  60. lionagi/experimental/directive/evaluator/ast_evaluator.py +115 -0
  61. lionagi/experimental/directive/evaluator/base_evaluator.py +202 -0
  62. lionagi/experimental/directive/evaluator/sandbox_.py +14 -0
  63. lionagi/experimental/directive/evaluator/script_engine.py +83 -0
  64. lionagi/experimental/directive/parser/__init__.py +0 -0
  65. lionagi/experimental/directive/parser/base_parser.py +215 -0
  66. lionagi/experimental/directive/schema.py +36 -0
  67. lionagi/experimental/directive/template_/__init__.py +0 -0
  68. lionagi/experimental/directive/template_/base_template.py +63 -0
  69. lionagi/experimental/tool/__init__.py +0 -0
  70. lionagi/experimental/tool/function_calling.py +43 -0
  71. lionagi/experimental/tool/manual.py +66 -0
  72. lionagi/experimental/tool/schema.py +59 -0
  73. lionagi/experimental/tool/tool_manager.py +138 -0
  74. lionagi/experimental/tool/util.py +16 -0
  75. lionagi/experimental/work/__init__.py +0 -0
  76. lionagi/experimental/work/_logger.py +25 -0
  77. lionagi/experimental/work/exchange.py +0 -0
  78. lionagi/experimental/work/schema.py +30 -0
  79. lionagi/experimental/work/tests.py +72 -0
  80. lionagi/experimental/work/util.py +0 -0
  81. lionagi/experimental/work/work_function.py +89 -0
  82. lionagi/experimental/work/worker.py +12 -0
  83. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  84. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  85. lionagi/integrations/bridge/llamaindex_/get_index.py +294 -0
  86. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  87. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  88. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  89. lionagi/integrations/chunker/chunk.py +7 -7
  90. lionagi/integrations/config/oai_configs.py +5 -5
  91. lionagi/integrations/config/ollama_configs.py +1 -1
  92. lionagi/integrations/config/openrouter_configs.py +1 -1
  93. lionagi/integrations/loader/load.py +6 -6
  94. lionagi/integrations/loader/load_util.py +8 -8
  95. lionagi/integrations/storage/__init__.py +3 -0
  96. lionagi/integrations/storage/neo4j.py +673 -0
  97. lionagi/integrations/storage/storage_util.py +289 -0
  98. lionagi/integrations/storage/to_csv.py +63 -0
  99. lionagi/integrations/storage/to_excel.py +67 -0
  100. lionagi/libs/ln_api.py +3 -3
  101. lionagi/libs/ln_knowledge_graph.py +405 -0
  102. lionagi/libs/ln_parse.py +43 -6
  103. lionagi/libs/ln_queue.py +101 -0
  104. lionagi/libs/ln_tokenizer.py +57 -0
  105. lionagi/libs/ln_validate.py +288 -0
  106. lionagi/libs/sys_util.py +29 -7
  107. lionagi/lions/__init__.py +0 -0
  108. lionagi/lions/coder/__init__.py +0 -0
  109. lionagi/lions/coder/add_feature.py +20 -0
  110. lionagi/lions/coder/base_prompts.py +22 -0
  111. lionagi/lions/coder/coder.py +121 -0
  112. lionagi/lions/coder/util.py +91 -0
  113. lionagi/lions/researcher/__init__.py +0 -0
  114. lionagi/lions/researcher/data_source/__init__.py +0 -0
  115. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  116. lionagi/lions/researcher/data_source/google_.py +199 -0
  117. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  118. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  119. lionagi/tests/integrations/__init__.py +0 -0
  120. lionagi/tests/libs/__init__.py +0 -0
  121. lionagi/tests/libs/test_async.py +0 -0
  122. lionagi/tests/libs/test_field_validators.py +353 -0
  123. lionagi/tests/libs/test_queue.py +67 -0
  124. lionagi/tests/test_core/test_base_branch.py +0 -1
  125. lionagi/tests/test_core/test_branch.py +2 -0
  126. lionagi/tests/test_core/test_session_base_util.py +1 -0
  127. lionagi/version.py +1 -1
  128. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/METADATA +1 -1
  129. lionagi-0.1.1.dist-info/RECORD +190 -0
  130. lionagi/core/prompt/prompt_template.py +0 -312
  131. lionagi/core/schema/__init__.py +0 -22
  132. lionagi/core/schema/action_node.py +0 -29
  133. lionagi/core/schema/base_mixin.py +0 -296
  134. lionagi/core/schema/base_node.py +0 -199
  135. lionagi/core/schema/condition.py +0 -24
  136. lionagi/core/schema/data_logger.py +0 -354
  137. lionagi/core/schema/data_node.py +0 -93
  138. lionagi/core/schema/prompt_template.py +0 -67
  139. lionagi/core/schema/structure.py +0 -912
  140. lionagi/core/tool/manual.py +0 -1
  141. lionagi-0.0.316.dist-info/RECORD +0 -121
  142. /lionagi/core/{branch/base → execute}/__init__.py +0 -0
  143. /lionagi/core/flow/{base/baseflow.py → baseflow.py} +0 -0
  144. /lionagi/core/flow/{base/__init__.py → mono_chat_mixin.py} +0 -0
  145. /lionagi/core/{prompt → form}/__init__.py +0 -0
  146. /lionagi/{tests/test_integrations → core/graph}/__init__.py +0 -0
  147. /lionagi/{tests/test_libs → experimental}/__init__.py +0 -0
  148. /lionagi/{tests/test_libs/test_async.py → experimental/directive/__init__.py} +0 -0
  149. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  150. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  151. /lionagi/tests/{test_libs → libs}/test_func_call.py +0 -0
  152. /lionagi/tests/{test_libs → libs}/test_nested.py +0 -0
  153. /lionagi/tests/{test_libs → libs}/test_parse.py +0 -0
  154. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  155. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/LICENSE +0 -0
  156. {lionagi-0.0.316.dist-info → lionagi-0.1.1.dist-info}/WHEEL +0 -0
  157. {lionagi-0.0.316.dist-info → lionagi-0.1.1.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()
@@ -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