mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-09 08:08:32 +00:00
fix: improve type annotations across codebase
This commit is contained in:
@@ -1,29 +1,42 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
from collections.abc import Callable, Sequence
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Literal,
|
Literal,
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
|
||||||
Tuple,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from pydantic import Field, InstanceOf, PrivateAttr, model_validator
|
from pydantic import Field, InstanceOf, PrivateAttr, model_validator
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
from crewai.agents.cache.cache_handler import CacheHandler
|
|
||||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||||
|
from crewai.agents.cache.cache_handler import CacheHandler
|
||||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||||
|
from crewai.events.event_bus import crewai_event_bus
|
||||||
|
from crewai.events.types.agent_events import (
|
||||||
|
AgentExecutionCompletedEvent,
|
||||||
|
AgentExecutionErrorEvent,
|
||||||
|
AgentExecutionStartedEvent,
|
||||||
|
)
|
||||||
|
from crewai.events.types.knowledge_events import (
|
||||||
|
KnowledgeQueryCompletedEvent,
|
||||||
|
KnowledgeQueryFailedEvent,
|
||||||
|
KnowledgeQueryStartedEvent,
|
||||||
|
KnowledgeRetrievalCompletedEvent,
|
||||||
|
KnowledgeRetrievalStartedEvent,
|
||||||
|
KnowledgeSearchQueryFailedEvent,
|
||||||
|
)
|
||||||
|
from crewai.events.types.memory_events import (
|
||||||
|
MemoryRetrievalCompletedEvent,
|
||||||
|
MemoryRetrievalStartedEvent,
|
||||||
|
)
|
||||||
from crewai.knowledge.knowledge import Knowledge
|
from crewai.knowledge.knowledge import Knowledge
|
||||||
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
||||||
from crewai.knowledge.utils.knowledge_utils import extract_knowledge_context
|
from crewai.knowledge.utils.knowledge_utils import extract_knowledge_context
|
||||||
from crewai.lite_agent import LiteAgent, LiteAgentOutput
|
from crewai.lite_agent import LiteAgent, LiteAgentOutput
|
||||||
from crewai.llm import BaseLLM
|
from crewai.llms.base_llm import BaseLLM
|
||||||
from crewai.memory.contextual.contextual_memory import ContextualMemory
|
from crewai.memory.contextual.contextual_memory import ContextualMemory
|
||||||
from crewai.security import Fingerprint
|
from crewai.security import Fingerprint
|
||||||
from crewai.task import Task
|
from crewai.task import Task
|
||||||
@@ -38,24 +51,6 @@ from crewai.utilities.agent_utils import (
|
|||||||
)
|
)
|
||||||
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
|
from crewai.utilities.constants import TRAINED_AGENTS_DATA_FILE, TRAINING_DATA_FILE
|
||||||
from crewai.utilities.converter import generate_model_description
|
from crewai.utilities.converter import generate_model_description
|
||||||
from crewai.events.types.agent_events import (
|
|
||||||
AgentExecutionCompletedEvent,
|
|
||||||
AgentExecutionErrorEvent,
|
|
||||||
AgentExecutionStartedEvent,
|
|
||||||
)
|
|
||||||
from crewai.events.event_bus import crewai_event_bus
|
|
||||||
from crewai.events.types.memory_events import (
|
|
||||||
MemoryRetrievalStartedEvent,
|
|
||||||
MemoryRetrievalCompletedEvent,
|
|
||||||
)
|
|
||||||
from crewai.events.types.knowledge_events import (
|
|
||||||
KnowledgeQueryCompletedEvent,
|
|
||||||
KnowledgeQueryFailedEvent,
|
|
||||||
KnowledgeQueryStartedEvent,
|
|
||||||
KnowledgeRetrievalCompletedEvent,
|
|
||||||
KnowledgeRetrievalStartedEvent,
|
|
||||||
KnowledgeSearchQueryFailedEvent,
|
|
||||||
)
|
|
||||||
from crewai.utilities.llm_utils import create_llm
|
from crewai.utilities.llm_utils import create_llm
|
||||||
from crewai.utilities.token_counter_callback import TokenCalcHandler
|
from crewai.utilities.token_counter_callback import TokenCalcHandler
|
||||||
from crewai.utilities.training_handler import CrewTrainingHandler
|
from crewai.utilities.training_handler import CrewTrainingHandler
|
||||||
@@ -101,10 +96,10 @@ class Agent(BaseAgent):
|
|||||||
default=True,
|
default=True,
|
||||||
description="Use system prompt for the agent.",
|
description="Use system prompt for the agent.",
|
||||||
)
|
)
|
||||||
llm: Union[str, InstanceOf[BaseLLM], Any] = Field(
|
llm: str | InstanceOf[BaseLLM] | Any = Field(
|
||||||
description="Language model that will run the agent.", default=None
|
description="Language model that will run the agent.", default=None
|
||||||
)
|
)
|
||||||
function_calling_llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
|
function_calling_llm: Optional[str | InstanceOf[BaseLLM] | Any] = Field(
|
||||||
description="Language model that will run the agent.", default=None
|
description="Language model that will run the agent.", default=None
|
||||||
)
|
)
|
||||||
system_template: Optional[str] = Field(
|
system_template: Optional[str] = Field(
|
||||||
@@ -151,7 +146,7 @@ class Agent(BaseAgent):
|
|||||||
default=None,
|
default=None,
|
||||||
description="Maximum number of reasoning attempts before executing the task. If None, will try until ready.",
|
description="Maximum number of reasoning attempts before executing the task. If None, will try until ready.",
|
||||||
)
|
)
|
||||||
embedder: Optional[Dict[str, Any]] = Field(
|
embedder: Optional[dict[str, Any]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Embedder configuration for the agent.",
|
description="Embedder configuration for the agent.",
|
||||||
)
|
)
|
||||||
@@ -171,7 +166,7 @@ class Agent(BaseAgent):
|
|||||||
default=None,
|
default=None,
|
||||||
description="The Agent's role to be used from your repository.",
|
description="The Agent's role to be used from your repository.",
|
||||||
)
|
)
|
||||||
guardrail: Optional[Union[Callable[[Any], Tuple[bool, Any]], str]] = Field(
|
guardrail: Optional[Callable[[Any], tuple[bool, Any]] | str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Function or string description of a guardrail to validate agent output",
|
description="Function or string description of a guardrail to validate agent output",
|
||||||
)
|
)
|
||||||
@@ -180,13 +175,14 @@ class Agent(BaseAgent):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
def validate_from_repository(cls, v):
|
@classmethod
|
||||||
|
def validate_from_repository(cls, v: Any) -> Any:
|
||||||
if v is not None and (from_repository := v.get("from_repository")):
|
if v is not None and (from_repository := v.get("from_repository")):
|
||||||
return load_agent_from_repository(from_repository) | v
|
return load_agent_from_repository(from_repository) | v
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def post_init_setup(self):
|
def post_init_setup(self) -> Self:
|
||||||
self.agent_ops_agent_name = self.role
|
self.agent_ops_agent_name = self.role
|
||||||
|
|
||||||
self.llm = create_llm(self.llm)
|
self.llm = create_llm(self.llm)
|
||||||
@@ -203,12 +199,12 @@ class Agent(BaseAgent):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _setup_agent_executor(self):
|
def _setup_agent_executor(self) -> None:
|
||||||
if not self.cache_handler:
|
if not self.cache_handler:
|
||||||
self.cache_handler = CacheHandler()
|
self.cache_handler = CacheHandler()
|
||||||
self.set_cache_handler(self.cache_handler)
|
self.set_cache_handler(self.cache_handler)
|
||||||
|
|
||||||
def set_knowledge(self, crew_embedder: Optional[Dict[str, Any]] = None):
|
def set_knowledge(self, crew_embedder: Optional[dict[str, Any]] = None) -> None:
|
||||||
try:
|
try:
|
||||||
if self.embedder is None and crew_embedder:
|
if self.embedder is None and crew_embedder:
|
||||||
self.embedder = crew_embedder
|
self.embedder = crew_embedder
|
||||||
@@ -245,7 +241,7 @@ class Agent(BaseAgent):
|
|||||||
self,
|
self,
|
||||||
task: Task,
|
task: Task,
|
||||||
context: Optional[str] = None,
|
context: Optional[str] = None,
|
||||||
tools: Optional[List[BaseTool]] = None,
|
tools: Optional[list[BaseTool]] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Execute a task with the agent.
|
"""Execute a task with the agent.
|
||||||
|
|
||||||
@@ -492,7 +488,7 @@ class Agent(BaseAgent):
|
|||||||
# If there was any tool in self.tools_results that had result_as_answer
|
# If there was any tool in self.tools_results that had result_as_answer
|
||||||
# set to True, return the results of the last tool that had
|
# set to True, return the results of the last tool that had
|
||||||
# result_as_answer set to True
|
# result_as_answer set to True
|
||||||
for tool_result in self.tools_results: # type: ignore # Item "None" of "list[Any] | None" has no attribute "__iter__" (not iterable)
|
for tool_result in self.tools_results:
|
||||||
if tool_result.get("result_as_answer", False):
|
if tool_result.get("result_as_answer", False):
|
||||||
result = tool_result["result"]
|
result = tool_result["result"]
|
||||||
crewai_event_bus.emit(
|
crewai_event_bus.emit(
|
||||||
@@ -554,14 +550,14 @@ class Agent(BaseAgent):
|
|||||||
)["output"]
|
)["output"]
|
||||||
|
|
||||||
def create_agent_executor(
|
def create_agent_executor(
|
||||||
self, tools: Optional[List[BaseTool]] = None, task=None
|
self, tools: Optional[list[BaseTool]] = None, task: Optional[Task] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Create an agent executor for the agent.
|
"""Create an agent executor for the agent.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An instance of the CrewAgentExecutor class.
|
An instance of the CrewAgentExecutor class.
|
||||||
"""
|
"""
|
||||||
raw_tools: List[BaseTool] = tools or self.tools or []
|
raw_tools: list[BaseTool] = tools or self.tools or []
|
||||||
parsed_tools = parse_tools(raw_tools)
|
parsed_tools = parse_tools(raw_tools)
|
||||||
|
|
||||||
prompt = Prompts(
|
prompt = Prompts(
|
||||||
@@ -603,7 +599,7 @@ class Agent(BaseAgent):
|
|||||||
callbacks=[TokenCalcHandler(self._token_process)],
|
callbacks=[TokenCalcHandler(self._token_process)],
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_delegation_tools(self, agents: List[BaseAgent]):
|
def get_delegation_tools(self, agents: list[BaseAgent]) -> list[BaseTool]:
|
||||||
agent_tools = AgentTools(agents=agents)
|
agent_tools = AgentTools(agents=agents)
|
||||||
tools = agent_tools.tools()
|
tools = agent_tools.tools()
|
||||||
return tools
|
return tools
|
||||||
@@ -613,7 +609,7 @@ class Agent(BaseAgent):
|
|||||||
|
|
||||||
return [AddImageTool()]
|
return [AddImageTool()]
|
||||||
|
|
||||||
def get_code_execution_tools(self):
|
def get_code_execution_tools(self) -> list[BaseTool]:
|
||||||
try:
|
try:
|
||||||
from crewai_tools import CodeInterpreterTool # type: ignore
|
from crewai_tools import CodeInterpreterTool # type: ignore
|
||||||
|
|
||||||
@@ -625,7 +621,9 @@ class Agent(BaseAgent):
|
|||||||
"info", "Coding tools not available. Install crewai_tools. "
|
"info", "Coding tools not available. Install crewai_tools. "
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_output_converter(self, llm, text, model, instructions):
|
def get_output_converter(
|
||||||
|
self, llm: BaseLLM, text: str, model: str, instructions: str
|
||||||
|
) -> Converter:
|
||||||
return Converter(llm=llm, text=text, model=model, instructions=instructions)
|
return Converter(llm=llm, text=text, model=model, instructions=instructions)
|
||||||
|
|
||||||
def _training_handler(self, task_prompt: str) -> str:
|
def _training_handler(self, task_prompt: str) -> str:
|
||||||
@@ -654,7 +652,7 @@ class Agent(BaseAgent):
|
|||||||
)
|
)
|
||||||
return task_prompt
|
return task_prompt
|
||||||
|
|
||||||
def _render_text_description(self, tools: List[Any]) -> str:
|
def _render_text_description(self, tools: list[Any]) -> str:
|
||||||
"""Render the tool name and description in plain text.
|
"""Render the tool name and description in plain text.
|
||||||
|
|
||||||
Output will be in the format of:
|
Output will be in the format of:
|
||||||
@@ -673,7 +671,7 @@ class Agent(BaseAgent):
|
|||||||
|
|
||||||
return description
|
return description
|
||||||
|
|
||||||
def _inject_date_to_task(self, task):
|
def _inject_date_to_task(self, task: str) -> str:
|
||||||
"""Inject the current date into the task description if inject_date is enabled."""
|
"""Inject the current date into the task description if inject_date is enabled."""
|
||||||
if self.inject_date:
|
if self.inject_date:
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -723,7 +721,7 @@ class Agent(BaseAgent):
|
|||||||
f"Docker is not running. Please start Docker to use code execution with agent: {self.role}"
|
f"Docker is not running. Please start Docker to use code execution with agent: {self.role}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
|
return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -736,7 +734,7 @@ class Agent(BaseAgent):
|
|||||||
"""
|
"""
|
||||||
return self.security_config.fingerprint
|
return self.security_config.fingerprint
|
||||||
|
|
||||||
def set_fingerprint(self, fingerprint: Fingerprint):
|
def set_fingerprint(self, fingerprint: Fingerprint) -> None:
|
||||||
self.security_config.fingerprint = fingerprint
|
self.security_config.fingerprint = fingerprint
|
||||||
|
|
||||||
def _get_knowledge_search_query(self, task_prompt: str) -> str | None:
|
def _get_knowledge_search_query(self, task_prompt: str) -> str | None:
|
||||||
@@ -796,8 +794,8 @@ class Agent(BaseAgent):
|
|||||||
|
|
||||||
def kickoff(
|
def kickoff(
|
||||||
self,
|
self,
|
||||||
messages: Union[str, List[Dict[str, str]]],
|
messages: str | list[dict[str, str]],
|
||||||
response_format: Optional[Type[Any]] = None,
|
response_format: Optional[type[Any]] = None,
|
||||||
) -> LiteAgentOutput:
|
) -> LiteAgentOutput:
|
||||||
"""
|
"""
|
||||||
Execute the agent with the given messages using a LiteAgent instance.
|
Execute the agent with the given messages using a LiteAgent instance.
|
||||||
@@ -836,8 +834,8 @@ class Agent(BaseAgent):
|
|||||||
|
|
||||||
async def kickoff_async(
|
async def kickoff_async(
|
||||||
self,
|
self,
|
||||||
messages: Union[str, List[Dict[str, str]]],
|
messages: str | list[dict[str, str]],
|
||||||
response_format: Optional[Type[Any]] = None,
|
response_format: Optional[type[Any]] = None,
|
||||||
) -> LiteAgentOutput:
|
) -> LiteAgentOutput:
|
||||||
"""
|
"""
|
||||||
Execute the agent asynchronously with the given messages using a LiteAgent instance.
|
Execute the agent asynchronously with the given messages using a LiteAgent instance.
|
||||||
|
|||||||
@@ -3,26 +3,18 @@ import json
|
|||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
|
from collections.abc import Callable, Mapping, Set
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from copy import copy as shallow_copy
|
from copy import copy as shallow_copy
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Optional,
|
Optional,
|
||||||
Set,
|
|
||||||
Tuple,
|
|
||||||
Union,
|
|
||||||
cast,
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
from opentelemetry import baggage
|
from opentelemetry import baggage
|
||||||
from opentelemetry.context import attach, detach
|
from opentelemetry.context import attach, detach
|
||||||
|
|
||||||
from crewai.utilities.crew.models import CrewContext
|
|
||||||
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
UUID4,
|
UUID4,
|
||||||
BaseModel,
|
BaseModel,
|
||||||
@@ -34,15 +26,36 @@ from pydantic import (
|
|||||||
model_validator,
|
model_validator,
|
||||||
)
|
)
|
||||||
from pydantic_core import PydanticCustomError
|
from pydantic_core import PydanticCustomError
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
from crewai.agent import Agent
|
from crewai.agent import Agent
|
||||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||||
from crewai.agents.cache.cache_handler import CacheHandler
|
from crewai.agents.cache.cache_handler import CacheHandler
|
||||||
from crewai.crews.crew_output import CrewOutput
|
from crewai.crews.crew_output import CrewOutput
|
||||||
|
from crewai.events.event_bus import crewai_event_bus
|
||||||
|
from crewai.events.event_listener import EventListener
|
||||||
|
from crewai.events.listeners.tracing.trace_listener import (
|
||||||
|
TraceCollectionListener,
|
||||||
|
)
|
||||||
|
from crewai.events.listeners.tracing.utils import (
|
||||||
|
is_tracing_enabled,
|
||||||
|
)
|
||||||
|
from crewai.events.types.crew_events import (
|
||||||
|
CrewKickoffCompletedEvent,
|
||||||
|
CrewKickoffFailedEvent,
|
||||||
|
CrewKickoffStartedEvent,
|
||||||
|
CrewTestCompletedEvent,
|
||||||
|
CrewTestFailedEvent,
|
||||||
|
CrewTestStartedEvent,
|
||||||
|
CrewTrainCompletedEvent,
|
||||||
|
CrewTrainFailedEvent,
|
||||||
|
CrewTrainStartedEvent,
|
||||||
|
)
|
||||||
from crewai.flow.flow_trackable import FlowTrackable
|
from crewai.flow.flow_trackable import FlowTrackable
|
||||||
from crewai.knowledge.knowledge import Knowledge
|
from crewai.knowledge.knowledge import Knowledge
|
||||||
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
|
||||||
from crewai.llm import LLM, BaseLLM
|
from crewai.llm import LLM
|
||||||
|
from crewai.llms.base_llm import BaseLLM
|
||||||
from crewai.memory.entity.entity_memory import EntityMemory
|
from crewai.memory.entity.entity_memory import EntityMemory
|
||||||
from crewai.memory.external.external_memory import ExternalMemory
|
from crewai.memory.external.external_memory import ExternalMemory
|
||||||
from crewai.memory.long_term.long_term_memory import LongTermMemory
|
from crewai.memory.long_term.long_term_memory import LongTermMemory
|
||||||
@@ -57,29 +70,9 @@ from crewai.tools.base_tool import BaseTool, Tool
|
|||||||
from crewai.types.usage_metrics import UsageMetrics
|
from crewai.types.usage_metrics import UsageMetrics
|
||||||
from crewai.utilities import I18N, FileHandler, Logger, RPMController
|
from crewai.utilities import I18N, FileHandler, Logger, RPMController
|
||||||
from crewai.utilities.constants import NOT_SPECIFIED, TRAINING_DATA_FILE
|
from crewai.utilities.constants import NOT_SPECIFIED, TRAINING_DATA_FILE
|
||||||
|
from crewai.utilities.crew.models import CrewContext
|
||||||
from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator
|
from crewai.utilities.evaluators.crew_evaluator_handler import CrewEvaluator
|
||||||
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
|
from crewai.utilities.evaluators.task_evaluator import TaskEvaluator
|
||||||
from crewai.events.types.crew_events import (
|
|
||||||
CrewKickoffCompletedEvent,
|
|
||||||
CrewKickoffFailedEvent,
|
|
||||||
CrewKickoffStartedEvent,
|
|
||||||
CrewTestCompletedEvent,
|
|
||||||
CrewTestFailedEvent,
|
|
||||||
CrewTestStartedEvent,
|
|
||||||
CrewTrainCompletedEvent,
|
|
||||||
CrewTrainFailedEvent,
|
|
||||||
CrewTrainStartedEvent,
|
|
||||||
)
|
|
||||||
from crewai.events.event_bus import crewai_event_bus
|
|
||||||
from crewai.events.event_listener import EventListener
|
|
||||||
from crewai.events.listeners.tracing.trace_listener import (
|
|
||||||
TraceCollectionListener,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
from crewai.events.listeners.tracing.utils import (
|
|
||||||
is_tracing_enabled,
|
|
||||||
)
|
|
||||||
from crewai.utilities.formatter import (
|
from crewai.utilities.formatter import (
|
||||||
aggregate_raw_outputs_from_task_outputs,
|
aggregate_raw_outputs_from_task_outputs,
|
||||||
aggregate_raw_outputs_from_tasks,
|
aggregate_raw_outputs_from_tasks,
|
||||||
@@ -116,9 +109,12 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
planning: Plan the crew execution and add the plan to the crew.
|
planning: Plan the crew execution and add the plan to the crew.
|
||||||
chat_llm: The language model used for orchestrating chat interactions with the crew.
|
chat_llm: The language model used for orchestrating chat interactions with the crew.
|
||||||
security_config: Security configuration for the crew, including fingerprinting.
|
security_config: Security configuration for the crew, including fingerprinting.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
TODO: Improve the embedder type from dict[str, Any] to a more specific TypedDict or dataclass.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__hash__ = object.__hash__ # type: ignore
|
__hash__ = object.__hash__
|
||||||
_execution_span: Any = PrivateAttr()
|
_execution_span: Any = PrivateAttr()
|
||||||
_rpm_controller: RPMController = PrivateAttr()
|
_rpm_controller: RPMController = PrivateAttr()
|
||||||
_logger: Logger = PrivateAttr()
|
_logger: Logger = PrivateAttr()
|
||||||
@@ -130,7 +126,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
_external_memory: Optional[InstanceOf[ExternalMemory]] = PrivateAttr()
|
_external_memory: Optional[InstanceOf[ExternalMemory]] = PrivateAttr()
|
||||||
_train: Optional[bool] = PrivateAttr(default=False)
|
_train: Optional[bool] = PrivateAttr(default=False)
|
||||||
_train_iteration: Optional[int] = PrivateAttr()
|
_train_iteration: Optional[int] = PrivateAttr()
|
||||||
_inputs: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
_inputs: Optional[dict[str, Any]] = PrivateAttr(default=None)
|
||||||
_logging_color: str = PrivateAttr(
|
_logging_color: str = PrivateAttr(
|
||||||
default="bold_purple",
|
default="bold_purple",
|
||||||
)
|
)
|
||||||
@@ -140,8 +136,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
name: Optional[str] = Field(default="crew")
|
name: Optional[str] = Field(default="crew")
|
||||||
cache: bool = Field(default=True)
|
cache: bool = Field(default=True)
|
||||||
tasks: List[Task] = Field(default_factory=list)
|
tasks: list[Task] = Field(default_factory=list)
|
||||||
agents: List[BaseAgent] = Field(default_factory=list)
|
agents: list[BaseAgent] = Field(default_factory=list)
|
||||||
process: Process = Field(default=Process.sequential)
|
process: Process = Field(default=Process.sequential)
|
||||||
verbose: bool = Field(default=False)
|
verbose: bool = Field(default=False)
|
||||||
memory: bool = Field(
|
memory: bool = Field(
|
||||||
@@ -164,7 +160,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
default=None,
|
default=None,
|
||||||
description="An Instance of the ExternalMemory to be used by the Crew",
|
description="An Instance of the ExternalMemory to be used by the Crew",
|
||||||
)
|
)
|
||||||
embedder: Optional[dict] = Field(
|
embedder: Optional[dict[str, Any]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Configuration for the embedder to be used for the crew.",
|
description="Configuration for the embedder to be used for the crew.",
|
||||||
)
|
)
|
||||||
@@ -172,16 +168,16 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
default=None,
|
default=None,
|
||||||
description="Metrics for the LLM usage during all tasks execution.",
|
description="Metrics for the LLM usage during all tasks execution.",
|
||||||
)
|
)
|
||||||
manager_llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
|
manager_llm: Optional[str | InstanceOf[BaseLLM] | Any] = Field(
|
||||||
description="Language model that will run the agent.", default=None
|
description="Language model that will run the agent.", default=None
|
||||||
)
|
)
|
||||||
manager_agent: Optional[BaseAgent] = Field(
|
manager_agent: Optional[BaseAgent] = Field(
|
||||||
description="Custom agent that will be used as manager.", default=None
|
description="Custom agent that will be used as manager.", default=None
|
||||||
)
|
)
|
||||||
function_calling_llm: Optional[Union[str, InstanceOf[LLM], Any]] = Field(
|
function_calling_llm: Optional[str | InstanceOf[LLM] | Any] = Field(
|
||||||
description="Language model that will run the agent.", default=None
|
description="Language model that will run the agent.", default=None
|
||||||
)
|
)
|
||||||
config: Optional[Union[Json, Dict[str, Any]]] = Field(default=None)
|
config: Optional[Json[dict[str, Any]] | dict[str, Any]] = Field(default=None)
|
||||||
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
||||||
share_crew: Optional[bool] = Field(default=False)
|
share_crew: Optional[bool] = Field(default=False)
|
||||||
step_callback: Optional[Any] = Field(
|
step_callback: Optional[Any] = Field(
|
||||||
@@ -192,13 +188,13 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
default=None,
|
default=None,
|
||||||
description="Callback to be executed after each task for all agents execution.",
|
description="Callback to be executed after each task for all agents execution.",
|
||||||
)
|
)
|
||||||
before_kickoff_callbacks: List[
|
before_kickoff_callbacks: list[
|
||||||
Callable[[Optional[Dict[str, Any]]], Optional[Dict[str, Any]]]
|
Callable[[Optional[dict[str, Any]]], Optional[dict[str, Any]]]
|
||||||
] = Field(
|
] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
description="List of callbacks to be executed before crew kickoff. It may be used to adjust inputs before the crew is executed.",
|
description="List of callbacks to be executed before crew kickoff. It may be used to adjust inputs before the crew is executed.",
|
||||||
)
|
)
|
||||||
after_kickoff_callbacks: List[Callable[[CrewOutput], CrewOutput]] = Field(
|
after_kickoff_callbacks: list[Callable[[CrewOutput], CrewOutput]] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
description="List of callbacks to be executed after crew kickoff. It may be used to adjust the output of the crew.",
|
description="List of callbacks to be executed after crew kickoff. It may be used to adjust the output of the crew.",
|
||||||
)
|
)
|
||||||
@@ -210,7 +206,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
default=None,
|
default=None,
|
||||||
description="Path to the prompt json file to be used for the crew.",
|
description="Path to the prompt json file to be used for the crew.",
|
||||||
)
|
)
|
||||||
output_log_file: Optional[Union[bool, str]] = Field(
|
output_log_file: Optional[bool | str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Path to the log file to be saved",
|
description="Path to the log file to be saved",
|
||||||
)
|
)
|
||||||
@@ -218,23 +214,23 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
default=False,
|
default=False,
|
||||||
description="Plan the crew execution and add the plan to the crew.",
|
description="Plan the crew execution and add the plan to the crew.",
|
||||||
)
|
)
|
||||||
planning_llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
|
planning_llm: Optional[str | InstanceOf[BaseLLM] | Any] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Language model that will run the AgentPlanner if planning is True.",
|
description="Language model that will run the AgentPlanner if planning is True.",
|
||||||
)
|
)
|
||||||
task_execution_output_json_files: Optional[List[str]] = Field(
|
task_execution_output_json_files: Optional[list[str]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="List of file paths for task execution JSON files.",
|
description="List of file paths for task execution JSON files.",
|
||||||
)
|
)
|
||||||
execution_logs: List[Dict[str, Any]] = Field(
|
execution_logs: list[dict[str, Any]] = Field(
|
||||||
default=[],
|
default=[],
|
||||||
description="List of execution logs for tasks",
|
description="List of execution logs for tasks",
|
||||||
)
|
)
|
||||||
knowledge_sources: Optional[List[BaseKnowledgeSource]] = Field(
|
knowledge_sources: Optional[list[BaseKnowledgeSource]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="Knowledge sources for the crew. Add knowledge sources to the knowledge object.",
|
description="Knowledge sources for the crew. Add knowledge sources to the knowledge object.",
|
||||||
)
|
)
|
||||||
chat_llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
|
chat_llm: Optional[str | InstanceOf[BaseLLM] | Any] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="LLM used to handle chatting with the crew.",
|
description="LLM used to handle chatting with the crew.",
|
||||||
)
|
)
|
||||||
@@ -267,8 +263,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
@field_validator("config", mode="before")
|
@field_validator("config", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_config_type(
|
def check_config_type(
|
||||||
cls, v: Union[Json, Dict[str, Any]]
|
cls, v: Json[dict[str, Any]] | dict[str, Any]
|
||||||
) -> Union[Json, Dict[str, Any]]:
|
) -> Json[dict[str, Any]] | dict[str, Any]:
|
||||||
"""Validates that the config is a valid type.
|
"""Validates that the config is a valid type.
|
||||||
Args:
|
Args:
|
||||||
v: The config to be validated.
|
v: The config to be validated.
|
||||||
@@ -277,10 +273,10 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: Improve typing
|
# TODO: Improve typing
|
||||||
return json.loads(v) if isinstance(v, Json) else v # type: ignore
|
return json.loads(v) if isinstance(v, Json) else v
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def set_private_attrs(self) -> "Crew":
|
def set_private_attrs(self) -> Self:
|
||||||
"""Set private attributes."""
|
"""Set private attributes."""
|
||||||
|
|
||||||
self._cache_handler = CacheHandler()
|
self._cache_handler = CacheHandler()
|
||||||
@@ -300,7 +296,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _initialize_default_memories(self):
|
def _initialize_default_memories(self) -> None:
|
||||||
self._long_term_memory = self._long_term_memory or LongTermMemory()
|
self._long_term_memory = self._long_term_memory or LongTermMemory()
|
||||||
self._short_term_memory = self._short_term_memory or ShortTermMemory(
|
self._short_term_memory = self._short_term_memory or ShortTermMemory(
|
||||||
crew=self,
|
crew=self,
|
||||||
@@ -311,7 +307,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def create_crew_memory(self) -> "Crew":
|
def create_crew_memory(self) -> Self:
|
||||||
"""Initialize private memory attributes."""
|
"""Initialize private memory attributes."""
|
||||||
self._external_memory = (
|
self._external_memory = (
|
||||||
# External memory doesn’t support a default value since it was designed to be managed entirely externally
|
# External memory doesn’t support a default value since it was designed to be managed entirely externally
|
||||||
@@ -328,7 +324,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def create_crew_knowledge(self) -> "Crew":
|
def create_crew_knowledge(self) -> Self:
|
||||||
"""Create the knowledge for the crew."""
|
"""Create the knowledge for the crew."""
|
||||||
if self.knowledge_sources:
|
if self.knowledge_sources:
|
||||||
try:
|
try:
|
||||||
@@ -349,7 +345,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def check_manager_llm(self):
|
def check_manager_llm(self) -> Self:
|
||||||
"""Validates that the language model is set when using hierarchical process."""
|
"""Validates that the language model is set when using hierarchical process."""
|
||||||
if self.process == Process.hierarchical:
|
if self.process == Process.hierarchical:
|
||||||
if not self.manager_llm and not self.manager_agent:
|
if not self.manager_llm and not self.manager_agent:
|
||||||
@@ -371,7 +367,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def check_config(self):
|
def check_config(self) -> Self:
|
||||||
"""Validates that the crew is properly configured with agents and tasks."""
|
"""Validates that the crew is properly configured with agents and tasks."""
|
||||||
if not self.config and not self.tasks and not self.agents:
|
if not self.config and not self.tasks and not self.agents:
|
||||||
raise PydanticCustomError(
|
raise PydanticCustomError(
|
||||||
@@ -392,20 +388,20 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_tasks(self):
|
def validate_tasks(self) -> Self:
|
||||||
if self.process == Process.sequential:
|
if self.process == Process.sequential:
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
if task.agent is None:
|
if task.agent is None:
|
||||||
raise PydanticCustomError(
|
raise PydanticCustomError(
|
||||||
"missing_agent_in_task",
|
"missing_agent_in_task",
|
||||||
f"Sequential process error: Agent is missing in the task with the following description: {task.description}", # type: ignore # Argument of type "str" cannot be assigned to parameter "message_template" of type "LiteralString"
|
f"Sequential process error: Agent is missing in the task with the following description: {task.description}",
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_end_with_at_most_one_async_task(self):
|
def validate_end_with_at_most_one_async_task(self) -> Self:
|
||||||
"""Validates that the crew ends with at most one asynchronous task."""
|
"""Validates that the crew ends with at most one asynchronous task."""
|
||||||
final_async_task_count = 0
|
final_async_task_count = 0
|
||||||
|
|
||||||
@@ -426,7 +422,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_must_have_non_conditional_task(self) -> "Crew":
|
def validate_must_have_non_conditional_task(self) -> Self:
|
||||||
"""Ensure that a crew has at least one non-conditional task."""
|
"""Ensure that a crew has at least one non-conditional task."""
|
||||||
if not self.tasks:
|
if not self.tasks:
|
||||||
return self
|
return self
|
||||||
@@ -442,7 +438,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_first_task(self) -> "Crew":
|
def validate_first_task(self) -> Self:
|
||||||
"""Ensure the first task is not a ConditionalTask."""
|
"""Ensure the first task is not a ConditionalTask."""
|
||||||
if self.tasks and isinstance(self.tasks[0], ConditionalTask):
|
if self.tasks and isinstance(self.tasks[0], ConditionalTask):
|
||||||
raise PydanticCustomError(
|
raise PydanticCustomError(
|
||||||
@@ -453,19 +449,21 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_async_tasks_not_async(self) -> "Crew":
|
def validate_async_tasks_not_async(self) -> Self:
|
||||||
"""Ensure that ConditionalTask is not async."""
|
"""Ensure that ConditionalTask is not async."""
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
if task.async_execution and isinstance(task, ConditionalTask):
|
if task.async_execution and isinstance(task, ConditionalTask):
|
||||||
raise PydanticCustomError(
|
raise PydanticCustomError(
|
||||||
"invalid_async_conditional_task",
|
"invalid_async_conditional_task",
|
||||||
f"Conditional Task: {task.description} , cannot be executed asynchronously.", # type: ignore # Argument of type "str" cannot be assigned to parameter "message_template" of type "LiteralString"
|
f"Conditional Task: {task.description} , cannot be executed asynchronously.",
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_async_task_cannot_include_sequential_async_tasks_in_context(self):
|
def validate_async_task_cannot_include_sequential_async_tasks_in_context(
|
||||||
|
self,
|
||||||
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Validates that if a task is set to be executed asynchronously,
|
Validates that if a task is set to be executed asynchronously,
|
||||||
it cannot include other asynchronous tasks in its context unless
|
it cannot include other asynchronous tasks in its context unless
|
||||||
@@ -485,7 +483,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_context_no_future_tasks(self):
|
def validate_context_no_future_tasks(self) -> Self:
|
||||||
"""Validates that a task's context does not include future tasks."""
|
"""Validates that a task's context does not include future tasks."""
|
||||||
task_indices = {id(task): i for i, task in enumerate(self.tasks)}
|
task_indices = {id(task): i for i, task in enumerate(self.tasks)}
|
||||||
|
|
||||||
@@ -502,7 +500,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def key(self) -> str:
|
def key(self) -> str:
|
||||||
source: List[str] = [agent.key for agent in self.agents] + [
|
source: list[str] = [agent.key for agent in self.agents] + [
|
||||||
task.key for task in self.tasks
|
task.key for task in self.tasks
|
||||||
]
|
]
|
||||||
return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
|
return md5("|".join(source).encode(), usedforsecurity=False).hexdigest()
|
||||||
@@ -517,7 +515,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
"""
|
"""
|
||||||
return self.security_config.fingerprint
|
return self.security_config.fingerprint
|
||||||
|
|
||||||
def _setup_from_config(self):
|
def _setup_from_config(self) -> None:
|
||||||
assert self.config is not None, "Config should not be None."
|
assert self.config is not None, "Config should not be None."
|
||||||
|
|
||||||
"""Initializes agents and tasks from the provided config."""
|
"""Initializes agents and tasks from the provided config."""
|
||||||
@@ -530,7 +528,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
self.agents = [Agent(**agent) for agent in self.config["agents"]]
|
self.agents = [Agent(**agent) for agent in self.config["agents"]]
|
||||||
self.tasks = [self._create_task(task) for task in self.config["tasks"]]
|
self.tasks = [self._create_task(task) for task in self.config["tasks"]]
|
||||||
|
|
||||||
def _create_task(self, task_config: Dict[str, Any]) -> Task:
|
def _create_task(self, task_config: dict[str, Any]) -> Task:
|
||||||
"""Creates a task instance from its configuration.
|
"""Creates a task instance from its configuration.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -559,7 +557,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
CrewTrainingHandler(filename).initialize_file()
|
CrewTrainingHandler(filename).initialize_file()
|
||||||
|
|
||||||
def train(
|
def train(
|
||||||
self, n_iterations: int, filename: str, inputs: Optional[Dict[str, Any]] = None
|
self, n_iterations: int, filename: str, inputs: Optional[dict[str, Any]] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Trains the crew for a given number of iterations."""
|
"""Trains the crew for a given number of iterations."""
|
||||||
inputs = inputs or {}
|
inputs = inputs or {}
|
||||||
@@ -611,7 +609,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
def kickoff(
|
def kickoff(
|
||||||
self,
|
self,
|
||||||
inputs: Optional[Dict[str, Any]] = None,
|
inputs: Optional[dict[str, Any]] = None,
|
||||||
) -> CrewOutput:
|
) -> CrewOutput:
|
||||||
ctx = baggage.set_baggage(
|
ctx = baggage.set_baggage(
|
||||||
"crew_context", CrewContext(id=str(self.id), key=self.key)
|
"crew_context", CrewContext(id=str(self.id), key=self.key)
|
||||||
@@ -643,7 +641,6 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
for agent in self.agents:
|
for agent in self.agents:
|
||||||
agent.i18n = i18n
|
agent.i18n = i18n
|
||||||
# type: ignore[attr-defined] # Argument 1 to "_interpolate_inputs" of "Crew" has incompatible type "dict[str, Any] | None"; expected "dict[str, Any]"
|
|
||||||
agent.crew = self # type: ignore[attr-defined]
|
agent.crew = self # type: ignore[attr-defined]
|
||||||
agent.set_knowledge(crew_embedder=self.embedder)
|
agent.set_knowledge(crew_embedder=self.embedder)
|
||||||
# TODO: Create an AgentFunctionCalling protocol for future refactoring
|
# TODO: Create an AgentFunctionCalling protocol for future refactoring
|
||||||
@@ -682,9 +679,9 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
finally:
|
finally:
|
||||||
detach(token)
|
detach(token)
|
||||||
|
|
||||||
def kickoff_for_each(self, inputs: List[Dict[str, Any]]) -> List[CrewOutput]:
|
def kickoff_for_each(self, inputs: list[dict[str, Any]]) -> list[CrewOutput]:
|
||||||
"""Executes the Crew's workflow for each input in the list and aggregates results."""
|
"""Executes the Crew's workflow for each input in the list and aggregates results."""
|
||||||
results: List[CrewOutput] = []
|
results: list[CrewOutput] = []
|
||||||
|
|
||||||
# Initialize the parent crew's usage metrics
|
# Initialize the parent crew's usage metrics
|
||||||
total_usage_metrics = UsageMetrics()
|
total_usage_metrics = UsageMetrics()
|
||||||
@@ -704,16 +701,18 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
async def kickoff_async(
|
async def kickoff_async(
|
||||||
self, inputs: Optional[Dict[str, Any]] = None
|
self, inputs: Optional[dict[str, Any]] = None
|
||||||
) -> CrewOutput:
|
) -> CrewOutput:
|
||||||
"""Asynchronous kickoff method to start the crew execution."""
|
"""Asynchronous kickoff method to start the crew execution."""
|
||||||
inputs = inputs or {}
|
inputs = inputs or {}
|
||||||
return await asyncio.to_thread(self.kickoff, inputs)
|
return await asyncio.to_thread(self.kickoff, inputs)
|
||||||
|
|
||||||
async def kickoff_for_each_async(self, inputs: List[Dict]) -> List[CrewOutput]:
|
async def kickoff_for_each_async(
|
||||||
|
self, inputs: list[dict[str, Any]]
|
||||||
|
) -> list[CrewOutput]:
|
||||||
crew_copies = [self.copy() for _ in inputs]
|
crew_copies = [self.copy() for _ in inputs]
|
||||||
|
|
||||||
async def run_crew(crew, input_data):
|
async def run_crew(crew: Self, input_data: dict[str, Any]) -> CrewOutput:
|
||||||
return await crew.kickoff_async(inputs=input_data)
|
return await crew.kickoff_async(inputs=input_data)
|
||||||
|
|
||||||
tasks = [
|
tasks = [
|
||||||
@@ -732,7 +731,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
self._task_output_handler.reset()
|
self._task_output_handler.reset()
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def _handle_crew_planning(self):
|
def _handle_crew_planning(self) -> None:
|
||||||
"""Handles the Crew planning."""
|
"""Handles the Crew planning."""
|
||||||
self._logger.log("info", "Planning the crew execution")
|
self._logger.log("info", "Planning the crew execution")
|
||||||
result = CrewPlanner(
|
result = CrewPlanner(
|
||||||
@@ -748,7 +747,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
output: TaskOutput,
|
output: TaskOutput,
|
||||||
task_index: int,
|
task_index: int,
|
||||||
was_replayed: bool = False,
|
was_replayed: bool = False,
|
||||||
):
|
) -> None:
|
||||||
if self._inputs:
|
if self._inputs:
|
||||||
inputs = self._inputs
|
inputs = self._inputs
|
||||||
else:
|
else:
|
||||||
@@ -780,7 +779,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
self._create_manager_agent()
|
self._create_manager_agent()
|
||||||
return self._execute_tasks(self.tasks)
|
return self._execute_tasks(self.tasks)
|
||||||
|
|
||||||
def _create_manager_agent(self):
|
def _create_manager_agent(self) -> None:
|
||||||
i18n = I18N(prompt_file=self.prompt_file)
|
i18n = I18N(prompt_file=self.prompt_file)
|
||||||
if self.manager_agent is not None:
|
if self.manager_agent is not None:
|
||||||
self.manager_agent.allow_delegation = True
|
self.manager_agent.allow_delegation = True
|
||||||
@@ -807,7 +806,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
def _execute_tasks(
|
def _execute_tasks(
|
||||||
self,
|
self,
|
||||||
tasks: List[Task],
|
tasks: list[Task],
|
||||||
start_index: Optional[int] = 0,
|
start_index: Optional[int] = 0,
|
||||||
was_replayed: bool = False,
|
was_replayed: bool = False,
|
||||||
) -> CrewOutput:
|
) -> CrewOutput:
|
||||||
@@ -821,8 +820,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
CrewOutput: Final output of the crew
|
CrewOutput: Final output of the crew
|
||||||
"""
|
"""
|
||||||
|
|
||||||
task_outputs: List[TaskOutput] = []
|
task_outputs: list[TaskOutput] = []
|
||||||
futures: List[Tuple[Task, Future[TaskOutput], int]] = []
|
futures: list[tuple[Task, Future[TaskOutput], int]] = []
|
||||||
last_sync_output: Optional[TaskOutput] = None
|
last_sync_output: Optional[TaskOutput] = None
|
||||||
|
|
||||||
for task_index, task in enumerate(tasks):
|
for task_index, task in enumerate(tasks):
|
||||||
@@ -847,7 +846,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
tools_for_task = self._prepare_tools(
|
tools_for_task = self._prepare_tools(
|
||||||
agent_to_use,
|
agent_to_use,
|
||||||
task,
|
task,
|
||||||
cast(Union[List[Tool], List[BaseTool]], tools_for_task),
|
cast(list[Tool] | list[BaseTool], tools_for_task),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._log_task_start(task, agent_to_use.role)
|
self._log_task_start(task, agent_to_use.role)
|
||||||
@@ -867,7 +866,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
future = task.execute_async(
|
future = task.execute_async(
|
||||||
agent=agent_to_use,
|
agent=agent_to_use,
|
||||||
context=context,
|
context=context,
|
||||||
tools=cast(List[BaseTool], tools_for_task),
|
tools=tools_for_task,
|
||||||
)
|
)
|
||||||
futures.append((task, future, task_index))
|
futures.append((task, future, task_index))
|
||||||
else:
|
else:
|
||||||
@@ -879,7 +878,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
task_output = task.execute_sync(
|
task_output = task.execute_sync(
|
||||||
agent=agent_to_use,
|
agent=agent_to_use,
|
||||||
context=context,
|
context=context,
|
||||||
tools=cast(List[BaseTool], tools_for_task),
|
tools=tools_for_task,
|
||||||
)
|
)
|
||||||
task_outputs.append(task_output)
|
task_outputs.append(task_output)
|
||||||
self._process_task_result(task, task_output)
|
self._process_task_result(task, task_output)
|
||||||
@@ -893,8 +892,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
def _handle_conditional_task(
|
def _handle_conditional_task(
|
||||||
self,
|
self,
|
||||||
task: ConditionalTask,
|
task: ConditionalTask,
|
||||||
task_outputs: List[TaskOutput],
|
task_outputs: list[TaskOutput],
|
||||||
futures: List[Tuple[Task, Future[TaskOutput], int]],
|
futures: list[tuple[Task, Future[TaskOutput], int]],
|
||||||
task_index: int,
|
task_index: int,
|
||||||
was_replayed: bool,
|
was_replayed: bool,
|
||||||
) -> Optional[TaskOutput]:
|
) -> Optional[TaskOutput]:
|
||||||
@@ -909,7 +908,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
f"Skipping conditional task: {task.description}",
|
f"Skipping conditional task: {task.description}",
|
||||||
color="yellow",
|
color="yellow",
|
||||||
)
|
)
|
||||||
skipped_task_output = task.get_skipped_task_output()
|
skipped_task_output = cast(TaskOutput, task.get_skipped_task_output())
|
||||||
|
|
||||||
if not was_replayed:
|
if not was_replayed:
|
||||||
self._store_execution_log(task, skipped_task_output, task_index)
|
self._store_execution_log(task, skipped_task_output, task_index)
|
||||||
@@ -917,8 +916,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _prepare_tools(
|
def _prepare_tools(
|
||||||
self, agent: BaseAgent, task: Task, tools: Union[List[Tool], List[BaseTool]]
|
self, agent: BaseAgent, task: Task, tools: list[Tool] | list[BaseTool]
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
# Add delegation tools if agent allows delegation
|
# Add delegation tools if agent allows delegation
|
||||||
if hasattr(agent, "allow_delegation") and getattr(
|
if hasattr(agent, "allow_delegation") and getattr(
|
||||||
agent, "allow_delegation", False
|
agent, "allow_delegation", False
|
||||||
@@ -948,7 +947,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
tools = self._add_multimodal_tools(agent, tools)
|
tools = self._add_multimodal_tools(agent, tools)
|
||||||
|
|
||||||
# Return a List[BaseTool] which is compatible with both Task.execute_sync and Task.execute_async
|
# Return a List[BaseTool] which is compatible with both Task.execute_sync and Task.execute_async
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _get_agent_to_use(self, task: Task) -> Optional[BaseAgent]:
|
def _get_agent_to_use(self, task: Task) -> Optional[BaseAgent]:
|
||||||
if self.process == Process.hierarchical:
|
if self.process == Process.hierarchical:
|
||||||
@@ -957,12 +956,12 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
def _merge_tools(
|
def _merge_tools(
|
||||||
self,
|
self,
|
||||||
existing_tools: Union[List[Tool], List[BaseTool]],
|
existing_tools: list[Tool] | list[BaseTool],
|
||||||
new_tools: Union[List[Tool], List[BaseTool]],
|
new_tools: list[Tool] | list[BaseTool],
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
"""Merge new tools into existing tools list, avoiding duplicates by tool name."""
|
"""Merge new tools into existing tools list, avoiding duplicates by tool name."""
|
||||||
if not new_tools:
|
if not new_tools:
|
||||||
return cast(List[BaseTool], existing_tools)
|
return cast(list[BaseTool], existing_tools)
|
||||||
|
|
||||||
# Create mapping of tool names to new tools
|
# Create mapping of tool names to new tools
|
||||||
new_tool_map = {tool.name: tool for tool in new_tools}
|
new_tool_map = {tool.name: tool for tool in new_tools}
|
||||||
@@ -973,41 +972,41 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
# Add all new tools
|
# Add all new tools
|
||||||
tools.extend(new_tools)
|
tools.extend(new_tools)
|
||||||
|
|
||||||
return cast(List[BaseTool], tools)
|
return tools
|
||||||
|
|
||||||
def _inject_delegation_tools(
|
def _inject_delegation_tools(
|
||||||
self,
|
self,
|
||||||
tools: Union[List[Tool], List[BaseTool]],
|
tools: list[Tool] | list[BaseTool],
|
||||||
task_agent: BaseAgent,
|
task_agent: BaseAgent,
|
||||||
agents: List[BaseAgent],
|
agents: list[BaseAgent],
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
if hasattr(task_agent, "get_delegation_tools"):
|
if hasattr(task_agent, "get_delegation_tools"):
|
||||||
delegation_tools = task_agent.get_delegation_tools(agents)
|
delegation_tools = task_agent.get_delegation_tools(agents)
|
||||||
# Cast delegation_tools to the expected type for _merge_tools
|
# Cast delegation_tools to the expected type for _merge_tools
|
||||||
return self._merge_tools(tools, cast(List[BaseTool], delegation_tools))
|
return self._merge_tools(tools, cast(list[BaseTool], delegation_tools))
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _add_multimodal_tools(
|
def _add_multimodal_tools(
|
||||||
self, agent: BaseAgent, tools: Union[List[Tool], List[BaseTool]]
|
self, agent: BaseAgent, tools: list[Tool] | list[BaseTool]
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
if hasattr(agent, "get_multimodal_tools"):
|
if hasattr(agent, "get_multimodal_tools"):
|
||||||
multimodal_tools = agent.get_multimodal_tools()
|
multimodal_tools = agent.get_multimodal_tools()
|
||||||
# Cast multimodal_tools to the expected type for _merge_tools
|
# Cast multimodal_tools to the expected type for _merge_tools
|
||||||
return self._merge_tools(tools, cast(List[BaseTool], multimodal_tools))
|
return self._merge_tools(tools, cast(list[BaseTool], multimodal_tools))
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _add_code_execution_tools(
|
def _add_code_execution_tools(
|
||||||
self, agent: BaseAgent, tools: Union[List[Tool], List[BaseTool]]
|
self, agent: BaseAgent, tools: list[Tool] | list[BaseTool]
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
if hasattr(agent, "get_code_execution_tools"):
|
if hasattr(agent, "get_code_execution_tools"):
|
||||||
code_tools = agent.get_code_execution_tools()
|
code_tools = agent.get_code_execution_tools()
|
||||||
# Cast code_tools to the expected type for _merge_tools
|
# Cast code_tools to the expected type for _merge_tools
|
||||||
return self._merge_tools(tools, cast(List[BaseTool], code_tools))
|
return self._merge_tools(tools, cast(list[BaseTool], code_tools))
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _add_delegation_tools(
|
def _add_delegation_tools(
|
||||||
self, task: Task, tools: Union[List[Tool], List[BaseTool]]
|
self, task: Task, tools: list[Tool] | list[BaseTool]
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
agents_for_delegation = [agent for agent in self.agents if agent != task.agent]
|
agents_for_delegation = [agent for agent in self.agents if agent != task.agent]
|
||||||
if len(self.agents) > 1 and len(agents_for_delegation) > 0 and task.agent:
|
if len(self.agents) > 1 and len(agents_for_delegation) > 0 and task.agent:
|
||||||
if not tools:
|
if not tools:
|
||||||
@@ -1015,17 +1014,17 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
tools = self._inject_delegation_tools(
|
tools = self._inject_delegation_tools(
|
||||||
tools, task.agent, agents_for_delegation
|
tools, task.agent, agents_for_delegation
|
||||||
)
|
)
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _log_task_start(self, task: Task, role: str = "None"):
|
def _log_task_start(self, task: Task, role: str = "None") -> None:
|
||||||
if self.output_log_file:
|
if self.output_log_file:
|
||||||
self._file_handler.log(
|
self._file_handler.log(
|
||||||
task_name=task.name, task=task.description, agent=role, status="started"
|
task_name=task.name, task=task.description, agent=role, status="started"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_manager_tools(
|
def _update_manager_tools(
|
||||||
self, task: Task, tools: Union[List[Tool], List[BaseTool]]
|
self, task: Task, tools: list[Tool] | list[BaseTool]
|
||||||
) -> List[BaseTool]:
|
) -> list[BaseTool]:
|
||||||
if self.manager_agent:
|
if self.manager_agent:
|
||||||
if task.agent:
|
if task.agent:
|
||||||
tools = self._inject_delegation_tools(tools, task.agent, [task.agent])
|
tools = self._inject_delegation_tools(tools, task.agent, [task.agent])
|
||||||
@@ -1033,9 +1032,9 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
tools = self._inject_delegation_tools(
|
tools = self._inject_delegation_tools(
|
||||||
tools, self.manager_agent, self.agents
|
tools, self.manager_agent, self.agents
|
||||||
)
|
)
|
||||||
return cast(List[BaseTool], tools)
|
return cast(list[BaseTool], tools)
|
||||||
|
|
||||||
def _get_context(self, task: Task, task_outputs: List[TaskOutput]) -> str:
|
def _get_context(self, task: Task, task_outputs: list[TaskOutput]) -> str:
|
||||||
if not task.context:
|
if not task.context:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -1057,7 +1056,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
output=output.raw,
|
output=output.raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_crew_output(self, task_outputs: List[TaskOutput]) -> CrewOutput:
|
def _create_crew_output(self, task_outputs: list[TaskOutput]) -> CrewOutput:
|
||||||
if not task_outputs:
|
if not task_outputs:
|
||||||
raise ValueError("No task outputs available to create crew output.")
|
raise ValueError("No task outputs available to create crew output.")
|
||||||
|
|
||||||
@@ -1088,10 +1087,10 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
def _process_async_tasks(
|
def _process_async_tasks(
|
||||||
self,
|
self,
|
||||||
futures: List[Tuple[Task, Future[TaskOutput], int]],
|
futures: list[tuple[Task, Future[TaskOutput], int]],
|
||||||
was_replayed: bool = False,
|
was_replayed: bool = False,
|
||||||
) -> List[TaskOutput]:
|
) -> list[TaskOutput]:
|
||||||
task_outputs: List[TaskOutput] = []
|
task_outputs: list[TaskOutput] = []
|
||||||
for future_task, future, task_index in futures:
|
for future_task, future, task_index in futures:
|
||||||
task_output = future.result()
|
task_output = future.result()
|
||||||
task_outputs.append(task_output)
|
task_outputs.append(task_output)
|
||||||
@@ -1102,7 +1101,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return task_outputs
|
return task_outputs
|
||||||
|
|
||||||
def _find_task_index(
|
def _find_task_index(
|
||||||
self, task_id: str, stored_outputs: List[Any]
|
self, task_id: str, stored_outputs: list[Any]
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
return next(
|
return next(
|
||||||
(
|
(
|
||||||
@@ -1114,7 +1113,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def replay(
|
def replay(
|
||||||
self, task_id: str, inputs: Optional[Dict[str, Any]] = None
|
self, task_id: str, inputs: Optional[dict[str, Any]] = None
|
||||||
) -> CrewOutput:
|
) -> CrewOutput:
|
||||||
stored_outputs = self._task_output_handler.load()
|
stored_outputs = self._task_output_handler.load()
|
||||||
if not stored_outputs:
|
if not stored_outputs:
|
||||||
@@ -1155,15 +1154,15 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def query_knowledge(
|
def query_knowledge(
|
||||||
self, query: List[str], results_limit: int = 3, score_threshold: float = 0.35
|
self, query: list[str], results_limit: int = 3, score_threshold: float = 0.35
|
||||||
) -> Union[List[Dict[str, Any]], None]:
|
) -> list[dict[str, Any]] | None:
|
||||||
if self.knowledge:
|
if self.knowledge:
|
||||||
return self.knowledge.query(
|
return self.knowledge.query(
|
||||||
query, results_limit=results_limit, score_threshold=score_threshold
|
query, results_limit=results_limit, score_threshold=score_threshold
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def fetch_inputs(self) -> Set[str]:
|
def fetch_inputs(self) -> set[str]:
|
||||||
"""
|
"""
|
||||||
Gathers placeholders (e.g., {something}) referenced in tasks or agents.
|
Gathers placeholders (e.g., {something}) referenced in tasks or agents.
|
||||||
Scans each task's 'description' + 'expected_output', and each agent's
|
Scans each task's 'description' + 'expected_output', and each agent's
|
||||||
@@ -1172,7 +1171,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
Returns a set of all discovered placeholder names.
|
Returns a set of all discovered placeholder names.
|
||||||
"""
|
"""
|
||||||
placeholder_pattern = re.compile(r"\{(.+?)\}")
|
placeholder_pattern = re.compile(r"\{(.+?)\}")
|
||||||
required_inputs: Set[str] = set()
|
required_inputs: set[str] = set()
|
||||||
|
|
||||||
# Scan tasks for inputs
|
# Scan tasks for inputs
|
||||||
for task in self.tasks:
|
for task in self.tasks:
|
||||||
@@ -1188,7 +1187,18 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
return required_inputs
|
return required_inputs
|
||||||
|
|
||||||
def copy(self):
|
def copy(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
include: Optional[
|
||||||
|
Set[int] | Set[str] | Mapping[int, Any] | Mapping[str, Any]
|
||||||
|
] = None,
|
||||||
|
exclude: Optional[
|
||||||
|
Set[int] | Set[str] | Mapping[int, Any] | Mapping[str, Any]
|
||||||
|
] = None,
|
||||||
|
update: Optional[dict[str, Any]] = None,
|
||||||
|
deep: bool = True,
|
||||||
|
) -> "Crew":
|
||||||
"""
|
"""
|
||||||
Creates a deep copy of the Crew instance.
|
Creates a deep copy of the Crew instance.
|
||||||
|
|
||||||
@@ -1219,7 +1229,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
manager_agent = self.manager_agent.copy() if self.manager_agent else None
|
manager_agent = self.manager_agent.copy() if self.manager_agent else None
|
||||||
manager_llm = shallow_copy(self.manager_llm) if self.manager_llm else None
|
manager_llm = shallow_copy(self.manager_llm) if self.manager_llm else None
|
||||||
|
|
||||||
task_mapping = {}
|
task_mapping: dict[str, Task] = {}
|
||||||
|
|
||||||
cloned_tasks = []
|
cloned_tasks = []
|
||||||
existing_knowledge_sources = shallow_copy(self.knowledge_sources)
|
existing_knowledge_sources = shallow_copy(self.knowledge_sources)
|
||||||
@@ -1274,16 +1284,10 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
if not task.callback:
|
if not task.callback:
|
||||||
task.callback = self.task_callback
|
task.callback = self.task_callback
|
||||||
|
|
||||||
def _interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
|
def _interpolate_inputs(self, inputs: dict[str, Any]) -> None:
|
||||||
"""Interpolates the inputs in the tasks and agents."""
|
"""Interpolates the inputs in the tasks and agents."""
|
||||||
[
|
for task in self.tasks:
|
||||||
task.interpolate_inputs_and_add_conversation_history(
|
task.interpolate_inputs_and_add_conversation_history(inputs)
|
||||||
# type: ignore # "interpolate_inputs" of "Task" does not return a value (it only ever returns None)
|
|
||||||
inputs
|
|
||||||
)
|
|
||||||
for task in self.tasks
|
|
||||||
]
|
|
||||||
# type: ignore # "interpolate_inputs" of "Agent" does not return a value (it only ever returns None)
|
|
||||||
for agent in self.agents:
|
for agent in self.agents:
|
||||||
agent.interpolate_inputs(inputs)
|
agent.interpolate_inputs(inputs)
|
||||||
|
|
||||||
@@ -1307,8 +1311,8 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
def test(
|
def test(
|
||||||
self,
|
self,
|
||||||
n_iterations: int,
|
n_iterations: int,
|
||||||
eval_llm: Union[str, InstanceOf[BaseLLM]],
|
eval_llm: str | InstanceOf[BaseLLM],
|
||||||
inputs: Optional[Dict[str, Any]] = None,
|
inputs: Optional[dict[str, Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test and evaluate the Crew with the given inputs for n iterations concurrently using concurrent.futures."""
|
"""Test and evaluate the Crew with the given inputs for n iterations concurrently using concurrent.futures."""
|
||||||
try:
|
try:
|
||||||
@@ -1349,7 +1353,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"Crew(id={self.id}, process={self.process}, number_of_agents={len(self.agents)}, number_of_tasks={len(self.tasks)})"
|
return f"Crew(id={self.id}, process={self.process}, number_of_agents={len(self.agents)}, number_of_tasks={len(self.tasks)})"
|
||||||
|
|
||||||
def reset_memories(self, command_type: str) -> None:
|
def reset_memories(self, command_type: str) -> None:
|
||||||
@@ -1401,7 +1405,9 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
if (system := config.get("system")) is not None:
|
if (system := config.get("system")) is not None:
|
||||||
name = config.get("name")
|
name = config.get("name")
|
||||||
try:
|
try:
|
||||||
reset_fn: Callable = cast(Callable, config.get("reset"))
|
reset_fn: Callable[..., None] = cast(
|
||||||
|
Callable[..., None], config.get("reset")
|
||||||
|
)
|
||||||
reset_fn(system)
|
reset_fn(system)
|
||||||
self._logger.log(
|
self._logger.log(
|
||||||
"info",
|
"info",
|
||||||
@@ -1430,7 +1436,9 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
raise RuntimeError(f"{name} memory system is not initialized")
|
raise RuntimeError(f"{name} memory system is not initialized")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reset_fn: Callable = cast(Callable, config.get("reset"))
|
reset_fn: Callable[..., None] = cast(
|
||||||
|
Callable[..., None], config.get("reset")
|
||||||
|
)
|
||||||
reset_fn(system)
|
reset_fn(system)
|
||||||
self._logger.log(
|
self._logger.log(
|
||||||
"info",
|
"info",
|
||||||
@@ -1441,18 +1449,18 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
f"[Crew ({self.name if self.name else self.id})] Failed to reset {name} memory: {str(e)}"
|
f"[Crew ({self.name if self.name else self.id})] Failed to reset {name} memory: {str(e)}"
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
def _get_memory_systems(self):
|
def _get_memory_systems(self) -> dict[str, dict[str, Any]]:
|
||||||
"""Get all available memory systems with their configuration.
|
"""Get all available memory systems with their configuration.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict containing all memory systems with their reset functions and display names.
|
Dict containing all memory systems with their reset functions and display names.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def default_reset(memory):
|
def default_reset(memory: Any) -> None:
|
||||||
return memory.reset()
|
memory.reset()
|
||||||
|
|
||||||
def knowledge_reset(memory):
|
def knowledge_reset(memory: Any) -> None:
|
||||||
return self.reset_knowledge(memory)
|
self.reset_knowledge(memory)
|
||||||
|
|
||||||
# Get knowledge for agents
|
# Get knowledge for agents
|
||||||
agent_knowledges = [
|
agent_knowledges = [
|
||||||
@@ -1506,12 +1514,12 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def reset_knowledge(self, knowledges: List[Knowledge]) -> None:
|
def reset_knowledge(self, knowledges: list[Knowledge]) -> None:
|
||||||
"""Reset crew and agent knowledge storage."""
|
"""Reset crew and agent knowledge storage."""
|
||||||
for ks in knowledges:
|
for ks in knowledges:
|
||||||
ks.reset()
|
ks.reset()
|
||||||
|
|
||||||
def _set_allow_crewai_trigger_context_for_first_task(self):
|
def _set_allow_crewai_trigger_context_for_first_task(self) -> None:
|
||||||
crewai_trigger_payload = self._inputs and self._inputs.get(
|
crewai_trigger_payload = self._inputs and self._inputs.get(
|
||||||
"crewai_trigger_payload"
|
"crewai_trigger_payload"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,35 +1,25 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import uuid
|
import uuid
|
||||||
|
from collections.abc import Callable
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
|
||||||
Dict,
|
|
||||||
List,
|
|
||||||
Optional,
|
Optional,
|
||||||
Tuple,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
cast,
|
cast,
|
||||||
get_args,
|
get_args,
|
||||||
get_origin,
|
get_origin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from typing import Self
|
|
||||||
except ImportError:
|
|
||||||
from typing_extensions import Self
|
|
||||||
|
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
UUID4,
|
UUID4,
|
||||||
BaseModel,
|
BaseModel,
|
||||||
Field,
|
Field,
|
||||||
InstanceOf,
|
InstanceOf,
|
||||||
PrivateAttr,
|
PrivateAttr,
|
||||||
model_validator,
|
|
||||||
field_validator,
|
field_validator,
|
||||||
|
model_validator,
|
||||||
)
|
)
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||||
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
|
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
|
||||||
@@ -39,12 +29,19 @@ from crewai.agents.parser import (
|
|||||||
AgentFinish,
|
AgentFinish,
|
||||||
OutputParserException,
|
OutputParserException,
|
||||||
)
|
)
|
||||||
|
from crewai.events.event_bus import crewai_event_bus
|
||||||
|
from crewai.events.types.agent_events import (
|
||||||
|
LiteAgentExecutionCompletedEvent,
|
||||||
|
LiteAgentExecutionErrorEvent,
|
||||||
|
LiteAgentExecutionStartedEvent,
|
||||||
|
)
|
||||||
|
from crewai.events.types.logging_events import AgentLogsExecutionEvent
|
||||||
from crewai.flow.flow_trackable import FlowTrackable
|
from crewai.flow.flow_trackable import FlowTrackable
|
||||||
from crewai.llm import LLM, BaseLLM
|
from crewai.llm import LLM
|
||||||
|
from crewai.llms.base_llm import BaseLLM
|
||||||
from crewai.tools.base_tool import BaseTool
|
from crewai.tools.base_tool import BaseTool
|
||||||
from crewai.tools.structured_tool import CrewStructuredTool
|
from crewai.tools.structured_tool import CrewStructuredTool
|
||||||
from crewai.utilities import I18N
|
from crewai.utilities import I18N
|
||||||
from crewai.utilities.guardrail import process_guardrail
|
|
||||||
from crewai.utilities.agent_utils import (
|
from crewai.utilities.agent_utils import (
|
||||||
enforce_rpm_limit,
|
enforce_rpm_limit,
|
||||||
format_message_for_llm,
|
format_message_for_llm,
|
||||||
@@ -62,14 +59,7 @@ from crewai.utilities.agent_utils import (
|
|||||||
render_text_description_and_args,
|
render_text_description_and_args,
|
||||||
)
|
)
|
||||||
from crewai.utilities.converter import generate_model_description
|
from crewai.utilities.converter import generate_model_description
|
||||||
from crewai.events.types.logging_events import AgentLogsExecutionEvent
|
from crewai.utilities.guardrail import process_guardrail
|
||||||
from crewai.events.types.agent_events import (
|
|
||||||
LiteAgentExecutionCompletedEvent,
|
|
||||||
LiteAgentExecutionErrorEvent,
|
|
||||||
LiteAgentExecutionStartedEvent,
|
|
||||||
)
|
|
||||||
from crewai.events.event_bus import crewai_event_bus
|
|
||||||
|
|
||||||
from crewai.utilities.llm_utils import create_llm
|
from crewai.utilities.llm_utils import create_llm
|
||||||
from crewai.utilities.printer import Printer
|
from crewai.utilities.printer import Printer
|
||||||
from crewai.utilities.token_counter_callback import TokenCalcHandler
|
from crewai.utilities.token_counter_callback import TokenCalcHandler
|
||||||
@@ -86,11 +76,11 @@ class LiteAgentOutput(BaseModel):
|
|||||||
description="Pydantic output of the agent", default=None
|
description="Pydantic output of the agent", default=None
|
||||||
)
|
)
|
||||||
agent_role: str = Field(description="Role of the agent that produced this output")
|
agent_role: str = Field(description="Role of the agent that produced this output")
|
||||||
usage_metrics: Optional[Dict[str, Any]] = Field(
|
usage_metrics: Optional[dict[str, Any]] = Field(
|
||||||
description="Token usage metrics for this execution", default=None
|
description="Token usage metrics for this execution", default=None
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
"""Convert pydantic_output to a dictionary."""
|
"""Convert pydantic_output to a dictionary."""
|
||||||
if self.pydantic:
|
if self.pydantic:
|
||||||
return self.pydantic.model_dump()
|
return self.pydantic.model_dump()
|
||||||
@@ -130,10 +120,10 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
role: str = Field(description="Role of the agent")
|
role: str = Field(description="Role of the agent")
|
||||||
goal: str = Field(description="Goal of the agent")
|
goal: str = Field(description="Goal of the agent")
|
||||||
backstory: str = Field(description="Backstory of the agent")
|
backstory: str = Field(description="Backstory of the agent")
|
||||||
llm: Optional[Union[str, InstanceOf[BaseLLM], Any]] = Field(
|
llm: Optional[str | InstanceOf[BaseLLM] | Any] = Field(
|
||||||
default=None, description="Language model that will run the agent"
|
default=None, description="Language model that will run the agent"
|
||||||
)
|
)
|
||||||
tools: List[BaseTool] = Field(
|
tools: list[BaseTool] = Field(
|
||||||
default_factory=list, description="Tools at agent's disposal"
|
default_factory=list, description="Tools at agent's disposal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -159,29 +149,27 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
|
i18n: I18N = Field(default=I18N(), description="Internationalization settings.")
|
||||||
|
|
||||||
# Output and Formatting Properties
|
# Output and Formatting Properties
|
||||||
response_format: Optional[Type[BaseModel]] = Field(
|
response_format: Optional[type[BaseModel]] = Field(
|
||||||
default=None, description="Pydantic model for structured output"
|
default=None, description="Pydantic model for structured output"
|
||||||
)
|
)
|
||||||
verbose: bool = Field(
|
verbose: bool = Field(
|
||||||
default=False, description="Whether to print execution details"
|
default=False, description="Whether to print execution details"
|
||||||
)
|
)
|
||||||
callbacks: List[Callable] = Field(
|
callbacks: list[Callable[..., Any]] = Field(
|
||||||
default=[], description="Callbacks to be used for the agent"
|
default=[], description="Callbacks to be used for the agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Guardrail Properties
|
# Guardrail Properties
|
||||||
guardrail: Optional[Union[Callable[[LiteAgentOutput], Tuple[bool, Any]], str]] = (
|
guardrail: Optional[Callable[[LiteAgentOutput], tuple[bool, Any]] | str] = Field(
|
||||||
Field(
|
default=None,
|
||||||
default=None,
|
description="Function or string description of a guardrail to validate agent output",
|
||||||
description="Function or string description of a guardrail to validate agent output",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
guardrail_max_retries: int = Field(
|
guardrail_max_retries: int = Field(
|
||||||
default=3, description="Maximum number of retries when guardrail fails"
|
default=3, description="Maximum number of retries when guardrail fails"
|
||||||
)
|
)
|
||||||
|
|
||||||
# State and Results
|
# State and Results
|
||||||
tools_results: List[Dict[str, Any]] = Field(
|
tools_results: list[dict[str, Any]] = Field(
|
||||||
default=[], description="Results of the tools used by the agent."
|
default=[], description="Results of the tools used by the agent."
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -190,18 +178,20 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
default=None, description="Reference to the agent that created this LiteAgent"
|
default=None, description="Reference to the agent that created this LiteAgent"
|
||||||
)
|
)
|
||||||
# Private Attributes
|
# Private Attributes
|
||||||
_parsed_tools: List[CrewStructuredTool] = PrivateAttr(default_factory=list)
|
_parsed_tools: list[CrewStructuredTool] = PrivateAttr(default_factory=list)
|
||||||
_token_process: TokenProcess = PrivateAttr(default_factory=TokenProcess)
|
_token_process: TokenProcess = PrivateAttr(default_factory=TokenProcess)
|
||||||
_cache_handler: CacheHandler = PrivateAttr(default_factory=CacheHandler)
|
_cache_handler: CacheHandler = PrivateAttr(default_factory=CacheHandler)
|
||||||
_key: str = PrivateAttr(default_factory=lambda: str(uuid.uuid4()))
|
_key: str = PrivateAttr(default_factory=lambda: str(uuid.uuid4()))
|
||||||
_messages: List[Dict[str, str]] = PrivateAttr(default_factory=list)
|
_messages: list[dict[str, str]] = PrivateAttr(default_factory=list)
|
||||||
_iterations: int = PrivateAttr(default=0)
|
_iterations: int = PrivateAttr(default=0)
|
||||||
_printer: Printer = PrivateAttr(default_factory=Printer)
|
_printer: Printer = PrivateAttr(default_factory=Printer)
|
||||||
_guardrail: Optional[Callable] = PrivateAttr(default=None)
|
_guardrail: Optional[Callable[[LiteAgentOutput], tuple[bool, Any]]] = PrivateAttr(
|
||||||
|
default=None
|
||||||
|
)
|
||||||
_guardrail_retry_count: int = PrivateAttr(default=0)
|
_guardrail_retry_count: int = PrivateAttr(default=0)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def setup_llm(self):
|
def setup_llm(self) -> Self:
|
||||||
"""Set up the LLM and other components after initialization."""
|
"""Set up the LLM and other components after initialization."""
|
||||||
self.llm = create_llm(self.llm)
|
self.llm = create_llm(self.llm)
|
||||||
if not isinstance(self.llm, BaseLLM):
|
if not isinstance(self.llm, BaseLLM):
|
||||||
@@ -216,7 +206,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def parse_tools(self):
|
def parse_tools(self) -> Self:
|
||||||
"""Parse the tools and convert them to CrewStructuredTool instances."""
|
"""Parse the tools and convert them to CrewStructuredTool instances."""
|
||||||
self._parsed_tools = parse_tools(self.tools)
|
self._parsed_tools = parse_tools(self.tools)
|
||||||
|
|
||||||
@@ -241,8 +231,8 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
@field_validator("guardrail", mode="before")
|
@field_validator("guardrail", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_guardrail_function(
|
def validate_guardrail_function(
|
||||||
cls, v: Optional[Union[Callable, str]]
|
cls, v: Optional[Callable[[Any], tuple[bool, Any]] | str]
|
||||||
) -> Optional[Union[Callable, str]]:
|
) -> Optional[Callable[[Any], tuple[bool, Any]] | str]:
|
||||||
"""Validate that the guardrail function has the correct signature.
|
"""Validate that the guardrail function has the correct signature.
|
||||||
|
|
||||||
If v is a callable, validate that it has the correct signature.
|
If v is a callable, validate that it has the correct signature.
|
||||||
@@ -267,7 +257,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
|
|
||||||
# Check return annotation if present
|
# Check return annotation if present
|
||||||
if sig.return_annotation is not sig.empty:
|
if sig.return_annotation is not sig.empty:
|
||||||
if sig.return_annotation == Tuple[bool, Any]:
|
if sig.return_annotation == tuple[bool, Any]:
|
||||||
return v
|
return v
|
||||||
|
|
||||||
origin = get_origin(sig.return_annotation)
|
origin = get_origin(sig.return_annotation)
|
||||||
@@ -290,7 +280,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
"""Return the original role for compatibility with tool interfaces."""
|
"""Return the original role for compatibility with tool interfaces."""
|
||||||
return self.role
|
return self.role
|
||||||
|
|
||||||
def kickoff(self, messages: Union[str, List[Dict[str, str]]]) -> LiteAgentOutput:
|
def kickoff(self, messages: str | list[dict[str, str]]) -> LiteAgentOutput:
|
||||||
"""
|
"""
|
||||||
Execute the agent with the given messages.
|
Execute the agent with the given messages.
|
||||||
|
|
||||||
@@ -338,7 +328,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def _execute_core(self, agent_info: Dict[str, Any]) -> LiteAgentOutput:
|
def _execute_core(self, agent_info: dict[str, Any]) -> LiteAgentOutput:
|
||||||
# Emit event for agent execution start
|
# Emit event for agent execution start
|
||||||
crewai_event_bus.emit(
|
crewai_event_bus.emit(
|
||||||
self,
|
self,
|
||||||
@@ -428,7 +418,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
async def kickoff_async(
|
async def kickoff_async(
|
||||||
self, messages: Union[str, List[Dict[str, str]]]
|
self, messages: str | list[dict[str, str]]
|
||||||
) -> LiteAgentOutput:
|
) -> LiteAgentOutput:
|
||||||
"""
|
"""
|
||||||
Execute the agent asynchronously with the given messages.
|
Execute the agent asynchronously with the given messages.
|
||||||
@@ -475,8 +465,8 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
return base_prompt
|
return base_prompt
|
||||||
|
|
||||||
def _format_messages(
|
def _format_messages(
|
||||||
self, messages: Union[str, List[Dict[str, str]]]
|
self, messages: str | list[dict[str, str]]
|
||||||
) -> List[Dict[str, str]]:
|
) -> list[dict[str, str]]:
|
||||||
"""Format messages for the LLM."""
|
"""Format messages for the LLM."""
|
||||||
if isinstance(messages, str):
|
if isinstance(messages, str):
|
||||||
messages = [{"role": "user", "content": messages}]
|
messages = [{"role": "user", "content": messages}]
|
||||||
@@ -582,7 +572,7 @@ class LiteAgent(FlowTrackable, BaseModel):
|
|||||||
self._show_logs(formatted_answer)
|
self._show_logs(formatted_answer)
|
||||||
return formatted_answer
|
return formatted_answer
|
||||||
|
|
||||||
def _show_logs(self, formatted_answer: Union[AgentAction, AgentFinish]):
|
def _show_logs(self, formatted_answer: AgentAction | AgentFinish) -> None:
|
||||||
"""Show logs for the agent's execution."""
|
"""Show logs for the agent's execution."""
|
||||||
crewai_event_bus.emit(
|
crewai_event_bus.emit(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Any, Dict, List
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from mem0 import Memory, MemoryClient
|
from mem0 import Memory, MemoryClient
|
||||||
from crewai.utilities.chromadb import sanitize_collection_name
|
|
||||||
|
|
||||||
from crewai.memory.storage.interface import Storage
|
from crewai.memory.storage.interface import Storage
|
||||||
|
from crewai.utilities.chromadb import sanitize_collection_name
|
||||||
|
|
||||||
MAX_AGENT_ID_LENGTH_MEM0 = 255
|
MAX_AGENT_ID_LENGTH_MEM0 = 255
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ class Mem0Storage(Storage):
|
|||||||
"""
|
"""
|
||||||
Extends Storage to handle embedding and searching across entities using Mem0.
|
Extends Storage to handle embedding and searching across entities using Mem0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, type, crew=None, config=None):
|
def __init__(self, type, crew=None, config=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
@@ -86,21 +88,21 @@ class Mem0Storage(Storage):
|
|||||||
|
|
||||||
return filter
|
return filter
|
||||||
|
|
||||||
def save(self, value: Any, metadata: Dict[str, Any]) -> None:
|
def save(self, value: Any, metadata: dict[str, Any]) -> None:
|
||||||
user_id = self.config.get("user_id", "")
|
user_id = self.config.get("user_id", "")
|
||||||
assistant_message = [{"role" : "assistant","content" : value}]
|
assistant_message = [{"role": "assistant", "content": value}]
|
||||||
|
|
||||||
base_metadata = {
|
base_metadata = {
|
||||||
"short_term": "short_term",
|
"short_term": "short_term",
|
||||||
"long_term": "long_term",
|
"long_term": "long_term",
|
||||||
"entities": "entity",
|
"entities": "entity",
|
||||||
"external": "external"
|
"external": "external",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Shared base params
|
# Shared base params
|
||||||
params: dict[str, Any] = {
|
params: dict[str, Any] = {
|
||||||
"metadata": {"type": base_metadata[self.memory_type], **metadata},
|
"metadata": {"type": base_metadata[self.memory_type], **metadata},
|
||||||
"infer": self.infer
|
"infer": self.infer,
|
||||||
}
|
}
|
||||||
|
|
||||||
# MemoryClient-specific overrides
|
# MemoryClient-specific overrides
|
||||||
@@ -121,13 +123,15 @@ class Mem0Storage(Storage):
|
|||||||
|
|
||||||
self.memory.add(assistant_message, **params)
|
self.memory.add(assistant_message, **params)
|
||||||
|
|
||||||
def search(self,query: str,limit: int = 3,score_threshold: float = 0.35) -> List[Any]:
|
def search(
|
||||||
|
self, query: str, limit: int = 3, score_threshold: float = 0.35
|
||||||
|
) -> list[Any]:
|
||||||
params = {
|
params = {
|
||||||
"query": query,
|
"query": query,
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
"version": "v2",
|
"version": "v2",
|
||||||
"output_format": "v1.1"
|
"output_format": "v1.1",
|
||||||
}
|
}
|
||||||
|
|
||||||
if user_id := self.config.get("user_id", ""):
|
if user_id := self.config.get("user_id", ""):
|
||||||
params["user_id"] = user_id
|
params["user_id"] = user_id
|
||||||
@@ -148,10 +152,10 @@ class Mem0Storage(Storage):
|
|||||||
# automatically when the crew is created.
|
# automatically when the crew is created.
|
||||||
|
|
||||||
params["filters"] = self._create_filter_for_search()
|
params["filters"] = self._create_filter_for_search()
|
||||||
params['threshold'] = score_threshold
|
params["threshold"] = score_threshold
|
||||||
|
|
||||||
if isinstance(self.memory, Memory):
|
if isinstance(self.memory, Memory):
|
||||||
del params["metadata"], params["version"], params['output_format']
|
del params["metadata"], params["version"], params["output_format"]
|
||||||
if params.get("run_id"):
|
if params.get("run_id"):
|
||||||
del params["run_id"]
|
del params["run_id"]
|
||||||
|
|
||||||
@@ -160,10 +164,10 @@ class Mem0Storage(Storage):
|
|||||||
# This makes it compatible for Contextual Memory to retrieve
|
# This makes it compatible for Contextual Memory to retrieve
|
||||||
for result in results["results"]:
|
for result in results["results"]:
|
||||||
result["context"] = result["memory"]
|
result["context"] = result["memory"]
|
||||||
|
|
||||||
return [r for r in results["results"]]
|
return [r for r in results["results"]]
|
||||||
|
|
||||||
def reset(self):
|
def reset(self) -> None:
|
||||||
if self.memory:
|
if self.memory:
|
||||||
self.memory.reset()
|
self.memory.reset()
|
||||||
|
|
||||||
@@ -180,4 +184,6 @@ class Mem0Storage(Storage):
|
|||||||
agents = self.crew.agents
|
agents = self.crew.agents
|
||||||
agents = [self._sanitize_role(agent.role) for agent in agents]
|
agents = [self._sanitize_role(agent.role) for agent in agents]
|
||||||
agents = "_".join(agents)
|
agents = "_".join(agents)
|
||||||
return sanitize_collection_name(name=agents, max_collection_length=MAX_AGENT_ID_LENGTH_MEM0)
|
return sanitize_collection_name(
|
||||||
|
name=agents, max_collection_length=MAX_AGENT_ID_LENGTH_MEM0
|
||||||
|
)
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ class CacheTools(BaseModel):
|
|||||||
default_factory=CacheHandler,
|
default_factory=CacheHandler,
|
||||||
)
|
)
|
||||||
|
|
||||||
def tool(self):
|
def tool(self) -> CrewStructuredTool:
|
||||||
return CrewStructuredTool.from_function(
|
return CrewStructuredTool.from_function(
|
||||||
func=self.hit_cache,
|
func=self.hit_cache,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
description="Reads directly from the cache",
|
description="Reads directly from the cache",
|
||||||
)
|
)
|
||||||
|
|
||||||
def hit_cache(self, key):
|
def hit_cache(self, key: str) -> str:
|
||||||
split = key.split("tool:")
|
split = key.split("tool:")
|
||||||
tool = split[1].split("|input:")[0].strip()
|
tool = split[1].split("|input:")[0].strip()
|
||||||
tool_input = split[1].split("|input:")[1].strip()
|
tool_input = split[1].split("|input:")[1].strip()
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import time
|
|||||||
from difflib import SequenceMatcher
|
from difflib import SequenceMatcher
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||||
|
|
||||||
import json5
|
import json5
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json # type: ignore[import-untyped]
|
||||||
|
|
||||||
from crewai.agents.tools_handler import ToolsHandler
|
from crewai.agents.tools_handler import ToolsHandler
|
||||||
from crewai.events.event_bus import crewai_event_bus
|
from crewai.events.event_bus import crewai_event_bus
|
||||||
@@ -100,7 +100,7 @@ class ToolUsage:
|
|||||||
self._max_parsing_attempts = 2
|
self._max_parsing_attempts = 2
|
||||||
self._remember_format_after_usages = 4
|
self._remember_format_after_usages = 4
|
||||||
|
|
||||||
def parse_tool_calling(self, tool_string: str):
|
def parse_tool_calling(self, tool_string: str) -> Any:
|
||||||
"""Parse the tool string and return the tool calling."""
|
"""Parse the tool string and return the tool calling."""
|
||||||
return self._tool_calling(tool_string)
|
return self._tool_calling(tool_string)
|
||||||
|
|
||||||
@@ -149,7 +149,21 @@ class ToolUsage:
|
|||||||
tool: CrewStructuredTool,
|
tool: CrewStructuredTool,
|
||||||
calling: ToolCalling | InstructorToolCalling,
|
calling: ToolCalling | InstructorToolCalling,
|
||||||
) -> str:
|
) -> str:
|
||||||
if self._check_tool_repeated_usage(calling=calling): # type: ignore # _check_tool_repeated_usage of "ToolUsage" does not return a value (it only ever returns None)
|
"""Use a tool with the given calling information.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool_string: The string representation of the tool call.
|
||||||
|
tool: The CrewStructuredTool instance to use.
|
||||||
|
calling: The tool calling information.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The formatted result of the tool usage.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
TODO: Investigate why BaseAgent/LiteAgent don't have fingerprint attribute.
|
||||||
|
Currently using hasattr check as a workaround (lines 179-180).
|
||||||
|
"""
|
||||||
|
if self._check_tool_repeated_usage(calling=calling):
|
||||||
try:
|
try:
|
||||||
result = self._i18n.errors("task_repeated_usage").format(
|
result = self._i18n.errors("task_repeated_usage").format(
|
||||||
tool_names=self.tools_names
|
tool_names=self.tools_names
|
||||||
@@ -159,8 +173,8 @@ class ToolUsage:
|
|||||||
tool_name=tool.name,
|
tool_name=tool.name,
|
||||||
attempts=self._run_attempts,
|
attempts=self._run_attempts,
|
||||||
)
|
)
|
||||||
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
|
result = self._format_result(result=result)
|
||||||
return result # type: ignore # Fix the return type of this function
|
return result
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
if self.task:
|
if self.task:
|
||||||
@@ -176,7 +190,7 @@ class ToolUsage:
|
|||||||
"agent": self.agent,
|
"agent": self.agent,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.agent.fingerprint:
|
if hasattr(self.agent, "fingerprint") and self.agent.fingerprint:
|
||||||
event_data.update(self.agent.fingerprint)
|
event_data.update(self.agent.fingerprint)
|
||||||
if self.task:
|
if self.task:
|
||||||
event_data["task_name"] = self.task.name or self.task.description
|
event_data["task_name"] = self.task.name or self.task.description
|
||||||
@@ -264,19 +278,19 @@ class ToolUsage:
|
|||||||
self._printer.print(
|
self._printer.print(
|
||||||
content=f"\n\n{error_message}\n", color="red"
|
content=f"\n\n{error_message}\n", color="red"
|
||||||
)
|
)
|
||||||
return error # type: ignore # No return value expected
|
return error
|
||||||
|
|
||||||
if self.task:
|
if self.task:
|
||||||
self.task.increment_tools_errors()
|
self.task.increment_tools_errors()
|
||||||
return self.use(calling=calling, tool_string=tool_string) # type: ignore # No return value expected
|
return self.use(calling=calling, tool_string=tool_string)
|
||||||
|
|
||||||
if self.tools_handler:
|
if self.tools_handler:
|
||||||
should_cache = True
|
should_cache = True
|
||||||
if (
|
if (
|
||||||
hasattr(available_tool, "cache_function")
|
hasattr(available_tool, "cache_function")
|
||||||
and available_tool.cache_function # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
and available_tool.cache_function
|
||||||
):
|
):
|
||||||
should_cache = available_tool.cache_function( # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
should_cache = available_tool.cache_function(
|
||||||
calling.arguments, result
|
calling.arguments, result
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -288,7 +302,7 @@ class ToolUsage:
|
|||||||
tool_name=tool.name,
|
tool_name=tool.name,
|
||||||
attempts=self._run_attempts,
|
attempts=self._run_attempts,
|
||||||
)
|
)
|
||||||
result = self._format_result(result=result) # type: ignore # "_format_result" of "ToolUsage" does not return a value (it only ever returns None)
|
result = self._format_result(result=result)
|
||||||
data = {
|
data = {
|
||||||
"result": result,
|
"result": result,
|
||||||
"tool_name": tool.name,
|
"tool_name": tool.name,
|
||||||
@@ -505,7 +519,7 @@ class ToolUsage:
|
|||||||
self.task.increment_tools_errors()
|
self.task.increment_tools_errors()
|
||||||
if self.agent and self.agent.verbose:
|
if self.agent and self.agent.verbose:
|
||||||
self._printer.print(content=f"\n\n{e}\n", color="red")
|
self._printer.print(content=f"\n\n{e}\n", color="red")
|
||||||
return ToolUsageErrorException( # type: ignore # Incompatible return value type (got "ToolUsageErrorException", expected "ToolCalling | InstructorToolCalling")
|
return ToolUsageErrorException(
|
||||||
f"{self._i18n.errors('tool_usage_error').format(error=e)}\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
|
f"{self._i18n.errors('tool_usage_error').format(error=e)}\nMoving on then. {self._i18n.slice('format').format(tool_names=self.tools_names)}"
|
||||||
)
|
)
|
||||||
return self._tool_calling(tool_string)
|
return self._tool_calling(tool_string)
|
||||||
@@ -564,7 +578,7 @@ class ToolUsage:
|
|||||||
# If all parsing attempts fail, raise an error
|
# If all parsing attempts fail, raise an error
|
||||||
raise Exception(error_message)
|
raise Exception(error_message)
|
||||||
|
|
||||||
def _emit_validate_input_error(self, final_error: str):
|
def _emit_validate_input_error(self, final_error: str) -> None:
|
||||||
tool_selection_data = {
|
tool_selection_data = {
|
||||||
"agent_key": getattr(self.agent, "key", None) if self.agent else None,
|
"agent_key": getattr(self.agent, "key", None) if self.agent else None,
|
||||||
"agent_role": getattr(self.agent, "role", None) if self.agent else None,
|
"agent_role": getattr(self.agent, "role", None) if self.agent else None,
|
||||||
@@ -617,7 +631,20 @@ class ToolUsage:
|
|||||||
|
|
||||||
def _prepare_event_data(
|
def _prepare_event_data(
|
||||||
self, tool: Any, tool_calling: ToolCalling | InstructorToolCalling
|
self, tool: Any, tool_calling: ToolCalling | InstructorToolCalling
|
||||||
) -> dict:
|
) -> dict[str, Any]:
|
||||||
|
"""Prepare event data for tool usage events.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: The tool being used.
|
||||||
|
tool_calling: The tool calling information containing arguments.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dictionary containing event data for tool usage tracking.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
TODO: Create a better type representation for the return value,
|
||||||
|
possibly using TypedDict or a dataclass for stronger typing.
|
||||||
|
"""
|
||||||
event_data = {
|
event_data = {
|
||||||
"run_attempts": self._run_attempts,
|
"run_attempts": self._run_attempts,
|
||||||
"delegations": self.task.delegations if self.task else 0,
|
"delegations": self.task.delegations if self.task else 0,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
@@ -16,10 +16,10 @@ class ExecutionLog(BaseModel):
|
|||||||
|
|
||||||
task_id: str
|
task_id: str
|
||||||
expected_output: Optional[str] = None
|
expected_output: Optional[str] = None
|
||||||
output: Dict[str, Any]
|
output: dict[str, Any]
|
||||||
timestamp: datetime = Field(default_factory=datetime.now)
|
timestamp: datetime = Field(default_factory=datetime.now)
|
||||||
task_index: int
|
task_index: int
|
||||||
inputs: Dict[str, Any] = Field(default_factory=dict)
|
inputs: dict[str, Any] = Field(default_factory=dict)
|
||||||
was_replayed: bool = False
|
was_replayed: bool = False
|
||||||
|
|
||||||
def __getitem__(self, key: str) -> Any:
|
def __getitem__(self, key: str) -> Any:
|
||||||
@@ -33,7 +33,7 @@ class TaskOutputStorageHandler:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.storage = KickoffTaskOutputsSQLiteStorage()
|
self.storage = KickoffTaskOutputsSQLiteStorage()
|
||||||
|
|
||||||
def update(self, task_index: int, log: Dict[str, Any]):
|
def update(self, task_index: int, log: dict[str, Any]):
|
||||||
saved_outputs = self.load()
|
saved_outputs = self.load()
|
||||||
if saved_outputs is None:
|
if saved_outputs is None:
|
||||||
raise ValueError("Logs cannot be None")
|
raise ValueError("Logs cannot be None")
|
||||||
@@ -56,16 +56,16 @@ class TaskOutputStorageHandler:
|
|||||||
def add(
|
def add(
|
||||||
self,
|
self,
|
||||||
task: Task,
|
task: Task,
|
||||||
output: Dict[str, Any],
|
output: dict[str, Any],
|
||||||
task_index: int,
|
task_index: int,
|
||||||
inputs: Dict[str, Any] | None = None,
|
inputs: dict[str, Any] | None = None,
|
||||||
was_replayed: bool = False,
|
was_replayed: bool = False,
|
||||||
):
|
):
|
||||||
inputs = inputs or {}
|
inputs = inputs or {}
|
||||||
self.storage.add(task, output, task_index, was_replayed, inputs)
|
self.storage.add(task, output, task_index, was_replayed, inputs)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self) -> None:
|
||||||
self.storage.delete_all()
|
self.storage.delete_all()
|
||||||
|
|
||||||
def load(self) -> Optional[List[Dict[str, Any]]]:
|
def load(self) -> Optional[list[dict[str, Any]]]:
|
||||||
return self.storage.load()
|
return self.storage.load()
|
||||||
|
|||||||
Reference in New Issue
Block a user