|
1 | 1 | import ast
|
| 2 | +import datetime |
2 | 3 | import os
|
| 4 | +import time |
3 | 5 | from difflib import SequenceMatcher
|
4 | 6 | from textwrap import dedent
|
5 | 7 | from typing import Any, List, Union
|
|
8 | 10 | from crewai.task import Task
|
9 | 11 | from crewai.telemetry import Telemetry
|
10 | 12 | from crewai.tools.tool_calling import InstructorToolCalling, ToolCalling
|
| 13 | +from crewai.tools.tool_usage_events import ToolUsageError, ToolUsageFinished |
11 | 14 | from crewai.utilities import I18N, Converter, ConverterError, Printer
|
| 15 | +import crewai.utilities.events as events |
| 16 | + |
12 | 17 |
|
13 | 18 | agentops = None
|
14 | 19 | if os.environ.get("AGENTOPS_API_KEY"):
|
@@ -126,12 +131,16 @@ def _use(
|
126 | 131 | except Exception:
|
127 | 132 | self.task.increment_tools_errors()
|
128 | 133 |
|
129 |
| - result = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str") |
| 134 | + started_at = time.time() |
| 135 | + from_cache = False |
130 | 136 |
|
| 137 | + result = None # type: ignore # Incompatible types in assignment (expression has type "None", variable has type "str") |
| 138 | + # check if cache is available |
131 | 139 | if self.tools_handler.cache:
|
132 | 140 | result = self.tools_handler.cache.read( # type: ignore # Incompatible types in assignment (expression has type "str | None", variable has type "str")
|
133 | 141 | tool=calling.tool_name, input=calling.arguments
|
134 | 142 | )
|
| 143 | + from_cache = result is not None |
135 | 144 |
|
136 | 145 | original_tool = next(
|
137 | 146 | (ot for ot in self.original_tools if ot.name == tool.name), None
|
@@ -163,6 +172,7 @@ def _use(
|
163 | 172 | else:
|
164 | 173 | result = tool.invoke(input={})
|
165 | 174 | except Exception as e:
|
| 175 | + self.on_tool_error(tool=tool, tool_calling=calling, e=e) |
166 | 176 | self._run_attempts += 1
|
167 | 177 | if self._run_attempts > self._max_parsing_attempts:
|
168 | 178 | self._telemetry.tool_usage_error(llm=self.function_calling_llm)
|
@@ -214,6 +224,13 @@ def _use(
|
214 | 224 | "tool_args": calling.arguments,
|
215 | 225 | }
|
216 | 226 |
|
| 227 | + self.on_tool_use_finished( |
| 228 | + tool=tool, |
| 229 | + tool_calling=calling, |
| 230 | + from_cache=from_cache, |
| 231 | + started_at=started_at, |
| 232 | + ) |
| 233 | + |
217 | 234 | if (
|
218 | 235 | hasattr(original_tool, "result_as_answer")
|
219 | 236 | and original_tool.result_as_answer # type: ignore # Item "None" of "Any | None" has no attribute "cache_function"
|
@@ -431,3 +448,34 @@ def _validate_tool_input(self, tool_input: str) -> str:
|
431 | 448 | # Reconstruct the JSON string
|
432 | 449 | new_json_string = "{" + ", ".join(formatted_entries) + "}"
|
433 | 450 | return new_json_string
|
| 451 | + |
| 452 | + def on_tool_error(self, tool: Any, tool_calling: ToolCalling, e: Exception) -> None: |
| 453 | + event_data = self._prepare_event_data(tool, tool_calling) |
| 454 | + events.emit( |
| 455 | + source=self, event=ToolUsageError(**{**event_data, "error": str(e)}) |
| 456 | + ) |
| 457 | + |
| 458 | + def on_tool_use_finished( |
| 459 | + self, tool: Any, tool_calling: ToolCalling, from_cache: bool, started_at: float |
| 460 | + ) -> None: |
| 461 | + finished_at = time.time() |
| 462 | + event_data = self._prepare_event_data(tool, tool_calling) |
| 463 | + event_data.update( |
| 464 | + { |
| 465 | + "started_at": datetime.datetime.fromtimestamp(started_at), |
| 466 | + "finished_at": datetime.datetime.fromtimestamp(finished_at), |
| 467 | + "from_cache": from_cache, |
| 468 | + } |
| 469 | + ) |
| 470 | + events.emit(source=self, event=ToolUsageFinished(**event_data)) |
| 471 | + |
| 472 | + def _prepare_event_data(self, tool: Any, tool_calling: ToolCalling) -> dict: |
| 473 | + return { |
| 474 | + "agent_key": self.agent.key, |
| 475 | + "agent_role": (self.agent._original_role or self.agent.role), |
| 476 | + "run_attempts": self._run_attempts, |
| 477 | + "delegations": self.task.delegations, |
| 478 | + "tool_name": tool.name, |
| 479 | + "tool_args": tool_calling.arguments, |
| 480 | + "tool_class": tool.__class__.__name__, |
| 481 | + } |
0 commit comments