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

import typing
import urllib.parse
from json.decoder import JSONDecodeError

import httpx

from .core.api_error import ApiError
from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
from .core.jsonable_encoder import jsonable_encoder
from .core.remove_none_from_dict import remove_none_from_dict
from .environment import JulepApiEnvironment
from .types.agent import Agent
from .types.agent_default_settings import AgentDefaultSettings
from .types.chat_response import ChatResponse
from .types.chat_settings_response_format import ChatSettingsResponseFormat
from .types.chat_settings_stop import ChatSettingsStop
from .types.create_additional_info_request import CreateAdditionalInfoRequest
from .types.create_tool_request import CreateToolRequest
from .types.function_def import FunctionDef
from .types.get_agent_additional_info_response import GetAgentAdditionalInfoResponse
from .types.get_agent_memories_response import GetAgentMemoriesResponse
from .types.get_agent_tools_response import GetAgentToolsResponse
from .types.get_history_response import GetHistoryResponse
from .types.get_suggestions_response import GetSuggestionsResponse
from .types.get_user_additional_info_response import GetUserAdditionalInfoResponse
from .types.input_chat_ml_message import InputChatMlMessage
from .types.instruction import Instruction
from .types.list_agents_response import ListAgentsResponse
from .types.list_sessions_response import ListSessionsResponse
from .types.list_users_response import ListUsersResponse
from .types.resource_created_response import ResourceCreatedResponse
from .types.resource_updated_response import ResourceUpdatedResponse
from .types.session import Session
from .types.tool import Tool
from .types.tool_choice_option import ToolChoiceOption
from .types.user import User

try:
    import pydantic.v1 as pydantic  # type: ignore
except ImportError:
    import pydantic  # type: ignore

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


