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.
Files changed (268) hide show
  1. lionagi/__init__.py +60 -5
  2. lionagi/core/__init__.py +0 -25
  3. lionagi/core/_setting/_setting.py +59 -0
  4. lionagi/core/action/__init__.py +14 -0
  5. lionagi/core/action/function_calling.py +136 -0
  6. lionagi/core/action/manual.py +1 -0
  7. lionagi/core/action/node.py +109 -0
  8. lionagi/core/action/tool.py +114 -0
  9. lionagi/core/action/tool_manager.py +356 -0
  10. lionagi/core/agent/base_agent.py +27 -13
  11. lionagi/core/agent/eval/evaluator.py +1 -0
  12. lionagi/core/agent/eval/vote.py +40 -0
  13. lionagi/core/agent/learn/learner.py +59 -0
  14. lionagi/core/agent/plan/unit_template.py +1 -0
  15. lionagi/core/collections/__init__.py +17 -0
  16. lionagi/core/{generic/data_logger.py → collections/_logger.py} +69 -55
  17. lionagi/core/collections/abc/__init__.py +53 -0
  18. lionagi/core/collections/abc/component.py +615 -0
  19. lionagi/core/collections/abc/concepts.py +297 -0
  20. lionagi/core/collections/abc/exceptions.py +150 -0
  21. lionagi/core/collections/abc/util.py +45 -0
  22. lionagi/core/collections/exchange.py +161 -0
  23. lionagi/core/collections/flow.py +426 -0
  24. lionagi/core/collections/model.py +419 -0
  25. lionagi/core/collections/pile.py +913 -0
  26. lionagi/core/collections/progression.py +236 -0
  27. lionagi/core/collections/util.py +64 -0
  28. lionagi/core/director/direct.py +314 -0
  29. lionagi/core/director/director.py +2 -0
  30. lionagi/core/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
  31. lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
  32. lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
  33. lionagi/core/executor/base_executor.py +90 -0
  34. lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +62 -66
  35. lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
  36. lionagi/core/generic/__init__.py +3 -33
  37. lionagi/core/generic/edge.py +29 -79
  38. lionagi/core/generic/edge_condition.py +16 -0
  39. lionagi/core/generic/graph.py +236 -0
  40. lionagi/core/generic/hyperedge.py +1 -0
  41. lionagi/core/generic/node.py +156 -221
  42. lionagi/core/generic/tree.py +48 -0
  43. lionagi/core/generic/tree_node.py +79 -0
  44. lionagi/core/mail/__init__.py +12 -0
  45. lionagi/core/mail/mail.py +25 -0
  46. lionagi/core/mail/mail_manager.py +139 -58
  47. lionagi/core/mail/package.py +45 -0
  48. lionagi/core/mail/start_mail.py +36 -0
  49. lionagi/core/message/__init__.py +19 -0
  50. lionagi/core/message/action_request.py +133 -0
  51. lionagi/core/message/action_response.py +135 -0
  52. lionagi/core/message/assistant_response.py +95 -0
  53. lionagi/core/message/instruction.py +234 -0
  54. lionagi/core/message/message.py +101 -0
  55. lionagi/core/message/system.py +86 -0
  56. lionagi/core/message/util.py +283 -0
  57. lionagi/core/report/__init__.py +4 -0
  58. lionagi/core/report/base.py +217 -0
  59. lionagi/core/report/form.py +231 -0
  60. lionagi/core/report/report.py +166 -0
  61. lionagi/core/report/util.py +28 -0
  62. lionagi/core/rule/_default.py +16 -0
  63. lionagi/core/rule/action.py +99 -0
  64. lionagi/core/rule/base.py +238 -0
  65. lionagi/core/rule/boolean.py +56 -0
  66. lionagi/core/rule/choice.py +47 -0
  67. lionagi/core/rule/mapping.py +96 -0
  68. lionagi/core/rule/number.py +71 -0
  69. lionagi/core/rule/rulebook.py +109 -0
  70. lionagi/core/rule/string.py +52 -0
  71. lionagi/core/rule/util.py +35 -0
  72. lionagi/core/session/branch.py +431 -0
  73. lionagi/core/session/directive_mixin.py +287 -0
  74. lionagi/core/session/session.py +229 -903
  75. lionagi/core/structure/__init__.py +1 -0
  76. lionagi/core/structure/chain.py +1 -0
  77. lionagi/core/structure/forest.py +1 -0
  78. lionagi/core/structure/graph.py +1 -0
  79. lionagi/core/structure/tree.py +1 -0
  80. lionagi/core/unit/__init__.py +5 -0
  81. lionagi/core/unit/parallel_unit.py +245 -0
  82. lionagi/core/unit/template/action.py +81 -0
  83. lionagi/core/unit/template/base.py +51 -0
  84. lionagi/core/unit/template/plan.py +84 -0
  85. lionagi/core/unit/template/predict.py +109 -0
  86. lionagi/core/unit/template/score.py +124 -0
  87. lionagi/core/unit/template/select.py +104 -0
  88. lionagi/core/unit/unit.py +362 -0
  89. lionagi/core/unit/unit_form.py +305 -0
  90. lionagi/core/unit/unit_mixin.py +1168 -0
  91. lionagi/core/unit/util.py +71 -0
  92. lionagi/core/validator/validator.py +364 -0
  93. lionagi/core/work/work.py +76 -0
  94. lionagi/core/work/work_function.py +101 -0
  95. lionagi/core/work/work_queue.py +103 -0
  96. lionagi/core/work/worker.py +258 -0
  97. lionagi/core/work/worklog.py +120 -0
  98. lionagi/experimental/compressor/base.py +46 -0
  99. lionagi/experimental/compressor/llm_compressor.py +247 -0
  100. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  101. lionagi/experimental/compressor/util.py +70 -0
  102. lionagi/experimental/directive/__init__.py +19 -0
  103. lionagi/experimental/directive/parser/base_parser.py +69 -2
  104. lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
  105. lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
  106. lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
  107. lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
  108. lionagi/experimental/knowledge/base.py +10 -0
  109. lionagi/experimental/memory/__init__.py +0 -0
  110. lionagi/experimental/strategies/__init__.py +0 -0
  111. lionagi/experimental/strategies/base.py +1 -0
  112. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  113. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  114. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  115. lionagi/integrations/chunker/chunk.py +161 -24
  116. lionagi/integrations/config/oai_configs.py +34 -3
  117. lionagi/integrations/config/openrouter_configs.py +14 -2
  118. lionagi/integrations/loader/load.py +122 -21
  119. lionagi/integrations/loader/load_util.py +6 -77
  120. lionagi/integrations/provider/_mapping.py +46 -0
  121. lionagi/integrations/provider/litellm.py +2 -1
  122. lionagi/integrations/provider/mlx_service.py +16 -9
  123. lionagi/integrations/provider/oai.py +91 -4
  124. lionagi/integrations/provider/ollama.py +6 -5
  125. lionagi/integrations/provider/openrouter.py +115 -8
  126. lionagi/integrations/provider/services.py +2 -2
  127. lionagi/integrations/provider/transformers.py +18 -22
  128. lionagi/integrations/storage/__init__.py +3 -3
  129. lionagi/integrations/storage/neo4j.py +52 -60
  130. lionagi/integrations/storage/storage_util.py +44 -46
  131. lionagi/integrations/storage/structure_excel.py +43 -26
  132. lionagi/integrations/storage/to_excel.py +11 -4
  133. lionagi/libs/__init__.py +22 -1
  134. lionagi/libs/ln_api.py +75 -20
  135. lionagi/libs/ln_context.py +37 -0
  136. lionagi/libs/ln_convert.py +21 -9
  137. lionagi/libs/ln_func_call.py +69 -28
  138. lionagi/libs/ln_image.py +107 -0
  139. lionagi/libs/ln_nested.py +26 -11
  140. lionagi/libs/ln_parse.py +82 -23
  141. lionagi/libs/ln_queue.py +16 -0
  142. lionagi/libs/ln_tokenize.py +164 -0
  143. lionagi/libs/ln_validate.py +16 -0
  144. lionagi/libs/special_tokens.py +172 -0
  145. lionagi/libs/sys_util.py +95 -24
  146. lionagi/lions/coder/code_form.py +13 -0
  147. lionagi/lions/coder/coder.py +50 -3
  148. lionagi/lions/coder/util.py +30 -25
  149. lionagi/tests/libs/test_func_call.py +23 -21
  150. lionagi/tests/libs/test_nested.py +36 -21
  151. lionagi/tests/libs/test_parse.py +1 -1
  152. lionagi/tests/test_core/collections/__init__.py +0 -0
  153. lionagi/tests/test_core/collections/test_component.py +206 -0
  154. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  155. lionagi/tests/test_core/collections/test_flow.py +145 -0
  156. lionagi/tests/test_core/collections/test_pile.py +171 -0
  157. lionagi/tests/test_core/collections/test_progression.py +129 -0
  158. lionagi/tests/test_core/generic/test_edge.py +67 -0
  159. lionagi/tests/test_core/generic/test_graph.py +96 -0
  160. lionagi/tests/test_core/generic/test_node.py +106 -0
  161. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  162. lionagi/tests/test_core/test_branch.py +115 -294
  163. lionagi/tests/test_core/test_form.py +46 -0
  164. lionagi/tests/test_core/test_report.py +105 -0
  165. lionagi/tests/test_core/test_validator.py +111 -0
  166. lionagi/version.py +1 -1
  167. lionagi-0.2.1.dist-info/LICENSE +202 -0
  168. lionagi-0.2.1.dist-info/METADATA +272 -0
  169. lionagi-0.2.1.dist-info/RECORD +240 -0
  170. lionagi/core/branch/base.py +0 -653
  171. lionagi/core/branch/branch.py +0 -474
  172. lionagi/core/branch/flow_mixin.py +0 -96
  173. lionagi/core/branch/util.py +0 -323
  174. lionagi/core/direct/__init__.py +0 -19
  175. lionagi/core/direct/cot.py +0 -123
  176. lionagi/core/direct/plan.py +0 -164
  177. lionagi/core/direct/predict.py +0 -166
  178. lionagi/core/direct/react.py +0 -171
  179. lionagi/core/direct/score.py +0 -279
  180. lionagi/core/direct/select.py +0 -170
  181. lionagi/core/direct/sentiment.py +0 -1
  182. lionagi/core/direct/utils.py +0 -110
  183. lionagi/core/direct/vote.py +0 -64
  184. lionagi/core/execute/base_executor.py +0 -47
  185. lionagi/core/flow/baseflow.py +0 -23
  186. lionagi/core/flow/monoflow/ReAct.py +0 -240
  187. lionagi/core/flow/monoflow/__init__.py +0 -9
  188. lionagi/core/flow/monoflow/chat.py +0 -95
  189. lionagi/core/flow/monoflow/chat_mixin.py +0 -253
  190. lionagi/core/flow/monoflow/followup.py +0 -215
  191. lionagi/core/flow/polyflow/__init__.py +0 -1
  192. lionagi/core/flow/polyflow/chat.py +0 -251
  193. lionagi/core/form/action_form.py +0 -26
  194. lionagi/core/form/field_validator.py +0 -287
  195. lionagi/core/form/form.py +0 -302
  196. lionagi/core/form/mixin.py +0 -214
  197. lionagi/core/form/scored_form.py +0 -13
  198. lionagi/core/generic/action.py +0 -26
  199. lionagi/core/generic/component.py +0 -532
  200. lionagi/core/generic/condition.py +0 -46
  201. lionagi/core/generic/mail.py +0 -90
  202. lionagi/core/generic/mailbox.py +0 -36
  203. lionagi/core/generic/relation.py +0 -70
  204. lionagi/core/generic/signal.py +0 -22
  205. lionagi/core/generic/structure.py +0 -362
  206. lionagi/core/generic/transfer.py +0 -20
  207. lionagi/core/generic/work.py +0 -40
  208. lionagi/core/graph/graph.py +0 -126
  209. lionagi/core/graph/tree.py +0 -190
  210. lionagi/core/mail/schema.py +0 -63
  211. lionagi/core/messages/schema.py +0 -325
  212. lionagi/core/tool/__init__.py +0 -5
  213. lionagi/core/tool/tool.py +0 -28
  214. lionagi/core/tool/tool_manager.py +0 -283
  215. lionagi/experimental/report/form.py +0 -64
  216. lionagi/experimental/report/report.py +0 -138
  217. lionagi/experimental/report/util.py +0 -47
  218. lionagi/experimental/tool/function_calling.py +0 -43
  219. lionagi/experimental/tool/manual.py +0 -66
  220. lionagi/experimental/tool/schema.py +0 -59
  221. lionagi/experimental/tool/tool_manager.py +0 -138
  222. lionagi/experimental/tool/util.py +0 -16
  223. lionagi/experimental/validator/rule.py +0 -139
  224. lionagi/experimental/validator/validator.py +0 -56
  225. lionagi/experimental/work/__init__.py +0 -10
  226. lionagi/experimental/work/async_queue.py +0 -54
  227. lionagi/experimental/work/schema.py +0 -73
  228. lionagi/experimental/work/work_function.py +0 -67
  229. lionagi/experimental/work/worker.py +0 -56
  230. lionagi/experimental/work2/form.py +0 -371
  231. lionagi/experimental/work2/report.py +0 -289
  232. lionagi/experimental/work2/schema.py +0 -30
  233. lionagi/experimental/work2/tests.py +0 -72
  234. lionagi/experimental/work2/work_function.py +0 -89
  235. lionagi/experimental/work2/worker.py +0 -12
  236. lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
  237. lionagi/tests/test_core/generic/test_component.py +0 -89
  238. lionagi/tests/test_core/test_base_branch.py +0 -426
  239. lionagi/tests/test_core/test_chat_flow.py +0 -63
  240. lionagi/tests/test_core/test_mail_manager.py +0 -75
  241. lionagi/tests/test_core/test_prompts.py +0 -51
  242. lionagi/tests/test_core/test_session.py +0 -254
  243. lionagi/tests/test_core/test_session_base_util.py +0 -313
  244. lionagi/tests/test_core/test_tool_manager.py +0 -95
  245. lionagi-0.1.2.dist-info/LICENSE +0 -9
  246. lionagi-0.1.2.dist-info/METADATA +0 -174
  247. lionagi-0.1.2.dist-info/RECORD +0 -206
  248. /lionagi/core/{branch → _setting}/__init__.py +0 -0
  249. /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
  250. /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
  251. /lionagi/core/{form → agent/plan}/__init__.py +0 -0
  252. /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
  253. /lionagi/core/{graph → director}/__init__.py +0 -0
  254. /lionagi/core/{messages → engine}/__init__.py +0 -0
  255. /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
  256. /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
  257. /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
  258. /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
  259. /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
  260. /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
  261. /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
  262. /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
  263. /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
  264. /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
  265. /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
  266. /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
  267. {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,59 +0,0 @@
1
- from typing import Any
2
- from pydantic import field_serializer
3
- from functools import singledispatchmethod
4
- from lionagi import logging as _logging
5
- from lionagi.libs import func_call, AsyncUtil
6
- from lionagi.core.generic.node import Node
7
- from .function_calling import FunctionCalling
8
-
9
-
10
- class Tool(Node):
11
-
12
- func: Any
13
- schema_: dict | None = None
14
- manual: Any | None = None
15
- parser: Any | None = None
16
- pre_processor: Any | None = None
17
- post_processor: Any | None = None
18
-
19
- @property
20
- def name(self):
21
- return self.schema_["function"]["name"]
22
-
23
- @field_serializer("func")
24
- def serialize_func(self, func):
25
- return func.__name__
26
-
27
- @singledispatchmethod
28
- async def invoke(self, values: Any) -> Any:
29
- raise TypeError(f"Unsupported type {type(values)}")
30
-
31
- @invoke.register
32
- async def _(self, kwargs: dict):
33
-
34
- out = None
35
-
36
- if self.pre_processor:
37
- kwargs = await func_call.call_handler(self.pre_processor, kwargs)
38
- try:
39
- out = await func_call.call_handler(self.func, **kwargs)
40
-
41
- except Exception as e:
42
- _logging.error(f"Error invoking function {self.func_name}: {e}")
43
- return None
44
-
45
- if self.post_processor:
46
- return await func_call.call_handler(self.post_processor, out)
47
-
48
- return out
49
-
50
- @invoke.register
51
- async def _(self, function_calls: FunctionCalling):
52
- return await self.invoke(function_calls.kwargs)
53
-
54
- @invoke.register
55
- async def _(self, values: list):
56
- return await func_call.alcall(self.invoke, values)
57
-
58
-
59
- TOOL_TYPE = bool | Tool | str | list[Tool | str | dict] | dict
@@ -1,138 +0,0 @@
1
- import asyncio
2
- from functools import singledispatchmethod
3
- from collections import deque
4
- from typing import Tuple, Any, TypeVar, Callable
5
- from lionagi.libs import func_call, convert, ParseUtil
6
- from lionagi import logging as _logging
7
- from .schema import Tool, TOOL_TYPE
8
- from .util import func_to_tool, parse_tool_response
9
- from .function_calling import FunctionCalling
10
-
11
- T = TypeVar("T", bound=Tool)
12
-
13
-
14
- class ToolManager:
15
-
16
- def __init__(
17
- self,
18
- tool_registry: dict = {},
19
- function_calling_tasks: dict[str : deque[FunctionCalling]] = {},
20
- ):
21
- self.registry = tool_registry
22
- self.function_calling_tasks = function_calling_tasks
23
-
24
- @singledispatchmethod
25
- def register_tools(self, tools: Any):
26
- raise TypeError(f"Unsupported type {type(tools)}")
27
-
28
- @register_tools.register(Tool)
29
- def _(self, tools):
30
- name = tools.schema_["function"]["name"]
31
- if self._has_name(name):
32
- err_msg = f"Function {name} is already registered."
33
- _logging.error(err_msg)
34
- raise ValueError(err_msg)
35
- else:
36
- self.registry[name] = tools
37
- self.function_calling_tasks[name] = deque()
38
- return True
39
-
40
- @register_tools.register(Callable)
41
- def _(self, tools):
42
- tool = func_to_tool(tools)[0]
43
- return self.register_tools(tool)
44
-
45
- @register_tools.register(list)
46
- def _(self, tools):
47
- return func_call.lcall(tools, self.register_tools)
48
-
49
- @singledispatchmethod
50
- def register_function_calling(self, func_params: Any):
51
- raise TypeError(f"Unsupported type {type(func_params)}")
52
-
53
- @register_function_calling.register(tuple)
54
- def _(self, func_params):
55
- func = self.registry[func_params[0]].func
56
- kwargs = func_params[1]
57
- _function_calling = FunctionCalling(func=func, kwargs=kwargs)
58
- self.function_calling_tasks[func.__name__].append(_function_calling)
59
- return True
60
-
61
- @register_function_calling.register(dict)
62
- def _(self, response):
63
- tuple_ = parse_tool_response(response)
64
- return self.register_function_calling(tuple_)
65
-
66
- @register_function_calling.register(list)
67
- def _(self, func_params):
68
- return func_call.lcall(func_params, self.register_function_calling)
69
-
70
- async def invoke(self, func_params: Tuple[str, dict[str, Any]]) -> Any:
71
- name, kwargs = func_params
72
- if not self._has_name(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
- out = await func_call.call_handler(func, **kwargs)
79
- return parser(out) if parser else out
80
-
81
- except Exception as e:
82
- raise ValueError(
83
- f"Error when invoking function {name} with arguments {kwargs} with error message {e}"
84
- ) from e
85
-
86
- @property
87
- def _schema_list(self) -> list[dict[str, Any]]:
88
- return [tool.schema_ for tool in self.registry.values()]
89
-
90
- def get_tool_schema(self, tools: TOOL_TYPE, **kwargs):
91
- if isinstance(tools, bool):
92
- tool_kwarg = {"tools": self._schema_list}
93
- return tool_kwarg | kwargs
94
-
95
- else:
96
- if not isinstance(tools, list):
97
- tools = [tools]
98
- tool_kwarg = {"tools": self._get_tool_schema(tools)}
99
- return tool_kwarg | kwargs
100
-
101
- def _has_name(self, name: str) -> bool:
102
- return name in self.registry
103
-
104
- @singledispatchmethod
105
- def _get_tool_schema(self, tool: Any) -> dict:
106
- raise TypeError(f"Unsupported type {type(tool)}")
107
-
108
- @_get_tool_schema.register(dict)
109
- def _(self, tool):
110
- """
111
- assuming that the tool is a schema
112
- """
113
- return tool
114
-
115
- @_get_tool_schema.register(Tool)
116
- def _(self, tool):
117
- if self._has_name(tool.name):
118
- return self.registry[tool.name].schema_
119
- else:
120
- err_msg = f"Function {tool.name} is not registered."
121
- _logging.error(err_msg)
122
- raise ValueError(err_msg)
123
-
124
- @_get_tool_schema.register(str)
125
- def _(self, tool):
126
- """
127
- assuming that the tool is a name
128
- """
129
- if self._has_name(tool):
130
- return self.registry[tool].schema_
131
- else:
132
- err_msg = f"Function {tool} is not registered."
133
- _logging.error(err_msg)
134
- raise ValueError(err_msg)
135
-
136
- @_get_tool_schema.register(list)
137
- def _(self, tools):
138
- return func_call.lcall(tools, self._get_tool_schema)
@@ -1,16 +0,0 @@
1
- from typing import Tuple
2
- from lionagi.libs import convert
3
-
4
-
5
- def parse_tool_response(response: dict) -> Tuple[str, dict]:
6
- try:
7
- func = response["action"][7:]
8
- args = convert.to_dict(response["arguments"])
9
- return func, args
10
- except Exception:
11
- try:
12
- func = response["recipient_name"].split(".")[-1]
13
- args = response["parameters"]
14
- return func, args
15
- except:
16
- raise ValueError("response is not a valid function call")
@@ -1,139 +0,0 @@
1
- from lionagi.libs import validation_funcs
2
- from abc import abstractmethod
3
-
4
-
5
- class Rule:
6
-
7
- def __init__(self, **kwargs):
8
- self.validation_kwargs = kwargs
9
- self.fix = kwargs.get("fix", False)
10
-
11
- @abstractmethod
12
- def condition(self, **kwargs):
13
- pass
14
-
15
- @abstractmethod
16
- async def validate(self, value, **kwargs):
17
- pass
18
-
19
-
20
- class ChoiceRule(Rule):
21
-
22
- def condition(self, choices=None):
23
- return choices is not None
24
-
25
- def check(self, choices=None):
26
- if choices and not isinstance(choices, list):
27
- try:
28
- choices = [i.value for i in choices]
29
- except Exception as e:
30
- raise ValueError(f"failed to get choices") from e
31
- return choices
32
-
33
- def fix(self, value, choices=None, **kwargs):
34
- v_ = validation_funcs["enum"](value, choices=choices, fix_=True, **kwargs)
35
- return v_
36
-
37
- async def validate(self, value, choices=None, **kwargs):
38
- if self.condition(choices):
39
- if value in self.check(choices):
40
- return value
41
- if self.fix:
42
- kwargs = {**self.validation_kwargs, **kwargs}
43
- return self.fix(value, choices, **kwargs)
44
- raise ValueError(f"{value} is not in chocies {choices}")
45
-
46
-
47
- class ActionRequestRule(Rule):
48
-
49
- def condition(self, annotation=None):
50
- return any("actionrequest" in i for i in annotation)
51
-
52
- async def validate(self, value, annotation=None):
53
- if self.condition(annotation):
54
- try:
55
- return validation_funcs["action"](value)
56
- except Exception as e:
57
- raise ValueError(f"failed to validate field") from e
58
-
59
-
60
- class BooleanRule(Rule):
61
-
62
- def condition(self, annotation=None):
63
- return "bool" in annotation and "str" not in annotation
64
-
65
- async def validate(self, value, annotation=None):
66
- if self.condition(annotation):
67
- try:
68
- return validation_funcs["bool"](
69
- value, fix_=self.fix, **self.validation_kwargs
70
- )
71
- except Exception as e:
72
- raise ValueError(f"failed to validate field") from e
73
-
74
-
75
- class NumberRule(Rule):
76
-
77
- def condition(self, annotation=None):
78
- return (
79
- any([i in annotation for i in ["int", "float", "number"]])
80
- and "str" not in annotation
81
- )
82
-
83
- async def validate(self, value, annotation=None):
84
- if self.condition(annotation):
85
- if "float" in annotation:
86
- self.validation_kwargs["num_type"] = float
87
- if "precision" not in self.validation_kwargs:
88
- self.validation_kwargs["precision"] = 32
89
-
90
- try:
91
- return validation_funcs["number"](
92
- value, fix_=self.fix, **self.validation_kwargs
93
- )
94
- except Exception as e:
95
- raise ValueError(f"failed to validate field") from e
96
-
97
-
98
- class DictRule(Rule):
99
-
100
- def condition(self, annotation=None):
101
- return "dict" in annotation
102
-
103
- async def validate(self, value, annotation=None, keys=None):
104
- if self.condition(annotation):
105
- if "str" not in annotation or keys:
106
- try:
107
- return validation_funcs["dict"](
108
- value, keys=keys, fix_=self.fix, **self.validation_kwargs
109
- )
110
- except Exception as e:
111
- raise ValueError(f"failed to validate field") from e
112
- raise ValueError(f"failed to validate field")
113
-
114
-
115
- class StringRule(Rule):
116
-
117
- def condition(self, annotation=None):
118
- return "str" in annotation
119
-
120
- async def validate(self, value, annotation=None):
121
- if self.condition(annotation):
122
- try:
123
- return validation_funcs["str"](
124
- value, fix_=self.fix, **self.validation_kwargs
125
- )
126
- except Exception as e:
127
- raise ValueError(f"failed to validate field") from e
128
-
129
-
130
- from enum import Enum
131
-
132
-
133
- class DEFAULT_RULES(Enum):
134
- CHOICE = ChoiceRule
135
- ACTION_REQUEST = ActionRequestRule
136
- BOOL = BooleanRule
137
- NUMBER = NumberRule
138
- DICT = DictRule
139
- STR = StringRule
@@ -1,56 +0,0 @@
1
- from pydantic import BaseModel, Field
2
- from .rule import DEFAULT_RULES, Rule
3
-
4
-
5
- rules_ = {
6
- "choice": DEFAULT_RULES.CHOICE.value,
7
- "actionrequest": DEFAULT_RULES.ACTION_REQUEST.value,
8
- "bool": DEFAULT_RULES.BOOL.value,
9
- "number": DEFAULT_RULES.NUMBER.value,
10
- "dict": DEFAULT_RULES.DICT.value,
11
- "str": DEFAULT_RULES.STR.value,
12
- }
13
-
14
- order_ = [
15
- "choice",
16
- "actionrequest",
17
- "bool",
18
- "number",
19
- "dict",
20
- "str",
21
- ]
22
-
23
-
24
- class Validator(BaseModel):
25
- """
26
- rules contain all rules that this validator can apply to data
27
- the order determines which rule gets applied in what sequence.
28
- notice, if a rule is not present in the orders, it will not be applied.
29
- """
30
-
31
- rules: dict[str, Rule] = Field(
32
- default=rules_,
33
- description="The rules to be used for validation.",
34
- )
35
-
36
- order: list[str] = Field(
37
- default=order_,
38
- description="The order in which the rules should be applied.",
39
- )
40
-
41
- async def validate(self, value, *args, strict=False, **kwargs):
42
-
43
- for i in self.order:
44
- if i in self.rules:
45
- try:
46
- if (
47
- a := await self.rules[i].validate(value, *args, **kwargs)
48
- is not None
49
- ):
50
- return a
51
- except Exception as e:
52
- raise ValueError(f"failed to validate field") from e
53
- if strict:
54
- raise ValueError(f"failed to validate field")
55
-
56
- return value
@@ -1,10 +0,0 @@
1
- from .schema import WorkLog
2
- from .work_function import WorkFunction, work
3
- from .worker import Worker
4
-
5
- __all__ = [
6
- "WorkLog",
7
- "WorkFunction",
8
- "work",
9
- "Worker",
10
- ]
@@ -1,54 +0,0 @@
1
- import asyncio
2
-
3
-
4
- class WorkQueue:
5
-
6
- def __init__(self, capacity=5):
7
-
8
- self.queue = asyncio.Queue()
9
- self._stop_event = asyncio.Event()
10
- self.capacity = capacity
11
- self.semaphore = asyncio.Semaphore(capacity)
12
-
13
- async def enqueue(self, work) -> None:
14
- await self.queue.put(work)
15
-
16
- async def dequeue(self):
17
- return await self.queue.get()
18
-
19
- async def join(self) -> None:
20
- await self.queue.join()
21
-
22
- async def stop(self) -> None:
23
- self._stop_event.set()
24
-
25
- @property
26
- def available_capacity(self):
27
- if (a:= self.capacity - self.queue.qsize()) > 0:
28
- return a
29
- return None
30
-
31
- @property
32
- def stopped(self) -> bool:
33
- return self._stop_event.is_set()
34
-
35
-
36
- async def process(self, refresh_time=1) -> None:
37
- tasks = set()
38
- while self.queue.qsize() > 0 and not self.stopped:
39
- if not self.available_capacity and tasks:
40
- _, done = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
41
- tasks.difference_update(done)
42
-
43
- async with self.semaphore:
44
- next = await self.dequeue()
45
- if next is None:
46
- break
47
- task = asyncio.create_task(next.perform())
48
- tasks.add(task)
49
-
50
- if tasks:
51
- await asyncio.wait(tasks)
52
- await asyncio.sleep(refresh_time)
53
-
54
-
@@ -1,73 +0,0 @@
1
- from collections import deque
2
- from enum import Enum
3
- import asyncio
4
- from typing import Any
5
-
6
- from lionagi.libs import SysUtil
7
- from lionagi.core.generic import BaseComponent
8
-
9
- from .async_queue import WorkQueue
10
-
11
- class WorkStatus(str, Enum):
12
- """Enum to represent different statuses of work."""
13
-
14
- PENDING = "PENDING"
15
- IN_PROGRESS = "IN_PROGRESS"
16
- COMPLETED = "COMPLETED"
17
- FAILED = "FAILED"
18
-
19
-
20
- class Work(BaseComponent):
21
- status: WorkStatus = WorkStatus.PENDING
22
- result: Any = None
23
- error: Any = None
24
- async_task: asyncio.Task | None = None
25
- completion_timestamp: str | None = None
26
-
27
- async def perform(self):
28
- try:
29
- result = await self.async_task
30
- self.result = result
31
- self.status = WorkStatus.COMPLETED
32
- self.async_task = None
33
- except Exception as e:
34
- self.error = e
35
- self.status = WorkStatus.FAILED
36
- finally:
37
- self.completion_timestamp = SysUtil.get_timestamp()
38
-
39
-
40
- def __str__(self):
41
- return f"Work(id={self.id_}, status={self.status}, created_at={self.timestamp}, completed_at={self.completion_timestamp})"
42
-
43
- class WorkLog:
44
-
45
- def __init__(self, capacity=5, pile=None):
46
- self.pile = pile or {}
47
- self.pending_sequence = deque()
48
- self.queue = WorkQueue(capacity=capacity)
49
-
50
- async def append(self, work: Work):
51
- self.pile[work.id_] = work
52
- self.pending_sequence.append(work.id_)
53
-
54
- async def forward(self):
55
- if not self.queue.available_capacity:
56
- return
57
- else:
58
- while self.pending_sequence and self.queue.available_capacity:
59
- work = self.pile[self.pending_sequence.popleft()]
60
- work.status = WorkStatus.IN_PROGRESS
61
- await self.queue.enqueue(work)
62
-
63
-
64
- async def stop(self):
65
- await self.queue.stop()
66
-
67
- @property
68
- def stopped(self):
69
- return self.queue.stopped
70
-
71
- @property
72
- def completed_work(self):
73
- return {k: v for k, v in self.pile.items() if v.status == WorkStatus.COMPLETED}
@@ -1,67 +0,0 @@
1
- import asyncio
2
- from typing import Callable, Any
3
- from lionagi.libs import func_call
4
- from functools import wraps
5
- from pydantic import Field
6
-
7
- from lionagi.core.generic import BaseComponent
8
-
9
- from .schema import Work, WorkLog
10
-
11
-
12
-
13
- class WorkFunction:
14
-
15
- def __init__(
16
- self, assignment, function, retry_kwargs=None,
17
- instruction = None, capacity=5
18
- ):
19
-
20
- self.assignment = assignment
21
- self.function = function
22
- self.retry_kwargs = retry_kwargs or {}
23
- self.instruction = instruction or function.__doc__
24
- self.worklog = WorkLog(capacity=capacity)
25
-
26
-
27
- @property
28
- def name(self):
29
- return self.function.__name__
30
-
31
- async def perform(self, *args, **kwargs):
32
- kwargs = {**self.retry_kwargs, **kwargs}
33
- return await func_call.rcall(self.function, *args, **kwargs)
34
-
35
- async def process(self, refresh_time=1):
36
- await self.worklog.process(refresh_time=refresh_time)
37
-
38
- async def stop(self):
39
- await self.worklog.queue.stop()
40
-
41
-
42
-
43
- def work(assignment, capacity=5):
44
- def decorator(func):
45
- @wraps(func)
46
- async def wrapper(self, *args, retry_kwargs=None, instruction=None, **kwargs):
47
- if getattr(self, "work_functions", None) is None:
48
- self.work_functions = {}
49
-
50
- if func.__name__ not in self.work_functions:
51
- self.work_functions[func.__name__] = WorkFunction(
52
- assignment=assignment,
53
- function=func,
54
- retry_kwargs=retry_kwargs or {},
55
- instruction=instruction or func.__doc__,
56
- capacity=capacity
57
- )
58
-
59
- work_func: WorkFunction = self.work_functions[func.__name__]
60
- task = asyncio.create_task(work_func.perform(*args, **kwargs))
61
- work = Work(async_task=task)
62
- work_func: WorkFunction = self.work_functions[func.__name__]
63
- await work_func.worklog.append(work)
64
- return True
65
-
66
- return wrapper
67
- return decorator
@@ -1,56 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from lionagi import logging as _logging
3
- from .work_function import WorkFunction
4
- import asyncio
5
-
6
- class Worker(ABC):
7
- # This is a class that will be used to create a worker object
8
- # work_functions are keyed by assignment {assignment: WorkFunction}
9
-
10
- name: str = "Worker"
11
- work_functions: dict[str, WorkFunction] = {}
12
-
13
- def __init__(self) -> None:
14
- self.stopped = False
15
-
16
- async def stop(self):
17
- self.stopped = True
18
- _logging.info(f"Stopping worker {self.name}")
19
- non_stopped_ = []
20
-
21
- for func in self.work_functions.values():
22
- worklog = func.worklog
23
- await worklog.stop()
24
- if not worklog.stopped:
25
- non_stopped_.append(func.name)
26
-
27
- if len(non_stopped_) > 0:
28
- _logging.error(f"Could not stop worklogs: {non_stopped_}")
29
-
30
- _logging.info(f"Stopped worker {self.name}")
31
-
32
-
33
- async def process(self, refresh_time=1):
34
- while not self.stopped:
35
- tasks = [
36
- asyncio.create_task(func.process(refresh_time=refresh_time))
37
- for func in self.work_functions.values()
38
- ]
39
- await asyncio.wait(tasks)
40
- await asyncio.sleep(refresh_time)
41
-
42
-
43
- # # Example
44
- # from lionagi import Session
45
- # from lionagi.experimental.work.work_function import work
46
-
47
-
48
- # class MyWorker(Worker):
49
-
50
- # @work(assignment="instruction, context -> response")
51
- # async def chat(instruction=None, context=None):
52
- # session = Session()
53
- # return await session.chat(instruction=instruction, context=context)
54
-
55
-
56
- # await a.chat(instruction="Hello", context={})