lionagi 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +74 -0
  94. lionagi/core/work/work_function.py +92 -0
  95. lionagi/core/work/work_queue.py +81 -0
  96. lionagi/core/work/worker.py +195 -0
  97. lionagi/core/work/worklog.py +124 -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.0.dist-info/LICENSE +202 -0
  168. lionagi-0.2.0.dist-info/METADATA +272 -0
  169. lionagi-0.2.0.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.0.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,14 @@ from lionagi.libs import SysUtil
4
4
  from lionagi.integrations.storage.storage_util import output_node_list, output_edge_list
5
5
 
6
6
 
7
- def _output_excel(node_list, node_dict, edge_list, edge_cls_list, structure_name, dir="structure_storage"):
7
+ def _output_excel(
8
+ node_list,
9
+ node_dict,
10
+ edge_list,
11
+ edge_cls_list,
12
+ structure_name,
13
+ dir="structure_storage",
14
+ ):
8
15
  """
9
16
  Writes provided node and edge data into multiple sheets of a single Excel workbook.
10
17
 
@@ -35,13 +42,14 @@ def _output_excel(node_list, node_dict, edge_list, edge_cls_list, structure_name
35
42
  if edge_cls_list:
36
43
  tables["EdgesCondClass"] = pd.DataFrame(edge_cls_list)
37
44
  for i in node_dict:
38
- if i == "StructureExecutor":
45
+ if i == "GraphExecutor":
39
46
  structure_node = node_dict[i][0]
40
47
  structure_node["name"] = structure_name
41
- structure_id = structure_node["id"]
48
+ structure_id = structure_node["ln_id"]
42
49
  tables[i] = pd.DataFrame(node_dict[i])
43
50
 
44
51
  import os
52
+
45
53
  filepath = os.path.join(dir, f"{structure_name}_{structure_id}.xlsx")
46
54
 
47
55
  if not os.path.exists(dir):
@@ -73,4 +81,3 @@ def to_excel(structure, structure_name, dir="structure_storage"):
73
81
  edge_list, edge_cls_list = output_edge_list(structure)
74
82
 
75
83
  _output_excel(node_list, node_dict, edge_list, edge_cls_list, structure_name, dir)
76
-
lionagi/libs/__init__.py CHANGED
@@ -2,10 +2,19 @@ from lionagi.libs.sys_util import SysUtil
2
2
  from lionagi.libs.ln_async import AsyncUtil
3
3
 
4
4
  import lionagi.libs.ln_convert as convert
5
+ from lionagi.libs.ln_convert import (
6
+ to_str,
7
+ to_list,
8
+ to_dict,
9
+ to_df,
10
+ to_readable_dict,
11
+ to_num,
12
+ )
5
13
  import lionagi.libs.ln_dataframe as dataframe
6
14
  import lionagi.libs.ln_func_call as func_call
7
- from lionagi.libs.ln_func_call import CallDecorator
15
+ from lionagi.libs.ln_func_call import lcall, CallDecorator
8
16
  import lionagi.libs.ln_nested as nested
17
+ from lionagi.libs.ln_nested import nget, nset, ninsert
9
18
  from lionagi.libs.ln_parse import ParseUtil, StringMatch
10
19
 
11
20
  from lionagi.libs.ln_api import (
@@ -16,6 +25,7 @@ from lionagi.libs.ln_api import (
16
25
  PayloadPackage,
17
26
  )
18
27
 
28
+ from lionagi.libs.ln_image import ImageUtil
19
29
  from lionagi.libs.ln_validate import validation_funcs
20
30
 
21
31
 
@@ -35,4 +45,15 @@ __all__ = [
35
45
  "SimpleRateLimiter",
36
46
  "CallDecorator",
37
47
  "validation_funcs",
48
+ "ImageUtil",
49
+ "to_str",
50
+ "to_list",
51
+ "to_dict",
52
+ "to_df",
53
+ "lcall",
54
+ "to_readable_dict",
55
+ "to_num",
56
+ "nget",
57
+ "nset",
58
+ "ninsert",
38
59
  ]
lionagi/libs/ln_api.py CHANGED
@@ -1,8 +1,25 @@
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
+
1
17
  from collections.abc import Sequence, Mapping
2
18
 
3
19
  from abc import ABC
4
20
  from dataclasses import dataclass
5
21
 
22
+ import contextlib
6
23
  import logging
7
24
  import re
8
25
  import asyncio
@@ -11,9 +28,9 @@ import aiohttp
11
28
  from typing import Any, NoReturn, Type, Callable
12
29
 
13
30
  from lionagi.libs.ln_async import AsyncUtil
14
- import lionagi.libs.ln_convert as convert
15
- import lionagi.libs.ln_nested as nested
31
+ from lionagi.libs.ln_convert import to_dict, strip_lower, to_str
16
32
  import lionagi.libs.ln_func_call as func_call
33
+ from lionagi.libs.ln_nested import nget
17
34
 
18
35
 
19
36
  class APIUtil:
@@ -175,7 +192,7 @@ class APIUtil:
175
192
  """
