lionagi 0.0.312__py3-none-any.whl → 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lionagi/__init__.py +61 -3
- lionagi/core/__init__.py +0 -14
- lionagi/core/_setting/_setting.py +59 -0
- lionagi/core/action/__init__.py +14 -0
- lionagi/core/action/function_calling.py +136 -0
- lionagi/core/action/manual.py +1 -0
- lionagi/core/action/node.py +109 -0
- lionagi/core/action/tool.py +114 -0
- lionagi/core/action/tool_manager.py +356 -0
- lionagi/core/agent/__init__.py +0 -3
- lionagi/core/agent/base_agent.py +45 -36
- lionagi/core/agent/eval/evaluator.py +1 -0
- lionagi/core/agent/eval/vote.py +40 -0
- lionagi/core/agent/learn/learner.py +59 -0
- lionagi/core/agent/plan/unit_template.py +1 -0
- lionagi/core/collections/__init__.py +17 -0
- lionagi/core/collections/_logger.py +319 -0
- lionagi/core/collections/abc/__init__.py +53 -0
- lionagi/core/collections/abc/component.py +615 -0
- lionagi/core/collections/abc/concepts.py +297 -0
- lionagi/core/collections/abc/exceptions.py +150 -0
- lionagi/core/collections/abc/util.py +45 -0
- lionagi/core/collections/exchange.py +161 -0
- lionagi/core/collections/flow.py +426 -0
- lionagi/core/collections/model.py +419 -0
- lionagi/core/collections/pile.py +913 -0
- lionagi/core/collections/progression.py +236 -0
- lionagi/core/collections/util.py +64 -0
- lionagi/core/director/direct.py +314 -0
- lionagi/core/director/director.py +2 -0
- lionagi/core/engine/branch_engine.py +333 -0
- lionagi/core/engine/instruction_map_engine.py +204 -0
- lionagi/core/engine/sandbox_.py +14 -0
- lionagi/core/engine/script_engine.py +99 -0
- lionagi/core/executor/base_executor.py +90 -0
- lionagi/core/executor/graph_executor.py +330 -0
- lionagi/core/executor/neo4j_executor.py +384 -0
- lionagi/core/generic/__init__.py +7 -0
- lionagi/core/generic/edge.py +112 -0
- lionagi/core/generic/edge_condition.py +16 -0
- lionagi/core/generic/graph.py +236 -0
- lionagi/core/generic/hyperedge.py +1 -0
- lionagi/core/generic/node.py +220 -0
- lionagi/core/generic/tree.py +48 -0
- lionagi/core/generic/tree_node.py +79 -0
- lionagi/core/mail/__init__.py +7 -3
- lionagi/core/mail/mail.py +25 -0
- lionagi/core/mail/mail_manager.py +142 -58
- lionagi/core/mail/package.py +45 -0
- lionagi/core/mail/start_mail.py +36 -0
- lionagi/core/message/__init__.py +19 -0
- lionagi/core/message/action_request.py +133 -0
- lionagi/core/message/action_response.py +135 -0
- lionagi/core/message/assistant_response.py +95 -0
- lionagi/core/message/instruction.py +234 -0
- lionagi/core/message/message.py +101 -0
- lionagi/core/message/system.py +86 -0
- lionagi/core/message/util.py +283 -0
- lionagi/core/report/__init__.py +4 -0
- lionagi/core/report/base.py +217 -0
- lionagi/core/report/form.py +231 -0
- lionagi/core/report/report.py +166 -0
- lionagi/core/report/util.py +28 -0
- lionagi/core/rule/__init__.py +0 -0
- lionagi/core/rule/_default.py +16 -0
- lionagi/core/rule/action.py +99 -0
- lionagi/core/rule/base.py +238 -0
- lionagi/core/rule/boolean.py +56 -0
- lionagi/core/rule/choice.py +47 -0
- lionagi/core/rule/mapping.py +96 -0
- lionagi/core/rule/number.py +71 -0
- lionagi/core/rule/rulebook.py +109 -0
- lionagi/core/rule/string.py +52 -0
- lionagi/core/rule/util.py +35 -0
- lionagi/core/session/__init__.py +0 -3
- lionagi/core/session/branch.py +431 -0
- lionagi/core/session/directive_mixin.py +287 -0
- lionagi/core/session/session.py +230 -902
- lionagi/core/structure/__init__.py +1 -0
- lionagi/core/structure/chain.py +1 -0
- lionagi/core/structure/forest.py +1 -0
- lionagi/core/structure/graph.py +1 -0
- lionagi/core/structure/tree.py +1 -0
- lionagi/core/unit/__init__.py +5 -0
- lionagi/core/unit/parallel_unit.py +245 -0
- lionagi/core/unit/template/__init__.py +0 -0
- lionagi/core/unit/template/action.py +81 -0
- lionagi/core/unit/template/base.py +51 -0
- lionagi/core/unit/template/plan.py +84 -0
- lionagi/core/unit/template/predict.py +109 -0
- lionagi/core/unit/template/score.py +124 -0
- lionagi/core/unit/template/select.py +104 -0
- lionagi/core/unit/unit.py +362 -0
- lionagi/core/unit/unit_form.py +305 -0
- lionagi/core/unit/unit_mixin.py +1168 -0
- lionagi/core/unit/util.py +71 -0
- lionagi/core/validator/__init__.py +0 -0
- lionagi/core/validator/validator.py +364 -0
- lionagi/core/work/__init__.py +0 -0
- lionagi/core/work/work.py +76 -0
- lionagi/core/work/work_function.py +101 -0
- lionagi/core/work/work_queue.py +103 -0
- lionagi/core/work/worker.py +258 -0
- lionagi/core/work/worklog.py +120 -0
- lionagi/experimental/__init__.py +0 -0
- lionagi/experimental/compressor/__init__.py +0 -0
- lionagi/experimental/compressor/base.py +46 -0
- lionagi/experimental/compressor/llm_compressor.py +247 -0
- lionagi/experimental/compressor/llm_summarizer.py +61 -0
- lionagi/experimental/compressor/util.py +70 -0
- lionagi/experimental/directive/__init__.py +19 -0
- lionagi/experimental/directive/parser/__init__.py +0 -0
- lionagi/experimental/directive/parser/base_parser.py +282 -0
- lionagi/experimental/directive/template/__init__.py +0 -0
- lionagi/experimental/directive/template/base_template.py +79 -0
- lionagi/experimental/directive/template/schema.py +36 -0
- lionagi/experimental/directive/tokenizer.py +73 -0
- lionagi/experimental/evaluator/__init__.py +0 -0
- lionagi/experimental/evaluator/ast_evaluator.py +131 -0
- lionagi/experimental/evaluator/base_evaluator.py +218 -0
- lionagi/experimental/knowledge/__init__.py +0 -0
- lionagi/experimental/knowledge/base.py +10 -0
- lionagi/experimental/knowledge/graph.py +0 -0
- lionagi/experimental/memory/__init__.py +0 -0
- lionagi/experimental/strategies/__init__.py +0 -0
- lionagi/experimental/strategies/base.py +1 -0
- lionagi/integrations/bridge/autogen_/__init__.py +0 -0
- lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
- lionagi/integrations/bridge/langchain_/documents.py +4 -0
- lionagi/integrations/bridge/llamaindex_/index.py +30 -0
- lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
- lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
- lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
- lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
- lionagi/integrations/bridge/transformers_/__init__.py +0 -0
- lionagi/integrations/bridge/transformers_/install_.py +36 -0
- lionagi/integrations/chunker/__init__.py +0 -0
- lionagi/integrations/chunker/chunk.py +312 -0
- lionagi/integrations/config/oai_configs.py +38 -7
- lionagi/integrations/config/ollama_configs.py +1 -1
- lionagi/integrations/config/openrouter_configs.py +14 -2
- lionagi/integrations/loader/__init__.py +0 -0
- lionagi/integrations/loader/load.py +253 -0
- lionagi/integrations/loader/load_util.py +195 -0
- lionagi/integrations/provider/_mapping.py +46 -0
- lionagi/integrations/provider/litellm.py +2 -1
- lionagi/integrations/provider/mlx_service.py +16 -9
- lionagi/integrations/provider/oai.py +91 -4
- lionagi/integrations/provider/ollama.py +7 -6
- lionagi/integrations/provider/openrouter.py +115 -8
- lionagi/integrations/provider/services.py +2 -2
- lionagi/integrations/provider/transformers.py +18 -22
- lionagi/integrations/storage/__init__.py +3 -0
- lionagi/integrations/storage/neo4j.py +665 -0
- lionagi/integrations/storage/storage_util.py +287 -0
- lionagi/integrations/storage/structure_excel.py +285 -0
- lionagi/integrations/storage/to_csv.py +63 -0
- lionagi/integrations/storage/to_excel.py +83 -0
- lionagi/libs/__init__.py +26 -1
- lionagi/libs/ln_api.py +78 -23
- lionagi/libs/ln_context.py +37 -0
- lionagi/libs/ln_convert.py +21 -9
- lionagi/libs/ln_func_call.py +69 -28
- lionagi/libs/ln_image.py +107 -0
- lionagi/libs/ln_knowledge_graph.py +405 -0
- lionagi/libs/ln_nested.py +26 -11
- lionagi/libs/ln_parse.py +110 -14
- lionagi/libs/ln_queue.py +117 -0
- lionagi/libs/ln_tokenize.py +164 -0
- lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
- lionagi/libs/special_tokens.py +172 -0
- lionagi/libs/sys_util.py +107 -2
- lionagi/lions/__init__.py +0 -0
- lionagi/lions/coder/__init__.py +0 -0
- lionagi/lions/coder/add_feature.py +20 -0
- lionagi/lions/coder/base_prompts.py +22 -0
- lionagi/lions/coder/code_form.py +13 -0
- lionagi/lions/coder/coder.py +168 -0
- lionagi/lions/coder/util.py +96 -0
- lionagi/lions/researcher/__init__.py +0 -0
- lionagi/lions/researcher/data_source/__init__.py +0 -0
- lionagi/lions/researcher/data_source/finhub_.py +191 -0
- lionagi/lions/researcher/data_source/google_.py +199 -0
- lionagi/lions/researcher/data_source/wiki_.py +96 -0
- lionagi/lions/researcher/data_source/yfinance_.py +21 -0
- lionagi/tests/integrations/__init__.py +0 -0
- lionagi/tests/libs/__init__.py +0 -0
- lionagi/tests/libs/test_field_validators.py +353 -0
- lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
- lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
- lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
- lionagi/tests/libs/test_queue.py +67 -0
- lionagi/tests/test_core/collections/__init__.py +0 -0
- lionagi/tests/test_core/collections/test_component.py +206 -0
- lionagi/tests/test_core/collections/test_exchange.py +138 -0
- lionagi/tests/test_core/collections/test_flow.py +145 -0
- lionagi/tests/test_core/collections/test_pile.py +171 -0
- lionagi/tests/test_core/collections/test_progression.py +129 -0
- lionagi/tests/test_core/generic/__init__.py +0 -0
- lionagi/tests/test_core/generic/test_edge.py +67 -0
- lionagi/tests/test_core/generic/test_graph.py +96 -0
- lionagi/tests/test_core/generic/test_node.py +106 -0
- lionagi/tests/test_core/generic/test_tree_node.py +73 -0
- lionagi/tests/test_core/test_branch.py +115 -292
- lionagi/tests/test_core/test_form.py +46 -0
- lionagi/tests/test_core/test_report.py +105 -0
- lionagi/tests/test_core/test_validator.py +111 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
- lionagi-0.2.1.dist-info/RECORD +240 -0
- lionagi/core/branch/__init__.py +0 -4
- lionagi/core/branch/base_branch.py +0 -654
- lionagi/core/branch/branch.py +0 -471
- lionagi/core/branch/branch_flow_mixin.py +0 -96
- lionagi/core/branch/executable_branch.py +0 -347
- lionagi/core/branch/util.py +0 -323
- lionagi/core/direct/__init__.py +0 -6
- lionagi/core/direct/predict.py +0 -161
- lionagi/core/direct/score.py +0 -278
- lionagi/core/direct/select.py +0 -169
- lionagi/core/direct/utils.py +0 -87
- lionagi/core/direct/vote.py +0 -64
- lionagi/core/flow/base/baseflow.py +0 -23
- lionagi/core/flow/monoflow/ReAct.py +0 -238
- lionagi/core/flow/monoflow/__init__.py +0 -9
- lionagi/core/flow/monoflow/chat.py +0 -95
- lionagi/core/flow/monoflow/chat_mixin.py +0 -263
- lionagi/core/flow/monoflow/followup.py +0 -214
- lionagi/core/flow/polyflow/__init__.py +0 -1
- lionagi/core/flow/polyflow/chat.py +0 -248
- lionagi/core/mail/schema.py +0 -56
- lionagi/core/messages/__init__.py +0 -3
- lionagi/core/messages/schema.py +0 -533
- lionagi/core/prompt/prompt_template.py +0 -316
- lionagi/core/schema/__init__.py +0 -22
- lionagi/core/schema/action_node.py +0 -29
- lionagi/core/schema/base_mixin.py +0 -296
- lionagi/core/schema/base_node.py +0 -199
- lionagi/core/schema/condition.py +0 -24
- lionagi/core/schema/data_logger.py +0 -354
- lionagi/core/schema/data_node.py +0 -93
- lionagi/core/schema/prompt_template.py +0 -67
- lionagi/core/schema/structure.py +0 -910
- lionagi/core/tool/__init__.py +0 -3
- lionagi/core/tool/tool_manager.py +0 -280
- lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
- lionagi/tests/test_core/test_base_branch.py +0 -427
- lionagi/tests/test_core/test_chat_flow.py +0 -63
- lionagi/tests/test_core/test_mail_manager.py +0 -75
- lionagi/tests/test_core/test_prompts.py +0 -51
- lionagi/tests/test_core/test_session.py +0 -254
- lionagi/tests/test_core/test_session_base_util.py +0 -312
- lionagi/tests/test_core/test_tool_manager.py +0 -95
- lionagi-0.0.312.dist-info/RECORD +0 -111
- /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
- /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
- /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
- /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
- /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
- /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
- /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
- /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
- /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
import pandas as pd
|
2
|
+
from lionagi.libs import SysUtil
|
3
|
+
|
4
|
+
from lionagi.integrations.storage.storage_util import output_node_list, output_edge_list
|
5
|
+
|
6
|
+
|
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
|
+
):
|
15
|
+
"""
|
16
|
+
Writes provided node and edge data into multiple sheets of a single Excel workbook.
|
17
|
+
|
18
|
+
This helper function takes lists and dictionaries of nodes and edges, converts them into pandas DataFrames,
|
19
|
+
and then writes each DataFrame to a distinct sheet in an Excel workbook. This includes a separate sheet
|
20
|
+
for each type of node and edge, as well as edge conditions if they exist.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
node_list (list): A list of dictionaries where each dictionary contains attributes of a single node.
|
24
|
+
node_dict (dict): A dictionary of lists where each key represents a node type and the value is a list of
|
25
|
+
node attributes for nodes of that type.
|
26
|
+
edge_list (list): A list of dictionaries where each dictionary contains attributes of a single edge.
|
27
|
+
edge_cls_list (list): A list of dictionaries where each dictionary contains attributes of edge conditions.
|
28
|
+
structure_name (str): The base name for the output Excel file. The '.xlsx' extension will be added
|
29
|
+
automatically if not included.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
None: This function does not return a value but writes data directly to an Excel workbook.
|
33
|
+
|
34
|
+
Raises:
|
35
|
+
ImportError: If the required 'openpyxl' library is not installed, which is necessary for pandas to write Excel files.
|
36
|
+
"""
|
37
|
+
SysUtil.check_import("openpyxl")
|
38
|
+
|
39
|
+
structure_id = ""
|
40
|
+
|
41
|
+
tables = {"Nodes": pd.DataFrame(node_list), "Edges": pd.DataFrame(edge_list)}
|
42
|
+
if edge_cls_list:
|
43
|
+
tables["EdgesCondClass"] = pd.DataFrame(edge_cls_list)
|
44
|
+
for i in node_dict:
|
45
|
+
if i == "GraphExecutor":
|
46
|
+
structure_node = node_dict[i][0]
|
47
|
+
structure_node["name"] = structure_name
|
48
|
+
structure_id = structure_node["ln_id"]
|
49
|
+
tables[i] = pd.DataFrame(node_dict[i])
|
50
|
+
|
51
|
+
import os
|
52
|
+
|
53
|
+
filepath = os.path.join(dir, f"{structure_name}_{structure_id}.xlsx")
|
54
|
+
|
55
|
+
if not os.path.exists(dir):
|
56
|
+
os.makedirs(dir)
|
57
|
+
|
58
|
+
with pd.ExcelWriter(filepath) as writer:
|
59
|
+
for i in tables:
|
60
|
+
tables[i].to_excel(writer, sheet_name=i, index=False)
|
61
|
+
|
62
|
+
|
63
|
+
def to_excel(structure, structure_name, dir="structure_storage"):
|
64
|
+
"""
|
65
|
+
Converts a structure into a series of Excel sheets within a single workbook.
|
66
|
+
|
67
|
+
This function processes the specified structure to extract detailed node and edge information,
|
68
|
+
including conditions if applicable. These details are then saved into separate sheets within an
|
69
|
+
Excel workbook for nodes, edges, and any edge conditions.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
structure: An object representing the structure to be serialized. This should have methods
|
73
|
+
to return lists of nodes and edges suitable for output.
|
74
|
+
structure_name (str): The base name of the output Excel file. The '.xlsx' extension will be added
|
75
|
+
automatically if not included.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
None: This function does not return a value but outputs an Excel workbook with multiple sheets.
|
79
|
+
"""
|
80
|
+
node_list, node_dict = output_node_list(structure)
|
81
|
+
edge_list, edge_cls_list = output_edge_list(structure)
|
82
|
+
|
83
|
+
_output_excel(node_list, node_dict, edge_list, edge_cls_list, structure_name, dir)
|
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,10 @@ from lionagi.libs.ln_api import (
|
|
16
25
|
PayloadPackage,
|
17
26
|
)
|
18
27
|
|
28
|
+
from lionagi.libs.ln_image import ImageUtil
|
29
|
+
from lionagi.libs.ln_validate import validation_funcs
|
30
|
+
|
31
|
+
|
19
32
|
__all__ = [
|
20
33
|
"SysUtil",
|
21
34
|
"convert",
|
@@ -31,4 +44,16 @@ __all__ = [
|
|
31
44
|
"StatusTracker",
|
32
45
|
"SimpleRateLimiter",
|
33
46
|
"CallDecorator",
|
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",
|
34
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
|
-
|
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 =
|
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,30 +372,53 @@ 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
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
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:
|
380
|
-
prompt = payload["
|
381
|
-
if isinstance(prompt, str): # single
|
420
|
+
prompt = payload["format_prompt"]
|
421
|
+
if isinstance(prompt, str): # single format_prompt
|
382
422
|
prompt_tokens = len(encoding.encode(prompt))
|
383
423
|
return prompt_tokens + completion_tokens
|
384
424
|
elif isinstance(prompt, list): # multiple prompts
|
@@ -386,7 +426,7 @@ class APIUtil:
|
|
386
426
|
return prompt_tokens + completion_tokens * len(prompt)
|
387
427
|
else:
|
388
428
|
raise TypeError(
|
389
|
-
'Expecting either string or list of strings for "
|
429
|
+
'Expecting either string or list of strings for "format_prompt" field in completion request'
|
390
430
|
)
|
391
431
|
elif api_endpoint == "embeddings":
|
392
432
|
input = payload["input"]
|
@@ -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
|
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
|
-
|
556
|
-
|
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 =
|
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 =
|
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.
|
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
|
+
)
|
lionagi/libs/ln_convert.py
CHANGED
@@ -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] =
|
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
|
444
|
+
def to_readable_dict(input_: Any) -> str:
|
427
445
|
"""
|
428
|
-
Converts a given input to a readable dictionary format
|
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:
|
lionagi/libs/ln_func_call.py
CHANGED
@@ -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 = [
|
134
|
+
tasks = [call_handler(func, i, **kwargs) for i in lst]
|
119
135
|
|
120
136
|
else:
|
121
|
-
tasks = [
|
137
|
+
tasks = [call_handler(func, **kwargs)]
|
122
138
|
|
123
|
-
outs = await
|
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
|
294
|
-
backoff_factor: float = 2
|
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
|
-
|
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
|
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 = [
|
399
|
-
outs = await
|
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
|
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
|
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
|
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}
|
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
|
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,
|
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
|
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(
|
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(
|
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
|
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
|
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
|
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
|
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 =
|
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
|
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
|
-
|
1277
|
+
# else:
|
1278
|
+
# logging.error(f"Error in call_handler: {e}")
|
1238
1279
|
raise
|
1239
1280
|
|
1240
1281
|
|