lionagi 0.0.103__py3-none-any.whl → 0.0.105__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
lionagi/__init__.py CHANGED
@@ -16,7 +16,6 @@ Copyright 2023 HaiyangLi <ocean@lionagi.ai>
16
16
 
17
17
  import logging
18
18
  from .version import __version__
19
- from .config import *
20
19
  from .session import *
21
20
  from .utils import *
22
21
  from .api import *
lionagi/api/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
- from .OAIService import OpenAIRateLimiter, OpenAIService
1
+ from .oai_service import OpenAIService
2
+ from .oai_config import oai_llmconfig
2
3
 
3
4
  __all__ = [
4
- "OpenAIRateLimiter",
5
+ "oai_llmconfig",
5
6
  "OpenAIService",
6
7
  ]
@@ -1,7 +1,7 @@
1
- llmconfig = {
1
+ oai_llmconfig = {
2
2
  "model": "gpt-4-1106-preview",
3
3
  "frequency_penalty": 0,
4
- "max_tokens": 4000,
4
+ "max_tokens": None,
5
5
  "n": 1,
6
6
  "presence_penalty": 0,
7
7
  "response_format": {"type": "text"},
@@ -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
- self.rate_limit_replenisher_task = asyncio.create_task(self.rate_limit_replenisher())
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
- status_tracker, rate_limiter, queue)
184
+ max_requests_per_minute, max_tokens_per_minute,
185
+ ratelimiter, status_tracker, queue)
186
+
177
187
 
178
- async def call_api(self, session, request_url: str, payload: Dict[str, any]) -> Optional[Dict[str, any]]:
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 session.post(
220
- url=request_url, headers=request_headers, json=payload
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
-
@@ -1,9 +1,5 @@
1
- from .message import Message
2
- from .conversation import Conversation
3
1
  from .session import Session
4
2
 
5
3
  __all__ = [
6
- "Message",
7
- "Conversation",
8
4
  "Session",
9
5
  ]
@@ -2,87 +2,91 @@ from .message import Message
2
2
 
3
3
  class Conversation:
4
4
  """
5
- A class modeling conversations and managing messages within the conversation.
5
+ A class representing a conversation between users and the assistant.
6
6
 
7
- This class facilitates the organization and manipulation of messages in a conversation.
8
- It includes methods for initiating a conversation, adding messages, changing the system message,
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: A class-level attribute to track the number of responses.
13
- messages: A list containing messages in the conversation.
14
- msg: An instance of the Message class for creating and processing messages.
15
- responses: A list containing response messages.
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: Start a new conversation with system and user instructions.
19
- add_messages: Add messages to the conversation.
20
- change_system: Change the system message in the conversation.
21
- append_last_response: Append the last response to the conversation.
22
- keep_last_n_exchanges: Keep only the last N exchanges in the conversation.
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
- Args:
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
- Start a new conversation with a system message and an instruction.
45
+ Initiate a conversation with a system setting and user instruction.
41
46
 
42
- Args:
43
- system: The content of the system message.
44
- instruction: The content of the user instruction.
45
- context: Additional context for the user instruction.
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
- def add_messages(self, system=None, instruction=None, context=None, response=None):
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
- Args:
56
- system: The content of the system message.
57
- instruction: The content of the user instruction.
58
- context: Additional context for the user instruction.
59
- response: The content of the assistant's response.
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, context=context)
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 message in the conversation.
75
+ Change the system setting in the conversation.
67
76
 
68
- Args:
69
- system: The new content for the system message.
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 only the last N exchanges in the conversation.
84
+ Keep the last n exchanges in the conversation.
82
85
 
83
- Args:
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
  ]
@@ -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 modeling messages in conversations.
9
+ A class representing a message in a conversation.
10
10
 
11
- This class is designed to encapsulate messages exchanged between different roles
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 (user, assistant, or system).
17
- content: The content of the message.
18
- sender: The sender of the message.
19
- logger: An instance of DataLogger for logging message information.
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
- __call__: Process and log a message based on its role.
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 with attributes for role, content, sender, and a DataLogger.
35
+ Initialize a Message object.
27
36
  """
28
37
  self.role = None
29
38
  self.content = None
30
- self.sender = None
31
- self.logger = DataLogger()
32
-
33
- def __call__(self, system=None, instruction=None, response=None, context=None, sender=None):
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
- Process and log a message based on the specified role (system, instruction, or response).
45
+ Create a message based on the provided information.
36
46
 
37
- Args:
38
- system: The content of the message in the system role.
39
- instruction: The content of the message in the user role.
40
- response: The content of the message in the assistant role.
41
- context: Additional context for the user instruction.
42
- sender: The sender of the message.
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
- if sum(map(bool, [system, instruction, response])) > 1:
46
- raise ValueError("Message cannot have more than one role.")
47
- else:
58
+ else:
48
59
  if response:
49
60
  self.role = "assistant"
50
- self.sender = sender or "assistant"
51
- self.content = response['content']
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
- a = {**{
100
+
101
+ self.metadata = {
68
102
  "id": create_id(),
69
103
  "timestamp": datetime.now().isoformat(),
70
- "sender": self.sender
71
- }, **out}
72
- self.logger(a)
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)