Skip to content

Default Tools

Default tools included with every LLMAgent.

ReadFileTool

Bases: BaseTool

A tool for reading the contents of a local file.

Source code in src/llm_agents_from_scratch/tools/default/read_file.py
class ReadFileTool(BaseTool):
    """A tool for reading the contents of a local file."""

    @property
    def name(self) -> str:
        """Name of the read-file tool."""
        return "from_scratch__read_file"

    @property
    def description(self) -> str:
        """Description of the read-file tool."""
        return "Read the contents of a local file and return them as a string."

    @property
    def parameters_json_schema(self) -> dict[str, Any]:
        """JSON schema for tool parameters."""
        return {
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to the file to read.",
                },
            },
            "required": ["path"],
        }

    def __call__(
        self,
        tool_call: ToolCall,
        *args: Any,
        **kwargs: Any,
    ) -> ToolCallResult:
        """Execute the read-file tool.

        Args:
            tool_call (ToolCall): The ToolCall to execute.
            *args (Any): Additional positional arguments.
            **kwargs (Any): Additional keyword arguments.

        Returns:
            ToolCallResult: The file contents, or an error result.
        """
        raw_path = tool_call.arguments.get("path")
        if not raw_path:
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {"error_type": "ValueError", "message": "Missing 'path'."},
                ),
                error=True,
            )

        try:
            content = Path(raw_path).read_text(encoding="utf-8")
        except FileNotFoundError:
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {
                        "error_type": "FileNotFoundError",
                        "message": f"File not found: '{raw_path}'.",
                    },
                ),
                error=True,
            )
        except OSError as e:
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {"error_type": "OSError", "message": str(e)},
                ),
                error=True,
            )

        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=content,
            error=False,
        )

name property

name

Name of the read-file tool.

description property

description

Description of the read-file tool.

parameters_json_schema property

parameters_json_schema

JSON schema for tool parameters.

__call__

__call__(tool_call, *args, **kwargs)

Execute the read-file tool.

Parameters:

Name Type Description Default
tool_call ToolCall

The ToolCall to execute.

required
*args Any

Additional positional arguments.

()
**kwargs Any

Additional keyword arguments.

{}

Returns:

Name Type Description
ToolCallResult ToolCallResult

The file contents, or an error result.

Source code in src/llm_agents_from_scratch/tools/default/read_file.py
def __call__(
    self,
    tool_call: ToolCall,
    *args: Any,
    **kwargs: Any,
) -> ToolCallResult:
    """Execute the read-file tool.

    Args:
        tool_call (ToolCall): The ToolCall to execute.
        *args (Any): Additional positional arguments.
        **kwargs (Any): Additional keyword arguments.

    Returns:
        ToolCallResult: The file contents, or an error result.
    """
    raw_path = tool_call.arguments.get("path")
    if not raw_path:
        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=json.dumps(
                {"error_type": "ValueError", "message": "Missing 'path'."},
            ),
            error=True,
        )

    try:
        content = Path(raw_path).read_text(encoding="utf-8")
    except FileNotFoundError:
        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=json.dumps(
                {
                    "error_type": "FileNotFoundError",
                    "message": f"File not found: '{raw_path}'.",
                },
            ),
            error=True,
        )
    except OSError as e:
        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=json.dumps(
                {"error_type": "OSError", "message": str(e)},
            ),
            error=True,
        )

    return ToolCallResult(
        tool_call_id=tool_call.id_,
        content=content,
        error=False,
    )

PythonInterpreterTool

Bases: BaseTool

A tool for executing a local Python script.

