Skip to content

Commit 2485ed9

Browse files
feat: upgrade chromadb to v1.1.0, improve types
- update imports and include handling for chromadb v1.1.0 - fix mypy and typing_compat issues (required, typeddict, voyageai) - refine embedderconfig typing and allow base provider instances - handle mem0 as special case for external memory storage - bump tools and clean up redundant deps
1 parent ce5ea9b commit 2485ed9

File tree

35 files changed

+383
-316
lines changed

35 files changed

+383
-316
lines changed

pyproject.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ dependencies = [
2121
"opentelemetry-sdk>=1.30.0",
2222
"opentelemetry-exporter-otlp-proto-http>=1.30.0",
2323
# Data Handling
24-
"chromadb>=0.5.23",
24+
"chromadb~=1.1.0",
2525
"tokenizers>=0.20.3",
26-
"onnxruntime==1.22.0",
2726
"openpyxl>=3.1.5",
2827
"pyvis>=0.3.2",
2928
# Authentication and Security
@@ -49,7 +48,9 @@ Documentation = "https://docs.crewai.com"
4948
Repository = "https://github.com/crewAIInc/crewAI"
5049

5150
[project.optional-dependencies]
52-
tools = ["crewai-tools~=0.73.0"]
51+
tools = [
52+
"crewai-tools>=0.74.0",
53+
]
5354
embeddings = [
5455
"tiktoken~=0.8.0"
5556
]
@@ -195,3 +196,4 @@ exclude = [
195196
"docs/**",
196197
"docs/",
197198
]
199+

src/crewai/agent.py

Lines changed: 64 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
11
import shutil
22
import subprocess
33
import time
4+
from collections.abc import Callable, Sequence
45
from typing import (
56
Any,
6-
Callable,
7-
Dict,
8-
List,
97
Literal,
10-
Optional,
11-
Sequence,
12-
Tuple,
13-
Type,
14-
Union,
158
)
169

1710
from pydantic import Field, InstanceOf, PrivateAttr, model_validator
1811

1912
from crewai.agents import CacheHandler
2013
from crewai.agents.agent_builder.base_agent import BaseAgent
2114
from crewai.agents.crew_agent_executor import CrewAgentExecutor
15+
from crewai.events.event_bus import crewai_event_bus
16+
from crewai.events.types.agent_events import (
17+
AgentExecutionCompletedEvent,
18+
AgentExecutionErrorEvent,
19+
AgentExecutionStartedEvent,
20+
)
21+
from crewai.events.types.knowledge_events import (
22+
KnowledgeQueryCompletedEvent,
23+
KnowledgeQueryFailedEvent,
24+
KnowledgeQueryStartedEvent,
25+
KnowledgeRetrievalCompletedEvent,
26+
KnowledgeRetrievalStartedEvent,
27+
KnowledgeSearchQueryFailedEvent,
28+
)
29+
from crewai.events.types.memory_events import (
30+
MemoryRetrievalCompletedEvent,
31+
MemoryRetrievalStartedEvent,
32+
)
2233
from crewai.knowledge.knowledge import Knowledge
2334
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
2435
from crewai.knowledge.utils.knowledge_utils import extract_knowledge_context
2536
from crewai.lite_agent import LiteAgent, LiteAgentOutput
2637
from crewai.llm import BaseLLM
2738
from crewai.memory.contextual.contextual_memory import ContextualMemory
39+
from crewai.rag.embeddings.types import EmbedderConfig
2840
from crewai.security import Fingerprint
2941
from crewai.task import Task
3042
from crewai.tools import BaseTool
@@ -38,24 +50,6 @@
3850
)
3951
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
4052
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-
)
5953
from crewai.utilities.llm_utils import create_llm
6054
from crewai.utilities.token_counter_callback import TokenCalcHandler
6155
from crewai.utilities.training_handler import CrewTrainingHandler
@@ -87,36 +81,36 @@ class Agent(BaseAgent):
8781
"""
8882

8983
_times_executed: int = PrivateAttr(default=0)
90-
max_execution_time: Optional[int] = Field(
84+
max_execution_time: int | None = Field(
9185
default=None,
9286
description="Maximum execution time for an agent to execute a task",
9387
)
9488
agent_ops_agent_name: str = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str")
9589
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(
90+
step_callback: Any | None = Field(
9791
default=None,
9892
description="Callback to be executed after each step of the agent execution.",
9993
)
100-
use_system_prompt: Optional[bool] = Field(
94+
use_system_prompt: bool | None = Field(
10195
default=True,
10296
description="Use system prompt for the agent.",
10397
)
104-
llm: Union[str, InstanceOf[BaseLLM], Any] = Field(
98+
llm: str | InstanceOf[BaseLLM] | Any = Field(
10599
description="Language model that will run the agent.", default=None
106100
)
107-
function_calling_llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
101+
function_calling_llm: str | InstanceOf[BaseLLM] | Any | None = Field(
108102
description="Language model that will run the agent.", default=None
109103
)
110-
system_template: Optional[str] = Field(
104+
system_template: str | None = Field(
111105
default=None, description="System format for the agent."
112106
)
113-
prompt_template: Optional[str] = Field(
107+
prompt_template: str | None = Field(
114108
default=None, description="Prompt format for the agent."
115109
)
116-
response_template: Optional[str] = Field(
110+
response_template: str | None = Field(
117111
default=None, description="Response format for the agent."
118112
)
119-
allow_code_execution: Optional[bool] = Field(
113+
allow_code_execution: bool | None = Field(
120114
default=False, description="Enable code execution for the agent."
121115
)
122116
respect_context_window: bool = Field(
@@ -147,31 +141,31 @@ class Agent(BaseAgent):
147141
default=False,
148142
description="Whether the agent should reflect and create a plan before executing a task.",
149143
)
150-
max_reasoning_attempts: Optional[int] = Field(
144+
max_reasoning_attempts: int | None = Field(
151145
default=None,
152146
description="Maximum number of reasoning attempts before executing the task. If None, will try until ready.",
153147
)
154-
embedder: Optional[Dict[str, Any]] = Field(
148+
embedder: EmbedderConfig | None = Field(
155149
default=None,
156150
description="Embedder configuration for the agent.",
157151
)
158-
agent_knowledge_context: Optional[str] = Field(
152+
agent_knowledge_context: str | None = Field(
159153
default=None,
160154
description="Knowledge context for the agent.",
161155
)
162-
crew_knowledge_context: Optional[str] = Field(
156+
crew_knowledge_context: str | None = Field(
163157
default=None,
164158
description="Knowledge context for the crew.",
165159
)
166-
knowledge_search_query: Optional[str] = Field(
160+
knowledge_search_query: str | None = Field(
167161
default=None,
168162
description="Knowledge search query for the agent dynamically generated by the agent.",
169163
)
170-
from_repository: Optional[str] = Field(
164+
from_repository: str | None = Field(
171165
default=None,
172166
description="The Agent's role to be used from your repository.",
173167
)
174-
guardrail: Optional[Union[Callable[[Any], Tuple[bool, Any]], str]] = Field(
168+
guardrail: Callable[[Any], tuple[bool, Any]] | str | None = Field(
175169
default=None,
176170
description="Function or string description of a guardrail to validate agent output",
177171
)
@@ -180,7 +174,7 @@ class Agent(BaseAgent):
180174
)
181175

182176
@model_validator(mode="before")
183-
def validate_from_repository(cls, v):
177+
def validate_from_repository(cls, v): # noqa: N805
184178
if v is not None and (from_repository := v.get("from_repository")):
185179
return load_agent_from_repository(from_repository) | v
186180
return v
@@ -208,7 +202,7 @@ def _setup_agent_executor(self):
208202
self.cache_handler = CacheHandler()
209203
self.set_cache_handler(self.cache_handler)
210204

211-
def set_knowledge(self, crew_embedder: Optional[Dict[str, Any]] = None):
205+
def set_knowledge(self, crew_embedder: EmbedderConfig | None = None):
212206
try:
213207
if self.embedder is None and crew_embedder:
214208
self.embedder = crew_embedder
@@ -224,7 +218,7 @@ def set_knowledge(self, crew_embedder: Optional[Dict[str, Any]] = None):
224218
)
225219
self.knowledge.add_sources()
226220
except (TypeError, ValueError) as e:
227-
raise ValueError(f"Invalid Knowledge Configuration: {str(e)}")
221+
raise ValueError(f"Invalid Knowledge Configuration: {e!s}") from e
228222

229223
def _is_any_available_memory(self) -> bool:
230224
"""Check if any memory is available."""
@@ -244,8 +238,8 @@ def _is_any_available_memory(self) -> bool:
244238
def execute_task(
245239
self,
246240
task: Task,
247-
context: Optional[str] = None,
248-
tools: Optional[List[BaseTool]] = None,
241+
context: str | None = None,
242+
tools: list[BaseTool] | None = None,
249243
) -> str:
250244
"""Execute a task with the agent.
251245
@@ -278,11 +272,9 @@ def execute_task(
278272
task.description += f"\n\nReasoning Plan:\n{reasoning_output.plan.plan}"
279273
except Exception as e:
280274
if hasattr(self, "_logger"):
281-
self._logger.log(
282-
"error", f"Error during reasoning process: {str(e)}"
283-
)
275+
self._logger.log("error", f"Error during reasoning process: {e!s}")
284276
else:
285-
print(f"Error during reasoning process: {str(e)}")
277+
print(f"Error during reasoning process: {e!s}")
286278

287279
self._inject_date_to_task(task)
288280

@@ -335,7 +327,7 @@ def execute_task(
335327
agent=self,
336328
task=task,
337329
)
338-
memory = contextual_memory.build_context_for_task(task, context)
330+
memory = contextual_memory.build_context_for_task(task, context) # type: ignore[arg-type]
339331
if memory.strip() != "":
340332
task_prompt += self.i18n.slice("memory").format(memory=memory)
341333

@@ -525,14 +517,14 @@ def _execute_with_timeout(self, task_prompt: str, task: Task, timeout: int) -> s
525517

526518
try:
527519
return future.result(timeout=timeout)
528-
except concurrent.futures.TimeoutError:
520+
except concurrent.futures.TimeoutError as e:
529521
future.cancel()
530522
raise TimeoutError(
531523
f"Task '{task.description}' execution timed out after {timeout} seconds. Consider increasing max_execution_time or optimizing the task."
532-
)
524+
) from e
533525
except Exception as e:
534526
future.cancel()
535-
raise RuntimeError(f"Task execution failed: {str(e)}")
527+
raise RuntimeError(f"Task execution failed: {e!s}") from e
536528