class JulepApi:
    def __init__(
        self,
        *,
        base_url: typing.Optional[str] = None,
        environment: JulepApiEnvironment = JulepApiEnvironment.DEFAULT,
        api_key: str,
        timeout: typing.Optional[float] = 300,
        httpx_client: typing.Optional[httpx.Client] = None,
    ):
        self._client_wrapper = SyncClientWrapper(
            base_url=_get_base_url(base_url=base_url, environment=environment),
            api_key=api_key,
            httpx_client=httpx.Client(timeout=timeout)
            if httpx_client is None
            else httpx_client,
        )

    def list_sessions(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListSessionsResponse:
        """
        List sessions created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of sessions to return

            - offset: typing.Optional[int]. Number of sessions to skip (sorted created_at descending order)
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.list_sessions()
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/sessions"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListSessionsResponse, _response.json())  # type: ignore
        try:
            _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_session(
        self, *, user_id: str, agent_id: str, situation: typing.Optional[str] = OMIT
    ) -> ResourceCreatedResponse:
        """
        Create a session between an agent and a user

        Parameters:
            - user_id: str. User ID of user to associate with this session

            - agent_id: str. Agent ID of agent to associate with this session

            - situation: typing.Optional[str]. A specific situation that sets the background for this session
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_session(
            user_id="string",
            agent_id="string",
        )
        """
        _request: typing.Dict[str, typing.Any] = {
            "user_id": user_id,
            "agent_id": agent_id,
        }
        if situation is not OMIT:
            _request["situation"] = situation
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/sessions"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 list_users(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListUsersResponse:
        """
        List users created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of items to return

            - offset: typing.Optional[int]. Number of items to skip (sorted created_at descending order)
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.list_users()
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/users"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListUsersResponse, _response.json())  # type: ignore
        try:
            _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_user(
        self,
        *,
        name: typing.Optional[str] = OMIT,
        about: typing.Optional[str] = OMIT,
        additional_information: typing.Optional[
            typing.List[CreateAdditionalInfoRequest]
        ] = OMIT,
    ) -> ResourceCreatedResponse:
        """
        Create a new user

        Parameters:
            - name: typing.Optional[str]. Name of the user

            - about: typing.Optional[str]. About the user

            - additional_information: typing.Optional[typing.List[CreateAdditionalInfoRequest]]. List of additional info about user
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_user(
            additional_information=[
                CreateAdditionalInfoRequest(
                    title="string",
                    content="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if name is not OMIT:
            _request["name"] = name
        if about is not OMIT:
            _request["about"] = about
        if additional_information is not OMIT:
            _request["additional_information"] = additional_information
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/users"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 list_agents(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListAgentsResponse:
        """
        List agents created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of items to return

            - offset: typing.Optional[int]. Number of items to skip (sorted created_at descending order)
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.list_agents()
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/agents"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListAgentsResponse, _response.json())  # type: ignore
        try:
            _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_agent(
        self,
        *,
        name: str,
        about: str,
        instructions: typing.Optional[typing.List[Instruction]] = OMIT,
        tools: typing.Optional[typing.List[CreateToolRequest]] = OMIT,
        default_settings: typing.Optional[AgentDefaultSettings] = OMIT,
        model: str,
        additional_info: typing.Optional[
            typing.List[CreateAdditionalInfoRequest]
        ] = OMIT,
    ) -> ResourceCreatedResponse:
        """
        Create a new agent

        Parameters:
            - name: str. Name of the agent

            - about: str. About the agent

            - instructions: typing.Optional[typing.List[Instruction]]. List of instructions for the agent

            - tools: typing.Optional[typing.List[CreateToolRequest]]. A list of tools the model may call. Currently, only `function`s are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.

            - default_settings: typing.Optional[AgentDefaultSettings]. Default model settings to start every session with

            - model: str. Name of the model that the agent is supposed to use

            - additional_info: typing.Optional[typing.List[CreateAdditionalInfoRequest]]. List of additional info about agent
        ---
        from julep import (
            AgentDefaultSettings,
            CreateAdditionalInfoRequest,
            CreateToolRequest,
            CreateToolRequestType,
            FunctionDef,
            Instruction,
        )
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_agent(
            name="string",
            about="string",
            instructions=[
                Instruction(
                    content="string",
                )
            ],
            tools=[
                CreateToolRequest(
                    type=CreateToolRequestType.FUNCTION,
                    definition=FunctionDef(
                        name="string",
                        parameters={"string": "string"},
                    ),
                )
            ],
            default_settings=AgentDefaultSettings(
                temperature=1.0,
                top_p=1.0,
            ),
            model="string",
            additional_info=[
                CreateAdditionalInfoRequest(
                    title="string",
                    content="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {
            "name": name,
            "about": about,
            "model": model,
        }
        if instructions is not OMIT:
            _request["instructions"] = instructions
        if tools is not OMIT:
            _request["tools"] = tools
        if default_settings is not OMIT:
            _request["default_settings"] = default_settings
        if additional_info is not OMIT:
            _request["additional_info"] = additional_info
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/agents"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 get_session(self, session_id: str) -> Session:
        """


        Parameters:
            - session_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_session(
            session_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(Session, _response.json())  # type: ignore
        try:
            _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_session(
        self, session_id: str, *, situation: str
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - session_id: str.

            - situation: str. Updated situation for this session
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.update_session(
            session_id="string",
            situation="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            json=jsonable_encoder({"situation": situation}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_session(self, session_id: str) -> None:
        """


        Parameters:
            - session_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_session(
            session_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_suggestions(
        self,
        session_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetSuggestionsResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - session_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_suggestions(
            session_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/suggestions",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetSuggestionsResponse, _response.json())  # type: ignore
        try:
            _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 get_history(
        self,
        session_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetHistoryResponse:
        """
        Sorted (created_at ascending)

        Parameters:
            - session_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_history(
            session_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/history",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetHistoryResponse, _response.json())  # type: ignore
        try:
            _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 chat(
        self,
        session_id: str,
        *,
        messages: typing.List[InputChatMlMessage],
        tools: typing.Optional[typing.List[Tool]] = OMIT,
        tool_choice: typing.Optional[ToolChoiceOption] = OMIT,
        frequency_penalty: typing.Optional[float] = OMIT,
        length_penalty: typing.Optional[float] = OMIT,
        logit_bias: typing.Optional[typing.Dict[str, typing.Optional[int]]] = OMIT,
        max_tokens: typing.Optional[int] = OMIT,
        presence_penalty: typing.Optional[float] = OMIT,
        repetition_penalty: typing.Optional[float] = OMIT,
        response_format: typing.Optional[ChatSettingsResponseFormat] = OMIT,
        seed: typing.Optional[int] = OMIT,
        stop: typing.Optional[ChatSettingsStop] = OMIT,
        stream: typing.Optional[bool] = OMIT,
        temperature: typing.Optional[float] = OMIT,
        top_p: typing.Optional[float] = OMIT,
        recall: typing.Optional[bool] = OMIT,
        remember: typing.Optional[bool] = OMIT,
    ) -> ChatResponse:
        """


        Parameters:
            - session_id: str.

            - messages: typing.List[InputChatMlMessage]. A list of new input messages comprising the conversation so far.

            - tools: typing.Optional[typing.List[Tool]]. (Advanced) List of tools that are provided in addition to agent's default set of tools. Functions of same name in agent set are overriden

            - tool_choice: typing.Optional[ToolChoiceOption]. Can be one of existing tools given to the agent earlier or the ones included in the request

            - frequency_penalty: typing.Optional[float]. (OpenAI-like) Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - length_penalty: typing.Optional[float]. (Huggingface-like) Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.

            - logit_bias: typing.Optional[typing.Dict[str, typing.Optional[int]]]. Modify the likelihood of specified tokens appearing in the completion.

                                                                                   Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
            - max_tokens: typing.Optional[int]. The maximum number of tokens to generate in the chat completion.

                                                The total length of input tokens and generated tokens is limited by the model's context length.
            - presence_penalty: typing.Optional[float]. (OpenAI-like) Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - repetition_penalty: typing.Optional[float]. (Huggingface-like) Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - response_format: typing.Optional[ChatSettingsResponseFormat]. An object specifying the format that the model must output.

                                                                            Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON.

                                                                            **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly "stuck" request. Also note that the message content may be partially cut off if `finish_reason="length"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.
            - seed: typing.Optional[int]. This feature is in Beta.
                                          If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
                                          Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
            - stop: typing.Optional[ChatSettingsStop]. Up to 4 sequences where the API will stop generating further tokens.

            - stream: typing.Optional[bool]. If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).

            - temperature: typing.Optional[float]. What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.

            - top_p: typing.Optional[float]. Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.

            - recall: typing.Optional[bool]. Whether previous memories should be recalled or not

            - remember: typing.Optional[bool]. Whether this interaction should be recorded in memory or not
        ---
        from julep import (
            ChatSettingsResponseFormat,
            ChatSettingsResponseFormatType,
            FunctionDef,
            InputChatMlMessage,
            InputChatMlMessageRole,
            Tool,
            ToolType,
        )
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.chat(
            session_id="string",
            response_format=ChatSettingsResponseFormat(
                type=ChatSettingsResponseFormatType.TEXT,
            ),
            temperature=1.0,
            top_p=1.0,
            messages=[
                InputChatMlMessage(
                    role=InputChatMlMessageRole.USER,
                    content="string",
                )
            ],
            tools=[
                Tool(
                    type=ToolType.FUNCTION,
                    definition=FunctionDef(
                        name="string",
                        parameters={"string": "string"},
                    ),
                    id="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {"messages": messages}
        if tools is not OMIT:
            _request["tools"] = tools
        if tool_choice is not OMIT:
            _request["tool_choice"] = tool_choice
        if frequency_penalty is not OMIT:
            _request["frequency_penalty"] = frequency_penalty
        if length_penalty is not OMIT:
            _request["length_penalty"] = length_penalty
        if logit_bias is not OMIT:
            _request["logit_bias"] = logit_bias
        if max_tokens is not OMIT:
            _request["max_tokens"] = max_tokens
        if presence_penalty is not OMIT:
            _request["presence_penalty"] = presence_penalty
        if repetition_penalty is not OMIT:
            _request["repetition_penalty"] = repetition_penalty
        if response_format is not OMIT:
            _request["response_format"] = response_format
        if seed is not OMIT:
            _request["seed"] = seed
        if stop is not OMIT:
            _request["stop"] = stop
        if stream is not OMIT:
            _request["stream"] = stream
        if temperature is not OMIT:
            _request["temperature"] = temperature
        if top_p is not OMIT:
            _request["top_p"] = top_p
        if recall is not OMIT:
            _request["recall"] = recall
        if remember is not OMIT:
            _request["remember"] = remember
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/chat",
            ),
            json=jsonable_encoder(_request),
            headers=remove_none_from_dict(
                {**self._client_wrapper.get_headers(), "Accept": "application/json"}
            ),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ChatResponse, _response.json())  # type: ignore
        try:
            _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 get_agent_memories(
        self,
        agent_id: str,
        *,
        query: str,
        types: typing.Optional[typing.Union[str, typing.List[str]]] = None,
        user_id: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentMemoriesResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - query: str.

            - types: typing.Optional[typing.Union[str, typing.List[str]]]. episode, belief, entity

            - user_id: typing.Optional[str].

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_agent_memories(
            agent_id="string",
            query="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/memories",
            ),
            params=remove_none_from_dict(
                {
                    "query": query,
                    "types": types,
                    "user_id": user_id,
                    "limit": limit,
                    "offset": offset,
                }
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentMemoriesResponse, _response.json())  # type: ignore
        try:
            _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 get_user(self, user_id: str) -> User:
        """


        Parameters:
            - user_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_user(
            user_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(User, _response.json())  # type: ignore
        try:
            _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_user(
        self,
        user_id: str,
        *,
        about: typing.Optional[str] = OMIT,
        name: typing.Optional[str] = OMIT,
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - user_id: str.

            - about: typing.Optional[str]. About the user

            - name: typing.Optional[str]. Name of the user
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.update_user(
            user_id="string",
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if about is not OMIT:
            _request["about"] = about
        if name is not OMIT:
            _request["name"] = name
        _response = self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_user(self, user_id: str) -> None:
        """


        Parameters:
            - user_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_user(
            user_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent(self, agent_id: str) -> Agent:
        """


        Parameters:
            - agent_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_agent(
            agent_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(Agent, _response.json())  # type: ignore
        try:
            _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_agent(
        self,
        agent_id: str,
        *,
        about: typing.Optional[str] = OMIT,
        instructions: typing.Optional[typing.List[Instruction]] = OMIT,
        name: typing.Optional[str] = OMIT,
        model: typing.Optional[str] = OMIT,
        default_settings: typing.Optional[AgentDefaultSettings] = OMIT,
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - agent_id: str.

            - about: typing.Optional[str]. About the agent

            - instructions: typing.Optional[typing.List[Instruction]]. List of instructions for the agent

            - name: typing.Optional[str]. Name of the agent

            - model: typing.Optional[str]. Name of the model that the agent is supposed to use

            - default_settings: typing.Optional[AgentDefaultSettings]. Default model settings to start every session with
        ---
        from julep import AgentDefaultSettings, Instruction
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.update_agent(
            agent_id="string",
            instructions=[
                Instruction(
                    content="string",
                )
            ],
            default_settings=AgentDefaultSettings(
                temperature=1.0,
                top_p=1.0,
            ),
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if about is not OMIT:
            _request["about"] = about
        if instructions is not OMIT:
            _request["instructions"] = instructions
        if name is not OMIT:
            _request["name"] = name
        if model is not OMIT:
            _request["model"] = model
        if default_settings is not OMIT:
            _request["default_settings"] = default_settings
        _response = self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_agent(self, agent_id: str) -> None:
        """


        Parameters:
            - agent_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_agent(
            agent_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent_additional_info(
        self,
        agent_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentAdditionalInfoResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_agent_additional_info(
            agent_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentAdditionalInfoResponse, _response.json())  # type: ignore
        try:
            _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_agent_additional_info(
        self, agent_id: str, *, request: CreateAdditionalInfoRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - agent_id: str.

            - request: CreateAdditionalInfoRequest.
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_agent_additional_info(
            agent_id="string",
            request=CreateAdditionalInfoRequest(
                title="string",
                content="string",
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 get_user_additional_info(
        self,
        user_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetUserAdditionalInfoResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - user_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_user_additional_info(
            user_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetUserAdditionalInfoResponse, _response.json())  # type: ignore
        try:
            _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_user_additional_info(
        self, user_id: str, *, request: CreateAdditionalInfoRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - user_id: str.

            - request: CreateAdditionalInfoRequest.
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_user_additional_info(
            user_id="string",
            request=CreateAdditionalInfoRequest(
                title="string",
                content="string",
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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_user_additional_info(
        self, user_id: str, additional_info_id: str
    ) -> None:
        """


        Parameters:
            - user_id: str.

            - additional_info_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_user_additional_info(
            user_id="string",
            additional_info_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info/{additional_info_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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_agent_additional_info(
        self, agent_id: str, additional_info_id: str
    ) -> None:
        """


        Parameters:
            - agent_id: str.

            - additional_info_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_agent_additional_info(
            agent_id="string",
            additional_info_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info/{additional_info_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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_agent_memory(self, agent_id: str, memory_id: str) -> None:
        """


        Parameters:
            - agent_id: str.

            - memory_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_agent_memory(
            agent_id="string",
            memory_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/memories/{memory_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent_tools(
        self,
        agent_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentToolsResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.get_agent_tools(
            agent_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentToolsResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(
        self, agent_id: str, *, request: CreateToolRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - agent_id: str.

            - request: CreateToolRequest.
        ---
        from julep import CreateToolRequest, CreateToolRequestType, FunctionDef
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.create_agent_tool(
            agent_id="string",
            request=CreateToolRequest(
                type=CreateToolRequestType.FUNCTION,
                definition=FunctionDef(
                    name="string",
                    parameters={"string": "string"},
                ),
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(
        self, agent_id: str, tool_id: str, *, definition: FunctionDef
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - agent_id: str.

            - tool_id: str.

            - definition: FunctionDef. Function definition and parameters
        ---
        from julep import FunctionDef
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.update_agent_tool(
            agent_id="string",
            tool_id="string",
            definition=FunctionDef(
                name="string",
                parameters={"string": "string"},
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools/{tool_id}",
            ),
            json=jsonable_encoder({"definition": definition}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(self, agent_id: str, tool_id: str) -> None:
        """


        Parameters:
            - agent_id: str.

            - tool_id: str.
        ---
        from julep.client import JulepApi

        client = JulepApi(
            api_key="YOUR_API_KEY",
        )
        client.delete_agent_tool(
            agent_id="string",
            tool_id="string",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools/{tool_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 AsyncJulepApi:
    def __init__(
        self,
        *,
        base_url: typing.Optional[str] = None,
        environment: JulepApiEnvironment = JulepApiEnvironment.DEFAULT,
        api_key: str,
        timeout: typing.Optional[float] = 300,
        httpx_client: typing.Optional[httpx.AsyncClient] = None,
    ):
        self._client_wrapper = AsyncClientWrapper(
            base_url=_get_base_url(base_url=base_url, environment=environment),
            api_key=api_key,
            httpx_client=httpx.AsyncClient(timeout=timeout)
            if httpx_client is None
            else httpx_client,
        )

    async def list_sessions(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListSessionsResponse:
        """
        List sessions created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of sessions to return

            - offset: typing.Optional[int]. Number of sessions to skip (sorted created_at descending order)
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.list_sessions()
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/sessions"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListSessionsResponse, _response.json())  # type: ignore
        try:
            _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_session(
        self, *, user_id: str, agent_id: str, situation: typing.Optional[str] = OMIT
    ) -> ResourceCreatedResponse:
        """
        Create a session between an agent and a user

        Parameters:
            - user_id: str. User ID of user to associate with this session

            - agent_id: str. Agent ID of agent to associate with this session

            - situation: typing.Optional[str]. A specific situation that sets the background for this session
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_session(
            user_id="string",
            agent_id="string",
        )
        """
        _request: typing.Dict[str, typing.Any] = {
            "user_id": user_id,
            "agent_id": agent_id,
        }
        if situation is not OMIT:
            _request["situation"] = situation
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/sessions"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 list_users(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListUsersResponse:
        """
        List users created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of items to return

            - offset: typing.Optional[int]. Number of items to skip (sorted created_at descending order)
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.list_users()
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/users"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListUsersResponse, _response.json())  # type: ignore
        try:
            _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_user(
        self,
        *,
        name: typing.Optional[str] = OMIT,
        about: typing.Optional[str] = OMIT,
        additional_information: typing.Optional[
            typing.List[CreateAdditionalInfoRequest]
        ] = OMIT,
    ) -> ResourceCreatedResponse:
        """
        Create a new user

        Parameters:
            - name: typing.Optional[str]. Name of the user

            - about: typing.Optional[str]. About the user

            - additional_information: typing.Optional[typing.List[CreateAdditionalInfoRequest]]. List of additional info about user
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_user(
            additional_information=[
                CreateAdditionalInfoRequest(
                    title="string",
                    content="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if name is not OMIT:
            _request["name"] = name
        if about is not OMIT:
            _request["about"] = about
        if additional_information is not OMIT:
            _request["additional_information"] = additional_information
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/users"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 list_agents(
        self, *, limit: typing.Optional[int] = None, offset: typing.Optional[int] = None
    ) -> ListAgentsResponse:
        """
        List agents created (use limit/offset pagination to get large number of sessions; sorted by descending order of `created_at`)

        Parameters:
            - limit: typing.Optional[int]. Number of items to return

            - offset: typing.Optional[int]. Number of items to skip (sorted created_at descending order)
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.list_agents()
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/agents"
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ListAgentsResponse, _response.json())  # type: ignore
        try:
            _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_agent(
        self,
        *,
        name: str,
        about: str,
        instructions: typing.Optional[typing.List[Instruction]] = OMIT,
        tools: typing.Optional[typing.List[CreateToolRequest]] = OMIT,
        default_settings: typing.Optional[AgentDefaultSettings] = OMIT,
        model: str,
        additional_info: typing.Optional[
            typing.List[CreateAdditionalInfoRequest]
        ] = OMIT,
    ) -> ResourceCreatedResponse:
        """
        Create a new agent

        Parameters:
            - name: str. Name of the agent

            - about: str. About the agent

            - instructions: typing.Optional[typing.List[Instruction]]. List of instructions for the agent

            - tools: typing.Optional[typing.List[CreateToolRequest]]. A list of tools the model may call. Currently, only `function`s are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.

            - default_settings: typing.Optional[AgentDefaultSettings]. Default model settings to start every session with

            - model: str. Name of the model that the agent is supposed to use

            - additional_info: typing.Optional[typing.List[CreateAdditionalInfoRequest]]. List of additional info about agent
        ---
        from julep import (
            AgentDefaultSettings,
            CreateAdditionalInfoRequest,
            CreateToolRequest,
            CreateToolRequestType,
            FunctionDef,
            Instruction,
        )
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_agent(
            name="string",
            about="string",
            instructions=[
                Instruction(
                    content="string",
                )
            ],
            tools=[
                CreateToolRequest(
                    type=CreateToolRequestType.FUNCTION,
                    definition=FunctionDef(
                        name="string",
                        parameters={"string": "string"},
                    ),
                )
            ],
            default_settings=AgentDefaultSettings(
                temperature=1.0,
                top_p=1.0,
            ),
            model="string",
            additional_info=[
                CreateAdditionalInfoRequest(
                    title="string",
                    content="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {
            "name": name,
            "about": about,
            "model": model,
        }
        if instructions is not OMIT:
            _request["instructions"] = instructions
        if tools is not OMIT:
            _request["tools"] = tools
        if default_settings is not OMIT:
            _request["default_settings"] = default_settings
        if additional_info is not OMIT:
            _request["additional_info"] = additional_info
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", "api/agents"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 get_session(self, session_id: str) -> Session:
        """


        Parameters:
            - session_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_session(
            session_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(Session, _response.json())  # type: ignore
        try:
            _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_session(
        self, session_id: str, *, situation: str
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - session_id: str.

            - situation: str. Updated situation for this session
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.update_session(
            session_id="string",
            situation="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            json=jsonable_encoder({"situation": situation}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_session(self, session_id: str) -> None:
        """


        Parameters:
            - session_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_session(
            session_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/sessions/{session_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_suggestions(
        self,
        session_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetSuggestionsResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - session_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_suggestions(
            session_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/suggestions",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetSuggestionsResponse, _response.json())  # type: ignore
        try:
            _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 get_history(
        self,
        session_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetHistoryResponse:
        """
        Sorted (created_at ascending)

        Parameters:
            - session_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_history(
            session_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/history",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetHistoryResponse, _response.json())  # type: ignore
        try:
            _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 chat(
        self,
        session_id: str,
        *,
        messages: typing.List[InputChatMlMessage],
        tools: typing.Optional[typing.List[Tool]] = OMIT,
        tool_choice: typing.Optional[ToolChoiceOption] = OMIT,
        frequency_penalty: typing.Optional[float] = OMIT,
        length_penalty: typing.Optional[float] = OMIT,
        logit_bias: typing.Optional[typing.Dict[str, typing.Optional[int]]] = OMIT,
        max_tokens: typing.Optional[int] = OMIT,
        presence_penalty: typing.Optional[float] = OMIT,
        repetition_penalty: typing.Optional[float] = OMIT,
        response_format: typing.Optional[ChatSettingsResponseFormat] = OMIT,
        seed: typing.Optional[int] = OMIT,
        stop: typing.Optional[ChatSettingsStop] = OMIT,
        stream: typing.Optional[bool] = OMIT,
        temperature: typing.Optional[float] = OMIT,
        top_p: typing.Optional[float] = OMIT,
        recall: typing.Optional[bool] = OMIT,
        remember: typing.Optional[bool] = OMIT,
    ) -> ChatResponse:
        """


        Parameters:
            - session_id: str.

            - messages: typing.List[InputChatMlMessage]. A list of new input messages comprising the conversation so far.

            - tools: typing.Optional[typing.List[Tool]]. (Advanced) List of tools that are provided in addition to agent's default set of tools. Functions of same name in agent set are overriden

            - tool_choice: typing.Optional[ToolChoiceOption]. Can be one of existing tools given to the agent earlier or the ones included in the request

            - frequency_penalty: typing.Optional[float]. (OpenAI-like) Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - length_penalty: typing.Optional[float]. (Huggingface-like) Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.

            - logit_bias: typing.Optional[typing.Dict[str, typing.Optional[int]]]. Modify the likelihood of specified tokens appearing in the completion.

                                                                                   Accepts a JSON object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100. Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model, but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban or exclusive selection of the relevant token.
            - max_tokens: typing.Optional[int]. The maximum number of tokens to generate in the chat completion.

                                                The total length of input tokens and generated tokens is limited by the model's context length.
            - presence_penalty: typing.Optional[float]. (OpenAI-like) Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - repetition_penalty: typing.Optional[float]. (Huggingface-like) Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.

            - response_format: typing.Optional[ChatSettingsResponseFormat]. An object specifying the format that the model must output.

                                                                            Setting to `{ "type": "json_object" }` enables JSON mode, which guarantees the message the model generates is valid JSON.

                                                                            **Important:** when using JSON mode, you **must** also instruct the model to produce JSON yourself via a system or user message. Without this, the model may generate an unending stream of whitespace until the generation reaches the token limit, resulting in a long-running and seemingly "stuck" request. Also note that the message content may be partially cut off if `finish_reason="length"`, which indicates the generation exceeded `max_tokens` or the conversation exceeded the max context length.
            - seed: typing.Optional[int]. This feature is in Beta.
                                          If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same `seed` and parameters should return the same result.
                                          Determinism is not guaranteed, and you should refer to the `system_fingerprint` response parameter to monitor changes in the backend.
            - stop: typing.Optional[ChatSettingsStop]. Up to 4 sequences where the API will stop generating further tokens.

            - stream: typing.Optional[bool]. If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message. [Example Python code](https://cookbook.openai.com/examples/how_to_stream_completions).

            - temperature: typing.Optional[float]. What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.

            - top_p: typing.Optional[float]. Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.

            - recall: typing.Optional[bool]. Whether previous memories should be recalled or not

            - remember: typing.Optional[bool]. Whether this interaction should be recorded in memory or not
        ---
        from julep import (
            ChatSettingsResponseFormat,
            ChatSettingsResponseFormatType,
            FunctionDef,
            InputChatMlMessage,
            InputChatMlMessageRole,
            Tool,
            ToolType,
        )
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.chat(
            session_id="string",
            response_format=ChatSettingsResponseFormat(
                type=ChatSettingsResponseFormatType.TEXT,
            ),
            temperature=1.0,
            top_p=1.0,
            messages=[
                InputChatMlMessage(
                    role=InputChatMlMessageRole.USER,
                    content="string",
                )
            ],
            tools=[
                Tool(
                    type=ToolType.FUNCTION,
                    definition=FunctionDef(
                        name="string",
                        parameters={"string": "string"},
                    ),
                    id="string",
                )
            ],
        )
        """
        _request: typing.Dict[str, typing.Any] = {"messages": messages}
        if tools is not OMIT:
            _request["tools"] = tools
        if tool_choice is not OMIT:
            _request["tool_choice"] = tool_choice
        if frequency_penalty is not OMIT:
            _request["frequency_penalty"] = frequency_penalty
        if length_penalty is not OMIT:
            _request["length_penalty"] = length_penalty
        if logit_bias is not OMIT:
            _request["logit_bias"] = logit_bias
        if max_tokens is not OMIT:
            _request["max_tokens"] = max_tokens
        if presence_penalty is not OMIT:
            _request["presence_penalty"] = presence_penalty
        if repetition_penalty is not OMIT:
            _request["repetition_penalty"] = repetition_penalty
        if response_format is not OMIT:
            _request["response_format"] = response_format
        if seed is not OMIT:
            _request["seed"] = seed
        if stop is not OMIT:
            _request["stop"] = stop
        if stream is not OMIT:
            _request["stream"] = stream
        if temperature is not OMIT:
            _request["temperature"] = temperature
        if top_p is not OMIT:
            _request["top_p"] = top_p
        if recall is not OMIT:
            _request["recall"] = recall
        if remember is not OMIT:
            _request["remember"] = remember
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/sessions/{session_id}/chat",
            ),
            json=jsonable_encoder(_request),
            headers=remove_none_from_dict(
                {**self._client_wrapper.get_headers(), "Accept": "application/json"}
            ),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ChatResponse, _response.json())  # type: ignore
        try:
            _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 get_agent_memories(
        self,
        agent_id: str,
        *,
        query: str,
        types: typing.Optional[typing.Union[str, typing.List[str]]] = None,
        user_id: typing.Optional[str] = None,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentMemoriesResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - query: str.

            - types: typing.Optional[typing.Union[str, typing.List[str]]]. episode, belief, entity

            - user_id: typing.Optional[str].

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_agent_memories(
            agent_id="string",
            query="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/memories",
            ),
            params=remove_none_from_dict(
                {
                    "query": query,
                    "types": types,
                    "user_id": user_id,
                    "limit": limit,
                    "offset": offset,
                }
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentMemoriesResponse, _response.json())  # type: ignore
        try:
            _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 get_user(self, user_id: str) -> User:
        """


        Parameters:
            - user_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_user(
            user_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(User, _response.json())  # type: ignore
        try:
            _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_user(
        self,
        user_id: str,
        *,
        about: typing.Optional[str] = OMIT,
        name: typing.Optional[str] = OMIT,
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - user_id: str.

            - about: typing.Optional[str]. About the user

            - name: typing.Optional[str]. Name of the user
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.update_user(
            user_id="string",
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if about is not OMIT:
            _request["about"] = about
        if name is not OMIT:
            _request["name"] = name
        _response = await self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_user(self, user_id: str) -> None:
        """


        Parameters:
            - user_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_user(
            user_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/users/{user_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent(self, agent_id: str) -> Agent:
        """


        Parameters:
            - agent_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_agent(
            agent_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(Agent, _response.json())  # type: ignore
        try:
            _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_agent(
        self,
        agent_id: str,
        *,
        about: typing.Optional[str] = OMIT,
        instructions: typing.Optional[typing.List[Instruction]] = OMIT,
        name: typing.Optional[str] = OMIT,
        model: typing.Optional[str] = OMIT,
        default_settings: typing.Optional[AgentDefaultSettings] = OMIT,
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - agent_id: str.

            - about: typing.Optional[str]. About the agent

            - instructions: typing.Optional[typing.List[Instruction]]. List of instructions for the agent

            - name: typing.Optional[str]. Name of the agent

            - model: typing.Optional[str]. Name of the model that the agent is supposed to use

            - default_settings: typing.Optional[AgentDefaultSettings]. Default model settings to start every session with
        ---
        from julep import AgentDefaultSettings, Instruction
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.update_agent(
            agent_id="string",
            instructions=[
                Instruction(
                    content="string",
                )
            ],
            default_settings=AgentDefaultSettings(
                temperature=1.0,
                top_p=1.0,
            ),
        )
        """
        _request: typing.Dict[str, typing.Any] = {}
        if about is not OMIT:
            _request["about"] = about
        if instructions is not OMIT:
            _request["instructions"] = instructions
        if name is not OMIT:
            _request["name"] = name
        if model is not OMIT:
            _request["model"] = model
        if default_settings is not OMIT:
            _request["default_settings"] = default_settings
        _response = await self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            json=jsonable_encoder(_request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_agent(self, agent_id: str) -> None:
        """


        Parameters:
            - agent_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_agent(
            agent_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/", f"api/agents/{agent_id}"
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent_additional_info(
        self,
        agent_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentAdditionalInfoResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_agent_additional_info(
            agent_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentAdditionalInfoResponse, _response.json())  # type: ignore
        try:
            _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_agent_additional_info(
        self, agent_id: str, *, request: CreateAdditionalInfoRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - agent_id: str.

            - request: CreateAdditionalInfoRequest.
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_agent_additional_info(
            agent_id="string",
            request=CreateAdditionalInfoRequest(
                title="string",
                content="string",
            ),
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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 get_user_additional_info(
        self,
        user_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetUserAdditionalInfoResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - user_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_user_additional_info(
            user_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetUserAdditionalInfoResponse, _response.json())  # type: ignore
        try:
            _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_user_additional_info(
        self, user_id: str, *, request: CreateAdditionalInfoRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - user_id: str.

            - request: CreateAdditionalInfoRequest.
        ---
        from julep import CreateAdditionalInfoRequest
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_user_additional_info(
            user_id="string",
            request=CreateAdditionalInfoRequest(
                title="string",
                content="string",
            ),
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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_user_additional_info(
        self, user_id: str, additional_info_id: str
    ) -> None:
        """


        Parameters:
            - user_id: str.

            - additional_info_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_user_additional_info(
            user_id="string",
            additional_info_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/users/{user_id}/additional_info/{additional_info_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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_agent_additional_info(
        self, agent_id: str, additional_info_id: str
    ) -> None:
        """


        Parameters:
            - agent_id: str.

            - additional_info_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_agent_additional_info(
            agent_id="string",
            additional_info_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/additional_info/{additional_info_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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_agent_memory(self, agent_id: str, memory_id: str) -> None:
        """


        Parameters:
            - agent_id: str.

            - memory_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_agent_memory(
            agent_id="string",
            memory_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/memories/{memory_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 get_agent_tools(
        self,
        agent_id: str,
        *,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
    ) -> GetAgentToolsResponse:
        """
        Sorted (created_at descending)

        Parameters:
            - agent_id: str.

            - limit: typing.Optional[int].

            - offset: typing.Optional[int].
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.get_agent_tools(
            agent_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "GET",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools",
            ),
            params=remove_none_from_dict({"limit": limit, "offset": offset}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(GetAgentToolsResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(
        self, agent_id: str, *, request: CreateToolRequest
    ) -> ResourceCreatedResponse:
        """


        Parameters:
            - agent_id: str.

            - request: CreateToolRequest.
        ---
        from julep import CreateToolRequest, CreateToolRequestType, FunctionDef
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.create_agent_tool(
            agent_id="string",
            request=CreateToolRequest(
                type=CreateToolRequestType.FUNCTION,
                definition=FunctionDef(
                    name="string",
                    parameters={"string": "string"},
                ),
            ),
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "POST",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools",
            ),
            json=jsonable_encoder(request),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceCreatedResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(
        self, agent_id: str, tool_id: str, *, definition: FunctionDef
    ) -> ResourceUpdatedResponse:
        """


        Parameters:
            - agent_id: str.

            - tool_id: str.

            - definition: FunctionDef. Function definition and parameters
        ---
        from julep import FunctionDef
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.update_agent_tool(
            agent_id="string",
            tool_id="string",
            definition=FunctionDef(
                name="string",
                parameters={"string": "string"},
            ),
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "PUT",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools/{tool_id}",
            ),
            json=jsonable_encoder({"definition": definition}),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return pydantic.parse_obj_as(ResourceUpdatedResponse, _response.json())  # type: ignore
        try:
            _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_agent_tool(self, agent_id: str, tool_id: str) -> None:
        """


        Parameters:
            - agent_id: str.

            - tool_id: str.
        ---
        from julep.client import AsyncJulepApi

        client = AsyncJulepApi(
            api_key="YOUR_API_KEY",
        )
        await client.delete_agent_tool(
            agent_id="string",
            tool_id="string",
        )
        """
        _response = await self._client_wrapper.httpx_client.request(
            "DELETE",
            urllib.parse.urljoin(
                f"{self._client_wrapper.get_base_url()}/",
                f"api/agents/{agent_id}/tools/{tool_id}",
            ),
            headers=self._client_wrapper.get_headers(),
            timeout=300,
        )
        if 200 <= _response.status_code < 300:
            return
        try:
            _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 _get_base_url(
    *, base_url: typing.Optional[str] = None, environment: JulepApiEnvironment
) -> str:
    if base_url is not None:
        return base_url
    elif environment is not None:
        return environment.value
    else:
        raise Exception(
            "Please pass in either base_url or environment to construct the client"
        )
