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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. lionagi/__init__.py +61 -3
  2. lionagi/core/__init__.py +0 -14
  3. lionagi/core/_setting/_setting.py +59 -0
  4. lionagi/core/action/__init__.py +14 -0
  5. lionagi/core/action/function_calling.py +136 -0
  6. lionagi/core/action/manual.py +1 -0
  7. lionagi/core/action/node.py +109 -0
  8. lionagi/core/action/tool.py +114 -0
  9. lionagi/core/action/tool_manager.py +356 -0
  10. lionagi/core/agent/__init__.py +0 -3
  11. lionagi/core/agent/base_agent.py +45 -36
  12. lionagi/core/agent/eval/evaluator.py +1 -0
  13. lionagi/core/agent/eval/vote.py +40 -0
  14. lionagi/core/agent/learn/learner.py +59 -0
  15. lionagi/core/agent/plan/unit_template.py +1 -0
  16. lionagi/core/collections/__init__.py +17 -0
  17. lionagi/core/collections/_logger.py +319 -0
  18. lionagi/core/collections/abc/__init__.py +53 -0
  19. lionagi/core/collections/abc/component.py +615 -0
  20. lionagi/core/collections/abc/concepts.py +297 -0
  21. lionagi/core/collections/abc/exceptions.py +150 -0
  22. lionagi/core/collections/abc/util.py +45 -0
  23. lionagi/core/collections/exchange.py +161 -0
  24. lionagi/core/collections/flow.py +426 -0
  25. lionagi/core/collections/model.py +419 -0
  26. lionagi/core/collections/pile.py +913 -0
  27. lionagi/core/collections/progression.py +236 -0
  28. lionagi/core/collections/util.py +64 -0
  29. lionagi/core/director/direct.py +314 -0
  30. lionagi/core/director/director.py +2 -0
  31. lionagi/core/engine/branch_engine.py +333 -0
  32. lionagi/core/engine/instruction_map_engine.py +204 -0
  33. lionagi/core/engine/sandbox_.py +14 -0
  34. lionagi/core/engine/script_engine.py +99 -0
  35. lionagi/core/executor/base_executor.py +90 -0
  36. lionagi/core/executor/graph_executor.py +330 -0
  37. lionagi/core/executor/neo4j_executor.py +384 -0
  38. lionagi/core/generic/__init__.py +7 -0
  39. lionagi/core/generic/edge.py +112 -0
  40. lionagi/core/generic/edge_condition.py +16 -0
  41. lionagi/core/generic/graph.py +236 -0
  42. lionagi/core/generic/hyperedge.py +1 -0
  43. lionagi/core/generic/node.py +220 -0
  44. lionagi/core/generic/tree.py +48 -0
  45. lionagi/core/generic/tree_node.py +79 -0
  46. lionagi/core/mail/__init__.py +7 -3
  47. lionagi/core/mail/mail.py +25 -0
  48. lionagi/core/mail/mail_manager.py +142 -58
  49. lionagi/core/mail/package.py +45 -0
  50. lionagi/core/mail/start_mail.py +36 -0
  51. lionagi/core/message/__init__.py +19 -0
  52. lionagi/core/message/action_request.py +133 -0
  53. lionagi/core/message/action_response.py +135 -0
  54. lionagi/core/message/assistant_response.py +95 -0
  55. lionagi/core/message/instruction.py +234 -0
  56. lionagi/core/message/message.py +101 -0
  57. lionagi/core/message/system.py +86 -0
  58. lionagi/core/message/util.py +283 -0
  59. lionagi/core/report/__init__.py +4 -0
  60. lionagi/core/report/base.py +217 -0
  61. lionagi/core/report/form.py +231 -0
  62. lionagi/core/report/report.py +166 -0
  63. lionagi/core/report/util.py +28 -0
  64. lionagi/core/rule/__init__.py +0 -0
  65. lionagi/core/rule/_default.py +16 -0
  66. lionagi/core/rule/action.py +99 -0
  67. lionagi/core/rule/base.py +238 -0
  68. lionagi/core/rule/boolean.py +56 -0
  69. lionagi/core/rule/choice.py +47 -0
  70. lionagi/core/rule/mapping.py +96 -0
  71. lionagi/core/rule/number.py +71 -0
  72. lionagi/core/rule/rulebook.py +109 -0
  73. lionagi/core/rule/string.py +52 -0
  74. lionagi/core/rule/util.py +35 -0
  75. lionagi/core/session/__init__.py +0 -3
  76. lionagi/core/session/branch.py +431 -0
  77. lionagi/core/session/directive_mixin.py +287 -0
  78. lionagi/core/session/session.py +230 -902
  79. lionagi/core/structure/__init__.py +1 -0
  80. lionagi/core/structure/chain.py +1 -0
  81. lionagi/core/structure/forest.py +1 -0
  82. lionagi/core/structure/graph.py +1 -0
  83. lionagi/core/structure/tree.py +1 -0
  84. lionagi/core/unit/__init__.py +5 -0
  85. lionagi/core/unit/parallel_unit.py +245 -0
  86. lionagi/core/unit/template/__init__.py +0 -0
  87. lionagi/core/unit/template/action.py +81 -0
  88. lionagi/core/unit/template/base.py +51 -0
  89. lionagi/core/unit/template/plan.py +84 -0
  90. lionagi/core/unit/template/predict.py +109 -0
  91. lionagi/core/unit/template/score.py +124 -0
  92. lionagi/core/unit/template/select.py +104 -0
  93. lionagi/core/unit/unit.py +362 -0
  94. lionagi/core/unit/unit_form.py +305 -0
  95. lionagi/core/unit/unit_mixin.py +1168 -0
  96. lionagi/core/unit/util.py +71 -0
  97. lionagi/core/validator/__init__.py +0 -0
  98. lionagi/core/validator/validator.py +364 -0
  99. lionagi/core/work/__init__.py +0 -0
  100. lionagi/core/work/work.py +76 -0
  101. lionagi/core/work/work_function.py +101 -0
  102. lionagi/core/work/work_queue.py +103 -0
  103. lionagi/core/work/worker.py +258 -0
  104. lionagi/core/work/worklog.py +120 -0
  105. lionagi/experimental/__init__.py +0 -0
  106. lionagi/experimental/compressor/__init__.py +0 -0
  107. lionagi/experimental/compressor/base.py +46 -0
  108. lionagi/experimental/compressor/llm_compressor.py +247 -0
  109. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  110. lionagi/experimental/compressor/util.py +70 -0
  111. lionagi/experimental/directive/__init__.py +19 -0
  112. lionagi/experimental/directive/parser/__init__.py +0 -0
  113. lionagi/experimental/directive/parser/base_parser.py +282 -0
  114. lionagi/experimental/directive/template/__init__.py +0 -0
  115. lionagi/experimental/directive/template/base_template.py +79 -0
  116. lionagi/experimental/directive/template/schema.py +36 -0
  117. lionagi/experimental/directive/tokenizer.py +73 -0
  118. lionagi/experimental/evaluator/__init__.py +0 -0
  119. lionagi/experimental/evaluator/ast_evaluator.py +131 -0
  120. lionagi/experimental/evaluator/base_evaluator.py +218 -0
  121. lionagi/experimental/knowledge/__init__.py +0 -0
  122. lionagi/experimental/knowledge/base.py +10 -0
  123. lionagi/experimental/knowledge/graph.py +0 -0
  124. lionagi/experimental/memory/__init__.py +0 -0
  125. lionagi/experimental/strategies/__init__.py +0 -0
  126. lionagi/experimental/strategies/base.py +1 -0
  127. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  128. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  129. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  130. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  131. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  132. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  133. lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
  134. lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
  135. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  136. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  137. lionagi/integrations/chunker/__init__.py +0 -0
  138. lionagi/integrations/chunker/chunk.py +312 -0
  139. lionagi/integrations/config/oai_configs.py +38 -7
  140. lionagi/integrations/config/ollama_configs.py +1 -1
  141. lionagi/integrations/config/openrouter_configs.py +14 -2
  142. lionagi/integrations/loader/__init__.py +0 -0
  143. lionagi/integrations/loader/load.py +253 -0
  144. lionagi/integrations/loader/load_util.py +195 -0
  145. lionagi/integrations/provider/_mapping.py +46 -0
  146. lionagi/integrations/provider/litellm.py +2 -1
  147. lionagi/integrations/provider/mlx_service.py +16 -9
  148. lionagi/integrations/provider/oai.py +91 -4
  149. lionagi/integrations/provider/ollama.py +7 -6
  150. lionagi/integrations/provider/openrouter.py +115 -8
  151. lionagi/integrations/provider/services.py +2 -2
  152. lionagi/integrations/provider/transformers.py +18 -22
  153. lionagi/integrations/storage/__init__.py +3 -0
  154. lionagi/integrations/storage/neo4j.py +665 -0
  155. lionagi/integrations/storage/storage_util.py +287 -0
  156. lionagi/integrations/storage/structure_excel.py +285 -0
  157. lionagi/integrations/storage/to_csv.py +63 -0
  158. lionagi/integrations/storage/to_excel.py +83 -0
  159. lionagi/libs/__init__.py +26 -1
  160. lionagi/libs/ln_api.py +78 -23
  161. lionagi/libs/ln_context.py +37 -0
  162. lionagi/libs/ln_convert.py +21 -9
  163. lionagi/libs/ln_func_call.py +69 -28
  164. lionagi/libs/ln_image.py +107 -0
  165. lionagi/libs/ln_knowledge_graph.py +405 -0
  166. lionagi/libs/ln_nested.py +26 -11
  167. lionagi/libs/ln_parse.py +110 -14
  168. lionagi/libs/ln_queue.py +117 -0
  169. lionagi/libs/ln_tokenize.py +164 -0
  170. lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
  171. lionagi/libs/special_tokens.py +172 -0
  172. lionagi/libs/sys_util.py +107 -2
  173. lionagi/lions/__init__.py +0 -0
  174. lionagi/lions/coder/__init__.py +0 -0
  175. lionagi/lions/coder/add_feature.py +20 -0
  176. lionagi/lions/coder/base_prompts.py +22 -0
  177. lionagi/lions/coder/code_form.py +13 -0
  178. lionagi/lions/coder/coder.py +168 -0
  179. lionagi/lions/coder/util.py +96 -0
  180. lionagi/lions/researcher/__init__.py +0 -0
  181. lionagi/lions/researcher/data_source/__init__.py +0 -0
  182. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  183. lionagi/lions/researcher/data_source/google_.py +199 -0
  184. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  185. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  186. lionagi/tests/integrations/__init__.py +0 -0
  187. lionagi/tests/libs/__init__.py +0 -0
  188. lionagi/tests/libs/test_field_validators.py +353 -0
  189. lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
  190. lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
  191. lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
  192. lionagi/tests/libs/test_queue.py +67 -0
  193. lionagi/tests/test_core/collections/__init__.py +0 -0
  194. lionagi/tests/test_core/collections/test_component.py +206 -0
  195. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  196. lionagi/tests/test_core/collections/test_flow.py +145 -0
  197. lionagi/tests/test_core/collections/test_pile.py +171 -0
  198. lionagi/tests/test_core/collections/test_progression.py +129 -0
  199. lionagi/tests/test_core/generic/__init__.py +0 -0
  200. lionagi/tests/test_core/generic/test_edge.py +67 -0
  201. lionagi/tests/test_core/generic/test_graph.py +96 -0
  202. lionagi/tests/test_core/generic/test_node.py +106 -0
  203. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  204. lionagi/tests/test_core/test_branch.py +115 -292
  205. lionagi/tests/test_core/test_form.py +46 -0
  206. lionagi/tests/test_core/test_report.py +105 -0
  207. lionagi/tests/test_core/test_validator.py +111 -0
  208. lionagi/version.py +1 -1
  209. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
  210. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
  211. lionagi-0.2.1.dist-info/RECORD +240 -0
  212. lionagi/core/branch/__init__.py +0 -4
  213. lionagi/core/branch/base_branch.py +0 -654
  214. lionagi/core/branch/branch.py +0 -471
  215. lionagi/core/branch/branch_flow_mixin.py +0 -96
  216. lionagi/core/branch/executable_branch.py +0 -347
  217. lionagi/core/branch/util.py +0 -323
  218. lionagi/core/direct/__init__.py +0 -6
  219. lionagi/core/direct/predict.py +0 -161
  220. lionagi/core/direct/score.py +0 -278
  221. lionagi/core/direct/select.py +0 -169
  222. lionagi/core/direct/utils.py +0 -87
  223. lionagi/core/direct/vote.py +0 -64
  224. lionagi/core/flow/base/baseflow.py +0 -23
  225. lionagi/core/flow/monoflow/ReAct.py +0 -238
  226. lionagi/core/flow/monoflow/__init__.py +0 -9
  227. lionagi/core/flow/monoflow/chat.py +0 -95
  228. lionagi/core/flow/monoflow/chat_mixin.py +0 -263
  229. lionagi/core/flow/monoflow/followup.py +0 -214
  230. lionagi/core/flow/polyflow/__init__.py +0 -1
  231. lionagi/core/flow/polyflow/chat.py +0 -248
  232. lionagi/core/mail/schema.py +0 -56
  233. lionagi/core/messages/__init__.py +0 -3
  234. lionagi/core/messages/schema.py +0 -533
  235. lionagi/core/prompt/prompt_template.py +0 -316
  236. lionagi/core/schema/__init__.py +0 -22
  237. lionagi/core/schema/action_node.py +0 -29
  238. lionagi/core/schema/base_mixin.py +0 -296
  239. lionagi/core/schema/base_node.py +0 -199
  240. lionagi/core/schema/condition.py +0 -24
  241. lionagi/core/schema/data_logger.py +0 -354
  242. lionagi/core/schema/data_node.py +0 -93
  243. lionagi/core/schema/prompt_template.py +0 -67
  244. lionagi/core/schema/structure.py +0 -910
  245. lionagi/core/tool/__init__.py +0 -3
  246. lionagi/core/tool/tool_manager.py +0 -280
  247. lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
  248. lionagi/tests/test_core/test_base_branch.py +0 -427
  249. lionagi/tests/test_core/test_chat_flow.py +0 -63
  250. lionagi/tests/test_core/test_mail_manager.py +0 -75
  251. lionagi/tests/test_core/test_prompts.py +0 -51
  252. lionagi/tests/test_core/test_session.py +0 -254
  253. lionagi/tests/test_core/test_session_base_util.py +0 -312
  254. lionagi/tests/test_core/test_tool_manager.py +0 -95
  255. lionagi-0.0.312.dist-info/RECORD +0 -111
  256. /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
  257. /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
  258. /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
  259. /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
  260. /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
  261. /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
  262. /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
  263. /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
  264. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  265. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  266. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  267. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,79 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from typing import Any, Dict
