Source code for climb.tool.tools

import abc
from typing import Any, Dict, List, Optional

from climb.common.data_structures import UserInputRequest

from .tool_comms import ToolReturnIter, ToolThread

# TODO: Success/fail
# TODO: Streaming of STDOUT/STDERR


[docs] def get_str_up_to_marker(s: str, marker: str) -> str: if marker in s: return s[: s.index(marker)] return s
[docs] class ToolBase(abc.ABC): def __init__(self) -> None: super().__init__() self.user_input: Optional[UserInputRequest] = None self.working_directory: str = "TO_BE_SET_BY_ENGINE" self.tool_thread: Optional[ToolThread] = None
[docs] def execute(self, **kwargs: Any) -> ToolReturnIter: # For now this is just a "pass-through". if "session" not in kwargs: raise ValueError("Expected 'session' in kwargs.") if "additional_kwargs_required" not in kwargs: raise ValueError("Expected 'additional_kwargs_required' in kwargs.") yield from self._execute(**kwargs)
[docs] def stop_execution(self, timeout: Optional[int] = 1) -> None: if self.tool_thread is not None: self.tool_thread.kill(timeout=timeout) print("ToolBase.stop_execution(): Tool thread killed.")
@abc.abstractmethod def _execute(self, **kwargs: Any) -> ToolReturnIter: ... @property @abc.abstractmethod def name(self) -> str: ... @property @abc.abstractmethod def description(self) -> str: ... @property def logs_useful(self) -> bool: """Return `True` if the logs of this tool are *especially* useful for the LLM to understand what has been done. This will be used by the engine to determine whether to shorten the logs if needed for token reasons etc. This is up to the engine's discretion, this property just provides a hint. The user will always be able to see the full logs. Returns: bool: `True` if the logs of this tool are *especially* useful for the LLM to understand what has been done. """ return False # TODO: Factor this out to be engine-specific! @property @abc.abstractmethod def specification(self) -> Dict[str, Any]: ... @property def user_input_requested(self) -> List[UserInputRequest]: return []
[docs] def receive_user_inputs_requested(self, user_input: Optional[UserInputRequest]) -> None: self.user_input = user_input
[docs] def receive_working_directory(self, working_directory: str) -> None: self.working_directory = working_directory
# NOTE: Should make sense in the context "This tool <description_for_user>" @property @abc.abstractmethod def description_for_user(self) -> str: """A description of what this tool does, for the user. Should make sense in the context: "This tool `<description_for_user>`." """ ...