from __future__ import annotations from collections.abc import Callable from typing import Generic, TypeVar from crewai.tools import BaseTool T = TypeVar("T", bound=BaseTool) class ToolCollection(list[T], Generic[T]): """A collection of tools that can be accessed by index or name. This class extends the built-in list to provide dictionary-like access to tools based on their name property. Usage: tools = ToolCollection(list_of_tools) # Access by index (regular list behavior) first_tool = tools[0] # Access by name (new functionality) search_tool = tools["search"] """ def __init__(self, tools: list[T] | None = None): super().__init__(tools or []) self._name_cache: dict[str, T] = {} self._build_name_cache() def _build_name_cache(self) -> None: self._name_cache = {tool.name.lower(): tool for tool in self} def __getitem__(self, key: int | str) -> T: # type: ignore[override] if isinstance(key, str): return self._name_cache[key.lower()] result: T = super().__getitem__(key) return result def append(self, tool: T) -> None: super().append(tool) self._name_cache[tool.name.lower()] = tool def extend(self, tools: list[T]) -> None: # type: ignore[override] super().extend(tools) self._build_name_cache() def insert(self, index: int, tool: T) -> None: # type: ignore[override] super().insert(index, tool) self._name_cache[tool.name.lower()] = tool def remove(self, tool: T) -> None: super().remove(tool) if tool.name.lower() in self._name_cache: del self._name_cache[tool.name.lower()] def pop(self, index: int = -1) -> T: # type: ignore[override] tool: T = super().pop(index) if tool.name.lower() in self._name_cache: del self._name_cache[tool.name.lower()] return tool def filter_by_names(self, names: list[str] | None = None) -> ToolCollection[T]: if names is None: return self return ToolCollection( [ tool for name in names if (tool := self._name_cache.get(name.lower())) is not None ] ) def filter_where(self, func: Callable[[T], bool]) -> ToolCollection[T]: return ToolCollection([tool for tool in self if func(tool)]) def clear(self) -> None: super().clear() self._name_cache.clear()