swarms 7.7.8__py3-none-any.whl → 7.8.0__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.
Files changed (51) hide show
  1. swarms/__init__.py +0 -1
  2. swarms/agents/cort_agent.py +206 -0
  3. swarms/agents/react_agent.py +173 -0
  4. swarms/agents/self_agent_builder.py +40 -0
  5. swarms/communication/base_communication.py +290 -0
  6. swarms/communication/duckdb_wrap.py +369 -72
  7. swarms/communication/pulsar_struct.py +691 -0
  8. swarms/communication/redis_wrap.py +1362 -0
  9. swarms/communication/sqlite_wrap.py +547 -44
  10. swarms/prompts/agent_self_builder_prompt.py +103 -0
  11. swarms/prompts/safety_prompt.py +50 -0
  12. swarms/schemas/__init__.py +6 -1
  13. swarms/schemas/agent_class_schema.py +91 -0
  14. swarms/schemas/agent_mcp_errors.py +18 -0
  15. swarms/schemas/agent_tool_schema.py +13 -0
  16. swarms/schemas/llm_agent_schema.py +92 -0
  17. swarms/schemas/mcp_schemas.py +43 -0
  18. swarms/structs/__init__.py +4 -0
  19. swarms/structs/agent.py +315 -267
  20. swarms/structs/aop.py +3 -1
  21. swarms/structs/batch_agent_execution.py +64 -0
  22. swarms/structs/conversation.py +261 -57
  23. swarms/structs/council_judge.py +542 -0
  24. swarms/structs/deep_research_swarm.py +19 -22
  25. swarms/structs/long_agent.py +424 -0
  26. swarms/structs/ma_utils.py +11 -8
  27. swarms/structs/malt.py +30 -28
  28. swarms/structs/multi_model_gpu_manager.py +1 -1
  29. swarms/structs/output_types.py +1 -1
  30. swarms/structs/swarm_router.py +70 -15
  31. swarms/tools/__init__.py +12 -0
  32. swarms/tools/base_tool.py +2840 -264
  33. swarms/tools/create_agent_tool.py +104 -0
  34. swarms/tools/mcp_client_call.py +504 -0
  35. swarms/tools/py_func_to_openai_func_str.py +45 -7
  36. swarms/tools/pydantic_to_json.py +10 -27
  37. swarms/utils/audio_processing.py +343 -0
  38. swarms/utils/history_output_formatter.py +5 -5
  39. swarms/utils/index.py +226 -0
  40. swarms/utils/litellm_wrapper.py +65 -67
  41. swarms/utils/try_except_wrapper.py +2 -2
  42. swarms/utils/xml_utils.py +42 -0
  43. {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/METADATA +5 -4
  44. {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/RECORD +47 -30
  45. {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/WHEEL +1 -1
  46. swarms/client/__init__.py +0 -15
  47. swarms/client/main.py +0 -407
  48. swarms/tools/mcp_client.py +0 -246
  49. swarms/tools/mcp_integration.py +0 -340
  50. {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/LICENSE +0 -0
  51. {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/entry_points.txt +0 -0
swarms/structs/agent.py CHANGED
@@ -23,7 +23,6 @@ import yaml
23
23
  from loguru import logger
24
24
  from pydantic import BaseModel
25
25
 
26
- from swarms.agents.agent_print import agent_print
27
26
  from swarms.agents.ape_agent import auto_generate_prompt
28
27
  from swarms.artifacts.main_artifact import Artifact
29
28
  from swarms.prompts.agent_system_prompts import AGENT_SYSTEM_PROMPT_3
@@ -31,6 +30,10 @@ from swarms.prompts.multi_modal_autonomous_instruction_prompt import (
31
30
  MULTI_MODAL_AUTO_AGENT_SYSTEM_PROMPT_1,
32
31
  )
33
32
  from swarms.prompts.tools import tool_sop_prompt
33
+ from swarms.schemas.agent_mcp_errors import (
34
+ AgentMCPConnectionError,
35
+ AgentMCPToolError,
36
+ )
34
37
  from swarms.schemas.agent_step_schemas import ManySteps, Step
35
38
  from swarms.schemas.base_schemas import (
36
39
  AgentChatCompletionResponse,
@@ -46,14 +49,9 @@ from swarms.structs.safe_loading import (
46
49
  )
47
50
  from swarms.telemetry.main import log_agent_data
48
51
  from swarms.tools.base_tool import BaseTool
49
- from swarms.tools.mcp_client import (
50
- execute_mcp_tool,
51
- find_and_execute_tool,
52
- list_all,
53
- list_tools_for_multiple_urls,
52
+ from swarms.tools.py_func_to_openai_func_str import (
53
+ convert_multiple_functions_to_openai_function_schema,
54
54
  )
55
- from swarms.tools.mcp_integration import MCPServerSseParams
56
- from swarms.tools.tool_parse_exec import parse_and_execute_json
57
55
  from swarms.utils.any_to_str import any_to_str
58
56
  from swarms.utils.data_to_text import data_to_text
59
57
  from swarms.utils.file_processing import create_file_in_folder
@@ -64,9 +62,22 @@ from swarms.utils.history_output_formatter import (
64
62
  from swarms.utils.litellm_tokenizer import count_tokens
65
63
  from swarms.utils.litellm_wrapper import LiteLLM
66
64
  from swarms.utils.pdf_to_text import pdf_to_text
67
- from swarms.utils.str_to_dict import str_to_dict
68
65
  from swarms.prompts.react_base_prompt import REACT_SYS_PROMPT
69
66
  from swarms.prompts.max_loop_prompt import generate_reasoning_prompt
67
+ from swarms.prompts.safety_prompt import SAFETY_PROMPT
68
+ from swarms.structs.ma_utils import set_random_models_for_agents
69
+ from swarms.tools.mcp_client_call import (
70
+ execute_tool_call_simple,
71
+ get_mcp_tools_sync,
72
+ )
73
+ from swarms.schemas.mcp_schemas import (
74
+ MCPConnection,
75
+ )
76
+ from swarms.utils.index import (
77
+ exists,
78
+ format_data_structure,
79
+ format_dict_to_string,
80
+ )
70
81
 
71
82
 
72
83
  # Utils
@@ -88,10 +99,6 @@ def agent_id():
88
99
  return uuid.uuid4().hex
89
100
 
90
101
 
91
- def exists(val):
92
- return val is not None
93
-
94
-
95
102
  # Agent output types
96
103
  ToolUsageType = Union[BaseModel, Dict[str, Any]]
97
104
 
@@ -201,7 +208,7 @@ class Agent:
201
208
  limit_tokens_from_string (Callable): The function to limit tokens from a string
202
209
  custom_tools_prompt (Callable): The custom tools prompt
203
210
  tool_schema (ToolUsageType): The tool schema
204
- output_type (agent_output_type): The output type
211
+ output_type (agent_output_type): The output type. Supported: 'str', 'string', 'list', 'json', 'dict', 'yaml', 'xml'.
205
212
  function_calling_type (str): The function calling type
206
213
  output_cleaner (Callable): The output cleaner function
207
214
  function_calling_format_type (str): The function calling format type
@@ -279,7 +286,6 @@ class Agent:
279
286
 
280
287
  def __init__(
281
288
  self,
282
- agent_id: Optional[str] = agent_id(),
283
289
  id: Optional[str] = agent_id(),
284
290
  llm: Optional[Any] = None,
285
291
  template: Optional[str] = None,
@@ -358,9 +364,9 @@ class Agent:
358
364
  log_directory: str = None,
359
365
  tool_system_prompt: str = tool_sop_prompt(),
360
366
  max_tokens: int = 4096,
361
- frequency_penalty: float = 0.0,
362
- presence_penalty: float = 0.0,
363
- temperature: float = 0.1,
367
+ frequency_penalty: float = 0.8,
368
+ presence_penalty: float = 0.6,
369
+ temperature: float = 0.5,
364
370
  workspace_dir: str = "agent_workspace",
365
371
  timeout: Optional[int] = None,
366
372
  # short_memory: Optional[str] = None,
@@ -374,7 +380,6 @@ class Agent:
374
380
  "%Y-%m-%d %H:%M:%S", time.localtime()
375
381
  ),
376
382
  agent_output: ManySteps = None,
377
- executor_workers: int = os.cpu_count(),
378
383
  data_memory: Optional[Callable] = None,
379
384
  load_yaml_path: str = None,
380
385
  auto_generate_prompt: bool = False,
@@ -395,15 +400,17 @@ class Agent:
395
400
  role: agent_roles = "worker",
396
401
  no_print: bool = False,
397
402
  tools_list_dictionary: Optional[List[Dict[str, Any]]] = None,
398
- mcp_servers: MCPServerSseParams = None,
399
- mcp_url: str = None,
403
+ mcp_url: Optional[Union[str, MCPConnection]] = None,
400
404
  mcp_urls: List[str] = None,
401
405
  react_on: bool = False,
406
+ safety_prompt_on: bool = False,
407
+ random_models_on: bool = False,
408
+ mcp_config: Optional[MCPConnection] = None,
409
+ top_p: float = 0.90,
402
410
  *args,
403
411
  **kwargs,
404
412
  ):
405
413
  # super().__init__(*args, **kwargs)
406
- self.agent_id = agent_id
407
414
  self.id = id
408
415
  self.llm = llm
409
416
  self.template = template
@@ -416,6 +423,7 @@ class Agent:
416
423
  self.stopping_token = stopping_token
417
424
  self.interactive = interactive
418
425
  self.dashboard = dashboard
426
+ self.saved_state_path = saved_state_path
419
427
  self.return_history = return_history
420
428
  self.dynamic_temperature_enabled = dynamic_temperature_enabled
421
429
  self.dynamic_loops = dynamic_loops
@@ -518,10 +526,13 @@ class Agent:
518
526
  self.role = role
519
527
  self.no_print = no_print
520
528
  self.tools_list_dictionary = tools_list_dictionary
521
- self.mcp_servers = mcp_servers
522
529
  self.mcp_url = mcp_url
523
530
  self.mcp_urls = mcp_urls
524
531
  self.react_on = react_on
532
+ self.safety_prompt_on = safety_prompt_on
533
+ self.random_models_on = random_models_on
534
+ self.mcp_config = mcp_config
535
+ self.top_p = top_p
525
536
 
526
537
  self._cached_llm = (
527
538
  None # Add this line to cache the LLM instance
@@ -533,41 +544,58 @@ class Agent:
533
544
  self.feedback = []
534
545
 
535
546
  # self.init_handling()
536
- # Define tasks as pairs of (function, condition)
537
- # Each task will only run if its condition is True
538
547
  self.setup_config()
539
548
 
540
549
  if exists(self.docs_folder):
541
550
  self.get_docs_from_doc_folders()
542
551
 
543
- if exists(self.tools):
544
- self.handle_tool_init()
545
-
546
552
  if exists(self.tool_schema) or exists(self.list_base_models):
547
553
  self.handle_tool_schema_ops()
548
554
 
549
555
  if exists(self.sop) or exists(self.sop_list):
550
556
  self.handle_sop_ops()
551
557
 
558
+ if self.max_loops >= 2:
559
+ self.system_prompt += generate_reasoning_prompt(
560
+ self.max_loops
561
+ )
562
+
563
+ if self.react_on is True:
564
+ self.system_prompt += REACT_SYS_PROMPT
565
+
566
+ self.short_memory = self.short_memory_init()
567
+
552
568
  # Run sequential operations after all concurrent tasks are done
553
569
  # self.agent_output = self.agent_output_model()
554
570
  log_agent_data(self.to_dict())
555
571
 
572
+ if exists(self.tools):
573
+ self.tool_handling()
574
+
556
575
  if self.llm is None:
557
576
  self.llm = self.llm_handling()
558
577
 
559
- if self.mcp_url or self.mcp_servers is not None:
560
- self.add_mcp_tools_to_memory()
578
+ if self.random_models_on is True:
579
+ self.model_name = set_random_models_for_agents()
561
580
 
562
- if self.react_on is True:
563
- self.system_prompt += REACT_SYS_PROMPT
581
+ def tool_handling(self):
564
582
 
565
- if self.max_loops >= 2:
566
- self.system_prompt += generate_reasoning_prompt(
567
- self.max_loops
583
+ self.tool_struct = BaseTool(
584
+ tools=self.tools,
585
+ verbose=self.verbose,
586
+ )
587
+
588
+ # Convert all the tools into a list of dictionaries
589
+ self.tools_list_dictionary = (
590
+ convert_multiple_functions_to_openai_function_schema(
591
+ self.tools
568
592
  )
593
+ )
569
594
 
570
- self.short_memory = self.short_memory_init()
595
+ self.short_memory.add(
596
+ role=f"{self.agent_name}",
597
+ content=f"Tools available: {format_data_structure(self.tools_list_dictionary)}",
598
+ )
571
599
 
572
600
  def short_memory_init(self):
573
601
  if (
@@ -578,8 +606,11 @@ class Agent:
578
606
  else:
579
607
  prompt = self.system_prompt
580
608
 
609
+ if self.safety_prompt_on is True:
610
+ prompt += SAFETY_PROMPT
611
+
581
612
  # Initialize the short term memory
582
- self.short_memory = Conversation(
613
+ memory = Conversation(
583
614
  system_prompt=prompt,
584
615
  time_enabled=False,
585
616
  user=self.user_name,
@@ -587,7 +618,7 @@ class Agent:
587
618
  token_count=False,
588
619
  )
589
620
 
590
- return self.short_memory
621
+ return memory
591
622
 
592
623
  def agent_output_model(self):
593
624
  # Many steps
@@ -617,6 +648,11 @@ class Agent:
617
648
  if self.model_name is None:
618
649
  self.model_name = "gpt-4o-mini"
619
650
 
651
+ if exists(self.tools) and len(self.tools) >= 2:
652
+ parallel_tool_calls = True
653
+ else:
654
+ parallel_tool_calls = False
655
+
620
656
  try:
621
657
  # Simplify initialization logic
622
658
  common_args = {
@@ -635,10 +671,16 @@ class Agent:
635
671
  **common_args,
636
672
  tools_list_dictionary=self.tools_list_dictionary,
637
673
  tool_choice="auto",
638
- parallel_tool_calls=len(
639
- self.tools_list_dictionary
640
- )
641
- > 1,
674
+ parallel_tool_calls=parallel_tool_calls,
675
+ )
676
+
677
+ elif self.mcp_url is not None:
678
+ self._cached_llm = LiteLLM(
679
+ **common_args,
680
+ tools_list_dictionary=self.add_mcp_tools_to_memory(),
681
+ tool_choice="auto",
682
+ parallel_tool_calls=parallel_tool_calls,
683
+ mcp_call=True,
642
684
  )
643
685
  else:
644
686
  self._cached_llm = LiteLLM(
@@ -652,48 +694,6 @@ class Agent:
652
694
  )
653
695
  return None
654
696
 
655
- def handle_tool_init(self):
656
- # Initialize the tool struct
657
- if (
658
- exists(self.tools)
659
- or exists(self.list_base_models)
660
- or exists(self.tool_schema)
661
- ):
662
-
663
- self.tool_struct = BaseTool(
664
- tools=self.tools,
665
- base_models=self.list_base_models,
666
- tool_system_prompt=self.tool_system_prompt,
667
- )
668
-
669
- if self.tools is not None:
670
- logger.info(
671
- "Tools provided make sure the functions have documentation ++ type hints, otherwise tool execution won't be reliable."
672
- )
673
- # Add the tool prompt to the memory
674
- self.short_memory.add(
675
- role="system", content=self.tool_system_prompt
676
- )
677
-
678
- # Log the tools
679
- logger.info(
680
- f"Tools provided: Accessing {len(self.tools)} tools"
681
- )
682
-
683
- # Transform the tools into an openai schema
684
- # self.convert_tool_into_openai_schema()
685
-
686
- # Transform the tools into an openai schema
687
- tool_dict = (
688
- self.tool_struct.convert_tool_into_openai_schema()
689
- )
690
- self.short_memory.add(role="system", content=tool_dict)
691
-
692
- # Now create a function calling map for every tools
693
- self.function_map = {
694
- tool.__name__: tool for tool in self.tools
695
- }
696
-
697
697
  def add_mcp_tools_to_memory(self):
698
698
  """
699
699
  Adds MCP tools to the agent's short-term memory.
@@ -705,110 +705,23 @@ class Agent:
705
705
  Exception: If there's an error accessing the MCP tools
706
706
  """
707
707
  try:
708
- if self.mcp_url is not None:
709
- tools_available = list_all(
710
- self.mcp_url, output_type="json"
711
- )
712
- self.short_memory.add(
713
- role="Tools Available",
714
- content=f"\n{tools_available}",
715
- )
716
-
717
- elif (
718
- self.mcp_url is None
719
- and self.mcp_urls is not None
720
- and len(self.mcp_urls) > 1
721
- ):
722
- tools_available = list_tools_for_multiple_urls(
723
- urls=self.mcp_urls,
724
- output_type="json",
725
- )
726
-
727
- self.short_memory.add(
728
- role="Tools Available",
729
- content=f"\n{tools_available}",
730
- )
731
- except Exception as e:
732
- logger.error(f"Error adding MCP tools to memory: {e}")
733
- raise e
734
-
735
- def _single_mcp_tool_handling(self, response: any):
736
- """
737
- Handles execution of a single MCP tool.
738
-
739
- Args:
740
- response (str): The tool response to process
741
-
742
- Raises:
743
- Exception: If there's an error executing the tool
744
- """
745
- try:
746
- if isinstance(response, dict):
747
- result = response
748
- print(type(result))
708
+ if exists(self.mcp_url):
709
+ tools = get_mcp_tools_sync(server_path=self.mcp_url)
710
+ elif exists(self.mcp_config):
711
+ tools = get_mcp_tools_sync(connection=self.mcp_config)
712
+ logger.info(f"Tools: {tools}")
749
713
  else:
750
- result = str_to_dict(response)
751
- print(type(result))
752
-
753
- output = execute_mcp_tool(
754
- url=self.mcp_url,
755
- parameters=result,
756
- )
757
-
758
- self.short_memory.add(
759
- role="Tool Executor", content=str(output)
760
- )
761
- except Exception as e:
762
- logger.error(f"Error in single MCP tool handling: {e}")
763
- raise e
764
-
765
- def _multiple_mcp_tool_handling(self, response: any):
766
- """
767
- Handles execution of multiple MCP tools.
768
-
769
- Args:
770
- response (any): The tool response to process
771
-
772
- Raises:
773
- Exception: If there's an error executing the tools
774
- """
775
- try:
776
- if isinstance(response, str):
777
- response = str_to_dict(response)
778
-
779
- execution = find_and_execute_tool(
780
- self.mcp_urls,
781
- response["name"],
782
- parameters=response,
783
- )
784
-
785
- self.short_memory.add(
786
- role="Tool Executor", content=str(execution)
714
+ raise AgentMCPConnectionError(
715
+ "mcp_url must be either a string URL or MCPConnection object"
716
+ )
717
+ self.pretty_print(
718
+ f"✨ [SYSTEM] Successfully integrated {len(tools)} MCP tools into agent: {self.agent_name} | Status: ONLINE | Time: {time.strftime('%H:%M:%S')} ✨",
719
+ loop_count=0,
787
720
  )
788
- except Exception as e:
789
- logger.error(f"Error in multiple MCP tool handling: {e}")
790
- raise e
791
-
792
- def mcp_tool_handling(self, response: any):
793
- """
794
- Main handler for MCP tool execution.
795
721
 
796
- Args:
797
- response (any): The tool response to process
798
-
799
- Raises:
800
- ValueError: If no MCP URL or MCP Servers are provided
801
- Exception: If there's an error in tool handling
802
- """
803
- try:
804
- # if self.mcp_url is not None:
805
- self._single_mcp_tool_handling(response)
806
- # elif self.mcp_url is None and len(self.mcp_servers) > 1:
807
- # self._multiple_mcp_tool_handling(response)
808
- # else:
809
- # raise ValueError("No MCP URL or MCP Servers provided")
810
- except Exception as e:
811
- logger.error(f"Error in mcp_tool_handling: {e}")
722
+ return tools
723
+ except AgentMCPConnectionError as e:
724
+ logger.error(f"Error in MCP connection: {e}")
812
725
  raise e
813
726
 
814
727
  def setup_config(self):
@@ -1000,7 +913,7 @@ class Agent:
1000
913
 
1001
914
  Returns:
1002
915
  Any: The output of the agent.
1003
- (string, list, json, dict, yaml)
916
+ (string, list, json, dict, yaml, xml)
1004
917
 
1005
918
  Examples:
1006
919
  agent(task="What is the capital of France?")
@@ -1092,60 +1005,67 @@ class Agent:
1092
1005
  *response_args, **kwargs
1093
1006
  )
1094
1007
 
1095
- # Convert to a str if the response is not a str
1008
+ if exists(self.tools_list_dictionary):
1009
+ if isinstance(response, BaseModel):
1010
+ response = response.model_dump()
1011
+
1012
+ # # Convert to a str if the response is not a str
1013
+ # if self.mcp_url is None or self.tools is None:
1096
1014
  response = self.parse_llm_output(response)
1097
1015
 
1098
1016
  self.short_memory.add(
1099
- role=self.agent_name, content=response
1017
+ role=self.agent_name,
1018
+ content=format_dict_to_string(response),
1100
1019
  )
1101
1020
 
1102
1021
  # Print
1103
1022
  self.pretty_print(response, loop_count)
1104
1023
 
1105
- # Output Cleaner
1106
- self.output_cleaner_op(response)
1107
-
1108
- ####### MCP TOOL HANDLING #######
1109
- if (
1110
- self.mcp_servers
1111
- and self.tools_list_dictionary is not None
1112
- ):
1113
- self.mcp_tool_handling(response)
1114
-
1115
- ####### MCP TOOL HANDLING #######
1024
+ # # Output Cleaner
1025
+ # self.output_cleaner_op(response)
1116
1026
 
1117
1027
  # Check and execute tools
1118
- if self.tools is not None:
1119
- out = self.parse_and_execute_tools(
1120
- response
1028
+ if exists(self.tools):
1029
+ # out = self.parse_and_execute_tools(
1030
+ # response
1031
+ # )
1032
+
1033
+ # self.short_memory.add(
1034
+ # role="Tool Executor", content=out
1035
+ # )
1036
+
1037
+ # if self.no_print is False:
1038
+ # agent_print(
1039
+ # f"{self.agent_name} - Tool Executor",
1040
+ # out,
1041
+ # loop_count,
1042
+ # self.streaming_on,
1043
+ # )
1044
+
1045
+ # out = self.call_llm(task=out)
1046
+
1047
+ # self.short_memory.add(
1048
+ # role=self.agent_name, content=out
1049
+ # )
1050
+
1051
+ # if self.no_print is False:
1052
+ # agent_print(
1053
+ # f"{self.agent_name} - Agent Analysis",
1054
+ # out,
1055
+ # loop_count,
1056
+ # self.streaming_on,
1057
+ # )
1058
+
1059
+ self.execute_tools(
1060
+ response=response,
1061
+ loop_count=loop_count,
1121
1062
  )
1122
1063
 
1123
- self.short_memory.add(
1124
- role="Tool Executor", content=out
1064
+ if exists(self.mcp_url):
1065
+ self.mcp_tool_handling(
1066
+ response, loop_count
1125
1067
  )
1126
1068
 
1127
- if self.no_print is False:
1128
- agent_print(
1129
- f"{self.agent_name} - Tool Executor",
1130
- out,
1131
- loop_count,
1132
- self.streaming_on,
1133
- )
1134
-
1135
- out = self.call_llm(task=out)
1136
-
1137
- self.short_memory.add(
1138
- role=self.agent_name, content=out
1139
- )
1140
-
1141
- if self.no_print is False:
1142
- agent_print(
1143
- f"{self.agent_name} - Agent Analysis",
1144
- out,
1145
- loop_count,
1146
- self.streaming_on,
1147
- )
1148
-
1149
1069
  self.sentiment_and_evaluator(response)
1150
1070
 
1151
1071
  success = True # Mark as successful to exit the retry loop
@@ -1222,6 +1142,7 @@ class Agent:
1222
1142
  if self.autosave:
1223
1143
  self.save()
1224
1144
 
1145
+ # Output formatting based on output_type
1225
1146
  return history_output_formatter(
1226
1147
  self.short_memory, type=self.output_type
1227
1148
  )
@@ -1362,36 +1283,36 @@ class Agent:
1362
1283
 
1363
1284
  return output.getvalue()
1364
1285
 
1365
- def parse_and_execute_tools(self, response: str, *args, **kwargs):
1366
- max_retries = 3 # Maximum number of retries
1367
- retries = 0
1368
- while retries < max_retries:
1369
- try:
1370
- logger.info("Executing tool...")
1371
-
1372
- # try to Execute the tool and return a string
1373
- out = parse_and_execute_json(
1374
- functions=self.tools,
1375
- json_string=response,
1376
- parse_md=True,
1377
- *args,
1378
- **kwargs,
1379
- )
1380
- logger.info(f"Tool Output: {out}")
1381
- # Add the output to the memory
1382
- # self.short_memory.add(
1383
- # role="Tool Executor",
1384
- # content=out,
1385
- # )
1386
- return out
1387
- except Exception as error:
1388
- retries += 1
1389
- logger.error(
1390
- f"Attempt {retries}: Error executing tool: {error}"
1391
- )
1392
- if retries == max_retries:
1393
- raise error
1394
- time.sleep(1) # Wait for a bit before retrying
1286
+ # def parse_and_execute_tools(self, response: str, *args, **kwargs):
1287
+ # max_retries = 3 # Maximum number of retries
1288
+ # retries = 0
1289
+ # while retries < max_retries:
1290
+ # try:
1291
+ # logger.info("Executing tool...")
1292
+
1293
+ # # try to Execute the tool and return a string
1294
+ # out = parse_and_execute_json(
1295
+ # functions=self.tools,
1296
+ # json_string=response,
1297
+ # parse_md=True,
1298
+ # *args,
1299
+ # **kwargs,
1300
+ # )
1301
+ # logger.info(f"Tool Output: {out}")
1302
+ # # Add the output to the memory
1303
+ # # self.short_memory.add(
1304
+ # # role="Tool Executor",
1305
+ # # content=out,
1306
+ # # )
1307
+ # return out
1308
+ # except Exception as error:
1309
+ # retries += 1
1310
+ # logger.error(
1311
+ # f"Attempt {retries}: Error executing tool: {error}"
1312
+ # )
1313
+ # if retries == max_retries:
1314
+ # raise error
1315
+ # time.sleep(1) # Wait for a bit before retrying
1395
1316
 
1396
1317
  def add_memory(self, message: str):
1397
1318
  """Add a memory to the agent
@@ -2270,7 +2191,7 @@ class Agent:
2270
2191
  time=time.time(),
2271
2192
  tokens=total_tokens,
2272
2193
  response=AgentChatCompletionResponse(
2273
- id=self.agent_id,
2194
+ id=self.id,
2274
2195
  agent_name=self.agent_name,
2275
2196
  object="chat.completion",
2276
2197
  choices=ChatCompletionResponseChoice(
@@ -2706,7 +2627,7 @@ class Agent:
2706
2627
  f"Agent Name {self.agent_name} [Max Loops: {loop_count} ]",
2707
2628
  )
2708
2629
 
2709
- def parse_llm_output(self, response: Any) -> str:
2630
+ def parse_llm_output(self, response: Any):
2710
2631
  """Parse and standardize the output from the LLM.
2711
2632
 
2712
2633
  Args:
@@ -2719,7 +2640,7 @@ class Agent:
2719
2640
  ValueError: If the response format is unexpected and can't be handled
2720
2641
  """
2721
2642
  try:
2722
- # Handle dictionary responses
2643
+
2723
2644
  if isinstance(response, dict):
2724
2645
  if "choices" in response:
2725
2646
  return response["choices"][0]["message"][
@@ -2729,17 +2650,23 @@ class Agent:
2729
2650
  response
2730
2651
  ) # Convert other dicts to string
2731
2652
 
2732
- # Handle string responses
2733
- elif isinstance(response, str):
2734
- return response
2653
+ elif isinstance(response, BaseModel):
2654
+ out = response.model_dump()
2735
2655
 
2736
- # Handle list responses (from check_llm_outputs)
2737
- elif isinstance(response, list):
2738
- return "\n".join(response)
2656
+ # Handle List[BaseModel] responses
2657
+ elif (
2658
+ isinstance(response, list)
2659
+ and response
2660
+ and isinstance(response[0], BaseModel)
2661
+ ):
2662
+ return [item.model_dump() for item in response]
2739
2663
 
2740
- # Handle any other type by converting to string
2664
+ elif isinstance(response, list):
2665
+ out = format_data_structure(response)
2741
2666
  else:
2742
- return str(response)
2667
+ out = str(response)
2668
+
2669
+ return out
2743
2670
 
2744
2671
  except Exception as e:
2745
2672
  logger.error(f"Error parsing LLM output: {e}")
@@ -2776,3 +2703,124 @@ class Agent:
2776
2703
  role="Output Cleaner",
2777
2704
  content=response,
2778
2705
  )
2706
+
2707
+ def mcp_tool_handling(
2708
+ self, response: any, current_loop: Optional[int] = 0
2709
+ ):
2710
+ try:
2711
+
2712
+ if exists(self.mcp_url):
2713
+ # Execute the tool call
2714
+ tool_response = asyncio.run(
2715
+ execute_tool_call_simple(
2716
+ response=response,
2717
+ server_path=self.mcp_url,
2718
+ )
2719
+ )
2720
+ elif exists(self.mcp_config):
2721
+ # Execute the tool call
2722
+ tool_response = asyncio.run(
2723
+ execute_tool_call_simple(
2724
+ response=response,
2725
+ connection=self.mcp_config,
2726
+ )
2727
+ )
2728
+ else:
2729
+ raise AgentMCPConnectionError(
2730
+ "mcp_url must be either a string URL or MCPConnection object"
2731
+ )
2732
+
2733
+ # Get the text content from the tool response
2734
+ text_content = (
2735
+ tool_response.content[0].text
2736
+ if tool_response.content
2737
+ else str(tool_response)
2738
+ )
2739
+
2740
+ # Add to the memory
2741
+ self.short_memory.add(
2742
+ role="Tool Executor",
2743
+ content=text_content,
2744
+ )
2745
+
2746
+ # Create a temporary LLM instance without tools for the follow-up call
2747
+ try:
2748
+ temp_llm = LiteLLM(
2749
+ model_name=self.model_name,
2750
+ temperature=self.temperature,
2751
+ max_tokens=self.max_tokens,
2752
+ system_prompt=self.system_prompt,
2753
+ stream=self.streaming_on,
2754
+ )
2755
+
2756
+ summary = temp_llm.run(
2757
+ task=self.short_memory.get_str()
2758
+ )
2759
+ except Exception as e:
2760
+ logger.error(
2761
+ f"Error calling LLM after MCP tool execution: {e}"
2762
+ )
2763
+ # Fallback: provide a default summary
2764
+ summary = "I successfully executed the MCP tool and retrieved the information above."
2765
+
2766
+ self.pretty_print(summary, loop_count=current_loop)
2767
+
2768
+ # Add to the memory
2769
+ self.short_memory.add(
2770
+ role=self.agent_name, content=summary
2771
+ )
2772
+ except AgentMCPToolError as e:
2773
+ logger.error(f"Error in MCP tool: {e}")
2774
+ raise e
2775
+
2776
+ def execute_tools(self, response: any, loop_count: int):
2777
+
2778
+ output = (
2779
+ self.tool_struct.execute_function_calls_from_api_response(
2780
+ response
2781
+ )
2782
+ )
2783
+
2784
+ self.short_memory.add(
2785
+ role="Tool Executor",
2786
+ content=format_data_structure(output),
2787
+ )
2788
+
2789
+ self.pretty_print(
2790
+ f"{format_data_structure(output)}",
2791
+ loop_count,
2792
+ )
2793
+
2794
+ # Now run the LLM again without tools - create a temporary LLM instance
2795
+ # instead of modifying the cached one
2796
+ # Create a temporary LLM instance without tools for the follow-up call
2797
+ temp_llm = LiteLLM(
2798
+ model_name=self.model_name,
2799
+ temperature=self.temperature,
2800
+ max_tokens=self.max_tokens,
2801
+ system_prompt=self.system_prompt,
2802
+ stream=self.streaming_on,
2803
+ tools_list_dictionary=None,
2804
+ parallel_tool_calls=False,
2805
+ )
2806
+
2807
+ tool_response = temp_llm.run(
2808
+ f"""
2809
+ Please analyze and summarize the following tool execution output in a clear and concise way.
2810
+ Focus on the key information and insights that would be most relevant to the user's original request.
2811
+ If there are any errors or issues, highlight them prominently.
2812
+
2813
+ Tool Output:
2814
+ {output}
2815
+ """
2816
+ )
2817
+
2818
+ self.short_memory.add(
2819
+ role=self.agent_name,
2820
+ content=tool_response,
2821
+ )
2822
+
2823
+ self.pretty_print(
2824
+ f"{tool_response}",
2825
+ loop_count,
2826
+ )