Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: adding a check for the tools_handler before setting the cache handler #830

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Remove all files in the 'dist' directory
rm -f /mnt/d/Infra/crewAI/dist/*

# Remove all files in the '../ctim/crewAI/dist' directory
rm -f /mnt/d/agentia/agentia_ctim/ctim/crewAI/dist/*

# Build the project using Poetry
poetry build

# Copy all files from the 'dist' directory to '../ctim/crewAI/dist'
cp -r /mnt/d/Infra/crewAI/dist/* /mnt/d/agentia/agentia_ctim/ctim/crewAI/dist/

cd /mnt/d/agentia/agentia_ctim/ctim/

make build && make up

cd /mnt/d/agentia/agentia_ctim/crewAI
976 changes: 537 additions & 439 deletions poetry.lock

Large diffs are not rendered by default.

58 changes: 56 additions & 2 deletions src/crewai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.utilities.training_handler import CrewTrainingHandler

import logging

logger = logging.getLogger(__name__)


class Agent(BaseAgent):
"""Represents an agent in a system.
Expand Down Expand Up @@ -57,7 +61,7 @@ class Agent(BaseAgent):
)
llm: Any = Field(
default_factory=lambda: ChatOpenAI(
model=os.environ.get("OPENAI_MODEL_NAME", "gpt-4o")
model=os.environ.get("OPENAI_MODEL_NAME", "gpt-4o-mini")
),
description="Language model that will run the agent.",
)
Expand All @@ -84,6 +88,12 @@ class Agent(BaseAgent):
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
super().__init__(**config, **data)
logger.info(
f"[CrewAI.Agent.__init__]: Agent: {__pydantic_self__.role} has been init with ToolsHandler: {__pydantic_self__.tools_handler}"
)
logger.info(
f"[CrewAI.Agent.__init__]: Agent: {__pydantic_self__.role} has been init with callbacks: {__pydantic_self__.callbacks}"
)

@model_validator(mode="after")
def set_agent_executor(self) -> "Agent":
Expand All @@ -104,6 +114,9 @@ def set_agent_executor(self) -> "Agent":
if not self.agent_executor:
if not self.cache_handler:
self.cache_handler = CacheHandler()
logger.info(
f"[CrewAI.Agent.set_agent_executor]: calling set_cache_handler for agent {self.role}"
)
self.set_cache_handler(self.cache_handler)
return self

Expand All @@ -126,6 +139,13 @@ def execute_task(
if self.tools_handler:
# type: ignore # Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "ToolCalling")
self.tools_handler.last_used_tool = {}
logger.info(
f"[CrewAI.Agent.execute_task]: task has a tools_handler: {self.tools_handler}"
)
else:
logger.info(
"[CrewAI.Agent.execute_task]: task DOES NOT HAVE a tools_handler"
)

task_prompt = task.prompt()

Expand Down Expand Up @@ -159,13 +179,41 @@ def execute_task(
else:
task_prompt = self._use_trained_data(task_prompt=task_prompt)

logger.info(
f"[CrewAI.Agent.execute_task] Starting task execution for agent: {self.role}"
)

if hasattr(self, "callbacks") and self.callbacks:
logger.info(
f"[CrewAI.Agent.execute_task] Callbacks found for agent {self.role}: {self.callbacks}"
)
config = {"callbacks": self.callbacks}
else:
logger.warning(
f"[CrewAI.Agent.execute_task] No callbacks found for agent {self.role}"
)
config = {}

logger.info(
f"[CrewAI.Agent.execute_task] Invoking agent_executor with config: {config}"
)

result = self.agent_executor.invoke(
{
"input": task_prompt,
"tool_names": self.agent_executor.tools_names,
"tools": self.agent_executor.tools_description,
}
},
config=config,
)["output"]

logger.info(
f"[CrewAI.Agent.execute_task] Task execution completed for agent {self.role}"
)
logger.debug(
f"[CrewAI.Agent.execute_task] Task result: {result[:100]}..."
) # Log first 100 chars of result

if self.max_rpm:
self._rpm_controller.stop_rpm_counter()
return result
Expand Down Expand Up @@ -246,6 +294,12 @@ def create_agent_executor(self, tools=None) -> None:
bind = self.llm.bind(stop=stop_words)

inner_agent = agent_args | execution_prompt | bind | CrewAgentParser(agent=self)

logger.info(
f"[CrewAI.Agent.create_agent_executor]: setting the agent_executor for agent {self.role}"
)
callbacks = executor_args.get("callbacks", "No callbacks found")
logger.info(f"[CrewAI.Agent.create_agent_executor]: callbacks are: {callbacks}")
self.agent_executor = CrewAgentExecutor(
agent=RunnableAgent(runnable=inner_agent), **executor_args
)
Expand Down
15 changes: 13 additions & 2 deletions src/crewai/agents/agent_builder/base_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
from crewai.agents import CacheHandler, ToolsHandler
from crewai.utilities.token_counter_callback import TokenProcess

import logging

logger = logging.getLogger(__name__)


class BaseAgent(ABC, BaseModel):
"""Abstract Base Class for all third party agents compatible with CrewAI.
Expand Down Expand Up @@ -207,14 +211,21 @@ def set_cache_handler(self, cache_handler: CacheHandler) -> None:
Args:
cache_handler: An instance of the CacheHandler class.
"""
self.tools_handler = ToolsHandler()
if not self.tools_handler:
logger.info(
f"[CrewAI.BaseAgent.set_cache_handler]: Agent {self.role} 's ToolsHandler is being set to default"
)
self.tools_handler = ToolsHandler()
logger.info(
f"[CrewAI.BaseAgent.set_cache_handler]: Agent {self.role} 's ToolsHandler is = {self.tools_handler}"
)
if self.cache:
self.cache_handler = cache_handler
self.tools_handler.cache = cache_handler
self.create_agent_executor()

def increment_formatting_errors(self) -> None:
print("Formatting errors incremented")
logger.info("Formatting errors incremented")

def copy(self):
exclude = {
Expand Down
37 changes: 32 additions & 5 deletions src/crewai/agents/tools_handler.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import logging
import time

from typing import Any, Optional, Union

from ..tools.cache_tools import CacheTools
from ..tools.tool_calling import InstructorToolCalling, ToolCalling
from .cache.cache_handler import CacheHandler

logger = logging.getLogger(__name__)


class ToolsHandler:
"""Callback handler for tool usage."""
Expand All @@ -15,6 +20,7 @@ def __init__(self, cache: Optional[CacheHandler] = None):
"""Initialize the callback handler."""
self.cache = cache
self.last_used_tool = {} # type: ignore # BUG?: same as above
logger.info(f"ToolsHandler initialized with cache: {cache is not None}")

def on_tool_use(
self,
Expand All @@ -23,10 +29,31 @@ def on_tool_use(
should_cache: bool = True,
) -> Any:
"""Run when tool ends running."""
start_time = time.time()

logger.info(f"Tool used: {calling.tool_name}")
logger.debug(f"Tool arguments: {calling.arguments}")
logger.debug(f"Tool output length: {len(output)} characters")

self.last_used_tool = calling # type: ignore # BUG?: Incompatible types in assignment (expression has type "Union[ToolCalling, InstructorToolCalling]", variable has type "ToolCalling")
if self.cache and should_cache and calling.tool_name != CacheTools().name:
self.cache.add(
tool=calling.tool_name,
input=calling.arguments,
output=output,
)
logger.debug(f"Caching result for tool: {calling.tool_name}")
try:
self.cache.add(
tool=calling.tool_name,
input=calling.arguments,
output=output,
)
logger.info(f"Successfully cached result for tool: {calling.tool_name}")
except Exception as e:
logger.error(
f"Error caching result for tool {calling.tool_name}: {str(e)}",
exc_info=True,
)
elif not should_cache:
logger.info(f"Skipped caching for tool: {calling.tool_name}")
elif calling.tool_name == CacheTools().name:
logger.info("Skipped caching for CacheTools")

duration = time.time() - start_time
logger.info(f"Tool handling completed in {duration:.4f} seconds")
30 changes: 29 additions & 1 deletion src/crewai/task.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
from copy import deepcopy
import os
import re
Expand All @@ -14,6 +15,8 @@
from crewai.utilities.pydantic_schema_parser import PydanticSchemaParser
from crewai.agents.agent_builder.base_agent import BaseAgent

logger = logging.getLogger(__name__)


class Task(BaseModel):
"""Class that represents a task to be executed.
Expand Down Expand Up @@ -101,6 +104,12 @@ class Config:
def __init__(__pydantic_self__, **data):
config = data.pop("config", {})
super().__init__(**config, **data)
logger.info(
f"[CrewAI.Task.__init__]: Task initialized with description: {__pydantic_self__.description[:50]}..."
)
logger.info(
f"[CrewAI.Task.__init__]: Task has {len(__pydantic_self__.tools)} tools"
)

@field_validator("id", mode="before")
@classmethod
Expand All @@ -124,13 +133,17 @@ def set_attributes_based_on_config(self) -> "Task":
if self.config:
for key, value in self.config.items():
setattr(self, key, value)
logger.info("[CrewAI.Task.set_attributes_based_on_config]: Task config applied")
return self

@model_validator(mode="after")
def check_tools(self):
"""Check if the tools are set."""
if not self.tools and self.agent and self.agent.tools:
self.tools.extend(self.agent.tools)
logger.info(
f"[CrewAI.Task.check_tools]: Task has {len(self.tools)} tools after check"
)
return self

@model_validator(mode="after")
Expand All @@ -143,6 +156,7 @@ def check_output(self):
"Only one output type can be set, either output_pydantic or output_json.",
{},
)
logger.info("[CrewAI.Task.check_output]: Task output type checked")
return self

def execute( # type: ignore # Missing return statement
Expand All @@ -156,9 +170,13 @@ def execute( # type: ignore # Missing return statement
Returns:
Output of the task.
"""
logger.info(
f"[CrewAI.Task.execute]: Starting execution of task: {self.description[:50]}..."
)

agent = agent or self.agent
if not agent:
logger.error("[CrewAI.Task.execute]: No agent assigned to task")
raise Exception(
f"The task '{self.description}' has no agent assigned, therefore it can't be executed directly and should be executed in a Crew using a specific process that support that, like hierarchical."
)
Expand All @@ -175,15 +193,19 @@ def execute( # type: ignore # Missing return statement
# type: ignore # Argument 1 to "join" of "str" has incompatible type "str | None"; expected "Iterable[str]"
context = "\n".join(context)

logger.info("[CrewAI.Task.execute]: Context prepared for task")

self.prompt_context = context
tools = tools or self.tools

if self.async_execution:
logger.info("[CrewAI.Task.execute]: Starting asynchronous execution")
self.thread = threading.Thread(
target=self._execute, args=(agent, self, context, tools)
)
self.thread.start()
else:
logger.info("[CrewAI.Task.execute]: Starting synchronous execution")
result = self._execute(
task=self,
agent=agent,
Expand All @@ -193,14 +215,15 @@ def execute( # type: ignore # Missing return statement
return result

def _execute(self, agent, task, context, tools):
logger.info(f"[CrewAI.Task._execute]: Executing task with agent: {agent.role}")
result = agent.execute_task(
task=task,
context=context,
tools=tools,
)
exported_output = self._export_output(result)

# type: the responses are usually str but need to figuire out a more elegant solution here
# the responses are usually str but need to figuire out a more elegant solution here
self.output = TaskOutput(
description=self.description,
exported_output=exported_output,
Expand All @@ -209,8 +232,10 @@ def _execute(self, agent, task, context, tools):
)

if self.callback:
logger.info("[CrewAI.Task._execute]: Executing callback for task")
self.callback(self.output)

logger.info("[CrewAI.Task._execute]: Task execution completed")
return exported_output

def prompt(self) -> str:
Expand Down Expand Up @@ -350,3 +375,6 @@ def _save_file(self, result: Any) -> None:

def __repr__(self):
return f"Task(description={self.description}, expected_output={self.expected_output})"

def __str__(self):
return f"Task(description={self.description[:50]}[:50])"
Loading