From 5a089f5023f76cf7eb7f60fb16bacaf9d59cada8 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Sun, 18 May 2025 06:13:18 +0000 Subject: [PATCH 1/8] fix: robust agent serialization/deserialization and restoration of non-serializable properties (tokenizer, long_term_memory, logger_handler, agent_output, executor). Closes #640 --- examples/agent_save_load_full.py | 62 +++++++++++++++++++++++ swarms/structs/agent.py | 26 ++++++++++ swarms/structs/agent_non_serializable.py | 64 ++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 examples/agent_save_load_full.py create mode 100644 swarms/structs/agent_non_serializable.py diff --git a/examples/agent_save_load_full.py b/examples/agent_save_load_full.py new file mode 100644 index 00000000..758423f7 --- /dev/null +++ b/examples/agent_save_load_full.py @@ -0,0 +1,62 @@ +""" +Example: Fully Save and Load an Agent (Issue #640) + +This example demonstrates how to save and load an Agent instance such that all non-serializable properties +(tokenizer, long_term_memory, logger_handler, agent_output, executor) are restored after loading. + +This is a user-facing, production-grade demonstration for swarms. +""" + +from swarms.structs.agent import Agent +import os + +# Helper to safely print type or None for agent properties +def print_agent_properties(agent, label): + print(f"\n--- {label} ---") + for prop in ["tokenizer", "long_term_memory", "logger_handler", "agent_output", "executor"]: + value = getattr(agent, prop, None) + print(f"{prop}: {type(value)}") + +# --- Setup: Create and configure an agent --- +agent = Agent( + agent_name="test", + user_name="test_user", + system_prompt="This is a test agent", + max_loops=1, + context_length=200000, + autosave=True, + verbose=True, + artifacts_on=True, + artifacts_output_path="test", + artifacts_file_extension=".txt", +) + +# Optionally, interact with the agent to populate state +agent.run(task="hello") + +# Print non-serializable properties BEFORE saving +print_agent_properties(agent, "BEFORE SAVE") + +# Save the agent state +save_path = os.path.join(agent.workspace_dir, "test_state.json") +agent.save(save_path) + +# Delete the agent instance to simulate a fresh load +del agent + +# --- Load: Restore the agent from file --- +agent2 = Agent(agent_name="test") # Minimal init, will be overwritten by load +agent2.load(save_path) + +# Print non-serializable properties AFTER loading +print_agent_properties(agent2, "AFTER LOAD") + +# Confirm agent2 can still run tasks and autosave +result = agent2.run(task="What is 2+2?") +print("\nAgent2 run result:", result) + +# Clean up test file +try: + os.remove(save_path) +except Exception: + pass diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 2dece63b..eb5a7abc 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -67,6 +67,7 @@ from swarms.utils.pdf_to_text import pdf_to_text from swarms.utils.str_to_dict import str_to_dict from swarms.prompts.react_base_prompt import REACT_SYS_PROMPT from swarms.prompts.max_loop_prompt import generate_reasoning_prompt +from swarms.structs.agent_non_serializable import restore_non_serializable_properties # Utils @@ -1719,6 +1720,9 @@ class Agent: # Reinitialize any necessary runtime components self._reinitialize_after_load() + # Restore non-serializable properties (tokenizer, long_term_memory, logger_handler, agent_output, executor) + self.restore_non_serializable_properties() + if self.verbose: self._log_loaded_state_info(resolved_path) @@ -2775,3 +2779,25 @@ class Agent: role="Output Cleaner", content=response, ) + + def restore_non_serializable_properties(self): + """ + Restore non-serializable properties for the Agent instance after loading. + This should be called after loading agent state from disk. + """ + restore_non_serializable_properties(self) + + # Custom serialization for non-serializable properties + def __getstate__(self): + state = self.__dict__.copy() + # Remove non-serializable properties + for prop in ["tokenizer", "long_term_memory", "logger_handler", "agent_output", "executor"]: + if prop in state: + state[prop] = None # Or a serializable placeholder if needed + return state + + def __setstate__(self, state): + self.__dict__.update(state) + # Restore non-serializable properties after loading + if hasattr(self, 'restore_non_serializable_properties'): + self.restore_non_serializable_properties() diff --git a/swarms/structs/agent_non_serializable.py b/swarms/structs/agent_non_serializable.py new file mode 100644 index 00000000..e494d144 --- /dev/null +++ b/swarms/structs/agent_non_serializable.py @@ -0,0 +1,64 @@ +""" +Non-Serializable Properties Handler for Agent + +This module provides helper functions to save and restore non-serializable properties +(tokenizer, long_term_memory, logger_handler, agent_output, executor) for the Agent class. + +Usage: + from swarms.structs.agent_non_serializable import restore_non_serializable_properties + restore_non_serializable_properties(agent) +""" + +from transformers import AutoTokenizer +from concurrent.futures import ThreadPoolExecutor +import logging + +# Dummy/placeholder for long_term_memory and agent_output restoration +class DummyLongTermMemory: + def __init__(self): + self.memory = [] + def query(self, *args, **kwargs): + # Return an empty list or a default value to avoid errors + return [] + def save(self, path): + # Optionally implement a no-op save for compatibility + pass + +class DummyAgentOutput: + def __init__(self): + self.output = None + +def restore_non_serializable_properties(agent): + """ + Restore non-serializable properties for the Agent instance after loading. + This should be called after loading agent state from disk. + """ + # Restore tokenizer if model_name is available + if getattr(agent, "model_name", None): + try: + agent.tokenizer = AutoTokenizer.from_pretrained(agent.model_name) + except Exception: + agent.tokenizer = None + else: + agent.tokenizer = None + + # Restore long_term_memory (dummy for demo, replace with real backend as needed) + if getattr(agent, "long_term_memory", None) is None or not hasattr(agent.long_term_memory, "query"): + agent.long_term_memory = DummyLongTermMemory() + + # Restore logger_handler + try: + agent.logger_handler = logging.StreamHandler() + except Exception: + agent.logger_handler = None + + # Restore agent_output (dummy for demo, replace with real backend as needed) + agent.agent_output = DummyAgentOutput() + + # Restore executor + try: + agent.executor = ThreadPoolExecutor() + except Exception: + agent.executor = None + + return agent From 772460f5e3aaad478700940e985ac455c0437971 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Sun, 18 May 2025 06:48:42 +0000 Subject: [PATCH 2/8] fix: remove transformers import and tokenizer restoration logic from agent_non_serializable.py (resolves undefined import error) --- swarms/structs/agent_non_serializable.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/swarms/structs/agent_non_serializable.py b/swarms/structs/agent_non_serializable.py index e494d144..f3bdec9e 100644 --- a/swarms/structs/agent_non_serializable.py +++ b/swarms/structs/agent_non_serializable.py @@ -9,7 +9,6 @@ Usage: restore_non_serializable_properties(agent) """ -from transformers import AutoTokenizer from concurrent.futures import ThreadPoolExecutor import logging @@ -34,12 +33,15 @@ def restore_non_serializable_properties(agent): This should be called after loading agent state from disk. """ # Restore tokenizer if model_name is available - if getattr(agent, "model_name", None): - try: - agent.tokenizer = AutoTokenizer.from_pretrained(agent.model_name) - except Exception: - agent.tokenizer = None - else: + agent.tokenizer = None + try: + if getattr(agent, "model_name", None): + try: + from transformers import AutoTokenizer + agent.tokenizer = AutoTokenizer.from_pretrained(agent.model_name) + except Exception: + agent.tokenizer = None + except ImportError: agent.tokenizer = None # Restore long_term_memory (dummy for demo, replace with real backend as needed) From d40589e72016a79ac7dd666352cc0fd1b0100768 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Sun, 18 May 2025 07:02:52 +0000 Subject: [PATCH 3/8] fix: use LiteLLM tokenizer for agent restoration; robust non-serializable property handling --- swarms/structs/agent_non_serializable.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/swarms/structs/agent_non_serializable.py b/swarms/structs/agent_non_serializable.py index f3bdec9e..1342899f 100644 --- a/swarms/structs/agent_non_serializable.py +++ b/swarms/structs/agent_non_serializable.py @@ -32,16 +32,12 @@ def restore_non_serializable_properties(agent): Restore non-serializable properties for the Agent instance after loading. This should be called after loading agent state from disk. """ - # Restore tokenizer if model_name is available + # Restore tokenizer using LiteLLM if available agent.tokenizer = None try: - if getattr(agent, "model_name", None): - try: - from transformers import AutoTokenizer - agent.tokenizer = AutoTokenizer.from_pretrained(agent.model_name) - except Exception: - agent.tokenizer = None - except ImportError: + from swarms.utils.litellm_tokenizer import count_tokens + agent.tokenizer = count_tokens # Assign the function as a tokenizer interface + except Exception: agent.tokenizer = None # Restore long_term_memory (dummy for demo, replace with real backend as needed) From 61cb4aab15e39f9d7eff7b4028d856428ff7fa9c Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Mon, 26 May 2025 13:31:58 +0000 Subject: [PATCH 4/8] WIP: robust agent save/load example for conversation autosave and reload --- examples/agent_save_load_full.py | 95 ++++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/examples/agent_save_load_full.py b/examples/agent_save_load_full.py index 758423f7..96860cd5 100644 --- a/examples/agent_save_load_full.py +++ b/examples/agent_save_load_full.py @@ -1,14 +1,14 @@ """ -Example: Fully Save and Load an Agent (Issue #640) +Example: Fully Save and Load an Agent (with Conversation History) -This example demonstrates how to save and load an Agent instance such that all non-serializable properties -(tokenizer, long_term_memory, logger_handler, agent_output, executor) are restored after loading. - -This is a user-facing, production-grade demonstration for swarms. +This demonstrates how to: + 1. Auto-save conversation messages to JSON + 2. Save the full Agent state + 3. Load both the Agent state and the conversation back into a fresh Agent """ -from swarms.structs.agent import Agent import os +from swarms.structs.agent import Agent # Helper to safely print type or None for agent properties def print_agent_properties(agent, label): @@ -17,7 +17,18 @@ def print_agent_properties(agent, label): value = getattr(agent, prop, None) print(f"{prop}: {type(value)}") -# --- Setup: Create and configure an agent --- +# Helper to extract the conversation history list +def get_conversation_history(agent): + conv = getattr(agent, "conversation", None) or getattr(agent, "short_memory", None) + return getattr(conv, "conversation_history", None) + +# Robust helper to reload conversation from JSON into the correct attribute +def reload_conversation_from_json(agent, filepath): + conv = getattr(agent, "conversation", None) or getattr(agent, "short_memory", None) + if conv and hasattr(conv, "load_from_json"): + conv.load_from_json(filepath) + +# --- 1. Setup: Create and configure an agent with auto-save conversation --- agent = Agent( agent_name="test", user_name="test_user", @@ -29,34 +40,72 @@ agent = Agent( artifacts_on=True, artifacts_output_path="test", artifacts_file_extension=".txt", + conversation_kwargs={ + "auto_save": True, + "save_as_json_bool": True, + "save_filepath": "test_conversation_history.json" + } ) -# Optionally, interact with the agent to populate state +# --- 2. Interact to populate conversation --- agent.run(task="hello") +agent.run(task="What is your purpose?") +agent.run(task="Tell me a joke.") +agent.run(task="Summarize our conversation so far.") -# Print non-serializable properties BEFORE saving +# --- 3. Inspect before saving --- print_agent_properties(agent, "BEFORE SAVE") +print("\nConversation history BEFORE SAVE:", get_conversation_history(agent)) + +# --- 4. Save the agent state (conversation JSON was auto-saved under workspace) --- +state_path = os.path.join(agent.workspace_dir, "test_state.json") +agent.save(state_path) -# Save the agent state -save_path = os.path.join(agent.workspace_dir, "test_state.json") -agent.save(save_path) +# --- 5. Check that the conversation JSON file exists and print its contents --- +json_path = os.path.join(agent.workspace_dir, "test_conversation_history.json") +if os.path.exists(json_path): + print(f"\n[CHECK] Conversation JSON file found: {json_path}") + with open(json_path, "r") as f: + json_data = f.read() + print("[CHECK] JSON file contents:\n", json_data) +else: + print(f"[WARN] Conversation JSON file not found: {json_path}") -# Delete the agent instance to simulate a fresh load +# --- 6. Simulate fresh environment --- del agent -# --- Load: Restore the agent from file --- -agent2 = Agent(agent_name="test") # Minimal init, will be overwritten by load -agent2.load(save_path) +# --- 7. Load: Restore the agent configuration --- +agent2 = Agent(agent_name="test") +agent2.load(state_path) + +# --- 8. Load: Restore the conversation history from the workspace directory into a new Conversation object --- +from swarms.structs.conversation import Conversation +conversation_loaded = Conversation() +if os.path.exists(json_path): + conversation_loaded.load_from_json(json_path) + print("\n[CHECK] Loaded conversation from JSON into new Conversation object:") + print(conversation_loaded.conversation_history) +else: + print(f"[WARN] Conversation JSON file not found for loading: {json_path}") + +# --- 9. Assign loaded conversation to agent2 and check --- +if hasattr(agent2, "conversation"): + agent2.conversation = conversation_loaded +elif hasattr(agent2, "short_memory"): + agent2.short_memory = conversation_loaded +print("\n[CHECK] Agent2 conversation history after assigning loaded conversation:", get_conversation_history(agent2)) -# Print non-serializable properties AFTER loading +# --- 10. Inspect after loading --- print_agent_properties(agent2, "AFTER LOAD") +print("\nConversation history AFTER LOAD:", get_conversation_history(agent2)) -# Confirm agent2 can still run tasks and autosave +# --- 11. Confirm the agent can continue --- result = agent2.run(task="What is 2+2?") print("\nAgent2 run result:", result) -# Clean up test file -try: - os.remove(save_path) -except Exception: - pass +# --- 12. Cleanup test files --- +for path in (state_path, json_path): + try: + os.remove(path) + except OSError: + pass From 768467ccd5026c43a60a39b1c4570614cbf5d886 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Mon, 26 May 2025 13:44:06 +0000 Subject: [PATCH 5/8] fix: use short_memory for conversation save/load, ensure JSON file is created and loaded --- examples/agent_save_load_full.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/examples/agent_save_load_full.py b/examples/agent_save_load_full.py index 96860cd5..3e9bef83 100644 --- a/examples/agent_save_load_full.py +++ b/examples/agent_save_load_full.py @@ -61,8 +61,11 @@ print("\nConversation history BEFORE SAVE:", get_conversation_history(agent)) state_path = os.path.join(agent.workspace_dir, "test_state.json") agent.save(state_path) -# --- 5. Check that the conversation JSON file exists and print its contents --- +# --- 5. Ensure the conversation JSON file is saved and print its path and contents --- json_path = os.path.join(agent.workspace_dir, "test_conversation_history.json") +if hasattr(agent, "short_memory") and hasattr(agent.short_memory, "save_as_json"): + agent.short_memory.save_as_json(json_path) + if os.path.exists(json_path): print(f"\n[CHECK] Conversation JSON file found: {json_path}") with open(json_path, "r") as f: @@ -89,10 +92,7 @@ else: print(f"[WARN] Conversation JSON file not found for loading: {json_path}") # --- 9. Assign loaded conversation to agent2 and check --- -if hasattr(agent2, "conversation"): - agent2.conversation = conversation_loaded -elif hasattr(agent2, "short_memory"): - agent2.short_memory = conversation_loaded +agent2.short_memory = conversation_loaded print("\n[CHECK] Agent2 conversation history after assigning loaded conversation:", get_conversation_history(agent2)) # --- 10. Inspect after loading --- @@ -104,8 +104,18 @@ result = agent2.run(task="What is 2+2?") print("\nAgent2 run result:", result) # --- 12. Cleanup test files --- -for path in (state_path, json_path): - try: - os.remove(path) - except OSError: - pass +print(f"\n[INFO] Test complete. Conversation JSON and agent state files are available for inspection:") +print(f" Conversation JSON: {json_path}") +print(f" Agent state: {state_path}") +print("You can open and inspect these files to verify the agent's memory persistence.") +# Do NOT delete files automatically +# for path in (state_path, json_path): +# try: +# os.remove(path) +# except OSError: +# pass + +# --- 13. Test if agent2 remembers the previous conversation --- +print("\n[TEST] Checking if agent2 remembers the previous conversation after reload...") +probe = agent2.run(task="What did I ask you to do earlier?") +print("\nAgent2 memory probe result:", probe) From 9c58a314bb6fde743eb830a158a02d614cc5dcab Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Mon, 26 May 2025 14:16:06 +0000 Subject: [PATCH 6/8] update: README and examples removing usage of swarms.models and directly integrating models into agents using LiteLLM --- README.md | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 05b0cbdb..f4f85753 100644 --- a/README.md +++ b/README.md @@ -1386,10 +1386,7 @@ SpreadSheetSwarm manages thousands of agents concurrently for efficient task pro [Learn more:](https://docs.swarms.world/en/latest/swarms/structs/spreadsheet_swarm/) ```python -import os from swarms import Agent, SpreadSheetSwarm -from swarm_models import OpenAIChat - # Define custom system prompts for each social media platform TWITTER_AGENT_SYS_PROMPT = """ You are a Twitter marketing expert specializing in real estate. Your task is to create engaging, concise tweets to promote properties, analyze trends to maximize engagement, and use appropriate hashtags and timing to reach potential buyers. @@ -1416,7 +1413,7 @@ agents = [ Agent( agent_name="Twitter-RealEstate-Agent", system_prompt=TWITTER_AGENT_SYS_PROMPT, - model_name="gpt-4o", + model_name="gpt-4o-mini", max_loops=1, dynamic_temperature_enabled=True, saved_state_path="twitter_realestate_agent.json", @@ -1426,7 +1423,7 @@ agents = [ Agent( agent_name="Instagram-RealEstate-Agent", system_prompt=INSTAGRAM_AGENT_SYS_PROMPT, - model_name="gpt-4o", + model_name="gpt-4o-mini", max_loops=1, dynamic_temperature_enabled=True, saved_state_path="instagram_realestate_agent.json", @@ -1436,7 +1433,7 @@ agents = [ Agent( agent_name="Facebook-RealEstate-Agent", system_prompt=FACEBOOK_AGENT_SYS_PROMPT, - model_name="gpt-4o", + model_name="gpt-4o-mini", max_loops=1, dynamic_temperature_enabled=True, saved_state_path="facebook_realestate_agent.json", @@ -1446,7 +1443,7 @@ agents = [ Agent( agent_name="LinkedIn-RealEstate-Agent", system_prompt=LINKEDIN_AGENT_SYS_PROMPT, - model_name="gpt-4o", + model_name="gpt-4o-mini", max_loops=1, dynamic_temperature_enabled=True, saved_state_path="linkedin_realestate_agent.json", @@ -1456,7 +1453,7 @@ agents = [ Agent( agent_name="Email-RealEstate-Agent", system_prompt=EMAIL_AGENT_SYS_PROMPT, - model_name="gpt-4o", + model_name="gpt-4o-mini", max_loops=1, dynamic_temperature_enabled=True, saved_state_path="email_realestate_agent.json", @@ -1920,32 +1917,18 @@ A production-grade multi-agent system enabling sophisticated group conversations ```python - -import os -from dotenv import load_dotenv -from swarm_models import OpenAIChat from swarms import Agent, GroupChat, expertise_based if __name__ == "__main__": - load_dotenv() - - # Get the OpenAI API key from the environment variable - api_key = os.getenv("OPENAI_API_KEY") - - # Create an instance of the OpenAIChat class - model = OpenAIChat( - openai_api_key=api_key, - model_name="gpt-4o-mini", - temperature=0.1, - ) # Example agents agent1 = Agent( agent_name="Financial-Analysis-Agent", system_prompt="You are a financial analyst specializing in investment strategies.", - llm=model, + model_name="gpt-4o-mini", + temperature=0.1, max_loops=1, autosave=False, dashboard=False, @@ -1961,7 +1944,8 @@ if __name__ == "__main__": agent2 = Agent( agent_name="Tax-Adviser-Agent", system_prompt="You are a tax adviser who provides clear and concise guidance on tax-related queries.", - llm=model, + model_name="gpt-4o-mini", + temperature=0.1, max_loops=1, autosave=False, dashboard=False, @@ -1986,7 +1970,8 @@ if __name__ == "__main__": history = chat.run( "How to optimize tax strategy for investments?" ) - print(history.model_dump_json(indent=2)) + print(history) + ``` From 771e1e46dbc2aa99b13407f604873dd0cbe7b20c Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Tue, 3 Jun 2025 15:53:21 +0000 Subject: [PATCH 7/8] Revert agent.py to match master --- swarms/structs/agent.py | 593 +++++++++++++++++++++------------------- 1 file changed, 308 insertions(+), 285 deletions(-) diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index eb5a7abc..6fa058c7 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -23,7 +23,6 @@ import yaml from loguru import logger from pydantic import BaseModel -from swarms.agents.agent_print import agent_print from swarms.agents.ape_agent import auto_generate_prompt from swarms.artifacts.main_artifact import Artifact from swarms.prompts.agent_system_prompts import AGENT_SYSTEM_PROMPT_3 @@ -31,6 +30,10 @@ from swarms.prompts.multi_modal_autonomous_instruction_prompt import ( MULTI_MODAL_AUTO_AGENT_SYSTEM_PROMPT_1, ) from swarms.prompts.tools import tool_sop_prompt +from swarms.schemas.agent_mcp_errors import ( + AgentMCPConnectionError, + AgentMCPToolError, +) from swarms.schemas.agent_step_schemas import ManySteps, Step from swarms.schemas.base_schemas import ( AgentChatCompletionResponse, @@ -46,14 +49,9 @@ from swarms.structs.safe_loading import ( ) from swarms.telemetry.main import log_agent_data from swarms.tools.base_tool import BaseTool -from swarms.tools.mcp_client import ( - execute_mcp_tool, - find_and_execute_tool, - list_all, - list_tools_for_multiple_urls, +from swarms.tools.py_func_to_openai_func_str import ( + convert_multiple_functions_to_openai_function_schema, ) -from swarms.tools.mcp_integration import MCPServerSseParams -from swarms.tools.tool_parse_exec import parse_and_execute_json from swarms.utils.any_to_str import any_to_str from swarms.utils.data_to_text import data_to_text from swarms.utils.file_processing import create_file_in_folder @@ -64,10 +62,22 @@ from swarms.utils.history_output_formatter import ( from swarms.utils.litellm_tokenizer import count_tokens from swarms.utils.litellm_wrapper import LiteLLM from swarms.utils.pdf_to_text import pdf_to_text -from swarms.utils.str_to_dict import str_to_dict from swarms.prompts.react_base_prompt import REACT_SYS_PROMPT from swarms.prompts.max_loop_prompt import generate_reasoning_prompt -from swarms.structs.agent_non_serializable import restore_non_serializable_properties +from swarms.prompts.safety_prompt import SAFETY_PROMPT +from swarms.structs.ma_utils import set_random_models_for_agents +from swarms.tools.mcp_client_call import ( + execute_tool_call_simple, + get_mcp_tools_sync, +) +from swarms.schemas.mcp_schemas import ( + MCPConnection, +) +from swarms.utils.index import ( + exists, + format_data_structure, + format_dict_to_string, +) # Utils @@ -89,10 +99,6 @@ def agent_id(): return uuid.uuid4().hex -def exists(val): - return val is not None - - # Agent output types ToolUsageType = Union[BaseModel, Dict[str, Any]] @@ -358,9 +364,9 @@ class Agent: log_directory: str = None, tool_system_prompt: str = tool_sop_prompt(), max_tokens: int = 4096, - frequency_penalty: float = 0.0, - presence_penalty: float = 0.0, - temperature: float = 0.1, + frequency_penalty: float = 0.8, + presence_penalty: float = 0.6, + temperature: float = 0.5, workspace_dir: str = "agent_workspace", timeout: Optional[int] = None, # short_memory: Optional[str] = None, @@ -374,7 +380,6 @@ class Agent: "%Y-%m-%d %H:%M:%S", time.localtime() ), agent_output: ManySteps = None, - executor_workers: int = os.cpu_count(), data_memory: Optional[Callable] = None, load_yaml_path: str = None, auto_generate_prompt: bool = False, @@ -395,10 +400,13 @@ class Agent: role: agent_roles = "worker", no_print: bool = False, tools_list_dictionary: Optional[List[Dict[str, Any]]] = None, - mcp_servers: MCPServerSseParams = None, - mcp_url: str = None, + mcp_url: Optional[Union[str, MCPConnection]] = None, mcp_urls: List[str] = None, react_on: bool = False, + safety_prompt_on: bool = False, + random_models_on: bool = False, + mcp_config: Optional[MCPConnection] = None, + top_p: float = 0.90, *args, **kwargs, ): @@ -415,6 +423,7 @@ class Agent: self.stopping_token = stopping_token self.interactive = interactive self.dashboard = dashboard + self.saved_state_path = saved_state_path self.return_history = return_history self.dynamic_temperature_enabled = dynamic_temperature_enabled self.dynamic_loops = dynamic_loops @@ -517,10 +526,13 @@ class Agent: self.role = role self.no_print = no_print self.tools_list_dictionary = tools_list_dictionary - self.mcp_servers = mcp_servers self.mcp_url = mcp_url self.mcp_urls = mcp_urls self.react_on = react_on + self.safety_prompt_on = safety_prompt_on + self.random_models_on = random_models_on + self.mcp_config = mcp_config + self.top_p = top_p self._cached_llm = ( None # Add this line to cache the LLM instance @@ -532,41 +544,58 @@ class Agent: self.feedback = [] # self.init_handling() - # Define tasks as pairs of (function, condition) - # Each task will only run if its condition is True self.setup_config() if exists(self.docs_folder): self.get_docs_from_doc_folders() - if exists(self.tools): - self.handle_tool_init() - if exists(self.tool_schema) or exists(self.list_base_models): self.handle_tool_schema_ops() if exists(self.sop) or exists(self.sop_list): self.handle_sop_ops() + if self.max_loops >= 2: + self.system_prompt += generate_reasoning_prompt( + self.max_loops + ) + + if self.react_on is True: + self.system_prompt += REACT_SYS_PROMPT + + self.short_memory = self.short_memory_init() + # Run sequential operations after all concurrent tasks are done # self.agent_output = self.agent_output_model() log_agent_data(self.to_dict()) + if exists(self.tools): + self.tool_handling() + if self.llm is None: self.llm = self.llm_handling() - if self.mcp_url or self.mcp_servers is not None: - self.add_mcp_tools_to_memory() + if self.random_models_on is True: + self.model_name = set_random_models_for_agents() - if self.react_on is True: - self.system_prompt += REACT_SYS_PROMPT + def tool_handling(self): - if self.max_loops >= 2: - self.system_prompt += generate_reasoning_prompt( - self.max_loops + self.tool_struct = BaseTool( + tools=self.tools, + verbose=self.verbose, + ) + + # Convert all the tools into a list of dictionaries + self.tools_list_dictionary = ( + convert_multiple_functions_to_openai_function_schema( + self.tools ) + ) - self.short_memory = self.short_memory_init() + self.short_memory.add( + role=f"{self.agent_name}", + content=f"Tools available: {format_data_structure(self.tools_list_dictionary)}", + ) def short_memory_init(self): if ( @@ -577,8 +606,11 @@ class Agent: else: prompt = self.system_prompt + if self.safety_prompt_on is True: + prompt += SAFETY_PROMPT + # Initialize the short term memory - self.short_memory = Conversation( + memory = Conversation( system_prompt=prompt, time_enabled=False, user=self.user_name, @@ -586,7 +618,7 @@ class Agent: token_count=False, ) - return self.short_memory + return memory def agent_output_model(self): # Many steps @@ -616,6 +648,11 @@ class Agent: if self.model_name is None: self.model_name = "gpt-4o-mini" + if exists(self.tools) and len(self.tools) >= 2: + parallel_tool_calls = True + else: + parallel_tool_calls = False + try: # Simplify initialization logic common_args = { @@ -634,10 +671,16 @@ class Agent: **common_args, tools_list_dictionary=self.tools_list_dictionary, tool_choice="auto", - parallel_tool_calls=len( - self.tools_list_dictionary - ) - > 1, + parallel_tool_calls=parallel_tool_calls, + ) + + elif self.mcp_url is not None: + self._cached_llm = LiteLLM( + **common_args, + tools_list_dictionary=self.add_mcp_tools_to_memory(), + tool_choice="auto", + parallel_tool_calls=parallel_tool_calls, + mcp_call=True, ) else: self._cached_llm = LiteLLM( @@ -651,48 +694,6 @@ class Agent: ) return None - def handle_tool_init(self): - # Initialize the tool struct - if ( - exists(self.tools) - or exists(self.list_base_models) - or exists(self.tool_schema) - ): - - self.tool_struct = BaseTool( - tools=self.tools, - base_models=self.list_base_models, - tool_system_prompt=self.tool_system_prompt, - ) - - if self.tools is not None: - logger.info( - "Tools provided make sure the functions have documentation ++ type hints, otherwise tool execution won't be reliable." - ) - # Add the tool prompt to the memory - self.short_memory.add( - role="system", content=self.tool_system_prompt - ) - - # Log the tools - logger.info( - f"Tools provided: Accessing {len(self.tools)} tools" - ) - - # Transform the tools into an openai schema - # self.convert_tool_into_openai_schema() - - # Transform the tools into an openai schema - tool_dict = ( - self.tool_struct.convert_tool_into_openai_schema() - ) - self.short_memory.add(role="system", content=tool_dict) - - # Now create a function calling map for every tools - self.function_map = { - tool.__name__: tool for tool in self.tools - } - def add_mcp_tools_to_memory(self): """ Adds MCP tools to the agent's short-term memory. @@ -704,110 +705,23 @@ class Agent: Exception: If there's an error accessing the MCP tools """ try: - if self.mcp_url is not None: - tools_available = list_all( - self.mcp_url, output_type="json" - ) - self.short_memory.add( - role="Tools Available", - content=f"\n{tools_available}", - ) - - elif ( - self.mcp_url is None - and self.mcp_urls is not None - and len(self.mcp_urls) > 1 - ): - tools_available = list_tools_for_multiple_urls( - urls=self.mcp_urls, - output_type="json", - ) - - self.short_memory.add( - role="Tools Available", - content=f"\n{tools_available}", - ) - except Exception as e: - logger.error(f"Error adding MCP tools to memory: {e}") - raise e - - def _single_mcp_tool_handling(self, response: any): - """ - Handles execution of a single MCP tool. - - Args: - response (str): The tool response to process - - Raises: - Exception: If there's an error executing the tool - """ - try: - if isinstance(response, dict): - result = response - print(type(result)) + if exists(self.mcp_url): + tools = get_mcp_tools_sync(server_path=self.mcp_url) + elif exists(self.mcp_config): + tools = get_mcp_tools_sync(connection=self.mcp_config) + logger.info(f"Tools: {tools}") else: - result = str_to_dict(response) - print(type(result)) - - output = execute_mcp_tool( - url=self.mcp_url, - parameters=result, - ) - - self.short_memory.add( - role="Tool Executor", content=str(output) - ) - except Exception as e: - logger.error(f"Error in single MCP tool handling: {e}") - raise e - - def _multiple_mcp_tool_handling(self, response: any): - """ - Handles execution of multiple MCP tools. - - Args: - response (any): The tool response to process - - Raises: - Exception: If there's an error executing the tools - """ - try: - if isinstance(response, str): - response = str_to_dict(response) - - execution = find_and_execute_tool( - self.mcp_urls, - response["name"], - parameters=response, - ) - - self.short_memory.add( - role="Tool Executor", content=str(execution) + raise AgentMCPConnectionError( + "mcp_url must be either a string URL or MCPConnection object" + ) + self.pretty_print( + f"✨ [SYSTEM] Successfully integrated {len(tools)} MCP tools into agent: {self.agent_name} | Status: ONLINE | Time: {time.strftime('%H:%M:%S')} ✨", + loop_count=0, ) - except Exception as e: - logger.error(f"Error in multiple MCP tool handling: {e}") - raise e - - def mcp_tool_handling(self, response: any): - """ - Main handler for MCP tool execution. - - Args: - response (any): The tool response to process - Raises: - ValueError: If no MCP URL or MCP Servers are provided - Exception: If there's an error in tool handling - """ - try: - # if self.mcp_url is not None: - self._single_mcp_tool_handling(response) - # elif self.mcp_url is None and len(self.mcp_servers) > 1: - # self._multiple_mcp_tool_handling(response) - # else: - # raise ValueError("No MCP URL or MCP Servers provided") - except Exception as e: - logger.error(f"Error in mcp_tool_handling: {e}") + return tools + except AgentMCPConnectionError as e: + logger.error(f"Error in MCP connection: {e}") raise e def setup_config(self): @@ -1091,60 +1005,67 @@ class Agent: *response_args, **kwargs ) - # Convert to a str if the response is not a str + if exists(self.tools_list_dictionary): + if isinstance(response, BaseModel): + response = response.model_dump() + + # # Convert to a str if the response is not a str + # if self.mcp_url is None or self.tools is None: response = self.parse_llm_output(response) self.short_memory.add( - role=self.agent_name, content=response + role=self.agent_name, + content=format_dict_to_string(response), ) # Print self.pretty_print(response, loop_count) - # Output Cleaner - self.output_cleaner_op(response) - - ####### MCP TOOL HANDLING ####### - if ( - self.mcp_servers - and self.tools_list_dictionary is not None - ): - self.mcp_tool_handling(response) - - ####### MCP TOOL HANDLING ####### + # # Output Cleaner + # self.output_cleaner_op(response) # Check and execute tools - if self.tools is not None: - out = self.parse_and_execute_tools( - response + if exists(self.tools): + # out = self.parse_and_execute_tools( + # response + # ) + + # self.short_memory.add( + # role="Tool Executor", content=out + # ) + + # if self.no_print is False: + # agent_print( + # f"{self.agent_name} - Tool Executor", + # out, + # loop_count, + # self.streaming_on, + # ) + + # out = self.call_llm(task=out) + + # self.short_memory.add( + # role=self.agent_name, content=out + # ) + + # if self.no_print is False: + # agent_print( + # f"{self.agent_name} - Agent Analysis", + # out, + # loop_count, + # self.streaming_on, + # ) + + self.execute_tools( + response=response, + loop_count=loop_count, ) - self.short_memory.add( - role="Tool Executor", content=out + if exists(self.mcp_url): + self.mcp_tool_handling( + response, loop_count ) - if self.no_print is False: - agent_print( - f"{self.agent_name} - Tool Executor", - out, - loop_count, - self.streaming_on, - ) - - out = self.call_llm(task=out) - - self.short_memory.add( - role=self.agent_name, content=out - ) - - if self.no_print is False: - agent_print( - f"{self.agent_name} - Agent Analysis", - out, - loop_count, - self.streaming_on, - ) - self.sentiment_and_evaluator(response) success = True # Mark as successful to exit the retry loop @@ -1362,36 +1283,36 @@ class Agent: return output.getvalue() - def parse_and_execute_tools(self, response: str, *args, **kwargs): - max_retries = 3 # Maximum number of retries - retries = 0 - while retries < max_retries: - try: - logger.info("Executing tool...") - - # try to Execute the tool and return a string - out = parse_and_execute_json( - functions=self.tools, - json_string=response, - parse_md=True, - *args, - **kwargs, - ) - logger.info(f"Tool Output: {out}") - # Add the output to the memory - # self.short_memory.add( - # role="Tool Executor", - # content=out, - # ) - return out - except Exception as error: - retries += 1 - logger.error( - f"Attempt {retries}: Error executing tool: {error}" - ) - if retries == max_retries: - raise error - time.sleep(1) # Wait for a bit before retrying + # def parse_and_execute_tools(self, response: str, *args, **kwargs): + # max_retries = 3 # Maximum number of retries + # retries = 0 + # while retries < max_retries: + # try: + # logger.info("Executing tool...") + + # # try to Execute the tool and return a string + # out = parse_and_execute_json( + # functions=self.tools, + # json_string=response, + # parse_md=True, + # *args, + # **kwargs, + # ) + # logger.info(f"Tool Output: {out}") + # # Add the output to the memory + # # self.short_memory.add( + # # role="Tool Executor", + # # content=out, + # # ) + # return out + # except Exception as error: + # retries += 1 + # logger.error( + # f"Attempt {retries}: Error executing tool: {error}" + # ) + # if retries == max_retries: + # raise error + # time.sleep(1) # Wait for a bit before retrying def add_memory(self, message: str): """Add a memory to the agent @@ -1720,9 +1641,6 @@ class Agent: # Reinitialize any necessary runtime components self._reinitialize_after_load() - # Restore non-serializable properties (tokenizer, long_term_memory, logger_handler, agent_output, executor) - self.restore_non_serializable_properties() - if self.verbose: self._log_loaded_state_info(resolved_path) @@ -2709,7 +2627,7 @@ class Agent: f"Agent Name {self.agent_name} [Max Loops: {loop_count} ]", ) - def parse_llm_output(self, response: Any) -> str: + def parse_llm_output(self, response: Any): """Parse and standardize the output from the LLM. Args: @@ -2722,7 +2640,7 @@ class Agent: ValueError: If the response format is unexpected and can't be handled """ try: - # Handle dictionary responses + if isinstance(response, dict): if "choices" in response: return response["choices"][0]["message"][ @@ -2732,17 +2650,23 @@ class Agent: response ) # Convert other dicts to string - # Handle string responses - elif isinstance(response, str): - return response + elif isinstance(response, BaseModel): + out = response.model_dump() - # Handle list responses (from check_llm_outputs) - elif isinstance(response, list): - return "\n".join(response) + # Handle List[BaseModel] responses + elif ( + isinstance(response, list) + and response + and isinstance(response[0], BaseModel) + ): + return [item.model_dump() for item in response] - # Handle any other type by converting to string + elif isinstance(response, list): + out = format_data_structure(response) else: - return str(response) + out = str(response) + + return out except Exception as e: logger.error(f"Error parsing LLM output: {e}") @@ -2780,24 +2704,123 @@ class Agent: content=response, ) - def restore_non_serializable_properties(self): - """ - Restore non-serializable properties for the Agent instance after loading. - This should be called after loading agent state from disk. - """ - restore_non_serializable_properties(self) + def mcp_tool_handling( + self, response: any, current_loop: Optional[int] = 0 + ): + try: + + if exists(self.mcp_url): + # Execute the tool call + tool_response = asyncio.run( + execute_tool_call_simple( + response=response, + server_path=self.mcp_url, + ) + ) + elif exists(self.mcp_config): + # Execute the tool call + tool_response = asyncio.run( + execute_tool_call_simple( + response=response, + connection=self.mcp_config, + ) + ) + else: + raise AgentMCPConnectionError( + "mcp_url must be either a string URL or MCPConnection object" + ) - # Custom serialization for non-serializable properties - def __getstate__(self): - state = self.__dict__.copy() - # Remove non-serializable properties - for prop in ["tokenizer", "long_term_memory", "logger_handler", "agent_output", "executor"]: - if prop in state: - state[prop] = None # Or a serializable placeholder if needed - return state + # Get the text content from the tool response + text_content = ( + tool_response.content[0].text + if tool_response.content + else str(tool_response) + ) + + # Add to the memory + self.short_memory.add( + role="Tool Executor", + content=text_content, + ) + + # Create a temporary LLM instance without tools for the follow-up call + try: + temp_llm = LiteLLM( + model_name=self.model_name, + temperature=self.temperature, + max_tokens=self.max_tokens, + system_prompt=self.system_prompt, + stream=self.streaming_on, + ) + + summary = temp_llm.run( + task=self.short_memory.get_str() + ) + except Exception as e: + logger.error( + f"Error calling LLM after MCP tool execution: {e}" + ) + # Fallback: provide a default summary + summary = "I successfully executed the MCP tool and retrieved the information above." + + self.pretty_print(summary, loop_count=current_loop) + + # Add to the memory + self.short_memory.add( + role=self.agent_name, content=summary + ) + except AgentMCPToolError as e: + logger.error(f"Error in MCP tool: {e}") + raise e + + def execute_tools(self, response: any, loop_count: int): + + output = ( + self.tool_struct.execute_function_calls_from_api_response( + response + ) + ) + + self.short_memory.add( + role="Tool Executor", + content=format_data_structure(output), + ) + + self.pretty_print( + f"{format_data_structure(output)}", + loop_count, + ) + + # Now run the LLM again without tools - create a temporary LLM instance + # instead of modifying the cached one + # Create a temporary LLM instance without tools for the follow-up call + temp_llm = LiteLLM( + model_name=self.model_name, + temperature=self.temperature, + max_tokens=self.max_tokens, + system_prompt=self.system_prompt, + stream=self.streaming_on, + tools_list_dictionary=None, + parallel_tool_calls=False, + ) + + tool_response = temp_llm.run( + f""" + Please analyze and summarize the following tool execution output in a clear and concise way. + Focus on the key information and insights that would be most relevant to the user's original request. + If there are any errors or issues, highlight them prominently. + + Tool Output: + {output} + """ + ) + + self.short_memory.add( + role=self.agent_name, + content=tool_response, + ) - def __setstate__(self, state): - self.__dict__.update(state) - # Restore non-serializable properties after loading - if hasattr(self, 'restore_non_serializable_properties'): - self.restore_non_serializable_properties() + self.pretty_print( + f"{tool_response}", + loop_count, + ) \ No newline at end of file From 22735cd5bba34a67f5994e1ad4f9a38f44d514c9 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <66913595+ascender1729@users.noreply.github.com> Date: Wed, 4 Jun 2025 21:06:48 +0530 Subject: [PATCH 8/8] Remove agent_save_load_full example and non-serializable helper --- examples/agent_save_load_full.py | 121 ----------------------- swarms/structs/agent_non_serializable.py | 62 ------------ 2 files changed, 183 deletions(-) delete mode 100644 examples/agent_save_load_full.py delete mode 100644 swarms/structs/agent_non_serializable.py diff --git a/examples/agent_save_load_full.py b/examples/agent_save_load_full.py deleted file mode 100644 index 3e9bef83..00000000 --- a/examples/agent_save_load_full.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Example: Fully Save and Load an Agent (with Conversation History) - -This demonstrates how to: - 1. Auto-save conversation messages to JSON - 2. Save the full Agent state - 3. Load both the Agent state and the conversation back into a fresh Agent -""" - -import os -from swarms.structs.agent import Agent - -# Helper to safely print type or None for agent properties -def print_agent_properties(agent, label): - print(f"\n--- {label} ---") - for prop in ["tokenizer", "long_term_memory", "logger_handler", "agent_output", "executor"]: - value = getattr(agent, prop, None) - print(f"{prop}: {type(value)}") - -# Helper to extract the conversation history list -def get_conversation_history(agent): - conv = getattr(agent, "conversation", None) or getattr(agent, "short_memory", None) - return getattr(conv, "conversation_history", None) - -# Robust helper to reload conversation from JSON into the correct attribute -def reload_conversation_from_json(agent, filepath): - conv = getattr(agent, "conversation", None) or getattr(agent, "short_memory", None) - if conv and hasattr(conv, "load_from_json"): - conv.load_from_json(filepath) - -# --- 1. Setup: Create and configure an agent with auto-save conversation --- -agent = Agent( - agent_name="test", - user_name="test_user", - system_prompt="This is a test agent", - max_loops=1, - context_length=200000, - autosave=True, - verbose=True, - artifacts_on=True, - artifacts_output_path="test", - artifacts_file_extension=".txt", - conversation_kwargs={ - "auto_save": True, - "save_as_json_bool": True, - "save_filepath": "test_conversation_history.json" - } -) - -# --- 2. Interact to populate conversation --- -agent.run(task="hello") -agent.run(task="What is your purpose?") -agent.run(task="Tell me a joke.") -agent.run(task="Summarize our conversation so far.") - -# --- 3. Inspect before saving --- -print_agent_properties(agent, "BEFORE SAVE") -print("\nConversation history BEFORE SAVE:", get_conversation_history(agent)) - -# --- 4. Save the agent state (conversation JSON was auto-saved under workspace) --- -state_path = os.path.join(agent.workspace_dir, "test_state.json") -agent.save(state_path) - -# --- 5. Ensure the conversation JSON file is saved and print its path and contents --- -json_path = os.path.join(agent.workspace_dir, "test_conversation_history.json") -if hasattr(agent, "short_memory") and hasattr(agent.short_memory, "save_as_json"): - agent.short_memory.save_as_json(json_path) - -if os.path.exists(json_path): - print(f"\n[CHECK] Conversation JSON file found: {json_path}") - with open(json_path, "r") as f: - json_data = f.read() - print("[CHECK] JSON file contents:\n", json_data) -else: - print(f"[WARN] Conversation JSON file not found: {json_path}") - -# --- 6. Simulate fresh environment --- -del agent - -# --- 7. Load: Restore the agent configuration --- -agent2 = Agent(agent_name="test") -agent2.load(state_path) - -# --- 8. Load: Restore the conversation history from the workspace directory into a new Conversation object --- -from swarms.structs.conversation import Conversation -conversation_loaded = Conversation() -if os.path.exists(json_path): - conversation_loaded.load_from_json(json_path) - print("\n[CHECK] Loaded conversation from JSON into new Conversation object:") - print(conversation_loaded.conversation_history) -else: - print(f"[WARN] Conversation JSON file not found for loading: {json_path}") - -# --- 9. Assign loaded conversation to agent2 and check --- -agent2.short_memory = conversation_loaded -print("\n[CHECK] Agent2 conversation history after assigning loaded conversation:", get_conversation_history(agent2)) - -# --- 10. Inspect after loading --- -print_agent_properties(agent2, "AFTER LOAD") -print("\nConversation history AFTER LOAD:", get_conversation_history(agent2)) - -# --- 11. Confirm the agent can continue --- -result = agent2.run(task="What is 2+2?") -print("\nAgent2 run result:", result) - -# --- 12. Cleanup test files --- -print(f"\n[INFO] Test complete. Conversation JSON and agent state files are available for inspection:") -print(f" Conversation JSON: {json_path}") -print(f" Agent state: {state_path}") -print("You can open and inspect these files to verify the agent's memory persistence.") -# Do NOT delete files automatically -# for path in (state_path, json_path): -# try: -# os.remove(path) -# except OSError: -# pass - -# --- 13. Test if agent2 remembers the previous conversation --- -print("\n[TEST] Checking if agent2 remembers the previous conversation after reload...") -probe = agent2.run(task="What did I ask you to do earlier?") -print("\nAgent2 memory probe result:", probe) diff --git a/swarms/structs/agent_non_serializable.py b/swarms/structs/agent_non_serializable.py deleted file mode 100644 index 1342899f..00000000 --- a/swarms/structs/agent_non_serializable.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Non-Serializable Properties Handler for Agent - -This module provides helper functions to save and restore non-serializable properties -(tokenizer, long_term_memory, logger_handler, agent_output, executor) for the Agent class. - -Usage: - from swarms.structs.agent_non_serializable import restore_non_serializable_properties - restore_non_serializable_properties(agent) -""" - -from concurrent.futures import ThreadPoolExecutor -import logging - -# Dummy/placeholder for long_term_memory and agent_output restoration -class DummyLongTermMemory: - def __init__(self): - self.memory = [] - def query(self, *args, **kwargs): - # Return an empty list or a default value to avoid errors - return [] - def save(self, path): - # Optionally implement a no-op save for compatibility - pass - -class DummyAgentOutput: - def __init__(self): - self.output = None - -def restore_non_serializable_properties(agent): - """ - Restore non-serializable properties for the Agent instance after loading. - This should be called after loading agent state from disk. - """ - # Restore tokenizer using LiteLLM if available - agent.tokenizer = None - try: - from swarms.utils.litellm_tokenizer import count_tokens - agent.tokenizer = count_tokens # Assign the function as a tokenizer interface - except Exception: - agent.tokenizer = None - - # Restore long_term_memory (dummy for demo, replace with real backend as needed) - if getattr(agent, "long_term_memory", None) is None or not hasattr(agent.long_term_memory, "query"): - agent.long_term_memory = DummyLongTermMemory() - - # Restore logger_handler - try: - agent.logger_handler = logging.StreamHandler() - except Exception: - agent.logger_handler = None - - # Restore agent_output (dummy for demo, replace with real backend as needed) - agent.agent_output = DummyAgentOutput() - - # Restore executor - try: - agent.executor = ThreadPoolExecutor() - except Exception: - agent.executor = None - - return agent