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.
- lionagi/__init__.py +61 -3
- lionagi/core/__init__.py +0 -14
- lionagi/core/_setting/_setting.py +59 -0
- lionagi/core/action/__init__.py +14 -0
- lionagi/core/action/function_calling.py +136 -0
- lionagi/core/action/manual.py +1 -0
- lionagi/core/action/node.py +109 -0
- lionagi/core/action/tool.py +114 -0
- lionagi/core/action/tool_manager.py +356 -0
- lionagi/core/agent/__init__.py +0 -3
- lionagi/core/agent/base_agent.py +45 -36
- lionagi/core/agent/eval/evaluator.py +1 -0
- lionagi/core/agent/eval/vote.py +40 -0
- lionagi/core/agent/learn/learner.py +59 -0
- lionagi/core/agent/plan/unit_template.py +1 -0
- lionagi/core/collections/__init__.py +17 -0
- lionagi/core/collections/_logger.py +319 -0
- lionagi/core/collections/abc/__init__.py +53 -0
- lionagi/core/collections/abc/component.py +615 -0
- lionagi/core/collections/abc/concepts.py +297 -0
- lionagi/core/collections/abc/exceptions.py +150 -0
- lionagi/core/collections/abc/util.py +45 -0
- lionagi/core/collections/exchange.py +161 -0
- lionagi/core/collections/flow.py +426 -0
- lionagi/core/collections/model.py +419 -0
- lionagi/core/collections/pile.py +913 -0
- lionagi/core/collections/progression.py +236 -0
- lionagi/core/collections/util.py +64 -0
- lionagi/core/director/direct.py +314 -0
- lionagi/core/director/director.py +2 -0
- lionagi/core/engine/branch_engine.py +333 -0
- lionagi/core/engine/instruction_map_engine.py +204 -0
- lionagi/core/engine/sandbox_.py +14 -0
- lionagi/core/engine/script_engine.py +99 -0
- lionagi/core/executor/base_executor.py +90 -0
- lionagi/core/executor/graph_executor.py +330 -0
- lionagi/core/executor/neo4j_executor.py +384 -0
- lionagi/core/generic/__init__.py +7 -0
- lionagi/core/generic/edge.py +112 -0
- lionagi/core/generic/edge_condition.py +16 -0
- lionagi/core/generic/graph.py +236 -0
- lionagi/core/generic/hyperedge.py +1 -0
- lionagi/core/generic/node.py +220 -0
- lionagi/core/generic/tree.py +48 -0
- lionagi/core/generic/tree_node.py +79 -0
- lionagi/core/mail/__init__.py +7 -3
- lionagi/core/mail/mail.py +25 -0
- lionagi/core/mail/mail_manager.py +142 -58
- lionagi/core/mail/package.py +45 -0
- lionagi/core/mail/start_mail.py +36 -0
- lionagi/core/message/__init__.py +19 -0
- lionagi/core/message/action_request.py +133 -0
- lionagi/core/message/action_response.py +135 -0
- lionagi/core/message/assistant_response.py +95 -0
- lionagi/core/message/instruction.py +234 -0
- lionagi/core/message/message.py +101 -0
- lionagi/core/message/system.py +86 -0
- lionagi/core/message/util.py +283 -0
- lionagi/core/report/__init__.py +4 -0
- lionagi/core/report/base.py +217 -0
- lionagi/core/report/form.py +231 -0
- lionagi/core/report/report.py +166 -0
- lionagi/core/report/util.py +28 -0
- lionagi/core/rule/__init__.py +0 -0
- lionagi/core/rule/_default.py +16 -0
- lionagi/core/rule/action.py +99 -0
- lionagi/core/rule/base.py +238 -0
- lionagi/core/rule/boolean.py +56 -0
- lionagi/core/rule/choice.py +47 -0
- lionagi/core/rule/mapping.py +96 -0
- lionagi/core/rule/number.py +71 -0
- lionagi/core/rule/rulebook.py +109 -0
- lionagi/core/rule/string.py +52 -0
- lionagi/core/rule/util.py +35 -0
- lionagi/core/session/__init__.py +0 -3
- lionagi/core/session/branch.py +431 -0
- lionagi/core/session/directive_mixin.py +287 -0
- lionagi/core/session/session.py +230 -902
- lionagi/core/structure/__init__.py +1 -0
- lionagi/core/structure/chain.py +1 -0
- lionagi/core/structure/forest.py +1 -0
- lionagi/core/structure/graph.py +1 -0
- lionagi/core/structure/tree.py +1 -0
- lionagi/core/unit/__init__.py +5 -0
- lionagi/core/unit/parallel_unit.py +245 -0
- lionagi/core/unit/template/__init__.py +0 -0
- lionagi/core/unit/template/action.py +81 -0
- lionagi/core/unit/template/base.py +51 -0
- lionagi/core/unit/template/plan.py +84 -0
- lionagi/core/unit/template/predict.py +109 -0
- lionagi/core/unit/template/score.py +124 -0
- lionagi/core/unit/template/select.py +104 -0
- lionagi/core/unit/unit.py +362 -0
- lionagi/core/unit/unit_form.py +305 -0
- lionagi/core/unit/unit_mixin.py +1168 -0
- lionagi/core/unit/util.py +71 -0
- lionagi/core/validator/__init__.py +0 -0
- lionagi/core/validator/validator.py +364 -0
- lionagi/core/work/__init__.py +0 -0
- lionagi/core/work/work.py +76 -0
- lionagi/core/work/work_function.py +101 -0
- lionagi/core/work/work_queue.py +103 -0
- lionagi/core/work/worker.py +258 -0
- lionagi/core/work/worklog.py +120 -0
- lionagi/experimental/__init__.py +0 -0
- lionagi/experimental/compressor/__init__.py +0 -0
- lionagi/experimental/compressor/base.py +46 -0
- lionagi/experimental/compressor/llm_compressor.py +247 -0
- lionagi/experimental/compressor/llm_summarizer.py +61 -0
- lionagi/experimental/compressor/util.py +70 -0
- lionagi/experimental/directive/__init__.py +19 -0
- lionagi/experimental/directive/parser/__init__.py +0 -0
- lionagi/experimental/directive/parser/base_parser.py +282 -0
- lionagi/experimental/directive/template/__init__.py +0 -0
- lionagi/experimental/directive/template/base_template.py +79 -0
- lionagi/experimental/directive/template/schema.py +36 -0
- lionagi/experimental/directive/tokenizer.py +73 -0
- lionagi/experimental/evaluator/__init__.py +0 -0
- lionagi/experimental/evaluator/ast_evaluator.py +131 -0
- lionagi/experimental/evaluator/base_evaluator.py +218 -0
- lionagi/experimental/knowledge/__init__.py +0 -0
- lionagi/experimental/knowledge/base.py +10 -0
- lionagi/experimental/knowledge/graph.py +0 -0
- lionagi/experimental/memory/__init__.py +0 -0
- lionagi/experimental/strategies/__init__.py +0 -0
- lionagi/experimental/strategies/base.py +1 -0
- lionagi/integrations/bridge/autogen_/__init__.py +0 -0
- lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
- lionagi/integrations/bridge/langchain_/documents.py +4 -0
- lionagi/integrations/bridge/llamaindex_/index.py +30 -0
- lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
- lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
- lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
- lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
- lionagi/integrations/bridge/transformers_/__init__.py +0 -0
- lionagi/integrations/bridge/transformers_/install_.py +36 -0
- lionagi/integrations/chunker/__init__.py +0 -0
- lionagi/integrations/chunker/chunk.py +312 -0
- lionagi/integrations/config/oai_configs.py +38 -7
- lionagi/integrations/config/ollama_configs.py +1 -1
- lionagi/integrations/config/openrouter_configs.py +14 -2
- lionagi/integrations/loader/__init__.py +0 -0
- lionagi/integrations/loader/load.py +253 -0
- lionagi/integrations/loader/load_util.py +195 -0
- lionagi/integrations/provider/_mapping.py +46 -0
- lionagi/integrations/provider/litellm.py +2 -1
- lionagi/integrations/provider/mlx_service.py +16 -9
- lionagi/integrations/provider/oai.py +91 -4
- lionagi/integrations/provider/ollama.py +7 -6
- lionagi/integrations/provider/openrouter.py +115 -8
- lionagi/integrations/provider/services.py +2 -2
- lionagi/integrations/provider/transformers.py +18 -22
- lionagi/integrations/storage/__init__.py +3 -0
- lionagi/integrations/storage/neo4j.py +665 -0
- lionagi/integrations/storage/storage_util.py +287 -0
- lionagi/integrations/storage/structure_excel.py +285 -0
- lionagi/integrations/storage/to_csv.py +63 -0
- lionagi/integrations/storage/to_excel.py +83 -0
- lionagi/libs/__init__.py +26 -1
- lionagi/libs/ln_api.py +78 -23
- lionagi/libs/ln_context.py +37 -0
- lionagi/libs/ln_convert.py +21 -9
- lionagi/libs/ln_func_call.py +69 -28
- lionagi/libs/ln_image.py +107 -0
- lionagi/libs/ln_knowledge_graph.py +405 -0
- lionagi/libs/ln_nested.py +26 -11
- lionagi/libs/ln_parse.py +110 -14
- lionagi/libs/ln_queue.py +117 -0
- lionagi/libs/ln_tokenize.py +164 -0
- lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
- lionagi/libs/special_tokens.py +172 -0
- lionagi/libs/sys_util.py +107 -2
- lionagi/lions/__init__.py +0 -0
- lionagi/lions/coder/__init__.py +0 -0
- lionagi/lions/coder/add_feature.py +20 -0
- lionagi/lions/coder/base_prompts.py +22 -0
- lionagi/lions/coder/code_form.py +13 -0
- lionagi/lions/coder/coder.py +168 -0
- lionagi/lions/coder/util.py +96 -0
- lionagi/lions/researcher/__init__.py +0 -0
- lionagi/lions/researcher/data_source/__init__.py +0 -0
- lionagi/lions/researcher/data_source/finhub_.py +191 -0
- lionagi/lions/researcher/data_source/google_.py +199 -0
- lionagi/lions/researcher/data_source/wiki_.py +96 -0
- lionagi/lions/researcher/data_source/yfinance_.py +21 -0
- lionagi/tests/integrations/__init__.py +0 -0
- lionagi/tests/libs/__init__.py +0 -0
- lionagi/tests/libs/test_field_validators.py +353 -0
- lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
- lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
- lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
- lionagi/tests/libs/test_queue.py +67 -0
- lionagi/tests/test_core/collections/__init__.py +0 -0
- lionagi/tests/test_core/collections/test_component.py +206 -0
- lionagi/tests/test_core/collections/test_exchange.py +138 -0
- lionagi/tests/test_core/collections/test_flow.py +145 -0
- lionagi/tests/test_core/collections/test_pile.py +171 -0
- lionagi/tests/test_core/collections/test_progression.py +129 -0
- lionagi/tests/test_core/generic/__init__.py +0 -0
- lionagi/tests/test_core/generic/test_edge.py +67 -0
- lionagi/tests/test_core/generic/test_graph.py +96 -0
- lionagi/tests/test_core/generic/test_node.py +106 -0
- lionagi/tests/test_core/generic/test_tree_node.py +73 -0
- lionagi/tests/test_core/test_branch.py +115 -292
- lionagi/tests/test_core/test_form.py +46 -0
- lionagi/tests/test_core/test_report.py +105 -0
- lionagi/tests/test_core/test_validator.py +111 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
- lionagi-0.2.1.dist-info/RECORD +240 -0
- lionagi/core/branch/__init__.py +0 -4
- lionagi/core/branch/base_branch.py +0 -654
- lionagi/core/branch/branch.py +0 -471
- lionagi/core/branch/branch_flow_mixin.py +0 -96
- lionagi/core/branch/executable_branch.py +0 -347
- lionagi/core/branch/util.py +0 -323
- lionagi/core/direct/__init__.py +0 -6
- lionagi/core/direct/predict.py +0 -161
- lionagi/core/direct/score.py +0 -278
- lionagi/core/direct/select.py +0 -169
- lionagi/core/direct/utils.py +0 -87
- lionagi/core/direct/vote.py +0 -64
- lionagi/core/flow/base/baseflow.py +0 -23
- lionagi/core/flow/monoflow/ReAct.py +0 -238
- lionagi/core/flow/monoflow/__init__.py +0 -9
- lionagi/core/flow/monoflow/chat.py +0 -95
- lionagi/core/flow/monoflow/chat_mixin.py +0 -263
- lionagi/core/flow/monoflow/followup.py +0 -214
- lionagi/core/flow/polyflow/__init__.py +0 -1
- lionagi/core/flow/polyflow/chat.py +0 -248
- lionagi/core/mail/schema.py +0 -56
- lionagi/core/messages/__init__.py +0 -3
- lionagi/core/messages/schema.py +0 -533
- lionagi/core/prompt/prompt_template.py +0 -316
- lionagi/core/schema/__init__.py +0 -22
- lionagi/core/schema/action_node.py +0 -29
- lionagi/core/schema/base_mixin.py +0 -296
- lionagi/core/schema/base_node.py +0 -199
- lionagi/core/schema/condition.py +0 -24
- lionagi/core/schema/data_logger.py +0 -354
- lionagi/core/schema/data_node.py +0 -93
- lionagi/core/schema/prompt_template.py +0 -67
- lionagi/core/schema/structure.py +0 -910
- lionagi/core/tool/__init__.py +0 -3
- lionagi/core/tool/tool_manager.py +0 -280
- lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
- lionagi/tests/test_core/test_base_branch.py +0 -427
- lionagi/tests/test_core/test_chat_flow.py +0 -63
- lionagi/tests/test_core/test_mail_manager.py +0 -75
- lionagi/tests/test_core/test_prompts.py +0 -51
- lionagi/tests/test_core/test_session.py +0 -254
- lionagi/tests/test_core/test_session_base_util.py +0 -312
- lionagi/tests/test_core/test_tool_manager.py +0 -95
- lionagi-0.0.312.dist-info/RECORD +0 -111
- /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
- /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
- /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
- /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
- /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
- /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
- /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
- /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
- /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
- {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
|
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)
|