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,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.")
|
@@ -0,0 +1,330 @@
|
|
1
|
+
from collections import deque
|
2
|
+
|
3
|
+
from lionagi.libs import AsyncUtil, convert
|
4
|
+
|
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
|
8
|
+
|
9
|
+
from lionagi.core.action import Tool, DirectiveSelection, ActionNode
|
10
|
+
|
11
|
+
from lionagi.core.mail import Mail
|
12
|
+
from lionagi.core.generic.graph import Graph
|
13
|
+
from lionagi.core.collections.progression import progression
|
14
|
+
|
15
|
+
|
16
|
+
class GraphExecutor(BaseExecutor, Graph):
|
17
|
+
"""
|
18
|
+
Executes tasks within a graph structure, handling dynamic node flows and conditional edge logic.
|
19
|
+
|
20
|
+
Attributes:
|
21
|
+
condition_check_result (bool | None): Result of the last condition check performed during execution,
|
22
|
+
used to control flow based on dynamic conditions.
|
23
|
+
"""
|
24
|
+
|
25
|
+
condition_check_result: bool | None = None
|
26
|
+
|
27
|
+
async def check_edge_condition(self, edge: Edge, executable_id, request_source):
|
28
|
+
"""
|
29
|
+
Evaluates the condition associated with an edge, determining if execution should proceed along that edge based
|
30
|
+
on the condition's source type.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
edge (Edge): The edge whose condition needs to be checked.
|
34
|
+
executable_id (str): ID of the executor handling this edge's condition.
|
35
|
+
request_source (str): Origin of the request prompting this condition check.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
bool: Result of the condition evaluation.
|
39
|
+
|
40
|
+
Raises:
|
41
|
+
ValueError: If the source_type of the condition is invalid.
|
42
|
+
"""
|
43
|
+
if edge.condition.source == "structure" or isinstance(
|
44
|
+
edge.condition.source, Node
|
45
|
+
):
|
46
|
+
return await edge.check_condition(self)
|
47
|
+
|
48
|
+
elif edge.condition.source == "executable":
|
49
|
+
return await self._check_executable_condition(
|
50
|
+
edge, executable_id, request_source
|
51
|
+
)
|
52
|
+
|
53
|
+
else:
|
54
|
+
raise ValueError("Invalid source_type.")
|
55
|
+
|
56
|
+
def _process_edge_condition(self, edge_id):
|
57
|
+
"""
|
58
|
+
Process the condition of a edge.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
edge_id (str): The ID of the edge.
|
62
|
+
"""
|
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]
|
68
|
+
if (
|
69
|
+
mail.category == "condition"
|
70
|
+
and mail.package.package["edge_id"] == edge_id
|
71
|
+
):
|
72
|
+
self.mailbox.pile.pop(mail_id)
|
73
|
+
self.condition_check_result = mail.package.package["check_result"]
|
74
|
+
else:
|
75
|
+
skipped_requests.append(mail)
|
76
|
+
self.mailbox.pending_ins[key] = skipped_requests
|
77
|
+
|
78
|
+
async def _check_executable_condition(
|
79
|
+
self, edge: Edge, executable_id, request_source
|
80
|
+
):
|
81
|
+
"""
|
82
|
+
Sends the edge's condition to an external executable for evaluation and waits for the result.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
edge (Edge): The edge containing the condition to be checked.
|
86
|
+
executable_id (str): ID of the executable that will evaluate the condition.
|
87
|
+
request_source (str): Source of the request for condition evaluation.
|
88
|
+
|
89
|
+
Returns:
|
90
|
+
bool: The result of the condition check.
|
91
|
+
"""
|
92
|
+
self.send(
|
93
|
+
recipient=executable_id,
|
94
|
+
category="condition",
|
95
|
+
package=edge,
|
96
|
+
request_source=request_source,
|
97
|
+
)
|
98
|
+
while self.condition_check_result is None:
|
99
|
+
await AsyncUtil.sleep(0.1)
|
100
|
+
self._process_edge_condition(edge.ln_id)
|
101
|
+
continue
|
102
|
+
check_result = self.condition_check_result
|
103
|
+
self.condition_check_result = None
|
104
|
+
return check_result
|
105
|
+
|
106
|
+
async def _handle_node_id(self, mail: Mail):
|
107
|
+
"""
|
108
|
+
Processes the node identified by its ID in the mail's package, ensuring it exists and retrieving the next set of
|
109
|
+
nodes based on the current node.
|
110
|
+
|
111
|
+
Args:
|
112
|
+
mail (BaseMail): The mail containing the node ID and related execution details.
|
113
|
+
|
114
|
+
Raises:
|
115
|
+
ValueError: If the node does not exist within the structure.
|
116
|
+
"""
|
117
|
+
if mail.package.package not in self.internal_nodes:
|
118
|
+
raise ValueError(
|
119
|
+
f"Node {mail.package.package}: Node does not exist in the structure {self.ln_id}"
|
120
|
+
)
|
121
|
+
return await self._next_node(
|
122
|
+
self.internal_nodes[mail.package.package],
|
123
|
+
mail.sender,
|
124
|
+
mail.package.request_source,
|
125
|
+
)
|
126
|
+
|
127
|
+
async def _handle_node(self, mail: Mail):
|
128
|
+
"""
|
129
|
+
Processes the node specified in the mail's package, ensuring it exists within the structure.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
mail (BaseMail): The mail containing the node details to be processed.
|
133
|
+
|
134
|
+
Raises:
|
135
|
+
ValueError: If the node does not exist within the structure.
|
136
|
+
"""
|
137
|
+
if not self.node_exist(mail.package.package):
|
138
|
+
raise ValueError(
|
139
|
+
f"Node {mail.package.package.ln_id}: does not exist in the structure {self.ln_id}"
|
140
|
+
)
|
141
|
+
return await self._next_node(
|
142
|
+
mail.package.package, mail.sender, mail.package.request_source
|
143
|
+
)
|
144
|
+
|
145
|
+
async def _handle_mail(self, mail: Mail):
|
146
|
+
"""
|
147
|
+
Processes incoming mail based on its category, initiating node execution or structure operations accordingly.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
mail (BaseMail): The mail to be processed, containing category and package information.
|
151
|
+
|
152
|
+
Raises:
|
153
|
+
ValueError: If the mail type is invalid for the current structure or an error occurs in handling the node ID.
|
154
|
+
"""
|
155
|
+
if mail.category == "start":
|
156
|
+
return self.get_heads()
|
157
|
+
|
158
|
+
elif mail.category == "end":
|
159
|
+
self.execute_stop = True
|
160
|
+
return None
|
161
|
+
|
162
|
+
elif mail.category == "node_id":
|
163
|
+
try:
|
164
|
+
return await self._handle_node_id(mail)
|
165
|
+
except Exception as e:
|
166
|
+
raise ValueError(f"Error handling node_id: {e}") from e
|
167
|
+
|
168
|
+
elif mail.category == "node" and isinstance(mail.package.package, Node):
|
169
|
+
try:
|
170
|
+
return await self._handle_node(mail)
|
171
|
+
except Exception as e:
|
172
|
+
raise ValueError(f"Error handling node: {e}") from e
|
173
|
+
|
174
|
+
else:
|
175
|
+
raise ValueError(f"Invalid mail type for structure")
|
176
|
+
|
177
|
+
async def _next_node(self, current_node: Node, executable_id, request_source):
|
178
|
+
"""
|
179
|
+
Get the next step nodes based on the current node.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
current_node (Node): The current node.
|
183
|
+
executable_id (str): The ID of the executable.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
list[Node]: The next step nodes.
|
187
|
+
"""
|
188
|
+
next_nodes = []
|
189
|
+
next_edges = self.get_node_edges(current_node, direction="out")
|
190
|
+
for edge in convert.to_list(list(next_edges.values())):
|
191
|
+
if edge.bundle:
|
192
|
+
continue
|
193
|
+
if edge.condition:
|
194
|
+
check = await self.check_edge_condition(
|
195
|
+
edge, executable_id, request_source
|
196
|
+
)
|
197
|
+
if not check:
|
198
|
+
continue
|
199
|
+
node = self.internal_nodes[edge.tail]
|
200
|
+
further_edges = self.get_node_edges(node, direction="out")
|
201
|
+
bundled_nodes = deque()
|
202
|
+
for f_edge in convert.to_list(list(further_edges.values())):
|
203
|
+
if f_edge.bundle:
|
204
|
+
bundled_nodes.append(self.internal_nodes[f_edge.tail])
|
205
|
+
if bundled_nodes:
|
206
|
+
node = self.parse_bundled_to_action(node, bundled_nodes)
|
207
|
+
next_nodes.append(node)
|
208
|
+
return next_nodes
|
209
|
+
|
210
|
+
def _send_mail(self, next_nodes: list | None, mail: Mail):
|
211
|
+
"""
|
212
|
+
Sends mails to the next nodes or signals the end of execution if no next nodes exist.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
next_nodes (list | None): List of next nodes to process or None if no further nodes are available.
|
216
|
+
mail (BaseMail): The base mail used for sending follow-up actions.
|
217
|
+
"""
|
218
|
+
if not next_nodes: # tail
|
219
|
+
self.send(
|
220
|
+
recipient=mail.sender,
|
221
|
+
category="end",
|
222
|
+
package="end",
|
223
|
+
request_source=mail.package.request_source,
|
224
|
+
)
|
225
|
+
else:
|
226
|
+
if len(next_nodes) == 1:
|
227
|
+
self.send(
|
228
|
+
recipient=mail.sender,
|
229
|
+
category="node",
|
230
|
+
package=next_nodes[0],
|
231
|
+
request_source=mail.package.request_source,
|
232
|
+
)
|
233
|
+
else:
|
234
|
+
self.send(
|
235
|
+
recipient=mail.sender,
|
236
|
+
category="node_list",
|
237
|
+
package=next_nodes,
|
238
|
+
request_source=mail.package.request_source,
|
239
|
+
)
|
240
|
+
|
241
|
+
@staticmethod
|
242
|
+
def parse_bundled_to_action(instruction: Node, bundled_nodes: deque):
|
243
|
+
"""
|
244
|
+
Constructs an action node from a bundle of nodes, combining various types of nodes like ActionSelection or Tool
|
245
|
+
into a single actionable unit.
|
246
|
+
|
247
|
+
This method takes a bundle of nodes and systematically integrates their functionalities into a single `ActionNode`.
|
248
|
+
This is crucial in scenarios where multiple actions or tools need to be executed sequentially or in a coordinated
|
249
|
+
manner as part of a larger instruction flow.
|
250
|
+
|
251
|
+
Args:
|
252
|
+
instruction (Node): The initial instruction node leading to this action.
|
253
|
+
bundled_nodes (deque): A deque containing nodes to be bundled into the action. These nodes typically represent
|
254
|
+
either actions to be taken or tools to be utilized.
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
ActionNode: An `ActionNode` that encapsulates the combined functionality of the bundled nodes, ready for execution.
|
258
|
+
|
259
|
+
Raises:
|
260
|
+
ValueError: If an unrecognized node type is encountered within the bundled nodes. Only `ActionSelection` and
|
261
|
+
`Tool` nodes are valid for bundling into an `ActionNode`.
|
262
|
+
"""
|
263
|
+
action_node = ActionNode(instruction=instruction)
|
264
|
+
while bundled_nodes:
|
265
|
+
node = bundled_nodes.popleft()
|
266
|
+
if isinstance(node, DirectiveSelection):
|
267
|
+
action_node.directive = node.directive
|
268
|
+
action_node.directive_kwargs = node.directive_kwargs
|
269
|
+
elif isinstance(node, Tool):
|
270
|
+
action_node.tools.append(node)
|
271
|
+
else:
|
272
|
+
raise ValueError("Invalid bundles nodes")
|
273
|
+
return action_node
|
274
|
+
|
275
|
+
async def forward(self) -> None:
|
276
|
+
"""
|
277
|
+
Process the pending incoming mails and perform the corresponding actions.
|
278
|
+
"""
|
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)
|
283
|
+
try:
|
284
|
+
if mail.category == "end":
|
285
|
+
self.execute_stop = True
|
286
|
+
return
|
287
|
+
next_nodes = await self._handle_mail(mail)
|
288
|
+
self._send_mail(next_nodes, mail)
|
289
|
+
except Exception as e:
|
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)
|
293
|
+
|
294
|
+
async def execute(self, refresh_time=1):
|
295
|
+
"""
|
296
|
+
Executes the forward processing loop, checking conditions and processing nodes at defined intervals.
|
297
|
+
|
298
|
+
Args:
|
299
|
+
refresh_time (int): The delay between execution cycles, allowing for asynchronous operations to complete.
|
300
|
+
|
301
|
+
Raises:
|
302
|
+
ValueError: If the graph structure is found to be cyclic, which is unsupported.
|
303
|
+
"""
|
304
|
+
if not self.is_acyclic():
|
305
|
+
raise ValueError("Structure is not acyclic")
|
306
|
+
|
307
|
+
while not self.execute_stop:
|
308
|
+
await self.forward()
|
309
|
+
await AsyncUtil.sleep(refresh_time)
|
310
|
+
|
311
|
+
def to_excel(self, structure_name, dir="structure_storage"):
|
312
|
+
"""
|
313
|
+
Exports the current structure to an Excel file using a specified structure name and directory.
|
314
|
+
|
315
|
+
This method utilizes the `to_excel` function from the `lionagi.integrations.storage.to_excel` module,
|
316
|
+
saving the current structure instance into an Excel file format. The Excel file will contain details
|
317
|
+
about nodes, edges, and other relevant data as separate sheets within the file.
|
318
|
+
|
319
|
+
Args:
|
320
|
+
structure_name (str): The name to assign to the structure within the Excel file. This name is
|
321
|
+
used as part of the file naming convention.
|
322
|
+
dir (str, optional): The directory where the Excel file will be saved. Defaults to "structure_storage".
|
323
|
+
|
324
|
+
Raises:
|
325
|
+
Exception: Propagates any exceptions raised by the `to_excel` function, which might occur during
|
326
|
+
the file writing process or data formatting.
|
327
|
+
"""
|
328
|
+
from lionagi.integrations.storage.to_excel import to_excel
|
329
|
+
|
330
|
+
to_excel(self, structure_name, dir)
|