lionagi 0.0.114__py3-none-any.whl → 0.0.116__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- lionagi/__init__.py +7 -4
- lionagi/bridge/__init__.py +19 -4
- lionagi/bridge/langchain.py +23 -3
- lionagi/bridge/llama_index.py +5 -3
- lionagi/configs/__init__.py +1 -1
- lionagi/configs/oai_configs.py +88 -1
- lionagi/core/__init__.py +6 -9
- lionagi/core/conversations/__init__.py +5 -0
- lionagi/core/conversations/conversation.py +107 -0
- lionagi/core/flows/__init__.py +8 -0
- lionagi/core/flows/flow.py +8 -0
- lionagi/core/flows/flow_util.py +62 -0
- lionagi/core/instruction_set/__init__.py +5 -0
- lionagi/core/instruction_set/instruction_sets.py +7 -0
- lionagi/core/sessions/__init__.py +5 -0
- lionagi/core/sessions/sessions.py +187 -0
- lionagi/endpoints/__init__.py +5 -0
- lionagi/endpoints/assistants.py +0 -0
- lionagi/endpoints/audio.py +17 -0
- lionagi/endpoints/chatcompletion.py +54 -0
- lionagi/endpoints/embeddings.py +0 -0
- lionagi/endpoints/finetune.py +0 -0
- lionagi/endpoints/image.py +0 -0
- lionagi/endpoints/moderation.py +0 -0
- lionagi/endpoints/vision.py +0 -0
- lionagi/{loader → loaders}/__init__.py +7 -1
- lionagi/{loader → loaders}/chunker.py +6 -12
- lionagi/{utils/load_utils.py → loaders/load_util.py} +47 -6
- lionagi/{loader → loaders}/reader.py +4 -12
- lionagi/messages/__init__.py +11 -0
- lionagi/messages/instruction.py +15 -0
- lionagi/messages/message.py +110 -0
- lionagi/messages/response.py +33 -0
- lionagi/messages/system.py +12 -0
- lionagi/objs/__init__.py +10 -6
- lionagi/objs/abc_objs.py +39 -0
- lionagi/objs/async_queue.py +135 -0
- lionagi/objs/messenger.py +70 -148
- lionagi/objs/status_tracker.py +37 -0
- lionagi/objs/{tool_registry.py → tool_manager.py} +8 -6
- lionagi/schema/__init__.py +3 -3
- lionagi/schema/base_node.py +251 -0
- lionagi/schema/base_tool.py +8 -3
- lionagi/schema/data_logger.py +2 -3
- lionagi/schema/data_node.py +37 -0
- lionagi/services/__init__.py +1 -4
- lionagi/services/base_api_service.py +15 -5
- lionagi/services/oai.py +2 -2
- lionagi/services/openrouter.py +2 -3
- lionagi/structures/graph.py +96 -0
- lionagi/{structure → structures}/relationship.py +10 -2
- lionagi/structures/structure.py +102 -0
- lionagi/tests/test_api_util.py +46 -0
- lionagi/tests/test_call_util.py +115 -0
- lionagi/tests/test_convert_util.py +202 -0
- lionagi/tests/test_encrypt_util.py +33 -0
- lionagi/tests/{test_flatten_util.py → test_flat_util.py} +1 -1
- lionagi/tests/test_io_util.py +0 -0
- lionagi/tests/test_sys_util.py +0 -0
- lionagi/tools/__init__.py +5 -0
- lionagi/tools/tool_util.py +7 -0
- lionagi/utils/__init__.py +55 -35
- lionagi/utils/api_util.py +19 -17
- lionagi/utils/call_util.py +2 -1
- lionagi/utils/convert_util.py +229 -0
- lionagi/utils/encrypt_util.py +16 -0
- lionagi/utils/flat_util.py +38 -0
- lionagi/utils/io_util.py +2 -2
- lionagi/utils/sys_util.py +45 -10
- lionagi/version.py +1 -1
- {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/METADATA +2 -2
- lionagi-0.0.116.dist-info/RECORD +110 -0
- lionagi/core/conversations.py +0 -108
- lionagi/core/flows.py +0 -1
- lionagi/core/instruction_sets.py +0 -1
- lionagi/core/messages.py +0 -166
- lionagi/core/sessions.py +0 -297
- lionagi/schema/base_schema.py +0 -252
- lionagi/services/chatcompletion.py +0 -48
- lionagi/services/service_objs.py +0 -282
- lionagi/structure/structure.py +0 -160
- lionagi/tools/coder.py +0 -1
- lionagi/tools/sandbox.py +0 -1
- lionagi/utils/tool_util.py +0 -92
- lionagi/utils/type_util.py +0 -81
- lionagi-0.0.114.dist-info/RECORD +0 -84
- /lionagi/configs/{openrouter_config.py → openrouter_configs.py} +0 -0
- /lionagi/{datastore → datastores}/__init__.py +0 -0
- /lionagi/{datastore → datastores}/chroma.py +0 -0
- /lionagi/{datastore → datastores}/deeplake.py +0 -0
- /lionagi/{datastore → datastores}/elasticsearch.py +0 -0
- /lionagi/{datastore → datastores}/lantern.py +0 -0
- /lionagi/{datastore → datastores}/pinecone.py +0 -0
- /lionagi/{datastore → datastores}/postgres.py +0 -0
- /lionagi/{datastore → datastores}/qdrant.py +0 -0
- /lionagi/{structure → structures}/__init__.py +0 -0
- {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/LICENSE +0 -0
- {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/WHEEL +0 -0
- {lionagi-0.0.114.dist-info → lionagi-0.0.116.dist-info}/top_level.txt +0 -0
lionagi/objs/messenger.py
CHANGED
@@ -1,163 +1,85 @@
|
|
1
|
-
|
1
|
+
from collections import deque
|
2
|
+
from typing import Optional, Any, Union, Dict, Tuple
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
from lionagi.utils.call_util import lcall
|
5
|
+
from lionagi.schema.data_logger import DataLogger
|
6
|
+
from lionagi.messages import Message, Response, Instruction, System
|
6
7
|
|
7
8
|
|
8
|
-
|
9
|
-
# """
|
10
|
-
# Messenger handles the creation, logging, and exporting of messages.
|
11
|
-
|
12
|
-
# This class is responsible for creating various types of messages (system, instruction, response),
|
13
|
-
# logging them, and optionally exporting the log to a CSV file.
|
14
|
-
|
15
|
-
# Attributes:
|
16
|
-
# _logger (DataLogger): An instance of DataLogger to manage message logging.
|
9
|
+
class Messenger:
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# set_log: Sets the log object for the DataLogger.
|
22
|
-
|
23
|
-
# log_message: Logs a message in JSON format.
|
11
|
+
def __init__(self) -> None:
|
12
|
+
self._logger = DataLogger()
|
24
13
|
|
25
|
-
|
14
|
+
def set_dir(self, dir: str) -> None:
|
15
|
+
self._logger.dir = dir
|
16
|
+
|
17
|
+
def set_log(self, log) -> None:
|
18
|
+
self._logger.log = log
|
26
19
|
|
27
|
-
|
20
|
+
def log_message(self, msg: Message) -> None:
|
21
|
+
self._logger(msg.to_json())
|
28
22
|
|
29
|
-
|
30
|
-
|
23
|
+
def to_csv(self, **kwargs) -> None:
|
24
|
+
self._logger.to_csv(**kwargs)
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
# Initializes the Messenger with a DataLogger instance.
|
35
|
-
# """
|
36
|
-
# self._logger = DataLogger()
|
37
|
-
|
38
|
-
# def set_dir(self, dir: str) -> None:
|
39
|
-
# """
|
40
|
-
# Sets the directory where the DataLogger will save CSV files.
|
41
|
-
|
42
|
-
# Parameters:
|
43
|
-
# dir (str): The directory path to set for the DataLogger.
|
44
|
-
# """
|
45
|
-
# self._logger.dir = dir
|
26
|
+
def clear_log(self):
|
27
|
+
self._logger.log = deque()
|
46
28
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
29
|
+
def _create_message(
|
30
|
+
self,
|
31
|
+
system: Optional[Any] = None,
|
32
|
+
instruction: Optional[Any] = None,
|
33
|
+
context: Optional[Any] = None,
|
34
|
+
response: Optional[Any] = None,
|
35
|
+
name: Optional[str] = None
|
36
|
+
) -> Message:
|
55
37
|
|
56
|
-
|
57
|
-
|
58
|
-
# Logs a message in JSON format using the DataLogger.
|
59
|
-
|
60
|
-
# Parameters:
|
61
|
-
# msg (Message): The message object to be logged.
|
62
|
-
# """
|
63
|
-
# self._logger(msg.to_json())
|
64
|
-
|
65
|
-
# def to_csv(self, **kwargs) -> None:
|
66
|
-
# """
|
67
|
-
# Exports the logged messages to a CSV file.
|
68
|
-
|
69
|
-
# Parameters:
|
70
|
-
# **kwargs: Additional keyword arguments to be passed to the DataLogger's to_csv method.
|
71
|
-
# """
|
72
|
-
# self._logger.to_csv(**kwargs)
|
38
|
+
if sum(lcall([system, instruction, response], bool)) != 1:
|
39
|
+
raise ValueError("Error: Message must have one and only one role.")
|
73
40
|
|
74
|
-
|
75
|
-
|
76
|
-
# instruction: Optional[Any] = None,
|
77
|
-
# context: Optional[Any] = None,
|
78
|
-
# response: Optional[Any] = None,
|
79
|
-
# name: Optional[str] = None) -> Message:
|
80
|
-
# """
|
81
|
-
# Creates a specific type of message based on the provided parameters.
|
82
|
-
|
83
|
-
# Parameters:
|
84
|
-
# system (Optional[Any]): System message content.
|
85
|
-
|
86
|
-
# instruction (Optional[Any]): Instruction message content.
|
87
|
-
|
88
|
-
# context (Optional[Any]): Context for the instruction message.
|
89
|
-
|
90
|
-
# response (Optional[Any]): Response message content.
|
41
|
+
else:
|
42
|
+
msg = 0
|
91
43
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# msg.create_message(response=response,
|
110
|
-
# name=name,)
|
111
|
-
# elif instruction:
|
112
|
-
# msg = Instruction()
|
113
|
-
# msg.create_message(instruction=instruction,
|
114
|
-
# context=context,
|
115
|
-
# name=name,)
|
116
|
-
# elif system:
|
117
|
-
# msg = System()
|
118
|
-
# msg.create_message(system=system,
|
119
|
-
# name=name,)
|
120
|
-
# return msg
|
44
|
+
if response:
|
45
|
+
msg = Response()
|
46
|
+
msg._create_message(
|
47
|
+
response=response, name=name,
|
48
|
+
)
|
49
|
+
elif instruction:
|
50
|
+
msg = Instruction()
|
51
|
+
msg._create_message(
|
52
|
+
instruction=instruction, context=context,
|
53
|
+
name=name,
|
54
|
+
)
|
55
|
+
elif system:
|
56
|
+
msg = System()
|
57
|
+
msg._create_message(
|
58
|
+
system=system, name=name,
|
59
|
+
)
|
60
|
+
return msg
|
121
61
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# response (Optional[Any]): Response message content.
|
141
|
-
|
142
|
-
# name (Optional[str]): Name associated with the message.
|
143
|
-
|
144
|
-
# obj (bool): If True, returns the Message object and its dictionary representation. Defaults to False.
|
145
|
-
|
146
|
-
# log_ (bool): If True, logs the created message. Defaults to True.
|
147
|
-
|
148
|
-
# Returns:
|
149
|
-
# Union[Message, Tuple[Message, Dict]]: The created message in the specified format.
|
150
|
-
# """
|
62
|
+
def create_message(
|
63
|
+
self,
|
64
|
+
system: Optional[Any] = None,
|
65
|
+
instruction: Optional[Any] = None,
|
66
|
+
context: Optional[Any] = None,
|
67
|
+
response: Optional[Any] = None,
|
68
|
+
name: Optional[str] = None,
|
69
|
+
obj: bool = True,
|
70
|
+
log_: bool = True
|
71
|
+
) -> Union[Message, Tuple[Message, Dict]]:
|
72
|
+
|
73
|
+
msg = self._create_message(
|
74
|
+
system=system,
|
75
|
+
instruction=instruction,
|
76
|
+
context=context,
|
77
|
+
response=response,
|
78
|
+
name=name
|
79
|
+
)
|
151
80
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# name=name)
|
157
|
-
# if log_:
|
158
|
-
# self.log_message(msg)
|
159
|
-
# if obj:
|
160
|
-
# return (msg, msg._to_message())
|
161
|
-
# else:
|
162
|
-
# return msg._to_message()
|
81
|
+
if log_:
|
82
|
+
self.log_message(msg)
|
83
|
+
if obj:
|
84
|
+
return msg
|
163
85
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
|
4
|
+
# credit to OpenAI for the following object
|
5
|
+
@dataclass
|
6
|
+
class StatusTracker:
|
7
|
+
"""
|
8
|
+
Class for keeping track of various task statuses.
|
9
|
+
|
10
|
+
This class serves as a simple way to monitor different types of task
|
11
|
+
outcomes and errors within a system. It uses dataclasses for easy
|
12
|
+
creation and management of state.
|
13
|
+
|
14
|
+
Attributes:
|
15
|
+
num_tasks_started:
|
16
|
+
The number of tasks that have been initiated.
|
17
|
+
num_tasks_in_progress:
|
18
|
+
The number of tasks currently being processed.
|
19
|
+
num_tasks_succeeded:
|
20
|
+
The number of tasks that have completed successfully.
|
21
|
+
num_tasks_failed:
|
22
|
+
The number of tasks that have failed.
|
23
|
+
num_rate_limit_errors:
|
24
|
+
The number of tasks that failed due to rate limiting.
|
25
|
+
num_api_errors:
|
26
|
+
The number of tasks that failed due to API errors.
|
27
|
+
num_other_errors:
|
28
|
+
The number of tasks that failed due to other errors.
|
29
|
+
"""
|
30
|
+
num_tasks_started: int = 0
|
31
|
+
num_tasks_in_progress: int = 0
|
32
|
+
num_tasks_succeeded: int = 0
|
33
|
+
num_tasks_failed: int = 0
|
34
|
+
num_rate_limit_errors: int = 0
|
35
|
+
num_api_errors: int = 0
|
36
|
+
num_other_errors: int = 0
|
37
|
+
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import json
|
2
2
|
import asyncio
|
3
3
|
from typing import Dict
|
4
|
-
from
|
5
|
-
from
|
4
|
+
from lionagi.utils import lcall
|
5
|
+
from lionagi.schema import BaseNode, Tool
|
6
6
|
|
7
7
|
|
8
8
|
class ToolManager(BaseNode):
|
9
9
|
registry: Dict = {}
|
10
10
|
|
11
|
-
def
|
11
|
+
def name_existed(self, name: str):
|
12
12
|
return True if name in self.registry.keys() else False
|
13
13
|
|
14
14
|
def _register_tool(self, tool): #,update=False, new=False, prefix=None, postfix=None):
|
@@ -28,12 +28,14 @@ class ToolManager(BaseNode):
|
|
28
28
|
|
29
29
|
# name = f"{prefix or ''}{name}{postfix}" if new else tool.func.__name__
|
30
30
|
|
31
|
+
if not isinstance(tool, Tool):
|
32
|
+
raise TypeError('Please register a Tool object.')
|
31
33
|
name = tool.schema_['function']['name']
|
32
34
|
self.registry.update({name: tool})
|
33
35
|
|
34
36
|
async def invoke(self, func_call):
|
35
37
|
name, kwargs = func_call
|
36
|
-
if self.
|
38
|
+
if self.name_existed(name):
|
37
39
|
tool = self.registry[name]
|
38
40
|
func = tool.func
|
39
41
|
parser = tool.parser
|
@@ -48,7 +50,7 @@ class ToolManager(BaseNode):
|
|
48
50
|
raise ValueError(f"Function {name} is not registered.")
|
49
51
|
|
50
52
|
@staticmethod
|
51
|
-
def
|
53
|
+
def get_function_call(response):
|
52
54
|
"""
|
53
55
|
Extract function name and arguments from a response JSON.
|
54
56
|
|
@@ -73,7 +75,7 @@ class ToolManager(BaseNode):
|
|
73
75
|
def register_tools(self, tools): #, update=False, new=False, prefix=None, postfix=None ):
|
74
76
|
lcall(tools, self._register_tool) #, update=update, new=new, prefix=prefix, postfix=postfix)
|
75
77
|
|
76
|
-
def
|
78
|
+
def to_tool_schema_list(self):
|
77
79
|
schema_list = []
|
78
80
|
for tool in self.registry.values():
|
79
81
|
schema_list.append(tool.schema_)
|
lionagi/schema/__init__.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
from .
|
1
|
+
from .base_node import BaseNode
|
2
2
|
from .base_tool import Tool
|
3
3
|
from .data_logger import DataLogger
|
4
|
+
from .data_node import DataNode
|
4
5
|
|
5
6
|
__all__ = [
|
6
7
|
"BaseNode",
|
7
8
|
"DataNode",
|
8
|
-
"Message",
|
9
9
|
"Tool",
|
10
|
-
"DataLogger"
|
10
|
+
"DataLogger",
|
11
11
|
]
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# uses utils
|
2
|
+
import json
|
3
|
+
import xml.etree.ElementTree as ET
|
4
|
+
from typing import Any, Dict, Optional, TypeVar, Type, List, Callable, Union
|
5
|
+
from pydantic import BaseModel, Field, AliasChoices
|
6
|
+
|
7
|
+
from lionagi.utils import (
|
8
|
+
create_id, is_schema, change_dict_key, create_copy,
|
9
|
+
encrypt, decrypt, dict_to_xml
|
10
|
+
)
|
11
|
+
|
12
|
+
T = TypeVar('T', bound='BaseNode')
|
13
|
+
|
14
|
+
|
15
|
+
class BaseNode(BaseModel):
|
16
|
+
"""
|
17
|
+
A foundational building block for representing a node in a graph-like structure.
|
18
|
+
This class includes functionalities for serialization, metadata manipulation,
|
19
|
+
content encryption/decryption, and utility methods.
|
20
|
+
|
21
|
+
Attributes:
|
22
|
+
id_ (str): Unique identifier for the node, aliased as 'node_id'.
|
23
|
+
metadata (Dict[str, Any]): Dictionary of metadata related to the node.
|
24
|
+
label (Optional[str]): Label categorizing or identifying the node.
|
25
|
+
related_nodes (List[str]): Identifiers for nodes related to this node.
|
26
|
+
content (Union[str, Dict[str, Any], None, Any]): Content of the node.
|
27
|
+
"""
|
28
|
+
id_: str = Field(default_factory=lambda: str(create_id()), alias="node_id")
|
29
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
30
|
+
label: Optional[str] = None
|
31
|
+
related_nodes: List[str] = Field(default_factory=list)
|
32
|
+
content: Union[str, Dict[str, Any], None, Any] = Field(
|
33
|
+
default=None, validation_alias=AliasChoices('text', 'page_content', 'chunk_content')
|
34
|
+
)
|
35
|
+
|
36
|
+
class Config:
|
37
|
+
extra = 'allow'
|
38
|
+
populate_by_name = True
|
39
|
+
validate_assignment = True
|
40
|
+
str_strip_whitespace = True
|
41
|
+
|
42
|
+
# from-to [json, dict, xml]
|
43
|
+
@classmethod
|
44
|
+
def from_dict(cls, data: Dict[str, Any]) -> T:
|
45
|
+
"""Creates a BaseNode instance from a dictionary."""
|
46
|
+
return cls(**data)
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def from_json(cls: Type[T], json_str: str, **kwargs) -> T:
|
50
|
+
"""Creates a BaseNode instance from a JSON string."""
|
51
|
+
try:
|
52
|
+
data = json.loads(json_str, **kwargs)
|
53
|
+
return cls(**data)
|
54
|
+
except json.JSONDecodeError as e:
|
55
|
+
raise ValueError("Invalid JSON string provided for deserialization.") from e
|
56
|
+
|
57
|
+
@classmethod
|
58
|
+
def from_xml(cls, xml_str: str) -> T:
|
59
|
+
"""Creates a BaseNode instance from an XML string."""
|
60
|
+
root = ET.fromstring(xml_str)
|
61
|
+
data = cls._xml_to_dict(root)
|
62
|
+
return cls(**data)
|
63
|
+
|
64
|
+
def to_json(self) -> str:
|
65
|
+
"""Converts the BaseNode instance into a JSON string."""
|
66
|
+
return self.model_dump_json(by_alias=True)
|
67
|
+
|
68
|
+
def to_dict(self) -> Dict[str, Any]:
|
69
|
+
"""Converts the BaseNode instance into a dictionary."""
|
70
|
+
return self.model_dump(by_alias=True)
|
71
|
+
|
72
|
+
def to_xml(self) -> str:
|
73
|
+
"""Converts the BaseNode instance into an XML string."""
|
74
|
+
return dict_to_xml(self.to_dict())
|
75
|
+
|
76
|
+
# Metadata manipulation methods
|
77
|
+
def set_meta(self, metadata_: Dict[str, Any]) -> None:
|
78
|
+
"""Sets the metadata for the node."""
|
79
|
+
self.metadata = metadata_
|
80
|
+
|
81
|
+
def get_meta_key(self, key: str) -> Any:
|
82
|
+
"""Retrieves a specific metadata value by key."""
|
83
|
+
return self.metadata.get(key)
|
84
|
+
|
85
|
+
def change_meta_key(self, old_key: str, new_key: str) -> None:
|
86
|
+
"""Changes a metadata key from old_key to new_key."""
|
87
|
+
change_dict_key(self.metadata,old_key=old_key, new_key=new_key)
|
88
|
+
|
89
|
+
def delete_meta_key(self, key: str) -> None:
|
90
|
+
"""Deletes a metadata key from the node."""
|
91
|
+
if key in self.metadata:
|
92
|
+
del self.metadata[key]
|
93
|
+
|
94
|
+
def merge_metadata(self, other_metadata: Dict[str, Any], overwrite: bool = True) -> None:
|
95
|
+
"""Merges another metadata dictionary into the node's metadata."""
|
96
|
+
if not overwrite:
|
97
|
+
other_metadata = ({
|
98
|
+
k: v for k, v in other_metadata.items()
|
99
|
+
if k not in self.metadata
|
100
|
+
})
|
101
|
+
self.metadata.update(other_metadata)
|
102
|
+
|
103
|
+
def clear_metadata(self) -> None:
|
104
|
+
"""Clears all metadata from the node."""
|
105
|
+
self.metadata.clear()
|
106
|
+
|
107
|
+
@property
|
108
|
+
def meta_keys(self) -> List[str]:
|
109
|
+
"""Returns a list of all metadata keys."""
|
110
|
+
return list(self.metadata.keys())
|
111
|
+
|
112
|
+
def has_meta_key(self, key: str) -> bool:
|
113
|
+
"""Checks if a specific metadata key exists."""
|
114
|
+
return key in self.metadata
|
115
|
+
|
116
|
+
def filter_meta(self, filter_func: Callable[[Any], bool]) -> Dict[str, Any]:
|
117
|
+
"""Filters metadata based on a provided function."""
|
118
|
+
return {k: v for k, v in self.metadata.items() if filter_func(v)}
|
119
|
+
|
120
|
+
def apply_to_meta(self, apply_func: Callable[[Any], Any]) -> None:
|
121
|
+
"""Applies a function to each metadata value."""
|
122
|
+
for key in self.metadata:
|
123
|
+
self.metadata[key] = apply_func(self.metadata[key])
|
124
|
+
|
125
|
+
def meta_schema_is_valid(self, schema: Dict[str, type]) -> bool:
|
126
|
+
"""Checks if the metadata matches a given schema."""
|
127
|
+
return is_schema(dict_=self.metadata, schema=schema)
|
128
|
+
|
129
|
+
def update_meta(self, **kwargs) -> None:
|
130
|
+
"""Updates metadata with the provided key-value pairs."""
|
131
|
+
self.metadata.update(kwargs)
|
132
|
+
|
133
|
+
# Encryption methods
|
134
|
+
def encrypt_content(self, key: str) -> None:
|
135
|
+
"""Encrypts the content of the node."""
|
136
|
+
self.content = encrypt(self.content, key)
|
137
|
+
|
138
|
+
def decrypt_content(self, key: str) -> None:
|
139
|
+
"""Decrypts the content of the node."""
|
140
|
+
self.content = decrypt(self.content, key)
|
141
|
+
|
142
|
+
# Getters and setters
|
143
|
+
def set_content(self, content: Optional[Any]) -> None:
|
144
|
+
"""Sets the content of the node."""
|
145
|
+
self.content = content
|
146
|
+
|
147
|
+
def get_content(self) -> Optional[Any]:
|
148
|
+
"""Retrieves the content of the node."""
|
149
|
+
return self.content
|
150
|
+
|
151
|
+
def set_id(self, id_: str) -> None:
|
152
|
+
"""Sets the ID of the node."""
|
153
|
+
self.id_ = id_
|
154
|
+
|
155
|
+
def get_id(self) -> str:
|
156
|
+
"""Retrieves the ID of the node."""
|
157
|
+
return self.id_
|
158
|
+
|
159
|
+
def add_related_node(self, node_id: str) -> None:
|
160
|
+
"""Adds a related node ID."""
|
161
|
+
if node_id not in self.related_nodes:
|
162
|
+
self.related_nodes.append(node_id)
|
163
|
+
|
164
|
+
def remove_related_node(self, node_id: str) -> None:
|
165
|
+
"""Removes a related node ID."""
|
166
|
+
self.related_nodes = [id_ for id_ in self.related_nodes if id_ != node_id]
|
167
|
+
|
168
|
+
# Utility methods
|
169
|
+
def is_empty(self) -> bool:
|
170
|
+
"""Checks if the node is empty (no content and metadata)."""
|
171
|
+
return not self.content and not self.metadata
|
172
|
+
|
173
|
+
def has_label(self, label: str) -> bool:
|
174
|
+
"""Checks if the node has a specific label."""
|
175
|
+
return self.label == label
|
176
|
+
|
177
|
+
def is_metadata_key_present(self, key: str) -> bool:
|
178
|
+
"""Checks if a specific key is present in the metadata."""
|
179
|
+
return key in self.metadata
|
180
|
+
|
181
|
+
def copy(self, n: int = 1) -> Union[List[T], T]:
|
182
|
+
"""
|
183
|
+
Creates one or multiple deep copies of the node.
|
184
|
+
|
185
|
+
Parameters:
|
186
|
+
n (int): Number of copies to create.
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
Union[List[T], T]: A copy or list of copies of the node.
|
190
|
+
"""
|
191
|
+
return create_copy(self, n)
|
192
|
+
|
193
|
+
def data_equals(self, other: 'BaseNode') -> bool:
|
194
|
+
"""
|
195
|
+
Checks if the node's data equals another node's data.
|
196
|
+
|
197
|
+
Parameters:
|
198
|
+
other (BaseNode): Another node to compare with.
|
199
|
+
|
200
|
+
Returns:
|
201
|
+
bool: True if data is equal, False otherwise.
|
202
|
+
"""
|
203
|
+
return (
|
204
|
+
self.content == other.content and
|
205
|
+
self.metadata == other.metadata and
|
206
|
+
self.related_nodes == other.related_nodes
|
207
|
+
)
|
208
|
+
|
209
|
+
def is_copy_of(self, other: 'BaseNode') -> bool:
|
210
|
+
"""
|
211
|
+
Checks if the node is a copy of another node.
|
212
|
+
|
213
|
+
Parameters:
|
214
|
+
other (BaseNode): Another node to compare with.
|
215
|
+
|
216
|
+
Returns:
|
217
|
+
bool: True if it is a copy, False otherwise.
|
218
|
+
"""
|
219
|
+
return (
|
220
|
+
self.data_equals(other) and
|
221
|
+
self is not other
|
222
|
+
)
|
223
|
+
|
224
|
+
def __eq__(self, other: 'BaseNode') -> bool:
|
225
|
+
"""
|
226
|
+
Overrides the equality operator to compare nodes.
|
227
|
+
|
228
|
+
Parameters:
|
229
|
+
other (BaseNode): Another node to compare with.
|
230
|
+
|
231
|
+
Returns:
|
232
|
+
bool: True if nodes are equal, False otherwise.
|
233
|
+
"""
|
234
|
+
return (self.id_ == other.id_ and self.data_equals(other))
|
235
|
+
|
236
|
+
def __str__(self) -> str:
|
237
|
+
"""
|
238
|
+
Returns a user-friendly string representation of the BaseNode.
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
str: A string representation of the BaseNode.
|
242
|
+
"""
|
243
|
+
content_preview = (str(self.content)[:75] + '...') if len(str(self.content)) > 75 else str(self.content)
|
244
|
+
return f"{self.__class__.__name__}(id={self.id_}, label={self.label}, content='{content_preview}')"
|
245
|
+
|
246
|
+
def __repr__(self):
|
247
|
+
"""
|
248
|
+
Official string representation of Message object.
|
249
|
+
Utilizes the json representation of the object for clarity.
|
250
|
+
"""
|
251
|
+
return f"{self.__class__.__name__}({self.to_json()})"
|
lionagi/schema/base_tool.py
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
from typing import Any
|
2
|
-
from
|
2
|
+
from pydantic import field_serializer
|
3
|
+
from .base_node import BaseNode
|
3
4
|
|
4
5
|
class Tool(BaseNode):
|
5
|
-
name: str = None
|
6
|
+
# name: str = None
|
6
7
|
func: Any
|
7
8
|
content: Any = None
|
8
9
|
parser: Any = None
|
9
|
-
schema_:
|
10
|
+
schema_: dict
|
11
|
+
|
12
|
+
@field_serializer('func')
|
13
|
+
def serialize_func(self, func):
|
14
|
+
return func.__name__
|
lionagi/schema/data_logger.py
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
from .base_node import BaseNode
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
|
5
|
+
class DataNode(BaseNode):
|
6
|
+
|
7
|
+
def to_llama_index(self, **kwargs):
|
8
|
+
# to llama_index textnode
|
9
|
+
from lionagi.bridge.llama_index import to_llama_index_textnode
|
10
|
+
return to_llama_index_textnode(self, **kwargs)
|
11
|
+
|
12
|
+
def to_langchain(self, **kwargs):
|
13
|
+
# to langchain document
|
14
|
+
from lionagi.bridge.langchain import to_langchain_document
|
15
|
+
return to_langchain_document(self, **kwargs)
|
16
|
+
|
17
|
+
@classmethod
|
18
|
+
def from_llama_index(cls, llama_node: Any, **kwargs):
|
19
|
+
llama_dict = llama_node.to_dict(**kwargs)
|
20
|
+
return cls.from_dict(llama_dict)
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_langchain(cls, lc_doc: Any):
|
24
|
+
info_json = lc_doc.to_json()
|
25
|
+
info_node = {'lc_id': info_json['id']}
|
26
|
+
info_node = {**info_node, **info_json['kwargs']}
|
27
|
+
return cls(**info_node)
|
28
|
+
|
29
|
+
|
30
|
+
class File(DataNode):
|
31
|
+
|
32
|
+
...
|
33
|
+
|
34
|
+
|
35
|
+
class Chunk(DataNode):
|
36
|
+
|
37
|
+
...
|
lionagi/services/__init__.py
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
from .chatcompletion import ChatCompletion
|
2
1
|
from .base_api_service import BaseAPIService, BaseAPIRateLimiter
|
3
2
|
from .oai import OpenAIService
|
4
3
|
from .openrouter import OpenRouterService
|
5
4
|
|
6
5
|
|
7
|
-
|
8
6
|
__all__ = [
|
9
7
|
"BaseAPIService",
|
8
|
+
"BaseAPIRateLimiter",
|
10
9
|
"OpenAIService",
|
11
10
|
"OpenRouterService",
|
12
|
-
"ChatCompletion",
|
13
|
-
"BaseAPIRateLimiter"
|
14
11
|
]
|