18
+ import re
19
+
20
+ from ..evaluator.base_evaluator import BaseEvaluator
21
+
22
+
23
+ class DirectiveTemplate:
24
+ """Enhanced base template class for processing templates with conditionals and loops."""
25
+
26
+ def __init__(self, template_str: str):
27
+ self.template_str = template_str
28
+ self.evaluator = BaseEvaluator()
29
+
30
+ def _render_conditionals(self, context: Dict[str, Any]) -> str:
31
+ """Processes conditional statements with improved logic and support for 'else'."""
32
+ pattern = re.compile(r"\{if (.*?)\}(.*?)\{else\}(.*?)\{endif\}", re.DOTALL)
33
+
34
+ def evaluate_condition(match):
35
+ condition, if_text, else_text = match.groups()
36
+ if self.evaluator.evaluate(condition, context):
37
+ return if_text
38
+ else:
39
+ return else_text
40
+
41
+ return pattern.sub(evaluate_condition, self.template_str)
42
+
43
+ def _render_loops(self, template: str, context: Dict[str, Any]) -> str:
44
+ """Processes loop statements within the template."""
45
+ loop_pattern = re.compile(r"\{for (\w+) in (\w+)\}(.*?)\{endfor\}", re.DOTALL)
46
+
47
+ def render_loop(match):
48
+ iterator_var, collection_name, loop_body = match.groups()
49
+ collection = context.get(collection_name, [])
50
+ if not isinstance(collection, (list, range)):
51
+ raise ValueError(
52
+ f"Expected list or range for '{collection_name}', got {type(collection).__name__}."
53
+ )
54
+
55
+ loop_result = ""
56
+ for item in collection:
57
+ loop_context = context.copy()
58
+ loop_context[iterator_var] = item
59
+ loop_result += self.fill(loop_body, loop_context)
60
+
61
+ return loop_result
62
+
63
+ return loop_pattern.sub(render_loop, template)
64
+
65
+ def fill(self, template_str: str = "", context: Dict[str, Any] = {}) -> str:
66
+ """Fills the template with values from context after processing conditionals and loops."""
67
+ if not template_str: # Use the instance's template if not provided
68
+ template_str = self.template_str
69
+
70
+ # First, process conditionals with 'else'
71
+ template_with_conditionals = self._render_conditionals(template_str)
72
+ # Then, process loops
73
+ template_with_loops = self._render_loops(template_with_conditionals, context)
74
+ # Finally, substitute the placeholders with context values
75
+ try:
76
+ return template_with_loops.format(**context)
77
+ except KeyError as e:
78
+ print(f"Missing key in context: {e}")
79
+ return template_with_loops
@@ -0,0 +1,36 @@
1
+ class Node:
2
+ """Base class for all nodes in the abstract syntax tree (AST)."""
3
+
4
+ pass
5
+
6
+
7
+ class IfNode(Node):
8
+ """Represents an 'IF' statement in the AST."""
9
+
10
+ def __init__(self, condition, true_block, false_block=None):
11
+ self.condition = condition
12
+ self.true_block = true_block
13
+ self.false_block = false_block
14
+
15
+
16
+ class ForNode(Node):
17
+ """Represents a 'FOR' loop in the AST."""
18
+
19
+ def __init__(self, iterator, collection, block):
20
+ self.iterator = iterator
21
+ self.collection = collection
22
+ self.block = block
23
+
24
+
25
+ class TryNode(Node):
26
+ """Represents a 'TRY-EXCEPT' block in the AST."""
27
+
28
+ def __init__(self, try_block, except_block):
29
+ self.try_block = try_block
30
+ self.except_block = except_block
31
+
32
+
33
+ class ActionNode(Node):
34
+
35
+ def __init__(self, action) -> None:
36
+ self.action = action
@@ -0,0 +1,73 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ import re
18
+
19
+
20
+ class BaseToken:
21
+ def __init__(self, type_, value):
22
+ self.type = type_
23
+ self.value = value
24
+
25
+ def __repr__(self):
26
+ return f"BaseDirectiveToken({self.type}, {self.value})"
27
+
28
+
29
+ class BaseTokenizer:
30
+ TOKEN_TYPES = {
31
+ "KEYWORD": r"\b(BEGIN|END|IF|ELSE|FOR|IN|TRY|EXCEPT|ENDIF|ENDFOR|ENDTRY|DO)\b",
32
+ "OPERATOR": r"(==|!=|>=|<=|>|<|&&|\|\||!)",
33
+ "FUNCTION_CALL": r"\b[a-zA-Z_][a-zA-Z0-9_]*\b\((.*?)\)",
34
+ "LITERAL": r'(\d+|\'.*?\'|".*?")',
35
+ "IDENTIFIER": r"\b[a-zA-Z_][a-zA-Z0-9_]*\b",
36
+ "PUNCTUATION": r"(;|,|\(|\))",
37
+ "WHITESPACE": r"\s+",
38
+ }
39
+
40
+ def __init__(self, script):
41
+ self.script = script
42
+ self.tokens = []
43
+ self.tokenize()
44
+
45
+ @property
46
+ def is_empty(self):
47
+ return self.tokens == []
48
+
49
+ def tokenize(self):
50
+ position = 0
51
+ while position < len(self.script):
52
+ match = None
53
+ for type_, pattern in self.TOKEN_TYPES.items():
54
+ regex = re.compile(pattern)
55
+ match = regex.match(self.script, position)
56
+ if match:
57
+ if type_ != "WHITESPACE": # Ignore whitespace
58
+ token = BaseToken(type_, match.group())
59
+ self.tokens.append(token)
60
+ position = match.end() # Move past the matched token
61
+ break
62
+ if not match: # No match found, unrecognized token
63
+ raise SyntaxError(f"Unexpected character: {self.script[position]}")
64
+ # break
65
+
66
+ def get_tokens(self):
67
+ if self.is_empty:
68
+ try:
69
+ self.tokenize()
70
+ except SyntaxError as e:
71
+ print(e)
72
+ return []
73
+ return self.tokens
File without changes
@@ -0,0 +1,131 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ import ast
18
+ import operator
19
+
20
+
21
+ class ASTEvaluator:
22
+ """
23
+ Safely evaluates expressions using AST parsing to prevent unsafe operations.
24
+ """
25
+
26
+ def __init__(self):
27
+ self.allowed_operators = {
28
+ ast.Eq: operator.eq,
29
+ ast.NotEq: operator.ne,
30
+ ast.Lt: operator.lt,
31
+ ast.LtE: operator.le,
32
+ ast.Gt: operator.gt,
33
+ ast.GtE: operator.ge,
34
+ # Additional operators can be added here as needed
35
+ }
36
+
37
+ def evaluate(self, expression, context):
38
+ """
39
+ Evaluate a condition expression within a given context using AST parsing.
40
+ """
41
+ try:
42
+ tree = ast.parse(expression, mode="eval")
43
+ return self._evaluate_node(tree.body, context)
44
+ except Exception as e:
45
+ raise ValueError(f"Failed to evaluate expression: {expression}. Error: {e}")
46
+
47
+ def _evaluate_node(self, node, context):
48
+ if isinstance(node, ast.Compare):
49
+ left = self._evaluate_node(node.left, context)
50
+ for operation, comparator in zip(node.ops, node.comparators):
51
+ op_func = self.allowed_operators.get(type(operation))
52
+ if not op_func:
53
+ raise ValueError(
54
+ f"Operation {type(operation).__name__} is not allowed."
55
+ )
56
+ right = self._evaluate_node(comparator, context)
57
+ if not op_func(left, right):
58
+ return False
59
+ return True
60
+ elif isinstance(node, ast.Name):
61
+ return context.get(node.id)
62
+ elif isinstance(node, ast.Constant):
63
+ return node.n
64
+ else:
65
+ raise ValueError(
66
+ "Unsupported AST node type encountered in condition evaluation."
67
+ )
68
+
69
+
70
+ class ASTEvaluationEngine:
71
+ """
72
+ Executes scripts safely using the SafeEvaluator for expression evaluation.
73
+ """
74
+
75
+ def __init__(self):
76
+ self.variables = {}
77
+ self.safe_evaluator = ASTEvaluator()
78
+ self.functions = {
79
+ "processData": self.process_data,
80
+ }
81
+
82
+ def process_data(self, data):
83
+ # Example placeholder function for data processing
84
+ return data * 2
85
+
86
+ def _evaluate_expression(self, expression):
87
+ """
88
+ Evaluates expressions within scripts using SafeEvaluator.
89
+ """
90
+ # Here, 'self.variables' serves as the context for the evaluation
91
+ return self.safe_evaluator.evaluate(expression, self.variables)
92
+
93
+ def _assign_variable(self, var_name, value):
94
+ """
95
+ Assigns a value to a variable within the script's context.
96
+ """
97
+ self.variables[var_name] = value
98
+
99
+ def _execute_function(self, func_name, arg):
100
+ """
101
+ Executes a predefined function with the given argument.
102
+ """
103
+ if func_name in self.functions:
104
+ function = self.functions[func_name]
105
+ return function(arg)
106
+ else:
107
+ raise ValueError(f"Function '{func_name}' is not defined.")
108
+
109
+ def execute(self, script):
110
+ """
111
+ Parses and executes a script, handling variable assignments and function calls.
112
+ """
113
+ tree = ast.parse(script, mode="exec")
114
+ for stmt in tree.body:
115
+ if isinstance(stmt, ast.Assign):
116
+ var_name = stmt.targets[
117
+ 0
118
+ ].id # Assumes single target assignment for simplicity
119
+ # Convert the AST node back to a string for evaluation
120
+ value_expr = ast.unparse(stmt.value)
121
+ value = self._evaluate_expression(value_expr)
122
+ self._assign_variable(var_name, value)
123
+ elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
124
+ func_name = stmt.value.func.id
125
+ arg_expr = ast.unparse(stmt.value.args[0])
126
+ arg = self._evaluate_expression(arg_expr)
127
+ self._execute_function(func_name, arg)
128
+ else:
129
+ raise ValueError(
130
+ "Unsupported statement type encountered in script execution."
131
+ )
@@ -0,0 +1,218 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ import ast
18
+ import operator
19
+ from typing import Any, Dict, Tuple, Callable
20
+
21
+ from lionagi.libs.ln_convert import to_dict
22
+
23
+
24
+ class BaseEvaluator:
25
+ """
26
+ A class to evaluate mathematical and boolean expressions from strings using Python's AST.
27
+
28
+ Attributes:
29
+ allowed_operators (Dict[type, Any]): A dictionary mapping AST node types to their corresponding Python operator functions.
30
+ cache (Dict[Tuple[str, Tuple], Any]): A dictionary used to cache the results of evaluated expressions and sub-expressions.
31
+ """
32
+
33
+ def __init__(self) -> None:
34
+ """Initializes the evaluator with supported operators and an empty cache."""
35
+ self.allowed_operators: Dict[type, Any] = {
36
+ ast.Add: operator.add,
37
+ ast.Sub: operator.sub,
38
+ ast.Mult: operator.mul,
39
+ ast.Div: operator.truediv,
40
+ ast.Pow: operator.pow,
41
+ ast.Mod: operator.mod,
42
+ ast.Eq: operator.eq,
43
+ ast.NotEq: operator.ne,
44
+ ast.Lt: operator.lt,
45
+ ast.LtE: operator.le,
46
+ ast.Gt: operator.gt,
47
+ ast.GtE: operator.ge,
48
+ ast.And: lambda x, y: x and y,
49
+ ast.Or: lambda x, y: x or y,
50
+ ast.Not: operator.not_,
51
+ ast.USub: operator.neg,
52
+ }
53
+ self.cache: Dict[Tuple[str, Tuple], Any] = {}
54
+
55
+ def evaluate(self, expression: str, context: Dict[str, Any]) -> Any:
56
+ """
57
+ Evaluates a given expression string using the provided context.
58
+
59
+ Args:
60
+ expression (str): The expression to evaluate.
61
+ context (Dict[str, Any]): A dictionary mapping variable names to their values.
62
+
63
+ Returns:
64
+ Any: The result of the evaluated expression.
65
+
66
+ Raises:
67
+ ValueError: If the expression cannot be evaluated.
68
+ """
69
+ cache_key = (expression, tuple(sorted(context.items())))
70
+ if cache_key in self.cache:
71
+ return self.cache[cache_key]
72
+
73
+ try:
74
+ tree = ast.parse(expression, mode="eval")
75
+ result = self._evaluate_node(tree.body, context)
76
+ self.cache[cache_key] = result
77
+ return result
78
+ except Exception as e:
79
+ raise ValueError(f"Failed to evaluate expression: {expression}. Error: {e}")
80
+
81
+ def _evaluate_node(self, node: ast.AST, context: Dict[str, Any]) -> Any:
82
+ """Recursively evaluates an AST node."""
83
+ if isinstance(node, ast.BinOp):
84
+ left = self._evaluate_node(node.left, context)
85
+ op_func = self.allowed_operators[type(node.op)]
86
+ right = self._evaluate_node(node.right, context)
87
+ result = op_func(left, right)
88
+ elif isinstance(node, ast.UnaryOp):
89
+ operand = self._evaluate_node(node.operand, context)
90
+ result = self.allowed_operators[type(node.op)](operand)
91
+ elif isinstance(node, ast.Name):
92
+ result = context.get(node.id, None)
93
+ elif isinstance(node, ast.Constant):
94
+ result = node.value
95
+ elif isinstance(node, ast.Compare):
96
+ left = self._evaluate_node(node.left, context)
97
+ result = True
98
+ for operation, comparator in zip(node.ops, node.comparators):
99
+ op_func = self.allowed_operators[type(operation)]
100
+ right = self._evaluate_node(comparator, context)
101
+ result = result and op_func(left, right)
102
+ if not result:
103
+ break
104
+ left = right
105
+ elif isinstance(node, ast.BoolOp):
106
+ values = [self._evaluate_node(value, context) for value in node.values]
107
+ if isinstance(node.op, ast.And):
108
+ result = all(values)
109
+ elif isinstance(node.op, ast.Or):
110
+ result = any(values)
111
+ else:
112
+ raise ValueError("Unsupported boolean operation.")
113
+ else:
114
+ raise ValueError("Unsupported operation in condition.")
115
+ return result
116
+
117
+ def add_custom_operator(self, operator_name, operation_func):
118
+ """Adds a custom operator to the evaluator."""
119
+ custom_node_class = type(operator_name, (ast.AST,), {})
120
+ if custom_node_class not in self.allowed_operators:
121
+ self.allowed_operators[custom_node_class] = operation_func
122
+ else:
123
+ raise ValueError(f"Custom operator '{operator_name}' is already defined.")
124
+
125
+ def evaluate_file(self, file_path, context, format="line"):
126
+ """Evaluates expressions from a file."""
127
+ if format == "line":
128
+ with open(file_path, "r") as file:
129
+ last_result = None
130
+ for line in file:
131
+ line = line.strip()
132
+ if line:
133
+ last_result = self.evaluate(line, context)
134
+ return last_result
135
+ elif format == "json":
136
+ with open(file_path, "r") as file:
137
+ data = to_dict(file)
138
+ last_result = None
139
+ for expression in data:
140
+ last_result = self.evaluate(expression, context)
141
+ return last_result
142
+ else:
143
+ raise ValueError(f"Unsupported file format: {format}")
144
+
145
+ def validate_expression(self, expression):
146
+ """Validates the given expression."""
147
+ try:
148
+ tree = ast.parse(expression, mode="eval")
149
+ self._validate_node(tree.body)
150
+ return True, "Expression is valid."
151
+ except Exception as e:
152
+ return False, f"Invalid expression: {str(e)}"
153
+
154
+ def _validate_node(self, node):
155
+ """Validates an AST node."""
156
+ if isinstance(
157
+ node, (ast.BinOp, ast.Compare, ast.BoolOp, ast.Name, ast.Constant)
158
+ ):
159
+ if (
160
+ isinstance(node, ast.BinOp)
161
+ and type(node.op) not in self.allowed_operators
162
+ ):
163
+ raise ValueError(
164
+ f"Operation {type(node.op).__name__} is not supported."
165
+ )
166
+ else:
167
+ raise ValueError("Unsupported node type in expression.")
168
+
169
+
170
+ class BaseEvaluationEngine:
171
+ def __init__(self) -> None:
172
+ self.variables: Dict[str, Any] = {}
173
+ self.functions: Dict[str, Callable] = {
174
+ "print": print,
175
+ }
176
+
177
+ def _evaluate_expression(self, expression: str) -> Any:
178
+ try:
179
+ return eval(expression, {}, self.variables)
180
+ except NameError as e:
181
+ raise ValueError(f"Undefined variable. {e}")
182
+
183
+ def _assign_variable(self, var_name: str, value: Any) -> None:
184
+ self.variables[var_name] = value
185
+
186
+ def _execute_function(self, func_name: str, *args: Any) -> None:
187
+ if func_name in self.functions:
188
+ self.functions[func_name](*args)
189
+ else:
190
+ raise ValueError(f"Function {func_name} not defined.")
191
+
192
+ def _execute_statement(self, stmt: ast.AST) -> None:
193
+ if isinstance(stmt, ast.Assign):
194
+ var_name = stmt.targets[0].id
195
+ value = self._evaluate_expression(ast.unparse(stmt.value))
196
+ self._assign_variable(var_name, value)
197
+ elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
198
+ func_name = stmt.value.func.id
199
+ args = [
200
+ self._evaluate_expression(ast.unparse(arg)) for arg in stmt.value.args
201
+ ]
202
+ self._execute_function(func_name, *args)
203
+ elif isinstance(stmt, ast.For):
204
+ iter_var = stmt.target.id
205
+ if isinstance(stmt.iter, ast.Call) and stmt.iter.func.id == "range":
206
+ start, end = [
207
+ self._evaluate_expression(ast.unparse(arg))
208
+ for arg in stmt.iter.args
209
+ ]
210
+ for i in range(start, end):
211
+ self.variables[iter_var] = i
212
+ for body_stmt in stmt.body:
213
+ self._execute_statement(body_stmt)
214
+
215
+ def execute(self, script: str) -> None:
216
+ tree = ast.parse(script)
217
+ for stmt in tree.body:
218
+ self._execute_statement(stmt)
File without changes
@@ -0,0 +1,10 @@
1
+ from ..collections.abc import Component, Field
2
+ from ..collections import Pile, pile
3
+ from ..generic import Node
4
+
5
+
6
+ class Knowledge(Component):
7
+
8
+ knowledge_base: Pile[Node] = Field(
9
+ default_factory=pile,
10
+ )
File without changes
File without changes
File without changes
@@ -0,0 +1 @@
1
+ # TODO
File without changes
@@ -0,0 +1,124 @@
1
+ from typing import Dict, Union
2
+
3
+
4
+ def get_ipython_user_proxy():
5
+
6
+ try:
7
+ from lionagi.libs import SysUtil
8
+
9
+ SysUtil.check_import("autogen", pip_name="pyautogen")
10
+
11
+ import autogen
12
+ from IPython import get_ipython
13
+ except Exception as e:
14
+ raise ImportError(f"Please install autogen and IPython. {e}")
15
+
16
+ class IPythonUserProxyAgent(autogen.UserProxyAgent):
17
+
18
+ def __init__(self, name: str, **kwargs):
19
+ super().__init__(name, **kwargs)
20
+ self._ipython = get_ipython()
21
+
22
+ def generate_init_message(self, *args, **kwargs) -> Union[str, Dict]:
23
+ return (
24
+ super().generate_init_message(*args, **kwargs)
25
+ + """If you suggest code, the code will be executed in IPython."""
26
+ )
27
+
28
+ def run_code(self, code, **kwargs):
29
+ result = self._ipython.run_cell("%%capture --no-display cap\n" + code)
30
+ log = self._ipython.ev("cap.stdout")
31
+ log += self._ipython.ev("cap.stderr")
32
+ if result.result is not None:
33
+ log += str(result.result)
34
+ exitcode = 0 if result.success else 1
35
+ if result.error_before_exec is not None:
36
+ log += f"\n{result.error_before_exec}"
37
+ exitcode = 1
38
+ if result.error_in_exec is not None:
39
+ log += f"\n{result.error_in_exec}"
40
+ exitcode = 1
41
+ return exitcode, log, None
42
+
43
+ return IPythonUserProxyAgent
44
+
45
+
46
+ def get_autogen_coder(
47
+ llm_config=None,
48
+ code_execution_config=None,
49
+ kernal="python",
50
+ config_list=None,
51
+ max_consecutive_auto_reply=15,
52
+ temperature=0,
53
+ cache_seed=42,
54
+ env_="local",
55
+ assistant_instruction=None,
56
+ ):
57
+ assistant = ""
58
+ try:
59
+ from lionagi.libs import SysUtil
60
+
61
+ SysUtil.check_import("autogen", pip_name="pyautogen")
62
+
63
+ import autogen
64
+ from autogen.agentchat.contrib.gpt_assistant_agent import GPTAssistantAgent
65
+ except Exception as e:
66
+ raise ImportError(f"Please install autogen. {e}")
67
+
68
+ if env_ == "local":
69
+ assistant = autogen.AssistantAgent(
70
+ name="assistant",
71
+ llm_config=llm_config
72
+ or {
73
+ "cache_seed": cache_seed,
74
+ "config_list": config_list,
75
+ "temperature": temperature,
76
+ },
77
+ )
78
+
79
+ elif env_ == "oai_assistant":
80
+ assistant = GPTAssistantAgent(
81
+ name="Coder Assistant",
82
+ llm_config={
83
+ "tools": [{"type": "code_interpreter"}],
84
+ "config_list": config_list,
85
+ },
86
+ instructions=assistant_instruction,
87
+ )
88
+
89
+ if kernal == "python":
90
+ user_proxy = autogen.UserProxyAgent(
91
+ name="user_proxy",
92
+ human_input_mode="NEVER",
93
+ max_consecutive_auto_reply=max_consecutive_auto_reply,
94
+ is_termination_msg=lambda x: x.get("content", "")
95
+ .rstrip()
96
+ .endswith("TERMINATE"),
97
+ code_execution_config=code_execution_config
98
+ or {
99
+ "work_dir": "coding",
100
+ "use_docker": False,
101
+ },
102
+ )
103
+ return user_proxy, assistant
104
+
105
+ elif kernal == "ipython":
106
+ user_proxy = get_ipython_user_proxy(
107
+ "ipython_user_proxy",
108
+ human_input_mode="NEVER",
109
+ max_consecutive_auto_reply=max_consecutive_auto_reply,
110
+ is_termination_msg=lambda x: x.get("content", "")
111
+ .rstrip()
112
+ .endswith("TERMINATE")
113
+ or x.get("content", "").rstrip().endswith('"TERMINATE".'),
114
+ )
115
+ return user_proxy, assistant
116
+
117
+ # # Sample Usage Pattern
118
+ # context = "def my_function():\n pass\n"
119
+ # task1 = "I need help with the following code:\n"
120
+ # task2 = "Please write a function that returns the sum of two numbers."
121
+
122
+ # user_proxy, assistant = get_autogen_coder()
123
+ # user_proxy.initiate_chat(assistant, message=task1+context)
124
+ # user_proxy.send(recipient=assistant, message=task2)