lionagi 0.0.103__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/__init__.py +0 -1
- lionagi/api/__init__.py +3 -2
- lionagi/{config/llmconfig.py → api/oai_config.py} +2 -2
- lionagi/api/{OAIService.py → oai_service.py} +23 -13
- lionagi/session/__init__.py +0 -4
- lionagi/session/conversation.py +47 -43
- lionagi/session/message.py +102 -36
- lionagi/session/session.py +214 -72
- lionagi/tools/__init__.py +0 -0
- lionagi/utils/__init__.py +4 -5
- lionagi/utils/api_util.py +12 -20
- lionagi/utils/doc_util.py +38 -38
- lionagi/utils/sys_util.py +6 -3
- lionagi/utils/tool_util.py +194 -0
- lionagi/version.py +1 -1
- lionagi-0.0.105.dist-info/METADATA +311 -0
- lionagi-0.0.105.dist-info/RECORD +21 -0
- lionagi/config/__init__.py +0 -4
- lionagi/config/oaiconfig.py +0 -19
- lionagi-0.0.103.dist-info/METADATA +0 -97
- lionagi-0.0.103.dist-info/RECORD +0 -21
- {lionagi-0.0.103.dist-info → lionagi-0.0.105.dist-info}/LICENSE +0 -0
- {lionagi-0.0.103.dist-info → lionagi-0.0.105.dist-info}/WHEEL +0 -0
- {lionagi-0.0.103.dist-info → lionagi-0.0.105.dist-info}/top_level.txt +0 -0
lionagi/__init__.py
CHANGED
lionagi/api/__init__.py
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
import os
|
2
|
+
import dotenv
|
1
3
|
import asyncio
|
2
4
|
import logging
|
3
5
|
import tiktoken
|
4
6
|
from typing import Optional, NoReturn, Dict, Any
|
5
7
|
|
8
|
+
dotenv.load_dotenv()
|
9
|
+
|
6
10
|
from ..utils.api_util import AsyncQueue, StatusTracker, RateLimiter, BaseAPIService
|
7
11
|
|
8
12
|
|
@@ -32,7 +36,8 @@ class OpenAIRateLimiter(RateLimiter):
|
|
32
36
|
max_tokens_per_minute (int): The maximum number of tokens that can accumulate per minute.
|
33
37
|
"""
|
34
38
|
super().__init__(max_requests_per_minute, max_tokens_per_minute)
|
35
|
-
|
39
|
+
if not os.getenv('env_readthedocs'):
|
40
|
+
self.rate_limit_replenisher_task = asyncio.create_task(self.rate_limit_replenisher())
|
36
41
|
|
37
42
|
async def rate_limit_replenisher(self) -> NoReturn:
|
38
43
|
"""
|
@@ -52,8 +57,8 @@ class OpenAIRateLimiter(RateLimiter):
|
|
52
57
|
self.available_request_capacity = self.max_requests_per_minute
|
53
58
|
self.available_token_capacity = self.max_tokens_per_minute
|
54
59
|
|
55
|
-
def calculate_num_token(self, payload: Dict[str, Any],
|
56
|
-
api_endpoint: str, token_encoding_name: str) -> int:
|
60
|
+
def calculate_num_token(self, payload: Dict[str, Any] =None,
|
61
|
+
api_endpoint: str =None, token_encoding_name: str =None) -> int:
|
57
62
|
"""
|
58
63
|
Calculates the number of tokens required for a request based on the payload and API endpoint.
|
59
64
|
|
@@ -143,11 +148,13 @@ class OpenAIService(BaseAPIService):
|
|
143
148
|
|
144
149
|
def __init__(
|
145
150
|
self,
|
146
|
-
api_key: str,
|
147
|
-
token_encoding_name: str,
|
148
|
-
max_attempts: int,
|
151
|
+
api_key: str = None,
|
152
|
+
token_encoding_name: str = "cl100k_base",
|
153
|
+
max_attempts: int = 3,
|
154
|
+
max_requests_per_minute: int = 500,
|
155
|
+
max_tokens_per_minute: int = 150_000,
|
156
|
+
ratelimiter = OpenAIRateLimiter,
|
149
157
|
status_tracker: Optional[StatusTracker] = None,
|
150
|
-
rate_limiter: Optional[OpenAIRateLimiter] = None,
|
151
158
|
queue: Optional[AsyncQueue] = None
|
152
159
|
):
|
153
160
|
"""
|
@@ -172,10 +179,13 @@ class OpenAIService(BaseAPIService):
|
|
172
179
|
... )
|
173
180
|
# Service is configured for interacting with OpenAI API.
|
174
181
|
"""
|
182
|
+
api_key = api_key or os.getenv("OPENAI_API_KEY")
|
175
183
|
super().__init__(api_key, token_encoding_name, max_attempts,
|
176
|
-
|
184
|
+
max_requests_per_minute, max_tokens_per_minute,
|
185
|
+
ratelimiter, status_tracker, queue)
|
186
|
+
|
177
187
|
|
178
|
-
async def call_api(self,
|
188
|
+
async def call_api(self, http_session, endpoint, payload: Dict[str, any] =None) -> Optional[Dict[str, any]]:
|
179
189
|
"""
|
180
190
|
Call an OpenAI API endpoint with a specific payload and handle the response.
|
181
191
|
|
@@ -199,12 +209,13 @@ class OpenAIService(BaseAPIService):
|
|
199
209
|
... )
|
200
210
|
# Calls the specified API endpoint with the given payload.
|
201
211
|
"""
|
212
|
+
endpoint = self.api_endpoint_from_url(self.base_url+endpoint)
|
213
|
+
|
202
214
|
while True:
|
203
215
|
if self.rate_limiter.available_request_capacity < 1 or self.rate_limiter.available_token_capacity < 10: # Minimum token count
|
204
216
|
await asyncio.sleep(1) # Wait for capacity
|
205
217
|
continue
|
206
218
|
|
207
|
-
endpoint = self.api_endpoint_from_url(request_url)
|
208
219
|
required_tokens = self.rate_limiter.calculate_num_token(payload, endpoint, self.token_encoding_name)
|
209
220
|
|
210
221
|
if self.rate_limiter.available_token_capacity >= required_tokens:
|
@@ -216,8 +227,8 @@ class OpenAIService(BaseAPIService):
|
|
216
227
|
|
217
228
|
while attempts_left > 0:
|
218
229
|
try:
|
219
|
-
async with
|
220
|
-
url=
|
230
|
+
async with http_session.post(
|
231
|
+
url=(self.base_url+endpoint), headers=request_headers, json=payload
|
221
232
|
) as response:
|
222
233
|
response_json = await response.json()
|
223
234
|
|
@@ -239,4 +250,3 @@ class OpenAIService(BaseAPIService):
|
|
239
250
|
break
|
240
251
|
else:
|
241
252
|
await asyncio.sleep(1) # Wait for token capacity
|
242
|
-
|
lionagi/session/__init__.py
CHANGED
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,73 +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.
|
73
|
-
return out
|
104
|
+
"name": self.name}
|
105
|
+
|
106
|
+
self._logger({**self.metadata, **out})
|
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()
|
126
|
+
|
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)
|