176
193
  import hashlib
177
194
 
178
- param_str = convert.to_str(params, sort_keys=True) if params else ""
195
+ param_str = to_str(params, sort_keys=True) if params else ""
179
196
  return hashlib.md5((url + param_str).encode("utf-8")).hexdigest()
180
197
 
181
198
  @staticmethod
@@ -355,25 +372,48 @@ class APIUtil:
355
372
  # Expected token calculation for the given payload and endpoint.
356
373
  """
357
374
  import tiktoken
375
+ from .ln_image import ImageUtil
358
376
 
377
+ token_encoding_name = token_encoding_name or "cl100k_base"
359
378
  encoding = tiktoken.get_encoding(token_encoding_name)
360
379
  if api_endpoint.endswith("completions"):
361
380
  max_tokens = payload.get("max_tokens", 15)
362
381
  n = payload.get("n", 1)
363
382
  completion_tokens = n * max_tokens
364
-
365
- # chat completions
366
383
  if api_endpoint.startswith("chat/"):
367
384
  num_tokens = 0
385
+
368
386
  for message in payload["messages"]:
369
387
  num_tokens += 4 # every message follows <im_start>{role/name}\n{content}<im_end>\n
370
- for key, value in message.items():
371
- num_tokens += len(encoding.encode(value))
372
- if key == "name": # if there's a name, the role is omitted
373
- num_tokens -= (
374
- 1
375
- # role is always required and always 1 token
376
- )
388
+
389
+ _content = message.get("content")
390
+ if isinstance(_content, str):
391
+ num_tokens += len(encoding.encode(_content))
392
+
393
+ elif isinstance(_content, list):
394
+ for item in _content:
395
+ if isinstance(item, dict):
396
+ if "text" in item:
397
+ num_tokens += len(
398
+ encoding.encode(to_str(item["text"]))
399
+ )
400
+ elif "image_url" in item:
401
+ a: str = item["image_url"]["url"]
402
+ if "data:image/jpeg;base64," in a:
403
+ a = a.split("data:image/jpeg;base64,")[
404
+ 1
405
+ ].strip()
406
+ num_tokens += ImageUtil.calculate_image_token_usage_from_base64(
407
+ a, item.get("detail", "low")
408
+ )
409
+ num_tokens += (
410
+ 20 # for every image we add 20 tokens buffer
411
+ )
412
+ elif isinstance(item, str):
413
+ num_tokens += len(encoding.encode(item))
414
+ else:
415
+ num_tokens += len(encoding.encode(str(item)))
416
+
377
417
  num_tokens += 2 # every reply is primed with <im_start>assistant
378
418
  return num_tokens + completion_tokens
379
419
  else:
@@ -412,7 +452,7 @@ class APIUtil:
412
452
  payload[key] = config[key]
413
453
 
414
454
  for key in optional_:
415
- if bool(config[key]) and convert.strip_lower(config[key]) != "none":
455
+ if bool(config[key]) and strip_lower(config[key]) != "none":
416
456
  payload[key] = config[key]
417
457
 
418
458
  return payload
@@ -527,6 +567,7 @@ class BaseRateLimiter(ABC):
527
567
  max_attempts: int = 3,
528
568
  method: str = "post",
529
569
  payload: Mapping[str, any] = None,
570
+ required_tokens: int = None,
530
571
  **kwargs,
531
572
  ) -> Mapping[str, any] | None:
532
573
  """
