lionagi 0.0.104__py3-none-any.whl → 0.0.105__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/session/conversation.py +47 -43
- lionagi/session/message.py +100 -37
- lionagi/session/session.py +197 -71
- lionagi/tools/__init__.py +0 -0
- lionagi/utils/__init__.py +2 -1
- lionagi/utils/doc_util.py +38 -38
- lionagi/utils/sys_util.py +6 -2
- lionagi/utils/tool_util.py +194 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.104.dist-info → lionagi-0.0.105.dist-info}/METADATA +4 -3
- lionagi-0.0.105.dist-info/RECORD +21 -0
- lionagi-0.0.104.dist-info/RECORD +0 -19
- {lionagi-0.0.104.dist-info → lionagi-0.0.105.dist-info}/LICENSE +0 -0
- {lionagi-0.0.104.dist-info → lionagi-0.0.105.dist-info}/WHEEL +0 -0
- {lionagi-0.0.104.dist-info → lionagi-0.0.105.dist-info}/top_level.txt +0 -0
lionagi/session/conversation.py
CHANGED
@@ -2,87 +2,91 @@ from .message import Message
|
|
2
2
|
|
3
3
|
class Conversation:
|
4
4
|
"""
|
5
|
-
A class
|
5
|
+
A class representing a conversation between users and the assistant.
|
6
6
|
|
7
|
-
This class
|
8
|
-
|
9
|
-
appending the last response, and keeping the last N exchanges.
|
7
|
+
This class manages the exchange of messages within a conversation, including system settings,
|
8
|
+
user instructions, and assistant responses.
|
10
9
|
|
11
10
|
Attributes:
|
12
|
-
response_counts:
|
13
|
-
messages: A list
|
14
|
-
msg: An instance of the Message class for creating
|
15
|
-
responses: A list
|
11
|
+
response_counts (int): The count of assistant responses in the conversation.
|
12
|
+
messages (list): A list to store messages in the conversation.
|
13
|
+
msg (Message): An instance of the Message class for creating messages.
|
14
|
+
responses (list): A list to store assistant responses in the conversation.
|
16
15
|
|
17
16
|
Methods:
|
18
|
-
initiate_conversation
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
initiate_conversation(system, instruction, context=None, name=None):
|
18
|
+
Initiate a conversation with a system setting and user instruction.
|
19
|
+
|
20
|
+
add_messages(system, instruction, context=None, response=None, tool=None, name=None):
|
21
|
+
Add messages to the conversation, including system setting, user instruction, and assistant response.
|
22
|
+
|
23
|
+
change_system(system):
|
24
|
+
Change the system setting in the conversation.
|
25
|
+
|
26
|
+
keep_last_n_exchanges(n: int):
|
27
|
+
Keep the last n exchanges in the conversation.
|
23
28
|
"""
|
24
|
-
|
25
29
|
response_counts = 0
|
26
30
|
|
27
31
|
def __init__(self, messages=None) -> None:
|
28
32
|
"""
|
29
33
|
Initialize a Conversation object.
|
30
34
|
|
31
|
-
|
32
|
-
messages: A list of messages to initialize the conversation.
|
35
|
+
Parameters:
|
36
|
+
messages (list): A list of messages to initialize the conversation. Default is None.
|
37
|
+
|
33
38
|
"""
|
34
39
|
self.messages = messages or []
|
35
40
|
self.msg = Message()
|
36
41
|
self.responses = []
|
37
42
|
|
38
|
-
def initiate_conversation(self, system, instruction, context=None):
|
43
|
+
def initiate_conversation(self, system, instruction, context=None, name=None):
|
39
44
|
"""
|
40
|
-
|
45
|
+
Initiate a conversation with a system setting and user instruction.
|
41
46
|
|
42
|
-
|
43
|
-
system: The
|
44
|
-
instruction: The
|
45
|
-
context: Additional context for the
|
47
|
+
Parameters:
|
48
|
+
system (str): The system setting for the conversation.
|
49
|
+
instruction (str): The user instruction to initiate the conversation.
|
50
|
+
context (dict): Additional context for the conversation. Default is None.
|
51
|
+
name (str): The name associated with the user. Default is None.
|
46
52
|
"""
|
47
53
|
self.messages, self.responses = [], []
|
48
54
|
self.add_messages(system=system)
|
49
|
-
self.add_messages(instruction=instruction, context=context)
|
55
|
+
self.add_messages(instruction=instruction, context=context, name=name)
|
50
56
|
|
51
|
-
|
57
|
+
# modify the message adding to accomodate tools
|
58
|
+
def add_messages(self, system=None, instruction=None, context=None, response=None, tool=None, name=None):
|
52
59
|
"""
|
53
|
-
Add messages to the conversation.
|
60
|
+
Add messages to the conversation, including system setting, user instruction, and assistant response.
|
54
61
|
|
55
|
-
|
56
|
-
system: The
|
57
|
-
instruction: The content
|
58
|
-
context: Additional context for the
|
59
|
-
response: The content
|
62
|
+
Parameters:
|
63
|
+
system (str): The system setting for the message. Default is None.
|
64
|
+
instruction (str): The instruction content for the message. Default is None.
|
65
|
+
context (dict): Additional context for the message. Default is None.
|
66
|
+
response (dict): The response content for the message. Default is None.
|
67
|
+
tool (dict): The tool information for the message. Default is None.
|
68
|
+
name (str): The name associated with the message. Default is None.
|
60
69
|
"""
|
61
|
-
msg = self.msg(system=system, instruction=instruction, response=response,
|
70
|
+
msg = self.msg(system=system, instruction=instruction, context=context, response=response, tool=tool, name=name)
|
62
71
|
self.messages.append(msg)
|
63
72
|
|
64
73
|
def change_system(self, system):
|
65
74
|
"""
|
66
|
-
Change the system
|
75
|
+
Change the system setting in the conversation.
|
67
76
|
|
68
|
-
|
69
|
-
system: The new
|
77
|
+
Parameters:
|
78
|
+
system (str): The new system setting for the conversation.
|
70
79
|
"""
|
71
80
|
self.messages[0] = self.msg(system=system)
|
72
81
|
|
73
|
-
def append_last_response(self):
|
74
|
-
"""
|
75
|
-
Append the last response to the conversation.
|
76
|
-
"""
|
77
|
-
self.add_messages(response=self.responses[-1])
|
78
|
-
|
79
82
|
def keep_last_n_exchanges(self, n: int):
|
80
83
|
"""
|
81
|
-
Keep
|
84
|
+
Keep the last n exchanges in the conversation.
|
82
85
|
|
83
|
-
|
84
|
-
n: The number of exchanges to keep.
|
86
|
+
Parameters:
|
87
|
+
n (int): The number of exchanges to keep.
|
85
88
|
"""
|
89
|
+
# keep last n_exchanges, one exchange is marked by one assistant response
|
86
90
|
response_indices = [
|
87
91
|
index for index, message in enumerate(self.messages[1:]) if message["role"] == "assistant"
|
88
92
|
]
|
lionagi/session/message.py
CHANGED
@@ -1,76 +1,139 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
import json
|
3
|
-
from ..utils.sys_util import create_id
|
3
|
+
from ..utils.sys_util import create_id, l_call
|
4
4
|
from ..utils.log_util import DataLogger
|
5
5
|
|
6
6
|
|
7
7
|
class Message:
|
8
8
|
"""
|
9
|
-
A class
|
9
|
+
A class representing a message in a conversation.
|
10
10
|
|
11
|
-
This class
|
12
|
-
(user, assistant, or system) in a conversation. It includes functionality to process,
|
13
|
-
log, and manage messages.
|
11
|
+
This class encapsulates messages from users, the assistant, systems, and external tools.
|
14
12
|
|
15
13
|
Attributes:
|
16
|
-
role: The role of the message
|
17
|
-
content: The content of the message.
|
18
|
-
|
19
|
-
|
14
|
+
role (str): The role of the message, indicating if it's from the user, assistant, system, or tool.
|
15
|
+
content: The content of the message, which can be an instruction, response, system setting, or tool information.
|
16
|
+
name (str): The name associated with the message, specifying the source (user, assistant, system, or tool).
|
17
|
+
metadata (dict): Additional metadata including id, timestamp, and name.
|
18
|
+
_logger (DataLogger): An instance of the DataLogger class for logging message details.
|
20
19
|
|
21
20
|
Methods:
|
22
|
-
|
21
|
+
create_message(system, instruction, context, response, tool, name):
|
22
|
+
Create a message based on the provided information.
|
23
|
+
|
24
|
+
to_json() -> dict:
|
25
|
+
Convert the message to a JSON format.
|
26
|
+
|
27
|
+
__call__(system, instruction, context, response, name, tool) -> dict:
|
28
|
+
Create and return a message in JSON format.
|
29
|
+
|
30
|
+
to_csv(dir, filename, verbose, timestamp, dir_exist_ok, file_exist_ok):
|
31
|
+
Save the message to a CSV file.
|
23
32
|
"""
|
24
33
|
def __init__(self) -> None:
|
25
34
|
"""
|
26
|
-
Initialize a Message object
|
35
|
+
Initialize a Message object.
|
27
36
|
"""
|
28
37
|
self.role = None
|
29
38
|
self.content = None
|
30
|
-
self.
|
31
|
-
self.
|
32
|
-
|
33
|
-
|
39
|
+
self.name = None
|
40
|
+
self.metadata = None
|
41
|
+
self._logger = DataLogger()
|
42
|
+
|
43
|
+
def create_message(self, system=None, instruction=None, context=None, response=None, tool=None, name=None):
|
34
44
|
"""
|
35
|
-
|
45
|
+
Create a message based on the provided information.
|
36
46
|
|
37
|
-
|
38
|
-
system: The
|
39
|
-
instruction: The content
|
40
|
-
|
41
|
-
|
42
|
-
|
47
|
+
Parameters:
|
48
|
+
system (str): The system setting for the message. Default is None.
|
49
|
+
instruction (str): The instruction content for the message. Default is None.
|
50
|
+
context (dict): Additional context for the message. Default is None.
|
51
|
+
response (dict): The response content for the message. Default is None.
|
52
|
+
tool (dict): The tool information for the message. Default is None.
|
53
|
+
name (str): The name associated with the message. Default is None.
|
43
54
|
"""
|
55
|
+
if sum(l_call([system, instruction, response, tool], bool)) > 1:
|
56
|
+
raise ValueError("Error: Message cannot have more than one role.")
|
44
57
|
|
45
|
-
|
46
|
-
raise ValueError("Message cannot have more than one role.")
|
47
|
-
else:
|
58
|
+
else:
|
48
59
|
if response:
|
49
60
|
self.role = "assistant"
|
50
|
-
|
51
|
-
|
61
|
+
response = response["message"]
|
62
|
+
if str(response['content']) == "None":
|
63
|
+
try:
|
64
|
+
# currently can only support a single function response
|
65
|
+
if response['tool_calls'][0]['type'] == 'function':
|
66
|
+
self.name = name or ("func_" + response['tool_calls'][0]['function']['name'])
|
67
|
+
content = response['tool_calls'][0]['function']['arguments']
|
68
|
+
self.content = {"function":self.name, "arguments": content}
|
69
|
+
except:
|
70
|
+
raise ValueError("Response message must be one of regular response or function calling")
|
71
|
+
else:
|
72
|
+
self.content = response['content']
|
73
|
+
self.name = name or "assistant"
|
52
74
|
elif instruction:
|
53
75
|
self.role = "user"
|
54
|
-
self.sender = sender or "user"
|
55
76
|
self.content = {"instruction": instruction}
|
77
|
+
self.name = name or "user"
|
56
78
|
if context:
|
57
|
-
self.content.update(context)
|
79
|
+
self.content.update({"context": context})
|
58
80
|
elif system:
|
59
81
|
self.role = "system"
|
60
|
-
self.sender = sender or "system"
|
61
82
|
self.content = system
|
83
|
+
self.name = name or "system"
|
84
|
+
elif tool:
|
85
|
+
self.role = "tool"
|
86
|
+
self.content = tool
|
87
|
+
self.name = name or "tool"
|
88
|
+
|
89
|
+
def to_json(self):
|
90
|
+
"""
|
91
|
+
Convert the message to a JSON format.
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
- dict: The message in JSON format.
|
95
|
+
"""
|
62
96
|
out = {
|
63
97
|
"role": self.role,
|
64
98
|
"content": json.dumps(self.content) if isinstance(self.content, dict) else self.content
|
65
99
|
}
|
66
|
-
|
67
|
-
|
100
|
+
|
101
|
+
self.metadata = {
|
68
102
|
"id": create_id(),
|
69
103
|
"timestamp": datetime.now().isoformat(),
|
70
|
-
"
|
71
|
-
|
72
|
-
self.
|
104
|
+
"name": self.name}
|
105
|
+
|
106
|
+
self._logger({**self.metadata, **out})
|
73
107
|
return out
|
108
|
+
|
109
|
+
def __call__(self, system=None, instruction=None, context=None, response=None, name=None, tool=None):
|
110
|
+
"""
|
111
|
+
Create and return a message in JSON format.
|
112
|
+
|
113
|
+
Parameters:
|
114
|
+
system (str): The system setting for the message. Default is None.
|
115
|
+
instruction (str): The instruction content for the message. Default is None.
|
116
|
+
context (dict): Additional context for the message. Default is None.
|
117
|
+
response (dict): The response content for the message. Default is None.
|
118
|
+
name (str): The name associated with the message. Default is None.
|
119
|
+
tool (dict): The tool information for the message. Default is None.
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
dict: The message in JSON format.
|
123
|
+
"""
|
124
|
+
self.create_message(system, instruction, context, response, tool, name)
|
125
|
+
return self.to_json()
|
74
126
|
|
75
|
-
def
|
76
|
-
|
127
|
+
def to_csv(self, dir=None, filename=None, verbose=True, timestamp=True, dir_exist_ok=True, file_exist_ok=False):
|
128
|
+
"""
|
129
|
+
Save the message to a CSV file.
|
130
|
+
|
131
|
+
Parameters:
|
132
|
+
dir (str): The directory path for saving the CSV file. Default is None.
|
133
|
+
filename (str): The filename for the CSV file. Default is None.
|
134
|
+
verbose (bool): Whether to include verbose information in the CSV. Default is True.
|
135
|
+
timestamp (bool): Whether to include timestamps in the CSV. Default is True.
|
136
|
+
dir_exist_ok (bool): Whether to allow the directory to exist. Default is True.
|
137
|
+
file_exist_ok (bool): Whether to allow the file to exist. Default is False.
|
138
|
+
"""
|
139
|
+
self._logger.to_csv(dir, filename, verbose, timestamp, dir_exist_ok, file_exist_ok)
|
lionagi/session/session.py
CHANGED
@@ -3,108 +3,222 @@ import asyncio
|
|
3
3
|
from typing import Any
|
4
4
|
|
5
5
|
from .conversation import Conversation
|
6
|
-
from ..
|
6
|
+
from ..utils.sys_util import to_list
|
7
7
|
from ..utils.log_util import DataLogger
|
8
8
|
from ..utils.api_util import StatusTracker
|
9
|
+
from ..utils.tool_util import ToolManager
|
9
10
|
from ..api.oai_service import OpenAIService
|
10
11
|
|
12
|
+
from ..api.oai_config import oai_llmconfig
|
13
|
+
|
11
14
|
|
12
15
|
status_tracker = StatusTracker()
|
13
16
|
OAIService = OpenAIService()
|
14
17
|
|
15
18
|
class Session():
|
16
19
|
"""
|
17
|
-
A class representing a conversation session with
|
20
|
+
A class representing a conversation session with a conversational AI system.
|
18
21
|
|
19
|
-
This class manages
|
20
|
-
and logs the interactions using a DataLogger.
|
22
|
+
This class manages the flow of conversation, system settings, and interactions with external tools.
|
21
23
|
|
22
24
|
Attributes:
|
23
|
-
conversation: An instance of the Conversation class
|
24
|
-
system: The system
|
25
|
-
llmconfig: Configuration
|
26
|
-
|
27
|
-
api_service: An instance of the API service for making
|
25
|
+
conversation (Conversation): An instance of the Conversation class to manage messages.
|
26
|
+
system (str): The current system setting for the conversation.
|
27
|
+
llmconfig (dict): Configuration settings for the language model.
|
28
|
+
_logger (DataLogger): An instance of the DataLogger class for logging conversation details.
|
29
|
+
api_service: An instance of the API service for making calls to the conversational AI model.
|
30
|
+
toolmanager (ToolManager): An instance of the ToolManager class for managing external tools.
|
28
31
|
|
29
32
|
Methods:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
set_dir(dir):
|
34
|
+
Set the directory for logging.
|
35
|
+
|
36
|
+
set_system(system):
|
37
|
+
Set the system for the conversation.
|
38
|
+
|
39
|
+
set_llmconfig(llmconfig):
|
40
|
+
Set the language model configuration.
|
41
|
+
|
42
|
+
set_api_service(api_service):
|
43
|
+
Set the API service for making model calls.
|
44
|
+
|
45
|
+
_output(output, invoke=True, out=True) -> Any:
|
46
|
+
Process the output, invoke tools if needed, and optionally return the output.
|
47
|
+
|
48
|
+
register_tools(tools, funcs, update=False, new=False, prefix=None, postfix=None):
|
49
|
+
Register tools and their corresponding functions.
|
50
|
+
|
51
|
+
initiate(instruction, system=None, context=None, out=True, name=None, invoke=True, **kwargs) -> Any:
|
52
|
+
Start a new conversation session with the provided instruction.
|
53
|
+
|
54
|
+
followup(instruction, system=None, context=None, out=True, name=None, invoke=True, **kwargs) -> Any:
|
55
|
+
Continue the conversation with the provided instruction.
|
56
|
+
|
57
|
+
create_payload_chatcompletion(**kwargs) -> dict:
|
58
|
+
Create a payload for chat completion based on the conversation state and configuration.
|
59
|
+
|
60
|
+
call_chatcompletion(sleep=0.1, **kwargs) -> None:
|
61
|
+
Make a call to the chat completion API and process the response.
|
62
|
+
|
63
|
+
messages_to_csv(dir=None, filename="_messages.csv", **kwargs) -> None:
|
64
|
+
Save conversation messages to a CSV file.
|
65
|
+
|
66
|
+
log_to_csv(dir=None, filename="_llmlog.csv", **kwargs) -> None:
|
67
|
+
Save conversation logs to a CSV file.
|
34
68
|
"""
|
35
69
|
|
36
70
|
def __init__(self, system, dir=None, llmconfig=oai_llmconfig, api_service=OAIService):
|
37
71
|
"""
|
38
|
-
Initialize a Session object.
|
72
|
+
Initialize a Session object with default or provided settings.
|
39
73
|
|
40
|
-
|
41
|
-
system: The system
|
42
|
-
dir: The directory for logging
|
43
|
-
llmconfig: Configuration
|
44
|
-
api_service: An instance of the API service for making
|
74
|
+
Parameters:
|
75
|
+
system (str): The initial system setting for the conversation.
|
76
|
+
dir (Optional[str]): The directory for logging. Default is None.
|
77
|
+
llmconfig (Optional[dict]): Configuration settings for the language model. Default is oai_llmconfig.
|
78
|
+
api_service: An instance of the API service for making calls to the conversational AI model.
|
45
79
|
"""
|
46
80
|
self.conversation = Conversation()
|
47
81
|
self.system = system
|
48
82
|
self.llmconfig = llmconfig
|
49
|
-
self.
|
83
|
+
self._logger = DataLogger(dir=dir)
|
84
|
+
self.api_service = api_service
|
85
|
+
self.toolmanager = ToolManager()
|
86
|
+
|
87
|
+
def set_dir(self, dir):
|
88
|
+
"""
|
89
|
+
Set the directory for logging.
|
90
|
+
|
91
|
+
Parameters:
|
92
|
+
dir (str): The directory path.
|
93
|
+
"""
|
94
|
+
self._logger.dir = dir
|
95
|
+
|
96
|
+
def set_system(self, system):
|
97
|
+
"""
|
98
|
+
Set the system for the conversation.
|
99
|
+
|
100
|
+
Parameters:
|
101
|
+
system (str): The system setting.
|
102
|
+
"""
|
103
|
+
self.conversation.change_system(system)
|
104
|
+
|
105
|
+
def set_llmconfig(self, llmconfig):
|
106
|
+
"""
|
107
|
+
Set the language model configuration.
|
108
|
+
|
109
|
+
Parameters:
|
110
|
+
llmconfig (dict): Configuration settings for the language model.
|
111
|
+
"""
|
112
|
+
self.llmconfig = llmconfig
|
113
|
+
|
114
|
+
def set_api_service(self, api_service):
|
115
|
+
"""
|
116
|
+
Set the API service for making model calls.
|
117
|
+
|
118
|
+
Parameters:
|
119
|
+
api_service: An instance of the API service.
|
120
|
+
"""
|
50
121
|
self.api_service = api_service
|
51
122
|
|
52
|
-
async def
|
123
|
+
async def _output(self, output, invoke=True, out=True):
|
53
124
|
"""
|
54
|
-
|
125
|
+
Process the output, invoke tools if needed, and optionally return the output.
|
55
126
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
out: Whether to return the output content.
|
127
|
+
Parameters:
|
128
|
+
output: The output to process.
|
129
|
+
invoke (bool): Whether to invoke tools based on the output. Default is True.
|
130
|
+
out (bool): Whether to return the output. Default is True.
|
61
131
|
|
62
132
|
Returns:
|
63
|
-
Any: The output
|
133
|
+
Any: The processed output.
|
134
|
+
"""
|
135
|
+
if invoke:
|
136
|
+
try:
|
137
|
+
func, args = self.toolmanager._get_function_call(output)
|
138
|
+
outs = await self.toolmanager.ainvoke(func, args)
|
139
|
+
self.conversation.add_messages(tool=outs)
|
140
|
+
except:
|
141
|
+
pass
|
142
|
+
if out:
|
143
|
+
return output
|
144
|
+
|
145
|
+
def register_tools(self, tools, funcs, update=False, new=False, prefix=None, postfix=None):
|
146
|
+
"""
|
147
|
+
Register tools and their corresponding functions.
|
148
|
+
|
149
|
+
Parameters:
|
150
|
+
tools (list): The list of tool information dictionaries.
|
151
|
+
funcs (list): The list of corresponding functions.
|
152
|
+
update (bool): Whether to update existing functions.
|
153
|
+
new (bool): Whether to create new registries for existing functions.
|
154
|
+
prefix (Optional[str]): A prefix to add to the function names.
|
155
|
+
postfix (Optional[str]): A postfix to add to the function names.
|
156
|
+
"""
|
157
|
+
funcs = to_list(funcs)
|
158
|
+
self.toolmanager.register_tools(tools, funcs, update, new, prefix, postfix)
|
159
|
+
|
160
|
+
async def initiate(self, instruction, system=None, context=None, out=True, name=None, invoke=True, **kwargs) -> Any:
|
161
|
+
"""
|
162
|
+
Start a new conversation session with the provided instruction.
|
163
|
+
|
164
|
+
Parameters:
|
165
|
+
instruction (str): The instruction to initiate the conversation.
|
166
|
+
system (Optional[str]): The system setting for the conversation. Default is None.
|
167
|
+
context (Optional[dict]): Additional context for the instruction. Default is None.
|
168
|
+
out (bool): Whether to return the output. Default is True.
|
169
|
+
name (Optional[str]): The name associated with the instruction. Default is None.
|
170
|
+
invoke (bool): Whether to invoke tools based on the output. Default is True.
|
171
|
+
kwargs: Additional keyword arguments for configuration.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
Any: The processed output.
|
64
175
|
"""
|
65
176
|
config = {**self.llmconfig, **kwargs}
|
66
177
|
system = system or self.system
|
67
|
-
self.conversation.initiate_conversation(system=system, instruction=instruction, context=context)
|
68
|
-
|
178
|
+
self.conversation.initiate_conversation(system=system, instruction=instruction, context=context, name=name)
|
69
179
|
await self.call_chatcompletion(**config)
|
70
|
-
|
71
|
-
|
180
|
+
output = self.conversation.responses[-1]['content']
|
181
|
+
|
182
|
+
return await self._output(output, invoke, out)
|
72
183
|
|
73
|
-
async def followup(self, instruction, system=None, context=None, out=True, **kwargs) -> Any:
|
184
|
+
async def followup(self, instruction, system=None, context=None, out=True, name=None, invoke=True, **kwargs) -> Any:
|
74
185
|
"""
|
75
|
-
Continue the conversation
|
186
|
+
Continue the conversation with the provided instruction.
|
76
187
|
|
77
|
-
|
78
|
-
instruction: The
|
79
|
-
system: The
|
80
|
-
context: Additional context for the
|
81
|
-
out: Whether to return the output
|
188
|
+
Parameters:
|
189
|
+
instruction (str): The instruction to continue the conversation.
|
190
|
+
system (Optional[str]): The system setting for the conversation. Default is None.
|
191
|
+
context (Optional[dict]): Additional context for the instruction. Default is None.
|
192
|
+
out (bool): Whether to return the output. Default is True.
|
193
|
+
name (Optional[str]): The name associated with the instruction. Default is None.
|
194
|
+
invoke (bool): Whether to invoke tools based on the output. Default is True.
|
195
|
+
kwargs: Additional keyword arguments for configuration.
|
82
196
|
|
83
197
|
Returns:
|
84
|
-
Any: The output
|
198
|
+
Any: The processed output.
|
85
199
|
"""
|
86
|
-
self.conversation.append_last_response()
|
87
200
|
if system:
|
88
201
|
self.conversation.change_system(system)
|
89
|
-
self.conversation.add_messages(instruction=instruction, context=context)
|
90
|
-
|
202
|
+
self.conversation.add_messages(instruction=instruction, context=context, name=name)
|
91
203
|
config = {**self.llmconfig, **kwargs}
|
92
204
|
await self.call_chatcompletion(**config)
|
93
|
-
|
94
|
-
|
205
|
+
output = self.conversation.responses[-1]['content']
|
206
|
+
|
207
|
+
return await self._output(output, invoke, out)
|
95
208
|
|
96
209
|
def create_payload_chatcompletion(self, **kwargs):
|
97
210
|
"""
|
98
|
-
Create a payload for chat completion
|
211
|
+
Create a payload for chat completion based on the conversation state and configuration.
|
212
|
+
|
213
|
+
Parameters:
|
214
|
+
kwargs: Additional keyword arguments for configuration.
|
99
215
|
|
100
|
-
|
101
|
-
|
216
|
+
Returns:
|
217
|
+
dict: The payload for chat completion.
|
102
218
|
"""
|
103
219
|
# currently only openai chat completions are supported
|
104
220
|
messages = self.conversation.messages
|
105
|
-
|
106
221
|
config = {**self.llmconfig, **kwargs}
|
107
|
-
|
108
222
|
payload = {
|
109
223
|
"messages": messages,
|
110
224
|
"model": config.get('model'),
|
@@ -117,34 +231,30 @@ class Session():
|
|
117
231
|
}
|
118
232
|
|
119
233
|
for key in ["seed", "stop", "stream", "tools", "tool_choice", "user", "max_tokens"]:
|
120
|
-
if config[key] is True:
|
234
|
+
if bool(config[key]) is True and str(config[key]) != "none":
|
121
235
|
payload.update({key: config[key]})
|
122
|
-
|
123
236
|
return payload
|
124
|
-
|
125
|
-
async def call_chatcompletion(self,
|
237
|
+
|
238
|
+
async def call_chatcompletion(self, sleep=0.1, **kwargs):
|
126
239
|
"""
|
127
|
-
Make
|
240
|
+
Make a call to the chat completion API and process the response.
|
128
241
|
|
129
|
-
|
130
|
-
|
131
|
-
kwargs: Additional keyword arguments for
|
242
|
+
Parameters:
|
243
|
+
sleep (float): The sleep duration after making the API call. Default is 0.1.
|
244
|
+
kwargs: Additional keyword arguments for configuration.
|
132
245
|
"""
|
133
|
-
# currently only openai chat completions are supported
|
134
|
-
|
135
246
|
endpoint = f"chat/completions"
|
136
247
|
try:
|
137
248
|
async with aiohttp.ClientSession() as session:
|
249
|
+
payload = self.create_payload_chatcompletion(**kwargs)
|
138
250
|
completion = await self.api_service.call_api(
|
139
|
-
session, endpoint,
|
140
|
-
self.create_payload_chatcompletion(**kwargs))
|
251
|
+
session, endpoint, payload)
|
141
252
|
if "choices" in completion:
|
142
|
-
|
143
|
-
self.
|
144
|
-
|
145
|
-
self.conversation.responses.append(response)
|
253
|
+
self._logger({"input":payload, "output": completion})
|
254
|
+
self.conversation.add_messages(response=completion['choices'][0])
|
255
|
+
self.conversation.responses.append(self.conversation.messages[-1])
|
146
256
|
self.conversation.response_counts += 1
|
147
|
-
await asyncio.sleep(
|
257
|
+
await asyncio.sleep(sleep)
|
148
258
|
status_tracker.num_tasks_succeeded += 1
|
149
259
|
else:
|
150
260
|
status_tracker.num_tasks_failed += 1
|
@@ -153,13 +263,29 @@ class Session():
|
|
153
263
|
raise e
|
154
264
|
|
155
265
|
def messages_to_csv(self, dir=None, filename="_messages.csv", **kwags):
|
156
|
-
|
266
|
+
"""
|
267
|
+
Save conversation messages to a CSV file.
|
268
|
+
|
269
|
+
Parameters:
|
270
|
+
dir (Optional[str]): The directory path for saving the CSV file. Default is None.
|
271
|
+
filename (Optional[str]): The filename for the CSV file. Default is "_messages.csv".
|
272
|
+
kwargs: Additional keyword arguments for CSV file settings.
|
273
|
+
"""
|
274
|
+
dir = dir or self._logger.dir
|
157
275
|
if dir is None:
|
158
276
|
raise ValueError("No directory specified.")
|
159
|
-
self.conversation.msg.
|
277
|
+
self.conversation.msg.to_csv(dir=dir, filename=filename, **kwags)
|
160
278
|
|
161
279
|
def log_to_csv(self, dir=None, filename="_llmlog.csv", **kwags):
|
162
|
-
|
280
|
+
"""
|
281
|
+
Save conversation logs to a CSV file.
|
282
|
+
|
283
|
+
Parameters:
|
284
|
+
dir (Optional[str]): The directory path for saving the CSV file. Default is None.
|
285
|
+
filename (Optional[str]): The filename for the CSV file. Default is "_llmlog.csv".
|
286
|
+
kwargs: Additional keyword arguments for CSV file settings.
|
287
|
+
"""
|
288
|
+
dir = dir or self._logger.dir
|
163
289
|
if dir is None:
|
164
290
|
raise ValueError("No directory specified.")
|
165
|
-
self.
|
291
|
+
self._logger.to_csv(dir=dir, filename=filename, **kwags)
|
File without changes
|
lionagi/utils/__init__.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
from .sys_util import to_flat_dict, append_to_jsonl, to_list, str_to_num, make_copy, to_temp, to_csv, hold_call, ahold_call, l_call, al_call, m_call, am_call, e_call, ae_call, get_timestamp, create_path
|
2
2
|
from .doc_util import dir_to_path, read_text, dir_to_files, chunk_text, file_to_chunks, file_to_chunks, get_bins
|
3
3
|
from .log_util import DataLogger
|
4
|
+
from .tool_util import ToolManager
|
4
5
|
|
5
6
|
__all__ = [
|
6
7
|
"to_list", "str_to_num", "make_copy", "to_temp", "to_csv", "hold_call", "ahold_call", "l_call", "al_call", "m_call", "am_call", "e_call", "ae_call", "get_timestamp", "create_path", "to_flat_dict", "append_to_jsonl",
|
7
8
|
"dir_to_path", "read_text", "dir_to_files", "chunk_text", "file_to_chunks", "file_to_chunks", "get_bins",
|
8
|
-
"DataLogger"
|
9
|
+
"DataLogger", "ToolManager"
|
9
10
|
]
|
lionagi/utils/doc_util.py
CHANGED
@@ -26,7 +26,7 @@ def dir_to_path(dir: str, ext, recursive: bool = False, flat: bool = True):
|
|
26
26
|
def _dir_to_path(ext, recursive=recursive):
|
27
27
|
tem = '**/*' if recursive else '*'
|
28
28
|
return list(Path(dir).glob(tem + ext))
|
29
|
-
|
29
|
+
|
30
30
|
return to_list(l_call(ext, _dir_to_path, flat=True), flat=flat)
|
31
31
|
|
32
32
|
def read_text(filepath: str, clean: bool = True) -> str:
|
@@ -48,15 +48,15 @@ def read_text(filepath: str, clean: bool = True) -> str:
|
|
48
48
|
content = f.read()
|
49
49
|
if clean:
|
50
50
|
# Define characters to replace and their replacements
|
51
|
-
replacements = {'\\': ' ', '
|
51
|
+
replacements = {'\\': ' ', '\n': ' ', '\t': ' ', ' ': ' ', '\'': ' '}
|
52
52
|
for old, new in replacements.items():
|
53
53
|
content = content.replace(old, new)
|
54
54
|
return content
|
55
55
|
|
56
|
-
def dir_to_files(dir: str, ext: str, recursive: bool = False,
|
57
|
-
reader: Callable = read_text, clean: bool = True,
|
56
|
+
def dir_to_files(dir: str, ext: str, recursive: bool = False,
|
57
|
+
reader: Callable = read_text, clean: bool = True,
|
58
58
|
to_csv: bool = False, project: str = 'project',
|
59
|
-
output_dir: str = 'data/logs/sources/', filename: Optional[str] = None,
|
59
|
+
output_dir: str = 'data/logs/sources/', filename: Optional[str] = None,
|
60
60
|
verbose: bool = True, timestamp: bool = True, logger: Optional[DataLogger] = None):
|
61
61
|
"""
|
62
62
|
Reads and processes files in a specified directory with the given extension.
|
@@ -81,9 +81,9 @@ def dir_to_files(dir: str, ext: str, recursive: bool = False,
|
|
81
81
|
Examples:
|
82
82
|
>>> logs = dir_to_files(dir='my_directory', ext='.txt', to_csv=True)
|
83
83
|
"""
|
84
|
-
|
84
|
+
|
85
85
|
sources = dir_to_path(dir, ext, recursive)
|
86
|
-
|
86
|
+
|
87
87
|
def split_path(path: Path) -> tuple:
|
88
88
|
folder_name = path.parent.name
|
89
89
|
file_name = path.name
|
@@ -99,9 +99,9 @@ def dir_to_files(dir: str, ext: str, recursive: bool = False,
|
|
99
99
|
"file_size": len(str(content)),
|
100
100
|
'content': content
|
101
101
|
} if content else None
|
102
|
-
|
102
|
+
|
103
103
|
logs = to_list(l_call(sources, to_dict, flat=True), dropna=True)
|
104
|
-
|
104
|
+
|
105
105
|
if to_csv:
|
106
106
|
filename = filename or f"{project}_sources.csv"
|
107
107
|
logger = DataLogger(dir=output_dir, log=logs) if not logger else logger
|
@@ -109,7 +109,7 @@ def dir_to_files(dir: str, ext: str, recursive: bool = False,
|
|
109
109
|
|
110
110
|
return logs
|
111
111
|
|
112
|
-
def chunk_text(input: str, chunk_size: int, overlap: float,
|
112
|
+
def chunk_text(input: str, chunk_size: int, overlap: float,
|
113
113
|
threshold: int) -> List[Union[str, None]]:
|
114
114
|
"""
|
115
115
|
Splits a string into chunks of a specified size, allowing for optional overlap between chunks.
|
@@ -127,19 +127,19 @@ def chunk_text(input: str, chunk_size: int, overlap: float,
|
|
127
127
|
Returns:
|
128
128
|
List[Union[str, None]]: List of text chunks.
|
129
129
|
"""
|
130
|
-
|
130
|
+
|
131
131
|
try:
|
132
132
|
# Ensure text is a string
|
133
133
|
if not isinstance(input, str):
|
134
134
|
input = str(input)
|
135
|
-
|
135
|
+
|
136
136
|
chunks = []
|
137
137
|
n_chunks = math.ceil(len(input) / chunk_size)
|
138
138
|
overlap_size = int(chunk_size * overlap / 2)
|
139
|
-
|
139
|
+
|
140
140
|
if n_chunks == 1:
|
141
141
|
return [input]
|
142
|
-
|
142
|
+
|
143
143
|
elif n_chunks == 2:
|
144
144
|
chunks.append(input[:chunk_size + overlap_size])
|
145
145
|
if len(input) - chunk_size > threshold:
|
@@ -147,28 +147,28 @@ def chunk_text(input: str, chunk_size: int, overlap: float,
|
|
147
147
|
else:
|
148
148
|
return [input]
|
149
149
|
return chunks
|
150
|
-
|
150
|
+
|
151
151
|
elif n_chunks > 2:
|
152
152
|
chunks.append(input[:chunk_size + overlap_size])
|
153
153
|
for i in range(1, n_chunks - 1):
|
154
154
|
start_idx = chunk_size * i - overlap_size
|
155
155
|
end_idx = chunk_size * (i + 1) + overlap_size
|
156
156
|
chunks.append(input[start_idx:end_idx])
|
157
|
-
|
157
|
+
|
158
158
|
if len(input) - chunk_size * (n_chunks - 1) > threshold:
|
159
159
|
chunks.append(input[chunk_size * (n_chunks - 1) - overlap_size:])
|
160
160
|
else:
|
161
|
-
chunks[-1] += input[chunk_size * (n_chunks - 1):]
|
162
|
-
|
161
|
+
chunks[-1] += input[chunk_size * (n_chunks - 1) + overlap_size:]
|
162
|
+
|
163
163
|
return chunks
|
164
|
-
|
164
|
+
|
165
165
|
except Exception as e:
|
166
166
|
raise ValueError(f"An error occurred while chunking the text. {e}")
|
167
167
|
|
168
|
-
def _file_to_chunks(input: Dict[str, Any],
|
169
|
-
field: str = 'content',
|
170
|
-
chunk_size: int = 1500,
|
171
|
-
overlap: float = 0.2,
|
168
|
+
def _file_to_chunks(input: Dict[str, Any],
|
169
|
+
field: str = 'content',
|
170
|
+
chunk_size: int = 1500,
|
171
|
+
overlap: float = 0.2,
|
172
172
|
threshold: int = 200) -> List[Dict[str, Any]]:
|
173
173
|
"""
|
174
174
|
Splits text from a specified dictionary field into chunks and returns a list of dictionaries.
|
@@ -195,7 +195,7 @@ def _file_to_chunks(input: Dict[str, Any],
|
|
195
195
|
try:
|
196
196
|
out = {key: value for key, value in input.items() if key != field}
|
197
197
|
out.update({"chunk_overlap": overlap, "chunk_threshold": threshold})
|
198
|
-
|
198
|
+
|
199
199
|
chunks = chunk_text(input[field], chunk_size=chunk_size, overlap=overlap, threshold=threshold)
|
200
200
|
logs = []
|
201
201
|
for i, chunk in enumerate(chunks):
|
@@ -209,22 +209,22 @@ def _file_to_chunks(input: Dict[str, Any],
|
|
209
209
|
logs.append(chunk_dict)
|
210
210
|
|
211
211
|
return logs
|
212
|
-
|
212
|
+
|
213
213
|
except Exception as e:
|
214
214
|
raise ValueError(f"An error occurred while chunking the file. {e}")
|
215
|
-
|
216
|
-
def file_to_chunks(input,
|
217
|
-
field: str = 'content',
|
218
|
-
chunk_size: int = 1500,
|
219
|
-
overlap: float = 0.2,
|
220
|
-
threshold: int = 200,
|
221
|
-
to_csv=False,
|
215
|
+
|
216
|
+
def file_to_chunks(input,
|
217
|
+
field: str = 'content',
|
218
|
+
chunk_size: int = 1500,
|
219
|
+
overlap: float = 0.2,
|
220
|
+
threshold: int = 200,
|
221
|
+
to_csv=False,
|
222
222
|
project='project',
|
223
|
-
output_dir='data/logs/sources/',
|
223
|
+
output_dir='data/logs/sources/',
|
224
224
|
chunk_func = _file_to_chunks,
|
225
|
-
filename=None,
|
226
|
-
verbose=True,
|
227
|
-
timestamp=True,
|
225
|
+
filename=None,
|
226
|
+
verbose=True,
|
227
|
+
timestamp=True,
|
228
228
|
logger=None):
|
229
229
|
"""
|
230
230
|
Splits text from a specified dictionary field into chunks and returns a list of dictionaries.
|
@@ -243,10 +243,10 @@ def file_to_chunks(input,
|
|
243
243
|
timestamp: If True, include a timestamp in the exported file name.
|
244
244
|
logger: An optional DataLogger instance for logging.
|
245
245
|
"""
|
246
|
-
|
246
|
+
|
247
247
|
f = lambda x: chunk_func(x, field=field, chunk_size=chunk_size, overlap=overlap, threshold=threshold)
|
248
248
|
logs = to_list(l_call(input, f), flat=True)
|
249
|
-
|
249
|
+
|
250
250
|
if to_csv:
|
251
251
|
filename = filename if filename else f"{project}_sources.csv"
|
252
252
|
logger = DataLogger(log=logs) if not logger else logger
|
lionagi/utils/sys_util.py
CHANGED
@@ -670,7 +670,7 @@ def create_id() -> str:
|
|
670
670
|
random_bytes = os.urandom(16)
|
671
671
|
return hashlib.sha256(current_time + random_bytes).hexdigest()[:16]
|
672
672
|
|
673
|
-
def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True) -> str:
|
673
|
+
def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: bool = True, time_prefix=False) -> str:
|
674
674
|
"""
|
675
675
|
Creates a file path by optionally appending a timestamp to the filename.
|
676
676
|
|
@@ -690,10 +690,14 @@ def create_path(dir: str, filename: str, timestamp: bool = True, dir_exist_ok: b
|
|
690
690
|
>>> create_path('/tmp/', 'log.txt', timestamp=False)
|
691
691
|
'/tmp/log.txt'
|
692
692
|
"""
|
693
|
+
|
694
|
+
dir = dir + '/' if str(dir)[-1] != '/' else dir
|
695
|
+
filename, ext = filename.split('.')
|
693
696
|
os.makedirs(dir, exist_ok=dir_exist_ok)
|
697
|
+
|
694
698
|
if timestamp:
|
695
699
|
timestamp = get_timestamp()
|
696
|
-
return f"{dir}{timestamp}{filename}"
|
700
|
+
return f"{dir}{timestamp}_{filename}.{ext}" if time_prefix else f"{dir}{filename}_{timestamp}.{ext}"
|
697
701
|
else:
|
698
702
|
return f"{dir}{filename}"
|
699
703
|
|
@@ -0,0 +1,194 @@
|
|
1
|
+
import json
|
2
|
+
import asyncio
|
3
|
+
from .sys_util import l_call
|
4
|
+
|
5
|
+
|
6
|
+
class ToolManager:
|
7
|
+
"""
|
8
|
+
A manager class for handling and invoking registered tools and functions.
|
9
|
+
|
10
|
+
This class allows the registration of tools and functions, enabling their invocation.
|
11
|
+
|
12
|
+
Attributes:
|
13
|
+
registry (dict): A dictionary storing the registered tools and their corresponding functions.
|
14
|
+
|
15
|
+
Methods:
|
16
|
+
_to_dict(name, function, content=None) -> dict:
|
17
|
+
Convert tool information to a dictionary entry.
|
18
|
+
|
19
|
+
_name_existed(name) -> bool:
|
20
|
+
Check if a given name exists in the registry.
|
21
|
+
|
22
|
+
_register_function(name, function, content=None, update=False, new=False, prefix=None, postfix=None) -> None:
|
23
|
+
Register a function with a specified name in the registry.
|
24
|
+
|
25
|
+
invoke(name, args) -> Any:
|
26
|
+
Invoke a registered function with the provided arguments.
|
27
|
+
|
28
|
+
ainvoke(name, args) -> Any:
|
29
|
+
Asynchronously invoke a registered function with the provided arguments.
|
30
|
+
|
31
|
+
_get_function_call(response) -> Tuple[str, dict]:
|
32
|
+
Extract function name and arguments from a response JSON.
|
33
|
+
|
34
|
+
_from_tool(tool, func) -> Tuple[str, callable, list]:
|
35
|
+
Convert tool information to function registration parameters.
|
36
|
+
|
37
|
+
register_tools(tools, functions, update=False, new=False, prefix=None, postfix=None) -> None:
|
38
|
+
Register multiple tools and their corresponding functions.
|
39
|
+
"""
|
40
|
+
def __init__(self):
|
41
|
+
"""
|
42
|
+
Initialize a ToolManager object with an empty registry.
|
43
|
+
"""
|
44
|
+
self.registry = {}
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def _to_dict(name, function, content=None):
|
48
|
+
"""
|
49
|
+
Convert tool information to a dictionary entry.
|
50
|
+
|
51
|
+
Parameters:
|
52
|
+
name (str): The name of the tool.
|
53
|
+
function (callable): The function associated with the tool.
|
54
|
+
content (Optional[str]): Additional content for the tool.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
dict: A dictionary entry representing the tool.
|
58
|
+
"""
|
59
|
+
return {name: {"function": function, "content": content or "none"}}
|
60
|
+
|
61
|
+
def _name_existed(self, name):
|
62
|
+
"""
|
63
|
+
Check if a given name exists in the registry.
|
64
|
+
|
65
|
+
Parameters:
|
66
|
+
name (str): The name to check.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
bool: True if the name exists in the registry, False otherwise.
|
70
|
+
|
71
|
+
"""
|
72
|
+
return True if name in self.registry.keys() else False
|
73
|
+
|
74
|
+
def _register_function(self, name, function, content=None, update=False, new=False, prefix=None, postfix=None):
|
75
|
+
"""
|
76
|
+
Register a function with a specified name in the registry.
|
77
|
+
|
78
|
+
Parameters:
|
79
|
+
name (str): The name of the function.
|
80
|
+
function (callable): The function to register.
|
81
|
+
content (Optional[str]): Additional content for the function.
|
82
|
+
update (bool): Whether to update an existing function with the same name.
|
83
|
+
new (bool): Whether to create a new registry for an existing function.
|
84
|
+
prefix (Optional[str]): A prefix to add to the function name.
|
85
|
+
postfix (Optional[str]): A postfix to add to the function name.
|
86
|
+
|
87
|
+
"""
|
88
|
+
if self._name_existed(name):
|
89
|
+
if update and new:
|
90
|
+
raise ValueError(f"Cannot both update and create new registry for existing function {name} at the same time")
|
91
|
+
|
92
|
+
name = f"{prefix or ''}{name}{postfix or '1'}" if new else name
|
93
|
+
self.registry.update(self._to_dict(name, function, content))
|
94
|
+
|
95
|
+
def invoke(self, name, args):
|
96
|
+
"""
|
97
|
+
Invoke a registered function with the provided arguments.
|
98
|
+
|
99
|
+
Parameters:
|
100
|
+
name (str): The name of the function to invoke.
|
101
|
+
args (dict): The arguments to pass to the function.
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
Any: The result of invoking the function.
|
105
|
+
"""
|
106
|
+
if self._name_existed(name):
|
107
|
+
try:
|
108
|
+
return self.registry[name](**args)
|
109
|
+
except Exception as e:
|
110
|
+
raise ValueError(f"Error when invoking function {name} with arguments {args} with error message {e}")
|
111
|
+
else:
|
112
|
+
raise ValueError(f"Function {name} is not registered.")
|
113
|
+
|
114
|
+
async def ainvoke(self, name, args):
|
115
|
+
"""
|
116
|
+
Asynchronously invoke a registered function with the provided arguments.
|
117
|
+
|
118
|
+
Parameters:
|
119
|
+
name (str): The name of the function to invoke.
|
120
|
+
args (dict): The arguments to pass to the function.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
Any: The result of invoking the function asynchronously.
|
124
|
+
|
125
|
+
"""
|
126
|
+
if self._name_existed(name):
|
127
|
+
function = self.registry[name]["function"]
|
128
|
+
try:
|
129
|
+
if asyncio.iscoroutinefunction(function):
|
130
|
+
return await function(**args)
|
131
|
+
else:
|
132
|
+
return function(**args)
|
133
|
+
except Exception as e:
|
134
|
+
raise ValueError(f"Error when invoking function {name} with arguments {args} with error message {e}")
|
135
|
+
else:
|
136
|
+
raise ValueError(f"Function {name} is not registered.")
|
137
|
+
|
138
|
+
@staticmethod
|
139
|
+
def _get_function_call(response):
|
140
|
+
"""
|
141
|
+
Extract function name and arguments from a response JSON.
|
142
|
+
|
143
|
+
Parameters:
|
144
|
+
response (str): The JSON response containing function information.
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
Tuple[str, dict]: The function name and its arguments.
|
148
|
+
"""
|
149
|
+
try:
|
150
|
+
out = json.loads(response)
|
151
|
+
func = out['function'][5:]
|
152
|
+
args = json.loads(out['arguments'])
|
153
|
+
return (func, args)
|
154
|
+
except:
|
155
|
+
try:
|
156
|
+
out = json.loads(response)
|
157
|
+
out = out['tool_uses'][0]
|
158
|
+
func = out['recipient_name'].split('.')[-1]
|
159
|
+
args = out['parameters']
|
160
|
+
return (func, args)
|
161
|
+
except:
|
162
|
+
raise ValueError('response is not a valid function call')
|
163
|
+
|
164
|
+
@staticmethod
|
165
|
+
def _from_tool(tool, func):
|
166
|
+
"""
|
167
|
+
Convert tool information to function registration parameters.
|
168
|
+
|
169
|
+
Parameters:
|
170
|
+
tool (dict): The tool information.
|
171
|
+
func (callable): The function associated with the tool.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
Tuple[str, callable, list]: The function name, the function, and the list of function parameters.
|
175
|
+
|
176
|
+
"""
|
177
|
+
return (tool['function']['name'], func,
|
178
|
+
tool['function']['parameters']['properties'].keys())
|
179
|
+
|
180
|
+
def register_tools(self, tools, functions, update=False, new=False, prefix=None, postfix=None):
|
181
|
+
"""
|
182
|
+
Register multiple tools and their corresponding functions.
|
183
|
+
|
184
|
+
Parameters:
|
185
|
+
tools (list): The list of tool information dictionaries.
|
186
|
+
functions (list): The list of corresponding functions.
|
187
|
+
update (bool): Whether to update existing functions.
|
188
|
+
new (bool): Whether to create new registries for existing functions.
|
189
|
+
prefix (Optional[str]): A prefix to add to the function names.
|
190
|
+
postfix (Optional[str]): A postfix to add to the function names.
|
191
|
+
|
192
|
+
"""
|
193
|
+
funcs = l_call(range(len(tools)), lambda i: self._from_tool(tools[i], functions[i]))
|
194
|
+
l_call(range(len(tools)), lambda i: self._register_function(funcs[i][0], funcs[i][1], update=update, new=new, prefix=prefix, postfix=postfix))
|
lionagi/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.0.
|
1
|
+
__version__ = "0.0.105"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lionagi
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.105
|
4
4
|
Summary: Towards automated general intelligence.
|
5
5
|
Author: HaiyangLi
|
6
6
|
Author-email: Haiyang Li <ocean@lionagi.ai>
|
@@ -227,7 +227,8 @@ Requires-Dist: httpx ==0.25.1
|
|
227
227
|
- PyPI: https://pypi.org/project/lionagi/
|
228
228
|
- Documentation: https://lionagi.readthedocs.io/en/latest/ (still a lot TODO)
|
229
229
|
- Website: TODO
|
230
|
-
- Discord: [Join Our Discord](https://discord.gg/
|
230
|
+
- Discord: [Join Our Discord](https://discord.gg/7RGWqpSxze)
|
231
|
+
|
231
232
|
|
232
233
|
# LionAGI
|
233
234
|
**Towards Automated General Intelligence**
|
@@ -286,7 +287,7 @@ Visit our notebooks for our examples.
|
|
286
287
|
|
287
288
|
### Community
|
288
289
|
|
289
|
-
We encourage contributions to LionAGI and invite you to enrich its features and capabilities. Engage with us and other community members
|
290
|
+
We encourage contributions to LionAGI and invite you to enrich its features and capabilities. Engage with us and other community members [Join Our Discord](https://discord.gg/7RGWqpSxze)
|
290
291
|
|
291
292
|
### Citation
|
292
293
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
lionagi/__init__.py,sha256=2Rko3tw94ZFVN_GSvcxAY1O77FxswcaMxNHKH5Bj7jc,788
|
2
|
+
lionagi/version.py,sha256=WyIu1E1JGmXY5vMstqMyTqow6kk_Dcs2wEzR6Z1x6Ts,24
|
3
|
+
lionagi/api/__init__.py,sha256=RcmOxPnbaj5R6JYqQzQZ67KyVByJfyUw6QSw22KSo8g,134
|
4
|
+
lionagi/api/oai_config.py,sha256=yhyZ4aEaF6r3XBbhxI47r8CL2-amc-4IKJhbXv2W9CM,356
|
5
|
+
lionagi/api/oai_service.py,sha256=ctX4k3du_sl52n2LilSxXGHdce-igHXtEfpB6RrKbwQ,11209
|
6
|
+
lionagi/session/__init__.py,sha256=hbM6VwWz0Oh-Vld79JDFo5eYaCIqAn_OswmLiT0z4UA,58
|
7
|
+
lionagi/session/conversation.py,sha256=ZeaNJgmhA6aMG8cZMagRSF4gfsJNQKsNWAFxY6M2Y34,4057
|
8
|
+
lionagi/session/message.py,sha256=rjNeOSDgweW_eQfmsgsWTJ30slnfMEv0Tn4LHeAOgxo,6147
|
9
|
+
lionagi/session/session.py,sha256=007xhhUUbn60LpOqLsGlvRcio7t_77bx3z_GFir0rOk,12194
|
10
|
+
lionagi/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
+
lionagi/utils/__init__.py,sha256=jTZigmOboszESjWr9dP8PKtZ8XF_RqVd4Ni8s-GkaVs,760
|
12
|
+
lionagi/utils/api_util.py,sha256=WE51kMpKzmBwRqD0dkzxAMWzK04_k_5dth2kiOjVkMA,14837
|
13
|
+
lionagi/utils/doc_util.py,sha256=uT2paXguEs26kkW7oe2rR3CozQmpP2P9eJJdnZ9uM_E,12338
|
14
|
+
lionagi/utils/log_util.py,sha256=qbmaZxiX_bKY-LLaZcpMbTwi3aeBcK9-Lc93vkLIBuk,3103
|
15
|
+
lionagi/utils/sys_util.py,sha256=K4dumJ0th082RITpoz_F_eUw-OwkaLOz-BHoezuxwlU,28045
|
16
|
+
lionagi/utils/tool_util.py,sha256=OWQsGqAxiyxz2Q7cVE_JmnF19GKN2D9BNvJ09c98DlM,7461
|
17
|
+
lionagi-0.0.105.dist-info/LICENSE,sha256=TBnSyG8fs_tMRtK805GzA1cIyExleKyzoN_kuVxT9IY,11358
|
18
|
+
lionagi-0.0.105.dist-info/METADATA,sha256=SACNHjsB9hpR-d3Y5kfIhUsox3cdMTe_dLPHHFl08YU,17370
|
19
|
+
lionagi-0.0.105.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
20
|
+
lionagi-0.0.105.dist-info/top_level.txt,sha256=szvch_d2jE1Lu9ZIKsl26Ll6BGfYfbOgt5lm-UpFSo4,8
|
21
|
+
lionagi-0.0.105.dist-info/RECORD,,
|
lionagi-0.0.104.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
lionagi/__init__.py,sha256=2Rko3tw94ZFVN_GSvcxAY1O77FxswcaMxNHKH5Bj7jc,788
|
2
|
-
lionagi/version.py,sha256=m9tOf0gVCkqN3dmK4Ruh3l4dO8KbQ-WtCnvEBME8wSU,24
|
3
|
-
lionagi/api/__init__.py,sha256=RcmOxPnbaj5R6JYqQzQZ67KyVByJfyUw6QSw22KSo8g,134
|
4
|
-
lionagi/api/oai_config.py,sha256=yhyZ4aEaF6r3XBbhxI47r8CL2-amc-4IKJhbXv2W9CM,356
|
5
|
-
lionagi/api/oai_service.py,sha256=ctX4k3du_sl52n2LilSxXGHdce-igHXtEfpB6RrKbwQ,11209
|
6
|
-
lionagi/session/__init__.py,sha256=hbM6VwWz0Oh-Vld79JDFo5eYaCIqAn_OswmLiT0z4UA,58
|
7
|
-
lionagi/session/conversation.py,sha256=dedqlGEfIT26WfTJhBOlYWpz_-BFad1BkzIya2mkFwA,3436
|
8
|
-
lionagi/session/message.py,sha256=bxvJbR0z9z97suz4sMKbNYIuJYsHuAXuTDgj3CI0qnM,2851
|
9
|
-
lionagi/session/session.py,sha256=zTRMNaV8R3DKD10hm-wOAW8dSRVobu_aa7_lAT8I9kg,6812
|
10
|
-
lionagi/utils/__init__.py,sha256=-9k5ILJjZ9bTq6U4NA3vNURG2HS7wsUdE1fEWItTGqM,710
|
11
|
-
lionagi/utils/api_util.py,sha256=WE51kMpKzmBwRqD0dkzxAMWzK04_k_5dth2kiOjVkMA,14837
|
12
|
-
lionagi/utils/doc_util.py,sha256=kZ3qRIKc5kAXFjeR8Z6oDzM4vRQVDw6rG5YDavme2zQ,12461
|
13
|
-
lionagi/utils/log_util.py,sha256=qbmaZxiX_bKY-LLaZcpMbTwi3aeBcK9-Lc93vkLIBuk,3103
|
14
|
-
lionagi/utils/sys_util.py,sha256=8S1S7V3Sk_Yyp2pfqrGyO_9w-wDHQ3Kzpez4yGvpZuM,27860
|
15
|
-
lionagi-0.0.104.dist-info/LICENSE,sha256=TBnSyG8fs_tMRtK805GzA1cIyExleKyzoN_kuVxT9IY,11358
|
16
|
-
lionagi-0.0.104.dist-info/METADATA,sha256=640tGgNFlJ91RYqHBU2B4VLx1dcGVlMztLmbXBPXeEs,17363
|
17
|
-
lionagi-0.0.104.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
18
|
-
lionagi-0.0.104.dist-info/top_level.txt,sha256=szvch_d2jE1Lu9ZIKsl26Ll6BGfYfbOgt5lm-UpFSo4,8
|
19
|
-
lionagi-0.0.104.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|