lionagi 0.1.2__py3-none-any.whl → 0.2.0__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 +60 -5
- lionagi/core/__init__.py +0 -25
- 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/base_agent.py +27 -13
- 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/{generic/data_logger.py → collections/_logger.py} +69 -55
- 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/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
- lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
- lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
- lionagi/core/executor/base_executor.py +90 -0
- lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +62 -66
- lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
- lionagi/core/generic/__init__.py +3 -33
- lionagi/core/generic/edge.py +29 -79
- 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 +156 -221
- lionagi/core/generic/tree.py +48 -0
- lionagi/core/generic/tree_node.py +79 -0
- lionagi/core/mail/__init__.py +12 -0
- lionagi/core/mail/mail.py +25 -0
- lionagi/core/mail/mail_manager.py +139 -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/_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/branch.py +431 -0
- lionagi/core/session/directive_mixin.py +287 -0
- lionagi/core/session/session.py +229 -903
- 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/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/validator.py +364 -0
- lionagi/core/work/work.py +74 -0
- lionagi/core/work/work_function.py +92 -0
- lionagi/core/work/work_queue.py +81 -0
- lionagi/core/work/worker.py +195 -0
- lionagi/core/work/worklog.py +124 -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/base_parser.py +69 -2
- lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
- lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
- lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
- lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
- lionagi/experimental/knowledge/base.py +10 -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/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/chunker/chunk.py +161 -24
- lionagi/integrations/config/oai_configs.py +34 -3
- lionagi/integrations/config/openrouter_configs.py +14 -2
- lionagi/integrations/loader/load.py +122 -21
- lionagi/integrations/loader/load_util.py +6 -77
- 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 +6 -5
- 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 -3
- lionagi/integrations/storage/neo4j.py +52 -60
- lionagi/integrations/storage/storage_util.py +44 -46
- lionagi/integrations/storage/structure_excel.py +43 -26
- lionagi/integrations/storage/to_excel.py +11 -4
- lionagi/libs/__init__.py +22 -1
- lionagi/libs/ln_api.py +75 -20
- 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_nested.py +26 -11
- lionagi/libs/ln_parse.py +82 -23
- lionagi/libs/ln_queue.py +16 -0
- lionagi/libs/ln_tokenize.py +164 -0
- lionagi/libs/ln_validate.py +16 -0
- lionagi/libs/special_tokens.py +172 -0
- lionagi/libs/sys_util.py +95 -24
- lionagi/lions/coder/code_form.py +13 -0
- lionagi/lions/coder/coder.py +50 -3
- lionagi/lions/coder/util.py +30 -25
- lionagi/tests/libs/test_func_call.py +23 -21
- lionagi/tests/libs/test_nested.py +36 -21
- lionagi/tests/libs/test_parse.py +1 -1
- 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/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 -294
- 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.2.0.dist-info/LICENSE +202 -0
- lionagi-0.2.0.dist-info/METADATA +272 -0
- lionagi-0.2.0.dist-info/RECORD +240 -0
- lionagi/core/branch/base.py +0 -653
- lionagi/core/branch/branch.py +0 -474
- lionagi/core/branch/flow_mixin.py +0 -96
- lionagi/core/branch/util.py +0 -323
- lionagi/core/direct/__init__.py +0 -19
- lionagi/core/direct/cot.py +0 -123
- lionagi/core/direct/plan.py +0 -164
- lionagi/core/direct/predict.py +0 -166
- lionagi/core/direct/react.py +0 -171
- lionagi/core/direct/score.py +0 -279
- lionagi/core/direct/select.py +0 -170
- lionagi/core/direct/sentiment.py +0 -1
- lionagi/core/direct/utils.py +0 -110
- lionagi/core/direct/vote.py +0 -64
- lionagi/core/execute/base_executor.py +0 -47
- lionagi/core/flow/baseflow.py +0 -23
- lionagi/core/flow/monoflow/ReAct.py +0 -240
- lionagi/core/flow/monoflow/__init__.py +0 -9
- lionagi/core/flow/monoflow/chat.py +0 -95
- lionagi/core/flow/monoflow/chat_mixin.py +0 -253
- lionagi/core/flow/monoflow/followup.py +0 -215
- lionagi/core/flow/polyflow/__init__.py +0 -1
- lionagi/core/flow/polyflow/chat.py +0 -251
- lionagi/core/form/action_form.py +0 -26
- lionagi/core/form/field_validator.py +0 -287
- lionagi/core/form/form.py +0 -302
- lionagi/core/form/mixin.py +0 -214
- lionagi/core/form/scored_form.py +0 -13
- lionagi/core/generic/action.py +0 -26
- lionagi/core/generic/component.py +0 -532
- lionagi/core/generic/condition.py +0 -46
- lionagi/core/generic/mail.py +0 -90
- lionagi/core/generic/mailbox.py +0 -36
- lionagi/core/generic/relation.py +0 -70
- lionagi/core/generic/signal.py +0 -22
- lionagi/core/generic/structure.py +0 -362
- lionagi/core/generic/transfer.py +0 -20
- lionagi/core/generic/work.py +0 -40
- lionagi/core/graph/graph.py +0 -126
- lionagi/core/graph/tree.py +0 -190
- lionagi/core/mail/schema.py +0 -63
- lionagi/core/messages/schema.py +0 -325
- lionagi/core/tool/__init__.py +0 -5
- lionagi/core/tool/tool.py +0 -28
- lionagi/core/tool/tool_manager.py +0 -283
- lionagi/experimental/report/form.py +0 -64
- lionagi/experimental/report/report.py +0 -138
- lionagi/experimental/report/util.py +0 -47
- lionagi/experimental/tool/function_calling.py +0 -43
- lionagi/experimental/tool/manual.py +0 -66
- lionagi/experimental/tool/schema.py +0 -59
- lionagi/experimental/tool/tool_manager.py +0 -138
- lionagi/experimental/tool/util.py +0 -16
- lionagi/experimental/validator/rule.py +0 -139
- lionagi/experimental/validator/validator.py +0 -56
- lionagi/experimental/work/__init__.py +0 -10
- lionagi/experimental/work/async_queue.py +0 -54
- lionagi/experimental/work/schema.py +0 -73
- lionagi/experimental/work/work_function.py +0 -67
- lionagi/experimental/work/worker.py +0 -56
- lionagi/experimental/work2/form.py +0 -371
- lionagi/experimental/work2/report.py +0 -289
- lionagi/experimental/work2/schema.py +0 -30
- lionagi/experimental/work2/tests.py +0 -72
- lionagi/experimental/work2/work_function.py +0 -89
- lionagi/experimental/work2/worker.py +0 -12
- lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
- lionagi/tests/test_core/generic/test_component.py +0 -89
- lionagi/tests/test_core/test_base_branch.py +0 -426
- 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 -313
- lionagi/tests/test_core/test_tool_manager.py +0 -95
- lionagi-0.1.2.dist-info/LICENSE +0 -9
- lionagi-0.1.2.dist-info/METADATA +0 -174
- lionagi-0.1.2.dist-info/RECORD +0 -206
- /lionagi/core/{branch → _setting}/__init__.py +0 -0
- /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
- /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
- /lionagi/core/{form → agent/plan}/__init__.py +0 -0
- /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
- /lionagi/core/{graph → director}/__init__.py +0 -0
- /lionagi/core/{messages → engine}/__init__.py +0 -0
- /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
- /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
- /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
- /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
- /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
- /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
- /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
- /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
- /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
- /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
- /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
- /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
- {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/WHEEL +0 -0
- {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from pydantic import Field
|
5
|
+
|
6
|
+
from lionagi.core.collections.abc import Element, Progressable, Executable
|
7
|
+
from lionagi.core.collections import Exchange
|
8
|
+
from lionagi.core.mail.mail import Mail, Package
|
9
|
+
|
10
|
+
|
11
|
+
class BaseExecutor(Element, Progressable, Executable, ABC):
|
12
|
+
"""
|
13
|
+
BaseExecutor is an abstract base class that defines the structure for executors
|
14
|
+
handling mails, execution control, and context management within the LionAGI system.
|
15
|
+
"""
|
16
|
+
|
17
|
+
mailbox: Exchange = Field(
|
18
|
+
default_factory=Exchange[Mail], description="The pending mails."
|
19
|
+
)
|
20
|
+
|
21
|
+
execute_stop: bool = Field(
|
22
|
+
False, description="A flag indicating whether to stop execution."
|
23
|
+
)
|
24
|
+
|
25
|
+
context: dict | str | list | None = Field(
|
26
|
+
None, description="The context buffer for the next instruction."
|
27
|
+
)
|
28
|
+
|
29
|
+
execution_responses: list = Field(
|
30
|
+
default_factory=list, description="The list of responses."
|
31
|
+
)
|
32
|
+
|
33
|
+
context_log: list = Field(default_factory=list, description="The context log.")
|
34
|
+
|
35
|
+
verbose: bool = Field(
|
36
|
+
True, description="A flag indicating whether to provide verbose output."
|
37
|
+
)
|
38
|
+
|
39
|
+
def send(
|
40
|
+
self, recipient: str, category: str, package: Any, request_source: str = None
|
41
|
+
) -> None:
|
42
|
+
"""
|
43
|
+
Sends a mail to a recipient.
|
44
|
+
|
45
|
+
Args:
|
46
|
+
recipient (str): The ID of the recipient.
|
47
|
+
category (str): The category of the mail.
|
48
|
+
package (Any): The package to send in the mail.
|
49
|
+
request_source (str): The source of the request.
|
50
|
+
"""
|
51
|
+
pack = Package(
|
52
|
+
category=category, package=package, request_source=request_source
|
53
|
+
)
|
54
|
+
mail = Mail(
|
55
|
+
sender=self.ln_id,
|
56
|
+
recipient=recipient,
|
57
|
+
package=pack,
|
58
|
+
)
|
59
|
+
self.mailbox.include(mail, "out")
|
60
|
+
|
61
|
+
@abstractmethod
|
62
|
+
async def execute(self, *args: Any, **kwargs: Any) -> Any:
|
63
|
+
"""
|
64
|
+
Execute the executor's main function.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
*args (Any): Positional arguments.
|
68
|
+
**kwargs (Any): Keyword arguments.
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
Any: The result of the execution.
|
72
|
+
|
73
|
+
Raises:
|
74
|
+
NotImplementedError: If the method is not implemented.
|
75
|
+
"""
|
76
|
+
raise NotImplementedError("The execute method must be implemented.")
|
77
|
+
|
78
|
+
@abstractmethod
|
79
|
+
async def forward(self, *args: Any, **kwargs: Any) -> None:
|
80
|
+
"""
|
81
|
+
Forward the execution flow.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
*args (Any): Positional arguments.
|
85
|
+
**kwargs (Any): Keyword arguments.
|
86
|
+
|
87
|
+
Raises:
|
88
|
+
NotImplementedError: If the method is not implemented.
|
89
|
+
"""
|
90
|
+
raise NotImplementedError("The forward method must be implemented.")
|
@@ -1,24 +1,19 @@
|
|
1
|
-
from typing import overload
|
2
|
-
|
3
|
-
from abc import ABC
|
4
1
|
from collections import deque
|
5
2
|
|
6
3
|
from lionagi.libs import AsyncUtil, convert
|
7
4
|
|
8
|
-
from lionagi.core.generic import
|
9
|
-
from lionagi.core.
|
10
|
-
from lionagi.core.
|
11
|
-
from lionagi.core.execute.base_executor import BaseExecutor
|
5
|
+
from lionagi.core.generic.node import Node
|
6
|
+
from lionagi.core.generic.edge import Edge
|
7
|
+
from lionagi.core.executor.base_executor import BaseExecutor
|
12
8
|
|
13
|
-
from lionagi.
|
14
|
-
from lionagi.core.generic import Node, ActionSelection, Edge
|
15
|
-
from lionagi.core.tool import Tool
|
9
|
+
from lionagi.core.action import Tool, DirectiveSelection, ActionNode
|
16
10
|
|
17
|
-
from lionagi.core.mail
|
18
|
-
from lionagi.core.
|
11
|
+
from lionagi.core.mail import Mail
|
12
|
+
from lionagi.core.generic.graph import Graph
|
13
|
+
from lionagi.core.collections.progression import progression
|
19
14
|
|
20
15
|
|
21
|
-
class
|
16
|
+
class GraphExecutor(BaseExecutor, Graph):
|
22
17
|
"""
|
23
18
|
Executes tasks within a graph structure, handling dynamic node flows and conditional edge logic.
|
24
19
|
|
@@ -45,10 +40,12 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
45
40
|
Raises:
|
46
41
|
ValueError: If the source_type of the condition is invalid.
|
47
42
|
"""
|
48
|
-
if edge.condition.
|
49
|
-
|
43
|
+
if edge.condition.source == "structure" or isinstance(
|
44
|
+
edge.condition.source, Node
|
45
|
+
):
|
46
|
+
return await edge.check_condition(self)
|
50
47
|
|
51
|
-
elif edge.condition.
|
48
|
+
elif edge.condition.source == "executable":
|
52
49
|
return await self._check_executable_condition(
|
53
50
|
edge, executable_id, request_source
|
54
51
|
)
|
@@ -63,20 +60,20 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
63
60
|
Args:
|
64
61
|
edge_id (str): The ID of the edge.
|
65
62
|
"""
|
66
|
-
for key in list(self.pending_ins.keys()):
|
67
|
-
skipped_requests =
|
68
|
-
while self.pending_ins[key]:
|
69
|
-
|
63
|
+
for key in list(self.mailbox.pending_ins.keys()):
|
64
|
+
skipped_requests = progression()
|
65
|
+
while self.mailbox.pending_ins[key].size() > 0:
|
66
|
+
mail_id = self.mailbox.pending_ins[key].popleft()
|
67
|
+
mail = self.mailbox.pile[mail_id]
|
70
68
|
if (
|
71
69
|
mail.category == "condition"
|
72
|
-
and mail.package
|
70
|
+
and mail.package.package["edge_id"] == edge_id
|
73
71
|
):
|
74
|
-
self.
|
75
|
-
|
76
|
-
]
|
72
|
+
self.mailbox.pile.pop(mail_id)
|
73
|
+
self.condition_check_result = mail.package.package["check_result"]
|
77
74
|
else:
|
78
75
|
skipped_requests.append(mail)
|
79
|
-
self.pending_ins[key] = skipped_requests
|
76
|
+
self.mailbox.pending_ins[key] = skipped_requests
|
80
77
|
|
81
78
|
async def _check_executable_condition(
|
82
79
|
self, edge: Edge, executable_id, request_source
|
@@ -93,19 +90,20 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
93
90
|
bool: The result of the condition check.
|
94
91
|
"""
|
95
92
|
self.send(
|
96
|
-
|
93
|
+
recipient=executable_id,
|
97
94
|
category="condition",
|
98
|
-
package=
|
95
|
+
package=edge,
|
96
|
+
request_source=request_source,
|
99
97
|
)
|
100
98
|
while self.condition_check_result is None:
|
101
99
|
await AsyncUtil.sleep(0.1)
|
102
|
-
self._process_edge_condition(edge.
|
100
|
+
self._process_edge_condition(edge.ln_id)
|
103
101
|
continue
|
104
102
|
check_result = self.condition_check_result
|
105
103
|
self.condition_check_result = None
|
106
104
|
return check_result
|
107
105
|
|
108
|
-
async def _handle_node_id(self, mail:
|
106
|
+
async def _handle_node_id(self, mail: Mail):
|
109
107
|
"""
|
110
108
|
Processes the node identified by its ID in the mail's package, ensuring it exists and retrieving the next set of
|
111
109
|
nodes based on the current node.
|
@@ -116,17 +114,17 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
116
114
|
Raises:
|
117
115
|
ValueError: If the node does not exist within the structure.
|
118
116
|
"""
|
119
|
-
if mail.package
|
117
|
+
if mail.package.package not in self.internal_nodes:
|
120
118
|
raise ValueError(
|
121
|
-
f"Node {mail.package} does not exist in the structure {self.
|
119
|
+
f"Node {mail.package.package}: Node does not exist in the structure {self.ln_id}"
|
122
120
|
)
|
123
121
|
return await self._next_node(
|
124
|
-
self.internal_nodes[mail.package
|
125
|
-
mail.
|
126
|
-
mail.package
|
122
|
+
self.internal_nodes[mail.package.package],
|
123
|
+
mail.sender,
|
124
|
+
mail.package.request_source,
|
127
125
|
)
|
128
126
|
|
129
|
-
async def _handle_node(self, mail:
|
127
|
+
async def _handle_node(self, mail: Mail):
|
130
128
|
"""
|
131
129
|
Processes the node specified in the mail's package, ensuring it exists within the structure.
|
132
130
|
|
@@ -136,15 +134,15 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
136
134
|
Raises:
|
137
135
|
ValueError: If the node does not exist within the structure.
|
138
136
|
"""
|
139
|
-
if not self.node_exist(mail.package
|
137
|
+
if not self.node_exist(mail.package.package):
|
140
138
|
raise ValueError(
|
141
|
-
f"Node {mail.package} does not exist in the structure {self.
|
139
|
+
f"Node {mail.package.package.ln_id}: does not exist in the structure {self.ln_id}"
|
142
140
|
)
|
143
141
|
return await self._next_node(
|
144
|
-
mail.package
|
142
|
+
mail.package.package, mail.sender, mail.package.request_source
|
145
143
|
)
|
146
144
|
|
147
|
-
async def _handle_mail(self, mail:
|
145
|
+
async def _handle_mail(self, mail: Mail):
|
148
146
|
"""
|
149
147
|
Processes incoming mail based on its category, initiating node execution or structure operations accordingly.
|
150
148
|
|
@@ -165,9 +163,9 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
165
163
|
try:
|
166
164
|
return await self._handle_node_id(mail)
|
167
165
|
except Exception as e:
|
168
|
-
raise ValueError(f"Error handling
|
166
|
+
raise ValueError(f"Error handling node_id: {e}") from e
|
169
167
|
|
170
|
-
elif mail.category == "node" and isinstance(mail.package
|
168
|
+
elif mail.category == "node" and isinstance(mail.package.package, Node):
|
171
169
|
try:
|
172
170
|
return await self._handle_node(mail)
|
173
171
|
except Exception as e:
|
@@ -188,7 +186,7 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
188
186
|
list[Node]: The next step nodes.
|
189
187
|
"""
|
190
188
|
next_nodes = []
|
191
|
-
next_edges = self.get_node_edges(current_node,
|
189
|
+
next_edges = self.get_node_edges(current_node, direction="out")
|
192
190
|
for edge in convert.to_list(list(next_edges.values())):
|
193
191
|
if edge.bundle:
|
194
192
|
continue
|
@@ -199,7 +197,7 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
199
197
|
if not check:
|
200
198
|
continue
|
201
199
|
node = self.internal_nodes[edge.tail]
|
202
|
-
further_edges = self.get_node_edges(node,
|
200
|
+
further_edges = self.get_node_edges(node, direction="out")
|
203
201
|
bundled_nodes = deque()
|
204
202
|
for f_edge in convert.to_list(list(further_edges.values())):
|
205
203
|
if f_edge.bundle:
|
@@ -209,7 +207,7 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
209
207
|
next_nodes.append(node)
|
210
208
|
return next_nodes
|
211
209
|
|
212
|
-
def _send_mail(self, next_nodes: list | None, mail:
|
210
|
+
def _send_mail(self, next_nodes: list | None, mail: Mail):
|
213
211
|
"""
|
214
212
|
Sends mails to the next nodes or signals the end of execution if no next nodes exist.
|
215
213
|
|
@@ -219,31 +217,25 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
219
217
|
"""
|
220
218
|
if not next_nodes: # tail
|
221
219
|
self.send(
|
222
|
-
|
220
|
+
recipient=mail.sender,
|
223
221
|
category="end",
|
224
|
-
package=
|
225
|
-
|
226
|
-
"package": "end",
|
227
|
-
},
|
222
|
+
package="end",
|
223
|
+
request_source=mail.package.request_source,
|
228
224
|
)
|
229
225
|
else:
|
230
226
|
if len(next_nodes) == 1:
|
231
227
|
self.send(
|
232
|
-
|
228
|
+
recipient=mail.sender,
|
233
229
|
category="node",
|
234
|
-
package=
|
235
|
-
|
236
|
-
"package": next_nodes[0],
|
237
|
-
},
|
230
|
+
package=next_nodes[0],
|
231
|
+
request_source=mail.package.request_source,
|
238
232
|
)
|
239
233
|
else:
|
240
234
|
self.send(
|
241
|
-
|
235
|
+
recipient=mail.sender,
|
242
236
|
category="node_list",
|
243
|
-
package=
|
244
|
-
|
245
|
-
"package": next_nodes,
|
246
|
-
},
|
237
|
+
package=next_nodes,
|
238
|
+
request_source=mail.package.request_source,
|
247
239
|
)
|
248
240
|
|
249
241
|
@staticmethod
|
@@ -271,9 +263,9 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
271
263
|
action_node = ActionNode(instruction=instruction)
|
272
264
|
while bundled_nodes:
|
273
265
|
node = bundled_nodes.popleft()
|
274
|
-
if isinstance(node,
|
275
|
-
action_node.
|
276
|
-
action_node.
|
266
|
+
if isinstance(node, DirectiveSelection):
|
267
|
+
action_node.directive = node.directive
|
268
|
+
action_node.directive_kwargs = node.directive_kwargs
|
277
269
|
elif isinstance(node, Tool):
|
278
270
|
action_node.tools.append(node)
|
279
271
|
else:
|
@@ -284,9 +276,10 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
284
276
|
"""
|
285
277
|
Process the pending incoming mails and perform the corresponding actions.
|
286
278
|
"""
|
287
|
-
for key in list(self.pending_ins.keys()):
|
288
|
-
while self.pending_ins[key]:
|
289
|
-
|
279
|
+
for key in list(self.mailbox.pending_ins.keys()):
|
280
|
+
while self.mailbox.pending_ins[key].size() > 0:
|
281
|
+
mail_id = self.mailbox.pending_ins[key].popleft()
|
282
|
+
mail = self.mailbox.pile.pop(mail_id)
|
290
283
|
try:
|
291
284
|
if mail.category == "end":
|
292
285
|
self.execute_stop = True
|
@@ -295,6 +288,8 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
295
288
|
self._send_mail(next_nodes, mail)
|
296
289
|
except Exception as e:
|
297
290
|
raise ValueError(f"Error handling mail: {e}") from e
|
291
|
+
if self.mailbox.pending_ins[key].size() == 0:
|
292
|
+
self.mailbox.pending_ins.pop(key)
|
298
293
|
|
299
294
|
async def execute(self, refresh_time=1):
|
300
295
|
"""
|
@@ -306,7 +301,7 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
306
301
|
Raises:
|
307
302
|
ValueError: If the graph structure is found to be cyclic, which is unsupported.
|
308
303
|
"""
|
309
|
-
if not self.
|
304
|
+
if not self.is_acyclic():
|
310
305
|
raise ValueError("Structure is not acyclic")
|
311
306
|
|
312
307
|
while not self.execute_stop:
|
@@ -331,4 +326,5 @@ class StructureExecutor(BaseExecutor, Graph):
|
|
331
326
|
the file writing process or data formatting.
|
332
327
|
"""
|
333
328
|
from lionagi.integrations.storage.to_excel import to_excel
|
329
|
+
|
334
330
|
to_excel(self, structure_name, dir)
|
@@ -2,16 +2,16 @@ from collections import deque
|
|
2
2
|
import json
|
3
3
|
from typing import Callable
|
4
4
|
|
5
|
-
from lionagi.core.
|
5
|
+
from lionagi.core.executor.base_executor import BaseExecutor
|
6
6
|
from lionagi.integrations.storage.neo4j import Neo4j
|
7
7
|
from lionagi.integrations.storage.storage_util import ParseNode
|
8
|
-
from lionagi.core.generic import ActionNode
|
9
8
|
from lionagi.core.agent.base_agent import BaseAgent
|
10
|
-
from lionagi.core.
|
9
|
+
from lionagi.core.engine.instruction_map_engine import InstructionMapEngine
|
11
10
|
|
12
|
-
from lionagi.core.mail
|
13
|
-
from lionagi.core.
|
14
|
-
from lionagi.core.generic import
|
11
|
+
from lionagi.core.mail import Mail
|
12
|
+
from lionagi.core.action import Tool, DirectiveSelection, ActionNode
|
13
|
+
from lionagi.core.generic.edge import Edge
|
14
|
+
from lionagi.core.collections.progression import progression
|
15
15
|
|
16
16
|
from lionagi.libs import AsyncUtil
|
17
17
|
|
@@ -33,9 +33,12 @@ class Neo4jExecutor(BaseExecutor):
|
|
33
33
|
structure_id: str = None
|
34
34
|
structure_name: str = None
|
35
35
|
middle_agents: list | None = None
|
36
|
-
default_agent_executable: BaseExecutor =
|
36
|
+
default_agent_executable: BaseExecutor = InstructionMapEngine()
|
37
37
|
condition_check_result: bool | None = None
|
38
38
|
|
39
|
+
class Config:
|
40
|
+
arbitrary_types_allowed = True
|
41
|
+
|
39
42
|
async def check_edge_condition(
|
40
43
|
self, condition, executable_id, request_source, head, tail
|
41
44
|
):
|
@@ -52,9 +55,9 @@ class Neo4jExecutor(BaseExecutor):
|
|
52
55
|
Returns:
|
53
56
|
bool: Result of the condition check.
|
54
57
|
"""
|
55
|
-
if condition.
|
56
|
-
return condition(self)
|
57
|
-
elif condition.
|
58
|
+
if condition.source == "structure":
|
59
|
+
return condition.applies(self)
|
60
|
+
elif condition.source == "executable":
|
58
61
|
return await self._check_executable_condition(
|
59
62
|
condition, executable_id, head, tail, request_source
|
60
63
|
)
|
@@ -66,20 +69,20 @@ class Neo4jExecutor(BaseExecutor):
|
|
66
69
|
Args:
|
67
70
|
edge_id (str): The ID of the edge.
|
68
71
|
"""
|
69
|
-
for key in list(self.pending_ins.keys()):
|
70
|
-
skipped_requests =
|
71
|
-
while self.pending_ins[key]:
|
72
|
-
|
72
|
+
for key in list(self.mailbox.pending_ins.keys()):
|
73
|
+
skipped_requests = progression()
|
74
|
+
while self.mailbox.pending_ins[key].size() > 0:
|
75
|
+
mail_id = self.mailbox.pending_ins[key].popleft()
|
76
|
+
mail = self.mailbox.pile[mail_id]
|
73
77
|
if (
|
74
78
|
mail.category == "condition"
|
75
|
-
and mail.package
|
79
|
+
and mail.package.package["edge_id"] == edge_id
|
76
80
|
):
|
77
|
-
self.
|
78
|
-
|
79
|
-
]
|
81
|
+
self.mailbox.pile.pop(mail_id)
|
82
|
+
self.condition_check_result = mail.package.package["check_result"]
|
80
83
|
else:
|
81
84
|
skipped_requests.append(mail)
|
82
|
-
self.pending_ins[key] = skipped_requests
|
85
|
+
self.mailbox.pending_ins[key] = skipped_requests
|
83
86
|
|
84
87
|
async def _check_executable_condition(
|
85
88
|
self, condition, executable_id, head, tail, request_source
|
@@ -99,13 +102,14 @@ class Neo4jExecutor(BaseExecutor):
|
|
99
102
|
"""
|
100
103
|
edge = Edge(head=head, tail=tail, condition=condition)
|
101
104
|
self.send(
|
102
|
-
|
105
|
+
recipient=executable_id,
|
103
106
|
category="condition",
|
104
|
-
package=
|
107
|
+
package=edge,
|
108
|
+
request_source=request_source,
|
105
109
|
)
|
106
110
|
while self.condition_check_result is None:
|
107
111
|
await AsyncUtil.sleep(0.1)
|
108
|
-
self._process_edge_condition(edge.
|
112
|
+
self._process_edge_condition(edge.ln_id)
|
109
113
|
continue
|
110
114
|
check_result = self.condition_check_result
|
111
115
|
self.condition_check_result = None
|
@@ -126,27 +130,27 @@ class Neo4jExecutor(BaseExecutor):
|
|
126
130
|
bundled_nodes = deque()
|
127
131
|
for node_labels, node_properties in bundle_list:
|
128
132
|
try:
|
129
|
-
if "
|
130
|
-
node = ParseNode.
|
133
|
+
if "DirectiveSelection" in node_labels:
|
134
|
+
node = ParseNode.parse_directiveSelection(node_properties)
|
131
135
|
bundled_nodes.append(node)
|
132
136
|
elif "Tool" in node_labels:
|
133
137
|
node = ParseNode.parse_tool(node_properties)
|
134
138
|
bundled_nodes.append(node)
|
135
139
|
else:
|
136
140
|
raise ValueError(
|
137
|
-
f"Invalid bundle node {node_properties.
|
141
|
+
f"Invalid bundle node {node_properties.ln_id}. Valid nodes are ActionSelection or Tool"
|
138
142
|
)
|
139
143
|
except Exception as e:
|
140
144
|
raise ValueError(
|
141
|
-
f"Failed to parse ActionSelection or Tool node {node_properties.
|
145
|
+
f"Failed to parse ActionSelection or Tool node {node_properties.ln_id}. Error: {e}"
|
142
146
|
)
|
143
147
|
|
144
148
|
action_node = ActionNode(instruction=instruction)
|
145
149
|
while bundled_nodes:
|
146
150
|
node = bundled_nodes.popleft()
|
147
|
-
if isinstance(node,
|
148
|
-
action_node.
|
149
|
-
action_node.
|
151
|
+
if isinstance(node, DirectiveSelection):
|
152
|
+
action_node.directive = node.directive
|
153
|
+
action_node.directive_kwargs = node.directive_kwargs
|
150
154
|
elif isinstance(node, Tool):
|
151
155
|
action_node.tools.append(node)
|
152
156
|
return action_node
|
@@ -161,7 +165,11 @@ class Neo4jExecutor(BaseExecutor):
|
|
161
165
|
Returns:
|
162
166
|
BaseAgent: An agent executor configured with the given properties.
|
163
167
|
"""
|
164
|
-
output_parser =
|
168
|
+
output_parser = (
|
169
|
+
ParseNode.convert_to_def(node_properties["outputParser"])
|
170
|
+
if "outputParser" in node_properties
|
171
|
+
else None
|
172
|
+
)
|
165
173
|
|
166
174
|
structure = Neo4jExecutor(
|
167
175
|
driver=self.driver, structure_id=node_properties["structureId"]
|
@@ -170,9 +178,9 @@ class Neo4jExecutor(BaseExecutor):
|
|
170
178
|
structure=structure,
|
171
179
|
executable=self.default_agent_executable,
|
172
180
|
output_parser=output_parser,
|
181
|
+
ln_id=node_properties["ln_id"],
|
182
|
+
timestamp=node_properties["timestamp"],
|
173
183
|
)
|
174
|
-
agent.id_ = node_properties["id"]
|
175
|
-
agent.timestamp = node_properties["timestamp"]
|
176
184
|
return agent
|
177
185
|
|
178
186
|
async def _next_node(
|
@@ -202,7 +210,7 @@ class Neo4jExecutor(BaseExecutor):
|
|
202
210
|
condition_obj = ParseNode.parse_condition(condition, condition_cls)
|
203
211
|
|
204
212
|
head = node_id
|
205
|
-
tail = node_properties["
|
213
|
+
tail = node_properties["ln_id"]
|
206
214
|
check = await self.check_edge_condition(
|
207
215
|
condition_obj, executable_id, request_source, head, tail
|
208
216
|
)
|
@@ -210,7 +218,7 @@ class Neo4jExecutor(BaseExecutor):
|
|
210
218
|
continue
|
211
219
|
except Exception as e:
|
212
220
|
raise ValueError(
|
213
|
-
f"Failed to use condition {edge_properties['condition']} from {node_id} to {node_properties['
|
221
|
+
f"Failed to use condition {edge_properties['condition']} from {node_id} to {node_properties['ln_id']}, Error: {e}"
|
214
222
|
)
|
215
223
|
|
216
224
|
try:
|
@@ -223,14 +231,14 @@ class Neo4jExecutor(BaseExecutor):
|
|
223
231
|
|
224
232
|
else:
|
225
233
|
raise ValueError(
|
226
|
-
f"Invalid start node {node_properties.
|
234
|
+
f"Invalid start node {node_properties.ln_id}. Valid nodes are System or Instruction"
|
227
235
|
)
|
228
236
|
except Exception as e:
|
229
237
|
raise ValueError(
|
230
|
-
f"Failed to parse System or Instruction node {node_properties.
|
238
|
+
f"Failed to parse System or Instruction node {node_properties.ln_id}. Error: {e}"
|
231
239
|
)
|
232
240
|
|
233
|
-
bundle_list = await self.driver.get_bundle(node.
|
241
|
+
bundle_list = await self.driver.get_bundle(node.ln_id)
|
234
242
|
|
235
243
|
if bundle_list and "System" in node_labels:
|
236
244
|
raise ValueError("System node does not support bundle edge")
|
@@ -247,10 +255,10 @@ class Neo4jExecutor(BaseExecutor):
|
|
247
255
|
ValueError: If there is an issue with finding or starting the structure.
|
248
256
|
"""
|
249
257
|
try:
|
250
|
-
|
258
|
+
id_, head_list = await self.driver.get_heads(
|
251
259
|
self.structure_name, self.structure_id
|
252
260
|
)
|
253
|
-
self.structure_id =
|
261
|
+
self.structure_id = id_
|
254
262
|
return await self._next_node(head_list)
|
255
263
|
except Exception as e:
|
256
264
|
raise ValueError(f"Error in searching for structure in Neo4j. Error: {e}")
|
@@ -273,12 +281,12 @@ class Neo4jExecutor(BaseExecutor):
|
|
273
281
|
node_list = await self.driver.get_forwards(node_id)
|
274
282
|
return await self._next_node(node_list, node_id, executable_id, request_source)
|
275
283
|
|
276
|
-
async def _handle_mail(self, mail:
|
284
|
+
async def _handle_mail(self, mail: Mail):
|
277
285
|
"""
|
278
286
|
Processes incoming mail, determining the next action based on the mail's category and content.
|
279
287
|
|
280
288
|
Args:
|
281
|
-
mail (
|
289
|
+
mail (Mail): The incoming mail to be processed.
|
282
290
|
|
283
291
|
Raises:
|
284
292
|
ValueError: If there is an error processing the mail.
|
@@ -295,9 +303,9 @@ class Neo4jExecutor(BaseExecutor):
|
|
295
303
|
|
296
304
|
elif mail.category == "node_id":
|
297
305
|
try:
|
298
|
-
node_id = mail.package
|
299
|
-
executable_id = mail.
|
300
|
-
request_source = mail.package
|
306
|
+
node_id = mail.package.package
|
307
|
+
executable_id = mail.sender
|
308
|
+
request_source = mail.package.request_source
|
301
309
|
return await self._handle_node_id(
|
302
310
|
node_id, executable_id, request_source
|
303
311
|
)
|
@@ -305,9 +313,9 @@ class Neo4jExecutor(BaseExecutor):
|
|
305
313
|
raise ValueError(f"Error in handling node_id: {e}")
|
306
314
|
elif mail.category == "node":
|
307
315
|
try:
|
308
|
-
node_id = mail.package
|
309
|
-
executable_id = mail.
|
310
|
-
request_source = mail.package
|
316
|
+
node_id = mail.package.package.ln_id
|
317
|
+
executable_id = mail.sender
|
318
|
+
request_source = mail.package.request_source
|
311
319
|
return await self._handle_node_id(
|
312
320
|
node_id, executable_id, request_source
|
313
321
|
)
|
@@ -316,50 +324,45 @@ class Neo4jExecutor(BaseExecutor):
|
|
316
324
|
else:
|
317
325
|
raise ValueError(f"Invalid mail type for structure")
|
318
326
|
|
319
|
-
def _send_mail(self, next_nodes: list | None, mail:
|
327
|
+
def _send_mail(self, next_nodes: list | None, mail: Mail):
|
320
328
|
"""
|
321
329
|
Sends out mail to the next nodes or marks the execution as ended if there are no next nodes.
|
322
330
|
|
323
331
|
Args:
|
324
332
|
next_nodes (list | None): List of next nodes to which mail should be sent.
|
325
|
-
mail (
|
333
|
+
mail (Mail): The current mail being processed.
|
326
334
|
"""
|
327
335
|
if not next_nodes: # tail
|
328
336
|
self.send(
|
329
|
-
|
337
|
+
recipient=mail.sender,
|
330
338
|
category="end",
|
331
|
-
package=
|
332
|
-
|
333
|
-
"package": "end",
|
334
|
-
},
|
339
|
+
package="end",
|
340
|
+
request_source=mail.package.request_source,
|
335
341
|
)
|
336
342
|
else:
|
337
343
|
if len(next_nodes) == 1:
|
338
344
|
self.send(
|
339
|
-
|
345
|
+
recipient=mail.sender,
|
340
346
|
category="node",
|
341
|
-
package=
|
342
|
-
|
343
|
-
"package": next_nodes[0],
|
344
|
-
},
|
347
|
+
package=next_nodes[0],
|
348
|
+
request_source=mail.package.request_source,
|
345
349
|
)
|
346
350
|
else:
|
347
351
|
self.send(
|
348
|
-
|
352
|
+
recipient=mail.sender,
|
349
353
|
category="node_list",
|
350
|
-
package=
|
351
|
-
|
352
|
-
"package": next_nodes,
|
353
|
-
},
|
354
|
+
package=next_nodes,
|
355
|
+
request_source=mail.package.request_source,
|
354
356
|
)
|
355
357
|
|
356
358
|
async def forward(self) -> None:
|
357
359
|
"""
|
358
360
|
Forwards execution by processing all pending mails and advancing to next nodes or actions.
|
359
361
|
"""
|
360
|
-
for key in list(self.pending_ins.keys()):
|
361
|
-
while self.pending_ins[key]:
|
362
|
-
|
362
|
+
for key in list(self.mailbox.pending_ins.keys()):
|
363
|
+
while self.mailbox.pending_ins[key].size() > 0:
|
364
|
+
mail_id = self.mailbox.pending_ins[key].popleft()
|
365
|
+
mail = self.mailbox.pile.pop(mail_id)
|
363
366
|
try:
|
364
367
|
if mail == "end":
|
365
368
|
self.execute_stop = True
|