lionagi 0.1.2__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 +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 +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/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.1.dist-info/LICENSE +202 -0
- lionagi-0.2.1.dist-info/METADATA +272 -0
- lionagi-0.2.1.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.1.dist-info}/WHEEL +0 -0
- {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,283 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
|
3
|
-
from typing import Tuple, Any, TypeVar, Callable
|
4
|
-
from lionagi.libs import func_call, convert, ParseUtil
|
5
|
-
|
6
|
-
from lionagi.core.tool.tool import Tool, TOOL_TYPE
|
7
|
-
|
8
|
-
T = TypeVar("T", bound=Tool)
|
9
|
-
|
10
|
-
|
11
|
-
class ToolManager:
|
12
|
-
"""
|
13
|
-
A manager class for handling the registration and invocation of tools that are subclasses of Tool.
|
14
|
-
|
15
|
-
This class maintains a registry of tool instances, allowing for dynamic invocation based on
|
16
|
-
tool name and provided arguments. It supports both synchronous and asynchronous tool function
|
17
|
-
calls.
|
18
|
-
|
19
|
-
Attributes:
|
20
|
-
registry (dict[str, Tool]): A dictionary to hold registered tools, keyed by their names.
|
21
|
-
"""
|
22
|
-
|
23
|
-
registry: dict = {}
|
24
|
-
|
25
|
-
def name_existed(self, name: str) -> bool:
|
26
|
-
"""
|
27
|
-
Checks if a tool name already exists in the registry.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
name (str): The name of the tool to check.
|
31
|
-
|
32
|
-
Returns:
|
33
|
-
bool: True if the name exists, False otherwise.
|
34
|
-
"""
|
35
|
-
return name in self.registry
|
36
|
-
|
37
|
-
@property
|
38
|
-
def has_tools(self):
|
39
|
-
return self.registry != {}
|
40
|
-
|
41
|
-
def _register_tool(self, tool: Tool | Callable) -> None:
|
42
|
-
"""
|
43
|
-
Registers a tool in the registry. Raises a TypeError if the object is not an instance of Tool.
|
44
|
-
|
45
|
-
Args:
|
46
|
-
tool (Tool): The tool instance to register.
|
47
|
-
|
48
|
-
Raises:
|
49
|
-
TypeError: If the provided object is not an instance of Tool.
|
50
|
-
"""
|
51
|
-
if isinstance(tool, Callable):
|
52
|
-
tool = func_to_tool(tool)[0]
|
53
|
-
if not isinstance(tool, Tool):
|
54
|
-
raise TypeError("Please register a Tool object.")
|
55
|
-
name = tool.schema_["function"]["name"]
|
56
|
-
self.registry.update({name: tool})
|
57
|
-
|
58
|
-
async def invoke(self, func_calls: Tuple[str, dict[str, Any]]) -> Any:
|
59
|
-
"""
|
60
|
-
Invokes a registered tool's function with the given arguments. Supports both coroutine and regular functions.
|
61
|
-
|
62
|
-
Args:
|
63
|
-
func_call (Tuple[str, Dict[str, Any]]): A tuple containing the function name and a dictionary of keyword arguments.
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
Any: The result of the function call.
|
67
|
-
|
68
|
-
Raises:
|
69
|
-
ValueError: If the function name is not registered or if there's an error during function invocation.
|
70
|
-
"""
|
71
|
-
name, kwargs = func_calls
|
72
|
-
if not self.name_existed(name):
|
73
|
-
raise ValueError(f"Function {name} is not registered.")
|
74
|
-
tool = self.registry[name]
|
75
|
-
func = tool.func
|
76
|
-
parser = tool.parser
|
77
|
-
try:
|
78
|
-
if func_call.is_coroutine_func(func):
|
79
|
-
tasks = [func_call.call_handler(func, **kwargs)]
|
80
|
-
out = await asyncio.gather(*tasks)
|
81
|
-
return parser(out[0]) if parser else out[0]
|
82
|
-
else:
|
83
|
-
out = func(**kwargs)
|
84
|
-
return parser(out) if parser else out
|
85
|
-
except Exception as e:
|
86
|
-
raise ValueError(
|
87
|
-
f"Error when invoking function {name} with arguments {kwargs} with error message {e}"
|
88
|
-
) from e
|
89
|
-
|
90
|
-
@staticmethod
|
91
|
-
def get_function_call(response: dict) -> Tuple[str, dict]:
|
92
|
-
"""
|
93
|
-
Extracts a function call and arguments from a response dictionary.
|
94
|
-
|
95
|
-
Args:
|
96
|
-
response (dict): The response dictionary containing the function call information.
|
97
|
-
|
98
|
-
Returns:
|
99
|
-
Tuple[str, dict]: A tuple containing the function name and a dictionary of arguments.
|
100
|
-
|
101
|
-
Raises:
|
102
|
-
ValueError: If the response does not contain valid function call information.
|
103
|
-
"""
|
104
|
-
try:
|
105
|
-
func = response["action"][7:]
|
106
|
-
args = convert.to_dict(response["arguments"])
|
107
|
-
return func, args
|
108
|
-
except Exception:
|
109
|
-
try:
|
110
|
-
func = response["recipient_name"].split(".")[-1]
|
111
|
-
args = response["parameters"]
|
112
|
-
return func, args
|
113
|
-
except:
|
114
|
-
raise ValueError("response is not a valid function call")
|
115
|
-
|
116
|
-
def register_tools(self, tools: list[Tool]) -> None:
|
117
|
-
"""
|
118
|
-
Registers multiple tools in the registry.
|
119
|
-
|
120
|
-
Args:
|
121
|
-
tools (list[Tool]): A list of tool instances to register.
|
122
|
-
"""
|
123
|
-
func_call.lcall(tools, self._register_tool)
|
124
|
-
|
125
|
-
def to_tool_schema_list(self) -> list[dict[str, Any]]:
|
126
|
-
"""
|
127
|
-
Generates a list of schemas for all registered tools.
|
128
|
-
|
129
|
-
Returns:
|
130
|
-
list[dict[str, Any]]: A list of tool schemas.
|
131
|
-
|
132
|
-
"""
|
133
|
-
return [tool.schema_ for tool in self.registry.values()]
|
134
|
-
|
135
|
-
def parse_tool(self, tools: TOOL_TYPE, **kwargs) -> dict:
|
136
|
-
"""
|
137
|
-
Parses tool information and generates a dictionary for tool invocation.
|
138
|
-
|
139
|
-
Args:
|
140
|
-
tools: Tool information which can be a single Tool instance, a list of Tool instances, a tool name, or a list of tool names.
|
141
|
-
**kwargs: Additional keyword arguments.
|
142
|
-
|
143
|
-
Returns:
|
144
|
-
dict: A dictionary containing tool schema information and any additional keyword arguments.
|
145
|
-
|
146
|
-
Raises:
|
147
|
-
ValueError: If a tool name is provided that is not registered.
|
148
|
-
"""
|
149
|
-
|
150
|
-
def tool_check(tool):
|
151
|
-
if isinstance(tool, dict):
|
152
|
-
return tool
|
153
|
-
elif isinstance(tool, Tool):
|
154
|
-
return tool.schema_
|
155
|
-
elif isinstance(tool, str):
|
156
|
-
if self.name_existed(tool):
|
157
|
-
tool: Tool = self.registry[tool]
|
158
|
-
return tool.schema_
|
159
|
-
else:
|
160
|
-
raise ValueError(f"Function {tool} is not registered.")
|
161
|
-
|
162
|
-
if tools:
|
163
|
-
if isinstance(tools, bool):
|
164
|
-
tool_kwarg = {"tools": self.to_tool_schema_list()}
|
165
|
-
kwargs = tool_kwarg | kwargs
|
166
|
-
|
167
|
-
else:
|
168
|
-
if not isinstance(tools, list):
|
169
|
-
tools = [tools]
|
170
|
-
tool_kwarg = {"tools": func_call.lcall(tools, tool_check)}
|
171
|
-
kwargs = tool_kwarg | kwargs
|
172
|
-
|
173
|
-
return kwargs
|
174
|
-
|
175
|
-
|
176
|
-
def func_to_tool(
|
177
|
-
func_: Callable | list[Callable], parser=None, docstring_style="google"
|
178
|
-
):
|
179
|
-
"""
|
180
|
-
Transforms a given function into a Tool object, equipped with a schema derived
|
181
|
-
from its docstring. This process involves parsing the function's docstring based
|
182
|
-
on a specified style ('google' or 'reST') to extract relevant metadata and
|
183
|
-
parameters, which are then used to construct a comprehensive schema for the Tool.
|
184
|
-
This schema facilitates the integration of the function with systems or
|
185
|
-
frameworks that rely on structured metadata for automation, documentation, or
|
186
|
-
interface generation purposes.
|
187
|
-
|
188
|
-
The function to be transformed can be any Callable that adheres to the
|
189
|
-
specified docstring conventions. The resulting Tool object encapsulates the
|
190
|
-
original function, allowing it to be utilized within environments that require
|
191
|
-
objects with structured metadata.
|
192
|
-
|
193
|
-
Args:
|
194
|
-
func_ (Callable): The function to be transformed into a Tool object. This
|
195
|
-
function should have a docstring that follows the
|
196
|
-
specified docstring style for accurate schema generation.
|
197
|
-
parser (Optional[Any]): An optional parser object associated with the Tool.
|
198
|
-
This parameter is currently not utilized in the
|
199
|
-
transformation process but is included for future
|
200
|
-
compatibility and extension purposes.
|
201
|
-
docstring_style (str): The format of the docstring to be parsed, indicating
|
202
|
-
the convention used in the function's docstring.
|
203
|
-
Supports 'google' for Google-style docstrings and
|
204
|
-
'reST' for reStructuredText-style docstrings. The
|
205
|
-
chosen style affects how the docstring is parsed and
|
206
|
-
how the schema is generated.
|
207
|
-
|
208
|
-
Returns:
|
209
|
-
Tool: An object representing the original function wrapped as a Tool, along
|
210
|
-
with its generated schema. This Tool object can be used in systems that
|
211
|
-
require detailed metadata about functions, facilitating tasks such as
|
212
|
-
automatic documentation generation, user interface creation, or
|
213
|
-
integration with other software tools.
|
214
|
-
|
215
|
-
Examples:
|
216
|
-
>>> def example_function_google(param1: int, param2: str) -> bool:
|
217
|
-
... '''
|
218
|
-
... An example function using Google style docstrings.
|
219
|
-
...
|
220
|
-
... Args:
|
221
|
-
... param1 (int): The first parameter, demonstrating an integer input_.
|
222
|
-
... param2 (str): The second parameter, demonstrating a string input_.
|
223
|
-
...
|
224
|
-
... Returns:
|
225
|
-
... bool: A boolean value, illustrating the return type.
|
226
|
-
... '''
|
227
|
-
... return True
|
228
|
-
...
|
229
|
-
>>> tool_google = func_to_tool(example_function_google, docstring_style='google')
|
230
|
-
>>> print(isinstance(tool_google, Tool))
|
231
|
-
True
|
232
|
-
|
233
|
-
>>> def example_function_reST(param1: int, param2: str) -> bool:
|
234
|
-
... '''
|
235
|
-
... An example function using reStructuredText (reST) style docstrings.
|
236
|
-
...
|
237
|
-
... :param param1: The first parameter, demonstrating an integer input_.
|
238
|
-
... :type param1: int
|
239
|
-
... :param param2: The second parameter, demonstrating a string input_.
|
240
|
-
... :type param2: str
|
241
|
-
... :returns: A boolean value, illustrating the return type.
|
242
|
-
... :rtype: bool
|
243
|
-
... '''
|
244
|
-
... return True
|
245
|
-
...
|
246
|
-
>>> tool_reST = func_to_tool(example_function_reST, docstring_style='reST')
|
247
|
-
>>> print(isinstance(tool_reST, Tool))
|
248
|
-
True
|
249
|
-
|
250
|
-
Note:
|
251
|
-
The transformation process relies heavily on the accuracy and completeness of
|
252
|
-
the function's docstring. Functions with incomplete or incorrectly formatted
|
253
|
-
docstrings may result in incomplete or inaccurate Tool schemas.
|
254
|
-
"""
|
255
|
-
|
256
|
-
fs = []
|
257
|
-
funcs = convert.to_list(func_, flatten=True, dropna=True)
|
258
|
-
parsers = convert.to_list(parser, flatten=True, dropna=True)
|
259
|
-
|
260
|
-
if parser:
|
261
|
-
if len(funcs) != len(parsers) != 1:
|
262
|
-
raise ValueError(
|
263
|
-
"Length of parser must match length of func. Except if you only pass one"
|
264
|
-
)
|
265
|
-
|
266
|
-
for idx in range(len(funcs)):
|
267
|
-
f_ = lambda _f: Tool(
|
268
|
-
func=_f,
|
269
|
-
schema_=ParseUtil._func_to_schema(_f, style=docstring_style),
|
270
|
-
parser=parsers[idx] if len(parsers) > 1 else parsers[0],
|
271
|
-
)
|
272
|
-
|
273
|
-
fs.append(f_)
|
274
|
-
|
275
|
-
else:
|
276
|
-
fs = func_call.lcall(
|
277
|
-
funcs,
|
278
|
-
lambda _f: Tool(
|
279
|
-
func=_f, schema_=ParseUtil._func_to_schema(_f, style=docstring_style)
|
280
|
-
),
|
281
|
-
)
|
282
|
-
|
283
|
-
return fs
|
@@ -1,64 +0,0 @@
|
|
1
|
-
from pydantic import Field
|
2
|
-
|
3
|
-
# from lionagi import logging as _logging
|
4
|
-
from lionagi.core.generic import BaseComponent
|
5
|
-
from lionagi.experimental.report.util import get_input_output_fields, system_fields
|
6
|
-
|
7
|
-
|
8
|
-
class Form(BaseComponent):
|
9
|
-
|
10
|
-
assignment: str = Field(..., examples=["input1, input2 -> output"])
|
11
|
-
|
12
|
-
input_fields: list[str] = Field(default_factory=list)
|
13
|
-
output_fields: list[str] = Field(default_factory=list)
|
14
|
-
|
15
|
-
def __init__(self, **kwargs):
|
16
|
-
"""
|
17
|
-
at initialization, all relevant fields if not already provided, are set to None,
|
18
|
-
not every field is required to be filled, nor required to be declared at initialization
|
19
|
-
"""
|
20
|
-
super().__init__(**kwargs)
|
21
|
-
self.input_fields, self.output_fields = get_input_output_fields(self.assignment)
|
22
|
-
for i in self.input_fields + self.output_fields:
|
23
|
-
if i not in self.model_fields:
|
24
|
-
self._add_field(i, value=None)
|
25
|
-
|
26
|
-
@property
|
27
|
-
def workable(self):
|
28
|
-
if self.filled:
|
29
|
-
return False
|
30
|
-
|
31
|
-
for i in self.input_fields:
|
32
|
-
if not getattr(self, i, None):
|
33
|
-
return False
|
34
|
-
|
35
|
-
return True
|
36
|
-
|
37
|
-
@property
|
38
|
-
def work_fields(self):
|
39
|
-
dict_ = self.to_dict()
|
40
|
-
return {
|
41
|
-
k: v
|
42
|
-
for k, v in dict_.items()
|
43
|
-
if k not in system_fields and k in self.input_fields + self.output_fields
|
44
|
-
}
|
45
|
-
|
46
|
-
@property
|
47
|
-
def filled(self):
|
48
|
-
return all([value is not None for _, value in self.work_fields.items()])
|
49
|
-
|
50
|
-
def fill(self, form: "Form" = None, **kwargs):
|
51
|
-
"""
|
52
|
-
only work fields for this form can be filled
|
53
|
-
a field can only be filled once
|
54
|
-
"""
|
55
|
-
if self.filled:
|
56
|
-
raise ValueError("Form is already filled")
|
57
|
-
|
58
|
-
fields = form.work_fields if form else {}
|
59
|
-
kwargs = {**fields, **kwargs}
|
60
|
-
|
61
|
-
for k, v in kwargs.items():
|
62
|
-
if k not in self.work_fields:
|
63
|
-
raise ValueError(f"Field {k} is not a valid work field")
|
64
|
-
setattr(self, k, v)
|
@@ -1,138 +0,0 @@
|
|
1
|
-
from typing import Any, Type
|
2
|
-
from pydantic import Field
|
3
|
-
|
4
|
-
# from lionagi import logging as _logging
|
5
|
-
from lionagi.core.generic import BaseComponent
|
6
|
-
from lionagi.experimental.report.form import Form
|
7
|
-
from lionagi.experimental.report.util import get_input_output_fields
|
8
|
-
|
9
|
-
|
10
|
-
class Report(BaseComponent):
|
11
|
-
|
12
|
-
assignment: str = Field(..., examples=["input1, input2 -> output"])
|
13
|
-
|
14
|
-
forms: dict[str, Form] = Field(
|
15
|
-
default_factory=dict,
|
16
|
-
description="A dictionary of forms related to the report, in {assignment: Form} format.",
|
17
|
-
)
|
18
|
-
|
19
|
-
form_assignments: list = Field(
|
20
|
-
[],
|
21
|
-
description="assignment for the report",
|
22
|
-
examples=[["a, b -> c", "a -> e", "b -> f", "c -> g", "e, f, g -> h"]],
|
23
|
-
)
|
24
|
-
|
25
|
-
form_template: Type[Form] = Field(
|
26
|
-
Form, description="The template for the forms in the report."
|
27
|
-
)
|
28
|
-
|
29
|
-
input_fields: list[str] = Field(default_factory=list)
|
30
|
-
output_fields: list[str] = Field(default_factory=list)
|
31
|
-
|
32
|
-
def __init__(self, **kwargs):
|
33
|
-
"""
|
34
|
-
at initialization, all relevant fields if not already provided, are set to None
|
35
|
-
"""
|
36
|
-
super().__init__(**kwargs)
|
37
|
-
self.input_fields, self.output_fields = get_input_output_fields(self.assignment)
|
38
|
-
|
39
|
-
# if assignments is not provided, set it to assignment
|
40
|
-
if self.form_assignments == []:
|
41
|
-
self.form_assignments.append(self.assignment)
|
42
|
-
|
43
|
-
# create forms
|
44
|
-
new_forms = {i: self.form_template(assignment=i) for i in self.form_assignments}
|
45
|
-
|
46
|
-
# add new forms into the report (will ignore new forms already in the
|
47
|
-
# report with same assignment)
|
48
|
-
for k, v in new_forms.items():
|
49
|
-
if k not in self.forms:
|
50
|
-
self.forms[k] = v
|
51
|
-
|
52
|
-
# if the fields are not declared in the report, add them to report
|
53
|
-
# with value set to None
|
54
|
-
for k, v in self.forms.items():
|
55
|
-
for f in list(v.work_fields.keys()):
|
56
|
-
if f not in self.model_fields:
|
57
|
-
field = v.model_fields[f]
|
58
|
-
self._add_field(f, value=None, field=field)
|
59
|
-
|
60
|
-
# if there are fields in the report that are not in the forms, add them to
|
61
|
-
# the forms with values
|
62
|
-
for k, v in self.model_fields.items():
|
63
|
-
if getattr(self, k, None) is not None:
|
64
|
-
for f in self.forms.values():
|
65
|
-
if k in f.work_fields:
|
66
|
-
f.fill(**{k: getattr(self, k)})
|
67
|
-
|
68
|
-
@property
|
69
|
-
def work_fields(self) -> dict[str, Any]:
|
70
|
-
"""
|
71
|
-
all work fields across all forms, including intermediate output fields,
|
72
|
-
this information is extracted from the forms
|
73
|
-
"""
|
74
|
-
|
75
|
-
all_fields = {}
|
76
|
-
for form in self.forms.values():
|
77
|
-
for k, v in form.work_fields.items():
|
78
|
-
if k not in all_fields:
|
79
|
-
all_fields[k] = v
|
80
|
-
return all_fields
|
81
|
-
|
82
|
-
def fill(self, **kwargs):
|
83
|
-
"""
|
84
|
-
fill the information to both the report and forms
|
85
|
-
"""
|
86
|
-
kwargs = {**self.work_fields, **kwargs}
|
87
|
-
for k, v in kwargs.items():
|
88
|
-
if k in self.work_fields and getattr(self, k, None) is None:
|
89
|
-
setattr(self, k, v)
|
90
|
-
|
91
|
-
for form in self.forms.values():
|
92
|
-
if not form.filled:
|
93
|
-
_kwargs = {k: v for k, v in kwargs.items() if k in form.work_fields}
|
94
|
-
form.fill(**_kwargs)
|
95
|
-
|
96
|
-
@property
|
97
|
-
def filled(self):
|
98
|
-
return all([value is not None for _, value in self.work_fields.items()])
|
99
|
-
|
100
|
-
@property
|
101
|
-
def workable(self) -> bool:
|
102
|
-
|
103
|
-
if self.filled:
|
104
|
-
# _logging.info("The report is already filled, no need to work on it.")
|
105
|
-
return False
|
106
|
-
|
107
|
-
for i in self.input_fields:
|
108
|
-
if not getattr(self, i, None):
|
109
|
-
# _logging.error(f"Field '{i}' is required to work on the report.")
|
110
|
-
return False
|
111
|
-
|
112
|
-
# this is the required fields from report's own assignment
|
113
|
-
fields = self.input_fields
|
114
|
-
fields.extend(self.output_fields)
|
115
|
-
|
116
|
-
# if the report's own assignment is not in the forms, return False
|
117
|
-
for f in fields:
|
118
|
-
if f not in self.work_fields:
|
119
|
-
# _logging.error(f"Field {f} is a required deliverable, not found in work field.")
|
120
|
-
return False
|
121
|
-
|
122
|
-
# get all the output fields from all the forms
|
123
|
-
outs = []
|
124
|
-
for form in self.forms.values():
|
125
|
-
outs.extend(form.output_fields)
|
126
|
-
|
127
|
-
# all output fields should be unique, not a single output field should be
|
128
|
-
# calculated by more than one form
|
129
|
-
if len(outs) != len(set(outs)):
|
130
|
-
# _logging.error("There are duplicate output fields in the forms.")
|
131
|
-
return False
|
132
|
-
|
133
|
-
return True
|
134
|
-
|
135
|
-
@property
|
136
|
-
def next_forms(self) -> list[Form] | None:
|
137
|
-
a = [i for i in self.forms.values() if i.workable]
|
138
|
-
return a if len(a) > 0 else None
|
@@ -1,47 +0,0 @@
|
|
1
|
-
from lionagi.libs import convert
|
2
|
-
|
3
|
-
system_fields = [
|
4
|
-
"id_",
|
5
|
-
"node_id",
|
6
|
-
"meta",
|
7
|
-
"metadata",
|
8
|
-
"timestamp",
|
9
|
-
"content",
|
10
|
-
"assignment",
|
11
|
-
"assignments",
|
12
|
-
"task",
|
13
|
-
"template_name",
|
14
|
-
"version",
|
15
|
-
"description",
|
16
|
-
"in_validation_kwargs",
|
17
|
-
"out_validation_kwargs",
|
18
|
-
"fix_input",
|
19
|
-
"fix_output",
|
20
|
-
"input_fields",
|
21
|
-
"output_fields",
|
22
|
-
"choices",
|
23
|
-
"prompt_fields",
|
24
|
-
"prompt_fields_annotation",
|
25
|
-
"instruction_context",
|
26
|
-
"instruction",
|
27
|
-
"instruction_output_fields",
|
28
|
-
"inputs",
|
29
|
-
"outputs",
|
30
|
-
"process",
|
31
|
-
"_validate_field",
|
32
|
-
"_process_input",
|
33
|
-
"_process_response",
|
34
|
-
"_validate_field_choices",
|
35
|
-
"_validate_input_choices",
|
36
|
-
"_validate_output_choices",
|
37
|
-
]
|
38
|
-
|
39
|
-
|
40
|
-
def get_input_output_fields(str_: str) -> list[list[str]]:
|
41
|
-
|
42
|
-
inputs, outputs = str_.split("->")
|
43
|
-
|
44
|
-
input_fields = [convert.strip_lower(i) for i in inputs.split(",")]
|
45
|
-
output_fields = [convert.strip_lower(o) for o in outputs.split(",")]
|
46
|
-
|
47
|
-
return input_fields, output_fields
|
@@ -1,43 +0,0 @@
|
|
1
|
-
from typing import Any, Callable
|
2
|
-
from pydantic import BaseModel, Field, field_serializer
|
3
|
-
from functools import singledispatchmethod
|
4
|
-
from lionagi.libs import convert
|
5
|
-
|
6
|
-
|
7
|
-
class FunctionCalling(BaseModel):
|
8
|
-
func: Any = Field(..., alias="function")
|
9
|
-
kwargs: Any = Field({}, alias="arguments")
|
10
|
-
|
11
|
-
@field_serializer("func")
|
12
|
-
def serialize_func(self, func: Callable):
|
13
|
-
return func.__name__
|
14
|
-
|
15
|
-
@property
|
16
|
-
def func_name(self):
|
17
|
-
return self.func.__name__
|
18
|
-
|
19
|
-
@classmethod
|
20
|
-
@singledispatchmethod
|
21
|
-
def create(cls, func_call: Any):
|
22
|
-
raise TypeError(f"Unsupported type {type(func_call)}")
|
23
|
-
|
24
|
-
@create.register
|
25
|
-
def _(cls, func_call: tuple):
|
26
|
-
if len(func_call) == 2:
|
27
|
-
return cls(func=func_call[0], kwargs=func_call[1])
|
28
|
-
else:
|
29
|
-
raise ValueError(f"Invalid tuple length {len(func_call)}")
|
30
|
-
|
31
|
-
@create.register
|
32
|
-
def _(cls, func_call: dict):
|
33
|
-
return cls(**func_call)
|
34
|
-
|
35
|
-
@create.register
|
36
|
-
def _(cls, func_call: str):
|
37
|
-
try:
|
38
|
-
return cls(**convert.to_dict(func_call))
|
39
|
-
except Exception as e:
|
40
|
-
raise ValueError(f"Invalid string {func_call}") from e
|
41
|
-
|
42
|
-
def __str__(self):
|
43
|
-
return f"{self.func_name}({self.kwargs})"
|
@@ -1,66 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
from typing import Dict, Union, Callable, Any
|
3
|
-
|
4
|
-
|
5
|
-
class BaseManual:
|
6
|
-
def __init__(self, template_str: str):
|
7
|
-
self.template_str = template_str
|
8
|
-
|
9
|
-
def _evaluate_condition(self, match, context):
|
10
|
-
condition, text = match.groups()
|
11
|
-
# Future implementations might parse and evaluate the condition more thoroughly
|
12
|
-
return text if condition in context and context[condition] else ""
|
13
|
-
|
14
|
-
def _render_conditionals(self, context: Dict[str, Union[str, int, float]]) -> str:
|
15
|
-
conditional_pattern = re.compile(r"\{if (.*?)\}(.*?)\{endif\}", re.DOTALL)
|
16
|
-
return conditional_pattern.sub(
|
17
|
-
lambda match: self._evaluate_condition(match, context), self.template_str
|
18
|
-
)
|
19
|
-
|
20
|
-
def _replace_callable(self, match, context):
|
21
|
-
key = match.group(1)
|
22
|
-
if key in context:
|
23
|
-
value = context[key]
|
24
|
-
return str(value() if callable(value) else value)
|
25
|
-
return match.group(0) # Unmatched placeholders remain unchanged.
|
26
|
-
|
27
|
-
def _render_placeholders(
|
28
|
-
self,
|
29
|
-
rendered_template: str,
|
30
|
-
context: Dict[str, Union[str, int, float, Callable]],
|
31
|
-
) -> str:
|
32
|
-
return re.sub(
|
33
|
-
r"\{(\w+)\}",
|
34
|
-
lambda match: self._replace_callable(match, context),
|
35
|
-
rendered_template,
|
36
|
-
)
|
37
|
-
|
38
|
-
def generate(self, context: Dict[str, Union[str, int, float, Callable]]) -> str:
|
39
|
-
"""
|
40
|
-
Generates output by first processing conditionals, then rendering placeholders,
|
41
|
-
including executing callable objects for dynamic data generation.
|
42
|
-
"""
|
43
|
-
template_with_conditionals = self._render_conditionals(context)
|
44
|
-
final_output = self._render_placeholders(template_with_conditionals, context)
|
45
|
-
return final_output
|
46
|
-
|
47
|
-
|
48
|
-
# from experiments.executor.executor import SafeEvaluator
|
49
|
-
|
50
|
-
# class DecisionTreeManual:
|
51
|
-
# def __init__(self, root):
|
52
|
-
# self.root = root
|
53
|
-
# self.evaluator = SafeEvaluator()
|
54
|
-
|
55
|
-
# def evaluate(self, context):
|
56
|
-
# return self._traverse_tree(self.root, context)
|
57
|
-
|
58
|
-
# def _traverse_tree(self, node, context):
|
59
|
-
# if isinstance(node, CompositeActionNode) or isinstance(node, ActionNode):
|
60
|
-
# return node.execute(context)
|
61
|
-
# elif isinstance(node, DecisionNode):
|
62
|
-
# condition_result = self.evaluator.evaluate(node.condition, context)
|
63
|
-
# next_node = node.true_branch if condition_result else node.false_branch
|
64
|
-
# return self._traverse_tree(next_node, context)
|
65
|
-
# else:
|
66
|
-
# raise ValueError("Invalid node type.")
|