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

import datetime as dt
import typing

from ..commons.types.attachment_request import AttachmentRequest
from ..commons.types.conversation_response import ConversationResponse
from ..commons.types.entity_id_base import EntityIdBase
from ..commons.types.feedback import Feedback
from ..commons.types.feedback_type import FeedbackType
from ..commons.types.response_config import ResponseConfig
from ..commons.types.simulation_context import SimulationContext
from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from ..core.request_options import RequestOptions
from .raw_client import AsyncRawConversationClient, RawConversationClient
from .types.action_form_request_param_value import ActionFormRequestParamValue
from .types.categorization_response import CategorizationResponse
from .types.conversation_field import ConversationField
from .types.conversation_filter import ConversationFilter
from .types.conversation_message_request import ConversationMessageRequest
from .types.conversation_metadata import ConversationMetadata
from .types.conversations_response import ConversationsResponse
from .types.deliver_message_request import DeliverMessageRequest
from .types.deliver_message_response import DeliverMessageResponse
from .types.object_stream_response import ObjectStreamResponse
from .types.stream_response import StreamResponse

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


class ConversationClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._raw_client = RawConversationClient(client_wrapper=client_wrapper)

    @property
    def with_raw_response(self) -> RawConversationClient:
        """
        Retrieves a raw implementation of this client that returns raw responses.

        Returns
        -------
        RawConversationClient
        """
        return self._raw_client

    def initialize(
        self,
        *,
        conversation_id: EntityIdBase,
        messages: typing.Sequence[ConversationMessageRequest],
        simulation_context: typing.Optional[SimulationContext] = OMIT,
        response_config: typing.Optional[ResponseConfig] = OMIT,
        subject: typing.Optional[str] = OMIT,
        url: typing.Optional[str] = OMIT,
        created_at: typing.Optional[dt.datetime] = OMIT,
        updated_at: typing.Optional[dt.datetime] = OMIT,
        tags: typing.Optional[typing.Set[str]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Initialize a new conversation.
        Only required if the ask request wishes to supply conversation level data or when syncing to external systems.

        Conversations can not be modified using this API. If the conversation already exists then the existing conversation will be returned.

        After initialization,
        - metadata can be changed using the `updateConversationMetadata` API.
        - messages can be added to the conversation with the `appendNewMessages` or `ask` APIs.

        Parameters
        ----------
        conversation_id : EntityIdBase
            An externally supplied ID to uniquely identify this conversation

        messages : typing.Sequence[ConversationMessageRequest]
            The messages in the conversation

        simulation_context : typing.Optional[SimulationContext]
            Additional context used for simulation runs. When provided, this conversation will be treated as a simulation and
            may only be created by apps with the appropriate permission. Simulation conversations are excluded from normal
            search results unless explicitly included via the `simulationFilter` field.

        response_config : typing.Optional[ResponseConfig]
            Optional configurations for responses to this conversation

        subject : typing.Optional[str]
            The subject of the conversation

        url : typing.Optional[str]
            The url of the conversation

        created_at : typing.Optional[dt.datetime]
            The date and time the conversation was created

        updated_at : typing.Optional[dt.datetime]
            The date and time the conversation was last updated

        tags : typing.Optional[typing.Set[str]]
            The tags of the conversation. Used for filtering in Agent Designer.

        metadata : typing.Optional[typing.Dict[str, str]]
            The metadata of the conversation supplied by the app which created the conversation.

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

        Returns
        -------
        ConversationResponse
            Initialized conversation

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import EntityIdBase
        from mavenagi.conversation import ConversationMessageRequest

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.initialize(
            conversation_id=EntityIdBase(
                reference_id="referenceId",
            ),
            messages=[
                ConversationMessageRequest(
                    conversation_message_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    user_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    text="text",
                    user_message_type="USER",
                ),
                ConversationMessageRequest(
                    conversation_message_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    user_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    text="text",
                    user_message_type="USER",
                ),
            ],
        )
        """
        _response = self._raw_client.initialize(
            conversation_id=conversation_id,
            messages=messages,
            simulation_context=simulation_context,
            response_config=response_config,
            subject=subject,
            url=url,
            created_at=created_at,
            updated_at=updated_at,
            tags=tags,
            metadata=metadata,
            request_options=request_options,
        )
        return _response.data

    def patch(
        self,
        conversation_id: str,
        *,
        app_id: typing.Optional[str] = OMIT,
        open: typing.Optional[bool] = OMIT,
        llm_enabled: typing.Optional[bool] = OMIT,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Update mutable conversation fields.

        The `appId` field can be provided to update a conversation owned by a different app.
        All other fields will overwrite the existing value on the conversation only if provided.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to patch

        app_id : typing.Optional[str]
            The App ID of the conversation to patch. If not provided the ID of the calling app will be used.

        open : typing.Optional[bool]
            Whether the conversation is able to receive asynchronous messages. Only valid for conversations with the `ASYNC` capability.

        llm_enabled : typing.Optional[bool]
            Whether the LLM is enabled for this conversation.

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            A list of attachments to add to the conversation. Attachments can only be appended. Removal is not allowed.

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

        Returns
        -------
        ConversationResponse

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.patch(
            conversation_id="conversation-0",
            llm_enabled=True,
        )
        """
        _response = self._raw_client.patch(
            conversation_id,
            app_id=app_id,
            open=open,
            llm_enabled=llm_enabled,
            attachments=attachments,
            request_options=request_options,
        )
        return _response.data

    def get(
        self,
        conversation_id: str,
        *,
        app_id: typing.Optional[str] = None,
        translation_language: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Get a conversation

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to get

        app_id : typing.Optional[str]
            The App ID of the conversation to get. If not provided the ID of the calling app will be used.

        translation_language : typing.Optional[str]
            The language to translate the conversation analysis into

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

        Returns
        -------
        ConversationResponse

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.get(
            conversation_id="conversationId",
        )
        """
        _response = self._raw_client.get(
            conversation_id, app_id=app_id, translation_language=translation_language, request_options=request_options
        )
        return _response.data

    def delete(
        self,
        conversation_id: str,
        *,
        reason: str,
        app_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Wipes a conversation of all user data.
        The conversation ID will still exist and non-user specific data will still be retained.
        Attempts to modify or add messages to the conversation will throw an error.

        Simulation conversations will no longer be visible in search results nor metrics.
        Non-simulation conversations will remain visible - they can not be fully removed from the system.

        <Warning>This is a destructive operation and cannot be undone. <br/><br/>
        The exact fields cleared include: the conversation subject, userRequest, agentResponse.
        As well as the text response, followup questions, and backend LLM prompt of all messages.</Warning>

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to delete

        reason : str
            The reason for deleting the conversation. This message will replace all user messages in the conversation.

        app_id : typing.Optional[str]
            The App ID of the conversation to delete. If not provided the ID of the calling app will be used.

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

        Returns
        -------
        None

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.delete(
            conversation_id="conversation-0",
            reason="GDPR deletion request 1234.",
        )
        """
        _response = self._raw_client.delete(
            conversation_id, reason=reason, app_id=app_id, request_options=request_options
        )
        return _response.data

    def append_new_messages(
        self,
        conversation_id: str,
        *,
        request: typing.Sequence[ConversationMessageRequest],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Append messages to an existing conversation. The conversation must be initialized first. If a message with the same ID already exists, it will be ignored. Messages do not allow modification.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to append messages to

        request : typing.Sequence[ConversationMessageRequest]

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

        Returns
        -------
        ConversationResponse
            Updated Conversation

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import EntityIdBase
        from mavenagi.conversation import ConversationMessageRequest

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.append_new_messages(
            conversation_id="conversationId",
            request=[
                ConversationMessageRequest(
                    conversation_message_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    user_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    text="text",
                    user_message_type="USER",
                ),
                ConversationMessageRequest(
                    conversation_message_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    user_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    text="text",
                    user_message_type="USER",
                ),
            ],
        )
        """
        _response = self._raw_client.append_new_messages(
            conversation_id, request=request, request_options=request_options
        )
        return _response.data

    def ask(
        self,
        conversation_id: str,
        *,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Get an answer from Maven for a given user question. If the user question or its answer already exists,
        they will be reused and will not be updated. Messages do not allow modification once generated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitation:
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the question

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Returns
        -------
        ConversationResponse
            Updated Conversation with a user message of the question and a bot message with the response.

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import AttachmentRequest, EntityIdBase

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.ask(
            conversation_id="conversation-0",
            conversation_message_id=EntityIdBase(
                reference_id="message-0",
            ),
            user_id=EntityIdBase(
                reference_id="user-0",
            ),
            text="How do I reset my password?",
            attachments=[
                AttachmentRequest(
                    type="image/png",
                    content="iVBORw0KGgo...",
                )
            ],
            transient_data={"userToken": "abcdef123", "queryApiKey": "foobar456"},
            timezone="America/New_York",
        )
        """
        _response = self._raw_client.ask(
            conversation_id,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        )
        return _response.data

    def ask_stream(
        self,
        conversation_id: str,
        *,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[StreamResponse]:
        """
        Get an answer from Maven for a given user question with a streaming response. The response will be sent as a stream of events.
        The text portions of stream responses should be concatenated to form the full response text.
        Action and metadata events should overwrite past data and do not need concatenation.

        If the user question or its answer already exists, they will be reused and will not be updated.
        Messages do not allow modification once generated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitation:
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the question

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Yields
        ------
        typing.Iterator[StreamResponse]

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import AttachmentRequest, EntityIdBase

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        response = client.conversation.ask_stream(
            conversation_id="conversation-0",
            conversation_message_id=EntityIdBase(
                reference_id="message-0",
            ),
            user_id=EntityIdBase(
                reference_id="user-0",
            ),
            text="How do I reset my password?",
            attachments=[
                AttachmentRequest(
                    type="image/png",
                    content="iVBORw0KGgo...",
                )
            ],
            transient_data={"userToken": "abcdef123", "queryApiKey": "foobar456"},
            timezone="America/New_York",
        )
        for chunk in response:
            yield chunk
        """
        with self._raw_client.ask_stream(
            conversation_id,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        ) as r:
            yield from r.data

    def ask_object_stream(
        self,
        conversation_id: str,
        *,
        schema: str,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[ObjectStreamResponse]:
        """
        Generate a structured object response based on a provided schema and user prompt with a streaming response.
        The response will be sent as a stream of events containing text, start, and end events.
        The text portions of stream responses should be concatenated to form the full response text.

        If the user question and object response already exist, they will be reused and not updated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitations:
        - Schema enforcement is best-effort and may not guarantee exact conformity.
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the object generation request

        schema : str
            JSON schema string defining the expected object shape.

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Yields
        ------
        typing.Iterator[ObjectStreamResponse]

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import EntityIdBase

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        response = client.conversation.ask_object_stream(
            conversation_id="conversationId",
            schema="schema",
            conversation_message_id=EntityIdBase(
                reference_id="referenceId",
            ),
            user_id=EntityIdBase(
                reference_id="referenceId",
            ),
            text="text",
        )
        for chunk in response:
            yield chunk
        """
        with self._raw_client.ask_object_stream(
            conversation_id,
            schema=schema,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        ) as r:
            yield from r.data

    def categorize(
        self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> CategorizationResponse:
        """
        Uses an LLM flow to categorize the conversation. Experimental.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to categorize

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

        Returns
        -------
        CategorizationResponse
            The conversation categorization

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.categorize(
            conversation_id="conversationId",
        )
        """
        _response = self._raw_client.categorize(conversation_id, request_options=request_options)
        return _response.data

    def create_feedback(
        self,
        *,
        feedback_id: EntityIdBase,
        conversation_id: EntityIdBase,
        conversation_message_id: EntityIdBase,
        type: FeedbackType,
        user_id: typing.Optional[EntityIdBase] = OMIT,
        text: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Feedback:
        """
        Update feedback or create it if it doesn't exist

        Parameters
        ----------
        feedback_id : EntityIdBase
            The ID that uniquely identifies this feedback

        conversation_id : EntityIdBase
            The ID that uniquely identifies the the conversation the feedback is about

        conversation_message_id : EntityIdBase
            The ID that uniquely identifies the message within the conversation the feedback is about

        type : FeedbackType
            The type of feedback

        user_id : typing.Optional[EntityIdBase]
            The ID of the user who is creating the feedback

        text : typing.Optional[str]
            The feedback text

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

        Returns
        -------
        Feedback

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import EntityIdBase

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.create_feedback(
            feedback_id=EntityIdBase(
                reference_id="feedback-0",
            ),
            user_id=EntityIdBase(
                reference_id="user-0",
            ),
            conversation_id=EntityIdBase(
                reference_id="conversation-0",
            ),
            conversation_message_id=EntityIdBase(
                reference_id="message-1",
            ),
            type="THUMBS_UP",
            text="Great answer!",
        )
        """
        _response = self._raw_client.create_feedback(
            feedback_id=feedback_id,
            conversation_id=conversation_id,
            conversation_message_id=conversation_message_id,
            type=type,
            user_id=user_id,
            text=text,
            request_options=request_options,
        )
        return _response.data

    def submit_action_form(
        self,
        conversation_id: str,
        *,
        action_form_id: str,
        parameters: typing.Dict[str, ActionFormRequestParamValue],
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Submit a filled out action form

        Parameters
        ----------
        conversation_id : str
            The ID of a conversation the form being submitted belongs to

        action_form_id : str

        parameters : typing.Dict[str, ActionFormRequestParamValue]
            Map of parameter IDs to values provided by the user. All required action fields must be provided.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken. For example, one may put in user tokens as transient data.

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

        Returns
        -------
        ConversationResponse
            Updated Conversation with a new BOT_RESPONSE after taking the action

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.submit_action_form(
            conversation_id="conversationId",
            action_form_id="actionFormId",
            parameters={"parameters": {"key": "value"}},
        )
        """
        _response = self._raw_client.submit_action_form(
            conversation_id,
            action_form_id=action_form_id,
            parameters=parameters,
            transient_data=transient_data,
            request_options=request_options,
        )
        return _response.data

    def add_conversation_metadata(
        self,
        conversation_id: str,
        *,
        request: typing.Dict[str, str],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Dict[str, str]:
        """
        Replaced by `updateConversationMetadata`.

        Adds metadata to an existing conversation. If a metadata field already exists, it will be overwritten.

        Parameters
        ----------
        conversation_id : str
            The ID of a conversation the metadata being added belongs to

        request : typing.Dict[str, str]

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

        Returns
        -------
        typing.Dict[str, str]
            The metadata of the conversation

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.add_conversation_metadata(
            conversation_id="conversationId",
            request={"string": "string"},
        )
        """
        _response = self._raw_client.add_conversation_metadata(
            conversation_id, request=request, request_options=request_options
        )
        return _response.data

    def update_conversation_metadata(
        self,
        conversation_id: str,
        *,
        values: typing.Dict[str, str],
        app_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationMetadata:
        """
        Update metadata supplied by the calling application for an existing conversation.
        Does not modify metadata saved by other apps.

        If a metadata field already exists for the calling app, it will be overwritten.
        If it does not exist, it will be added. Will not remove metadata fields.

        Returns all metadata saved by any app on the conversation.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to modify metadata for

        values : typing.Dict[str, str]
            The metadata values to add to the conversation.

        app_id : typing.Optional[str]
            The App ID of the conversation to modify metadata for. If not provided the ID of the calling app will be used.

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

        Returns
        -------
        ConversationMetadata

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.update_conversation_metadata(
            conversation_id="conversation-0",
            app_id="conversation-owning-app",
            values={"key": "newValue"},
        )
        """
        _response = self._raw_client.update_conversation_metadata(
            conversation_id, values=values, app_id=app_id, request_options=request_options
        )
        return _response.data

    def search(
        self,
        *,
        sort: typing.Optional[ConversationField] = OMIT,
        filter: typing.Optional[ConversationFilter] = OMIT,
        page: typing.Optional[int] = OMIT,
        size: typing.Optional[int] = OMIT,
        sort_desc: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsResponse:
        """
        Search conversations

        Parameters
        ----------
        sort : typing.Optional[ConversationField]

        filter : typing.Optional[ConversationFilter]

        page : typing.Optional[int]
            Page number to return, defaults to 0

        size : typing.Optional[int]
            The size of the page to return, defaults to 20

        sort_desc : typing.Optional[bool]
            Whether to sort descending, defaults to true

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

        Returns
        -------
        ConversationsResponse

        Examples
        --------
        from mavenagi import MavenAGI

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.search()
        """
        _response = self._raw_client.search(
            sort=sort, filter=filter, page=page, size=size, sort_desc=sort_desc, request_options=request_options
        )
        return _response.data

    def deliver_message(
        self, *, request: DeliverMessageRequest, request_options: typing.Optional[RequestOptions] = None
    ) -> DeliverMessageResponse:
        """
        Deliver a message to a user or conversation.

        <Warning>
        Currently, messages can only be successfully delivered to conversations with the `ASYNC` capability that are `open`.
        User message delivery is not yet supported.
        </Warning>

        Parameters
        ----------
        request : DeliverMessageRequest

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

        Returns
        -------
        DeliverMessageResponse

        Examples
        --------
        from mavenagi import MavenAGI
        from mavenagi.commons import EntityIdBase, EntityIdWithoutAgent
        from mavenagi.conversation import (
            ConversationMessageRequest,
            DeliverMessageRequest_User,
        )

        client = MavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )
        client.conversation.deliver_message(
            request=DeliverMessageRequest_User(
                user_id=EntityIdWithoutAgent(
                    type="AGENT",
                    app_id="appId",
                    reference_id="referenceId",
                ),
                message=ConversationMessageRequest(
                    conversation_message_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    user_id=EntityIdBase(
                        reference_id="referenceId",
                    ),
                    text="text",
                    user_message_type="USER",
                ),
            ),
        )
        """
        _response = self._raw_client.deliver_message(request=request, request_options=request_options)
        return _response.data


class AsyncConversationClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._raw_client = AsyncRawConversationClient(client_wrapper=client_wrapper)

    @property
    def with_raw_response(self) -> AsyncRawConversationClient:
        """
        Retrieves a raw implementation of this client that returns raw responses.

        Returns
        -------
        AsyncRawConversationClient
        """
        return self._raw_client

    async def initialize(
        self,
        *,
        conversation_id: EntityIdBase,
        messages: typing.Sequence[ConversationMessageRequest],
        simulation_context: typing.Optional[SimulationContext] = OMIT,
        response_config: typing.Optional[ResponseConfig] = OMIT,
        subject: typing.Optional[str] = OMIT,
        url: typing.Optional[str] = OMIT,
        created_at: typing.Optional[dt.datetime] = OMIT,
        updated_at: typing.Optional[dt.datetime] = OMIT,
        tags: typing.Optional[typing.Set[str]] = OMIT,
        metadata: typing.Optional[typing.Dict[str, str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Initialize a new conversation.
        Only required if the ask request wishes to supply conversation level data or when syncing to external systems.

        Conversations can not be modified using this API. If the conversation already exists then the existing conversation will be returned.

        After initialization,
        - metadata can be changed using the `updateConversationMetadata` API.
        - messages can be added to the conversation with the `appendNewMessages` or `ask` APIs.

        Parameters
        ----------
        conversation_id : EntityIdBase
            An externally supplied ID to uniquely identify this conversation

        messages : typing.Sequence[ConversationMessageRequest]
            The messages in the conversation

        simulation_context : typing.Optional[SimulationContext]
            Additional context used for simulation runs. When provided, this conversation will be treated as a simulation and
            may only be created by apps with the appropriate permission. Simulation conversations are excluded from normal
            search results unless explicitly included via the `simulationFilter` field.

        response_config : typing.Optional[ResponseConfig]
            Optional configurations for responses to this conversation

        subject : typing.Optional[str]
            The subject of the conversation

        url : typing.Optional[str]
            The url of the conversation

        created_at : typing.Optional[dt.datetime]
            The date and time the conversation was created

        updated_at : typing.Optional[dt.datetime]
            The date and time the conversation was last updated

        tags : typing.Optional[typing.Set[str]]
            The tags of the conversation. Used for filtering in Agent Designer.

        metadata : typing.Optional[typing.Dict[str, str]]
            The metadata of the conversation supplied by the app which created the conversation.

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

        Returns
        -------
        ConversationResponse
            Initialized conversation

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import EntityIdBase
        from mavenagi.conversation import ConversationMessageRequest

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.initialize(
                conversation_id=EntityIdBase(
                    reference_id="referenceId",
                ),
                messages=[
                    ConversationMessageRequest(
                        conversation_message_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        user_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        text="text",
                        user_message_type="USER",
                    ),
                    ConversationMessageRequest(
                        conversation_message_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        user_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        text="text",
                        user_message_type="USER",
                    ),
                ],
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.initialize(
            conversation_id=conversation_id,
            messages=messages,
            simulation_context=simulation_context,
            response_config=response_config,
            subject=subject,
            url=url,
            created_at=created_at,
            updated_at=updated_at,
            tags=tags,
            metadata=metadata,
            request_options=request_options,
        )
        return _response.data

    async def patch(
        self,
        conversation_id: str,
        *,
        app_id: typing.Optional[str] = OMIT,
        open: typing.Optional[bool] = OMIT,
        llm_enabled: typing.Optional[bool] = OMIT,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Update mutable conversation fields.

        The `appId` field can be provided to update a conversation owned by a different app.
        All other fields will overwrite the existing value on the conversation only if provided.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to patch

        app_id : typing.Optional[str]
            The App ID of the conversation to patch. If not provided the ID of the calling app will be used.

        open : typing.Optional[bool]
            Whether the conversation is able to receive asynchronous messages. Only valid for conversations with the `ASYNC` capability.

        llm_enabled : typing.Optional[bool]
            Whether the LLM is enabled for this conversation.

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            A list of attachments to add to the conversation. Attachments can only be appended. Removal is not allowed.

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

        Returns
        -------
        ConversationResponse

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.patch(
                conversation_id="conversation-0",
                llm_enabled=True,
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.patch(
            conversation_id,
            app_id=app_id,
            open=open,
            llm_enabled=llm_enabled,
            attachments=attachments,
            request_options=request_options,
        )
        return _response.data

    async def get(
        self,
        conversation_id: str,
        *,
        app_id: typing.Optional[str] = None,
        translation_language: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Get a conversation

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to get

        app_id : typing.Optional[str]
            The App ID of the conversation to get. If not provided the ID of the calling app will be used.

        translation_language : typing.Optional[str]
            The language to translate the conversation analysis into

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

        Returns
        -------
        ConversationResponse

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.get(
                conversation_id="conversationId",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.get(
            conversation_id, app_id=app_id, translation_language=translation_language, request_options=request_options
        )
        return _response.data

    async def delete(
        self,
        conversation_id: str,
        *,
        reason: str,
        app_id: typing.Optional[str] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Wipes a conversation of all user data.
        The conversation ID will still exist and non-user specific data will still be retained.
        Attempts to modify or add messages to the conversation will throw an error.

        Simulation conversations will no longer be visible in search results nor metrics.
        Non-simulation conversations will remain visible - they can not be fully removed from the system.

        <Warning>This is a destructive operation and cannot be undone. <br/><br/>
        The exact fields cleared include: the conversation subject, userRequest, agentResponse.
        As well as the text response, followup questions, and backend LLM prompt of all messages.</Warning>

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to delete

        reason : str
            The reason for deleting the conversation. This message will replace all user messages in the conversation.

        app_id : typing.Optional[str]
            The App ID of the conversation to delete. If not provided the ID of the calling app will be used.

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

        Returns
        -------
        None

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.delete(
                conversation_id="conversation-0",
                reason="GDPR deletion request 1234.",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.delete(
            conversation_id, reason=reason, app_id=app_id, request_options=request_options
        )
        return _response.data

    async def append_new_messages(
        self,
        conversation_id: str,
        *,
        request: typing.Sequence[ConversationMessageRequest],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Append messages to an existing conversation. The conversation must be initialized first. If a message with the same ID already exists, it will be ignored. Messages do not allow modification.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to append messages to

        request : typing.Sequence[ConversationMessageRequest]

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

        Returns
        -------
        ConversationResponse
            Updated Conversation

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import EntityIdBase
        from mavenagi.conversation import ConversationMessageRequest

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.append_new_messages(
                conversation_id="conversationId",
                request=[
                    ConversationMessageRequest(
                        conversation_message_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        user_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        text="text",
                        user_message_type="USER",
                    ),
                    ConversationMessageRequest(
                        conversation_message_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        user_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        text="text",
                        user_message_type="USER",
                    ),
                ],
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.append_new_messages(
            conversation_id, request=request, request_options=request_options
        )
        return _response.data

    async def ask(
        self,
        conversation_id: str,
        *,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Get an answer from Maven for a given user question. If the user question or its answer already exists,
        they will be reused and will not be updated. Messages do not allow modification once generated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitation:
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the question

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Returns
        -------
        ConversationResponse
            Updated Conversation with a user message of the question and a bot message with the response.

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import AttachmentRequest, EntityIdBase

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.ask(
                conversation_id="conversation-0",
                conversation_message_id=EntityIdBase(
                    reference_id="message-0",
                ),
                user_id=EntityIdBase(
                    reference_id="user-0",
                ),
                text="How do I reset my password?",
                attachments=[
                    AttachmentRequest(
                        type="image/png",
                        content="iVBORw0KGgo...",
                    )
                ],
                transient_data={"userToken": "abcdef123", "queryApiKey": "foobar456"},
                timezone="America/New_York",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.ask(
            conversation_id,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        )
        return _response.data

    async def ask_stream(
        self,
        conversation_id: str,
        *,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[StreamResponse]:
        """
        Get an answer from Maven for a given user question with a streaming response. The response will be sent as a stream of events.
        The text portions of stream responses should be concatenated to form the full response text.
        Action and metadata events should overwrite past data and do not need concatenation.

        If the user question or its answer already exists, they will be reused and will not be updated.
        Messages do not allow modification once generated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitation:
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the question

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Yields
        ------
        typing.AsyncIterator[StreamResponse]

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import AttachmentRequest, EntityIdBase

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            response = await client.conversation.ask_stream(
                conversation_id="conversation-0",
                conversation_message_id=EntityIdBase(
                    reference_id="message-0",
                ),
                user_id=EntityIdBase(
                    reference_id="user-0",
                ),
                text="How do I reset my password?",
                attachments=[
                    AttachmentRequest(
                        type="image/png",
                        content="iVBORw0KGgo...",
                    )
                ],
                transient_data={"userToken": "abcdef123", "queryApiKey": "foobar456"},
                timezone="America/New_York",
            )
            async for chunk in response:
                yield chunk


        asyncio.run(main())
        """
        async with self._raw_client.ask_stream(
            conversation_id,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        ) as r:
            async for _chunk in r.data:
                yield _chunk

    async def ask_object_stream(
        self,
        conversation_id: str,
        *,
        schema: str,
        conversation_message_id: EntityIdBase,
        user_id: EntityIdBase,
        text: str,
        attachments: typing.Optional[typing.Sequence[AttachmentRequest]] = OMIT,
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        timezone: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[ObjectStreamResponse]:
        """
        Generate a structured object response based on a provided schema and user prompt with a streaming response.
        The response will be sent as a stream of events containing text, start, and end events.
        The text portions of stream responses should be concatenated to form the full response text.

        If the user question and object response already exist, they will be reused and not updated.

        Concurrency Behavior:
        - If another API call is made for the same user question while a response is mid-stream, partial answers may be returned.
        - The second caller will receive a truncated or partial response depending on where the first stream is in its processing. The first caller's stream will remain unaffected and continue delivering the full response.

        Known Limitations:
        - Schema enforcement is best-effort and may not guarantee exact conformity.
        - The API does not currently expose metadata indicating whether a response or message is incomplete. This will be addressed in a future update.

        Parameters
        ----------
        conversation_id : str
            The ID of a new or existing conversation to use as context for the object generation request

        schema : str
            JSON schema string defining the expected object shape.

        conversation_message_id : EntityIdBase
            Externally supplied ID to uniquely identify this message within the conversation. If a message with this ID already exists it will be reused and will not be updated.

        user_id : EntityIdBase
            Externally supplied ID to uniquely identify the user that created this message

        text : str
            The text of the message

        attachments : typing.Optional[typing.Sequence[AttachmentRequest]]
            The attachments to the message. Image attachments will be sent to the LLM as additional data.
            Non-image attachments can be stored and downloaded from the API but will not be sent to the LLM.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken by this ask request. For example, one may put in user tokens as transient data.

        timezone : typing.Optional[str]
            IANA timezone identifier (e.g. "America/New_York", "Europe/London") to be used for time-based operations in the conversation.

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

        Yields
        ------
        typing.AsyncIterator[ObjectStreamResponse]

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import EntityIdBase

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            response = await client.conversation.ask_object_stream(
                conversation_id="conversationId",
                schema="schema",
                conversation_message_id=EntityIdBase(
                    reference_id="referenceId",
                ),
                user_id=EntityIdBase(
                    reference_id="referenceId",
                ),
                text="text",
            )
            async for chunk in response:
                yield chunk


        asyncio.run(main())
        """
        async with self._raw_client.ask_object_stream(
            conversation_id,
            schema=schema,
            conversation_message_id=conversation_message_id,
            user_id=user_id,
            text=text,
            attachments=attachments,
            transient_data=transient_data,
            timezone=timezone,
            request_options=request_options,
        ) as r:
            async for _chunk in r.data:
                yield _chunk

    async def categorize(
        self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None
    ) -> CategorizationResponse:
        """
        Uses an LLM flow to categorize the conversation. Experimental.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to categorize

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

        Returns
        -------
        CategorizationResponse
            The conversation categorization

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.categorize(
                conversation_id="conversationId",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.categorize(conversation_id, request_options=request_options)
        return _response.data

    async def create_feedback(
        self,
        *,
        feedback_id: EntityIdBase,
        conversation_id: EntityIdBase,
        conversation_message_id: EntityIdBase,
        type: FeedbackType,
        user_id: typing.Optional[EntityIdBase] = OMIT,
        text: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Feedback:
        """
        Update feedback or create it if it doesn't exist

        Parameters
        ----------
        feedback_id : EntityIdBase
            The ID that uniquely identifies this feedback

        conversation_id : EntityIdBase
            The ID that uniquely identifies the the conversation the feedback is about

        conversation_message_id : EntityIdBase
            The ID that uniquely identifies the message within the conversation the feedback is about

        type : FeedbackType
            The type of feedback

        user_id : typing.Optional[EntityIdBase]
            The ID of the user who is creating the feedback

        text : typing.Optional[str]
            The feedback text

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

        Returns
        -------
        Feedback

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import EntityIdBase

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.create_feedback(
                feedback_id=EntityIdBase(
                    reference_id="feedback-0",
                ),
                user_id=EntityIdBase(
                    reference_id="user-0",
                ),
                conversation_id=EntityIdBase(
                    reference_id="conversation-0",
                ),
                conversation_message_id=EntityIdBase(
                    reference_id="message-1",
                ),
                type="THUMBS_UP",
                text="Great answer!",
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.create_feedback(
            feedback_id=feedback_id,
            conversation_id=conversation_id,
            conversation_message_id=conversation_message_id,
            type=type,
            user_id=user_id,
            text=text,
            request_options=request_options,
        )
        return _response.data

    async def submit_action_form(
        self,
        conversation_id: str,
        *,
        action_form_id: str,
        parameters: typing.Dict[str, ActionFormRequestParamValue],
        transient_data: typing.Optional[typing.Dict[str, str]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationResponse:
        """
        Submit a filled out action form

        Parameters
        ----------
        conversation_id : str
            The ID of a conversation the form being submitted belongs to

        action_form_id : str

        parameters : typing.Dict[str, ActionFormRequestParamValue]
            Map of parameter IDs to values provided by the user. All required action fields must be provided.

        transient_data : typing.Optional[typing.Dict[str, str]]
            Transient data which the Maven platform will not persist. This data will only be forwarded to actions taken. For example, one may put in user tokens as transient data.

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

        Returns
        -------
        ConversationResponse
            Updated Conversation with a new BOT_RESPONSE after taking the action

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.submit_action_form(
                conversation_id="conversationId",
                action_form_id="actionFormId",
                parameters={"parameters": {"key": "value"}},
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.submit_action_form(
            conversation_id,
            action_form_id=action_form_id,
            parameters=parameters,
            transient_data=transient_data,
            request_options=request_options,
        )
        return _response.data

    async def add_conversation_metadata(
        self,
        conversation_id: str,
        *,
        request: typing.Dict[str, str],
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Dict[str, str]:
        """
        Replaced by `updateConversationMetadata`.

        Adds metadata to an existing conversation. If a metadata field already exists, it will be overwritten.

        Parameters
        ----------
        conversation_id : str
            The ID of a conversation the metadata being added belongs to

        request : typing.Dict[str, str]

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

        Returns
        -------
        typing.Dict[str, str]
            The metadata of the conversation

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.add_conversation_metadata(
                conversation_id="conversationId",
                request={"string": "string"},
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.add_conversation_metadata(
            conversation_id, request=request, request_options=request_options
        )
        return _response.data

    async def update_conversation_metadata(
        self,
        conversation_id: str,
        *,
        values: typing.Dict[str, str],
        app_id: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationMetadata:
        """
        Update metadata supplied by the calling application for an existing conversation.
        Does not modify metadata saved by other apps.

        If a metadata field already exists for the calling app, it will be overwritten.
        If it does not exist, it will be added. Will not remove metadata fields.

        Returns all metadata saved by any app on the conversation.

        Parameters
        ----------
        conversation_id : str
            The ID of the conversation to modify metadata for

        values : typing.Dict[str, str]
            The metadata values to add to the conversation.

        app_id : typing.Optional[str]
            The App ID of the conversation to modify metadata for. If not provided the ID of the calling app will be used.

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

        Returns
        -------
        ConversationMetadata

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.update_conversation_metadata(
                conversation_id="conversation-0",
                app_id="conversation-owning-app",
                values={"key": "newValue"},
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.update_conversation_metadata(
            conversation_id, values=values, app_id=app_id, request_options=request_options
        )
        return _response.data

    async def search(
        self,
        *,
        sort: typing.Optional[ConversationField] = OMIT,
        filter: typing.Optional[ConversationFilter] = OMIT,
        page: typing.Optional[int] = OMIT,
        size: typing.Optional[int] = OMIT,
        sort_desc: typing.Optional[bool] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ConversationsResponse:
        """
        Search conversations

        Parameters
        ----------
        sort : typing.Optional[ConversationField]

        filter : typing.Optional[ConversationFilter]

        page : typing.Optional[int]
            Page number to return, defaults to 0

        size : typing.Optional[int]
            The size of the page to return, defaults to 20

        sort_desc : typing.Optional[bool]
            Whether to sort descending, defaults to true

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

        Returns
        -------
        ConversationsResponse

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.search()


        asyncio.run(main())
        """
        _response = await self._raw_client.search(
            sort=sort, filter=filter, page=page, size=size, sort_desc=sort_desc, request_options=request_options
        )
        return _response.data

    async def deliver_message(
        self, *, request: DeliverMessageRequest, request_options: typing.Optional[RequestOptions] = None
    ) -> DeliverMessageResponse:
        """
        Deliver a message to a user or conversation.

        <Warning>
        Currently, messages can only be successfully delivered to conversations with the `ASYNC` capability that are `open`.
        User message delivery is not yet supported.
        </Warning>

        Parameters
        ----------
        request : DeliverMessageRequest

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

        Returns
        -------
        DeliverMessageResponse

        Examples
        --------
        import asyncio

        from mavenagi import AsyncMavenAGI
        from mavenagi.commons import EntityIdBase, EntityIdWithoutAgent
        from mavenagi.conversation import (
            ConversationMessageRequest,
            DeliverMessageRequest_User,
        )

        client = AsyncMavenAGI(
            organization_id="YOUR_ORGANIZATION_ID",
            agent_id="YOUR_AGENT_ID",
            app_id="YOUR_APP_ID",
            app_secret="YOUR_APP_SECRET",
        )


        async def main() -> None:
            await client.conversation.deliver_message(
                request=DeliverMessageRequest_User(
                    user_id=EntityIdWithoutAgent(
                        type="AGENT",
                        app_id="appId",
                        reference_id="referenceId",
                    ),
                    message=ConversationMessageRequest(
                        conversation_message_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        user_id=EntityIdBase(
                            reference_id="referenceId",
                        ),
                        text="text",
                        user_message_type="USER",
                    ),
                ),
            )


        asyncio.run(main())
        """
        _response = await self._raw_client.deliver_message(request=request, request_options=request_options)
        return _response.data
