lionagi 0.1.2__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 +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 +76 -0
  94. lionagi/core/work/work_function.py +101 -0
  95. lionagi/core/work/work_queue.py +103 -0
  96. lionagi/core/work/worker.py +258 -0
  97. lionagi/core/work/worklog.py +120 -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.1.dist-info/LICENSE +202 -0
  168. lionagi-0.2.1.dist-info/METADATA +272 -0
  169. lionagi-0.2.1.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.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,371 +0,0 @@
1
- from typing import Any
2
- from pydantic import Field
3
- from lionagi.core.generic import BaseComponent
4
- from lionagi.libs import validation_funcs, convert
5
- from lionagi.experimental.report.util import get_input_output_fields
6
-
7
-
8
- class Form(BaseComponent):
9
-
10
- form_name: str = Field(
11
- default="default_form",
12
- )
13
- description: Any = Field(default=None)
14
- assignment: str = Field(..., examples=["input1, input2 -> output"])
15
- instruction: Any = Field(
16
- default=None,
17
- )
18
- input_fields: list[str] = Field(default_factory=list)
19
- output_fields: list[str] = Field(default_factory=list)
20
- examples: Any = Field(
21
- default=None,
22
- )
23
- fix_input: bool = Field(False, description="whether to fix input")
24
- fix_output: bool = Field(True, description="whether to fix output")
25
- filled: bool = Field(False, description="whether the form is completed")
26
-
27
- def __init__(self, **kwargs):
28
- super().__init__(**kwargs)
29
- self.input_fields, self.output_fields = get_input_output_fields(self.assignment)
30
-
31
- for field in self.input_fields:
32
- if not hasattr(self, field):
33
- setattr(self, field, None) # zero initialization
34
- self.process()
35
-
36
- @property
37
- def work_fields(self):
38
- return self.input_fields + self.output_fields
39
-
40
- @property
41
- def is_completed(self):
42
- return all(getattr(self, i, None) for i in self.work_fields)
43
-
44
- def process(self, in_=True, out_=None):
45
- if not in_ and not out_:
46
- raise ValueError("at least one of in_ and out_ must be True")
47
- if in_:
48
- self._process_input()
49
- if out_:
50
- self._process_output(out_)
51
-
52
- def _process_input(self):
53
- for k in self.input_fields:
54
- try:
55
- valid_ = self._validate_field(
56
- k,
57
- getattr(self, k, None),
58
- choices=self._get_field_attr(k, "choices", None),
59
- keys=self._get_field_attr(k, "keys", None),
60
- fix_=self.fix_input,
61
- **self._get_field_attr(k, "validation_kwargs", {}),
62
- )
63
- if not valid_:
64
- raise ValueError(f"failed to validate field {k}")
65
- except Exception as e:
66
- raise ValueError(f"failed to validate field {k}") from e
67
-
68
- def _process_output(self, out_: dict = None):
69
- for k, v in out_.items():
70
- try:
71
- valid_ = self._validate_field(
72
- k,
73
- v,
74
- choices=self._get_field_attr(k, "choices", None),
75
- keys=self._get_field_attr(k, "keys", None),
76
- fix_=self.fix_output,
77
- **self._get_field_attr(k, "validation_kwargs", {}),
78
- )
79
- if not valid_:
80
- raise ValueError(f"failed to validate field {k}")
81
- except Exception as e:
82
- raise ValueError(f"failed to validate field {k}") from e
83
-
84
- if self.is_completed:
85
- self.filled = True
86
-
87
- def _validate_field(self, k, v, choices=None, keys=None, fix_=False, **kwargs):
88
- annotation = self.field_annotations[k]
89
-
90
- if choices:
91
- if choices and not isinstance(choices, list):
92
- try:
93
- choices = [i.value for i in choices]
94
- except Exception as e:
95
- raise ValueError(f"failed to get choices for field {k}") from e
96
- v_ = validation_funcs["enum"](v, choices=choices, fix_=fix_, **kwargs)
97
- if v_ not in choices:
98
- raise ValueError(f"{v} is not in chocies {choices}")
99
- setattr(self, k, v_)
100
- return True
101
-
102
- if any("actionrequest" in i for i in annotation):
103
- self.__setattr__(k, validation_funcs["action"](v))
104
- return True
105
-
106
- if "bool" in annotation and "str" not in annotation:
107
- self.__setattr__(k, validation_funcs["bool"](v, fix_=fix_, **kwargs))
108
- return True
109
-
110
- if (
111
- any([i in annotation for i in ["int", "float", "number"]])
112
- and "str" not in annotation
113
- ):
114
- if "float" in annotation:
115
- kwargs["num_type"] = float
116
- if "precision" not in kwargs:
117
- kwargs["precision"] = 10
118
-
119
- self.__setattr__(k, validation_funcs["number"](v, fix_=fix_, **kwargs))
120
- return True
121
-
122
- if "dict" in annotation:
123
- if "str" not in annotation or keys:
124
- v_ = validation_funcs["dict"](v, keys=keys, fix_=fix_, **kwargs)
125
- setattr(self, k, v_)
126
- return True
127
-
128
- if "str" in annotation:
129
- self.__setattr__(k, validation_funcs["str"](v, fix_=fix_, **kwargs))
130
- return True
131
-
132
- return False
133
-
134
-
135
- # from enum import Enum
136
- # from pydantic import Field
137
-
138
- # class EXAMPLES(str, Enum):
139
- # EXAMPLE1 = "example1"
140
- # EXAMPLE2 = "example2"
141
- # EXAMPLE3 = "example3"
142
-
143
- # class Form1(Form):
144
- # a: str | EXAMPLES = Field("example3", choices=EXAMPLES)
145
- # b: str = "input2"
146
- # c: str|None = Field(None, choices=["output1", "output2"])
147
- # d: float | None = Field(None, json_schema_extra={"validation_kwargs":{"num_type": float, "precision": 2}})
148
- # assignment: str='a, b -> c, d'
149
- # form_name: str='custom_form'
150
- # description: str='Test Form'
151
-
152
- # form = Form1()
153
- # form.process(out_ = {"c": "output1", "d": "1.1"})
154
-
155
-
156
- """
157
- usage pattern:
158
-
159
- from pydantic import Field
160
-
161
- class JokeForm(Form):
162
-
163
- # 1. use the key kwarg to specify the key in the dict,
164
- # which will be used validate the field value
165
-
166
- # 2. use fix flag to indicate whether to fix the value
167
- # if it is not valid, default to True
168
-
169
- material: dict = Field(
170
- ...,
171
- description="materials to read",
172
- keys=["title", "author", "year"], # use fix flag for dict key can be dangerous
173
- fix=True # the validator will force the value to match
174
- ) # the provided keys, which might pollute the data
175
-
176
-
177
- # 3. use choices to specify the available options
178
- topic: str = Field(
179
- ...,
180
- description="topic to write joke on",
181
- choices=["animal", "food", "people"],
182
- )
183
-
184
- # you can also use enum or subclass of Enum to explicitly
185
- # declare that the field requires
186
- # the value to be one of the provided choices
187
-
188
- from enum import Enum
189
-
190
- class TOPIC(str, Enum):
191
- ANIMAL = "animal"
192
- FOOD = "food"
193
- PEOPLE = "people"
194
-
195
-
196
- topic: TOPIC = Field(
197
- default = TOPIC.ANIMAL,
198
- description="topic to write joke on",
199
- choices=TOPIC,
200
- fix_input=True, # fix_input and fix_output are used to indicate
201
- ) # whether to fix invalid values, default to True
202
-
203
- # 4. using optional fields
204
- # you can add None in type annotation to indicate that the field is optional
205
- # otherwise there will be a validation error if there is no default
206
- # field value, and value is also not provided at initialization
207
-
208
- joke: str | None = Field(
209
- default = None,
210
- description = "joke to write"
211
- )
212
-
213
- # 5. using validation_kwargs
214
- # you can use validation_kwargs to specify the validation parameters
215
- # there are built-in validators for numbers
216
-
217
- rating: float | None = Field(
218
- default=None,
219
- description="rating, a numerical value",
220
- validation_kwargs={
221
- "upper_bound": 10, # this will ensure the value for this field
222
- "lower_bound": 0, # is a number between upper_bound and lower_bound
223
- "num_type": "float",
224
- "precision": 2}
225
- )
226
-
227
-
228
- # 6. using assignment
229
- # in a form, you do not need to specify the input output for each field,
230
- # instead, you can use assignment to specify the input and output of the form
231
-
232
- # an assignment is a string that describes the input and output of the form
233
- # it is used to generate a composable set of fields specific to different context
234
- # from the same custom Form class you specified, for example, two fields "a" and "b",
235
- # in one form, you can specify the assignment as "a -> b", and in another form,
236
- # you can specify the assignment as "b -> a", and the system will generate two different
237
- # instruction for the worker to perform.
238
-
239
- assignment: str = Field(
240
- default=...,
241
- examples=["input1, input2 -> output"]
242
- )
243
-
244
- # for example, "work -> review, rating" and "review, work -> rating" have completely different
245
- # meanings, and the system will generate two different forms for the worker to complete
246
- # the former means, a review task of a work performance with corresponding rating for the work
247
- # the latter means, a rating task on the review quality for the work performance,
248
- # such as, whether it is fair for the rating
249
-
250
-
251
- # 7. general guidance
252
- # in principle, a field should only be filled once, thus making form a single use object
253
- # the form has a filled flag to indicate whether the form is completed
254
-
255
- """
256
-
257
- # import unittest
258
- # from pydantic import Field
259
-
260
-
261
- # class Form1(Form):
262
- # input1: str = Field("input1", json_schema_extra={"choices": ["option1", "option2"]})
263
- # input2: str = "input2"
264
- # output1: str = "output1"
265
-
266
-
267
- # class TestForm(unittest.TestCase):
268
-
269
- # def test_default_initialization(self):
270
- # class Form1(Form):
271
- # input1: str = "input1"
272
- # input2: str = "input2"
273
- # output1: str = "output1"
274
- # assignment: str = "input1, input2 -> output1"
275
-
276
- # form = Form1()
277
- # self.assertEqual(form.form_name, "default_form")
278
- # self.assertIsNone(form.description)
279
- # self.assertEqual(form.input_fields, ["input1", "input2"])
280
- # self.assertEqual(form.output_fields, ["output1"])
281
- # self.assertFalse(form.filled)
282
- # self.assertFalse(form.fix_input)
283
- # self.assertTrue(form.fix_output)
284
-
285
- # def test_custom_initialization(self):
286
- # class Form1(Form):
287
- # a: str = "input1"
288
- # b: str = "input2"
289
- # c: str = "output1"
290
- # assignment: str = "a, b -> c"
291
- # form_name: str = "custom_form"
292
- # description: str = "Test Form"
293
-
294
- # form = Form1()
295
- # self.assertEqual(form.form_name, "custom_form")
296
- # self.assertEqual(form.description, "Test Form")
297
- # self.assertEqual(form.input_fields, ["a", "b"])
298
- # self.assertEqual(form.output_fields, ["c"])
299
-
300
- # def test_process_inputs_outputs(self):
301
- # class Form1(Form):
302
- # input1: str = Field(
303
- # "option1", json_schema_extra={"choices": ["option1", "option2"]}
304
- # )
305
- # input2: str = "input2"
306
- # output1: str = "output1"
307
- # assignment: str = "input1, input2 -> output1"
308
-
309
- # form = Form1()
310
- # setattr(form, "input1", "option1")
311
- # # Test processing valid input
312
- # form.process(in_=True) # Should process without error
313
-
314
- # setattr(form, "input1", "option3")
315
- # # Test processing invalid input
316
- # with self.assertRaises(ValueError):
317
- # form.process(in_=True) # Should raise error
318
-
319
- # def test_check_complete(self):
320
- # class Form1(Form):
321
- # input1: str = "input1"
322
- # input2: str = "input2"
323
- # output1: str = "output1"
324
-
325
- # form = Form1(assignment="input1, input2 -> output1")
326
- # setattr(form, "input1", "value1")
327
- # setattr(form, "input2", "value2")
328
- # setattr(form, "output1", "result1")
329
- # self.assertTrue(form.is_completed)
330
-
331
- # def test_input_output_fields_parsing(self):
332
- # class Form1(Form):
333
- # x: str = "input1"
334
- # y: str = "input2"
335
- # z: str = "output1"
336
-
337
- # form = Form1(assignment="x, y -> z")
338
- # self.assertEqual(form.input_fields, ["x", "y"])
339
- # self.assertEqual(form.output_fields, ["z"])
340
-
341
- # def test_validation_failure(self):
342
- # class Form1(Form):
343
- # input1: str = Field(
344
- # None, json_schema_extra={"choices": ["option1", "option2"]}
345
- # )
346
- # input2: str = "input2"
347
- # output1: str = "output1"
348
- # assignment: str = "input1, input2 -> output1"
349
-
350
- # # Test handling invalid input choice
351
- # with self.assertRaises(ValueError):
352
- # form = Form1()
353
-
354
- # def test_output_assignment(self):
355
- # class Form1(Form):
356
- # input1: str = "value1"
357
- # output1: str = Field("output1", choices=["result1", "result2"])
358
- # assignment: str = "input1 -> output1"
359
-
360
- # form = Form1()
361
- # # Test handling invalid output choice
362
- # try:
363
- # form.process(out_={"output1": "result3"})
364
- # except ValueError:
365
- # pass
366
-
367
- # form.process(out_={"output1": "result3"}) # Should process without error
368
-
369
-
370
- # if __name__ == "__main__":
371
- # unittest.main()
@@ -1,289 +0,0 @@
1
- from typing import Any
2
- from pydantic import Field
3
- from lionagi.experimental.report.form import Form
4
- from lionagi.core.generic import BaseComponent
5
- from lionagi.experimental.report.util import get_input_output_fields
6
-
7
- """
8
- ## Report Usage Pattern
9
-
10
-
11
-
12
-
13
-
14
-
15
- requirements,
16
- the assignments name should be consistent within a report,
17
- meaning, all forms will refer to the same field value when using the same name.
18
-
19
- the same input field can be used in multiple forms, but each unique output field
20
- should be filled only once.
21
- meaning, not two forms should have the any output fields in common.
22
-
23
- a filled field cannot be None
24
-
25
- usage pattern:
26
-
27
- class Report1(Report):
28
- a: Any = None
29
- b: Any = None
30
- c: Any = None
31
- d: Any = None
32
- e: Any = None
33
- f: Any = None
34
-
35
- assignments: list = [
36
- "a, b -> c",
37
- "c -> d",
38
- "b, d -> e"
39
- ]
40
-
41
- this means that the report will have 3 work steps, each corresponding to an unique
42
- form. the context for the report is the fields that cannot be output by any work
43
- teps in the report.
44
-
45
- the deliverable for the report is the fields that are output produced by the work
46
- steps but not used as input for any other work steps in the same report.
47
-
48
- under the hood:
49
-
50
- the report first create three forms and add to its forms dictionary
51
-
52
- form1 = Form1(assignment="a, b -> c")
53
- form2 = Form2(assignment="c -> d")
54
- form3 = Form3(assignment="b -> e")
55
- form3 = Form3(assignment="d, e -> f")
56
-
57
- The Form1, Form2, Form3 can be a single form class or multiple form classes,
58
- as long as they have the same fields to interact
59
-
60
- report is created by intaking different context inputs, in this case, we
61
- need to provide a and b to the report
62
-
63
- report let work scheduler know which form to fill next, by using the next_forms() method,
64
- this method check if the dependencies of a form are filled, if so, all such forms are
65
- deemed to be next to fill.
66
-
67
- so to begin with we need the context fields, a and b, then the report checks the
68
- next forms, notice, for (a, b -> c), we have a and b, so this form can be filled next
69
- also for (b -> e), we have b, so this form can also be filled next
70
-
71
- thus the report will first send forms of those two assignments to the scheduler.
72
-
73
- as work gets done, forms get filled, the completed forms will be sent back to scheduler,
74
- and the scheduler is in charge of filling in the fields onto the report. After each
75
- time a report gets new fields filled, the scheduler will check the next forms to fill,
76
- and the process continues until all forms are filled.
77
-
78
- so as (a, b -> c) is done, we get c, thus c -> d is then the next form
79
- and d, e -> f is the last form to fill
80
- once all forms are filled and all fields transfered to reports, the report is completed.
81
-
82
- """
83
-
84
-
85
- class Report(BaseComponent):
86
-
87
- report_name: str = Field(
88
- default="default_report",
89
- )
90
- description: Any = Field(default=None)
91
- task: Any = Field(default=None)
92
- forms: dict[str, Form] = Field(
93
- default_factory=dict,
94
- description="A dictionary of forms related to the report, in {form.id_: form} format.",
95
- )
96
- context: dict = Field(default_factory=dict, description="context for the report")
97
- deliverable: dict = Field(
98
- default_factory=dict, description="deliverable for the report"
99
- )
100
- intermediate: dict = Field(
101
- default_factory=dict, description="intermediate fields for the report"
102
- )
103
- filled: bool = Field(False, description="whether the report is completed")
104
- assignments: list = Field([], description="assignment for the report")
105
-
106
- def fill_report(self, form: Form | str):
107
- form = self.forms[form] if isinstance(form, str) else form
108
- if not form.filled or form not in self.forms.values():
109
- raise ValueError("The form is not filled or not in the report.")
110
-
111
- for i in form.input_fields:
112
- if i not in self._filled_fields:
113
- setattr(self, i, getattr(form, i))
114
- if i not in self.deliverable:
115
- self.intermediate[i] = getattr(form, i)
116
-
117
- for i in form.output_fields:
118
- setattr(self, i, getattr(form, i))
119
- if i not in self.deliverable:
120
- self.intermediate[i] = getattr(form, i)
121
-
122
- @property
123
- def work_fields(self):
124
- """
125
- all work fields across all forms, including intermediate output fields
126
- """
127
- all_fields = []
128
- for form in self.forms.values():
129
- all_fields.extend(form.work_fields)
130
- return list(set(all_fields))
131
-
132
- @property
133
- def is_completed(self):
134
- return all([hasattr(self, i) for i in self.work_fields])
135
-
136
- @property
137
- def is_workable(self):
138
- context_fields, deliverable_fields = get_input_output_fields(self.assignment)
139
- context_fields.extend(deliverable_fields)
140
-
141
- # check whether all work fields are assigned in the forms for the report
142
- if not all([i in self.work_fields for i in context_fields]):
143
- raise ValueError(
144
- f"Not all work fields are assigned in the forms for the report."
145
- )
146
-
147
- outs = []
148
- for form in self.forms.values():
149
- outs.extend(form.output_fields)
150
-
151
- if len(outs) != len(set(outs)):
152
- raise ValueError(f"Output fields should be unique across all forms.")
153
-
154
- inputs = []
155
- for form in self.forms.values():
156
- inputs.extend(form.input_fields)
157
-
158
- inputs = [i for i in inputs if i not in outs]
159
- if not all([i in self.work_fields for i in inputs]):
160
- raise ValueError(
161
- f"Not all input fields are assigned in the forms for the report."
162
- )
163
-
164
- return True
165
-
166
- def next_forms(self):
167
- to_do = []
168
- for i in self._unfilled_forms:
169
- if all([j in self._filled_fields for j in i.input_fields]):
170
- to_do.append(i)
171
- return to_do[0] if len(to_do) == 1 else to_do or None
172
-
173
- @property
174
- def _filled_forms(self):
175
- return [form for form in self.forms.values() if form.filled]
176
-
177
- @property
178
- def _unfilled_forms(self):
179
- return [form for form in self.forms.values() if not form.filled]
180
-
181
- @property
182
- def _filled_fields(self):
183
- filled_fields = []
184
- for i in self.work_fields:
185
- if getattr(self, i, None) is not None:
186
- filled_fields.append(i)
187
- return filled_fields
188
-
189
- @property
190
- def _unfilled_fields(self):
191
- return [i for i in self.work_fields if i not in self._filled_fields]
192
-
193
-
194
- # import unittest
195
- # from enum import Enum
196
- # from typing import Any
197
- # from pydantic import Field
198
- # from lionagi import logging as _logging
199
- # from lionagi.experimental.form.form import Form
200
- # from lionagi.core.generic import BaseComponent
201
- # from lionagi.experimental.form.util import get_input_output_fields
202
-
203
-
204
- # class EXAMPLES(str, Enum):
205
- # EXAMPLE1 = "example1"
206
- # EXAMPLE2 = "example2"
207
- # EXAMPLE3 = "example3"
208
-
209
-
210
- # class Form1(Form):
211
- # a: str | EXAMPLES = Field(EXAMPLES.EXAMPLE3, choices=list(EXAMPLES))
212
- # b: str = "input2"
213
- # c: str | None = Field(None, choices=["output1", "output2"])
214
- # d: float | None = Field(None)
215
- # assignment: str = "a, b -> c, d"
216
- # form_name: str = "custom_form"
217
- # description: str = "Test Form"
218
-
219
-
220
- # class TestReport(unittest.TestCase):
221
- # def setUp(self):
222
- # self.report = Report(assignment="a, b -> c, d")
223
- # self.form1 = Form1(assignment="a, b -> c, d")
224
- # self.report.forms[self.form1.id_] = self.form1
225
-
226
- # def test_initialization(self):
227
- # self.assertEqual(self.report.report_name, "default_report")
228
- # self.assertIn(self.form1.id_, self.report.forms)
229
-
230
- # def test_fill_report(self):
231
- # self.form1.process(out_={"c": "output1", "d": 1.1})
232
- # self.report.fill_report(self.form1)
233
- # self.assertTrue(self.form1.filled)
234
- # self.assertEqual(self.report.intermediate["c"], "output1")
235
-
236
- # def test_report_completeness(self):
237
- # self.form1.process(out_={"c": "output1", "d": 1.1})
238
- # self.report.fill_report(self.form1)
239
- # self.assertTrue(self.report.is_completed)
240
-
241
- # def test_report_workability(self):
242
- # # self.form1.process(out_={'c': 'output1', 'd': 1.1})
243
- # # self.report.fill_report(self.form1)
244
- # self.assertTrue(self.report.is_workable)
245
-
246
- # def test_handling_invalid_form_id(self):
247
- # with self.assertRaises(KeyError):
248
- # self.report.fill_report("nonexistent_form")
249
-
250
- # def test_next_forms_logic(self):
251
- # next_forms = self.report.next_forms()
252
- # self.assertEqual(next_forms, None)
253
-
254
- # def test_work_fields_property(self):
255
- # self.form1.process(out_={"c": "output1", "d": 1.1})
256
- # self.report.fill_report(self.form1)
257
- # self.assertIn("a", self.report.work_fields)
258
- # self.assertIn("b", self.report.work_fields)
259
- # self.assertIn("c", self.report.work_fields)
260
- # self.assertIn("d", self.report.work_fields)
261
-
262
- # def test_filled_forms_property(self):
263
- # self.form1.process(out_={"c": "output1", "d": 1.1})
264
- # self.report.fill_report(self.form1)
265
- # self.assertIn(self.form1, self.report._filled_forms)
266
-
267
- # def test_unfilled_forms_property(self):
268
- # self.assertIn(self.form1, self.report._unfilled_forms)
269
- # self.form1.process(out_={"c": "output1", "d": 1.1})
270
- # self.report.fill_report(self.form1)
271
- # self.assertNotIn(self.form1, self.report._unfilled_forms)
272
-
273
- # def test_filled_fields_property(self):
274
- # self.form1.process(out_={"c": "output1", "d": 1.1})
275
- # self.report.fill_report(self.form1)
276
- # self.assertIn("c", self.report._filled_fields)
277
- # self.assertIn("d", self.report._filled_fields)
278
-
279
- # def test_unfilled_fields_property(self):
280
- # self.assertIn("c", self.report._unfilled_fields)
281
- # self.assertIn("d", self.report._unfilled_fields)
282
- # self.form1.process(out_={"c": "output1", "d": 1.1})
283
- # self.report.fill_report(self.form1)
284
- # self.assertNotIn("c", self.report._unfilled_fields)
285
- # self.assertNotIn("d", self.report._unfilled_fields)
286
-
287
-
288
- # if __name__ == "__main__":
289
- # unittest.main()
@@ -1,30 +0,0 @@
1
- from enum import Enum
2
- from typing import Any, Dict, List
3
- from pydantic import Field
4
- from lionagi.core.generic import BaseComponent
5
-
6
-
7
- class WorkStatus(str, Enum):
8
- """Enum to represent different statuses of work."""
9
-
10
- PENDING = "PENDING"
11
- IN_PROGRESS = "IN_PROGRESS"
12
- COMPLETED = "COMPLETED"
13
- FAILED = "FAILED"
14
- CANCELLED = "CANCELLED"
15
-
16
-
17
- class Work(BaseComponent):
18
- """Base component for handling individual units of work."""
19
-
20
- form_id: str = Field(..., description="ID of the form for this work")
21
- priority: int = Field(default=0, description="Priority of the work")
22
- status: WorkStatus = Field(
23
- default=WorkStatus.PENDING, description="Current status of the work"
24
- )
25
- deliverables: Dict[str, Any] | list = Field(
26
- default={}, description="Deliverables produced by the work"
27
- )
28
- dependencies: List["Work"] = Field(
29
- default_factory=list, description="List of work items this work depends on"
30
- )