# This file was auto-generated by Fern from our API Definition.

import typing
from json.decoder import JSONDecodeError

from ..core.api_error import ApiError
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.http_response import AsyncHttpResponse, HttpResponse
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pagination import AsyncPager, BaseHttpResponse, SyncPager
from ..core.pydantic_utilities import parse_obj_as
from ..core.request_options import RequestOptions
from ..core.serialization import convert_and_respect_annotation_metadata
from ..errors.bad_request_error import BadRequestError
from ..errors.conflict_error import ConflictError
from ..errors.forbidden_error import ForbiddenError
from ..errors.internal_server_error import InternalServerError
from ..errors.not_found_error import NotFoundError
from ..types.bad_request_error_body import BadRequestErrorBody
from ..types.error import Error
from ..types.execution_configuration import ExecutionConfiguration
from ..types.list_tools_response import ListToolsResponse
from ..types.not_found_error_body import NotFoundErrorBody
from ..types.test_tool_response import TestToolResponse
from ..types.tool import Tool
from ..types.update_tool_request import UpdateToolRequest
from .types.tools_list_request_type import ToolsListRequestType

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class RawToolsClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def list(
        self,
        *,
        filter: typing.Optional[str] = None,
        type: typing.Optional[ToolsListRequestType] = None,
        enabled: typing.Optional[bool] = None,
        tool_server_id: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        page_key: typing.Optional[str] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[Tool]:
        """
        List all tools available to the authenticated user, with optional filtering and pagination.

        Parameters
        ----------
        filter : typing.Optional[str]
            A regular expression against tool names and descriptions to filter the results.

        type : typing.Optional[ToolsListRequestType]
            Filter tools by type.

        enabled : typing.Optional[bool]
            Filter tools by enabled status.

        tool_server_id : typing.Optional[str]
            Filter tools by the tool server they belong to.

        limit : typing.Optional[int]
            The maximum number of tools to return in the list.

        page_key : typing.Optional[str]
            Used to retrieve the next page of tools after the limit has been reached.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        SyncPager[Tool]
            List of available tools.
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/tools",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "filter": filter,
                "type": type,
                "enabled": enabled,
                "tool_server_id": tool_server_id,
                "limit": limit,
                "page_key": page_key,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListToolsResponse,
                    parse_obj_as(
                        type_=ListToolsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.tools
                _has_next = False
                _get_next = None
                if _parsed_response.metadata is not None:
                    _parsed_next = _parsed_response.metadata.page_key
                    _has_next = _parsed_next is not None and _parsed_next != ""
                    _get_next = lambda: self.list(
                        filter=filter,
                        type=type,
                        enabled=enabled,
                        tool_server_id=tool_server_id,
                        limit=limit,
                        page_key=_parsed_next,
                        request_timeout=request_timeout,
                        request_timeout_millis=request_timeout_millis,
                        request_options=request_options,
                    )
                return SyncPager(
                    has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def create(
        self,
        *,
        name: str,
        title: str,
        description: str,
        code: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        language: typing.Optional[typing.Literal["python"]] = OMIT,
        execution_configuration: typing.Optional[ExecutionConfiguration] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Tool]:
        """
        Create a new tool that agents can use. Currently supports Lambda tools for user-defined functions.
        Lambda tools allow you to write custom code that agents can execute in a secure sandbox.

        Parameters
        ----------
        name : str
            The unique name of the tool (used as the function identifier).

        title : str
            Human-readable title of the tool displayed in the UI.

        description : str
            A detailed description of what the function does, when to use it, and what it returns.

        code : str
            The Python 3.12 code for the function.

            **Required**: Must define a `process()` entry point function. Use type annotations on parameters for automatic schema discovery.

            **Parameters**: Passed as keyword arguments matched to the function signature.

            **Return types**: Can return any JSON-serializable type (strings, numbers, booleans, lists, or objects).

            **Example: Returning a number**
            ```python
            def process(x: int, y: int) -> int:
                return x + y
            ```

            **Example: Returning a string**
            ```python
            def process(name: str) -> str:
                return f"Hello, {name}!"
            ```

            **Example: Returning a boolean**
            ```python
            def process(value: int, threshold: int) -> bool:
                return value > threshold
            ```

            **Example: Returning a list**
            ```python
            from typing import List

            def process(items: List[str]) -> List[str]:
                return sorted(items)
            ```

            **Example: Returning an object (dict)**
            ```python
            def process(order_count: int, total_revenue: float, days_active: int = 1) -> dict:
                score = (order_count * 10 + total_revenue * 0.1) / days_active
                return {'score': round(score, 2), 'rating': 'high' if score > 100 else 'low'}
            ```

            For complex types, use the `typing` module:

            ```python
            from typing import List, Dict

            def process(items: List[str], config: Dict[str, float]) -> dict:
                count = len(items)
                multiplier = config.get('multiplier', 1.0)
                return {'count': count, 'adjusted': count * multiplier}
            ```

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        language : typing.Optional[typing.Literal["python"]]
            The programming language. Currently only 'python' (Python 3.12) is supported.

        execution_configuration : typing.Optional[ExecutionConfiguration]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[Tool]
            The tool has been created successfully.
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/tools",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "name": name,
                "title": title,
                "description": description,
                "language": language,
                "code": code,
                "execution_configuration": convert_and_respect_annotation_metadata(
                    object_=execution_configuration, annotation=ExecutionConfiguration, direction="write"
                ),
                "type": "lambda",
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 409:
                raise ConflictError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def get(
        self,
        tool_id: str,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Tool]:
        """
        Retrieve the details of a specific tool by its ID, including its configuration and capabilities.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to retrieve.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[Tool]
            The requested tool details.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def delete(
        self,
        tool_id: str,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[None]:
        """
        Permanently delete a tool and all its associated configuration. This action cannot be undone.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to delete.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[None]
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="DELETE",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return HttpResponse(response=_response, data=None)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def update(
        self,
        tool_id: str,
        *,
        request: UpdateToolRequest,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[Tool]:
        """
        Update an existing tool's configuration.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to update.

        request : UpdateToolRequest

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[Tool]
            The tool has been updated successfully.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="PATCH",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=UpdateToolRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    def test(
        self,
        tool_id: str,
        *,
        input: typing.Dict[str, typing.Optional[typing.Any]],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        timeout_seconds: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> HttpResponse[TestToolResponse]:
        """
        Execute a Lambda tool with test inputs to verify it works correctly.
        This endpoint allows users to test their functions before using them with agents.
        The function is executed in a secure sandbox environment with the same constraints as production.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the Lambda tool to test.

        input : typing.Dict[str, typing.Optional[typing.Any]]
            The input parameters to pass to the function. Must match the tool's input schema.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        timeout_seconds : typing.Optional[int]
            Maximum execution time in seconds. If not specified, uses the tool's configured timeout.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        HttpResponse[TestToolResponse]
            Test execution completed successfully.
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}/test",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "input": input,
                "timeout_seconds": timeout_seconds,
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestToolResponse,
                    parse_obj_as(
                        type_=TestToolResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return HttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)


class AsyncRawToolsClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def list(
        self,
        *,
        filter: typing.Optional[str] = None,
        type: typing.Optional[ToolsListRequestType] = None,
        enabled: typing.Optional[bool] = None,
        tool_server_id: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        page_key: typing.Optional[str] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[Tool]:
        """
        List all tools available to the authenticated user, with optional filtering and pagination.

        Parameters
        ----------
        filter : typing.Optional[str]
            A regular expression against tool names and descriptions to filter the results.

        type : typing.Optional[ToolsListRequestType]
            Filter tools by type.

        enabled : typing.Optional[bool]
            Filter tools by enabled status.

        tool_server_id : typing.Optional[str]
            Filter tools by the tool server they belong to.

        limit : typing.Optional[int]
            The maximum number of tools to return in the list.

        page_key : typing.Optional[str]
            Used to retrieve the next page of tools after the limit has been reached.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncPager[Tool]
            List of available tools.
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/tools",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "filter": filter,
                "type": type,
                "enabled": enabled,
                "tool_server_id": tool_server_id,
                "limit": limit,
                "page_key": page_key,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListToolsResponse,
                    parse_obj_as(
                        type_=ListToolsResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _items = _parsed_response.tools
                _has_next = False
                _get_next = None
                if _parsed_response.metadata is not None:
                    _parsed_next = _parsed_response.metadata.page_key
                    _has_next = _parsed_next is not None and _parsed_next != ""

                    async def _get_next():
                        return await self.list(
                            filter=filter,
                            type=type,
                            enabled=enabled,
                            tool_server_id=tool_server_id,
                            limit=limit,
                            page_key=_parsed_next,
                            request_timeout=request_timeout,
                            request_timeout_millis=request_timeout_millis,
                            request_options=request_options,
                        )

                return AsyncPager(
                    has_next=_has_next, items=_items, get_next=_get_next, response=BaseHttpResponse(response=_response)
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def create(
        self,
        *,
        name: str,
        title: str,
        description: str,
        code: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        language: typing.Optional[typing.Literal["python"]] = OMIT,
        execution_configuration: typing.Optional[ExecutionConfiguration] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Tool]:
        """
        Create a new tool that agents can use. Currently supports Lambda tools for user-defined functions.
        Lambda tools allow you to write custom code that agents can execute in a secure sandbox.

        Parameters
        ----------
        name : str
            The unique name of the tool (used as the function identifier).

        title : str
            Human-readable title of the tool displayed in the UI.

        description : str
            A detailed description of what the function does, when to use it, and what it returns.

        code : str
            The Python 3.12 code for the function.

            **Required**: Must define a `process()` entry point function. Use type annotations on parameters for automatic schema discovery.

            **Parameters**: Passed as keyword arguments matched to the function signature.

            **Return types**: Can return any JSON-serializable type (strings, numbers, booleans, lists, or objects).

            **Example: Returning a number**
            ```python
            def process(x: int, y: int) -> int:
                return x + y
            ```

            **Example: Returning a string**
            ```python
            def process(name: str) -> str:
                return f"Hello, {name}!"
            ```

            **Example: Returning a boolean**
            ```python
            def process(value: int, threshold: int) -> bool:
                return value > threshold
            ```

            **Example: Returning a list**
            ```python
            from typing import List

            def process(items: List[str]) -> List[str]:
                return sorted(items)
            ```

            **Example: Returning an object (dict)**
            ```python
            def process(order_count: int, total_revenue: float, days_active: int = 1) -> dict:
                score = (order_count * 10 + total_revenue * 0.1) / days_active
                return {'score': round(score, 2), 'rating': 'high' if score > 100 else 'low'}
            ```

            For complex types, use the `typing` module:

            ```python
            from typing import List, Dict

            def process(items: List[str], config: Dict[str, float]) -> dict:
                count = len(items)
                multiplier = config.get('multiplier', 1.0)
                return {'count': count, 'adjusted': count * multiplier}
            ```

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        language : typing.Optional[typing.Literal["python"]]
            The programming language. Currently only 'python' (Python 3.12) is supported.

        execution_configuration : typing.Optional[ExecutionConfiguration]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[Tool]
            The tool has been created successfully.
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/tools",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "name": name,
                "title": title,
                "description": description,
                "language": language,
                "code": code,
                "execution_configuration": convert_and_respect_annotation_metadata(
                    object_=execution_configuration, annotation=ExecutionConfiguration, direction="write"
                ),
                "type": "lambda",
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 409:
                raise ConflictError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def get(
        self,
        tool_id: str,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Tool]:
        """
        Retrieve the details of a specific tool by its ID, including its configuration and capabilities.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to retrieve.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[Tool]
            The requested tool details.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def delete(
        self,
        tool_id: str,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[None]:
        """
        Permanently delete a tool and all its associated configuration. This action cannot be undone.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to delete.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[None]
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="DELETE",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return AsyncHttpResponse(response=_response, data=None)
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def update(
        self,
        tool_id: str,
        *,
        request: UpdateToolRequest,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[Tool]:
        """
        Update an existing tool's configuration.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the tool to update.

        request : UpdateToolRequest

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[Tool]
            The tool has been updated successfully.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}",
            base_url=self._client_wrapper.get_environment().default,
            method="PATCH",
            json=convert_and_respect_annotation_metadata(
                object_=request, annotation=UpdateToolRequest, direction="write"
            ),
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    Tool,
                    parse_obj_as(
                        type_=Tool,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)

    async def test(
        self,
        tool_id: str,
        *,
        input: typing.Dict[str, typing.Optional[typing.Any]],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        timeout_seconds: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncHttpResponse[TestToolResponse]:
        """
        Execute a Lambda tool with test inputs to verify it works correctly.
        This endpoint allows users to test their functions before using them with agents.
        The function is executed in a secure sandbox environment with the same constraints as production.

        Parameters
        ----------
        tool_id : str
            The unique identifier of the Lambda tool to test.

        input : typing.Dict[str, typing.Optional[typing.Any]]
            The input parameters to pass to the function. Must match the tool's input schema.

        request_timeout : typing.Optional[int]
            The API will make a best effort to complete the request in the specified seconds or time out.

        request_timeout_millis : typing.Optional[int]
            The API will make a best effort to complete the request in the specified milliseconds or time out.

        timeout_seconds : typing.Optional[int]
            Maximum execution time in seconds. If not specified, uses the tool's configured timeout.

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        AsyncHttpResponse[TestToolResponse]
            Test execution completed successfully.
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/tools/{jsonable_encoder(tool_id)}/test",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "input": input,
                "timeout_seconds": timeout_seconds,
            },
            headers={
                "content-type": "application/json",
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                _data = typing.cast(
                    TestToolResponse,
                    parse_obj_as(
                        type_=TestToolResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                return AsyncHttpResponse(response=_response, data=_data)
            if _response.status_code == 400:
                raise BadRequestError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            if _response.status_code == 500:
                raise InternalServerError(
                    headers=dict(_response.headers),
                    body=typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
        raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