@@ -552,9 +593,11 @@ class BaseRateLimiter(ABC):
552
593
  ): # Minimum token count
553
594
  await AsyncUtil.sleep(1) # Wait for capacity
554
595
  continue
555
- required_tokens = APIUtil.calculate_num_token(
556
- payload, endpoint, self.token_encoding_name, **kwargs
557
- )
596
+
597
+ if not required_tokens:
598
+ required_tokens = APIUtil.calculate_num_token(
599
+ payload, endpoint, self.token_encoding_name, **kwargs
600
+ )
558
601
 
559
602
  if await self.request_permission(required_tokens):
560
603
  request_headers = {"Authorization": f"Bearer {api_key}"}
@@ -748,7 +791,7 @@ class BaseService:
748
791
  )
749
792
 
750
793
  if ep not in self.endpoints:
751
- endpoint_config = nested.nget(self.schema, [ep, "config"])
794
+ endpoint_config = nget(self.schema, [ep, "config"])
752
795
  self.schema.get(ep, {})
753
796
  if isinstance(ep, EndPoint):
754
797
  self.endpoints[ep.endpoint] = ep
@@ -792,7 +835,7 @@ class BaseService:
792
835
 
793
836
  else:
794
837
  for ep in self.available_endpoints:
795
- endpoint_config = nested.nget(self.schema, [ep, "config"])
838
+ endpoint_config = nget(self.schema, [ep, "config"])
796
839
  self.schema.get(ep, {})
797
840
  if ep not in self.endpoints:
798
841
  self.endpoints[ep] = EndPoint(
@@ -806,7 +849,7 @@ class BaseService:
806
849
  if not self.endpoints[ep]._has_initialized:
807
850
  await self.endpoints[ep].init_rate_limiter()
808
851
 
809
- async def call_api(self, payload, endpoint, method, **kwargs):
852
+ async def call_api(self, payload, endpoint, method, required_tokens=None, **kwargs):
810
853
  """
811
854
  Calls the specified API endpoint with the given payload and method.
812
855
 
@@ -831,12 +874,24 @@ class BaseService:
831
874
  api_key=self.api_key,
832
875
  method=method,
833
876
  payload=payload,
877
+ required_tokens=required_tokens,
834
878
  **kwargs,
835
879
  )
836
880
 
837
881
 
838
882
  class PayloadPackage:
839
883
 
884
+ @classmethod
885
+ def embeddings(cls, embed_str, llmconfig, schema, **kwargs):
886
+ return APIUtil.create_payload(
887
+ input_=embed_str,
888
+ config=llmconfig,
889
+ required_=schema["required"],
890
+ optional_=schema["optional"],
891
+ input_key="input",
892
+ **kwargs,
893
+ )
894
+
840
895
  @classmethod
841
896
  def chat_completion(cls, messages, llmconfig, schema, **kwargs):
842
897
  """
@@ -874,7 +929,7 @@ class PayloadPackage:
874
929
  Returns:
875
930
  The constructed payload.
876
931
  """
877
- return APIUtil._create_payload(
932
+ return APIUtil.create_payload(
878
933
  input_=training_file,
879
934
  config=llmconfig,
880
935
  required_=schema["required"],
@@ -0,0 +1,37 @@
1
+ import os
2
+ import sys
3
+ from contextlib import asynccontextmanager
4
+
5
+ # import builtins
6
+ # import traceback
7
+
8
+ # original_print = builtins.print
9
+
10
+
11
+ # def new_print(*args, **kwargs):
12
+ # # Get the last frame in the stack
13
+ # frame = traceback.extract_stack(limit=2)[0]
14
+ # # Insert the filename and line number
15
+ # original_print(f"Called from {frame.filename}:{frame.lineno}")
16
+ # # Call the original print
17
+ # original_print(*args, **kwargs)
18
+
19
+
20
+ # Replace the built-in print with the new print
21
+ # builtins.print = new_print
22
+
23
+
24
+ @asynccontextmanager
25
+ async def async_suppress_print():
26
+ """
27
+ An asynchronous context manager that redirects stdout to /dev/null to suppress print output.
28
+ """
29
+ original_stdout = sys.stdout # Save the reference to the original standard output
30
+ with open(os.devnull, "w") as devnull:
31
+ sys.stdout = devnull
32
+ try:
33
+ yield
34
+ finally:
35
+ sys.stdout = (
36
+ original_stdout # Restore standard output to the original value
37
+ )
@@ -1,3 +1,19 @@
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
+
1
17
  import json
2
18
  import re
3
19
  from functools import singledispatch
@@ -361,6 +377,8 @@ def _(
361
377
  reset_index: bool = True,
362
378
  **kwargs,
363
379
  ) -> pd.DataFrame:
380
+ if not input_:
381
+ return pd.DataFrame()
364
382
  if not isinstance(input_[0], (pd.DataFrame, pd.Series, pd.core.generic.NDFrame)):
365
383
  if drop_kwargs is None:
366
384
  drop_kwargs = {}
@@ -400,7 +418,7 @@ def to_num(
400
418
  *,
401
419
  upper_bound: int | float | None = None,
402
420
  lower_bound: int | float | None = None,
403
- num_type: Type[int | float] = int,
421
+ num_type: Type[int | float] = float,
404
422
  precision: int | None = None,
405
423
  ) -> int | float:
406
424
  """
@@ -423,15 +441,9 @@ def to_num(
423
441
  return _str_to_num(str_, upper_bound, lower_bound, num_type, precision)
424
442
 
425
443
 
426
- def to_readable_dict(input_: Any | list[Any]) -> str | list[Any]:
444
+ def to_readable_dict(input_: Any) -> str:
427
445
  """
428
- Converts a given input to a readable dictionary format, either as a string or a list of dictionaries.
429
-
430
- Args:
431
- input_ (Any | list[Any]): The input to convert to a readable dictionary format.
432
-
433
- Returns:
434
- str | list[str]: The readable dictionary format of the input.
446
+ Converts a given input to a readable dictionary format
435
447
  """
436
448
 
437
449
  try:
@@ -1,3 +1,19 @@
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
+
1
17
  from __future__ import annotations
2
18
 
3
19
  import functools
@@ -115,12 +131,12 @@ async def alcall(
115
131
  tasks = []
116
132
  if input_ is not None:
117
133
  lst = to_list(input_)
118
- tasks = [AsyncUtil.handle_async_sync(func, i, **kwargs) for i in lst]
134
+ tasks = [call_handler(func, i, **kwargs) for i in lst]
119
135
 
120
136
  else:
121
- tasks = [AsyncUtil.handle_async_sync(func, **kwargs)]
137
+ tasks = [call_handler(func, **kwargs)]
122
138
 
123
- outs = await AsyncUtil.execute_tasks(*tasks)
139
+ outs = await asyncio.gather(*tasks)
124
140
  outs_ = []
125
141
  for i in outs:
126
142
  outs_.append(await i if isinstance(i, (Coroutine, asyncio.Future)) else i)
@@ -128,6 +144,11 @@ async def alcall(
128
144
  return to_list(outs_, flatten=flatten, dropna=dropna)
129
145
 
130
146
 
147
+ async def pcall(funcs):
148
+ task = [call_handler(func) for func in funcs]
149
+ return await asyncio.gather(*task)
150
+
151
+
131
152
  async def mcall(
132
153
  input_: Any, /, func: Any, *, explode: bool = False, **kwargs
133
154
  ) -> tuple[Any]:
@@ -290,10 +311,12 @@ async def rcall(
290
311
  func: Callable,
291
312
  *args,
292
313
  retries: int = 0,
293
- delay: float = 1.0,
294
- backoff_factor: float = 2.0,
314
+ delay: float = 0.1,
315
+ backoff_factor: float = 2,
295
316
  default: Any = None,
296
317
  timeout: float | None = None,
318
+ timing: bool = False,
319
+ verbose: bool = True,
297
320
  **kwargs,
298
321
  ) -> Any:
299
322
  """
@@ -339,20 +362,34 @@ async def rcall(
339
362
  last_exception = None
340
363
  result = None
341
364
 
365
+ start = SysUtil.get_now(datetime_=False)
342
366
  for attempt in range(retries + 1) if retries == 0 else range(retries):
343
367
  try:
368
+ err_msg = f"Attempt {attempt + 1}/{retries}: " if retries > 0 else None
369
+ if timing:
370
+ return (
371
+ await _tcall(
372
+ func, *args, err_msg=err_msg, timeout=timeout, **kwargs
373
+ ),
374
+ SysUtil.get_now(datetime_=False) - start,
375
+ )
376
+
344
377
  return await _tcall(func, *args, timeout=timeout, **kwargs)
345
378
  except Exception as e:
346
379
  last_exception = e
347
380
  if attempt < retries:
348
- await AsyncUtil.sleep(delay)
381
+ if verbose:
382
+ print(f"Attempt {attempt + 1}/{retries} failed: {e}, retrying...")
383
+ await asyncio.sleep(delay)
349
384
  delay *= backoff_factor
350
385
  else:
351
386
  break
352
387
  if result is None and default is not None:
353
388
  return default
354
389
  elif last_exception is not None:
355
- raise last_exception
390
+ raise RuntimeError(
391
+ f"Operation failed after {retries+1} attempts: {last_exception}"
392
+ ) from last_exception
356
393
  else:
357
394
  raise RuntimeError("rcall failed without catching an exception")
358
395
 
@@ -395,8 +432,8 @@ async def _alcall(
395
432
  [1, 4, 9]
396
433
  """
397
434
  lst = to_list(input_)
398
- tasks = [AsyncUtil.handle_async_sync(func, i, **kwargs) for i in lst]
399
- outs = await AsyncUtil.execute_tasks(*tasks)
435
+ tasks = [call_handler(func, i, **kwargs) for i in lst]
436
+ outs = await asyncio.gather(*tasks)
400
437
  return to_list(outs, flatten=flatten)
401
438
 
402
439
 
@@ -435,11 +472,11 @@ async def _tcall(
435
472
  """
436
473
  start_time = SysUtil.get_now(datetime_=False)
437
474
  try:
438
- await AsyncUtil.sleep(delay)
475
+ await asyncio.sleep(delay)
439
476
  # Apply timeout to the function call
440
477
  if timeout is not None:
441
478
  coro = ""
442
- if AsyncUtil.is_coroutine_func(func):
479
+ if is_coroutine_func(func):
443
480
  coro = func(*args, **kwargs)
444
481
  else:
445
482
 
@@ -451,14 +488,13 @@ async def _tcall(
451
488
  result = await asyncio.wait_for(coro, timeout)
452
489
 
453
490
  else:
454
- if AsyncUtil.is_coroutine_func(func):
491
+ if is_coroutine_func(func):
455
492
  return await func(*args, **kwargs)
456
493
  return func(*args, **kwargs)
457
494
  duration = SysUtil.get_now(datetime_=False) - start_time
458
495
  return (result, duration) if timing else result
459
496
  except asyncio.TimeoutError as e:
460
- err_msg = f"{err_msg} Error: {e}" if err_msg else f"An error occurred: {e}"
461
- print(err_msg)
497
+ err_msg = f"{err_msg or ''}Timeout {timeout} seconds exceeded"
462
498
  if ignore_err:
463
499
  return (
464
500
  (default, SysUtil.get_now(datetime_=False) - start_time)
@@ -466,10 +502,9 @@ async def _tcall(
466
502
  else default
467
503
  )
468
504
  else:
469
- raise e # Re-raise the timeout exception
505
+ raise asyncio.TimeoutError(err_msg) # Re-raise the timeout exception
470
506
  except Exception as e:
471
507
  err_msg = f"{err_msg} Error: {e}" if err_msg else f"An error occurred: {e}"
472
- print(err_msg)
473
508
  if ignore_err:
474
509
  return (
475
510
  (default, SysUtil.get_now(datetime_=False) - start_time)
@@ -537,7 +572,11 @@ class CallDecorator:
537
572
 
538
573
  @staticmethod
539
574
  def retry(
540
- retries: int = 3, delay: float = 2.0, backoff_factor: float = 2.0
575
+ retries: int = 3,
576
+ delay: float = 2.0,
577
+ backoff_factor: float = 2.0,
578
+ default=...,
579
+ verbose=True,
541
580
  ) -> Callable:
542
581
  """
543
582
  Decorates an asynchronous function to automatically retry on failure,
@@ -581,6 +620,8 @@ class CallDecorator:
581
620
  retries=retries,
582
621
  delay=delay,
583
622
  backoff_factor=backoff_factor,
623
+ default=default,
624
+ verbose=verbose,
584
625
  **kwargs,
585
626
  )
586
627
 
@@ -690,7 +731,7 @@ class CallDecorator:
690
731
  """
691
732
 
692
733
  def decorator(func: Callable[..., list[Any]]) -> Callable:
693
- if AsyncUtil.is_coroutine_func(func):
734
+ if is_coroutine_func(func):
694
735
 
695
736
  @functools.wraps(func)
696
737
  async def async_wrapper(*args, **kwargs) -> list[Any]:
@@ -748,7 +789,7 @@ class CallDecorator:
748
789
  """
749
790
 
750
791
  def decorator(func: Callable) -> Callable:
751
- if not any(AsyncUtil.is_coroutine_func(f) for f in functions):
792
+ if not any(is_coroutine_func(f) for f in functions):
752
793
 
753
794
  @functools.wraps(func)
754
795
  def sync_wrapper(*args, **kwargs):
@@ -763,7 +804,7 @@ class CallDecorator:
763
804
  return value
764
805
 
765
806
  return sync_wrapper
766
- elif all(AsyncUtil.is_coroutine_func(f) for f in functions):
807
+ elif all(is_coroutine_func(f) for f in functions):
767
808
 
768
809
  @functools.wraps(func)
769
810
  async def async_wrapper(*args, **kwargs):
@@ -827,7 +868,7 @@ class CallDecorator:
827
868
  """
828
869
 
829
870
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
830
- if AsyncUtil.is_coroutine_func(func):
871
+ if is_coroutine_func(func):
831
872
 
832
873
  @functools.wraps(func)
833
874
  async def async_wrapper(*args, **kwargs) -> Any:
@@ -889,7 +930,7 @@ class CallDecorator:
889
930
  ... # will return the cached result without re-executing the function body.
890
931
  """
891
932
 
892
- if AsyncUtil.is_coroutine_func(func):
933
+ if is_coroutine_func(func):
893
934
  # Asynchronous function handling
894
935
  @AsyncUtil.cached(ttl=ttl)
895
936
  async def cached_async(*args, **kwargs) -> Any:
@@ -993,7 +1034,7 @@ class CallDecorator:
993
1034
  """
994
1035
 
995
1036
  def decorator(func: Callable[..., list[Any]]) -> Callable:
996
- if AsyncUtil.is_coroutine_func(func):
1037
+ if is_coroutine_func(func):
997
1038
 
998
1039
  @functools.wraps(func)
999
1040
  async def async_wrapper(*args, **kwargs) -> Any:
@@ -1039,11 +1080,11 @@ class CallDecorator:
1039
1080
  """
1040
1081
 
1041
1082
  def decorator(func: Callable) -> Callable:
1042
- if not AsyncUtil.is_coroutine_func(func):
1083
+ if not is_coroutine_func(func):
1043
1084
  raise TypeError(
1044
1085
  "max_concurrency decorator can only be used with async functions."
1045
1086
  )
1046
- semaphore = AsyncUtil.semaphore(limit)
1087
+ semaphore = asyncio.Semaphore(limit)
1047
1088
 
1048
1089
  @functools.wraps(func)
1049
1090
  async def wrapper(*args, **kwargs):
@@ -1089,7 +1130,7 @@ class CallDecorator:
1089
1130
  @functools.wraps(fn)
1090
1131
  def wrapper(*args, **kwargs):
1091
1132
  future = pool.submit(fn, *args, **kwargs)
1092
- return AsyncUtil.wrap_future(future) # make it awaitable
1133
+ return asyncio.wrap_future(future) # make it awaitable
1093
1134
 
1094
1135
  return wrapper
1095
1136
 
@@ -1233,8 +1274,8 @@ async def call_handler(
1233
1274
  except Exception as e:
1234
1275
  if error_map:
1235
1276
  _custom_error_handler(e, error_map)
1236
- else:
1237
- logging.error(f"Error in call_handler: {e}")
1277
+ # else:
1278
+ # logging.error(f"Error in call_handler: {e}")
1238
1279
  raise
1239
1280
 
1240
1281