lionagi 0.0.208__py3-none-any.whl → 0.0.210__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- lionagi/__init__.py +4 -6
- lionagi/api_service/base_endpoint.py +65 -0
- lionagi/api_service/base_rate_limiter.py +121 -0
- lionagi/api_service/base_service.py +146 -0
- lionagi/api_service/chat_completion.py +6 -0
- lionagi/api_service/embeddings.py +6 -0
- lionagi/api_service/payload_package.py +47 -0
- lionagi/api_service/status_tracker.py +29 -0
- lionagi/core/__init__.py +5 -9
- lionagi/core/branch.py +1191 -0
- lionagi/core/flow.py +423 -0
- lionagi/core/{instruction_set/instruction_set.py → instruction_set.py} +3 -3
- lionagi/core/session.py +872 -0
- lionagi/schema/__init__.py +5 -8
- lionagi/schema/base_schema.py +821 -0
- lionagi/{_services → services}/base_service.py +4 -4
- lionagi/{_services → services}/oai.py +4 -4
- lionagi/structures/graph.py +1 -1
- lionagi/structures/relationship.py +1 -1
- lionagi/structures/structure.py +1 -1
- lionagi/tools/tool_manager.py +0 -163
- lionagi/tools/tool_util.py +2 -1
- lionagi/utils/__init__.py +7 -14
- lionagi/utils/api_util.py +63 -2
- lionagi/utils/core_utils.py +338 -0
- lionagi/utils/sys_util.py +3 -3
- lionagi/version.py +1 -1
- {lionagi-0.0.208.dist-info → lionagi-0.0.210.dist-info}/METADATA +28 -29
- lionagi-0.0.210.dist-info/RECORD +56 -0
- lionagi/_services/anthropic.py +0 -79
- lionagi/_services/anyscale.py +0 -0
- lionagi/_services/azure.py +0 -1
- lionagi/_services/bedrock.py +0 -0
- lionagi/_services/everlyai.py +0 -0
- lionagi/_services/gemini.py +0 -0
- lionagi/_services/gpt4all.py +0 -0
- lionagi/_services/huggingface.py +0 -0
- lionagi/_services/litellm.py +0 -33
- lionagi/_services/localai.py +0 -0
- lionagi/_services/openllm.py +0 -0
- lionagi/_services/openrouter.py +0 -44
- lionagi/_services/perplexity.py +0 -0
- lionagi/_services/predibase.py +0 -0
- lionagi/_services/rungpt.py +0 -0
- lionagi/_services/vllm.py +0 -0
- lionagi/_services/xinference.py +0 -0
- lionagi/agents/planner.py +0 -1
- lionagi/agents/prompter.py +0 -1
- lionagi/agents/scorer.py +0 -1
- lionagi/agents/summarizer.py +0 -1
- lionagi/agents/validator.py +0 -1
- lionagi/bridge/__init__.py +0 -22
- lionagi/bridge/langchain.py +0 -195
- lionagi/bridge/llama_index.py +0 -266
- lionagi/core/branch/__init__.py +0 -0
- lionagi/core/branch/branch.py +0 -841
- lionagi/core/branch/cluster.py +0 -1
- lionagi/core/branch/conversation.py +0 -787
- lionagi/core/core_util.py +0 -0
- lionagi/core/flow/__init__.py +0 -0
- lionagi/core/flow/flow.py +0 -19
- lionagi/core/flow/flow_util.py +0 -62
- lionagi/core/instruction_set/__init__.py +0 -0
- lionagi/core/messages/__init__.py +0 -0
- lionagi/core/sessions/__init__.py +0 -0
- lionagi/core/sessions/session.py +0 -504
- lionagi/datastores/__init__.py +0 -1
- lionagi/datastores/chroma.py +0 -1
- lionagi/datastores/deeplake.py +0 -1
- lionagi/datastores/elasticsearch.py +0 -1
- lionagi/datastores/lantern.py +0 -1
- lionagi/datastores/pinecone.py +0 -1
- lionagi/datastores/postgres.py +0 -1
- lionagi/datastores/qdrant.py +0 -1
- lionagi/loaders/__init__.py +0 -18
- lionagi/loaders/chunker.py +0 -166
- lionagi/loaders/load_util.py +0 -240
- lionagi/loaders/reader.py +0 -122
- lionagi/models/__init__.py +0 -0
- lionagi/models/base_model.py +0 -0
- lionagi/models/imodel.py +0 -53
- lionagi/schema/async_queue.py +0 -158
- lionagi/schema/base_condition.py +0 -1
- lionagi/schema/base_node.py +0 -422
- lionagi/schema/base_tool.py +0 -44
- lionagi/schema/data_logger.py +0 -126
- lionagi/schema/data_node.py +0 -88
- lionagi/schema/status_tracker.py +0 -37
- lionagi/tests/test_utils/test_encrypt_util.py +0 -323
- lionagi/utils/encrypt_util.py +0 -283
- lionagi/utils/url_util.py +0 -55
- lionagi-0.0.208.dist-info/RECORD +0 -106
- lionagi/{agents → api_service}/__init__.py +0 -0
- lionagi/core/{branch/branch_manager.py → branch_manager.py} +0 -0
- lionagi/core/{messages/messages.py → messages.py} +3 -3
- /lionagi/{_services → services}/__init__.py +0 -0
- /lionagi/{_services → services}/mistralai.py +0 -0
- /lionagi/{_services → services}/mlx_service.py +0 -0
- /lionagi/{_services → services}/ollama.py +0 -0
- /lionagi/{_services → services}/services.py +0 -0
- /lionagi/{_services → services}/transformers.py +0 -0
- {lionagi-0.0.208.dist-info → lionagi-0.0.210.dist-info}/LICENSE +0 -0
- {lionagi-0.0.208.dist-info → lionagi-0.0.210.dist-info}/WHEEL +0 -0
- {lionagi-0.0.208.dist-info → lionagi-0.0.210.dist-info}/top_level.txt +0 -0
@@ -1,787 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import pandas as pd
|
3
|
-
from datetime import datetime
|
4
|
-
from typing import Any, Optional, Dict, Union
|
5
|
-
|
6
|
-
from lionagi.utils.sys_util import as_dict, create_copy, strip_lower, to_df
|
7
|
-
from lionagi.utils.call_util import lcall
|
8
|
-
from ..messages.messages import Message, System, Instruction, Response
|
9
|
-
|
10
|
-
class Conversation:
|
11
|
-
"""
|
12
|
-
A class to represent a conversation, encapsulating messages within a pandas DataFrame.
|
13
|
-
|
14
|
-
Attributes:
|
15
|
-
messages (pd.DataFrame): A DataFrame holding conversation messages with columns specified in _cols.
|
16
|
-
"""
|
17
|
-
|
18
|
-
_cols = ["node_id", "role", "sender", "timestamp", "content"]
|
19
|
-
|
20
|
-
def __init__(self) -> None:
|
21
|
-
"""
|
22
|
-
Initializes a Conversation instance with an empty DataFrame structured to hold messages.
|
23
|
-
"""
|
24
|
-
self.messages = pd.DataFrame(columns=Conversation._cols)
|
25
|
-
|
26
|
-
@classmethod
|
27
|
-
def from_csv(cls, filepath: str, **kwargs) -> 'Conversation':
|
28
|
-
"""
|
29
|
-
Create a Conversation instance from a CSV file containing messages.
|
30
|
-
|
31
|
-
Args:
|
32
|
-
filepath (str): The path to the CSV file to be loaded.
|
33
|
-
**kwargs: Additional keyword arguments passed to pandas.read_csv function.
|
34
|
-
|
35
|
-
Returns:
|
36
|
-
Conversation: An instance of Conversation with messages loaded from the specified CSV file.
|
37
|
-
"""
|
38
|
-
messages = pd.read_csv(filepath)
|
39
|
-
messages = to_df(messages)
|
40
|
-
self = cls(messages=messages, **kwargs)
|
41
|
-
return self
|
42
|
-
|
43
|
-
@classmethod
|
44
|
-
def from_json(cls, filepath: str, **kwargs) -> 'Conversation':
|
45
|
-
"""
|
46
|
-
Create a Conversation instance from a JSON file containing messages.
|
47
|
-
|
48
|
-
Args:
|
49
|
-
filepath (str): The path to the JSON file to be loaded.
|
50
|
-
**kwargs: Additional keyword arguments passed to pandas.read_json function.
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
Conversation: An instance of Conversation with messages loaded from the specified JSON file.
|
54
|
-
"""
|
55
|
-
messages = pd.read_json(filepath, orient="records", lines=True)
|
56
|
-
messages = to_df(messages)
|
57
|
-
self = cls(messages=messages, **kwargs)
|
58
|
-
return self
|
59
|
-
|
60
|
-
@property
|
61
|
-
def last_row(self) -> pd.Series:
|
62
|
-
"""
|
63
|
-
Retrieve the last row from the conversation messages as a pandas Series.
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
pd.Series: The last message in the conversation.
|
67
|
-
"""
|
68
|
-
return get_rows(self.messages, n=1, from_='last')
|
69
|
-
|
70
|
-
@property
|
71
|
-
def first_system(self) -> pd.Series:
|
72
|
-
"""
|
73
|
-
Retrieve the first system message from the conversation.
|
74
|
-
|
75
|
-
Returns:
|
76
|
-
pd.Series: The first message in the conversation where the role is 'system'.
|
77
|
-
"""
|
78
|
-
return get_rows(self.messages, role='system', n=1, from_='front')
|
79
|
-
|
80
|
-
@property
|
81
|
-
def last_response(self) -> pd.Series:
|
82
|
-
"""
|
83
|
-
Retrieve the last response message from the conversation.
|
84
|
-
|
85
|
-
Returns:
|
86
|
-
pd.Series: The last message in the conversation where the role is 'assistant'.
|
87
|
-
"""
|
88
|
-
return get_rows(self.messages, role='assistant', n=1, from_='last')
|
89
|
-
|
90
|
-
@property
|
91
|
-
def last_response_content(self) -> Dict:
|
92
|
-
"""
|
93
|
-
Retrieve the last response message content from the conversation.
|
94
|
-
|
95
|
-
Returns:
|
96
|
-
pd.Series: The last message in the conversation where the role is 'assistant'.
|
97
|
-
"""
|
98
|
-
return as_dict(self.last_response.content.iloc[-1])
|
99
|
-
|
100
|
-
@property
|
101
|
-
def last_instruction(self) -> pd.Series:
|
102
|
-
"""
|
103
|
-
Retrieve the last instruction message from the conversation.
|
104
|
-
|
105
|
-
Returns:
|
106
|
-
pd.Series: The last message in the conversation where the role is 'user'.
|
107
|
-
"""
|
108
|
-
return get_rows(self.messages, role='user', n=1, from_='last')
|
109
|
-
|
110
|
-
@property
|
111
|
-
def last_action_request(self):
|
112
|
-
"""
|
113
|
-
Retrieve the last action request message from the conversation.
|
114
|
-
|
115
|
-
Returns:
|
116
|
-
pd.Series: The last message in the conversation with sender 'action_request'.
|
117
|
-
"""
|
118
|
-
return get_rows(self.messages, sender='action_request', n=1, from_='last')
|
119
|
-
|
120
|
-
@property
|
121
|
-
def last_action_response(self):
|
122
|
-
"""
|
123
|
-
Retrieve the last action response message from the conversation.
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
pd.Series: The last message in the conversation with sender 'action_response'.
|
127
|
-
"""
|
128
|
-
return get_rows(self.messages, sender='action_response', n=1, from_='last')
|
129
|
-
|
130
|
-
@property
|
131
|
-
def len_messages(self):
|
132
|
-
"""
|
133
|
-
Get the total number of messages in the conversation.
|
134
|
-
|
135
|
-
Returns:
|
136
|
-
int: The total number of messages.
|
137
|
-
"""
|
138
|
-
return len(self.messages)
|
139
|
-
|
140
|
-
@property
|
141
|
-
def len_instructions(self):
|
142
|
-
"""
|
143
|
-
Get the total number of instruction messages (messages with role 'user') in the conversation.
|
144
|
-
|
145
|
-
Returns:
|
146
|
-
int: The total number of instruction messages.
|
147
|
-
"""
|
148
|
-
return len(self.messages[self.messages.role == 'user'])
|
149
|
-
|
150
|
-
@property
|
151
|
-
def len_responses(self):
|
152
|
-
"""
|
153
|
-
Get the total number of response messages (messages with role 'assistant') in the conversation.
|
154
|
-
|
155
|
-
Returns:
|
156
|
-
int: The total number of response messages.
|
157
|
-
"""
|
158
|
-
|
159
|
-
return len(self.messages[self.messages.role == 'assistant'])
|
160
|
-
|
161
|
-
@property
|
162
|
-
def len_systems(self):
|
163
|
-
"""
|
164
|
-
Get the total number of system messages (messages with role 'system') in the conversation.
|
165
|
-
|
166
|
-
Returns:
|
167
|
-
int: The total number of system messages.
|
168
|
-
"""
|
169
|
-
return len(self.messages[self.messages.role == 'system'])
|
170
|
-
|
171
|
-
@property
|
172
|
-
def info(self):
|
173
|
-
"""
|
174
|
-
Get a summary of the conversation messages categorized by role.
|
175
|
-
|
176
|
-
Returns:
|
177
|
-
Dict[str, int]: A dictionary with keys as message roles and values as counts.
|
178
|
-
"""
|
179
|
-
|
180
|
-
return self._info()
|
181
|
-
|
182
|
-
@property
|
183
|
-
def sender_info(self):
|
184
|
-
"""
|
185
|
-
Provides a descriptive summary of the conversation, including the total number of messages,
|
186
|
-
a summary by role, and the first five messages.
|
187
|
-
|
188
|
-
Returns:
|
189
|
-
Dict[str, Any]: A dictionary containing the total number of messages, summary by role,
|
190
|
-
and a list of the first five message dictionaries.
|
191
|
-
"""
|
192
|
-
return self._info(use_sender=True)
|
193
|
-
|
194
|
-
@property
|
195
|
-
def describe(self) -> Dict[str, Any]:
|
196
|
-
"""
|
197
|
-
Provides a descriptive summary of the conversation, including the total number of messages,
|
198
|
-
a summary by role, and the first five messages.
|
199
|
-
|
200
|
-
Returns:
|
201
|
-
Dict[str, Any]: A dictionary containing the total number of messages, summary by role, and a list of the first maximum five message dictionaries.
|
202
|
-
"""
|
203
|
-
return {
|
204
|
-
"total_messages": len(self.messages),
|
205
|
-
"summary_by_role": self._info(),
|
206
|
-
"messages": [
|
207
|
-
msg.to_dict() for _, msg in self.messages.iterrows()
|
208
|
-
][: self.len_messages -1 if self.len_messages < 5 else 5],
|
209
|
-
}
|
210
|
-
|
211
|
-
def clone(self, num: Optional[int] = None) -> 'Conversation':
|
212
|
-
"""
|
213
|
-
Creates a copy or multiple copies of the current Conversation instance.
|
214
|
-
|
215
|
-
Args:
|
216
|
-
num (Optional[int], optional): The number of copies to create. If None, a single copy is created.
|
217
|
-
Defaults to None.
|
218
|
-
|
219
|
-
Returns:
|
220
|
-
Conversation: A new Conversation instance or a list of Conversation instances if num is specified.
|
221
|
-
"""
|
222
|
-
cloned = Conversation()
|
223
|
-
cloned.logger.set_dir(self.logger.dir)
|
224
|
-
cloned.messages = self.messages.copy()
|
225
|
-
if num:
|
226
|
-
return create_copy(cloned, num=num)
|
227
|
-
return cloned
|
228
|
-
|
229
|
-
def add_message(
|
230
|
-
self,
|
231
|
-
system: Optional[Union[dict, list, System]] = None,
|
232
|
-
instruction: Optional[Union[dict, list, Instruction]] = None,
|
233
|
-
context: Optional[Union[str, Dict[str, Any]]] = None,
|
234
|
-
response: Optional[Union[dict, list, Response]] = None,
|
235
|
-
sender: Optional[str] = None
|
236
|
-
) -> None:
|
237
|
-
"""
|
238
|
-
Adds a message to the conversation.
|
239
|
-
|
240
|
-
Args:
|
241
|
-
system (Optional[Union[dict, list, System]], optional): System message content or object.
|
242
|
-
instruction (Optional[Union[dict, list, Instruction]], optional): Instruction message content or object.
|
243
|
-
context (Optional[Union[str, Dict[str, Any]]], optional): Context for the message.
|
244
|
-
response (Optional[Union[dict, list, Response]], optional): Response message content or object.
|
245
|
-
sender (Optional[str], optional): The sender of the message.
|
246
|
-
|
247
|
-
Raises:
|
248
|
-
ValueError: If the content cannot be converted to a JSON string.
|
249
|
-
"""
|
250
|
-
msg = self._create_message(
|
251
|
-
system=system, instruction=instruction,
|
252
|
-
context=context, response=response, sender=sender
|
253
|
-
)
|
254
|
-
message_dict = msg.to_dict()
|
255
|
-
if isinstance(as_dict(message_dict['content']), dict):
|
256
|
-
message_dict['content'] = json.dumps(message_dict['content'])
|
257
|
-
message_dict['timestamp'] = datetime.now().isoformat()
|
258
|
-
self.messages.loc[len(self.messages)] = message_dict
|
259
|
-
|
260
|
-
def remove_message(self, node_id: str) -> None:
|
261
|
-
"""
|
262
|
-
Removes a message from the conversation based on its node_id.
|
263
|
-
|
264
|
-
Args:
|
265
|
-
node_id (str): The node_id of the message to be removed.
|
266
|
-
"""
|
267
|
-
_remove_message(self.messages, node_id)
|
268
|
-
|
269
|
-
def update_message(
|
270
|
-
self, value: Any, node_id: Optional[str] = None, col: str = 'node_id'
|
271
|
-
) -> None:
|
272
|
-
"""
|
273
|
-
Updates a message in the conversation based on its node_id.
|
274
|
-
|
275
|
-
Args:
|
276
|
-
value (Any): The new value to update the message with.
|
277
|
-
node_id (Optional[str], optional): The node_id of the message to be updated. Defaults to None.
|
278
|
-
col (str, optional): The column to be updated. Defaults to 'node_id'.
|
279
|
-
|
280
|
-
Returns:
|
281
|
-
bool: True if the update was successful, False otherwise.
|
282
|
-
"""
|
283
|
-
return _update_row(self.messages, node_id=node_id, col=col, value=value)
|
284
|
-
|
285
|
-
def change_first_system_message(
|
286
|
-
self, system: Union[str, Dict[str, Any], System], sender: Optional[str] = None
|
287
|
-
):
|
288
|
-
"""
|
289
|
-
Updates the first system message in the conversation.
|
290
|
-
|
291
|
-
Args:
|
292
|
-
system (Union[str, Dict[str, Any], System]): The new system message content, which can be a string,
|
293
|
-
a dictionary of message content, or a System object.
|
294
|
-
sender (Optional[str], optional): The sender of the system message. Defaults to None.
|
295
|
-
|
296
|
-
Raises:
|
297
|
-
ValueError: If there are no system messages in the conversation or if the input cannot be
|
298
|
-
converted into a system message.
|
299
|
-
"""
|
300
|
-
if self.len_systems == 0:
|
301
|
-
raise ValueError("There is no system message in the messages.")
|
302
|
-
|
303
|
-
if not isinstance(system, (str, Dict, System)):
|
304
|
-
raise ValueError("Input cannot be converted into a system message.")
|
305
|
-
|
306
|
-
elif isinstance(system, (str, Dict)):
|
307
|
-
system = System(system, sender=sender)
|
308
|
-
|
309
|
-
elif isinstance(system, System):
|
310
|
-
message_dict = system.to_dict()
|
311
|
-
if sender:
|
312
|
-
message_dict['sender'] = sender
|
313
|
-
message_dict['timestamp'] = datetime.now().isoformat()
|
314
|
-
sys_index = self.messages[self.messages.role == 'system'].index
|
315
|
-
self.messages.loc[sys_index[0]] = message_dict
|
316
|
-
|
317
|
-
def rollback(self, steps: int) -> None:
|
318
|
-
"""
|
319
|
-
Removes the last 'n' messages from the conversation.
|
320
|
-
|
321
|
-
Args:
|
322
|
-
steps (int): The number of messages to remove from the end of the conversation.
|
323
|
-
|
324
|
-
Raises:
|
325
|
-
ValueError: If 'steps' is not a positive integer or exceeds the number of messages.
|
326
|
-
"""
|
327
|
-
return _remove_last_n_rows(self.messages, steps)
|
328
|
-
|
329
|
-
def clear_messages(self) -> None:
|
330
|
-
"""
|
331
|
-
Clears all messages from the conversation, resetting it to an empty state.
|
332
|
-
"""
|
333
|
-
self.messages = pd.DataFrame(columns=Conversation._cols)
|
334
|
-
|
335
|
-
def to_csv(self, filepath: str, **kwargs) -> None:
|
336
|
-
"""
|
337
|
-
Exports the conversation messages to a CSV file.
|
338
|
-
|
339
|
-
Args:
|
340
|
-
filepath (str): The path to the file where the CSV will be saved.
|
341
|
-
**kwargs: Additional keyword arguments passed to pandas.DataFrame.to_csv() method.
|
342
|
-
"""
|
343
|
-
self.messages.to_csv(filepath, **kwargs)
|
344
|
-
|
345
|
-
def to_json(self, filepath: str) -> None:
|
346
|
-
"""
|
347
|
-
Exports the conversation messages to a JSON file.
|
348
|
-
|
349
|
-
Args:
|
350
|
-
filepath (str): The path to the file where the JSON will be saved.
|
351
|
-
**kwargs: Additional keyword arguments passed to pandas.DataFrame.to_json() method, such as
|
352
|
-
'orient', 'lines', and 'date_format'.
|
353
|
-
|
354
|
-
Note:
|
355
|
-
The recommended kwargs for compatibility with the from_json class method are
|
356
|
-
orient='records', lines=True, and date_format='iso'.
|
357
|
-
"""
|
358
|
-
self.messages.to_json(
|
359
|
-
filepath, orient="records", lines=True, date_format="iso")
|
360
|
-
|
361
|
-
def replace_keyword(
|
362
|
-
self,
|
363
|
-
keyword: str,
|
364
|
-
replacement: str,
|
365
|
-
col: str = 'content',
|
366
|
-
case_sensitive: bool = False
|
367
|
-
) -> None:
|
368
|
-
"""
|
369
|
-
Replaces all occurrences of a keyword in a specified column of the conversation's messages with a given replacement.
|
370
|
-
|
371
|
-
Args:
|
372
|
-
keyword (str): The keyword to be replaced.
|
373
|
-
replacement (str): The string to replace the keyword with.
|
374
|
-
col (str, optional): The column where the replacement should occur. Defaults to 'content'.
|
375
|
-
case_sensitive (bool, optional): If True, the replacement is case sensitive. Defaults to False.
|
376
|
-
"""
|
377
|
-
_replace_keyword(
|
378
|
-
self.messages, keyword, replacement, col=col,
|
379
|
-
case_sensitive=case_sensitive
|
380
|
-
)
|
381
|
-
|
382
|
-
def search_keywords(
|
383
|
-
self,
|
384
|
-
keywords: Union[str, list],
|
385
|
-
case_sensitive: bool = False, reset_index: bool = False, dropna: bool = False
|
386
|
-
) -> pd.DataFrame:
|
387
|
-
"""
|
388
|
-
Searches for messages containing specified keywords within the conversation.
|
389
|
-
|
390
|
-
Args:
|
391
|
-
keywords (Union[str, list]): The keyword(s) to search for within the conversation's messages.
|
392
|
-
case_sensitive (bool, optional): If True, the search is case sensitive. Defaults to False.
|
393
|
-
reset_index (bool, optional): If True, resets the index of the resulting DataFrame. Defaults to False.
|
394
|
-
dropna (bool, optional): If True, drops messages with NA values before searching. Defaults to False.
|
395
|
-
|
396
|
-
Returns:
|
397
|
-
pd.DataFrame: A DataFrame containing messages that match the search criteria.
|
398
|
-
"""
|
399
|
-
return _search_keywords(
|
400
|
-
self.messages, keywords, case_sensitive, reset_index, dropna
|
401
|
-
)
|
402
|
-
|
403
|
-
def extend(self, messages: pd.DataFrame, **kwargs) -> None:
|
404
|
-
"""
|
405
|
-
Extends the conversation by appending new messages, optionally avoiding duplicates based on specified criteria.
|
406
|
-
|
407
|
-
Args:
|
408
|
-
messages (pd.DataFrame): A DataFrame containing new messages to append to the conversation.
|
409
|
-
**kwargs: Additional keyword arguments for handling duplicates (passed to pandas' drop_duplicates method).
|
410
|
-
"""
|
411
|
-
self.messages = _extend(self.messages, messages, **kwargs)
|
412
|
-
|
413
|
-
def filter_by(
|
414
|
-
self,
|
415
|
-
role: Optional[str] = None,
|
416
|
-
sender: Optional[str] = None,
|
417
|
-
start_time: Optional[datetime] = None,
|
418
|
-
end_time: Optional[datetime] = None,
|
419
|
-
content_keywords: Optional[Union[str, list]] = None,
|
420
|
-
case_sensitive: bool = False
|
421
|
-
) -> pd.DataFrame:
|
422
|
-
"""
|
423
|
-
Filters the conversation's messages based on specified criteria such as role, sender, time range, and keywords.
|
424
|
-
|
425
|
-
Args:
|
426
|
-
role (Optional[str]): Filter messages by role (e.g., 'user', 'assistant', 'system').
|
427
|
-
sender (Optional[str]): Filter messages by sender.
|
428
|
-
start_time (Optional[datetime]): Filter messages sent after this time.
|
429
|
-
end_time (Optional[datetime]): Filter messages sent before this time.
|
430
|
-
content_keywords (Optional[Union[str, list]]): Filter messages containing these keywords.
|
431
|
-
case_sensitive (bool, optional): If True, keyword search is case sensitive. Defaults to False.
|
432
|
-
|
433
|
-
Returns:
|
434
|
-
pd.DataFrame: A DataFrame containing messages that match the filter criteria.
|
435
|
-
"""
|
436
|
-
return _filter_messages_by(
|
437
|
-
self.messages, role=role, sender=sender,
|
438
|
-
start_time=start_time, end_time=end_time,
|
439
|
-
content_keywords=content_keywords, case_sensitive=case_sensitive
|
440
|
-
)
|
441
|
-
|
442
|
-
def _create_message(
|
443
|
-
self,
|
444
|
-
system: Optional[Union[dict, list, System]] = None,
|
445
|
-
instruction: Optional[Union[dict, list, Instruction]] = None,
|
446
|
-
context: Optional[Union[str, Dict[str, Any]]] = None,
|
447
|
-
response: Optional[Union[dict, list, Response]] = None,
|
448
|
-
sender: Optional[str] = None
|
449
|
-
) -> Message:
|
450
|
-
"""
|
451
|
-
Creates a message object based on the given parameters, ensuring only one message type is specified.
|
452
|
-
|
453
|
-
Args:
|
454
|
-
system (Optional[Union[dict, list, System]]): System message to be added.
|
455
|
-
instruction (Optional[Union[dict, list, Instruction]]): Instruction message to be added.
|
456
|
-
context (Optional[Union[str, Dict[str, Any]]]): Context for the instruction message.
|
457
|
-
response (Optional[Union[dict, list, Response]]): Response message to be added.
|
458
|
-
sender (Optional[str]): The sender of the message.
|
459
|
-
|
460
|
-
Returns:
|
461
|
-
Message: A Message object created from the provided parameters.
|
462
|
-
|
463
|
-
Raises:
|
464
|
-
ValueError: If more than one message type is specified or if the parameters do not form a valid message.
|
465
|
-
"""
|
466
|
-
if sum(lcall([system, instruction, response], bool)) != 1:
|
467
|
-
raise ValueError("Error: Message must have one and only one role.")
|
468
|
-
|
469
|
-
else:
|
470
|
-
if isinstance(any([system, instruction, response]), Message):
|
471
|
-
if system:
|
472
|
-
return system
|
473
|
-
elif instruction:
|
474
|
-
return instruction
|
475
|
-
elif response:
|
476
|
-
return response
|
477
|
-
|
478
|
-
msg = 0
|
479
|
-
if response:
|
480
|
-
msg = Response(response=response, sender=sender)
|
481
|
-
elif instruction:
|
482
|
-
msg = Instruction(instruction=instruction,
|
483
|
-
context=context, sender=sender)
|
484
|
-
elif system:
|
485
|
-
msg = System(system=system, sender=sender)
|
486
|
-
return msg
|
487
|
-
|
488
|
-
def _info(self, use_sender: bool = False) -> Dict[str, int]:
|
489
|
-
"""
|
490
|
-
Generates a summary of the conversation's messages, either by role or sender.
|
491
|
-
|
492
|
-
Args:
|
493
|
-
use_sender (bool, optional): If True, generates the summary based on sender. If False, uses role. Defaults to False.
|
494
|
-
|
495
|
-
Returns:
|
496
|
-
Dict[str, int]: A dictionary with counts of messages, categorized either by role or sender.
|
497
|
-
"""
|
498
|
-
messages = self.messages['sender'] if use_sender else self.messages['role']
|
499
|
-
result = messages.value_counts().to_dict()
|
500
|
-
result['total'] = len(self.len_messages)
|
501
|
-
return result
|
502
|
-
|
503
|
-
def validate_messages(messages):
|
504
|
-
"""
|
505
|
-
Validates the structure and content of a DataFrame containing conversation messages.
|
506
|
-
|
507
|
-
Args:
|
508
|
-
messages (pd.DataFrame): The DataFrame containing conversation messages to validate.
|
509
|
-
|
510
|
-
Returns:
|
511
|
-
bool: True if the DataFrame is valid, raises a ValueError otherwise.
|
512
|
-
|
513
|
-
Raises:
|
514
|
-
ValueError: If the DataFrame has unmatched columns, contains null values, has an unsupported role, or
|
515
|
-
if the content cannot be parsed as a JSON string.
|
516
|
-
"""
|
517
|
-
if list(messages.columns) != ['node_id', 'role', 'sender', 'timestamp', 'content']:
|
518
|
-
raise ValueError('Invalid messages dataframe. Unmatched columns.')
|
519
|
-
if messages.isnull().values.any():
|
520
|
-
raise ValueError('Invalid messages dataframe. Cannot have null.')
|
521
|
-
if not all(role in ['system', 'user', 'assistant'] for role in messages['role'].unique()):
|
522
|
-
raise ValueError('Invalid messages dataframe. Cannot have role other than ["system", "user", "assistant"].')
|
523
|
-
for cont in messages['content']:
|
524
|
-
if cont.startswith('Sender'):
|
525
|
-
cont = cont.split(':', 1)[1]
|
526
|
-
try:
|
527
|
-
json.loads(cont)
|
528
|
-
except:
|
529
|
-
raise ValueError('Invalid messages dataframe. Content expect json string.')
|
530
|
-
return True
|
531
|
-
|
532
|
-
def _sign_message(messages, sender: str):
|
533
|
-
"""
|
534
|
-
Prefixes each message in the DataFrame with 'Sender <sender>:' to indicate the message's origin.
|
535
|
-
|
536
|
-
Args:
|
537
|
-
messages (pd.DataFrame): The DataFrame containing conversation messages to sign.
|
538
|
-
sender (str): The name or identifier of the sender to prefix the messages with.
|
539
|
-
|
540
|
-
Returns:
|
541
|
-
pd.DataFrame: The DataFrame with updated messages signed by the specified sender.
|
542
|
-
|
543
|
-
Raises:
|
544
|
-
ValueError: If the sender is None or equivalent to the string 'none'.
|
545
|
-
"""
|
546
|
-
if sender is None or strip_lower(sender) == 'none':
|
547
|
-
raise ValueError("sender cannot be None")
|
548
|
-
df = messages.copy()
|
549
|
-
|
550
|
-
for i in df.index:
|
551
|
-
if not df.loc[i, 'content'].startswith('Sender'):
|
552
|
-
df.loc[i, 'content'] = f"Sender {sender}: {df.loc[i, 'content']}"
|
553
|
-
else:
|
554
|
-
content = df.loc[i, 'content'].split(':', 1)[1]
|
555
|
-
df.loc[i, 'content'] = f"Sender {sender}: {content}"
|
556
|
-
|
557
|
-
return to_df(df)
|
558
|
-
|
559
|
-
def _search_keywords(
|
560
|
-
messages,
|
561
|
-
keywords: Union[str, list],
|
562
|
-
case_sensitive: bool = False, reset_index=False, dropna=False
|
563
|
-
):
|
564
|
-
"""
|
565
|
-
Searches for keywords in the 'content' column of a DataFrame and returns matching rows.
|
566
|
-
|
567
|
-
Args:
|
568
|
-
messages (pd.DataFrame): The DataFrame to search within.
|
569
|
-
keywords (Union[str, List[str]]): Keyword(s) to search for. If a list, combines keywords with an OR condition.
|
570
|
-
case_sensitive (bool, optional): Whether the search should be case-sensitive. Defaults to False.
|
571
|
-
reset_index (bool, optional): Whether to reset the index of the resulting DataFrame. Defaults to False.
|
572
|
-
dropna (bool, optional): Whether to drop rows with NA values in the 'content' column. Defaults to False.
|
573
|
-
|
574
|
-
Returns:
|
575
|
-
pd.DataFrame: A DataFrame containing rows where the 'content' column matches the search criteria.
|
576
|
-
"""
|
577
|
-
out = ''
|
578
|
-
if isinstance(keywords, list):
|
579
|
-
keywords = '|'.join(keywords)
|
580
|
-
if not case_sensitive:
|
581
|
-
out = messages[
|
582
|
-
messages["content"].str.contains(keywords, case=False)
|
583
|
-
]
|
584
|
-
out = messages[messages["content"].str.contains(keywords)]
|
585
|
-
if reset_index or dropna:
|
586
|
-
out = to_df(out, reset_index=reset_index)
|
587
|
-
return out
|
588
|
-
|
589
|
-
def _filter_messages_by(
|
590
|
-
messages,
|
591
|
-
role: Optional[str] = None,
|
592
|
-
sender: Optional[str] = None,
|
593
|
-
start_time: Optional[datetime] = None,
|
594
|
-
end_time: Optional[datetime] = None,
|
595
|
-
content_keywords: Optional[Union[str, list]] = None,
|
596
|
-
case_sensitive: bool = False
|
597
|
-
) -> pd.DataFrame:
|
598
|
-
"""
|
599
|
-
Filters messages in a DataFrame based on specified criteria such as role, sender, time range, and keywords.
|
600
|
-
|
601
|
-
Args:
|
602
|
-
messages (pd.DataFrame): The DataFrame of messages to filter.
|
603
|
-
role (Optional[str]): The role to filter messages by (e.g., 'user', 'assistant').
|
604
|
-
sender (Optional[str]): The sender to filter messages by.
|
605
|
-
start_time (Optional[datetime]): The start time for filtering messages.
|
606
|
-
end_time (Optional[datetime]): The end time for filtering messages.
|
607
|
-
content_keywords (Optional[Union[str, list]]): Keywords to filter messages by content.
|
608
|
-
case_sensitive (bool): Determines if the keyword search should be case-sensitive.
|
609
|
-
|
610
|
-
Returns:
|
611
|
-
pd.DataFrame: A DataFrame containing messages that match the filter criteria.
|
612
|
-
|
613
|
-
Raises:
|
614
|
-
ValueError: If an error occurs during the filtering process.
|
615
|
-
"""
|
616
|
-
|
617
|
-
try:
|
618
|
-
outs = messages.copy()
|
619
|
-
|
620
|
-
if content_keywords:
|
621
|
-
outs = _search_keywords(content_keywords, case_sensitive)
|
622
|
-
|
623
|
-
outs = outs[outs['role'] == role] if role else outs
|
624
|
-
outs = outs[outs['sender'] == sender] if sender else outs
|
625
|
-
outs = outs[outs['timestamp'] > start_time] if start_time else outs
|
626
|
-
outs = outs[outs['timestamp'] < end_time] if end_time else outs
|
627
|
-
|
628
|
-
return to_df(outs)
|
629
|
-
|
630
|
-
except Exception as e:
|
631
|
-
raise ValueError(f"Error in filtering messages: {e}")
|
632
|
-
|
633
|
-
def _replace_keyword(
|
634
|
-
df,
|
635
|
-
keyword: str,
|
636
|
-
replacement: str,
|
637
|
-
col='content',
|
638
|
-
case_sensitive: bool = False
|
639
|
-
) -> None:
|
640
|
-
"""
|
641
|
-
Replaces occurrences of a keyword within a specified column of a DataFrame with a given replacement.
|
642
|
-
|
643
|
-
Args:
|
644
|
-
df (pd.DataFrame): The DataFrame to operate on.
|
645
|
-
keyword (str): The keyword to search for and replace.
|
646
|
-
replacement (str): The string to replace the keyword with.
|
647
|
-
col (str): The column to search for the keyword in.
|
648
|
-
case_sensitive (bool): If True, the search and replacement are case-sensitive.
|
649
|
-
|
650
|
-
Returns:
|
651
|
-
None: This function modifies the DataFrame in place.
|
652
|
-
"""
|
653
|
-
if not case_sensitive:
|
654
|
-
df[col] = df[col].str.replace(
|
655
|
-
keyword, replacement, case=False
|
656
|
-
)
|
657
|
-
else:
|
658
|
-
df[col] = df[col].str.replace(
|
659
|
-
keyword, replacement
|
660
|
-
)
|
661
|
-
|
662
|
-
def _remove_message(df, node_id: str) -> bool:
|
663
|
-
"""
|
664
|
-
Removes a message from the DataFrame based on its node_id.
|
665
|
-
|
666
|
-
Args:
|
667
|
-
df (pd.DataFrame): The DataFrame from which the message should be removed.
|
668
|
-
node_id (str): The node_id of the message to be removed.
|
669
|
-
|
670
|
-
Returns:
|
671
|
-
bool: True if the message was successfully removed, False otherwise.
|
672
|
-
"""
|
673
|
-
initial_length = len(df)
|
674
|
-
df = df[df["node_id"] != node_id]
|
675
|
-
|
676
|
-
return len(df) < initial_length
|
677
|
-
|
678
|
-
def _update_row(
|
679
|
-
df, node_id = None, col = "node_id", value = None
|
680
|
-
) -> bool:
|
681
|
-
"""
|
682
|
-
Updates the value of a specified column for a row identified by node_id in a DataFrame.
|
683
|
-
|
684
|
-
Args:
|
685
|
-
df (pd.DataFrame): The DataFrame to update.
|
686
|
-
node_id (Optional[str]): The node_id of the row to be updated.
|
687
|
-
col (str): The column to update.
|
688
|
-
value (Any): The new value to be assigned to the column.
|
689
|
-
|
690
|
-
Returns:
|
691
|
-
bool: True if the update was successful, False otherwise.
|
692
|
-
"""
|
693
|
-
index = df.index[df[col] == node_id].tolist()
|
694
|
-
if index:
|
695
|
-
df.at[index[0], col] = value
|
696
|
-
return True
|
697
|
-
return False
|
698
|
-
|
699
|
-
def _remove_last_n_rows(df, steps: int) -> None:
|
700
|
-
"""
|
701
|
-
Removes the last 'n' rows from a DataFrame.
|
702
|
-
|
703
|
-
Args:
|
704
|
-
df (pd.DataFrame): The DataFrame from which rows will be removed.
|
705
|
-
steps (int): The number of rows to remove.
|
706
|
-
|
707
|
-
Returns:
|
708
|
-
pd.DataFrame: The DataFrame after the last 'n' rows have been removed.
|
709
|
-
|
710
|
-
Raises:
|
711
|
-
ValueError: If 'steps' is less than 0 or greater than the number of rows in the DataFrame.
|
712
|
-
"""
|
713
|
-
if steps < 0 or steps > len(df):
|
714
|
-
raise ValueError("Steps must be a non-negative integer less than or equal to the number of messages.")
|
715
|
-
df = to_df(df[:-steps])
|
716
|
-
|
717
|
-
def get_rows(
|
718
|
-
df,
|
719
|
-
sender: Optional[str] = None,
|
720
|
-
role: Optional[str] = None,
|
721
|
-
n: int = 1,
|
722
|
-
sign_ = False,
|
723
|
-
from_="front",
|
724
|
-
) -> pd.DataFrame:
|
725
|
-
"""
|
726
|
-
Retrieves rows from a DataFrame based on specified sender, role, and quantity, optionally signing them.
|
727
|
-
|
728
|
-
Args:
|
729
|
-
df (pd.DataFrame): The DataFrame to retrieve rows from.
|
730
|
-
sender (Optional[str]): The sender based on which to filter rows.
|
731
|
-
role (Optional[str]): The role based on which to filter rows.
|
732
|
-
n (int): The number of rows to retrieve.
|
733
|
-
sign_ (bool): Whether to sign the retrieved rows.
|
734
|
-
from_ (str): Direction to retrieve rows ('front' for the first rows, 'last' for the last rows).
|
735
|
-
|
736
|
-
Returns:
|
737
|
-
pd.DataFrame: A DataFrame containing the retrieved rows.
|
738
|
-
"""
|
739
|
-
|
740
|
-
if from_ == "last":
|
741
|
-
if sender is None and role is None:
|
742
|
-
outs = df.iloc[-n:]
|
743
|
-
elif sender and role:
|
744
|
-
outs = df[(df['sender'] == sender) & (df['role'] == role)].iloc[-n:]
|
745
|
-
elif sender:
|
746
|
-
outs = df[df['sender'] == sender].iloc[-n:]
|
747
|
-
else:
|
748
|
-
outs = df[df['role'] == role].iloc[-n:]
|
749
|
-
|
750
|
-
elif from_ == "front":
|
751
|
-
if sender is None and role is None:
|
752
|
-
outs = df.iloc[:n]
|
753
|
-
elif sender and role:
|
754
|
-
outs = df[(df['sender'] == sender) & (df['role'] == role)].iloc[:n]
|
755
|
-
elif sender:
|
756
|
-
outs = df[df['sender'] == sender].iloc[:n]
|
757
|
-
else:
|
758
|
-
outs = df[df['role'] == role].iloc[:n]
|
759
|
-
|
760
|
-
return _sign_message(outs, sender) if sign_ else outs
|
761
|
-
|
762
|
-
def _extend(df1: pd.DataFrame, df2: pd.DataFrame, **kwargs) -> pd.DataFrame:
|
763
|
-
"""
|
764
|
-
Extends a DataFrame with another DataFrame, optionally removing duplicates based on specified criteria.
|
765
|
-
|
766
|
-
Args:
|
767
|
-
df1 (pd.DataFrame): The original DataFrame to be extended.
|
768
|
-
df2 (pd.DataFrame): The DataFrame containing new rows to add to df1.
|
769
|
-
**kwargs: Additional keyword arguments for pandas.DataFrame.drop_duplicates().
|
770
|
-
|
771
|
-
Returns:
|
772
|
-
pd.DataFrame: The extended DataFrame after adding rows from df2 and removing duplicates.
|
773
|
-
|
774
|
-
Raises:
|
775
|
-
ValueError: If an error occurs during the extension process.
|
776
|
-
"""
|
777
|
-
validate_messages(df2)
|
778
|
-
try:
|
779
|
-
if len(df2.dropna(how='all')) > 0 and len(df1.dropna(how='all')) > 0:
|
780
|
-
df = to_df([df1, df2])
|
781
|
-
df.drop_duplicates(
|
782
|
-
inplace=True, subset=['node_id'], keep='first', **kwargs
|
783
|
-
)
|
784
|
-
return to_df(df)
|
785
|
-
except Exception as e:
|
786
|
-
raise ValueError(f"Error in extending messages: {e}")
|
787
|
-
|