swarms 7.9.0__py3-none-any.whl → 7.9.1__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.
- swarms/structs/__init__.py +11 -1
- swarms/structs/agent.py +156 -57
- swarms/structs/conversation.py +103 -25
- swarms/structs/interactive_groupchat.py +570 -170
- swarms/structs/swarm_router.py +7 -8
- swarms/utils/litellm_wrapper.py +2 -0
- swarms/utils/retry_func.py +66 -0
- {swarms-7.9.0.dist-info → swarms-7.9.1.dist-info}/METADATA +1 -1
- {swarms-7.9.0.dist-info → swarms-7.9.1.dist-info}/RECORD +12 -11
- {swarms-7.9.0.dist-info → swarms-7.9.1.dist-info}/LICENSE +0 -0
- {swarms-7.9.0.dist-info → swarms-7.9.1.dist-info}/WHEEL +0 -0
- {swarms-7.9.0.dist-info → swarms-7.9.1.dist-info}/entry_points.txt +0 -0
swarms/structs/__init__.py
CHANGED
@@ -83,7 +83,13 @@ from swarms.structs.swarming_architectures import (
|
|
83
83
|
staircase_swarm,
|
84
84
|
star_swarm,
|
85
85
|
)
|
86
|
-
from swarms.structs.interactive_groupchat import
|
86
|
+
from swarms.structs.interactive_groupchat import (
|
87
|
+
InteractiveGroupChat,
|
88
|
+
round_robin_speaker,
|
89
|
+
random_speaker,
|
90
|
+
priority_speaker,
|
91
|
+
random_dynamic_speaker,
|
92
|
+
)
|
87
93
|
|
88
94
|
__all__ = [
|
89
95
|
"Agent",
|
@@ -156,4 +162,8 @@ __all__ = [
|
|
156
162
|
"find_agent_by_name",
|
157
163
|
"run_agent",
|
158
164
|
"InteractiveGroupChat",
|
165
|
+
"round_robin_speaker",
|
166
|
+
"random_speaker",
|
167
|
+
"priority_speaker",
|
168
|
+
"random_dynamic_speaker",
|
159
169
|
]
|
swarms/structs/agent.py
CHANGED
@@ -5,6 +5,7 @@ import os
|
|
5
5
|
import random
|
6
6
|
import threading
|
7
7
|
import time
|
8
|
+
import traceback
|
8
9
|
import uuid
|
9
10
|
from concurrent.futures import ThreadPoolExecutor
|
10
11
|
from datetime import datetime
|
@@ -85,6 +86,7 @@ from swarms.utils.index import (
|
|
85
86
|
)
|
86
87
|
from swarms.schemas.conversation_schema import ConversationSchema
|
87
88
|
from swarms.utils.output_types import OutputType
|
89
|
+
from swarms.utils.retry_func import retry_function
|
88
90
|
|
89
91
|
|
90
92
|
def stop_when_repeats(response: str) -> bool:
|
@@ -153,6 +155,12 @@ class AgentLLMInitializationError(AgentError):
|
|
153
155
|
pass
|
154
156
|
|
155
157
|
|
158
|
+
class AgentToolExecutionError(AgentError):
|
159
|
+
"""Exception raised when the agent fails to execute a tool. Check the tool's configuration and availability."""
|
160
|
+
|
161
|
+
pass
|
162
|
+
|
163
|
+
|
156
164
|
# [FEAT][AGENT]
|
157
165
|
class Agent:
|
158
166
|
"""
|
@@ -425,6 +433,7 @@ class Agent:
|
|
425
433
|
tool_call_summary: bool = True,
|
426
434
|
output_raw_json_from_tool_call: bool = False,
|
427
435
|
summarize_multiple_images: bool = False,
|
436
|
+
tool_retry_attempts: int = 3,
|
428
437
|
*args,
|
429
438
|
**kwargs,
|
430
439
|
):
|
@@ -564,6 +573,7 @@ class Agent:
|
|
564
573
|
output_raw_json_from_tool_call
|
565
574
|
)
|
566
575
|
self.summarize_multiple_images = summarize_multiple_images
|
576
|
+
self.tool_retry_attempts = tool_retry_attempts
|
567
577
|
|
568
578
|
# self.short_memory = self.short_memory_init()
|
569
579
|
|
@@ -791,10 +801,11 @@ class Agent:
|
|
791
801
|
or exists(self.mcp_urls)
|
792
802
|
or exists(self.mcp_config)
|
793
803
|
):
|
794
|
-
self.
|
795
|
-
|
796
|
-
|
797
|
-
|
804
|
+
if self.print_on is True:
|
805
|
+
self.pretty_print(
|
806
|
+
f"✨ [SYSTEM] Successfully integrated {len(tools)} MCP tools into agent: {self.agent_name} | Status: ONLINE | Time: {time.strftime('%H:%M:%S')} ✨",
|
807
|
+
loop_count=0,
|
808
|
+
)
|
798
809
|
|
799
810
|
return tools
|
800
811
|
except AgentMCPConnectionError as e:
|
@@ -1015,8 +1026,8 @@ class Agent:
|
|
1015
1026
|
# Print the request
|
1016
1027
|
if print_task is True:
|
1017
1028
|
formatter.print_panel(
|
1018
|
-
f"\n User: {task}",
|
1019
|
-
f"Task Request for {self.agent_name}",
|
1029
|
+
content=f"\n User: {task}",
|
1030
|
+
title=f"Task Request for {self.agent_name}",
|
1020
1031
|
)
|
1021
1032
|
|
1022
1033
|
while (
|
@@ -1077,6 +1088,8 @@ class Agent:
|
|
1077
1088
|
**kwargs,
|
1078
1089
|
)
|
1079
1090
|
|
1091
|
+
# If streaming is enabled, then don't print the response
|
1092
|
+
|
1080
1093
|
# Parse the response from the agent with the output type
|
1081
1094
|
if exists(self.tools_list_dictionary):
|
1082
1095
|
if isinstance(response, BaseModel):
|
@@ -1091,26 +1104,24 @@ class Agent:
|
|
1091
1104
|
)
|
1092
1105
|
|
1093
1106
|
# Print
|
1094
|
-
self.
|
1107
|
+
if self.print_on is True:
|
1108
|
+
if isinstance(response, list):
|
1109
|
+
self.pretty_print(
|
1110
|
+
f"Structured Output - Attempting Function Call Execution [{time.strftime('%H:%M:%S')}] \n\n {format_data_structure(response)} ",
|
1111
|
+
loop_count,
|
1112
|
+
)
|
1113
|
+
elif self.streaming_on is True:
|
1114
|
+
pass
|
1115
|
+
else:
|
1116
|
+
self.pretty_print(
|
1117
|
+
response, loop_count
|
1118
|
+
)
|
1095
1119
|
|
1096
1120
|
# Check and execute callable tools
|
1097
1121
|
if exists(self.tools):
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
):
|
1102
|
-
response = response
|
1103
|
-
else:
|
1104
|
-
# Only execute tools if response is not None
|
1105
|
-
if response is not None:
|
1106
|
-
self.execute_tools(
|
1107
|
-
response=response,
|
1108
|
-
loop_count=loop_count,
|
1109
|
-
)
|
1110
|
-
else:
|
1111
|
-
logger.warning(
|
1112
|
-
f"LLM returned None response in loop {loop_count}, skipping tool execution"
|
1113
|
-
)
|
1122
|
+
self.tool_execution_retry(
|
1123
|
+
response, loop_count
|
1124
|
+
)
|
1114
1125
|
|
1115
1126
|
# Handle MCP tools
|
1116
1127
|
if (
|
@@ -1141,8 +1152,12 @@ class Agent:
|
|
1141
1152
|
self.save()
|
1142
1153
|
|
1143
1154
|
logger.error(
|
1144
|
-
f"Attempt {attempt+1}: Error generating"
|
1145
|
-
f"
|
1155
|
+
f"Attempt {attempt+1}/{self.max_retries}: Error generating response in loop {loop_count} for agent '{self.agent_name}': {str(e)} | "
|
1156
|
+
f"Error type: {type(e).__name__}, Error details: {e.__dict__ if hasattr(e, '__dict__') else 'No additional details'} | "
|
1157
|
+
f"Current task: '{task}', Agent state: max_loops={self.max_loops}, "
|
1158
|
+
f"model={getattr(self.llm, 'model_name', 'unknown')}, "
|
1159
|
+
f"temperature={getattr(self.llm, 'temperature', 'unknown')}"
|
1160
|
+
f"{f' | Traceback: {e.__traceback__}' if hasattr(e, '__traceback__') else ''}"
|
1146
1161
|
)
|
1147
1162
|
attempt += 1
|
1148
1163
|
|
@@ -1164,13 +1179,19 @@ class Agent:
|
|
1164
1179
|
self.stopping_condition is not None
|
1165
1180
|
and self._check_stopping_condition(response)
|
1166
1181
|
):
|
1167
|
-
logger.info(
|
1182
|
+
logger.info(
|
1183
|
+
f"Agent '{self.agent_name}' stopping condition met. "
|
1184
|
+
f"Loop: {loop_count}, Response length: {len(str(response)) if response else 0}"
|
1185
|
+
)
|
1168
1186
|
break
|
1169
1187
|
elif (
|
1170
1188
|
self.stopping_func is not None
|
1171
1189
|
and self.stopping_func(response)
|
1172
1190
|
):
|
1173
|
-
logger.info(
|
1191
|
+
logger.info(
|
1192
|
+
f"Agent '{self.agent_name}' stopping function condition met. "
|
1193
|
+
f"Loop: {loop_count}, Response length: {len(str(response)) if response else 0}"
|
1194
|
+
)
|
1174
1195
|
break
|
1175
1196
|
|
1176
1197
|
if self.interactive:
|
@@ -1217,14 +1238,27 @@ class Agent:
|
|
1217
1238
|
self._handle_run_error(error)
|
1218
1239
|
|
1219
1240
|
def __handle_run_error(self, error: any):
|
1241
|
+
import traceback
|
1242
|
+
|
1220
1243
|
log_agent_data(self.to_dict())
|
1221
1244
|
|
1222
1245
|
if self.autosave is True:
|
1223
1246
|
self.save()
|
1224
1247
|
|
1225
|
-
|
1226
|
-
|
1248
|
+
# Get detailed error information
|
1249
|
+
error_type = type(error).__name__
|
1250
|
+
error_message = str(error)
|
1251
|
+
traceback_info = traceback.format_exc()
|
1252
|
+
|
1253
|
+
logger.error(
|
1254
|
+
f"Error detected running your agent {self.agent_name}\n"
|
1255
|
+
f"Error Type: {error_type}\n"
|
1256
|
+
f"Error Message: {error_message}\n"
|
1257
|
+
f"Traceback:\n{traceback_info}\n"
|
1258
|
+
f"Agent State: {self.to_dict()}\n"
|
1259
|
+
f"Optimize your input parameters and or add an issue on the swarms github and contact our team on discord for support ;)"
|
1227
1260
|
)
|
1261
|
+
|
1228
1262
|
raise error
|
1229
1263
|
|
1230
1264
|
def _handle_run_error(self, error: any):
|
@@ -2790,19 +2824,23 @@ class Agent:
|
|
2790
2824
|
return self.role
|
2791
2825
|
|
2792
2826
|
def pretty_print(self, response: str, loop_count: int):
|
2793
|
-
if self.print_on is False:
|
2794
|
-
|
2795
|
-
|
2796
|
-
|
2797
|
-
|
2798
|
-
|
2799
|
-
|
2800
|
-
|
2801
|
-
|
2802
|
-
|
2803
|
-
|
2804
|
-
|
2805
|
-
|
2827
|
+
# if self.print_on is False:
|
2828
|
+
# if self.streaming_on is True:
|
2829
|
+
# # Skip printing here since real streaming is handled in call_llm
|
2830
|
+
# # This avoids double printing when streaming_on=True
|
2831
|
+
# pass
|
2832
|
+
# elif self.print_on is False:
|
2833
|
+
# pass
|
2834
|
+
# else:
|
2835
|
+
# # logger.info(f"Response: {response}")
|
2836
|
+
# formatter.print_panel(
|
2837
|
+
# response,
|
2838
|
+
# f"Agent Name {self.agent_name} [Max Loops: {loop_count} ]",
|
2839
|
+
# )
|
2840
|
+
formatter.print_panel(
|
2841
|
+
response,
|
2842
|
+
f"Agent Name {self.agent_name} [Max Loops: {loop_count} ]",
|
2843
|
+
)
|
2806
2844
|
|
2807
2845
|
def parse_llm_output(self, response: Any):
|
2808
2846
|
"""Parse and standardize the output from the LLM.
|
@@ -2915,10 +2953,10 @@ class Agent:
|
|
2915
2953
|
# execute_tool_call_simple returns a string directly, not an object with content attribute
|
2916
2954
|
text_content = f"MCP Tool Response: \n\n {json.dumps(tool_response, indent=2)}"
|
2917
2955
|
|
2918
|
-
if self.print_on is
|
2956
|
+
if self.print_on is True:
|
2919
2957
|
formatter.print_panel(
|
2920
|
-
text_content,
|
2921
|
-
"MCP Tool Response: 🛠️",
|
2958
|
+
content=text_content,
|
2959
|
+
title="MCP Tool Response: 🛠️",
|
2922
2960
|
style="green",
|
2923
2961
|
)
|
2924
2962
|
|
@@ -2942,7 +2980,8 @@ class Agent:
|
|
2942
2980
|
# Fallback: provide a default summary
|
2943
2981
|
summary = "I successfully executed the MCP tool and retrieved the information above."
|
2944
2982
|
|
2945
|
-
self.
|
2983
|
+
if self.print_on is True:
|
2984
|
+
self.pretty_print(summary, loop_count=current_loop)
|
2946
2985
|
|
2947
2986
|
# Add to the memory
|
2948
2987
|
self.short_memory.add(
|
@@ -2974,21 +3013,30 @@ class Agent:
|
|
2974
3013
|
)
|
2975
3014
|
return
|
2976
3015
|
|
2977
|
-
|
2978
|
-
self.tool_struct.execute_function_calls_from_api_response(
|
3016
|
+
try:
|
3017
|
+
output = self.tool_struct.execute_function_calls_from_api_response(
|
2979
3018
|
response
|
2980
3019
|
)
|
2981
|
-
|
3020
|
+
except Exception as e:
|
3021
|
+
# Retry the tool call
|
3022
|
+
output = self.tool_struct.execute_function_calls_from_api_response(
|
3023
|
+
response
|
3024
|
+
)
|
3025
|
+
|
3026
|
+
if output is None:
|
3027
|
+
logger.error(f"Error executing tools: {e}")
|
3028
|
+
raise e
|
2982
3029
|
|
2983
3030
|
self.short_memory.add(
|
2984
3031
|
role="Tool Executor",
|
2985
3032
|
content=format_data_structure(output),
|
2986
3033
|
)
|
2987
3034
|
|
2988
|
-
self.
|
2989
|
-
|
2990
|
-
|
2991
|
-
|
3035
|
+
if self.print_on is True:
|
3036
|
+
self.pretty_print(
|
3037
|
+
f"Tool Executed Successfully [{time.strftime('%H:%M:%S')}]",
|
3038
|
+
loop_count,
|
3039
|
+
)
|
2992
3040
|
|
2993
3041
|
# Now run the LLM again without tools - create a temporary LLM instance
|
2994
3042
|
# instead of modifying the cached one
|
@@ -3012,10 +3060,11 @@ class Agent:
|
|
3012
3060
|
content=tool_response,
|
3013
3061
|
)
|
3014
3062
|
|
3015
|
-
self.
|
3016
|
-
|
3017
|
-
|
3018
|
-
|
3063
|
+
if self.print_on is True:
|
3064
|
+
self.pretty_print(
|
3065
|
+
tool_response,
|
3066
|
+
loop_count,
|
3067
|
+
)
|
3019
3068
|
|
3020
3069
|
def list_output_types(self):
|
3021
3070
|
return OutputType
|
@@ -3150,3 +3199,53 @@ class Agent:
|
|
3150
3199
|
raise Exception(
|
3151
3200
|
f"Failed to find correct answer '{correct_answer}' after {max_attempts} attempts"
|
3152
3201
|
)
|
3202
|
+
|
3203
|
+
def tool_execution_retry(self, response: any, loop_count: int):
|
3204
|
+
"""
|
3205
|
+
Execute tools with retry logic for handling failures.
|
3206
|
+
|
3207
|
+
This method attempts to execute tools based on the LLM response. If the response
|
3208
|
+
is None, it logs a warning and skips execution. If an exception occurs during
|
3209
|
+
tool execution, it logs the error with full traceback and retries the operation
|
3210
|
+
using the configured retry attempts.
|
3211
|
+
|
3212
|
+
Args:
|
3213
|
+
response (any): The response from the LLM that may contain tool calls to execute.
|
3214
|
+
Can be None if the LLM failed to provide a valid response.
|
3215
|
+
loop_count (int): The current iteration loop number for logging and debugging purposes.
|
3216
|
+
|
3217
|
+
Returns:
|
3218
|
+
None
|
3219
|
+
|
3220
|
+
Raises:
|
3221
|
+
Exception: Re-raises any exception that occurs during tool execution after
|
3222
|
+
all retry attempts have been exhausted.
|
3223
|
+
|
3224
|
+
Note:
|
3225
|
+
- Uses self.tool_retry_attempts for the maximum number of retry attempts
|
3226
|
+
- Logs detailed error information including agent name and loop count
|
3227
|
+
- Skips execution gracefully if response is None
|
3228
|
+
"""
|
3229
|
+
try:
|
3230
|
+
if response is not None:
|
3231
|
+
self.execute_tools(
|
3232
|
+
response=response,
|
3233
|
+
loop_count=loop_count,
|
3234
|
+
)
|
3235
|
+
else:
|
3236
|
+
logger.warning(
|
3237
|
+
f"Agent '{self.agent_name}' received None response from LLM in loop {loop_count}. "
|
3238
|
+
f"This may indicate an issue with the model or prompt. Skipping tool execution."
|
3239
|
+
)
|
3240
|
+
except Exception as e:
|
3241
|
+
logger.error(
|
3242
|
+
f"Agent '{self.agent_name}' encountered error during tool execution in loop {loop_count}: {str(e)}. "
|
3243
|
+
f"Full traceback: {traceback.format_exc()}. "
|
3244
|
+
f"Attempting to retry tool execution with 3 attempts"
|
3245
|
+
)
|
3246
|
+
retry_function(
|
3247
|
+
self.execute_tools,
|
3248
|
+
response=response,
|
3249
|
+
loop_count=loop_count,
|
3250
|
+
max_retries=self.tool_retry_attempts,
|
3251
|
+
)
|
swarms/structs/conversation.py
CHANGED
@@ -221,27 +221,6 @@ class Conversation(BaseStructure):
|
|
221
221
|
):
|
222
222
|
super().__init__()
|
223
223
|
|
224
|
-
# Support both 'provider' and 'backend' parameters for backwards compatibility
|
225
|
-
# 'backend' takes precedence if both are provided
|
226
|
-
self.backend = backend or provider
|
227
|
-
self.backend_instance = None
|
228
|
-
|
229
|
-
# Validate backend
|
230
|
-
valid_backends = [
|
231
|
-
"in-memory",
|
232
|
-
"mem0",
|
233
|
-
"supabase",
|
234
|
-
"redis",
|
235
|
-
"sqlite",
|
236
|
-
"duckdb",
|
237
|
-
"pulsar",
|
238
|
-
]
|
239
|
-
if self.backend not in valid_backends:
|
240
|
-
raise ValueError(
|
241
|
-
f"Invalid backend: '{self.backend}'. "
|
242
|
-
f"Valid backends are: {', '.join(valid_backends)}"
|
243
|
-
)
|
244
|
-
|
245
224
|
# Initialize all attributes first
|
246
225
|
self.id = id
|
247
226
|
self.name = name or id
|
@@ -275,6 +254,27 @@ class Conversation(BaseStructure):
|
|
275
254
|
self.provider = provider # Keep for backwards compatibility
|
276
255
|
self.conversations_dir = conversations_dir
|
277
256
|
|
257
|
+
# Support both 'provider' and 'backend' parameters for backwards compatibility
|
258
|
+
# 'backend' takes precedence if both are provided
|
259
|
+
self.backend = backend or provider
|
260
|
+
self.backend_instance = None
|
261
|
+
|
262
|
+
# Validate backend
|
263
|
+
valid_backends = [
|
264
|
+
"in-memory",
|
265
|
+
"mem0",
|
266
|
+
"supabase",
|
267
|
+
"redis",
|
268
|
+
"sqlite",
|
269
|
+
"duckdb",
|
270
|
+
"pulsar",
|
271
|
+
]
|
272
|
+
if self.backend not in valid_backends:
|
273
|
+
raise ValueError(
|
274
|
+
f"Invalid backend: '{self.backend}'. "
|
275
|
+
f"Valid backends are: {', '.join(valid_backends)}"
|
276
|
+
)
|
277
|
+
|
278
278
|
# Initialize backend if using persistent storage
|
279
279
|
if self.backend in [
|
280
280
|
"supabase",
|
@@ -484,8 +484,7 @@ class Conversation(BaseStructure):
|
|
484
484
|
self,
|
485
485
|
role: str,
|
486
486
|
content: Union[str, dict, list, Any],
|
487
|
-
|
488
|
-
**kwargs,
|
487
|
+
category: Optional[str] = None,
|
489
488
|
):
|
490
489
|
"""Add a message to the conversation history.
|
491
490
|
|
@@ -505,6 +504,9 @@ class Conversation(BaseStructure):
|
|
505
504
|
if self.message_id_on:
|
506
505
|
message["message_id"] = str(uuid.uuid4())
|
507
506
|
|
507
|
+
if category:
|
508
|
+
message["category"] = category
|
509
|
+
|
508
510
|
# Add message to conversation history
|
509
511
|
self.conversation_history.append(message)
|
510
512
|
|
@@ -520,6 +522,79 @@ class Conversation(BaseStructure):
|
|
520
522
|
f"Failed to autosave conversation: {str(e)}"
|
521
523
|
)
|
522
524
|
|
525
|
+
def export_and_count_categories(
|
526
|
+
self, tokenizer_model_name: Optional[str] = "gpt-4.1-mini"
|
527
|
+
) -> Dict[str, int]:
|
528
|
+
"""Export all messages with category 'input' and 'output' and count their tokens.
|
529
|
+
|
530
|
+
This method searches through the conversation history and:
|
531
|
+
1. Extracts all messages marked with category 'input' or 'output'
|
532
|
+
2. Concatenates the content of each category
|
533
|
+
3. Counts tokens for each category using the specified tokenizer model
|
534
|
+
|
535
|
+
Args:
|
536
|
+
tokenizer_model_name (str): Name of the model to use for tokenization
|
537
|
+
|
538
|
+
Returns:
|
539
|
+
Dict[str, int]: A dictionary containing:
|
540
|
+
- input_tokens: Number of tokens in input messages
|
541
|
+
- output_tokens: Number of tokens in output messages
|
542
|
+
- total_tokens: Total tokens across both categories
|
543
|
+
"""
|
544
|
+
try:
|
545
|
+
# Extract input and output messages
|
546
|
+
input_messages = []
|
547
|
+
output_messages = []
|
548
|
+
|
549
|
+
for message in self.conversation_history:
|
550
|
+
# Get message content and ensure it's a string
|
551
|
+
content = message.get("content", "")
|
552
|
+
if not isinstance(content, str):
|
553
|
+
content = str(content)
|
554
|
+
|
555
|
+
# Sort messages by category
|
556
|
+
category = message.get("category", "")
|
557
|
+
if category == "input":
|
558
|
+
input_messages.append(content)
|
559
|
+
elif category == "output":
|
560
|
+
output_messages.append(content)
|
561
|
+
|
562
|
+
# Join messages with spaces
|
563
|
+
all_input_text = " ".join(input_messages)
|
564
|
+
all_output_text = " ".join(output_messages)
|
565
|
+
|
566
|
+
print(all_input_text)
|
567
|
+
print(all_output_text)
|
568
|
+
|
569
|
+
# Count tokens only if there is text
|
570
|
+
input_tokens = (
|
571
|
+
count_tokens(all_input_text, tokenizer_model_name)
|
572
|
+
if all_input_text.strip()
|
573
|
+
else 0
|
574
|
+
)
|
575
|
+
output_tokens = (
|
576
|
+
count_tokens(all_output_text, tokenizer_model_name)
|
577
|
+
if all_output_text.strip()
|
578
|
+
else 0
|
579
|
+
)
|
580
|
+
total_tokens = input_tokens + output_tokens
|
581
|
+
|
582
|
+
return {
|
583
|
+
"input_tokens": input_tokens,
|
584
|
+
"output_tokens": output_tokens,
|
585
|
+
"total_tokens": total_tokens,
|
586
|
+
}
|
587
|
+
|
588
|
+
except Exception as e:
|
589
|
+
logger.error(
|
590
|
+
f"Error in export_and_count_categories: {str(e)}"
|
591
|
+
)
|
592
|
+
return {
|
593
|
+
"input_tokens": 0,
|
594
|
+
"output_tokens": 0,
|
595
|
+
"total_tokens": 0,
|
596
|
+
}
|
597
|
+
|
523
598
|
def add_mem0(
|
524
599
|
self,
|
525
600
|
role: str,
|
@@ -546,8 +621,9 @@ class Conversation(BaseStructure):
|
|
546
621
|
def add(
|
547
622
|
self,
|
548
623
|
role: str,
|
549
|
-
content: Union[str, dict, list],
|
624
|
+
content: Union[str, dict, list, Any],
|
550
625
|
metadata: Optional[dict] = None,
|
626
|
+
category: Optional[str] = None,
|
551
627
|
):
|
552
628
|
"""Add a message to the conversation history."""
|
553
629
|
# If using a persistent backend, delegate to it
|
@@ -562,7 +638,9 @@ class Conversation(BaseStructure):
|
|
562
638
|
)
|
563
639
|
return self.add_in_memory(role, content)
|
564
640
|
elif self.provider == "in-memory":
|
565
|
-
return self.add_in_memory(
|
641
|
+
return self.add_in_memory(
|
642
|
+
role=role, content=content, category=category
|
643
|
+
)
|
566
644
|
elif self.provider == "mem0":
|
567
645
|
return self.add_mem0(
|
568
646
|
role=role, content=content, metadata=metadata
|