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
@@ -1,292 +1,115 @@
1
- # from lionagi.core.branch.branch import Branch
2
- # from lionagi.core.tool.tool_manager import ToolManager, func_to_tool
3
- # from lionagi.core.schema import DataLogger
4
- # from lionagi.core.branch.util import MessageUtil
5
-
6
-
7
- # import unittest
8
- # from unittest.mock import patch, MagicMock
9
- # import pandas as pd
10
- # import json
11
- # from collections import deque
12
-
13
-
14
- # class TestBranch(unittest.TestCase):
15
- # def setUp(self):
16
- # # Assuming no need for actual files or external services for initialization
17
- # self.test_messages = [
18
- # {
19
- # "node_id": "1",
20
- # "timestamp": "2021-01-01 00:00:00",
21
- # "role": "system",
22
- # "sender": "system",
23
- # "content": json.dumps({"system_info": "System message"}),
24
- # },
25
- # {
26
- # "node_id": "2",
27
- # "timestamp": "2021-01-01 00:01:00",
28
- # "role": "user",
29
- # "sender": "user1",
30
- # "content": json.dumps({"instruction": "User message"}),
31
- # },
32
- # {
33
- # "node_id": "3",
34
- # "timestamp": "2021-01-01 00:02:00",
35
- # "role": "assistant",
36
- # "sender": "assistant",
37
- # "content": json.dumps({"response": "Assistant response"}),
38
- # },
39
- # {
40
- # "node_id": "4",
41
- # "timestamp": "2021-01-01 00:03:00",
42
- # "role": "assistant",
43
- # "sender": "action_request",
44
- # "content": json.dumps({"action_request": "Action request"}),
45
- # },
46
- # {
47
- # "node_id": "5",
48
- # "timestamp": "2021-01-01 00:04:00",
49
- # "role": "assistant",
50
- # "sender": "action_response",
51
- # "content": json.dumps({"action_response": "Action response"}),
52
- # },
53
- # ]
54
- # self.branch = Branch(
55
- # branch_name="TestBranch", messages=pd.DataFrame(self.test_messages)
56
- # )
57
-
58
- # def sample_func(param1: int) -> bool:
59
- # """Sample function.
60
-
61
- # Args:
62
- # param1 (int): Description of param1.
63
-
64
- # Returns:
65
- # bool: Description of return value.
66
- # """
67
- # return True
68
-
69
- # self.tool = func_to_tool(sample_func)
70
-
71
- # def test_initialization(self):
72
- # """Test the initialization of the Branch class."""
73
- # self.assertEqual(self.branch.branch_name, "TestBranch")
74
- # self.assertIsInstance(self.branch.tool_manager, ToolManager)
75
- # self.assertIsInstance(self.branch.datalogger, DataLogger)
76
- # self.assertEqual(self.branch.sender, "system")
77
-
78
- # def test_has_tools_property(self):
79
- # """Test the has_tools property."""
80
- # # Initially, no tools are registered
81
- # self.assertFalse(self.branch.has_tools)
82
-
83
- # # Mock tool registration
84
- # self.branch.register_tools(self.tool)
85
- # self.assertTrue(self.branch.has_tools)
86
-
87
- # # @patch("lionagi.core.branch.BaseBranch._from_csv")
88
- # # def test_from_csv(self, mock_from_csv):
89
- # # """Test creating a Branch instance from a CSV file."""
90
- # # filepath = "path/to/your/csvfile.csv"
91
- # # Branch.from_csv(filepath=filepath, branch_name="TestBranchFromCSV")
92
- # # mock_from_csv.assert_called_once_with(
93
- # # filepath=filepath,
94
- # # read_kwargs=None,
95
- # # branch_name="TestBranchFromCSV",
96
- # # service=None,
97
- # # llmconfig=None,
98
- # # tools=None,
99
- # # datalogger=None,
100
- # # persist_path=None,
101
- # # tool_manager=None,
102
- # # )
103
-
104
- # # @patch("lionagi.core.branch.BaseBranch._from_json")
105
- # # def test_from_json(self, mock_from_json):
106
- # # """Test creating a Branch instance from a JSON file."""
107
- # # filepath = "path/to/your/jsonfile.json"
108
- # # Branch.from_json_string(filepath=filepath, branch_name="TestBranchFromJSON")
109
- # # mock_from_json.assert_called_once_with(
110
- # # filepath=filepath,
111
- # # read_kwargs=None,
112
- # # branch_name="TestBranchFromJSON",
113
- # # service=None,
114
- # # llmconfig=None,
115
- # # tools=None,
116
- # # datalogger=None,
117
- # # persist_path=None,
118
- # # tool_manager=None,
119
- # # )
120
-
121
- # def test_messages_describe(self):
122
- # """Test the messages_describe method for accuracy."""
123
- # # Assuming self.branch has been set up with some messages
124
- # description = self.branch.messages_describe()
125
- # self.assertIn("total_messages", description)
126
- # self.assertIn("summary_by_role", description)
127
- # self.assertIn("summary_by_sender", description)
128
- # self.assertIn("registered_tools", description)
129
-
130
- # def test_merge_branch(self):
131
- # """Test merging another Branch instance into the current one."""
132
- # mes = [
133
- # {
134
- # "node_id": "6",
135
- # "timestamp": "2021-01-01 00:01:00",
136
- # "role": "user",
137
- # "sender": "user1",
138
- # "content": json.dumps({"instruction": "User message"}),
139
- # }
140
- # ]
141
- # other_branch = Branch(branch_name="OtherBranch", messages=pd.DataFrame(mes))
142
-
143
- # original_message_count = len(self.branch.messages)
144
- # self.branch.merge_branch(other_branch)
145
- # merged_message_count = len(self.branch.messages)
146
- # self.assertTrue(merged_message_count > original_message_count)
147
-
148
- # def test_register_and_delete_tools(self):
149
- # """Test tool registration and deletion."""
150
- # self.branch.register_tools(self.tool)
151
- # self.assertIn("sample_func", self.branch.tool_manager.registry)
152
- # self.branch.delete_tools(self.tool, verbose=False)
153
- # self.assertNotIn("sample_func", self.branch.tool_manager.registry)
154
-
155
- # def test_send(self):
156
- # """Test sending a mail package."""
157
- # package = {"data": "example"}
158
- # self.branch.send(recipient="BranchB", category="messages", package=package)
159
- # self.assertEqual(len(self.branch.pending_outs), 1)
160
- # mail = self.branch.pending_outs[0]
161
- # self.assertEqual(mail.sender, "system")
162
- # self.assertEqual(mail.recipient, "BranchB")
163
- # self.assertEqual(mail.category, "messages")
164
- # self.assertEqual(mail.package, package)
165
-
166
- # # def test_is_invoked_true(self):
167
- # # branch = Branch()
168
-
169
- # # mock_messages = [
170
- # # MessageUtil.to_json_content({"action_response": {"function": "func_name", "arguments": {}, "output": "result"}})
171
- # # ]
172
- # # branch.messages = pd.DataFrame(mock_messages, columns=['content'])
173
- # # self.assertTrue(branch._is_invoked())
174
-
175
- # def test_is_invoked_false(self):
176
- # """Test that _is_invoked returns False when the last message is not a valid action response."""
177
- # self.assertFalse(self.branch._is_invoked())
178
-
179
-
180
- # class TestBranchReceive(unittest.TestCase):
181
- # def setUp(self):
182
- # self.branch = Branch(branch_name="TestBranch")
183
- # # Set up a mock sender and initial pending_ins structure
184
- # self.sender = "MockSender"
185
- # self.branch.pending_ins[self.sender] = deque()
186
-
187
- # # @patch("lionagi.core.mail.BaseMail")
188
- # # @patch("lionagi.core.branch.util.MessageUtil.validate_messages")
189
- # # def test_receive_messages(self, mock_validate_messages, mock_base_mail):
190
- # # # Prepare a mock mail package with messages
191
- # # messages_df = pd.DataFrame(
192
- # # [
193
- # # {
194
- # # "node_id": "1",
195
- # # "timestamp": "2021-01-01 00:00:00",
196
- # # "role": "system",
197
- # # "sender": "system",
198
- # # "content": json.dumps({"system_info": "System message"}),
199
- # # }
200
- # # ]
201
- # # )
202
- # # mail_package_messages = MagicMock(category="messages", package=messages_df)
203
- # # self.branch.pending_ins[self.sender].append(mail_package_messages)
204
-
205
- # # # Test receiving messages
206
- # # self.branch.receive(self.sender)
207
- # # mock_validate_messages.assert_called_once_with(messages_df)
208
- # # self.assertTrue(len(self.branch.messages) > 0)
209
- # # self.assertEqual(self.branch.pending_ins, {})
210
-
211
- # # def test_receive_tools(self):
212
- # # def sample_func(param1: int) -> bool:
213
- # # """Sample function.
214
-
215
- # # Args:
216
- # # param1 (int): Description of param1.
217
-
218
- # # Returns:
219
- # # bool: Description of return value.
220
- # # """
221
- # # return True
222
-
223
- # # tool = func_to_tool(sample_func)
224
- # # mail_package_tools = MagicMock(category="tools", package=tool)
225
- # # self.branch.pending_ins[self.sender].append(mail_package_tools)
226
-
227
- # # # Test receiving tools
228
- # # self.branch.receive(self.sender)
229
- # # self.assertIn(tool, self.branch.tool_manager.registry.values())
230
-
231
- # def test_receive_service(self):
232
- # # Prepare a mock mail package with a service
233
- # from lionagi.libs.ln_api import BaseService
234
-
235
- # service = BaseService()
236
- # mail_package_service = MagicMock(category="provider", package=service)
237
- # self.branch.pending_ins[self.sender].append(mail_package_service)
238
-
239
- # # Test receiving service
240
- # self.branch.receive(self.sender)
241
- # self.assertEqual(self.branch.service, service)
242
-
243
- # def test_receive_llmconfig(self):
244
- # # Prepare a mock mail package with llmconfig
245
- # llmconfig = self.branch.llmconfig.copy()
246
- # mail_package_llmconfig = MagicMock(category="llmconfig", package=llmconfig)
247
- # self.branch.pending_ins[self.sender].append(mail_package_llmconfig)
248
-
249
- # # Test receiving llmconfig
250
- # self.branch.receive(self.sender)
251
- # self.assertEqual(llmconfig, self.branch.llmconfig)
252
-
253
- # def test_invalid_format(self):
254
- # # Test handling of invalid package format
255
- # invalid_package = MagicMock(category="messages", package="Not a DataFrame")
256
- # self.branch.pending_ins[self.sender].append(invalid_package)
257
-
258
- # with self.assertRaises(ValueError) as context:
259
- # self.branch.receive(self.sender)
260
- # self.assertTrue("Invalid messages format" in str(context.exception))
261
-
262
- # def test_receive_all(self):
263
- # messages_df = pd.DataFrame(
264
- # [
265
- # {
266
- # "node_id": "1",
267
- # "timestamp": "2021-01-01 00:00:00",
268
- # "role": "system",
269
- # "sender": "system",
270
- # "content": json.dumps({"system_info": "System message"}),
271
- # }
272
- # ]
273
- # )
274
- # mail_package_messages = MagicMock(category="messages", package=messages_df)
275
- # self.branch.pending_ins[self.sender].append(mail_package_messages)
276
-
277
- # llmconfig = self.branch.llmconfig.copy()
278
- # mail_package_llmconfig = MagicMock(category="llmconfig", package=llmconfig)
279
- # self.branch.pending_ins[self.sender].append(mail_package_llmconfig)
280
-
281
- # self.branch.receive_all()
282
- # self.assertTrue(
283
- # not self.branch.pending_ins,
284
- # "pending_ins should be empty or contain only skipped requests",
285
- # )
286
- # self.assertTrue(..., "Additional assertions based on your implementation")
287
-
288
-
289
- # # Chatflow: call_chatcompletion, chat, ReAct, auto_followup
290
-
291
- # if __name__ == "__main__":
292
- # unittest.main()
1
+ import unittest
2
+ from unittest.mock import MagicMock, patch
3
+ import lionagi as li
4
+ from lionagi.core.message import System, Instruction, AssistantResponse, ActionResponse
5
+ from lionagi.core.collections import Pile, Progression, Exchange
6
+ from lionagi.core.action.tool_manager import ToolManager
7
+
8
+
9
+ class TestBranch(unittest.TestCase):
10
+
11
+ def setUp(self):
12
+ self.branch = li.Branch()
13
+
14
+ def test_initialize_branch(self):
15
+ self.assertIsInstance(self.branch, li.Branch)
16
+ self.assertIsInstance(self.branch.messages, Pile)
17
+ self.assertIsInstance(self.branch.progress, Progression)
18
+ self.assertIsInstance(self.branch.tool_manager, ToolManager)
19
+ self.assertIsInstance(self.branch.mailbox, Exchange)
20
+ self.assertIsInstance(self.branch.imodel, li.iModel)
21
+
22
+ def test_add_message_system(self):
23
+ self.branch.add_message(
24
+ system="You are a helpful assistant, let's think step by step"
25
+ )
26
+ self.assertEqual(len(self.branch.messages), 1)
27
+ self.assertEqual(
28
+ self.branch.messages[0].content,
29
+ {"system_info": "You are a helpful assistant, let's think step by step"},
30
+ )
31
+
32
+ def test_to_df(self):
33
+ self.branch.add_message(
34
+ system="You are a helpful assistant, let's think step by step"
35
+ )
36
+ df = self.branch.to_df()
37
+ self.assertEqual(df.iloc[0]["message_type"], "System")
38
+ self.assertEqual(df.iloc[0]["role"], "system")
39
+
40
+ def test_to_chat_messages(self):
41
+ self.branch.add_message(
42
+ system="You are a helpful assistant, let's think step by step"
43
+ )
44
+ chat_msgs = self.branch.to_chat_messages()
45
+ self.assertEqual(chat_msgs[0]["role"], "system")
46
+ self.assertEqual(
47
+ chat_msgs[0]["content"],
48
+ "You are a helpful assistant, let's think step by step",
49
+ )
50
+
51
+ @patch("lionagi.Branch.chat")
52
+ async def test_chat(self, mock_chat):
53
+ mock_chat.return_value = (
54
+ "Rain poured, but their love shone brighter than any storm."
55
+ )
56
+ response = await self.branch.chat("tell me a 10 word story", logprobs=True)
57
+ self.assertEqual(
58
+ response, "Rain poured, but their love shone brighter than any storm."
59
+ )
60
+ mock_chat.assert_called_once_with("tell me a 10 word story", logprobs=True)
61
+
62
+ def test_metadata(self):
63
+ self.branch.add_message(
64
+ system="You are a helpful assistant, let's think step by step"
65
+ )
66
+ self.assertIn("last_updated", self.branch.messages[0].metadata)
67
+
68
+ # def test_register_tools(self):
69
+ # tool = MagicMock()
70
+ # self.branch.register_tools([tool])
71
+ # self.assertIn(tool, self.branch.tool_manager.registry.values())
72
+
73
+ # def test_delete_tools(self):
74
+ # tool = MagicMock()
75
+ # tool.schema_ = {"function": {"name": "test_tool"}}
76
+ # self.branch.register_tools([tool])
77
+ # self.branch.delete_tools([tool])
78
+ # self.assertNotIn("test_tool", self.branch.tool_manager.registry)
79
+
80
+ def test_send_receive_mail(self):
81
+ self.branch.send = MagicMock()
82
+ self.branch.receive = MagicMock()
83
+ package = MagicMock()
84
+ self.branch.send(recipient="recipient_id", category="message", package=package)
85
+ self.branch.receive(sender="recipient_id")
86
+ self.branch.send.assert_called_once_with(
87
+ recipient="recipient_id", category="message", package=package
88
+ )
89
+ self.branch.receive.assert_called_once_with(sender="recipient_id")
90
+
91
+ async def test_chat_with_tool(self):
92
+ async def mock_multiply(number1, number2, number3=1):
93
+ return number1 * number2 * number3
94
+
95
+ instruction = """
96
+ solve the following problem
97
+ """
98
+ context = """
99
+ I have 730_000 trees, with average 123 apples per tree, each weigh 0.4 lbs.
100
+ 20 percent are bad and sold for 0.1 dollar per lbs, 30 percent are sold to
101
+ brewery for 0.3 dollar per apple, what is my revenue?
102
+ """
103
+
104
+ self.branch = li.Branch(
105
+ "act like a calculator, invoke tool uses", tools=[mock_multiply]
106
+ )
107
+ response = await self.branch.chat(
108
+ instruction=instruction, context=context, tools=True
109
+ )
110
+ self.assertIsNotNone(response)
111
+ self.assertIn("revenue", response.lower())
112
+
113
+
114
+ if __name__ == "__main__":
115
+ unittest.main()
@@ -0,0 +1,46 @@
1
+ import unittest
2
+ from lionagi import Form
3
+
4
+
5
+ class TestForm(unittest.TestCase):
6
+
7
+ def setUp(self):
8
+ self.form = Form(assignment="input1, input2 -> output")
9
+
10
+ def test_initial_state(self):
11
+ self.assertEqual(self.form.input_fields, ["input1", "input2"])
12
+ self.assertEqual(self.form.requested_fields, ["output"])
13
+ self.assertFalse(self.form.workable)
14
+ self.assertFalse(self.form.filled)
15
+ self.assertEqual(
16
+ self.form.work_fields, {"input1": None, "input2": None, "output": None}
17
+ )
18
+
19
+ def test_fill_input_fields(self):
20
+ self.form.fill(input1=1, input2=2)
21
+ self.assertTrue(self.form.workable)
22
+ self.assertFalse(self.form.filled)
23
+ self.assertEqual(
24
+ self.form.work_fields, {"input1": 1, "input2": 2, "output": None}
25
+ )
26
+
27
+ def test_fill_all_fields(self):
28
+ self.form.fill(input1=1, input2=2)
29
+ self.form.fill(output=3)
30
+ self.assertTrue(self.form.filled)
31
+ self.assertFalse(self.form.workable)
32
+ self.assertEqual(self.form.work_fields, {"input1": 1, "input2": 2, "output": 3})
33
+
34
+ def test_fill_once(self):
35
+ self.form.fill(input1=1, input2=2)
36
+ self.form.fill(output=3)
37
+ with self.assertRaises(ValueError) as context:
38
+ self.form.fill(input1=2, input2=3)
39
+ self.assertTrue(
40
+ "Form is filled, cannot be worked on again" in str(context.exception)
41
+ )
42
+ self.assertEqual(self.form.work_fields, {"input1": 1, "input2": 2, "output": 3})
43
+
44
+
45
+ if __name__ == "__main__":
46
+ unittest.main()
@@ -0,0 +1,105 @@
1
+ import unittest
2
+ from lionagi.core.report.form import Form
3
+ from lionagi.core.report.report import Report
4
+
5
+
6
+ class TestForm(unittest.TestCase):
7
+
8
+ def setUp(self):
9
+ self.form = Form(assignment="input1, input2 -> output")
10
+
11
+ def test_initial_fields(self):
12
+ self.assertEqual(self.form.input_fields, ["input1", "input2"])
13
+ self.assertEqual(self.form.requested_fields, ["output"])
14
+ self.assertFalse(self.form.workable)
15
+ self.assertFalse(self.form.filled)
16
+ self.assertEqual(
17
+ self.form.work_fields, {"input1": None, "input2": None, "output": None}
18
+ )
19
+
20
+ def test_fill_input_fields(self):
21
+ self.form.fill(input1=1, input2=2)
22
+ self.assertTrue(self.form.workable)
23
+ self.assertFalse(self.form.filled)
24
+ self.assertEqual(
25
+ self.form.work_fields, {"input1": 1, "input2": 2, "output": None}
26
+ )
27
+
28
+ def test_fill_output_field(self):
29
+ self.form.fill(input1=1, input2=2)
30
+ self.form.fill(output=3)
31
+ self.assertTrue(self.form.filled)
32
+ self.assertEqual(self.form.work_fields, {"input1": 1, "input2": 2, "output": 3})
33
+ self.assertFalse(self.form.workable)
34
+
35
+ def test_fill_again_raises_error(self):
36
+ self.form.fill(input1=1, input2=2, output=3)
37
+ with self.assertRaises(ValueError):
38
+ self.form.fill(input1=2, input2=3)
39
+
40
+
41
+ class TestReport(unittest.TestCase):
42
+
43
+ def setUp(self):
44
+ self.report = Report(assignment="a, b -> c")
45
+
46
+ def test_initial_fields(self):
47
+ self.assertEqual(self.report.input_fields, ["a", "b"])
48
+ self.assertEqual(self.report.requested_fields, ["c"])
49
+ self.assertEqual(self.report.work_fields, {"a": None, "b": None, "c": None})
50
+ self.assertFalse(self.report.filled)
51
+ self.assertFalse(self.report.workable)
52
+
53
+ def test_fill_input_fields(self):
54
+ self.report.fill(a=3, b=4)
55
+ self.assertEqual(self.report.work_fields, {"a": 3, "b": 4, "c": None})
56
+ self.assertTrue(self.report.workable)
57
+
58
+ def test_fill_output_field(self):
59
+ self.report.fill(a=3, b=4)
60
+ self.report.fill(c=4)
61
+ self.assertEqual(self.report.work_fields, {"a": 3, "b": 4, "c": 4})
62
+ self.assertTrue(self.report.filled)
63
+ self.assertFalse(self.report.workable)
64
+
65
+ def test_next_forms_none(self):
66
+ self.assertIsNone(self.report.next_forms())
67
+
68
+ def test_complex_assignment(self):
69
+ self.report = Report(
70
+ assignment="a, b -> h",
71
+ form_assignments=[
72
+ "a, b -> c",
73
+ "a -> e",
74
+ "b -> f",
75
+ "c -> g",
76
+ "e, f, g -> h",
77
+ ],
78
+ a=3,
79
+ b=4,
80
+ )
81
+ self.assertEqual(
82
+ self.report.work_fields,
83
+ {
84
+ "a": None,
85
+ "b": None,
86
+ "c": None,
87
+ "e": None,
88
+ "f": None,
89
+ "g": None,
90
+ "h": None,
91
+ },
92
+ )
93
+ self.report.fill(c=5, e=6, f=7, g=8, h=10)
94
+ self.assertEqual(
95
+ self.report.work_fields,
96
+ {"a": None, "b": None, "c": 5, "e": 6, "f": 7, "g": 8, "h": 10},
97
+ )
98
+ self.assertFalse(self.report.filled)
99
+ self.assertFalse(self.report.workable)
100
+ with self.assertRaises(ValueError):
101
+ self.report.fill(c="xx", e="yy")
102
+
103
+
104
+ if __name__ == "__main__":
105
+ unittest.main()
@@ -0,0 +1,111 @@
1
+ import unittest
2
+ from typing import Dict, List, Any
3
+ from lionagi.core.report.form import Form
4
+ from lionagi.core.report.report import Report
5
+ from lionagi.core.collections.abc import FieldError
6
+ from lionagi.core.rule.base import Rule
7
+ from lionagi.core.rule.rulebook import RuleBook
8
+ from lionagi.core.validator.validator import (
9
+ Validator,
10
+ _DEFAULT_RULEORDER,
11
+ _DEFAULT_RULES,
12
+ )
13
+
14
+
15
+ class MockRule(Rule):
16
+ def applies(self, *args, **kwargs):
17
+ return True
18
+
19
+ async def invoke(self, field, value, form, *args, **kwargs):
20
+ if isinstance(value, int) and value < 0:
21
+ raise ValueError(f"Field {field} cannot be negative.")
22
+ return value
23
+
24
+ async def validate(self, value: Any) -> Any:
25
+ return value
26
+
27
+
28
+ class ValidatorTestCase(unittest.TestCase):
29
+ def setUp(self):
30
+ self.validator = Validator(
31
+ rulebook=RuleBook(rules=_DEFAULT_RULES, ruleorder=_DEFAULT_RULEORDER)
32
+ )
33
+ self.form = Form(
34
+ assignment="input1, input2 -> output",
35
+ input_fields=["input1", "input2"],
36
+ requested_fields=["output"],
37
+ )
38
+ self.form2 = Form(
39
+ assignment="total_amount, bike_price -> repair_price",
40
+ input_fields=["total_amount", "bike_price"],
41
+ requested_fields=["repair_price"],
42
+ )
43
+
44
+ def test_initiate_rules(self):
45
+ self.assertTrue(isinstance(self.validator.active_rules, dict))
46
+
47
+ async def test_validate_field(self):
48
+ self.validator.add_rule("mock_rule", MockRule)
49
+ valid_value = await self.validator.validate_field("input1", 10, self.form)
50
+ self.assertEqual(valid_value, 10)
51
+
52
+ with self.assertRaises(FieldError):
53
+ await self.validator.validate_field("input1", -5, self.form)
54
+
55
+ async def test_validate_response(self):
56
+ response = {"output": 20}
57
+ validated_form = await self.validator.validate_response(self.form, response)
58
+ self.assertEqual(validated_form.output, 20)
59
+
60
+ response_str = "20"
61
+ self.form.requested_fields = ["output"]
62
+ validated_form = await self.validator.validate_response(self.form, response_str)
63
+ self.assertEqual(validated_form.output, 20)
64
+
65
+ with self.assertRaises(ValueError):
66
+ await self.validator.validate_response(self.form, "invalid_response")
67
+
68
+ async def test_validate_report(self):
69
+ report = Report(assignment="a, b -> c", forms=[self.form])
70
+ forms = [self.form, self.form2]
71
+ validated_report = await self.validator.validate_report(report, forms)
72
+ self.assertIsInstance(validated_report, Report)
73
+
74
+ def test_add_rule(self):
75
+ self.validator.add_rule("mock_rule", MockRule)
76
+ self.assertIn("mock_rule", self.validator.active_rules)
77
+
78
+ def test_remove_rule(self):
79
+ self.validator.remove_rule("mock_rule")
80
+ self.assertNotIn("mock_rule", self.validator.active_rules)
81
+
82
+ def test_enable_rule(self):
83
+ self.validator.enable_rule("mock_rule", enable=False)
84
+ self.assertFalse(self.validator.active_rules["mock_rule"].enabled)
85
+ self.validator.enable_rule("mock_rule", enable=True)
86
+ self.assertTrue(self.validator.active_rules["mock_rule"].enabled)
87
+
88
+ def test_disable_rule(self):
89
+ self.validator.disable_rule("mock_rule")
90
+ self.assertFalse(self.validator.active_rules["mock_rule"].enabled)
91
+
92
+ def test_log_validation_attempt(self):
93
+ result = {"output": 20}
94
+ self.validator.log_validation_attempt(self.form, result)
95
+ self.assertEqual(len(self.validator.validation_log), 1)
96
+
97
+ def test_log_validation_error(self):
98
+ self.validator.log_validation_error("input1", 10, "Error message")
99
+ self.assertEqual(len(self.validator.validation_log), 1)
100
+
101
+ def test_get_validation_summary(self):
102
+ self.validator.log_validation_attempt(self.form, {"output": 20})
103
+ self.validator.log_validation_error("input1", 10, "Error message")
104
+ summary = self.validator.get_validation_summary()
105
+ self.assertEqual(summary["total_attempts"], 2)
106
+ self.assertEqual(len(summary["errors"]), 1)
107
+ self.assertEqual(len(summary["successful_attempts"]), 1)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ unittest.main()