lionagi 0.1.2__py3-none-any.whl → 0.2.0__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.
Files changed (268) hide show
  1. lionagi/__init__.py +60 -5
  2. lionagi/core/__init__.py +0 -25
  3. lionagi/core/_setting/_setting.py +59 -0
  4. lionagi/core/action/__init__.py +14 -0
  5. lionagi/core/action/function_calling.py +136 -0
  6. lionagi/core/action/manual.py +1 -0
  7. lionagi/core/action/node.py +109 -0
  8. lionagi/core/action/tool.py +114 -0
  9. lionagi/core/action/tool_manager.py +356 -0
  10. lionagi/core/agent/base_agent.py +27 -13
  11. lionagi/core/agent/eval/evaluator.py +1 -0
  12. lionagi/core/agent/eval/vote.py +40 -0
  13. lionagi/core/agent/learn/learner.py +59 -0
  14. lionagi/core/agent/plan/unit_template.py +1 -0
  15. lionagi/core/collections/__init__.py +17 -0
  16. lionagi/core/{generic/data_logger.py → collections/_logger.py} +69 -55
  17. lionagi/core/collections/abc/__init__.py +53 -0
  18. lionagi/core/collections/abc/component.py +615 -0
  19. lionagi/core/collections/abc/concepts.py +297 -0
  20. lionagi/core/collections/abc/exceptions.py +150 -0
  21. lionagi/core/collections/abc/util.py +45 -0
  22. lionagi/core/collections/exchange.py +161 -0
  23. lionagi/core/collections/flow.py +426 -0
  24. lionagi/core/collections/model.py +419 -0
  25. lionagi/core/collections/pile.py +913 -0
  26. lionagi/core/collections/progression.py +236 -0
  27. lionagi/core/collections/util.py +64 -0
  28. lionagi/core/director/direct.py +314 -0
  29. lionagi/core/director/director.py +2 -0
  30. lionagi/core/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
  31. lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
  32. lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
  33. lionagi/core/executor/base_executor.py +90 -0
  34. lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +62 -66
  35. lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
  36. lionagi/core/generic/__init__.py +3 -33
  37. lionagi/core/generic/edge.py +29 -79
  38. lionagi/core/generic/edge_condition.py +16 -0
  39. lionagi/core/generic/graph.py +236 -0
  40. lionagi/core/generic/hyperedge.py +1 -0
  41. lionagi/core/generic/node.py +156 -221
  42. lionagi/core/generic/tree.py +48 -0
  43. lionagi/core/generic/tree_node.py +79 -0
  44. lionagi/core/mail/__init__.py +12 -0
  45. lionagi/core/mail/mail.py +25 -0
  46. lionagi/core/mail/mail_manager.py +139 -58
  47. lionagi/core/mail/package.py +45 -0
  48. lionagi/core/mail/start_mail.py +36 -0
  49. lionagi/core/message/__init__.py +19 -0
  50. lionagi/core/message/action_request.py +133 -0
  51. lionagi/core/message/action_response.py +135 -0
  52. lionagi/core/message/assistant_response.py +95 -0
  53. lionagi/core/message/instruction.py +234 -0
  54. lionagi/core/message/message.py +101 -0
  55. lionagi/core/message/system.py +86 -0
  56. lionagi/core/message/util.py +283 -0
  57. lionagi/core/report/__init__.py +4 -0
  58. lionagi/core/report/base.py +217 -0
  59. lionagi/core/report/form.py +231 -0
  60. lionagi/core/report/report.py +166 -0
  61. lionagi/core/report/util.py +28 -0
  62. lionagi/core/rule/_default.py +16 -0
  63. lionagi/core/rule/action.py +99 -0
  64. lionagi/core/rule/base.py +238 -0
  65. lionagi/core/rule/boolean.py +56 -0
  66. lionagi/core/rule/choice.py +47 -0
  67. lionagi/core/rule/mapping.py +96 -0
  68. lionagi/core/rule/number.py +71 -0
  69. lionagi/core/rule/rulebook.py +109 -0
  70. lionagi/core/rule/string.py +52 -0
  71. lionagi/core/rule/util.py +35 -0
  72. lionagi/core/session/branch.py +431 -0
  73. lionagi/core/session/directive_mixin.py +287 -0
  74. lionagi/core/session/session.py +229 -903
  75. lionagi/core/structure/__init__.py +1 -0
  76. lionagi/core/structure/chain.py +1 -0
  77. lionagi/core/structure/forest.py +1 -0
  78. lionagi/core/structure/graph.py +1 -0
  79. lionagi/core/structure/tree.py +1 -0
  80. lionagi/core/unit/__init__.py +5 -0
  81. lionagi/core/unit/parallel_unit.py +245 -0
  82. lionagi/core/unit/template/action.py +81 -0
  83. lionagi/core/unit/template/base.py +51 -0
  84. lionagi/core/unit/template/plan.py +84 -0
  85. lionagi/core/unit/template/predict.py +109 -0
  86. lionagi/core/unit/template/score.py +124 -0
  87. lionagi/core/unit/template/select.py +104 -0
  88. lionagi/core/unit/unit.py +362 -0
  89. lionagi/core/unit/unit_form.py +305 -0
  90. lionagi/core/unit/unit_mixin.py +1168 -0
  91. lionagi/core/unit/util.py +71 -0
  92. lionagi/core/validator/validator.py +364 -0
  93. lionagi/core/work/work.py +74 -0
  94. lionagi/core/work/work_function.py +92 -0
  95. lionagi/core/work/work_queue.py +81 -0
  96. lionagi/core/work/worker.py +195 -0
  97. lionagi/core/work/worklog.py +124 -0
  98. lionagi/experimental/compressor/base.py +46 -0
  99. lionagi/experimental/compressor/llm_compressor.py +247 -0
  100. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  101. lionagi/experimental/compressor/util.py +70 -0
  102. lionagi/experimental/directive/__init__.py +19 -0
  103. lionagi/experimental/directive/parser/base_parser.py +69 -2
  104. lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
  105. lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
  106. lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
  107. lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
  108. lionagi/experimental/knowledge/base.py +10 -0
  109. lionagi/experimental/memory/__init__.py +0 -0
  110. lionagi/experimental/strategies/__init__.py +0 -0
  111. lionagi/experimental/strategies/base.py +1 -0
  112. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  113. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  114. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  115. lionagi/integrations/chunker/chunk.py +161 -24
  116. lionagi/integrations/config/oai_configs.py +34 -3
  117. lionagi/integrations/config/openrouter_configs.py +14 -2
  118. lionagi/integrations/loader/load.py +122 -21
  119. lionagi/integrations/loader/load_util.py +6 -77
  120. lionagi/integrations/provider/_mapping.py +46 -0
  121. lionagi/integrations/provider/litellm.py +2 -1
  122. lionagi/integrations/provider/mlx_service.py +16 -9
  123. lionagi/integrations/provider/oai.py +91 -4
  124. lionagi/integrations/provider/ollama.py +6 -5
  125. lionagi/integrations/provider/openrouter.py +115 -8
  126. lionagi/integrations/provider/services.py +2 -2
  127. lionagi/integrations/provider/transformers.py +18 -22
  128. lionagi/integrations/storage/__init__.py +3 -3
  129. lionagi/integrations/storage/neo4j.py +52 -60
  130. lionagi/integrations/storage/storage_util.py +44 -46
  131. lionagi/integrations/storage/structure_excel.py +43 -26
  132. lionagi/integrations/storage/to_excel.py +11 -4
  133. lionagi/libs/__init__.py +22 -1
  134. lionagi/libs/ln_api.py +75 -20
  135. lionagi/libs/ln_context.py +37 -0
  136. lionagi/libs/ln_convert.py +21 -9
  137. lionagi/libs/ln_func_call.py +69 -28
  138. lionagi/libs/ln_image.py +107 -0
  139. lionagi/libs/ln_nested.py +26 -11
  140. lionagi/libs/ln_parse.py +82 -23
  141. lionagi/libs/ln_queue.py +16 -0
  142. lionagi/libs/ln_tokenize.py +164 -0
  143. lionagi/libs/ln_validate.py +16 -0
  144. lionagi/libs/special_tokens.py +172 -0
  145. lionagi/libs/sys_util.py +95 -24
  146. lionagi/lions/coder/code_form.py +13 -0
  147. lionagi/lions/coder/coder.py +50 -3
  148. lionagi/lions/coder/util.py +30 -25
  149. lionagi/tests/libs/test_func_call.py +23 -21
  150. lionagi/tests/libs/test_nested.py +36 -21
  151. lionagi/tests/libs/test_parse.py +1 -1
  152. lionagi/tests/test_core/collections/__init__.py +0 -0
  153. lionagi/tests/test_core/collections/test_component.py +206 -0
  154. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  155. lionagi/tests/test_core/collections/test_flow.py +145 -0
  156. lionagi/tests/test_core/collections/test_pile.py +171 -0
  157. lionagi/tests/test_core/collections/test_progression.py +129 -0
  158. lionagi/tests/test_core/generic/test_edge.py +67 -0
  159. lionagi/tests/test_core/generic/test_graph.py +96 -0
  160. lionagi/tests/test_core/generic/test_node.py +106 -0
  161. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  162. lionagi/tests/test_core/test_branch.py +115 -294
  163. lionagi/tests/test_core/test_form.py +46 -0
  164. lionagi/tests/test_core/test_report.py +105 -0
  165. lionagi/tests/test_core/test_validator.py +111 -0
  166. lionagi/version.py +1 -1
  167. lionagi-0.2.0.dist-info/LICENSE +202 -0
  168. lionagi-0.2.0.dist-info/METADATA +272 -0
  169. lionagi-0.2.0.dist-info/RECORD +240 -0
  170. lionagi/core/branch/base.py +0 -653
  171. lionagi/core/branch/branch.py +0 -474
  172. lionagi/core/branch/flow_mixin.py +0 -96
  173. lionagi/core/branch/util.py +0 -323
  174. lionagi/core/direct/__init__.py +0 -19
  175. lionagi/core/direct/cot.py +0 -123
  176. lionagi/core/direct/plan.py +0 -164
  177. lionagi/core/direct/predict.py +0 -166
  178. lionagi/core/direct/react.py +0 -171
  179. lionagi/core/direct/score.py +0 -279
  180. lionagi/core/direct/select.py +0 -170
  181. lionagi/core/direct/sentiment.py +0 -1
  182. lionagi/core/direct/utils.py +0 -110
  183. lionagi/core/direct/vote.py +0 -64
  184. lionagi/core/execute/base_executor.py +0 -47
  185. lionagi/core/flow/baseflow.py +0 -23
  186. lionagi/core/flow/monoflow/ReAct.py +0 -240
  187. lionagi/core/flow/monoflow/__init__.py +0 -9
  188. lionagi/core/flow/monoflow/chat.py +0 -95
  189. lionagi/core/flow/monoflow/chat_mixin.py +0 -253
  190. lionagi/core/flow/monoflow/followup.py +0 -215
  191. lionagi/core/flow/polyflow/__init__.py +0 -1
  192. lionagi/core/flow/polyflow/chat.py +0 -251
  193. lionagi/core/form/action_form.py +0 -26
  194. lionagi/core/form/field_validator.py +0 -287
  195. lionagi/core/form/form.py +0 -302
  196. lionagi/core/form/mixin.py +0 -214
  197. lionagi/core/form/scored_form.py +0 -13
  198. lionagi/core/generic/action.py +0 -26
  199. lionagi/core/generic/component.py +0 -532
  200. lionagi/core/generic/condition.py +0 -46
  201. lionagi/core/generic/mail.py +0 -90
  202. lionagi/core/generic/mailbox.py +0 -36
  203. lionagi/core/generic/relation.py +0 -70
  204. lionagi/core/generic/signal.py +0 -22
  205. lionagi/core/generic/structure.py +0 -362
  206. lionagi/core/generic/transfer.py +0 -20
  207. lionagi/core/generic/work.py +0 -40
  208. lionagi/core/graph/graph.py +0 -126
  209. lionagi/core/graph/tree.py +0 -190
  210. lionagi/core/mail/schema.py +0 -63
  211. lionagi/core/messages/schema.py +0 -325
  212. lionagi/core/tool/__init__.py +0 -5
  213. lionagi/core/tool/tool.py +0 -28
  214. lionagi/core/tool/tool_manager.py +0 -283
  215. lionagi/experimental/report/form.py +0 -64
  216. lionagi/experimental/report/report.py +0 -138
  217. lionagi/experimental/report/util.py +0 -47
  218. lionagi/experimental/tool/function_calling.py +0 -43
  219. lionagi/experimental/tool/manual.py +0 -66
  220. lionagi/experimental/tool/schema.py +0 -59
  221. lionagi/experimental/tool/tool_manager.py +0 -138
  222. lionagi/experimental/tool/util.py +0 -16
  223. lionagi/experimental/validator/rule.py +0 -139
  224. lionagi/experimental/validator/validator.py +0 -56
  225. lionagi/experimental/work/__init__.py +0 -10
  226. lionagi/experimental/work/async_queue.py +0 -54
  227. lionagi/experimental/work/schema.py +0 -73
  228. lionagi/experimental/work/work_function.py +0 -67
  229. lionagi/experimental/work/worker.py +0 -56
  230. lionagi/experimental/work2/form.py +0 -371
  231. lionagi/experimental/work2/report.py +0 -289
  232. lionagi/experimental/work2/schema.py +0 -30
  233. lionagi/experimental/work2/tests.py +0 -72
  234. lionagi/experimental/work2/work_function.py +0 -89
  235. lionagi/experimental/work2/worker.py +0 -12
  236. lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
  237. lionagi/tests/test_core/generic/test_component.py +0 -89
  238. lionagi/tests/test_core/test_base_branch.py +0 -426
  239. lionagi/tests/test_core/test_chat_flow.py +0 -63
  240. lionagi/tests/test_core/test_mail_manager.py +0 -75
  241. lionagi/tests/test_core/test_prompts.py +0 -51
  242. lionagi/tests/test_core/test_session.py +0 -254
  243. lionagi/tests/test_core/test_session_base_util.py +0 -313
  244. lionagi/tests/test_core/test_tool_manager.py +0 -95
  245. lionagi-0.1.2.dist-info/LICENSE +0 -9
  246. lionagi-0.1.2.dist-info/METADATA +0 -174
  247. lionagi-0.1.2.dist-info/RECORD +0 -206
  248. /lionagi/core/{branch → _setting}/__init__.py +0 -0
  249. /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
  250. /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
  251. /lionagi/core/{form → agent/plan}/__init__.py +0 -0
  252. /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
  253. /lionagi/core/{graph → director}/__init__.py +0 -0
  254. /lionagi/core/{messages → engine}/__init__.py +0 -0
  255. /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
  256. /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
  257. /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
  258. /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
  259. /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
  260. /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
  261. /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
  262. /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
  263. /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
  264. /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
  265. /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
  266. /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
  267. {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,238 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from abc import abstractmethod
18
+ from typing import Any, List, Dict
19
+ from pandas import Series
20
+ from lionagi.libs import SysUtil
21
+
22
+ from lionagi.core.collections.abc import FieldError, Condition, Actionable, Component
23
+
24
+ _rule_classes = {}
25
+
26
+
27
+ class Rule(Component, Condition, Actionable):
28
+ """
29
+ Combines a condition and an action that can be applied based on it.
30
+
31
+ Attributes:
32
+ apply_type (str): The type of data to which the rule applies.
33
+ fix (bool): Indicates whether the rule includes a fix action.
34
+ fields (list[str]): List of fields to which the rule applies.
35
+ validation_kwargs (dict): Keyword arguments for validation.
36
+ applied_log (list): Log of applied rules.
37
+ invoked_log (list): Log of invoked rules.
38
+ _is_init (bool): Indicates whether the rule is initialized.
39
+ """
40
+
41
+ exclude_type: list[str] = []
42
+ apply_type: list[str] | str = None
43
+ fix: bool = True
44
+ fields: list[str] = []
45
+ validation_kwargs: dict = {}
46
+ applied_log: list = []
47
+ invoked_log: list = []
48
+ _is_init: bool = False
49
+
50
+ def __init_subclass__(cls, **kwargs):
51
+ super().__init_subclass__(**kwargs)
52
+ if cls.__name__ not in _rule_classes:
53
+ _rule_classes[cls.__name__] = cls
54
+
55
+ def add_log(self, field: str, form: Any, apply: bool = True, **kwargs) -> None:
56
+ """
57
+ Adds an entry to the applied or invoked log.
58
+
59
+ Args:
60
+ field (str): The field being validated.
61
+ form (Any): The form being validated.
62
+ apply (bool): Indicates whether the log is for an applied rule.
63
+ **kwargs: Additional configuration parameters.
64
+ """
65
+ a = {
66
+ "type": "rule",
67
+ "class": self.class_name,
68
+ "ln_id": self.ln_id,
69
+ "timestamp": SysUtil.get_timestamp(sep=None)[:-6],
70
+ "field": field,
71
+ "form": form.ln_id,
72
+ "config": kwargs,
73
+ }
74
+ if apply:
75
+ self.applied_log.append(a)
76
+ else:
77
+ self.invoked_log.append(a)
78
+
79
+ async def applies(
80
+ self,
81
+ field: str,
82
+ value: Any,
83
+ form: Any,
84
+ *args,
85
+ annotation: List[str] = None,
86
+ use_annotation: bool = True,
87
+ **kwargs,
88
+ ) -> bool:
89
+ """
90
+ Determines whether the rule applies to a given field and value.
91
+
92
+ Args:
93
+ field (str): The field being validated.
94
+ value (Any): The value of the field.
95
+ form (Any): The form being validated.
96
+ *args: Additional arguments.
97
+ annotation (list[str], optional): Annotations for the field.
98
+ use_annotation (bool): Indicates whether to use annotations.
99
+ **kwargs: Additional keyword arguments.
100
+
101
+ Returns:
102
+ bool: True if the rule applies, otherwise False.
103
+ """
104
+ if self.fields:
105
+ if field in self.fields:
106
+ self.add_log(field, form, **kwargs)
107
+ return True
108
+
109
+ if use_annotation:
110
+ annotation = annotation or form._get_field_annotation(field)
111
+ annotation = [annotation] if isinstance(annotation, str) else annotation
112
+
113
+ for i in annotation:
114
+ if i in self.apply_type and i not in self.exclude_type:
115
+ self.add_log(field, form, **kwargs)
116
+ return True
117
+ return False
118
+
119
+ a = await self.rule_condition(field, value, *args, **kwargs)
120
+
121
+ if a:
122
+ self.add_log(field, form, **kwargs)
123
+ return True
124
+ return False
125
+
126
+ async def invoke(self, field: str, value: Any, form: Any) -> Any:
127
+ """
128
+ Invokes the rule's validation logic on a field and value.
129
+
130
+ Args:
131
+ field (str): The field being validated.
132
+ value (Any): The value of the field.
133
+ form (Any): The form being validated.
134
+
135
+ Returns:
136
+ Any: The validated or fixed value.
137
+
138
+ Raises:
139
+ ValueError: If validation or fixing fails.
140
+ """
141
+ try:
142
+ a = await self.validate(value, **self.validation_kwargs)
143
+ self.add_log(field, form, apply=False, **self.validation_kwargs)
144
+ return a
145
+
146
+ except Exception as e1:
147
+ if self.fix:
148
+ try:
149
+ a = await self.perform_fix(value, **self.validation_kwargs)
150
+ self.add_log(field, form, apply=False, **self.validation_kwargs)
151
+ return a
152
+ except Exception as e2:
153
+ raise FieldError(f"failed to fix field") from e2
154
+ raise FieldError(f"failed to validate field") from e1
155
+
156
+ async def rule_condition(self, field, value, *args, **kwargs) -> bool:
157
+ """
158
+ Additional condition, if choosing not to use annotation as a qualifier.
159
+
160
+ Args:
161
+ *args: Additional arguments.
162
+ **kwargs: Additional keyword arguments.
163
+
164
+ Returns:
165
+ bool: False by default, should be overridden by subclasses.
166
+ """
167
+ return False
168
+
169
+ async def perform_fix(self, value: Any, *args, **kwargs) -> Any:
170
+ """
171
+ Attempts to fix a value if validation fails.
172
+
173
+ Args:
174
+ value (Any): The value to fix.
175
+ *args: Additional arguments.
176
+ **kwargs: Additional keyword arguments.
177
+
178
+ Returns:
179
+ Any: The fixed value.
180
+
181
+ Raises:
182
+ ValueError: If the fix fails.
183
+ """
184
+ return value
185
+
186
+ @abstractmethod
187
+ async def validate(self, value: Any) -> Any:
188
+ """
189
+ Abstract method to validate a value.
190
+
191
+ Args:
192
+ value (Any): The value to validate.
193
+
194
+ Returns:
195
+ Any: The validated value.
196
+
197
+ Raises:
198
+ ValueError: If validation fails.
199
+ """
200
+ pass
201
+
202
+ def _to_dict(self) -> Dict[str, Any]:
203
+ """
204
+ Converts the rule's attributes to a dictionary.
205
+
206
+ Returns:
207
+ dict: A dictionary representation of the rule.
208
+ """
209
+ return {
210
+ "ln_id": self.ln_id[:8] + "...",
211
+ "rule": self.__class__.__name__,
212
+ "apply_type": self.apply_type,
213
+ "fix": self.fix,
214
+ "fields": self.fields,
215
+ "validation_kwargs": self.validation_kwargs,
216
+ "num_applied": len(self.applied_log),
217
+ "num_invoked": len(self.invoked_log),
218
+ }
219
+
220
+ def __str__(self) -> str:
221
+ """
222
+ Returns a string representation of the rule using a pandas Series.
223
+
224
+ Returns:
225
+ str: A string representation of the rule.
226
+ """
227
+ series = Series(self._to_dict())
228
+ return series.__str__()
229
+
230
+ def __repr__(self) -> str:
231
+ """
232
+ Returns a string representation of the rule using a pandas Series.
233
+
234
+ Returns:
235
+ str: A string representation of the rule.
236
+ """
237
+ series = Series(self._to_dict())
238
+ return series.__repr__()
@@ -0,0 +1,56 @@
1
+ from typing import Any
2
+ from lionagi.libs.ln_convert import to_str, strip_lower
3
+ from lionagi.core.rule.base import Rule
4
+
5
+
6
+ class BooleanRule(Rule):
7
+ """
8
+ Rule for validating that a value is a boolean.
9
+
10
+ Attributes:
11
+ apply_type (str): The type of data to which the rule applies.
12
+ """
13
+
14
+ fields: list[str] = ["action_required"]
15
+
16
+ def __init__(self, apply_type="bool", **kwargs):
17
+ super().__init__(apply_type=apply_type, **kwargs)
18
+
19
+ async def validate(self, value: Any) -> bool:
20
+ """
21
+ Validate that the value is a boolean.
22
+
23
+ Args:
24
+ value (Any): The value to validate.
25
+
26
+ Returns:
27
+ bool: The validated value.
28
+
29
+ Raises:
30
+ ValueError: If the value is not a valid boolean.
31
+ """
32
+ if isinstance(value, bool):
33
+ return value
34
+ raise ValueError(f"Invalid boolean value.")
35
+
36
+ async def perform_fix(self, value: Any) -> bool:
37
+ """
38
+ Attempt to fix the value by converting it to a boolean.
39
+
40
+ Args:
41
+ value (Any): The value to fix.
42
+
43
+ Returns:
44
+ bool: The fixed value.
45
+
46
+ Raises:
47
+ ValueError: If the value cannot be converted to a boolean.
48
+ """
49
+ value = strip_lower(to_str(value))
50
+ if value in ["true", "1", "correct", "yes"]:
51
+ return True
52
+
53
+ elif value in ["false", "0", "incorrect", "no", "none", "n/a"]:
54
+ return False
55
+
56
+ raise ValueError(f"Failed to convert {value} into a boolean value")
@@ -0,0 +1,47 @@
1
+ from lionagi.libs.ln_parse import StringMatch
2
+ from lionagi.core.rule.base import Rule
3
+
4
+
5
+ class ChoiceRule(Rule):
6
+ """
7
+ Rule for validating that a value is within a set of predefined choices.
8
+
9
+ Attributes:
10
+ apply_type (str): The type of data to which the rule applies.
11
+ keys (list): The list of valid choices.
12
+ """
13
+
14
+ def __init__(self, apply_type="enum", **kwargs):
15
+ super().__init__(apply_type=apply_type, **kwargs)
16
+ self.keys = self.validation_kwargs.get("keys", None)
17
+
18
+ async def validate(self, value: str, *args, **kwargs) -> str:
19
+ """
20
+ Validate that the value is within the set of predefined choices.
21
+
22
+ Args:
23
+ value (str): The value to validate.
24
+ *args: Additional arguments.
25
+ **kwargs: Additional keyword arguments.
26
+
27
+ Returns:
28
+ str: The validated value.
29
+
30
+ Raises:
31
+ ValueError: If the value is not in the set of choices.
32
+ """
33
+ if not value in self.keys:
34
+ raise ValueError(f"{value} is not in chocies {self.keys}")
35
+ return value
36
+
37
+ async def perform_fix(self, value):
38
+ """
39
+ Suggest a fix for a value that is not within the set of predefined choices.
40
+
41
+ Args:
42
+ value (str): The value to suggest a fix for.
43
+
44
+ Returns:
45
+ str: The most similar value from the set of predefined choices.
46
+ """
47
+ return StringMatch.choose_most_similar(value, self.keys)
@@ -0,0 +1,96 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from typing import Any
18
+ from collections.abc import Mapping
19
+ from lionagi.libs.ln_convert import to_dict
20
+ from lionagi.libs import StringMatch, ParseUtil
21
+
22
+ from lionagi.core.rule.choice import ChoiceRule
23
+
24
+
25
+ class MappingRule(ChoiceRule):
26
+ """
27
+ Rule for validating that a value is a mapping (dictionary) with specific keys.
28
+
29
+ Attributes:
30
+ apply_type (str): The type of data to which the rule applies.
31
+ """
32
+
33
+ def __init__(self, apply_type="dict", **kwargs):
34
+ super().__init__(apply_type=apply_type, **kwargs)
35
+
36
+ async def validate(self, value: Any, *args, **kwargs) -> Any:
37
+ """
38
+ Validate that the value is a mapping with specific keys.
39
+
40
+ Args:
41
+ value (Any): The value to validate.
42
+ *args: Additional arguments.
43
+ **kwargs: Additional keyword arguments.
44
+
45
+ Returns:
46
+ Any: The validated value.
47
+
48
+ Raises:
49
+ ValueError: If the value is not a valid mapping or has incorrect keys.
50
+ """
51
+ if not isinstance(value, Mapping):
52
+ raise ValueError("Invalid mapping field type.")
53
+
54
+ if self.keys:
55
+ if (keys := set(value.keys())) != set(self.keys):
56
+ raise ValueError(
57
+ f"Invalid mapping keys. Current keys {[i for i in keys]} != {self.keys}"
58
+ )
59
+ return value
60
+
61
+ async def perform_fix(self, value: Any, *args, **kwargs) -> Any:
62
+ """
63
+ Attempt to fix the value by converting it to a dict and validating its keys.
64
+
65
+ Args:
66
+ value (Any): The value to fix.
67
+ *args: Additional arguments.
68
+ **kwargs: Additional keyword arguments.
69
+
70
+ Returns:
71
+ Any: The fixed value.
72
+
73
+ Raises:
74
+ ValueError: If the value cannot be fixed.
75
+ """
76
+ if not isinstance(value, dict):
77
+ try:
78
+ value = to_dict(value)
79
+ except Exception as e:
80
+ raise ValueError("Invalid dict field type.") from e
81
+
82
+ if self.keys:
83
+ check_keys = set(value.keys())
84
+ if check_keys != set(self.keys):
85
+ try:
86
+ return StringMatch.force_validate_dict(value, keys=self.keys)
87
+ except Exception as e:
88
+ raise ValueError("Invalid dict keys.") from e
89
+
90
+ else:
91
+ try:
92
+ return ParseUtil.fuzzy_parse_json(value)
93
+ except Exception as e:
94
+ raise ValueError("Invalid dict keys.") from e
95
+
96
+ return value
@@ -0,0 +1,71 @@
1
+ from typing import Any
2
+ from lionagi.libs.ln_convert import to_num
3
+ from lionagi.core.rule.base import Rule
4
+
5
+
6
+ class NumberRule(Rule):
7
+ """
8
+ Rule for validating that a value is a number within specified bounds.
9
+
10
+ Attributes:
11
+ apply_type (str): The type of data to which the rule applies.
12
+ upper_bound (int | float | None): The upper bound for the value.
13
+ lower_bound (int | float | None): The lower bound for the value.
14
+ num_type (Type[int | float]): The type of number (int or float).
15
+ precision (int | None): The precision for floating point numbers.
16
+ """
17
+
18
+ fields: list[str] = ["confidence_score", "score"]
19
+
20
+ def __init__(self, apply_type="int, float", **kwargs):
21
+ super().__init__(apply_type=apply_type, **kwargs)
22
+ self.upper_bound = self.validation_kwargs.get("upper_bound")
23
+ self.lower_bound = self.validation_kwargs.get("lower_bound")
24
+ self.num_type = self.validation_kwargs.get("num_type", float)
25
+ self.precision = self.validation_kwargs.get("precision")
26
+
27
+ async def validate(self, value: Any) -> Any:
28
+ """
29
+ Validate that the value is a number.
30
+
31
+ Args:
32
+ value (Any): The value to validate.
33
+
34
+ Returns:
35
+ Any: The validated value.
36
+
37
+ Raises:
38
+ ValueError: If the value is not a valid number.
39
+ """
40
+ if isinstance(value, (int, float)):
41
+ return value
42
+ raise ValueError(f"Invalid number field: {value}")
43
+
44
+ async def perform_fix(self, value: Any) -> Any:
45
+ """
46
+ Attempt to fix the value by converting it to a number.
47
+
48
+ Args:
49
+ value (Any): The value to fix.
50
+
51
+ Returns:
52
+ Any: The fixed value.
53
+
54
+ Raises:
55
+ ValueError: If the value cannot be converted to a number.
56
+ """
57
+ if isinstance(value, (int, float)):
58
+ return value
59
+
60
+ value = to_num(
61
+ value,
62
+ **{
63
+ k: v
64
+ for k, v in self.validation_kwargs.items()
65
+ if k in ["num_type", "precision", "upper_bound", "lower_bound"]
66
+ },
67
+ )
68
+
69
+ if isinstance(value, (int, float)):
70
+ return value
71
+ raise ValueError(f"Failed to convert {value} into a numeric value")
@@ -0,0 +1,109 @@
1
+ from lionagi.libs.ln_func_call import lcall
2
+ from lionagi.core.rule.base import Rule
3
+
4
+
5
+ """
6
+ rule config schema
7
+
8
+ {
9
+ rule_name: {
10
+ "fields: [],
11
+ "config": {},
12
+ ...
13
+ }
14
+ }
15
+ """
16
+
17
+
18
+ class RuleBook:
19
+
20
+ def __init__(
21
+ self,
22
+ rules: dict[str, Rule] | list[Rule] = None,
23
+ ruleorder: list[str] = None,
24
+ rule_config: dict[str, dict] = None,
25
+ ):
26
+ self.rules = rules
27
+ self.ruleorder = ruleorder
28
+ self.rule_config = rule_config or {k: {} for k in self.ruleorder}
29
+
30
+ @property
31
+ def _all_applied_log(self):
32
+ """return all applied logs from all rules in the rulebook"""
33
+ return lcall(self.rules.values(), lambda x: x.applied_log, flatten=True)
34
+
35
+ @property
36
+ def _all_invoked_log(self):
37
+ """return all invoked logs from all rules in the rulebook"""
38
+ return lcall(self.rules.values(), lambda x: x.invoked_log, flatten=True)
39
+
40
+ def __getitem__(self, key: str) -> Rule:
41
+ return self.rules[key]
42
+
43
+ # def add_rule(self, rule_name: str, rule: Rule, config: dict = None):
44
+ # if rule_name in self.rules:
45
+ # raise ValueError(f"Rule '{rule_name}' already exists.")
46
+ # self.rules[rule_name] = rule
47
+ # self.ruleorder.append(rule_name)
48
+ # self.rule_config[rule_name] = config or {}
49
+
50
+ # def remove_rule(self, rule_name: str):
51
+ # if rule_name not in self.rules:
52
+ # raise ValueError(f"Rule '{rule_name}' does not exist.")
53
+ # del self.rules[rule_name]
54
+ # self.ruleorder.remove(rule_name)
55
+ # del self.rule_config[rule_name]
56
+
57
+ # def update_rule_config(self, rule_name: str, config: dict):
58
+ # if rule_name not in self.rules:
59
+ # raise ValueError(f"Rule '{rule_name}' does not exist.")
60
+ # self.rule_config[rule_name] = config
61
+
62
+ # def list_rules(self) -> list[str]:
63
+ # return self.ruleorder
64
+
65
+ # def get_rule_details(self, rule_name: str) -> dict:
66
+ # if rule_name not in self.rules:
67
+ # raise ValueError(f"Rule '{rule_name}' does not exist.")
68
+ # return {
69
+ # "rule": self.rules[rule_name],
70
+ # "config": self.rule_config[rule_name]
71
+ # }
72
+
73
+ # async def validate_data(self, data: Any) -> bool:
74
+ # for rule in self.rules.values():
75
+ # if not await rule.validate(data):
76
+ # return False
77
+ # return True
78
+
79
+ # def export_rulebook(self, filepath: str):
80
+ # import json
81
+ # with open(filepath, 'w') as f:
82
+ # json.dump({
83
+ # "rules": list(self.rules.keys()),
84
+ # "ruleorder": self.ruleorder,
85
+ # "rule_config": self.rule_config
86
+ # }, f)
87
+
88
+ # @classmethod
89
+ # def import_rulebook(cls, filepath: str) -> 'RuleBook':
90
+ # import json
91
+ # with open(filepath, 'r') as f:
92
+ # config = json.load(f)
93
+ # rules = {name: Rule() for name in config["rules"]}
94
+ # return cls(rules=rules, ruleorder=config["ruleorder"], rule_config=config["rule_config"])
95
+
96
+ # def enable_rule(self, rule_name: str, enable: bool = True):
97
+ # if rule_name not in self.rules:
98
+ # raise ValueError(f"Rule '{rule_name}' does not exist.")
99
+ # self.rules[rule_name].enabled = enable
100
+
101
+ # def log_rule_application(self, rule_name: str, data: Any):
102
+ # if rule_name not in self.rules:
103
+ # raise ValueError(f"Rule '{rule_name}' does not exist.")
104
+ # log_entry = {
105
+ # "rule": rule_name,
106
+ # "data": data,
107
+ # "timestamp": SysUtil.get_timestamp()
108
+ # }
109
+ # # Append log_entry to a log file or a logging system
@@ -0,0 +1,52 @@
1
+ from lionagi.libs.ln_convert import to_str
2
+ from lionagi.core.rule.base import Rule
3
+
4
+
5
+ class StringRule(Rule):
6
+ """
7
+ Rule for validating and converting string values.
8
+
9
+ Attributes:
10
+ fields (list[str]): The list of fields to which the rule applies.
11
+ apply_type (str): The type of data to which the rule applies.
12
+ """
13
+
14
+ fields: list[str] = ["reason", "prediction", "answer"]
15
+
16
+ def __init__(self, apply_type="str", **kwargs):
17
+ super().__init__(apply_type=apply_type, **kwargs)
18
+
19
+ async def validate(self, value):
20
+ """
21
+ Validate that the value is a string.
22
+
23
+ Args:
24
+ value: The value to validate.
25
+
26
+ Returns:
27
+ str: The validated string value.
28
+
29
+ Raises:
30
+ ValueError: If the value is not a string or is an empty string.
31
+ """
32
+ if isinstance(value, str) or value == "":
33
+ return value
34
+ raise ValueError(f"Invalid string field type.")
35
+
36
+ async def perform_fix(self, value):
37
+ """
38
+ Attempt to convert a value to a string.
39
+
40
+ Args:
41
+ value: The value to convert to a string.
42
+
43
+ Returns:
44
+ str: The value converted to a string.
45
+
46
+ Raises:
47
+ ValueError: If the value cannot be converted to a string.
48
+ """
49
+ try:
50
+ return to_str(value, **self.validation_kwargs)
51
+ except Exception as e:
52
+ raise ValueError(f"Failed to convert {value} into a string value") from e