1
1
import shutil
2
2
import subprocess
3
3
import time
4
+ from collections .abc import Callable , Sequence
4
5
from typing import (
5
6
Any ,
6
- Callable ,
7
- Dict ,
8
- List ,
9
7
Literal ,
10
- Optional ,
11
- Sequence ,
12
- Tuple ,
13
- Type ,
14
- Union ,
8
+ cast ,
15
9
)
16
10
17
11
from pydantic import Field , InstanceOf , PrivateAttr , model_validator
18
12
19
13
from crewai .agents import CacheHandler
20
- from crewai .agents .agent_builder .base_agent import BaseAgent
14
+ from crewai .agents .agent_builder .base_agent import BaseAgent , PlatformAppOrAction
21
15
from crewai .agents .crew_agent_executor import CrewAgentExecutor
16
+ from crewai .events .event_bus import crewai_event_bus
17
+ from crewai .events .types .agent_events import (
18
+ AgentExecutionCompletedEvent ,
19
+ AgentExecutionErrorEvent ,
20
+ AgentExecutionStartedEvent ,
21
+ )
22
+ from crewai .events .types .knowledge_events import (
23
+ KnowledgeQueryCompletedEvent ,
24
+ KnowledgeQueryFailedEvent ,
25
+ KnowledgeQueryStartedEvent ,
26
+ KnowledgeRetrievalCompletedEvent ,
27
+ KnowledgeRetrievalStartedEvent ,
28
+ KnowledgeSearchQueryFailedEvent ,
29
+ )
30
+ from crewai .events .types .memory_events import (
31
+ MemoryRetrievalCompletedEvent ,
32
+ MemoryRetrievalStartedEvent ,
33
+ )
22
34
from crewai .knowledge .knowledge import Knowledge
23
35
from crewai .knowledge .source .base_knowledge_source import BaseKnowledgeSource
24
36
from crewai .knowledge .utils .knowledge_utils import extract_knowledge_context
38
50
)
39
51
from crewai .utilities .constants import TRAINED_AGENTS_DATA_FILE , TRAINING_DATA_FILE
40
52
from crewai .utilities .converter import generate_model_description
41
- from crewai .events .types .agent_events import (
42
- AgentExecutionCompletedEvent ,
43
- AgentExecutionErrorEvent ,
44
- AgentExecutionStartedEvent ,
45
- )
46
- from crewai .events .event_bus import crewai_event_bus
47
- from crewai .events .types .memory_events import (
48
- MemoryRetrievalStartedEvent ,
49
- MemoryRetrievalCompletedEvent ,
50
- )
51
- from crewai .events .types .knowledge_events import (
52
- KnowledgeQueryCompletedEvent ,
53
- KnowledgeQueryFailedEvent ,
54
- KnowledgeQueryStartedEvent ,
55
- KnowledgeRetrievalCompletedEvent ,
56
- KnowledgeRetrievalStartedEvent ,
57
- KnowledgeSearchQueryFailedEvent ,
58
- )
59
53
from crewai .utilities .llm_utils import create_llm
60
54
from crewai .utilities .token_counter_callback import TokenCalcHandler
61
55
from crewai .utilities .training_handler import CrewTrainingHandler
@@ -84,39 +78,40 @@ class Agent(BaseAgent):
84
78
step_callback: Callback to be executed after each step of the agent execution.
85
79
knowledge_sources: Knowledge sources for the agent.
86
80
embedder: Embedder configuration for the agent.
81
+ apps: List of applications that the agent can access through CrewAI Platform.
87
82
"""
88
83
89
84
_times_executed : int = PrivateAttr (default = 0 )
90
- max_execution_time : Optional [ int ] = Field (
85
+ max_execution_time : int | None = Field (
91
86
default = None ,
92
87
description = "Maximum execution time for an agent to execute a task" ,
93
88
)
94
89
agent_ops_agent_name : str = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
95
90
agent_ops_agent_id : str = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
96
- step_callback : Optional [ Any ] = Field (
91
+ step_callback : Any | None = Field (
97
92
default = None ,
98
93
description = "Callback to be executed after each step of the agent execution." ,
99
94
)
100
- use_system_prompt : Optional [ bool ] = Field (
95
+ use_system_prompt : bool | None = Field (
101
96
default = True ,
102
97
description = "Use system prompt for the agent." ,
103
98
)
104
- llm : Union [ str , InstanceOf [BaseLLM ], Any ] = Field (
99
+ llm : str | InstanceOf [BaseLLM ] | Any = Field (
105
100
description = "Language model that will run the agent." , default = None
106
101
)
107
- function_calling_llm : Optional [ Union [ str , InstanceOf [BaseLLM ], Any ]] = Field (
102
+ function_calling_llm : str | InstanceOf [BaseLLM ] | Any | None = Field (
108
103
description = "Language model that will run the agent." , default = None
109
104
)
110
- system_template : Optional [ str ] = Field (
105
+ system_template : str | None = Field (
111
106
default = None , description = "System format for the agent."
112
107
)
113
- prompt_template : Optional [ str ] = Field (
108
+ prompt_template : str | None = Field (
114
109
default = None , description = "Prompt format for the agent."
115
110
)
116
- response_template : Optional [ str ] = Field (
111
+ response_template : str | None = Field (
117
112
default = None , description = "Response format for the agent."
118
113
)
119
- allow_code_execution : Optional [ bool ] = Field (
114
+ allow_code_execution : bool | None = Field (
120
115
default = False , description = "Enable code execution for the agent."
121
116
)
122
117
respect_context_window : bool = Field (
@@ -147,31 +142,31 @@ class Agent(BaseAgent):
147
142
default = False ,
148
143
description = "Whether the agent should reflect and create a plan before executing a task." ,
149
144
)
150
- max_reasoning_attempts : Optional [ int ] = Field (
145
+ max_reasoning_attempts : int | None = Field (
151
146
default = None ,
152
147
description = "Maximum number of reasoning attempts before executing the task. If None, will try until ready." ,
153
148
)
154
- embedder : Optional [ Dict [ str , Any ]] = Field (
149
+ embedder : dict [ str , Any ] | None = Field (
155
150
default = None ,
156
151
description = "Embedder configuration for the agent." ,
157
152
)
158
- agent_knowledge_context : Optional [ str ] = Field (
153
+ agent_knowledge_context : str | None = Field (
159
154
default = None ,
160
155
description = "Knowledge context for the agent." ,
161
156
)
162
- crew_knowledge_context : Optional [ str ] = Field (
157
+ crew_knowledge_context : str | None = Field (
163
158
default = None ,
164
159
description = "Knowledge context for the crew." ,
165
160
)
166
- knowledge_search_query : Optional [ str ] = Field (
161
+ knowledge_search_query : str | None = Field (
167
162
default = None ,
168
163
description = "Knowledge search query for the agent dynamically generated by the agent." ,
169
164
)
170
- from_repository : Optional [ str ] = Field (
165
+ from_repository : str | None = Field (
171
166
default = None ,
172
167
description = "The Agent's role to be used from your repository." ,
173
168
)
174
- guardrail : Optional [ Union [ Callable [[Any ], Tuple [bool , Any ]], str ]] = Field (
169
+ guardrail : Callable [[Any ], tuple [bool , Any ]] | str | None = Field (
175
170
default = None ,
176
171
description = "Function or string description of a guardrail to validate agent output" ,
177
172
)
@@ -180,6 +175,7 @@ class Agent(BaseAgent):
180
175
)
181
176
182
177
@model_validator (mode = "before" )
178
+ @classmethod
183
179
def validate_from_repository (cls , v ):
184
180
if v is not None and (from_repository := v .get ("from_repository" )):
185
181
return load_agent_from_repository (from_repository ) | v
@@ -208,7 +204,7 @@ def _setup_agent_executor(self):
208
204
self .cache_handler = CacheHandler ()
209
205
self .set_cache_handler (self .cache_handler )
210
206
211
- def set_knowledge (self , crew_embedder : Optional [ Dict [ str , Any ]] = None ):
207
+ def set_knowledge (self , crew_embedder : dict [ str , Any ] | None = None ):
212
208
try :
213
209
if self .embedder is None and crew_embedder :
214
210
self .embedder = crew_embedder
@@ -224,7 +220,7 @@ def set_knowledge(self, crew_embedder: Optional[Dict[str, Any]] = None):
224
220
)
225
221
self .knowledge .add_sources ()
226
222
except (TypeError , ValueError ) as e :
227
- raise ValueError (f"Invalid Knowledge Configuration: { str ( e ) } " )
223
+ raise ValueError (f"Invalid Knowledge Configuration: { e !s } " ) from e
228
224
229
225
def _is_any_available_memory (self ) -> bool :
230
226
"""Check if any memory is available."""
@@ -244,8 +240,8 @@ def _is_any_available_memory(self) -> bool:
244
240
def execute_task (
245
241
self ,
246
242
task : Task ,
247
- context : Optional [ str ] = None ,
248
- tools : Optional [ List [ BaseTool ]] = None ,
243
+ context : str | None = None ,
244
+ tools : list [ BaseTool ] | None = None ,
249
245
) -> str :
250
246
"""Execute a task with the agent.
251
247
@@ -277,13 +273,9 @@ def execute_task(
277
273
# Add the reasoning plan to the task description
278
274
task .description += f"\n \n Reasoning Plan:\n { reasoning_output .plan .plan } "
279
275
except Exception as e :
280
- if hasattr (self , "_logger" ):
281
- self ._logger .log (
282
- "error" , f"Error during reasoning process: { str (e )} "
283
- )
284
- else :
285
- print (f"Error during reasoning process: { str (e )} " )
286
-
276
+ self ._logger .log (
277
+ "error" , f"Error during reasoning process: { e !s} "
278
+ )
287
279
self ._inject_date_to_task (task )
288
280
289
281
if self .tools_handler :
@@ -335,7 +327,7 @@ def execute_task(
335
327
agent = self ,
336
328
task = task ,
337
329
)
338
- memory = contextual_memory .build_context_for_task (task , context )
330
+ memory = contextual_memory .build_context_for_task (task , context or "" )
339
331
if memory .strip () != "" :
340
332
task_prompt += self .i18n .slice ("memory" ).format (memory = memory )
341
333
@@ -525,14 +517,14 @@ def _execute_with_timeout(self, task_prompt: str, task: Task, timeout: int) -> s
525
517
526
518
try :
527
519
return future .result (timeout = timeout )
528
- except concurrent .futures .TimeoutError :
520
+ except concurrent .futures .TimeoutError as e :
529
521
future .cancel ()
530
522
raise TimeoutError (
531
523
f"Task '{ task .description } ' execution timed out after { timeout } seconds. Consider increasing max_execution_time or optimizing the task."
532
- )
524
+ ) from e
533
525
except Exception as e :
534
526
future .cancel ()
535
- raise RuntimeError (f"Task execution failed: { str ( e ) } " )
527
+ raise RuntimeError (f"Task execution failed: { e !s } " ) from e
536
528
537
529
def _execute_without_timeout (self , task_prompt : str , task : Task ) -> str :
538
530
"""Execute a task without a timeout.
@@ -554,14 +546,14 @@ def _execute_without_timeout(self, task_prompt: str, task: Task) -> str:
554
546
)["output" ]
555
547
556
548
def create_agent_executor (
557
- self , tools : Optional [ List [ BaseTool ]] = None , task = None
549
+ self , tools : list [ BaseTool ] | None = None , task = None
558
550
) -> None :
559
551
"""Create an agent executor for the agent.
560
552
561
553
Returns:
562
554
An instance of the CrewAgentExecutor class.
563
555
"""
564
- raw_tools : List [BaseTool ] = tools or self .tools or []
556
+ raw_tools : list [BaseTool ] = tools or self .tools or []
565
557
parsed_tools = parse_tools (raw_tools )
566
558
567
559
prompt = Prompts (
@@ -587,7 +579,7 @@ def create_agent_executor(
587
579
agent = self ,
588
580
crew = self .crew ,
589
581
tools = parsed_tools ,
590
- prompt = prompt ,
582
+ prompt = cast ( dict [ str , str ], prompt ) ,
591
583
original_tools = raw_tools ,
592
584
stop_words = stop_words ,
593
585
max_iter = self .max_iter ,
@@ -603,10 +595,18 @@ def create_agent_executor(
603
595
callbacks = [TokenCalcHandler (self ._token_process )],
604
596
)
605
597
606
- def get_delegation_tools (self , agents : List [BaseAgent ]):
598
+ def get_delegation_tools (self , agents : list [BaseAgent ]):
607
599
agent_tools = AgentTools (agents = agents )
608
- tools = agent_tools .tools ()
609
- return tools
600
+ return agent_tools .tools ()
601
+
602
+ def get_platform_tools (self , apps : list [PlatformAppOrAction ]) -> list [BaseTool ]:
603
+ try :
604
+ from crewai_tools import CrewaiPlatformTools # type: ignore[import-untyped]
605
+
606
+ return CrewaiPlatformTools (apps = apps )
607
+ except Exception as e :
608
+ self ._logger .log ("error" , f"Error getting platform tools: { e !s} " )
609
+ return []
610
610
611
611
def get_multimodal_tools (self ) -> Sequence [BaseTool ]:
612
612
from crewai .tools .agent_tools .add_image_tool import AddImageTool
@@ -654,7 +654,7 @@ def _use_trained_data(self, task_prompt: str) -> str:
654
654
)
655
655
return task_prompt
656
656
657
- def _render_text_description (self , tools : List [Any ]) -> str :
657
+ def _render_text_description (self , tools : list [Any ]) -> str :
658
658
"""Render the tool name and description in plain text.
659
659
660
660
Output will be in the format of:
@@ -664,14 +664,13 @@ def _render_text_description(self, tools: List[Any]) -> str:
664
664
search: This tool is used for search
665
665
calculator: This tool is used for math
666
666
"""
667
- description = "\n " .join (
667
+ return "\n " .join (
668
668
[
669
669
f"Tool name: { tool .name } \n Tool description:\n { tool .description } "
670
670
for tool in tools
671
671
]
672
672
)
673
673
674
- return description
675
674
676
675
def _inject_date_to_task (self , task ):
677
676
"""Inject the current date into the task description if inject_date is enabled."""
@@ -700,28 +699,33 @@ def _inject_date_to_task(self, task):
700
699
task .description += f"\n \n Current Date: { current_date } "
701
700
except Exception as e :
702
701
if hasattr (self , "_logger" ):
703
- self ._logger .log ("warning" , f"Failed to inject date: { str ( e ) } " )
702
+ self ._logger .log ("warning" , f"Failed to inject date: { e !s } " )
704
703
else :
705
- print (f"Warning: Failed to inject date: { str ( e ) } " )
704
+ print (f"Warning: Failed to inject date: { e !s } " )
706
705
707
706
def _validate_docker_installation (self ) -> None :
708
707
"""Check if Docker is installed and running."""
709
- if not shutil .which ("docker" ):
708
+ docker_path = shutil .which ("docker" )
709
+ if not docker_path :
710
710
raise RuntimeError (
711
711
f"Docker is not installed. Please install Docker to use code execution with agent: { self .role } "
712
712
)
713
713
714
714
try :
715
- subprocess .run (
716
- ["docker" , "info" ],
715
+ subprocess .run ( # noqa: S603
716
+ [docker_path , "info" ],
717
717
check = True ,
718
718
stdout = subprocess .PIPE ,
719
719
stderr = subprocess .PIPE ,
720
720
)
721
- except subprocess .CalledProcessError :
721
+ except subprocess .CalledProcessError as e :
722
722
raise RuntimeError (
723
723
f"Docker is not running. Please start Docker to use code execution with agent: { self .role } "
724
- )
724
+ ) from e
725
+ except subprocess .TimeoutExpired as e :
726
+ raise RuntimeError (
727
+ f"Docker command timed out. Please check your Docker installation for agent: { self .role } "
728
+ ) from e
725
729
726
730
def __repr__ (self ):
727
731
return f"Agent(role={ self .role } , goal={ self .goal } , backstory={ self .backstory } )"
@@ -796,8 +800,8 @@ def _get_knowledge_search_query(self, task_prompt: str) -> str | None:
796
800
797
801
def kickoff (
798
802
self ,
799
- messages : Union [ str , List [ Dict [str , str ] ]],
800
- response_format : Optional [ Type [ Any ]] = None ,
803
+ messages : str | list [ dict [str , str ]],
804
+ response_format : type [ Any ] | None = None ,
801
805
) -> LiteAgentOutput :
802
806
"""
803
807
Execute the agent with the given messages using a LiteAgent instance.
@@ -836,8 +840,8 @@ def kickoff(
836
840
837
841
async def kickoff_async (
838
842
self ,
839
- messages : Union [ str , List [ Dict [str , str ] ]],
840
- response_format : Optional [ Type [ Any ]] = None ,
843
+ messages : str | list [ dict [str , str ]],
844
+ response_format : type [ Any ] | None = None ,
841
845
) -> LiteAgentOutput :
842
846
"""
843
847
Execute the agent asynchronously with the given messages using a LiteAgent instance.
0 commit comments