lionagi 0.0.312__py3-none-any.whl → 0.2.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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()