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.
- swarms/__init__.py +0 -1
- swarms/agents/cort_agent.py +206 -0
- swarms/agents/react_agent.py +173 -0
- swarms/agents/self_agent_builder.py +40 -0
- swarms/communication/base_communication.py +290 -0
- swarms/communication/duckdb_wrap.py +369 -72
- swarms/communication/pulsar_struct.py +691 -0
- swarms/communication/redis_wrap.py +1362 -0
- swarms/communication/sqlite_wrap.py +547 -44
- swarms/prompts/agent_self_builder_prompt.py +103 -0
- swarms/prompts/safety_prompt.py +50 -0
- swarms/schemas/__init__.py +6 -1
- swarms/schemas/agent_class_schema.py +91 -0
- swarms/schemas/agent_mcp_errors.py +18 -0
- swarms/schemas/agent_tool_schema.py +13 -0
- swarms/schemas/llm_agent_schema.py +92 -0
- swarms/schemas/mcp_schemas.py +43 -0
- swarms/structs/__init__.py +4 -0
- swarms/structs/agent.py +315 -267
- swarms/structs/aop.py +3 -1
- swarms/structs/batch_agent_execution.py +64 -0
- swarms/structs/conversation.py +261 -57
- swarms/structs/council_judge.py +542 -0
- swarms/structs/deep_research_swarm.py +19 -22
- swarms/structs/long_agent.py +424 -0
- swarms/structs/ma_utils.py +11 -8
- swarms/structs/malt.py +30 -28
- swarms/structs/multi_model_gpu_manager.py +1 -1
- swarms/structs/output_types.py +1 -1
- swarms/structs/swarm_router.py +70 -15
- swarms/tools/__init__.py +12 -0
- swarms/tools/base_tool.py +2840 -264
- swarms/tools/create_agent_tool.py +104 -0
- swarms/tools/mcp_client_call.py +504 -0
- swarms/tools/py_func_to_openai_func_str.py +45 -7
- swarms/tools/pydantic_to_json.py +10 -27
- swarms/utils/audio_processing.py +343 -0
- swarms/utils/history_output_formatter.py +5 -5
- swarms/utils/index.py +226 -0
- swarms/utils/litellm_wrapper.py +65 -67
- swarms/utils/try_except_wrapper.py +2 -2
- swarms/utils/xml_utils.py +42 -0
- {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/METADATA +5 -4
- {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/RECORD +47 -30
- {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/WHEEL +1 -1
- swarms/client/__init__.py +0 -15
- swarms/client/main.py +0 -407
- swarms/tools/mcp_client.py +0 -246
- swarms/tools/mcp_integration.py +0 -340
- {swarms-7.7.8.dist-info → swarms-7.8.0.dist-info}/LICENSE +0 -0
- {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.
|
50
|
-
|
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.
|
362
|
-
presence_penalty: float = 0.
|
363
|
-
temperature: float = 0.
|
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
|
-
|
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.
|
560
|
-
self.
|
578
|
+
if self.random_models_on is True:
|
579
|
+
self.model_name = set_random_models_for_agents()
|
561
580
|
|
562
|
-
|
563
|
-
self.system_prompt += REACT_SYS_PROMPT
|
581
|
+
def tool_handling(self):
|
564
582
|
|
565
|
-
|
566
|
-
self.
|
567
|
-
|
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
|
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
|
-
|
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
|
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=
|
639
|
-
|
640
|
-
|
641
|
-
|
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
|
709
|
-
|
710
|
-
|
711
|
-
)
|
712
|
-
|
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
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
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
|
-
|
797
|
-
|
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
|
-
|
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,
|
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
|
1119
|
-
out = self.parse_and_execute_tools(
|
1120
|
-
|
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
|
-
|
1124
|
-
|
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
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
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.
|
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)
|
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
|
-
|
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
|
-
|
2733
|
-
|
2734
|
-
return response
|
2653
|
+
elif isinstance(response, BaseModel):
|
2654
|
+
out = response.model_dump()
|
2735
2655
|
|
2736
|
-
# Handle
|
2737
|
-
elif
|
2738
|
-
|
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
|
-
|
2664
|
+
elif isinstance(response, list):
|
2665
|
+
out = format_data_structure(response)
|
2741
2666
|
else:
|
2742
|
-
|
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
|
+
)
|