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

import typing
from ..core.client_wrapper import SyncClientWrapper
from ..core.request_options import RequestOptions
from ..types.guideline import Guideline
from ..core.jsonable_encoder import jsonable_encoder
from ..core.pydantic_utilities import parse_obj_as
from ..errors.not_found_error import NotFoundError
from ..errors.unprocessable_entity_error import UnprocessableEntityError
from json.decoder import JSONDecodeError
from ..core.api_error import ApiError
from ..types.invoice import Invoice
from ..types.guideline_creation_result import GuidelineCreationResult
from ..core.serialization import convert_and_respect_annotation_metadata
from ..types.guideline_with_connections_and_tool_associations import (
    GuidelineWithConnectionsAndToolAssociations,
)
from ..types.guideline_connection_update_params import GuidelineConnectionUpdateParams
from ..types.guideline_tool_association_update_params import (
    GuidelineToolAssociationUpdateParams,
)
from ..core.client_wrapper import AsyncClientWrapper

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


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

    def list(
        self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> typing.List[Guideline]:
        """
        Lists all guidelines for the specified agent.

        Returns an empty list if no guidelines exist.
        Guidelines are returned in no guaranteed order.
        Does not include connections or tool associations.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

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

        Returns
        -------
        typing.List[Guideline]
            List of all guidelines for the specified agent

        Examples
        --------
        from parlant.client import ParlantClient

        client = ParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )
        client.guidelines.list(
            agent_id="agent_id",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    typing.List[Guideline],
                    parse_obj_as(
                        type_=typing.List[Guideline],  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def create(
        self,
        agent_id: str,
        *,
        invoices: typing.Sequence[Invoice],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineCreationResult:
        """
        Creates new guidelines from the provided invoices.

        Invoices are obtained by calling the `create_evaluation` method of the client.
        (Equivalent to making a POST request to `/index/evaluations`)
        See the [documentation](https://parlant.io/docs/concepts/customization/guidelines) for more information.

        The guidelines are created in the specified agent's guideline set.
        Tool associations and connections are automatically handled.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        invoices : typing.Sequence[Invoice]

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

        Returns
        -------
        GuidelineCreationResult
            Guidelines successfully created. Returns the created guidelines with their connections and tool associations.

        Examples
        --------
        from parlant.client import (
            CoherenceCheck,
            ConnectionProposition,
            GuidelineContent,
            GuidelineInvoiceData,
            GuidelinePayload,
            Invoice,
            InvoiceData,
            ParlantClient,
            Payload,
        )

        client = ParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )
        client.guidelines.create(
            agent_id="agent_id",
            invoices=[
                Invoice(
                    payload=Payload(
                        guideline=GuidelinePayload(
                            content=GuidelineContent(
                                condition="when the customer asks about pricing",
                                action="provide current pricing information",
                            ),
                            operation="add",
                            coherence_check=True,
                            connection_proposition=True,
                        ),
                    ),
                    checksum="abc123",
                    approved=True,
                    data=InvoiceData(
                        guideline=GuidelineInvoiceData(
                            coherence_checks=[
                                CoherenceCheck(
                                    kind="contradiction_with_existing_guideline",
                                    first=GuidelineContent(
                                        condition="User is frustrated",
                                        action="Respond with technical details",
                                    ),
                                    second=GuidelineContent(
                                        condition="User is frustrated",
                                        action="Focus on emotional support first",
                                    ),
                                    issue="Conflicting approaches to handling user frustration",
                                    severity=7,
                                )
                            ],
                            connection_propositions=[
                                ConnectionProposition(
                                    check_kind="connection_with_existing_guideline",
                                    source=GuidelineContent(
                                        condition="User mentions technical problem",
                                        action="Request system logs",
                                    ),
                                    target=GuidelineContent(
                                        condition="System logs are available",
                                        action="Analyze logs for error patterns",
                                    ),
                                    connection_kind="suggests",
                                )
                            ],
                        ),
                    ),
                )
            ],
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines",
            method="POST",
            json={
                "invoices": convert_and_respect_annotation_metadata(
                    object_=invoices,
                    annotation=typing.Sequence[Invoice],
                    direction="write",
                ),
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineCreationResult,
                    parse_obj_as(
                        type_=GuidelineCreationResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def retrieve(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineWithConnectionsAndToolAssociations:
        """
        Retrieves a specific guideline with all its connections and tool associations.

        Returns both direct and indirect connections between guidelines.
        Tool associations indicate which tools the guideline can use.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

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

        Returns
        -------
        GuidelineWithConnectionsAndToolAssociations
            Guideline details successfully retrieved. Returns the complete guideline with its connections and tool associations.

        Examples
        --------
        from parlant.client import ParlantClient

        client = ParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )
        client.guidelines.retrieve(
            agent_id="agent_id",
            guideline_id="guideline_id",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineWithConnectionsAndToolAssociations,
                    parse_obj_as(
                        type_=GuidelineWithConnectionsAndToolAssociations,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def delete(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Deletes a guideline from the agent.

        Also removes all associated connections and tool associations.
        Deleting a non-existent guideline will return 404.
        No content will be returned from a successful deletion.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

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

        Returns
        -------
        None

        Examples
        --------
        from parlant.client import ParlantClient

        client = ParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )
        client.guidelines.delete(
            agent_id="agent_id",
            guideline_id="guideline_id",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def update(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        connections: typing.Optional[GuidelineConnectionUpdateParams] = OMIT,
        tool_associations: typing.Optional[GuidelineToolAssociationUpdateParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineWithConnectionsAndToolAssociations:
        """
        Updates a guideline's connections and tool associations.

        Only provided attributes will be updated; others remain unchanged.

        Connection rules:

        - A guideline cannot connect to itself
        - Only direct connections can be removed
        - The connection must specify this guideline as source or target

        Tool Association rules:

        - Tool services and tools must exist before creating associations

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

        connections : typing.Optional[GuidelineConnectionUpdateParams]

        tool_associations : typing.Optional[GuidelineToolAssociationUpdateParams]

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

        Returns
        -------
        GuidelineWithConnectionsAndToolAssociations
            Guideline successfully updated. Returns the updated guideline with its connections and tool associations.

        Examples
        --------
        from parlant.client import (
            GuidelineConnectionAddition,
            GuidelineConnectionUpdateParams,
            GuidelineToolAssociationUpdateParams,
            ParlantClient,
            ToolId,
        )

        client = ParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )
        client.guidelines.update(
            agent_id="agent_id",
            guideline_id="guideline_id",
            connections=GuidelineConnectionUpdateParams(
                add=[
                    GuidelineConnectionAddition(
                        source="guide_123xyz",
                        target="guide_789xyz",
                        kind="suggests",
                    )
                ],
                remove=["guide_456xyz"],
            ),
            tool_associations=GuidelineToolAssociationUpdateParams(
                add=[
                    ToolId(
                        service_name="pricing_service",
                        tool_name="get_prices",
                    )
                ],
                remove=[
                    ToolId(
                        service_name="old_service",
                        tool_name="old_tool",
                    )
                ],
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="PATCH",
            json={
                "connections": convert_and_respect_annotation_metadata(
                    object_=connections,
                    annotation=GuidelineConnectionUpdateParams,
                    direction="write",
                ),
                "tool_associations": convert_and_respect_annotation_metadata(
                    object_=tool_associations,
                    annotation=GuidelineToolAssociationUpdateParams,
                    direction="write",
                ),
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineWithConnectionsAndToolAssociations,
                    parse_obj_as(
                        type_=GuidelineWithConnectionsAndToolAssociations,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)


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

    async def list(
        self, agent_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> typing.List[Guideline]:
        """
        Lists all guidelines for the specified agent.

        Returns an empty list if no guidelines exist.
        Guidelines are returned in no guaranteed order.
        Does not include connections or tool associations.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

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

        Returns
        -------
        typing.List[Guideline]
            List of all guidelines for the specified agent

        Examples
        --------
        import asyncio

        from parlant.client import AsyncParlantClient

        client = AsyncParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )


        async def main() -> None:
            await client.guidelines.list(
                agent_id="agent_id",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    typing.List[Guideline],
                    parse_obj_as(
                        type_=typing.List[Guideline],  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def create(
        self,
        agent_id: str,
        *,
        invoices: typing.Sequence[Invoice],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineCreationResult:
        """
        Creates new guidelines from the provided invoices.

        Invoices are obtained by calling the `create_evaluation` method of the client.
        (Equivalent to making a POST request to `/index/evaluations`)
        See the [documentation](https://parlant.io/docs/concepts/customization/guidelines) for more information.

        The guidelines are created in the specified agent's guideline set.
        Tool associations and connections are automatically handled.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        invoices : typing.Sequence[Invoice]

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

        Returns
        -------
        GuidelineCreationResult
            Guidelines successfully created. Returns the created guidelines with their connections and tool associations.

        Examples
        --------
        import asyncio

        from parlant.client import (
            AsyncParlantClient,
            CoherenceCheck,
            ConnectionProposition,
            GuidelineContent,
            GuidelineInvoiceData,
            GuidelinePayload,
            Invoice,
            InvoiceData,
            Payload,
        )

        client = AsyncParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )


        async def main() -> None:
            await client.guidelines.create(
                agent_id="agent_id",
                invoices=[
                    Invoice(
                        payload=Payload(
                            guideline=GuidelinePayload(
                                content=GuidelineContent(
                                    condition="when the customer asks about pricing",
                                    action="provide current pricing information",
                                ),
                                operation="add",
                                coherence_check=True,
                                connection_proposition=True,
                            ),
                        ),
                        checksum="abc123",
                        approved=True,
                        data=InvoiceData(
                            guideline=GuidelineInvoiceData(
                                coherence_checks=[
                                    CoherenceCheck(
                                        kind="contradiction_with_existing_guideline",
                                        first=GuidelineContent(
                                            condition="User is frustrated",
                                            action="Respond with technical details",
                                        ),
                                        second=GuidelineContent(
                                            condition="User is frustrated",
                                            action="Focus on emotional support first",
                                        ),
                                        issue="Conflicting approaches to handling user frustration",
                                        severity=7,
                                    )
                                ],
                                connection_propositions=[
                                    ConnectionProposition(
                                        check_kind="connection_with_existing_guideline",
                                        source=GuidelineContent(
                                            condition="User mentions technical problem",
                                            action="Request system logs",
                                        ),
                                        target=GuidelineContent(
                                            condition="System logs are available",
                                            action="Analyze logs for error patterns",
                                        ),
                                        connection_kind="suggests",
                                    )
                                ],
                            ),
                        ),
                    )
                ],
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines",
            method="POST",
            json={
                "invoices": convert_and_respect_annotation_metadata(
                    object_=invoices,
                    annotation=typing.Sequence[Invoice],
                    direction="write",
                ),
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineCreationResult,
                    parse_obj_as(
                        type_=GuidelineCreationResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def retrieve(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineWithConnectionsAndToolAssociations:
        """
        Retrieves a specific guideline with all its connections and tool associations.

        Returns both direct and indirect connections between guidelines.
        Tool associations indicate which tools the guideline can use.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

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

        Returns
        -------
        GuidelineWithConnectionsAndToolAssociations
            Guideline details successfully retrieved. Returns the complete guideline with its connections and tool associations.

        Examples
        --------
        import asyncio

        from parlant.client import AsyncParlantClient

        client = AsyncParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )


        async def main() -> None:
            await client.guidelines.retrieve(
                agent_id="agent_id",
                guideline_id="guideline_id",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineWithConnectionsAndToolAssociations,
                    parse_obj_as(
                        type_=GuidelineWithConnectionsAndToolAssociations,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def delete(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Deletes a guideline from the agent.

        Also removes all associated connections and tool associations.
        Deleting a non-existent guideline will return 404.
        No content will be returned from a successful deletion.

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

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

        Returns
        -------
        None

        Examples
        --------
        import asyncio

        from parlant.client import AsyncParlantClient

        client = AsyncParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )


        async def main() -> None:
            await client.guidelines.delete(
                agent_id="agent_id",
                guideline_id="guideline_id",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="DELETE",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def update(
        self,
        agent_id: str,
        guideline_id: str,
        *,
        connections: typing.Optional[GuidelineConnectionUpdateParams] = OMIT,
        tool_associations: typing.Optional[GuidelineToolAssociationUpdateParams] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> GuidelineWithConnectionsAndToolAssociations:
        """
        Updates a guideline's connections and tool associations.

        Only provided attributes will be updated; others remain unchanged.

        Connection rules:

        - A guideline cannot connect to itself
        - Only direct connections can be removed
        - The connection must specify this guideline as source or target

        Tool Association rules:

        - Tool services and tools must exist before creating associations

        Parameters
        ----------
        agent_id : str
            Unique identifier for the agent

        guideline_id : str
            Unique identifier for the guideline

        connections : typing.Optional[GuidelineConnectionUpdateParams]

        tool_associations : typing.Optional[GuidelineToolAssociationUpdateParams]

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

        Returns
        -------
        GuidelineWithConnectionsAndToolAssociations
            Guideline successfully updated. Returns the updated guideline with its connections and tool associations.

        Examples
        --------
        import asyncio

        from parlant.client import (
            AsyncParlantClient,
            GuidelineConnectionAddition,
            GuidelineConnectionUpdateParams,
            GuidelineToolAssociationUpdateParams,
            ToolId,
        )

        client = AsyncParlantClient(
            base_url="https://yourhost.com/path/to/api",
        )


        async def main() -> None:
            await client.guidelines.update(
                agent_id="agent_id",
                guideline_id="guideline_id",
                connections=GuidelineConnectionUpdateParams(
                    add=[
                        GuidelineConnectionAddition(
                            source="guide_123xyz",
                            target="guide_789xyz",
                            kind="suggests",
                        )
                    ],
                    remove=["guide_456xyz"],
                ),
                tool_associations=GuidelineToolAssociationUpdateParams(
                    add=[
                        ToolId(
                            service_name="pricing_service",
                            tool_name="get_prices",
                        )
                    ],
                    remove=[
                        ToolId(
                            service_name="old_service",
                            tool_name="old_tool",
                        )
                    ],
                ),
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"agents/{jsonable_encoder(agent_id)}/guidelines/{jsonable_encoder(guideline_id)}",
            method="PATCH",
            json={
                "connections": convert_and_respect_annotation_metadata(
                    object_=connections,
                    annotation=GuidelineConnectionUpdateParams,
                    direction="write",
                ),
                "tool_associations": convert_and_respect_annotation_metadata(
                    object_=tool_associations,
                    annotation=GuidelineToolAssociationUpdateParams,
                    direction="write",
                ),
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    GuidelineWithConnectionsAndToolAssociations,
                    parse_obj_as(
                        type_=GuidelineWithConnectionsAndToolAssociations,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        typing.Optional[typing.Any],
                        parse_obj_as(
                            type_=typing.Optional[typing.Any],  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)