537529
def _execute_without_timeout(self, task_prompt: str, task: Task) -> str:
538530
"""Execute a task without a timeout.
@@ -554,14 +546,14 @@ def _execute_without_timeout(self, task_prompt: str, task: Task) -> str:
554546
)["output"]
555547

556548
def create_agent_executor(
557-
self, tools: Optional[List[BaseTool]] = None, task=None
549+
self, tools: list[BaseTool] | None = None, task=None
558550
) -> None:
559551
"""Create an agent executor for the agent.
560552
561553
Returns:
562554
An instance of the CrewAgentExecutor class.
563555
"""
564-
raw_tools: List[BaseTool] = tools or self.tools or []
556+
raw_tools: list[BaseTool] = tools or self.tools or []
565557
parsed_tools = parse_tools(raw_tools)
566558

567559
prompt = Prompts(
@@ -587,7 +579,7 @@ def create_agent_executor(
587579
agent=self,
588580
crew=self.crew,
589581
tools=parsed_tools,
590-
prompt=prompt,
582+
prompt=prompt, # type: ignore[arg-type]
591583
original_tools=raw_tools,
592584
stop_words=stop_words,
593585
max_iter=self.max_iter,
@@ -603,10 +595,9 @@ def create_agent_executor(
603595
callbacks=[TokenCalcHandler(self._token_process)],
604596
)
605597

606-
def get_delegation_tools(self, agents: List[BaseAgent]):
598+
def get_delegation_tools(self, agents: list[BaseAgent]):
607599
agent_tools = AgentTools(agents=agents)
608-
tools = agent_tools.tools()
609-
return tools
600+
return agent_tools.tools()
610601

611602
def get_multimodal_tools(self) -> Sequence[BaseTool]:
612603
from crewai.tools.agent_tools.add_image_tool import AddImageTool
@@ -654,7 +645,7 @@ def _use_trained_data(self, task_prompt: str) -> str:
654645
)
655646
return task_prompt
656647

657-
def _render_text_description(self, tools: List[Any]) -> str:
648+
def _render_text_description(self, tools: list[Any]) -> str:
658649
"""Render the tool name and description in plain text.
659650
660651
Output will be in the format of:
@@ -664,15 +655,13 @@ def _render_text_description(self, tools: List[Any]) -> str:
664655
search: This tool is used for search
665656
calculator: This tool is used for math
666657
"""
667-
description = "\n".join(
658+
return "\n".join(
668659
[
669660
f"Tool name: {tool.name}\nTool description:\n{tool.description}"
670661
for tool in tools
671662
]
672663
)
673664

674-
return description
675-
676665
def _inject_date_to_task(self, task):
677666
"""Inject the current date into the task description if inject_date is enabled."""
678667
if self.inject_date:
@@ -696,13 +685,13 @@ def _inject_date_to_task(self, task):
696685
if not is_valid:
697686
raise ValueError(f"Invalid date format: {self.date_format}")
698687

699-
current_date: str = datetime.now().strftime(self.date_format)
688+
current_date = datetime.now().strftime(self.date_format)
700689
task.description += f"\n\nCurrent Date: {current_date}"
701690
except Exception as e:
702691
if hasattr(self, "_logger"):
703-
self._logger.log("warning", f"Failed to inject date: {str(e)}")
692+
self._logger.log("warning", f"Failed to inject date: {e!s}")
704693
else:
705-
print(f"Warning: Failed to inject date: {str(e)}")
694+
print(f"Warning: Failed to inject date: {e!s}")
706695

707696
def _validate_docker_installation(self) -> None:
708697
"""Check if Docker is installed and running."""
@@ -713,15 +702,15 @@ def _validate_docker_installation(self) -> None:
713702

714703
try:
715704
subprocess.run(
716-
["docker", "info"],
705+
["/usr/bin/docker", "info"],
717706
check=True,
718707
stdout=subprocess.PIPE,
719708
stderr=subprocess.PIPE,
720709
)
721-
except subprocess.CalledProcessError:
710+
except subprocess.CalledProcessError as e:
722711
raise RuntimeError(
723712
f"Docker is not running. Please start Docker to use code execution with agent: {self.role}"
724-
)
713+
) from e
725714

726715
def __repr__(self):
727716
return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
@@ -796,8 +785,8 @@ def _get_knowledge_search_query(self, task_prompt: str) -> str | None:
796785

797786
def kickoff(
798787
self,
799-
messages: Union[str, List[Dict[str, str]]],
800-
response_format: Optional[Type[Any]] = None,
788+
messages: str | list[dict[str, str]],
789+
response_format: type[Any] | None = None,
801790
) -> LiteAgentOutput:
802791
"""
803792
Execute the agent with the given messages using a LiteAgent instance.
@@ -836,8 +825,8 @@ def kickoff(
836825

837826
async def kickoff_async(
838827
self,
839-
messages: Union[str, List[Dict[str, str]]],
840-
response_format: Optional[Type[Any]] = None,
828+
messages: str | list[dict[str, str]],
829+
response_format: type[Any] | None = None,
841830
) -> LiteAgentOutput:
842831
"""
843832
Execute the agent asynchronously with the given messages using a LiteAgent instance.

0 commit comments

Comments
 (0)