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
@@ -0,0 +1,455 @@
1
+ """This module provides base components used in lionagi"""
2
+
3
+ from abc import ABC
4
+ from functools import singledispatchmethod
5
+ from typing import Any, TypeVar
6
+
7
+ from pydantic import AliasChoices, BaseModel, Field, ValidationError
8
+ from pandas import DataFrame, Series
9
+
10
+ from lionagi.libs import SysUtil, func_call, convert, ParseUtil, nested
11
+
12
+
13
+ T = TypeVar("T")
14
+
15
+
16
+ class BaseComponent(BaseModel, ABC):
17
+ """
18
+ Base class for creating component models.
19
+
20
+ Attributes:
21
+ id_ (str): A 32-char unique hash identifier for the node.
22
+ timestamp (str): The timestamp of when the node was created.
23
+ """
24
+
25
+ id_: str = Field(
26
+ title="ID",
27
+ default_factory=SysUtil.create_id,
28
+ validation_alias=AliasChoices("node_id", "ID", "id"),
29
+ description="A 32-char unique hash identifier for the node.",
30
+ )
31
+
32
+ timestamp: str = Field(
33
+ default_factory=lambda: SysUtil.get_timestamp(sep=None),
34
+ description="The timestamp of when the node was created.",
35
+ )
36
+
37
+ class Config:
38
+ """Model configuration settings."""
39
+
40
+ extra = "allow"
41
+ arbitrary_types_allowed = True
42
+ populate_by_name = True
43
+ validate_assignment = True
44
+ validate_return = True
45
+ str_strip_whitespace = True
46
+
47
+ @classmethod
48
+ def class_name(cls) -> str:
49
+ """
50
+ Retrieve the name of the class.
51
+
52
+ Returns:
53
+ str: The name of the class.
54
+ """
55
+ return cls.__name__
56
+
57
+ @property
58
+ def _field_annotations(self) -> dict:
59
+ """
60
+ Return the annotations for each field in the model.
61
+
62
+ Returns:
63
+ dict: A dictionary mapping field names to their annotations.
64
+ """
65
+ return self._get_field_annotation(list(self.model_fields.keys()))
66
+
67
+ def _get_field_attr(self, k: str, attr: str, default=False) -> Any:
68
+ """
69
+ Get the value of a field attribute.
70
+
71
+ Args:
72
+ k (str): The field name.
73
+ attr (str): The attribute name.
74
+ default (Any): Default value to return if the attribute is not
75
+ found.
76
+
77
+ Returns:
78
+ Any: The value of the field attribute, or the default value if not
79
+ found.
80
+
81
+ Raises:
82
+ ValueError: If the field does not have the specified attribute.
83
+ """
84
+ try:
85
+ if not self._field_has_attr(k, attr):
86
+ raise ValueError(f"field {k} has no attribute {attr}")
87
+ field = self.model_fields[k]
88
+ a = getattr(field, attr)
89
+ if not a:
90
+ try:
91
+ a = field.json_schema_extra[attr]
92
+ return a
93
+ except Exception:
94
+ return None
95
+ return a
96
+ except Exception as e:
97
+ if default is not False:
98
+ return default
99
+ raise e
100
+
101
+ @singledispatchmethod
102
+ def _get_field_annotation(self, field_name: str) -> Any:
103
+ """
104
+ Get the annotation for a field.
105
+
106
+ Args:
107
+ field_name (str): The name of the field.
108
+
109
+ Raises:
110
+ TypeError: If the field_name is of an unsupported type.
111
+ """
112
+ raise TypeError(f"Unsupported type {type(field_name)}")
113
+
114
+ @_get_field_annotation.register(str)
115
+ def _(self, field_name) -> dict[str, Any]:
116
+ """
117
+ Get the annotation for a field as a dictionary.
118
+
119
+ Args:
120
+ field_name (str): The name of the field.
121
+
122
+ Returns:
123
+ dict[str, Any]: A dictionary mapping the field name to its
124
+ annotation.
125
+ """
126
+ dict_ = {field_name: self.model_fields[field_name].annotation}
127
+ for k, v in dict_.items():
128
+ if "|" in str(v):
129
+ v = str(v)
130
+ v = v.split("|")
131
+ dict_[k] = func_call.lcall(v, convert.strip_lower)
132
+ else:
133
+ dict_[k] = [v.__name__]
134
+ return dict_
135
+
136
+ @_get_field_annotation.register(list)
137
+ def _(self, field_name) -> dict[str, Any]:
138
+ """
139
+ Get the annotations for multiple fields as a dictionary.
140
+
141
+ Args:
142
+ field_name (list): A list or tuple of field names.
143
+
144
+ Returns:
145
+ dict[str, Any]: A dictionary mapping field names to their
146
+ annotations.
147
+ """
148
+ dict_ = {}
149
+ for i in field_name:
150
+ dict_.update(self._get_field_annotation(i))
151
+ return dict_
152
+
153
+ @_get_field_annotation.register(tuple)
154
+ def _(self, field_name) -> dict[str, Any]:
155
+ """
156
+ Get the annotations for multiple fields as a dictionary.
157
+
158
+ Args:
159
+ field_name (tuple): A list or tuple of field names.
160
+
161
+ Returns:
162
+ dict[str, Any]: A dictionary mapping field names to their
163
+ annotations.
164
+ """
165
+ dict_ = {}
166
+ for i in field_name:
167
+ dict_.update(self._get_field_annotation(i))
168
+ return dict_
169
+
170
+ def _field_has_attr(self, k: str, attr: str) -> bool:
171
+ """
172
+ Check if a field has a specific attribute.
173
+
174
+ Args:
175
+ k (str): The field name.
176
+ attr (str): The attribute name.
177
+
178
+ Returns:
179
+ bool: True if the field has the attribute, False otherwise.
180
+ """
181
+ field = self.model_fields.get(k, None)
182
+ if field is None:
183
+ raise ValueError(f"Field {k} not found in model fields.")
184
+
185
+ a = attr in str(field)
186
+ if not a:
187
+ try:
188
+ a = (
189
+ self.model_fields[k].json_schema_extra[attr] is not None
190
+ and attr in self.model_fields[k].json_schema_extra
191
+ )
192
+ return a if isinstance(a, bool) else False
193
+ except Exception:
194
+ return False
195
+ return a
196
+
197
+ def to_json_str(self, *args, **kwargs) -> str:
198
+ """
199
+ Convert the component to a JSON string.
200
+
201
+ Returns:
202
+ str: The JSON string representation of the component.
203
+ """
204
+ return self.model_dump_json(*args, by_alias=True, **kwargs)
205
+
206
+ def to_dict(self, *args, **kwargs) -> dict[str, Any]:
207
+ """
208
+ Convert the component to a dictionary.
209
+
210
+ Returns:
211
+ dict[str, Any]: The dictionary representation of the component.
212
+ """
213
+ return self.model_dump(*args, by_alias=True, **kwargs)
214
+
215
+ def to_xml(self) -> str:
216
+ """
217
+ Convert the component to an XML string.
218
+
219
+ Returns:
220
+ str: The XML string representation of the component.
221
+ """
222
+ import xml.etree.ElementTree as ET
223
+
224
+ root = ET.Element(self.__class__.__name__)
225
+
226
+ def convert(dict_obj, parent):
227
+ for key, val in dict_obj.items():
228
+ if isinstance(val, dict):
229
+ element = ET.SubElement(parent, key)
230
+ convert(val, element)
231
+ else:
232
+ element = ET.SubElement(parent, key)
233
+ element.text = str(val)
234
+
235
+ convert(self.to_dict(), root)
236
+ return ET.tostring(root, encoding="unicode")
237
+
238
+ def to_pd_series(self, *args, pd_kwargs: dict | None = None, **kwargs) -> Series:
239
+ """
240
+ Convert the node to a Pandas Series.
241
+
242
+ Returns:
243
+ Series: The Pandas Series representation of the node.
244
+ """
245
+ pd_kwargs = {} if pd_kwargs is None else pd_kwargs
246
+ dict_ = self.to_dict(*args, **kwargs)
247
+ return Series(dict_, **pd_kwargs)
248
+
249
+
250
+ class BaseNode(BaseComponent):
251
+ """
252
+ Base class for creating node models.
253
+
254
+ Attributes:
255
+ content (Any | None): The optional content of the node.
256
+ metadata (dict[str, Any]): Additional metadata for the node.
257
+ """
258
+
259
+ content: Any | None = Field(
260
+ default=None,
261
+ validation_alias=AliasChoices("text", "page_content", "chunk_content", "data"),
262
+ description="The optional content of the node.",
263
+ )
264
+
265
+ metadata: dict[str, Any] = Field(
266
+ default_factory=dict,
267
+ alias="meta",
268
+ description="Additional metadata for the node.",
269
+ )
270
+
271
+ @singledispatchmethod
272
+ @classmethod
273
+ def from_obj(cls, obj: Any, *args, **kwargs) -> T:
274
+ """
275
+ Create a node instance from an object.
276
+
277
+ Args:
278
+ obj (Any): The object to create the node from.
279
+
280
+ Raises:
281
+ NotImplementedError: If the object type is not supported.
282
+ """
283
+ raise NotImplementedError(f"Unsupported type: {type(obj)}")
284
+
285
+ @from_obj.register(dict)
286
+ @classmethod
287
+ def _from_dict(cls, obj, *args, **kwargs) -> T:
288
+ """
289
+ Create a node instance from a dictionary.
290
+
291
+ Args:
292
+ obj (dict): The dictionary to create the node from.
293
+
294
+ Returns:
295
+ T: The created node instance.
296
+ """
297
+ return cls.model_validate(obj, *args, **kwargs)
298
+
299
+ @from_obj.register(str)
300
+ @classmethod
301
+ def _from_str(cls, obj, *args, fuzzy_parse=False, **kwargs) -> T:
302
+ """
303
+ Create a node instance from a JSON string.
304
+
305
+ Args:
306
+ obj (str): The JSON string to create the node from.
307
+ fuzzy_parse (bool): Whether to perform fuzzy parsing.
308
+
309
+ Returns:
310
+ T: The created node instance.
311
+ """
312
+ obj = ParseUtil.fuzzy_parse_json(obj) if fuzzy_parse else convert.to_dict(obj)
313
+ try:
314
+ return cls.from_obj(obj, *args, **kwargs)
315
+ except ValidationError as e:
316
+ raise ValueError(f"Invalid JSON for deserialization: {e}") from e
317
+
318
+ @from_obj.register(list)
319
+ @classmethod
320
+ def _from_list(cls, obj, *args, **kwargs) -> list[T]:
321
+ """
322
+ Create a list of node instances from a list of objects.
323
+
324
+ Args:
325
+ obj (list): The list of objects to create nodes from.
326
+
327
+ Returns:
328
+ list[T]: The list of created node instances.
329
+ """
330
+ return [cls.from_obj(item, *args, **kwargs) for item in obj]
331
+
332
+ @from_obj.register(Series)
333
+ @classmethod
334
+ def _from_pd_series(cls, obj, *args, pd_kwargs=None, **kwargs) -> T:
335
+ """
336
+ Create a node instance from a Pandas Series.
337
+
338
+ Args:
339
+ obj (Series): The Pandas Series to create the node from.
340
+
341
+ Returns:
342
+ T: The created node instance.
343
+ """
344
+ if pd_kwargs is None:
345
+ pd_kwargs = {}
346
+ return cls.from_obj(obj.to_dict(**pd_kwargs), *args, **kwargs)
347
+
348
+ @from_obj.register(DataFrame)
349
+ @classmethod
350
+ def _from_pd_dataframe(cls, obj, *args, pd_kwargs=None, **kwargs) -> list[T]:
351
+ """
352
+ Create a list of node instances from a Pandas DataFrame.
353
+
354
+ Args:
355
+ obj (DataFrame): The Pandas DataFrame to create nodes from.
356
+
357
+ Returns:
358
+ list[T]: The list of created node instances.
359
+ """
360
+ if pd_kwargs is None:
361
+ pd_kwargs = {}
362
+ return [
363
+ cls.from_obj(row, *args, **pd_kwargs, **kwargs) for _, row in obj.iterrows()
364
+ ]
365
+
366
+ @from_obj.register(BaseModel)
367
+ @classmethod
368
+ def _from_base_model(cls, obj, pydantic_kwargs=None, **kwargs) -> T:
369
+ """
370
+ Create a node instance from a Pydantic BaseModel.
371
+
372
+ Args:
373
+ obj (BaseModel): The Pydantic BaseModel to create the node from.
374
+
375
+ Returns:
376
+ T: The created node instance.
377
+ """
378
+ if pydantic_kwargs is None:
379
+ pydantic_kwargs = {"by_alias": True}
380
+
381
+ config_ = {}
382
+ try:
383
+ config_ = obj.model_dump(**pydantic_kwargs)
384
+ except:
385
+ config_ = obj.dict(**pydantic_kwargs)
386
+
387
+ return cls.from_obj(config_ | kwargs)
388
+
389
+ def meta_get(
390
+ self, key: str, indices: list[str | int] = None, default: Any = None
391
+ ) -> Any:
392
+ """
393
+ Get a value from the metadata dictionary.
394
+
395
+ Args:
396
+ key (str): The key to retrieve the value for.
397
+ indices (list[str | int]): Optional list of indices for nested
398
+ retrieval.
399
+ default (Any): The default value to return if the key is not found.
400
+
401
+ Returns:
402
+ Any: The retrieved value or the default value if not found.
403
+ """
404
+ if indices:
405
+ return nested.nget(self.metadata, indices, default)
406
+ return self.metadata.get(key, default)
407
+
408
+ def meta_change_key(self, old_key: str, new_key: str) -> bool:
409
+ """
410
+ Change a key in the metadata dictionary.
411
+
412
+ Args:
413
+ old_key (str): The old key to be changed.
414
+ new_key (str): The new key to replace the old key.
415
+
416
+ Returns:
417
+ bool: True if the key was changed successfully, False otherwise.
418
+ """
419
+ if old_key in self.metadata:
420
+ SysUtil.change_dict_key(self.metadata, old_key, new_key)
421
+ return True
422
+ return False
423
+
424
+ def meta_insert(self, indices: str | list, value: Any, **kwargs) -> bool:
425
+ """
426
+ Insert a value into the metadata dictionary at the specified indices.
427
+
428
+ Args:
429
+ indices (str | list): The indices to insert the value at.
430
+ value (Any): The value to be inserted.
431
+ **kwargs: Additional keyword arguments for the `nested.ninsert`
432
+ function.
433
+
434
+ Returns:
435
+ bool: True if the value was inserted successfully, False otherwise.
436
+ """
437
+ return nested.ninsert(self.metadata, indices, value, **kwargs)
438
+
439
+ def meta_merge(
440
+ self, additional_metadata: dict[str, Any], overwrite: bool = False, **kwargs
441
+ ) -> None:
442
+ """
443
+ Merge additional metadata into the existing metadata dictionary.
444
+
445
+ Args:
446
+ additional_metadata (dict[str, Any]): The additional metadata to be
447
+ merged.
448
+ overwrite (bool): Whether to overwrite existing keys with the new
449
+ values.
450
+ **kwargs: Additional keyword arguments for the `nested.nmerge`
451
+ function.
452
+ """
453
+ self.metadata = nested.nmerge(
454
+ [self.metadata, additional_metadata], overwrite=overwrite, **kwargs
455
+ )
@@ -0,0 +1,44 @@
1
+ from abc import ABC, abstractmethod
2
+ from enum import Enum
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class ConditionSource(str, Enum):
7
+ """
8
+ Enumeration for specifying the source type of a condition.
9
+
10
+ Attributes:
11
+ STRUCTURE: Represents a condition based on the structure.
12
+ EXECUTABLE: Represents a condition that can be executed or evaluated.
13
+ """
14
+
15
+ STRUCTURE = "structure"
16
+ EXECUTABLE = "executable"
17
+
18
+
19
+ class Condition(BaseModel, ABC):
20
+ """
21
+ Abstract base class for defining conditions associated with edges.
22
+
23
+ Attributes:
24
+ source_type (ConditionSource): Specifies the type of source for the condition.
25
+
26
+ Methods:
27
+ check: Abstract method that should be implemented to evaluate the condition.
28
+ """
29
+
30
+ source_type: str = Field(..., description="The type of source for the condition.")
31
+
32
+ class Config:
33
+ """Model configuration settings."""
34
+
35
+ extra = "allow"
36
+
37
+ @abstractmethod
38
+ def __call__(self, executable) -> bool:
39
+ """Evaluates the condition based on implemented logic.
40
+
41
+ Returns:
42
+ bool: The boolean result of the condition evaluation.
43
+ """
44
+ pass