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.
Files changed (268) hide show
  1. lionagi/__init__.py +61 -3
  2. lionagi/core/__init__.py +0 -14
  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/__init__.py +0 -3
  11. lionagi/core/agent/base_agent.py +45 -36
  12. lionagi/core/agent/eval/evaluator.py +1 -0
  13. lionagi/core/agent/eval/vote.py +40 -0
  14. lionagi/core/agent/learn/learner.py +59 -0
  15. lionagi/core/agent/plan/unit_template.py +1 -0
  16. lionagi/core/collections/__init__.py +17 -0
  17. lionagi/core/collections/_logger.py +319 -0
  18. lionagi/core/collections/abc/__init__.py +53 -0
  19. lionagi/core/collections/abc/component.py +615 -0
  20. lionagi/core/collections/abc/concepts.py +297 -0
  21. lionagi/core/collections/abc/exceptions.py +150 -0
  22. lionagi/core/collections/abc/util.py +45 -0
  23. lionagi/core/collections/exchange.py +161 -0
  24. lionagi/core/collections/flow.py +426 -0
  25. lionagi/core/collections/model.py +419 -0
  26. lionagi/core/collections/pile.py +913 -0
  27. lionagi/core/collections/progression.py +236 -0
  28. lionagi/core/collections/util.py +64 -0
  29. lionagi/core/director/direct.py +314 -0
  30. lionagi/core/director/director.py +2 -0
  31. lionagi/core/engine/branch_engine.py +333 -0
  32. lionagi/core/engine/instruction_map_engine.py +204 -0
  33. lionagi/core/engine/sandbox_.py +14 -0
  34. lionagi/core/engine/script_engine.py +99 -0
  35. lionagi/core/executor/base_executor.py +90 -0
  36. lionagi/core/executor/graph_executor.py +330 -0
  37. lionagi/core/executor/neo4j_executor.py +384 -0
  38. lionagi/core/generic/__init__.py +7 -0
  39. lionagi/core/generic/edge.py +112 -0
  40. lionagi/core/generic/edge_condition.py +16 -0
  41. lionagi/core/generic/graph.py +236 -0
  42. lionagi/core/generic/hyperedge.py +1 -0
  43. lionagi/core/generic/node.py +220 -0
  44. lionagi/core/generic/tree.py +48 -0
  45. lionagi/core/generic/tree_node.py +79 -0
  46. lionagi/core/mail/__init__.py +7 -3
  47. lionagi/core/mail/mail.py +25 -0
  48. lionagi/core/mail/mail_manager.py +142 -58
  49. lionagi/core/mail/package.py +45 -0
  50. lionagi/core/mail/start_mail.py +36 -0
  51. lionagi/core/message/__init__.py +19 -0
  52. lionagi/core/message/action_request.py +133 -0
  53. lionagi/core/message/action_response.py +135 -0
  54. lionagi/core/message/assistant_response.py +95 -0
  55. lionagi/core/message/instruction.py +234 -0
  56. lionagi/core/message/message.py +101 -0
  57. lionagi/core/message/system.py +86 -0
  58. lionagi/core/message/util.py +283 -0
  59. lionagi/core/report/__init__.py +4 -0
  60. lionagi/core/report/base.py +217 -0
  61. lionagi/core/report/form.py +231 -0
  62. lionagi/core/report/report.py +166 -0
  63. lionagi/core/report/util.py +28 -0
  64. lionagi/core/rule/__init__.py +0 -0
  65. lionagi/core/rule/_default.py +16 -0
  66. lionagi/core/rule/action.py +99 -0
  67. lionagi/core/rule/base.py +238 -0
  68. lionagi/core/rule/boolean.py +56 -0
  69. lionagi/core/rule/choice.py +47 -0
  70. lionagi/core/rule/mapping.py +96 -0
  71. lionagi/core/rule/number.py +71 -0
  72. lionagi/core/rule/rulebook.py +109 -0
  73. lionagi/core/rule/string.py +52 -0
  74. lionagi/core/rule/util.py +35 -0
  75. lionagi/core/session/__init__.py +0 -3
  76. lionagi/core/session/branch.py +431 -0
  77. lionagi/core/session/directive_mixin.py +287 -0
  78. lionagi/core/session/session.py +230 -902
  79. lionagi/core/structure/__init__.py +1 -0
  80. lionagi/core/structure/chain.py +1 -0
  81. lionagi/core/structure/forest.py +1 -0
  82. lionagi/core/structure/graph.py +1 -0
  83. lionagi/core/structure/tree.py +1 -0
  84. lionagi/core/unit/__init__.py +5 -0
  85. lionagi/core/unit/parallel_unit.py +245 -0
  86. lionagi/core/unit/template/__init__.py +0 -0
  87. lionagi/core/unit/template/action.py +81 -0
  88. lionagi/core/unit/template/base.py +51 -0
  89. lionagi/core/unit/template/plan.py +84 -0
  90. lionagi/core/unit/template/predict.py +109 -0
  91. lionagi/core/unit/template/score.py +124 -0
  92. lionagi/core/unit/template/select.py +104 -0
  93. lionagi/core/unit/unit.py +362 -0
  94. lionagi/core/unit/unit_form.py +305 -0
  95. lionagi/core/unit/unit_mixin.py +1168 -0
  96. lionagi/core/unit/util.py +71 -0
  97. lionagi/core/validator/__init__.py +0 -0
  98. lionagi/core/validator/validator.py +364 -0
  99. lionagi/core/work/__init__.py +0 -0
  100. lionagi/core/work/work.py +76 -0
  101. lionagi/core/work/work_function.py +101 -0
  102. lionagi/core/work/work_queue.py +103 -0
  103. lionagi/core/work/worker.py +258 -0
  104. lionagi/core/work/worklog.py +120 -0
  105. lionagi/experimental/__init__.py +0 -0
  106. lionagi/experimental/compressor/__init__.py +0 -0
  107. lionagi/experimental/compressor/base.py +46 -0
  108. lionagi/experimental/compressor/llm_compressor.py +247 -0
  109. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  110. lionagi/experimental/compressor/util.py +70 -0
  111. lionagi/experimental/directive/__init__.py +19 -0
  112. lionagi/experimental/directive/parser/__init__.py +0 -0
  113. lionagi/experimental/directive/parser/base_parser.py +282 -0
  114. lionagi/experimental/directive/template/__init__.py +0 -0
  115. lionagi/experimental/directive/template/base_template.py +79 -0
  116. lionagi/experimental/directive/template/schema.py +36 -0
  117. lionagi/experimental/directive/tokenizer.py +73 -0
  118. lionagi/experimental/evaluator/__init__.py +0 -0
  119. lionagi/experimental/evaluator/ast_evaluator.py +131 -0
  120. lionagi/experimental/evaluator/base_evaluator.py +218 -0
  121. lionagi/experimental/knowledge/__init__.py +0 -0
  122. lionagi/experimental/knowledge/base.py +10 -0
  123. lionagi/experimental/knowledge/graph.py +0 -0
  124. lionagi/experimental/memory/__init__.py +0 -0
  125. lionagi/experimental/strategies/__init__.py +0 -0
  126. lionagi/experimental/strategies/base.py +1 -0
  127. lionagi/integrations/bridge/autogen_/__init__.py +0 -0
  128. lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
  129. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  130. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  131. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  132. lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
  133. lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
  134. lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
  135. lionagi/integrations/bridge/transformers_/__init__.py +0 -0
  136. lionagi/integrations/bridge/transformers_/install_.py +36 -0
  137. lionagi/integrations/chunker/__init__.py +0 -0
  138. lionagi/integrations/chunker/chunk.py +312 -0
  139. lionagi/integrations/config/oai_configs.py +38 -7
  140. lionagi/integrations/config/ollama_configs.py +1 -1
  141. lionagi/integrations/config/openrouter_configs.py +14 -2
  142. lionagi/integrations/loader/__init__.py +0 -0
  143. lionagi/integrations/loader/load.py +253 -0
  144. lionagi/integrations/loader/load_util.py +195 -0
  145. lionagi/integrations/provider/_mapping.py +46 -0
  146. lionagi/integrations/provider/litellm.py +2 -1
  147. lionagi/integrations/provider/mlx_service.py +16 -9
  148. lionagi/integrations/provider/oai.py +91 -4
  149. lionagi/integrations/provider/ollama.py +7 -6
  150. lionagi/integrations/provider/openrouter.py +115 -8
  151. lionagi/integrations/provider/services.py +2 -2
  152. lionagi/integrations/provider/transformers.py +18 -22
  153. lionagi/integrations/storage/__init__.py +3 -0
  154. lionagi/integrations/storage/neo4j.py +665 -0
  155. lionagi/integrations/storage/storage_util.py +287 -0
  156. lionagi/integrations/storage/structure_excel.py +285 -0
  157. lionagi/integrations/storage/to_csv.py +63 -0
  158. lionagi/integrations/storage/to_excel.py +83 -0
  159. lionagi/libs/__init__.py +26 -1
  160. lionagi/libs/ln_api.py +78 -23
  161. lionagi/libs/ln_context.py +37 -0
  162. lionagi/libs/ln_convert.py +21 -9
  163. lionagi/libs/ln_func_call.py +69 -28
  164. lionagi/libs/ln_image.py +107 -0
  165. lionagi/libs/ln_knowledge_graph.py +405 -0
  166. lionagi/libs/ln_nested.py +26 -11
  167. lionagi/libs/ln_parse.py +110 -14
  168. lionagi/libs/ln_queue.py +117 -0
  169. lionagi/libs/ln_tokenize.py +164 -0
  170. lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
  171. lionagi/libs/special_tokens.py +172 -0
  172. lionagi/libs/sys_util.py +107 -2
  173. lionagi/lions/__init__.py +0 -0
  174. lionagi/lions/coder/__init__.py +0 -0
  175. lionagi/lions/coder/add_feature.py +20 -0
  176. lionagi/lions/coder/base_prompts.py +22 -0
  177. lionagi/lions/coder/code_form.py +13 -0
  178. lionagi/lions/coder/coder.py +168 -0
  179. lionagi/lions/coder/util.py +96 -0
  180. lionagi/lions/researcher/__init__.py +0 -0
  181. lionagi/lions/researcher/data_source/__init__.py +0 -0
  182. lionagi/lions/researcher/data_source/finhub_.py +191 -0
  183. lionagi/lions/researcher/data_source/google_.py +199 -0
  184. lionagi/lions/researcher/data_source/wiki_.py +96 -0
  185. lionagi/lions/researcher/data_source/yfinance_.py +21 -0
  186. lionagi/tests/integrations/__init__.py +0 -0
  187. lionagi/tests/libs/__init__.py +0 -0
  188. lionagi/tests/libs/test_field_validators.py +353 -0
  189. lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
  190. lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
  191. lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
  192. lionagi/tests/libs/test_queue.py +67 -0
  193. lionagi/tests/test_core/collections/__init__.py +0 -0
  194. lionagi/tests/test_core/collections/test_component.py +206 -0
  195. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  196. lionagi/tests/test_core/collections/test_flow.py +145 -0
  197. lionagi/tests/test_core/collections/test_pile.py +171 -0
  198. lionagi/tests/test_core/collections/test_progression.py +129 -0
  199. lionagi/tests/test_core/generic/__init__.py +0 -0
  200. lionagi/tests/test_core/generic/test_edge.py +67 -0
  201. lionagi/tests/test_core/generic/test_graph.py +96 -0
  202. lionagi/tests/test_core/generic/test_node.py +106 -0
  203. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  204. lionagi/tests/test_core/test_branch.py +115 -292
  205. lionagi/tests/test_core/test_form.py +46 -0
  206. lionagi/tests/test_core/test_report.py +105 -0
  207. lionagi/tests/test_core/test_validator.py +111 -0
  208. lionagi/version.py +1 -1
  209. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
  210. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
  211. lionagi-0.2.1.dist-info/RECORD +240 -0
  212. lionagi/core/branch/__init__.py +0 -4
  213. lionagi/core/branch/base_branch.py +0 -654
  214. lionagi/core/branch/branch.py +0 -471
  215. lionagi/core/branch/branch_flow_mixin.py +0 -96
  216. lionagi/core/branch/executable_branch.py +0 -347
  217. lionagi/core/branch/util.py +0 -323
  218. lionagi/core/direct/__init__.py +0 -6
  219. lionagi/core/direct/predict.py +0 -161
  220. lionagi/core/direct/score.py +0 -278
  221. lionagi/core/direct/select.py +0 -169
  222. lionagi/core/direct/utils.py +0 -87
  223. lionagi/core/direct/vote.py +0 -64
  224. lionagi/core/flow/base/baseflow.py +0 -23
  225. lionagi/core/flow/monoflow/ReAct.py +0 -238
  226. lionagi/core/flow/monoflow/__init__.py +0 -9
  227. lionagi/core/flow/monoflow/chat.py +0 -95
  228. lionagi/core/flow/monoflow/chat_mixin.py +0 -263
  229. lionagi/core/flow/monoflow/followup.py +0 -214
  230. lionagi/core/flow/polyflow/__init__.py +0 -1
  231. lionagi/core/flow/polyflow/chat.py +0 -248
  232. lionagi/core/mail/schema.py +0 -56
  233. lionagi/core/messages/__init__.py +0 -3
  234. lionagi/core/messages/schema.py +0 -533
  235. lionagi/core/prompt/prompt_template.py +0 -316
  236. lionagi/core/schema/__init__.py +0 -22
  237. lionagi/core/schema/action_node.py +0 -29
  238. lionagi/core/schema/base_mixin.py +0 -296
  239. lionagi/core/schema/base_node.py +0 -199
  240. lionagi/core/schema/condition.py +0 -24
  241. lionagi/core/schema/data_logger.py +0 -354
  242. lionagi/core/schema/data_node.py +0 -93
  243. lionagi/core/schema/prompt_template.py +0 -67
  244. lionagi/core/schema/structure.py +0 -910
  245. lionagi/core/tool/__init__.py +0 -3
  246. lionagi/core/tool/tool_manager.py +0 -280
  247. lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
  248. lionagi/tests/test_core/test_base_branch.py +0 -427
  249. lionagi/tests/test_core/test_chat_flow.py +0 -63
  250. lionagi/tests/test_core/test_mail_manager.py +0 -75
  251. lionagi/tests/test_core/test_prompts.py +0 -51
  252. lionagi/tests/test_core/test_session.py +0 -254
  253. lionagi/tests/test_core/test_session_base_util.py +0 -312
  254. lionagi/tests/test_core/test_tool_manager.py +0 -95
  255. lionagi-0.0.312.dist-info/RECORD +0 -111
  256. /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
  257. /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
  258. /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
  259. /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
  260. /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
  261. /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
  262. /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
  263. /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
  264. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  265. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  266. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  267. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,103 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ import asyncio