Source code in src/llm_agents_from_scratch/tools/default/interpreter.py
class PythonInterpreterTool(BaseTool):
    """A tool for executing a local Python script."""

    @property
    def name(self) -> str:
        """Name of the Python interpreter tool."""
        return "from_scratch__python_interpreter"

    @property
    def description(self) -> str:
        """Description of the Python interpreter tool."""
        return (
            "Execute a Python script and return its stdout output. "
            "Provide either 'path' to run an existing script or 'code' to "
            "run inline code. Optionally pipe text to the script via stdin. "
            "Use 'cwd' to set the working directory — modules in that "
            "directory will be importable."
        )

    @property
    def parameters_json_schema(self) -> dict[str, Any]:
        """JSON schema for tool parameters."""
        return {
            "type": "object",
            "properties": {
                "path": {
                    "type": "string",
                    "description": "Path to an existing Python script.",
                },
                "code": {
                    "type": "string",
                    "description": (
                        "Inline Python code to execute. Use instead of 'path'."
                    ),
                },
                "stdin": {
                    "type": "string",
                    "description": (
                        "Optional text to pass to the script via stdin."
                    ),
                },
                "cwd": {
                    "type": "string",
                    "description": (
                        "Optional working directory for execution. "
                        "Modules inside this directory are importable."
                    ),
                },
            },
        }

    def __call__(
        self,
        tool_call: ToolCall,
        *args: Any,
        **kwargs: Any,
    ) -> ToolCallResult:
        """Execute the Python interpreter tool.

        Args:
            tool_call (ToolCall): The ToolCall to execute.
            *args (Any): Additional positional arguments.
            **kwargs (Any): Additional keyword arguments.

        Returns:
            ToolCallResult: The script stdout, or an error result.
        """
        raw_path = tool_call.arguments.get("path")
        code = tool_call.arguments.get("code")
        stdin_text = tool_call.arguments.get("stdin")
        raw_cwd = tool_call.arguments.get("cwd")

        if not raw_path and not code:
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {
                        "error_type": "ValueError",
                        "message": "Missing 'path' or 'code'.",
                    },
                ),
                error=True,
            )

        cwd = Path(raw_cwd) if raw_cwd else None
        tmp_path: Path | None = None

        if code:
            tmp_dir = cwd or Path(tempfile.gettempdir())
            with tempfile.NamedTemporaryFile(
                mode="w",
                suffix=".py",
                dir=tmp_dir,
                delete=False,
            ) as tmp_file:
                tmp_file.write(code)
                tmp_path = Path(tmp_file.name)
            script_path: str = str(tmp_path)
        else:
            if not Path(raw_path).exists():  # type: ignore[arg-type]
                return ToolCallResult(
                    tool_call_id=tool_call.id_,
                    content=json.dumps(
                        {
                            "error_type": "FileNotFoundError",
                            "message": f"Script not found: '{raw_path}'.",
                        },
                    ),
                    error=True,
                )
            script_path = raw_path  # type: ignore[assignment]

        try:
            result = subprocess.run(
                [sys.executable, script_path],
                check=False,
                capture_output=True,
                text=True,
                input=stdin_text,
                cwd=cwd,
            )
        finally:
            if tmp_path:
                tmp_path.unlink(missing_ok=True)

        if result.returncode != 0:
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {
                        "error_type": "RuntimeError",
                        "message": result.stderr,
                    },
                ),
                error=True,
            )

        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=result.stdout,
            error=False,
        )

name property

name

Name of the Python interpreter tool.

description property

description

Description of the Python interpreter tool.

parameters_json_schema property

parameters_json_schema

JSON schema for tool parameters.

__call__

__call__(tool_call, *args, **kwargs)

Execute the Python interpreter tool.

Parameters:

Name Type Description Default
tool_call ToolCall

The ToolCall to execute.

required
*args Any

Additional positional arguments.

()
**kwargs Any

Additional keyword arguments.

{}

Returns:

Name Type Description
ToolCallResult ToolCallResult

The script stdout, or an error result.

Source code in src/llm_agents_from_scratch/tools/default/interpreter.py
def __call__(
    self,
    tool_call: ToolCall,
    *args: Any,
    **kwargs: Any,
) -> ToolCallResult:
    """Execute the Python interpreter tool.

    Args:
        tool_call (ToolCall): The ToolCall to execute.
        *args (Any): Additional positional arguments.
        **kwargs (Any): Additional keyword arguments.

    Returns:
        ToolCallResult: The script stdout, or an error result.
    """
    raw_path = tool_call.arguments.get("path")
    code = tool_call.arguments.get("code")
    stdin_text = tool_call.arguments.get("stdin")
    raw_cwd = tool_call.arguments.get("cwd")

    if not raw_path and not code:
        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=json.dumps(
                {
                    "error_type": "ValueError",
                    "message": "Missing 'path' or 'code'.",
                },
            ),
            error=True,
        )

    cwd = Path(raw_cwd) if raw_cwd else None
    tmp_path: Path | None = None

    if code:
        tmp_dir = cwd or Path(tempfile.gettempdir())
        with tempfile.NamedTemporaryFile(
            mode="w",
            suffix=".py",
            dir=tmp_dir,
            delete=False,
        ) as tmp_file:
            tmp_file.write(code)
            tmp_path = Path(tmp_file.name)
        script_path: str = str(tmp_path)
    else:
        if not Path(raw_path).exists():  # type: ignore[arg-type]
            return ToolCallResult(
                tool_call_id=tool_call.id_,
                content=json.dumps(
                    {
                        "error_type": "FileNotFoundError",
                        "message": f"Script not found: '{raw_path}'.",
                    },
                ),
                error=True,
            )
        script_path = raw_path  # type: ignore[assignment]

    try:
        result = subprocess.run(
            [sys.executable, script_path],
            check=False,
            capture_output=True,
            text=True,
            input=stdin_text,
            cwd=cwd,
        )
    finally:
        if tmp_path:
            tmp_path.unlink(missing_ok=True)

    if result.returncode != 0:
        return ToolCallResult(
            tool_call_id=tool_call.id_,
            content=json.dumps(
                {
                    "error_type": "RuntimeError",
                    "message": result.stderr,
                },
            ),
            error=True,
        )

    return ToolCallResult(
        tool_call_id=tool_call.id_,
        content=result.stdout,
        error=False,
    )