lionagi 0.0.312__py3-none-any.whl → 0.2.1__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 +61 -3
  2. lionagi/core/__init__.py +0 -14
  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/__init__.py +0 -3
  11. lionagi/core/agent/base_agent.py +45 -36
  12. lionagi/core/agent/eval/evaluator.py +1 -0
  13. lionagi/core/agent/eval/vote.py +40 -0
  14. lionagi/core/agent/learn/learner.py +59 -0
  15. lionagi/core/agent/plan/unit_template.py +1 -0
  16. lionagi/core/collections/__init__.py +17 -0
  17. lionagi/core/collections/_logger.py +319 -0
  18. lionagi/core/collections/abc/__init__.py +53 -0
  19. lionagi/core/collections/abc/component.py +615 -0
  20. lionagi/core/collections/abc/concepts.py +297 -0
  21. lionagi/core/collections/abc/exceptions.py +150 -0
  22. lionagi/core/collections/abc/util.py +45 -0
  23. lionagi/core/collections/exchange.py +161 -0
  24. lionagi/core/collections/flow.py +426 -0
  25. lionagi/core/collections/model.py +419 -0
  26. lionagi/core/collections/pile.py +913 -0
  27. lionagi/core/collections/progression.py +236 -0
  28. lionagi/core/collections/util.py +64 -0
  29. lionagi/core/director/direct.py +314 -0
  30. lionagi/core/director/director.py +2 -0
  31. lionagi/core/engine/branch_engine.py +333 -0
  32. lionagi/core/engine/instruction_map_engine.py +204 -0
  33. lionagi/core/engine/sandbox_.py +14 -0
  34. lionagi/core/engine/script_engine.py +99 -0
  35. lionagi/core/executor/base_executor.py +90 -0
  36. lionagi/core/executor/graph_executor.py +330 -0
  37. lionagi/core/executor/neo4j_executor.py +384 -0
  38. lionagi/core/generic/__init__.py +7 -0
  39. lionagi/core/generic/edge.py +112 -0
  40. lionagi/core/generic/edge_condition.py +16 -0
  41. lionagi/core/generic/graph.py +236 -0
  42. lionagi/core/generic/hyperedge.py +1 -0
  43. lionagi/core/generic/node.py +220 -0
  44. lionagi/core/generic/tree.py +48 -0
  45. lionagi/core/generic/tree_node.py +79 -0
  46. lionagi/core/mail/__init__.py +7 -3
  47. lionagi/core/mail/mail.py +25 -0
  48. lionagi/core/mail/mail_manager.py +142 -58
  49. lionagi/core/mail/package.py +45 -0
  50. lionagi/core/mail/start_mail.py +36 -0
  51. lionagi/core/message/__init__.py +19 -0
  52. lionagi/core/message/action_request.py +133 -0
  53. lionagi/core/message/action_response.py +135 -0
  54. lionagi/core/message/assistant_response.py +95 -0
  55. lionagi/core/message/instruction.py +234 -0
  56. lionagi/core/message/message.py +101 -0
  57. lionagi/core/message/system.py +86 -0
  58. lionagi/core/message/util.py +283 -0
  59. lionagi/core/report/__init__.py +4 -0
  60. lionagi/core/report/base.py +217 -0
  61. lionagi/core/report/form.py +231 -0
  62. lionagi/core/report/report.py +166 -0
  63. lionagi/core/report/util.py +28 -0
  64. lionagi/core/rule/__init__.py +0 -0
  65. lionagi/core/rule/_default.py +16 -0
  66. lionagi/core/rule/action.py +99 -0
  67. lionagi/core/rule/base.py +238 -0
  68. lionagi/core/rule/boolean.py +56 -0
  69. lionagi/core/rule/choice.py +47 -0
  70. lionagi/core/rule/mapping.py +96 -0
  71. lionagi/core/rule/number.py +71 -0
  72. lionagi/core/rule/rulebook.py +109 -0
  73. lionagi/core/rule/string.py +52 -0
  74. lionagi/core/rule/util.py +35 -0
  75. lionagi/core/session/__init__.py +0 -3
  76. lionagi/core/session/branch.py +431 -0
  77. lionagi/core/session/directive_mixin.py +287 -0
  78. lionagi/core/session/session.py +230 -902
  79. lionagi/core/structure/__init__.py +1 -0
  80. lionagi/core/structure/chain.py +1 -0
  81. lionagi/core/structure/forest.py +1 -0
  82. lionagi/core/structure/graph.py +1 -0
  83. lionagi/core/structure/tree.py +1 -0
  84. lionagi/core/unit/__init__.py +5 -0
  85. lionagi/core/unit/parallel_unit.py +245 -0
  86. lionagi/core/unit/template/__init__.py +0 -0
  87. lionagi/core/unit/template/action.py +81 -0
  88. lionagi/core/unit/template/base.py +51 -0
  89. lionagi/core/unit/template/plan.py +84 -0
  90. lionagi/core/unit/template/predict.py +109 -0
  91. lionagi/core/unit/template/score.py +124 -0
  92. lionagi/core/unit/template/select.py +104 -0
  93. lionagi/core/unit/unit.py +362 -0
  94. lionagi/core/unit/unit_form.py +305 -0
  95. lionagi/core/unit/unit_mixin.py +1168 -0
  96. lionagi/core/unit/util.py +71 -0
  97. lionagi/core/validator/__init__.py +0 -0
  98. lionagi/core/validator/validator.py +364 -0
  99. lionagi/core/work/__init__.py +0 -0
  100. lionagi/core/work/work.py +76 -0
  101. lionagi/core/work/work_function.py +101 -0
  102. lionagi/core/work/work_queue.py +103 -0
  103. lionagi/core/work/worker.py +258 -0
  104. lionagi/core/work/worklog.py +120 -0
  105. lionagi/experimental/__init__.py +0 -0
  106. lionagi/experimental/compressor/__init__.py +0 -0
  107. lionagi/experimental/compressor/base.py +46 -0
  108. lionagi/experimental/compressor/llm_compressor.py +247 -0
  109. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  110. lionagi/experimental/compressor/util.py +70 -0
  111. lionagi/experimental/directive/__init__.py +19 -0
  112. lionagi/experimental/directive/parser/__init__.py +0 -0
  113. lionagi/experimental/directive/parser/base_parser.py +282 -0
  114. lionagi/experimental/directive/template/__init__.py +0 -0
  115. lionagi/experimental/directive/template/base_template.py +79 -0
  116. lionagi/experimental/directive/template/schema.py +36 -0
  117. lionagi/experimental/directive/tokenizer.py +73 -0
  118. lionagi/experimental/evaluator/__init__.py +0 -0
  119. lionagi/experimental/evaluator/ast_evaluator.py +131 -0
  120. lionagi/experimental/evaluator/base_evaluator.py +218 -0
  121. lionagi/experimental/knowledge/__init__.py +0 -0
  122. lionagi/experimental/knowledge/base.py +10 -0
  123. lionagi/experimental/knowledge/graph.py +0 -0
  124. lionagi/experimental/memory/__init__.py +0 -0
  125. lionagi/experimental/strategies/__init__.py +0 -0
  126. lionagi/experimental/strategies/base.py +1 -0
  127. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  128. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  129. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  130. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  131. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  132. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  133. lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
  134. lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
  135. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  136. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  137. lionagi/integrations/chunker/__init__.py +0 -0
  138. lionagi/integrations/chunker/chunk.py +312 -0
  139. lionagi/integrations/config/oai_configs.py +38 -7
  140. lionagi/integrations/config/ollama_configs.py +1 -1
  141. lionagi/integrations/config/openrouter_configs.py +14 -2
  142. lionagi/integrations/loader/__init__.py +0 -0
  143. lionagi/integrations/loader/load.py +253 -0
  144. lionagi/integrations/loader/load_util.py +195 -0
  145. lionagi/integrations/provider/_mapping.py +46 -0
  146. lionagi/integrations/provider/litellm.py +2 -1
  147. lionagi/integrations/provider/mlx_service.py +16 -9
  148. lionagi/integrations/provider/oai.py +91 -4
  149. lionagi/integrations/provider/ollama.py +7 -6
  150. lionagi/integrations/provider/openrouter.py +115 -8
  151. lionagi/integrations/provider/services.py +2 -2
  152. lionagi/integrations/provider/transformers.py +18 -22
  153. lionagi/integrations/storage/__init__.py +3 -0
  154. lionagi/integrations/storage/neo4j.py +665 -0
  155. lionagi/integrations/storage/storage_util.py +287 -0
  156. lionagi/integrations/storage/structure_excel.py +285 -0
  157. lionagi/integrations/storage/to_csv.py +63 -0
  158. lionagi/integrations/storage/to_excel.py +83 -0
  159. lionagi/libs/__init__.py +26 -1
  160. lionagi/libs/ln_api.py +78 -23
  161. lionagi/libs/ln_context.py +37 -0
  162. lionagi/libs/ln_convert.py +21 -9
  163. lionagi/libs/ln_func_call.py +69 -28
  164. lionagi/libs/ln_image.py +107 -0
  165. lionagi/libs/ln_knowledge_graph.py +405 -0
  166. lionagi/libs/ln_nested.py +26 -11
  167. lionagi/libs/ln_parse.py +110 -14
  168. lionagi/libs/ln_queue.py +117 -0
  169. lionagi/libs/ln_tokenize.py +164 -0
  170. lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
  171. lionagi/libs/special_tokens.py +172 -0
  172. lionagi/libs/sys_util.py +107 -2
  173. lionagi/lions/__init__.py +0 -0
  174. lionagi/lions/coder/__init__.py +0 -0
  175. lionagi/lions/coder/add_feature.py +20 -0
  176. lionagi/lions/coder/base_prompts.py +22 -0
  177. lionagi/lions/coder/code_form.py +13 -0
  178. lionagi/lions/coder/coder.py +168 -0
  179. lionagi/lions/coder/util.py +96 -0
  180. lionagi/lions/researcher/__init__.py +0 -0
  181. lionagi/lions/researcher/data_source/__init__.py +0 -0
  182. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  183. lionagi/lions/researcher/data_source/google_.py +199 -0
  184. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  185. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  186. lionagi/tests/integrations/__init__.py +0 -0
  187. lionagi/tests/libs/__init__.py +0 -0
  188. lionagi/tests/libs/test_field_validators.py +353 -0
  189. lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
  190. lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
  191. lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
  192. lionagi/tests/libs/test_queue.py +67 -0
  193. lionagi/tests/test_core/collections/__init__.py +0 -0
  194. lionagi/tests/test_core/collections/test_component.py +206 -0
  195. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  196. lionagi/tests/test_core/collections/test_flow.py +145 -0
  197. lionagi/tests/test_core/collections/test_pile.py +171 -0
  198. lionagi/tests/test_core/collections/test_progression.py +129 -0
  199. lionagi/tests/test_core/generic/__init__.py +0 -0
  200. lionagi/tests/test_core/generic/test_edge.py +67 -0
  201. lionagi/tests/test_core/generic/test_graph.py +96 -0
  202. lionagi/tests/test_core/generic/test_node.py +106 -0
  203. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  204. lionagi/tests/test_core/test_branch.py +115 -292
  205. lionagi/tests/test_core/test_form.py +46 -0
  206. lionagi/tests/test_core/test_report.py +105 -0
  207. lionagi/tests/test_core/test_validator.py +111 -0
  208. lionagi/version.py +1 -1
  209. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
  210. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
  211. lionagi-0.2.1.dist-info/RECORD +240 -0
  212. lionagi/core/branch/__init__.py +0 -4
  213. lionagi/core/branch/base_branch.py +0 -654
  214. lionagi/core/branch/branch.py +0 -471
  215. lionagi/core/branch/branch_flow_mixin.py +0 -96
  216. lionagi/core/branch/executable_branch.py +0 -347
  217. lionagi/core/branch/util.py +0 -323
  218. lionagi/core/direct/__init__.py +0 -6
  219. lionagi/core/direct/predict.py +0 -161
  220. lionagi/core/direct/score.py +0 -278
  221. lionagi/core/direct/select.py +0 -169
  222. lionagi/core/direct/utils.py +0 -87
  223. lionagi/core/direct/vote.py +0 -64
  224. lionagi/core/flow/base/baseflow.py +0 -23
  225. lionagi/core/flow/monoflow/ReAct.py +0 -238
  226. lionagi/core/flow/monoflow/__init__.py +0 -9
  227. lionagi/core/flow/monoflow/chat.py +0 -95
  228. lionagi/core/flow/monoflow/chat_mixin.py +0 -263
  229. lionagi/core/flow/monoflow/followup.py +0 -214
  230. lionagi/core/flow/polyflow/__init__.py +0 -1
  231. lionagi/core/flow/polyflow/chat.py +0 -248
  232. lionagi/core/mail/schema.py +0 -56
  233. lionagi/core/messages/__init__.py +0 -3
  234. lionagi/core/messages/schema.py +0 -533
  235. lionagi/core/prompt/prompt_template.py +0 -316
  236. lionagi/core/schema/__init__.py +0 -22
  237. lionagi/core/schema/action_node.py +0 -29
  238. lionagi/core/schema/base_mixin.py +0 -296
  239. lionagi/core/schema/base_node.py +0 -199
  240. lionagi/core/schema/condition.py +0 -24
  241. lionagi/core/schema/data_logger.py +0 -354
  242. lionagi/core/schema/data_node.py +0 -93
  243. lionagi/core/schema/prompt_template.py +0 -67
  244. lionagi/core/schema/structure.py +0 -910
  245. lionagi/core/tool/__init__.py +0 -3
  246. lionagi/core/tool/tool_manager.py +0 -280
  247. lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
  248. lionagi/tests/test_core/test_base_branch.py +0 -427
  249. lionagi/tests/test_core/test_chat_flow.py +0 -63
  250. lionagi/tests/test_core/test_mail_manager.py +0 -75
  251. lionagi/tests/test_core/test_prompts.py +0 -51
  252. lionagi/tests/test_core/test_session.py +0 -254
  253. lionagi/tests/test_core/test_session_base_util.py +0 -312
  254. lionagi/tests/test_core/test_tool_manager.py +0 -95
  255. lionagi-0.0.312.dist-info/RECORD +0 -111
  256. /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
  257. /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
  258. /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
  259. /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
  260. /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
  261. /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
  262. /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
  263. /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
  264. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  265. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  266. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  267. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,231 @@
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
+ """
18
+ This module extends the BaseForm class to implement the Form class, which
19
+ dynamically manages form operations based on specific assignments. It provides
20
+ functionalities for initializing fields, filling forms with data, and
21
+ validating the readiness of forms for further processing.
22
+ """
23
+
24
+ from typing import Dict, Any
25
+ from lionagi.libs.ln_convert import to_readable_dict
26
+ from lionagi.core.collections.abc import SYSTEM_FIELDS
27
+ from lionagi.core.report.util import get_input_output_fields
28
+ from lionagi.core.report.base import BaseForm
29
+
30
+
31
+ class Form(BaseForm):
32
+ """
33
+ A specialized implementation of BaseForm designed to manage form fields
34
+ dynamically based on specified assignments. Supports initialization and
35
+ management of input and requested fields, handles form filling operations,
36
+ and ensures forms are properly configured for use.
37
+
38
+ Attributes:
39
+ input_fields (List[str]): Fields required to carry out the objective of the form.
40
+ requested_fields (List[str]): Fields requested to be filled by the user.
41
+ """
42
+
43
+ def __init__(self, **kwargs):
44
+ """
45
+ Initializes a new instance of the Form, setting up input and requested
46
+ fields based on the form's assignment.
47
+ """
48
+ super().__init__(**kwargs)
49
+ self.input_fields, self.requested_fields = get_input_output_fields(
50
+ self.assignment
51
+ )
52
+
53
+ for i in self.input_fields:
54
+ self.append_to_input(i)
55
+
56
+ for i in self.input_fields + self.requested_fields:
57
+ if i not in self._all_fields:
58
+ self._add_field(i, value=None)
59
+
60
+ def append_to_request(self, field: str, value=None):
61
+ """
62
+ Appends a field to the requested fields.
63
+
64
+ Args:
65
+ field (str): The name of the field to be requested.
66
+ value (optional): The value to be assigned to the field. Defaults to None.
67
+ """
68
+ if "," in field:
69
+ field = field.split(",")
70
+ if not isinstance(field, list):
71
+ field = [field]
72
+
73
+ for i in field:
74
+ i = i.strip()
75
+ if i not in self._all_fields:
76
+ self._add_field(i, value=value)
77
+
78
+ if i not in self.requested_fields:
79
+ self.requested_fields.append(i)
80
+ self.validation_kwargs[i] = self._get_field_attr(
81
+ i, "validation_kwargs", {}
82
+ )
83
+
84
+ def append_to_input(self, field: str, value=None):
85
+ """
86
+ Appends a field to the input fields.
87
+
88
+ Args:
89
+ field (str): The name of the field to be added to input.
90
+ value (optional): The value to be assigned to the field. Defaults to None.
91
+ """
92
+ if "," in field:
93
+ field = field.split(",")
94
+ if not isinstance(field, list):
95
+ field = [field]
96
+
97
+ for i in field:
98
+ i = i.strip()
99
+ if i not in self._all_fields:
100
+ self._add_field(i, value=value)
101
+
102
+ if i not in self.input_fields:
103
+ self.input_fields.append(i)
104
+ self.validation_kwargs[i] = self._get_field_attr(
105
+ i, "validation_kwargs", {}
106
+ )
107
+
108
+ @property
109
+ def work_fields(self) -> Dict[str, Any]:
110
+ """
111
+ Retrieves a dictionary of the fields relevant to the current task,
112
+ excluding any SYSTEM_FIELDS and including only the input and requested
113
+ fields.
114
+
115
+ Returns:
116
+ Dict[str, Any]: The relevant fields for the current task.
117
+ """
118
+ dict_ = self.to_dict()
119
+ return {
120
+ k: v
121
+ for k, v in dict_.items()
122
+ if k not in SYSTEM_FIELDS and k in self.input_fields + self.requested_fields
123
+ }
124
+
125
+ def fill(self, form: "Form" = None, strict: bool = True, **kwargs) -> None:
126
+ """
127
+ Fills the form from another form instance or provided keyword arguments.
128
+ Raises an error if the form is already filled.
129
+
130
+ Args:
131
+ form (Form, optional): The form to fill from.
132
+ strict (bool, optional): Whether to enforce strict filling. Defaults to True.
133
+ **kwargs: Additional fields to fill.
134
+ """
135
+ if self.filled:
136
+ if strict:
137
+ raise ValueError("Form is filled, cannot be worked on again")
138
+
139
+ all_fields = self._get_all_fields(form, **kwargs)
140
+
141
+ for k, v in all_fields.items():
142
+ if (
143
+ k in self.work_fields
144
+ and v is not None
145
+ and getattr(self, k, None) is None
146
+ ):
147
+ setattr(self, k, v)
148
+
149
+ def is_workable(self) -> bool:
150
+ """
151
+ Determines if the form is ready for processing. Checks if all required
152
+ fields are filled and raises an error if the form is already filled or
153
+ if any required field is missing.
154
+
155
+ Returns:
156
+ bool: True if the form is workable, otherwise raises ValueError.
157
+ """
158
+ if self.filled:
159
+ raise ValueError("Form is already filled, cannot be worked on again")
160
+
161
+ for i in self.input_fields:
162
+ if not getattr(self, i, None):
163
+ raise ValueError(f"Required field {i} is not provided")
164
+
165
+ return True
166
+
167
+ @property
168
+ def _instruction_context(self) -> str:
169
+ """
170
+ Generates a detailed description of the input fields, including their
171
+ current values and descriptions.
172
+
173
+ Returns:
174
+ str: A detailed description of the input fields.
175
+ """
176
+ return "".join(
177
+ f"""
178
+ ## input: {i}:
179
+ - description: {getattr(self._all_fields[i], "description", "N/A")}
180
+ - value: {str(getattr(self, self.input_fields[idx]))}
181
+ """
182
+ for idx, i in enumerate(self.input_fields)
183
+ )
184
+
185
+ @property
186
+ def _instruction_prompt(self) -> str:
187
+ return f"""
188
+ ## Task Instructions
189
+ Please follow prompts to complete the task:
190
+ 1. Your task is: {self.task}
191
+ 2. The provided input fields are: {', '.join(self.input_fields)}
192
+ 3. The requested output fields are: {', '.join(self.requested_fields)}
193
+ 4. Provide your response in the specified JSON format.
194
+ """
195
+
196
+ @property
197
+ def _instruction_requested_fields(self) -> Dict[str, str]:
198
+ """
199
+ Provides a dictionary mapping requested field names to their
200
+ descriptions.
201
+
202
+ Returns:
203
+ Dict[str, str]: A dictionary of requested field descriptions.
204
+ """
205
+ return {
206
+ i: getattr(self._all_fields[i], "description", "N/A")
207
+ for i in self.requested_fields
208
+ }
209
+
210
+ def display(self, fields=None):
211
+ """
212
+ Displays the form fields using IPython display.
213
+
214
+ Args:
215
+ fields (optional): Specific fields to display. Defaults to None.
216
+ """
217
+ from IPython.display import display, Markdown
218
+
219
+ fields = fields or self.work_fields
220
+
221
+ if "answer" in fields:
222
+ answer = fields.pop("answer")
223
+ fields["answer"] = answer
224
+
225
+ for k, v in fields.items():
226
+ if isinstance(v, dict):
227
+ v = to_readable_dict(v)
228
+ if len(str(v)) > 50:
229
+ display(Markdown(f"**{k}**: \n {v}"))
230
+ else:
231
+ display(Markdown(f"**{k}**: {v}"))
@@ -0,0 +1,166 @@
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
+ """
18
+ This module introduces the Report class, an extension of the BaseForm class
19
+ designed to manage and synchronize a collection of Form instances based on
20
+ specific assignments. The Report class handles the creation and updating of
21
+ forms, ensuring each is properly configured according to the report's
22
+ requirements.
23
+ """
24
+
25
+ from typing import Any, Type
26
+ from lionagi.core.collections.abc import Field
27
+ from lionagi.core.collections import Pile, pile
28
+ from lionagi.core.report.util import get_input_output_fields
29
+ from lionagi.core.report.base import BaseForm
30
+ from lionagi.core.report.form import Form
31
+
32
+
33
+ class Report(BaseForm):
34
+ """
35
+ Extends BaseForm to handle a collection of Form instances based on specific
36
+ assignments, managing a pile of forms and ensuring synchronization and
37
+ proper configuration.
38
+ """
39
+
40
+ forms: Pile[Form] = Field(
41
+ None,
42
+ description="A pile of forms related to the report.",
43
+ )
44
+
45
+ form_assignments: list = Field(
46
+ [],
47
+ description="assignment for the report",
48
+ examples=[["a, b -> c", "a -> e", "b -> f", "c -> g", "e, f, g -> h"]],
49
+ )
50
+
51
+ form_template: Type[Form] = Field(
52
+ Form, description="The template for the forms in the report."
53
+ )
54
+
55
+ def __init__(self, **kwargs):
56
+ """
57
+ Initializes the Report with input and requested fields based on the
58
+ report's assignment, creating forms dynamically from provided assignments.
59
+ """
60
+ super().__init__(**kwargs)
61
+ self.input_fields, self.requested_fields = get_input_output_fields(
62
+ self.assignment
63
+ )
64
+
65
+ # if assignments is not provided, set it to report assignment
66
+ if self.form_assignments == []:
67
+ self.form_assignments.append(self.assignment)
68
+
69
+ # create forms
70
+ self.forms = pile(
71
+ [self.form_template(assignment=i) for i in self.form_assignments],
72
+ [Form, BaseForm, Report],
73
+ )
74
+
75
+ # Add undeclared fields to report with None values
76
+ for v in self.forms:
77
+ for _field in list(v.work_fields.keys()):
78
+ if _field not in self._all_fields:
79
+ field_obj = v._all_fields[_field]
80
+ self._add_field(_field, value=None, field_obj=field_obj)
81
+
82
+ # Synchronize fields between report and forms
83
+ for k, v in self._all_fields.items():
84
+ if getattr(self, k, None) is not None:
85
+ for _form in self.forms:
86
+ if k in _form.work_fields:
87
+ _form.fill(**{k: getattr(self, k)})
88
+
89
+ @property
90
+ def work_fields(self) -> dict[str, Any]:
91
+ all_fields = {}
92
+ for form in self.forms.values():
93
+ for k, v in form.work_fields.items():
94
+ if k not in all_fields:
95
+ all_fields[k] = v
96
+ return all_fields
97
+
98
+ def fill(self, form: Form | list[Form] | dict[Form] = None, strict=True, **kwargs):
99
+ if self.filled:
100
+ if strict:
101
+ raise ValueError("Form is filled, cannot be worked on again")
102
+
103
+ # gather all unique valid fields from input form,
104
+ # kwargs and self workfields data
105
+ all_fields = self._get_all_fields(form, **kwargs)
106
+
107
+ # if there are information in the forms that are not in the report,
108
+ # add them to the report
109
+ for k, v in all_fields.items():
110
+ if k in self.work_fields and getattr(self, k, None) is None:
111
+ setattr(self, k, v)
112
+
113
+ # if there are information in the report that are not in the forms,
114
+ # add them to the forms
115
+ for _form in self.forms:
116
+ for k, v in _form.work_fields.items():
117
+ _kwargs = {}
118
+ if v is None and (a := getattr(self, k, None)) is not None:
119
+ _kwargs[k] = a
120
+ _form.fill(**_kwargs)
121
+
122
+ def is_workable(self) -> bool:
123
+ """
124
+ Checks if the report is ready for processing, ensuring all necessary fields
125
+ are filled and output fields are unique across forms.
126
+
127
+ Returns:
128
+ bool: True if the report is workable, otherwise raises ValueError.
129
+ """
130
+ if self.filled:
131
+ raise ValueError("Form is already filled, cannot be worked on again")
132
+
133
+ for i in self.input_fields:
134
+ if not getattr(self, i, None):
135
+ raise ValueError(f"Required field {i} is not provided")
136
+
137
+ # this is the required fields from report's own assignment
138
+ fields = self.input_fields
139
+ fields.extend(self.requested_fields)
140
+
141
+ # if the report's own assignment is not in the forms, return False
142
+ for f in fields:
143
+ if f not in self.work_fields:
144
+ raise ValueError(f"Field {f} is not in the forms")
145
+
146
+ # get all the output fields from all the forms
147
+ outs = []
148
+ for form in self.forms.values():
149
+ outs.extend(form.requested_fields)
150
+
151
+ # all output fields should be unique, not a single output field should be
152
+ # calculated by more than one form
153
+ if len(outs) != len(set(outs)):
154
+ raise ValueError("Output fields are not unique")
155
+
156
+ return True
157
+
158
+ def next_forms(self) -> Pile[Form]:
159
+ """
160
+ Returns a pile of workable forms based on current form states within the report.
161
+
162
+ Returns:
163
+ Pile[Form]: A pile of workable forms or None if there are no workable forms.
164
+ """
165
+ a = [i for i in self.forms if i.workable]
166
+ return pile(a, Form) if len(a) > 0 else None
@@ -0,0 +1,28 @@
1
+ from lionagi.libs.ln_convert import strip_lower
2
+
3
+
4
+ def get_input_output_fields(str_: str) -> list[list[str]]:
5
+ """
6
+ Parses an assignment string to extract input and output fields.
7
+
8
+ Args:
9
+ str_ (str): The assignment string in the format 'inputs -> outputs'.
10
+
11
+ Returns:
12
+ list[list[str]]: A list containing two lists - one for input fields and one for requested fields.
13
+
14
+ Raises:
15
+ ValueError: If the assignment string is None or if it does not contain '->' indicating invalid format.
16
+ """
17
+ if str_ is None:
18
+ return [], []
19
+
20
+ if "->" not in str_:
21
+ raise ValueError("Invalid assignment format. Expected 'inputs -> outputs'.")
22
+
23
+ inputs, outputs = str_.split("->")
24
+
25
+ input_fields = [strip_lower(i) for i in inputs.split(",")]
26
+ requested_fields = [strip_lower(o) for o in outputs.split(",")]
27
+
28
+ return input_fields, requested_fields
File without changes
@@ -0,0 +1,16 @@
1
+ from enum import Enum
2
+ from .choice import ChoiceRule
3
+ from .mapping import MappingRule
4
+ from .number import NumberRule
5
+ from .boolean import BooleanRule
6
+ from .string import StringRule
7
+ from .action import ActionRequestRule
8
+
9
+
10
+ class DEFAULT_RULES(Enum):
11
+ CHOICE = ChoiceRule
12
+ MAPPING = MappingRule
13
+ NUMBER = NumberRule
14
+ BOOL = BooleanRule
15
+ STR = StringRule
16
+ ACTION = ActionRequestRule
@@ -0,0 +1,99 @@
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 enum import Enum
18
+ from lionagi.core.collections.abc import ActionError
19
+ from lionagi.libs import ParseUtil
20
+ from lionagi.libs.ln_convert import to_list, to_dict
21
+ from lionagi.core.rule.mapping import MappingRule
22
+
23
+
24
+ class ActionRequestKeys(Enum):
25
+ FUNCTION = "function"
26
+ ARGUMENTS = "arguments"
27
+
28
+
29
+ class ActionRequestRule(MappingRule):
30
+ """
31
+ Rule for validating and fixing action requests.
32
+
33
+ Inherits from `MappingRule` and provides specific validation and fix logic
34
+ for action requests.
35
+
36
+ Attributes:
37
+ discard (bool): Indicates whether to discard invalid action requests.
38
+ """
39
+
40
+ def __init__(self, apply_type="actionrequest", discard=True, **kwargs):
41
+ """
42
+ Initializes the ActionRequestRule.
43
+
44
+ Args:
45
+ apply_type (str): The type of data to which the rule applies.
46
+ discard (bool, optional): Indicates whether to discard invalid action requests.
47
+ **kwargs: Additional keyword arguments for initialization.
48
+ """
49
+ super().__init__(
50
+ apply_type=apply_type, keys=ActionRequestKeys, fix=True, **kwargs
51
+ )
52
+ self.discard = discard or self.validation_kwargs.get("discard", False)
53
+
54
+ async def validate(self, value):
55
+ """
56
+ Validates the action request.
57
+
58
+ Args:
59
+ value (Any): The value of the action request.
60
+
61
+ Returns:
62
+ Any: The validated action request.
63
+
64
+ Raises:
65
+ ActionError: If the action request is invalid.
66
+ """
67
+ if isinstance(value, dict) and list(value.keys()) >= ["function", "arguments"]:
68
+ return value
69
+ raise ActionError(f"Invalid action request: {value}")
70
+
71
+ async def perform_fix(self, value):
72
+ """
73
+ Attempts to fix an invalid action request.
74
+
75
+ Args:
76
+ value (Any): The value of the action request to fix.
77
+
78
+ Returns:
79
+ Any: The fixed action request.
80
+
81
+ Raises:
82
+ ActionError: If the action request cannot be fixed.
83
+ """
84
+ corrected = []
85
+ if isinstance(value, str):
86
+ value = ParseUtil.fuzzy_parse_json(value)
87
+
88
+ try:
89
+ value = to_list(value)
90
+ for i in value:
91
+ i = to_dict(i)
92
+ if list(i.keys()) >= ["function", "arguments"]:
93
+ corrected.append(i)
94
+ elif not self.discard:
95
+ raise ActionError(f"Invalid action request: {i}")
96
+ except Exception as e:
97
+ raise ActionError(f"Invalid action field: ") from e
98
+
99
+ return corrected