18
+ from lionagi.core.work.work import WorkStatus
19
+
20
+
21
+ class WorkQueue:
22
+ """
23
+ A class representing a queue for managing work.
24
+
25
+ Attributes:
26
+ capacity (int): The maximum number of tasks the queue can handle.
27
+ queue (asyncio.Queue): The queue holding the tasks.
28
+ _stop_event (asyncio.Event): Event to signal stopping `execute` of the queue.
29
+ available_capacity (int): The remaining number of tasks the queue can handle.
30
+ execution_mode (bool): If `execute` is running.
31
+ """
32
+
33
+ def __init__(self, capacity=5):
34
+ if capacity < 0:
35
+ raise ValueError("initial capacity must be >= 0")
36
+ self.capacity = capacity
37
+ self.queue = asyncio.Queue()
38
+ self._stop_event = asyncio.Event()
39
+ self.available_capacity = capacity
40
+ self.execution_mode = False
41
+
42
+ async def enqueue(self, work) -> None:
43
+ """Enqueue a work item."""
44
+ await self.queue.put(work)
45
+
46
+ async def dequeue(self):
47
+ """Dequeue a work item."""
48
+ return await self.queue.get()
49
+
50
+ async def join(self) -> None:
51
+ """Block until all items in the queue have been processed."""
52
+ await self.queue.join()
53
+
54
+ async def stop(self) -> None:
55
+ """Signal the queue to stop processing."""
56
+ self._stop_event.set()
57
+
58
+ @property
59
+ def stopped(self) -> bool:
60
+ """Return whether the queue has been stopped."""
61
+ return self._stop_event.is_set()
62
+
63
+ # async def process(self):
64
+ # async def _parse_work(work):
65
+ # async with self.semaphore:
66
+ # await work.perform()
67
+ #
68
+ # tasks = set()
69
+ # while self.queue.qsize() > 0:
70
+ # next = await self.dequeue()
71
+ # next.status = WorkStatus.IN_PROGRESS
72
+ # task = asyncio.create_task(_parse_work(next))
73
+ # tasks.add(task)
74
+ #
75
+ # await asyncio.wait(tasks)
76
+
77
+ async def process(self) -> None:
78
+ """Process the work items in the queue."""
79
+ tasks = set()
80
+ while self.available_capacity > 0 and self.queue.qsize() > 0:
81
+ next = await self.dequeue()
82
+ next.status = WorkStatus.IN_PROGRESS
83
+ task = asyncio.create_task(next.perform())
84
+ tasks.add(task)
85
+ self.available_capacity -= 1
86
+
87
+ if tasks:
88
+ await asyncio.wait(tasks)
89
+ self.available_capacity = self.capacity
90
+
91
+ async def execute(self, refresh_time=1):
92
+ """
93
+ Continuously executes the process method at a specified refresh interval.
94
+
95
+ Args:
96
+ refresh_time (int, optional): The time in seconds to wait between
97
+ successive calls to `process`. Defaults to 1.
98
+ """
99
+ self.execution_mode = True
100
+ while not self.stopped:
101
+ await self.process()
102
+ await asyncio.sleep(refresh_time)
103
+ self.execution_mode = False
@@ -0,0 +1,258 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from abc import ABC
18
+ from functools import wraps
19
+ from typing import Callable
20
+ import asyncio
21
+ import inspect
22
+ from lionagi import logging as _logging
23
+ from lionagi.libs.ln_func_call import pcall
24
+ from lionagi.core.work.work_function import WorkFunction
25
+ from lionagi.core.work.work import Work
26
+ from lionagi.core.report.form import Form
27
+ from lionagi.core.collections.abc import get_lion_id
28
+
29
+
30
+ class Worker(ABC):
31
+ """
32
+ This class represents a worker that handles multiple work functions.
33
+
34
+ Attributes:
35
+ name (str): The name of the worker.
36
+ work_functions (dict[str, WorkFunction]): Dictionary mapping assignments to WorkFunction objects.
37
+ forms (dict[str, Form]): Dictionary mapping form identifier to Form objects.
38
+ default_form (str|None): The default form to be used by the worker.
39
+ """
40
+
41
+ name: str = "Worker"
42
+ work_functions: dict[str, WorkFunction] = {}
43
+
44
+ def __init__(self, forms=None, default_form=None) -> None:
45
+ # self.stopped = False
46
+ self.forms: dict[str, Form] = forms or {}
47
+ self.default_form = default_form
48
+
49
+ async def stop(self):
50
+ """
51
+ Stops the worker and all associated work functions.
52
+ """
53
+ # self.stopped = True
54
+ _logging.info(f"Stopping worker {self.name}")
55
+ non_stopped_ = []
56
+
57
+ for func in self.work_functions.values():
58
+ worklog = func.worklog
59
+ await worklog.stop()
60
+ if not worklog.stopped:
61
+ non_stopped_.append(func.name)
62
+
63
+ if len(non_stopped_) > 0:
64
+ _logging.error(f"Could not stop worklogs: {non_stopped_}")
65
+ _logging.info(f"Stopped worker {self.name}")
66
+
67
+ async def is_progressable(self):
68
+ """
69
+ Checks if any work function is progressable and the worker is not stopped.
70
+
71
+ Returns:
72
+ bool: True if any work function is progressable and the worker is not stopped, else False.
73
+ """
74
+
75
+ return (
76
+ any([await i.is_progressable() for i in self.work_functions.values()])
77
+ and not self.stopped
78
+ )
79
+
80
+ async def change_default_form(self, form_key):
81
+ """
82
+ Changes the default form to the specified form key.
83
+
84
+ Args:
85
+ form_key (str): The key of the form to set as the default.
86
+
87
+ Raises:
88
+ ValueError: If the form key does not exist in the forms dictionary.
89
+
90
+ """
91
+ if form_key not in self.forms.keys():
92
+ raise ValueError(f"Unable to change default form. Key {form_key} does not exist.")
93
+ self.default_form = self.forms[form_key]
94
+
95
+ # async def process(self, refresh_time=1):
96
+ # """
97
+ # Processes all work functions periodically.
98
+ #
99
+ # Args:
100
+ # refresh_time (int): Time interval between each process cycle.
101
+ # """
102
+ # while await self.is_progressable():
103
+ # await pcall([i.process(refresh_time) for i in self.work_functions.values()])
104
+ # await asyncio.sleep(refresh_time)
105
+
106
+ # TODO: Implement process method
107
+
108
+ # async def process(self, refresh_time=1):
109
+ # while not self.stopped:
110
+ # tasks = [
111
+ # asyncio.create_task(func.process(refresh_time=refresh_time))
112
+ # for func in self.work_functions.values()
113
+ # ]
114
+ # await asyncio.wait(tasks)
115
+ # await asyncio.sleep(refresh_time)
116
+
117
+ async def _wrapper(
118
+ self,
119
+ *args,
120
+ func=None,
121
+ assignment=None,
122
+ form_param_key=None,
123
+ capacity=None,
124
+ retry_kwargs=None,
125
+ guidance=None,
126
+ **kwargs,
127
+ ):
128
+ """
129
+ Internal wrapper to handle work function execution.
130
+
131
+ Args:
132
+ func (Callable): The function to be executed.
133
+ assignment (str): The assignment description.
134
+ form_param_key (str): The key to identify the form parameter in
135
+ the function's signature. This parameter is used to locate and fill
136
+ the appropriate form according to the assignment. Raises an error
137
+ if the form parameter key is not found in the function's signature.
138
+ capacity (int): Capacity for the work log.
139
+ retry_kwargs (dict): Retry arguments for the function.
140
+ guidance (str): Guidance or documentation for the function.
141
+ """
142
+ if getattr(self, "work_functions", None) is None:
143
+ self.work_functions = {}
144
+
145
+ if func.__name__ not in self.work_functions:
146
+ self.work_functions[func.__name__] = WorkFunction(
147
+ assignment=assignment,
148
+ function=func,
149
+ retry_kwargs=retry_kwargs or {},
150
+ guidance=guidance,
151
+ capacity=capacity,
152
+ )
153
+
154
+ work_func: WorkFunction = self.work_functions[func.__name__]
155
+
156
+ # locate form that should be filled according to the assignment
157
+ if form_param_key:
158
+ func_signature = inspect.signature(func)
159
+ if form_param_key not in func_signature.parameters:
160
+ raise KeyError(f"Failed to locate form. \"{form_param_key}\" is not defined in the function.")
161
+ if "self" in func_signature.parameters:
162
+ bound_args = func_signature.bind(None, *args, **kwargs)
163
+ else:
164
+ bound_args = func_signature.bind(*args, **kwargs)
165
+ bound_args.apply_defaults()
166
+ arguments = bound_args.arguments
167
+
168
+ form_key = arguments.get(form_param_key)
169
+ try:
170
+ form_key = get_lion_id(form_key)
171
+ except:
172
+ pass
173
+ form = self.forms.get(form_key) or self.default_form
174
+
175
+ if form:
176
+ subform = form.__class__(assignment=work_func.assignment, task=work_func.guidance)
177
+ for k in subform.input_fields:
178
+ v = getattr(form, k, None)
179
+ setattr(subform, k, v)
180
+ subform.origin = form
181
+ kwargs = {"form": subform} | kwargs
182
+ else:
183
+ raise ValueError(f"Cannot locate form in Worker's forms and default_form is not available.")
184
+
185
+ task = work_func.perform(self, *args, **kwargs)
186
+ work = Work(async_task=task, async_task_name=work_func.name)
187
+ await work_func.worklog.append(work)
188
+ return work
189
+
190
+
191
+ def work(
192
+ assignment=None,
193
+ form_param_key=None,
194
+ capacity=10,
195
+ guidance=None,
196
+ retry_kwargs=None,
197
+ timeout=10,
198
+ ):
199
+ """
200
+ Decorator to mark a method as a work function.
201
+
202
+ Args:
203
+ assignment (str): The assignment description of the work function.
204
+ form_param_key (str): The key to identify the form parameter in
205
+ the function's signature. This parameter is used to locate and fill
206
+ the appropriate form according to the assignment. Raises an error
207
+ if the form parameter key is not found in the function's signature.
208
+ capacity (int): Capacity for the work log.
209
+ guidance (str): Guidance or documentation for the work function.
210
+ retry_kwargs (dict): Retry arguments for the work function.
211
+ timeout (int): Timeout for the work function.
212
+ """
213
+
214
+ def decorator(func):
215
+ @wraps(func)
216
+ async def wrapper(
217
+ self: Worker,
218
+ *args,
219
+ func=func,
220
+ assignment=assignment,
221
+ form_param_key=form_param_key,
222
+ capacity=capacity,
223
+ retry_kwargs=retry_kwargs,
224
+ guidance=guidance,
225
+ **kwargs,
226
+ ):
227
+ retry_kwargs = retry_kwargs or {}
228
+ retry_kwargs["timeout"] = retry_kwargs.get("timeout", timeout)
229
+ return await self._wrapper(
230
+ *args,
231
+ func=func,
232
+ assignment=assignment,
233
+ form_param_key=form_param_key,
234
+ capacity=capacity,
235
+ retry_kwargs=retry_kwargs,
236
+ guidance=guidance,
237
+ **kwargs,
238
+ )
239
+
240
+ return wrapper
241
+
242
+ return decorator
243
+
244
+
245
+ # # Example
246
+ # from lionagi import Session
247
+ # from lionagi.experimental.work.work_function import work
248
+
249
+
250
+ # class MyWorker(Worker):
251
+
252
+ # @work(assignment="instruction, context -> response")
253
+ # async def chat(instruction=None, context=None):
254
+ # session = Session()
255
+ # return await session.chat(instruction=instruction, context=context)
256
+
257
+
258
+ # await a.chat(instruction="Hello", context={})
@@ -0,0 +1,120 @@
1
+ """
2
+ Copyright 2024 HaiyangLi
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ from lionagi.core.collections.abc import Progressable
18
+ from lionagi.core.collections import pile, progression, Pile
19
+ from lionagi.core.work.work import Work, WorkStatus
20
+ from lionagi.core.work.work_queue import WorkQueue
21
+
22
+
23
+ class WorkLog(Progressable):
24
+ """
25
+ A class representing a log of work items.
26
+
27
+ Attributes:
28
+ pile (Pile): A pile containing work items.
29
+ pending (Progression): A progression of pending work items.
30
+ queue (WorkQueue): A queue to manage the execution of work items.
31
+ """
32
+
33
+ def __init__(self, capacity=10, workpile=None):
34
+ """
35
+ Initializes a new instance of WorkLog.
36
+
37
+ Args:
38
+ capacity (int): The capacity of the work queue.
39
+ workpile (Pile, optional): An optional pile of initial work items.
40
+ """
41
+ self.pile = (
42
+ workpile if workpile and isinstance(workpile, Pile) else pile({}, Work)
43
+ )
44
+ self.pending = progression(workpile) if workpile else progression()
45
+ self.queue = WorkQueue(capacity=capacity)
46
+
47
+ async def append(self, work: Work):
48
+ """
49
+ Appends a new work item to the log.
50
+
51
+ Args:
52
+ work (Work): The work item to append.
53
+ """
54
+ self.pile.append(work)
55
+ self.pending.append(work)
56
+
57
+ async def forward(self):
58
+ """
59
+ Forwards pending work items to the queue.
60
+ """
61
+ while len(self.pending) > 0:
62
+ work: Work = self.pile[self.pending.popleft()]
63
+ await self.queue.enqueue(work)
64
+
65
+ async def stop(self):
66
+ """
67
+ Stops the work queue.
68
+ """
69
+ await self.queue.stop()
70
+
71
+ @property
72
+ def pending_work(self):
73
+ """
74
+ Retrieves the pile of pending work items.
75
+
76
+ Returns:
77
+ Pile: A pile of pending work items.
78
+ """
79
+ return pile([i for i in self.pile if i.status == WorkStatus.PENDING])
80
+
81
+ @property
82
+ def stopped(self):
83
+ """
84
+ Checks if the work queue is stopped.
85
+
86
+ Returns:
87
+ bool: True if the work queue is stopped, else False.
88
+ """
89
+ return self.queue.stopped
90
+
91
+ @property
92
+ def completed_work(self):
93
+ """
94
+ Retrieves the pile of completed work items.
95
+
96
+ Returns:
97
+ Pile: A pile of completed work items.
98
+ """
99
+ return pile([i for i in self.pile if i.status == WorkStatus.COMPLETED])
100
+
101
+ def __contains__(self, work):
102
+ """
103
+ Checks if a work item is in the pile.
104
+
105
+ Args:
106
+ work (Work): The work item to check.
107
+
108
+ Returns:
109
+ bool: True if the work item is in the pile, else False.
110
+ """
111
+ return work in self.pile
112
+
113
+ def __iter__(self):
114
+ """
115
+ Returns an iterator over the work pile.
116
+
117
+ Returns:
118
+ Iterator: An iterator over the work pile.
119
+ """
120
+ return iter(self.pile)
File without changes
File without changes
@@ -0,0 +1,46 @@
1
+ from abc import ABC
2
+ from lionagi.core.collections import iModel
3
+
4
+
5
+ class TokenCompressor(ABC):
6
+ """
7
+ NOTICE:
8
+ The token compressor system is inspired by LLMLingua.
9
+ https://github.com/microsoft/LLMLingua
10
+
11
+ MIT License
12
+ Copyright (c) Microsoft Corporation.
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ Authors:
22
+ Huiqiang Jiang, Qianhui Wu, Chin-Yew Lin, Yuqing Yang, Lili Qiu
23
+ @inproceedings{jiang-etal-2023-llmlingua,
24
+ title = "{LLML}ingua: Compressing Prompts for Accelerated Inference of Large Language Models",
25
+ author = "Huiqiang Jiang and Qianhui Wu and Chin-Yew Lin and Yuqing Yang and Lili Qiu",
26
+ booktitle = "Proceedings of the 2023 Conference on Empirical Methods in Natural Language Processing",
27
+ month = dec,
28
+ year = "2023",
29
+ publisher = "Association for Computational Linguistics",
30
+ url = "https://aclanthology.org/2023.emnlp-main.825",
31
+ doi = "10.18653/v1/2023.emnlp-main.825",
32
+ pages = "13358--13376",
33
+ }
34
+
35
+ LionAGI Modifications:
36
+ - Only borrowed the concept of token compression via perplexity
37
+ - Removed the dependency on the LLMLingua library
38
+ - use logprobs from GPT model to calculate perplexity
39
+ - added async ability to the functions
40
+ - used lionagi existing iModel class for API calls
41
+ """
42
+
43
+ def __init__(self, imodel: iModel, tokenizer=None, splitter=None):
44
+ self.imodel = imodel
45
+ self.tokenizer = tokenizer
46
+ self.splitter = splitter