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

import typing
from ..core.client_wrapper import SyncClientWrapper
from ..core.request_options import RequestOptions
from ..core.pagination import SyncPager
from ..types.corpus import Corpus
from ..types.list_corpora_response import ListCorporaResponse
from ..core.pydantic_utilities import parse_obj_as
from ..errors.forbidden_error import ForbiddenError
from ..types.error import Error
from json.decoder import JSONDecodeError
from ..core.api_error import ApiError
from ..types.corpus_key import CorpusKey
from ..types.filter_attribute import FilterAttribute
from ..types.corpus_custom_dimension import CorpusCustomDimension
from ..core.serialization import convert_and_respect_annotation_metadata
from ..errors.bad_request_error import BadRequestError
from ..types.bad_request_error_body import BadRequestErrorBody
from ..core.jsonable_encoder import jsonable_encoder
from ..errors.not_found_error import NotFoundError
from ..types.not_found_error_body import NotFoundErrorBody
from ..types.replace_filter_attributes_response import ReplaceFilterAttributesResponse
from ..types.query_full_response import QueryFullResponse
from .types.search_corpus_parameters import SearchCorpusParameters
from ..types.generation_parameters import GenerationParameters
from ..types.query_streamed_response import QueryStreamedResponse
import json
from ..core.client_wrapper import AsyncClientWrapper
from ..core.pagination import AsyncPager

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


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

    def list(
        self,
        *,
        limit: typing.Optional[int] = None,
        filter: typing.Optional[str] = None,
        page_key: typing.Optional[str] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> SyncPager[Corpus]:
        """
        List corpora in the account. The corpus objects that are returned are less
        detailed than the direct corpus retrieval operation.

        Parameters
        ----------
        limit : typing.Optional[int]
            The maximum number of corpora to return at one time.

        filter : typing.Optional[str]
            A regular expression to filter the corpora by their name or summary.

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

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

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

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

        Returns
        -------
        SyncPager[Corpus]
            List of corpora.

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        response = client.corpora.list()
        for item in response:
            yield item
        # alternatively, you can paginate page-by-page
        for page in response.iter_pages():
            yield page
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/corpora",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "limit": limit,
                "filter": filter,
                "page_key": page_key,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListCorporaResponse,
                    parse_obj_as(
                        type_=ListCorporaResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _has_next = False
                _get_next = None
                if _parsed_response.metadata is not None:
                    _parsed_next = _parsed_response.metadata.page_key
                    _has_next = _parsed_next is not None and _parsed_next != ""
                    _get_next = lambda: self.list(
                        limit=limit,
                        filter=filter,
                        page_key=_parsed_next,
                        request_timeout=request_timeout,
                        request_timeout_millis=request_timeout_millis,
                        request_options=request_options,
                    )
                _items = _parsed_response.corpora
                return SyncPager(has_next=_has_next, items=_items, get_next=_get_next)
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def create(
        self,
        *,
        key: CorpusKey,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        queries_are_answers: typing.Optional[bool] = OMIT,
        documents_are_questions: typing.Optional[bool] = OMIT,
        encoder_id: typing.Optional[str] = OMIT,
        encoder_name: typing.Optional[str] = OMIT,
        filter_attributes: typing.Optional[typing.Sequence[FilterAttribute]] = OMIT,
        custom_dimensions: typing.Optional[typing.Sequence[CorpusCustomDimension]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Create a corpus, which is a container to store documents and associated metadata.

        Parameters
        ----------
        key : CorpusKey

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

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

        name : typing.Optional[str]
            The name for the corpus. This value defaults to the key.

        description : typing.Optional[str]
            Description for the corpus.

        queries_are_answers : typing.Optional[bool]
            Queries made to this corpus are considered answers, and not questions.

        documents_are_questions : typing.Optional[bool]
            Documents inside this corpus are considered questions, and not answers.

        encoder_id : typing.Optional[str]
            *Deprecated*: Use `encoder_name` instead.


        encoder_name : typing.Optional[str]
            The encoder used by the corpus.

        filter_attributes : typing.Optional[typing.Sequence[FilterAttribute]]
            The new filter attributes of the corpus.
            If unset then the corpus will not have filter attributes.


        custom_dimensions : typing.Optional[typing.Sequence[CorpusCustomDimension]]
            A custom dimension is an additional numerical field attached to a document part. You
            can then multiply this numerical field with a query time custom dimension of the same
            name. This allows boosting (or deboosting) document parts for arbitrary reasons.
            This feature is only enabled for Scale customers.


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

        Returns
        -------
        Corpus
            The corpus has been created.

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.create(
            key="my-corpus",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "v2/corpora",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "key": key,
                "name": name,
                "description": description,
                "queries_are_answers": queries_are_answers,
                "documents_are_questions": documents_are_questions,
                "encoder_id": encoder_id,
                "encoder_name": encoder_name,
                "filter_attributes": convert_and_respect_annotation_metadata(
                    object_=filter_attributes, annotation=typing.Sequence[FilterAttribute], direction="write"
                ),
                "custom_dimensions": convert_and_respect_annotation_metadata(
                    object_=custom_dimensions, annotation=typing.Sequence[CorpusCustomDimension], direction="write"
                ),
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def get(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Get metadata about a corpus. This operation is not a method of searching a corpus.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to retrieve.

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

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

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

        Returns
        -------
        Corpus
            A corpus.

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.get(
            corpus_key="my-corpus",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def delete(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Delete a corpus and all the data that it contains.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to delete

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

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

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

        Returns
        -------
        None

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.delete(
            corpus_key="my-corpus",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}",
            base_url=self._client_wrapper.get_environment().default,
            method="DELETE",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def update(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        enabled: typing.Optional[bool] = OMIT,
        name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Enable, disable, or update the name and description of a corpus. This lets you
        manage data availability without deleting the corpus, which is useful for
        maintenance and security purposes. Update the name and description of a corpus
        dynamically to help keep your data aligned with changing business needs.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to update.

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

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

        enabled : typing.Optional[bool]
            Set whether or not the corpus is enabled. If unset then the corpus will remain in the same state.

        name : typing.Optional[str]
            The name for the corpus. If unset or null then the corpus will remain in the same state.

        description : typing.Optional[str]
            Description of the corpus. If unset or null then the corpus will remain in the same state.

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

        Returns
        -------
        Corpus
            Successfully modified the corpus.

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.update(
            corpus_key="my-corpus",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}",
            base_url=self._client_wrapper.get_environment().default,
            method="PATCH",
            json={
                "enabled": enabled,
                "name": name,
                "description": description,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def reset(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Resets a corpus, which removes all documents and data from the specified corpus, while keeping the corpus itself.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to reset.

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

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

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

        Returns
        -------
        None

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.reset(
            corpus_key="my-corpus",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/reset",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def replace_filter_attributes(
        self,
        corpus_key: CorpusKey,
        *,
        filter_attributes: typing.Sequence[FilterAttribute],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ReplaceFilterAttributesResponse:
        """
        Replace the filter attributes of a corpus. This does not happen immediately, but
        instead creates a job and will complete when that job completes. Until that
        job completes, using new filter attributes will not work.

        You can monitor the status of the filter change using the returned job id.

        Parameters
        ----------
        corpus_key : CorpusKey
            Key of the corpus to have filters replaced.

        filter_attributes : typing.Sequence[FilterAttribute]
            The new filter attributes.

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

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

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

        Returns
        -------
        ReplaceFilterAttributesResponse
            Successfully created a job that will replace the filter attributes.

        Examples
        --------
        from vectara import FilterAttribute, Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.replace_filter_attributes(
            corpus_key="my-corpus",
            filter_attributes=[
                FilterAttribute(
                    name="Title",
                    level="document",
                    type="integer",
                )
            ],
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/replace_filter_attributes",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "filter_attributes": convert_and_respect_annotation_metadata(
                    object_=filter_attributes, annotation=typing.Sequence[FilterAttribute], direction="write"
                ),
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    ReplaceFilterAttributesResponse,
                    parse_obj_as(
                        type_=ReplaceFilterAttributesResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def search(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> QueryFullResponse:
        """
        Search a single corpus with a straightforward query request, specifying the corpus key and query parameters.

        - Specify the unique `corpus_key` identifying the corpus to query.
        - Enter the search `query` string for the corpus, which is the question you want to ask.
        - Set the maximum number of results (`limit`) to return. **Default**: 10, **minimum**: 1
        - Define the `offset` position from which to start in the result set.

        For more detailed information, see this [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string for the corpus, which is the question the user is asking.

        limit : typing.Optional[int]
            Maximum number of results to return.

        offset : typing.Optional[int]
            Position from which to start in the result set.

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

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

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

        Returns
        -------
        QueryFullResponse
            A response to a query.

        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.search(
            corpus_key="my-corpus",
            query="query",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "query": query,
                "limit": limit,
                "offset": offset,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    QueryFullResponse,
                    parse_obj_as(
                        type_=QueryFullResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def query_stream(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        search: typing.Optional[SearchCorpusParameters] = OMIT,
        generation: typing.Optional[GenerationParameters] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.Iterator[QueryStreamedResponse]:
        """
        Query a specific corpus and find relevant results, highlight relevant snippets, and use Retrieval Augmented Generation:

        - Customize your search by specifying the query text (`query`), pagination details (`offset` and `limit`), and metadata filters (`metadata_filter`) to tailor your search results. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#query-definition)
        - Leverage advanced search capabilities like reranking (`reranker`) and Retrieval Augmented Generation (RAG) (`generation`) for enhanced query performance. Generation is opt in by setting the `generation` property. By excluding the property or by setting it to null, the response
          will not include generation. [Learn more](https://docs.vectara.com/docs/learn/grounded-generation/configure-query-summarization).
        - Use hybrid search to achieve optimal results by setting different values for `lexical_interpolation` (e.g., `0.025`). [Learn more](https://docs.vectara.com/docs/learn/hybrid-search)
        - Specify a RAG-specific LLM like Mockingbird (`mockingbird-1.0-2024-07-16`) for the `generation_preset_name`. [Learn more](https://docs.vectara.com/docs/learn/mockingbird-llm)
        - Use advanced summarization options that utilize detailed summarization parameters such as `max_response_characters`, `temperature`, and `frequency_penalty` for generating precise and relevant summaries. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#advanced-summarization-options)

        For more detailed information, see [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string, which is the question the user is asking.

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

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

        search : typing.Optional[SearchCorpusParameters]
            The parameters to search one corpus.

        generation : typing.Optional[GenerationParameters]

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

        Yields
        ------
        typing.Iterator[QueryStreamedResponse]


        Examples
        --------
        from vectara import (
            CitationParameters,
            ContextConfiguration,
            CustomerSpecificReranker,
            GenerationParameters,
            ModelParameters,
            Vectara,
        )
        from vectara.corpora import SearchCorpusParameters

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        response = client.corpora.query_stream(
            corpus_key="string",
            request_timeout=1,
            request_timeout_millis=1,
            query="string",
            search=SearchCorpusParameters(
                custom_dimensions={"string": 1.1},
                metadata_filter="string",
                lexical_interpolation=1.1,
                semantics="default",
                offset=1,
                limit=1,
                context_configuration=ContextConfiguration(
                    characters_before=1,
                    characters_after=1,
                    sentences_before=1,
                    sentences_after=1,
                    start_tag="string",
                    end_tag="string",
                ),
                reranker=CustomerSpecificReranker(
                    reranker_id="string",
                    reranker_name="string",
                ),
            ),
            generation=GenerationParameters(
                generation_preset_name="string",
                prompt_name="string",
                max_used_search_results=1,
                prompt_template="string",
                prompt_text="string",
                max_response_characters=1,
                response_language="auto",
                model_parameters=ModelParameters(
                    max_tokens=1,
                    temperature=1.1,
                    frequency_penalty=1.1,
                    presence_penalty=1.1,
                ),
                citations=CitationParameters(
                    style="none",
                    url_pattern="string",
                    text_pattern="string",
                ),
                enable_factual_consistency_score=True,
            ),
        )
        for chunk in response:
            yield chunk
        """
        with self._client_wrapper.httpx_client.stream(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "query": query,
                "search": convert_and_respect_annotation_metadata(
                    object_=search, annotation=SearchCorpusParameters, direction="write"
                ),
                "generation": convert_and_respect_annotation_metadata(
                    object_=generation, annotation=GenerationParameters, direction="write"
                ),
                "stream_response": True,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:
            try:
                if 200 <= _response.status_code < 300:
                    for _text in _response.iter_lines():
                        try:
                            if len(_text) == 0:
                                continue
                            yield typing.cast(
                                QueryStreamedResponse,
                                parse_obj_as(
                                    type_=QueryStreamedResponse,  # type: ignore
                                    object_=json.loads(_text),
                                ),
                            )
                        except:
                            pass
                    return
                _response.read()
                if _response.status_code == 400:
                    raise BadRequestError(
                        typing.cast(
                            BadRequestErrorBody,
                            parse_obj_as(
                                type_=BadRequestErrorBody,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                if _response.status_code == 403:
                    raise ForbiddenError(
                        typing.cast(
                            Error,
                            parse_obj_as(
                                type_=Error,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                if _response.status_code == 404:
                    raise NotFoundError(
                        typing.cast(
                            NotFoundErrorBody,
                            parse_obj_as(
                                type_=NotFoundErrorBody,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                _response_json = _response.json()
            except JSONDecodeError:
                raise ApiError(status_code=_response.status_code, body=_response.text)
            raise ApiError(status_code=_response.status_code, body=_response_json)

    def query(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        search: typing.Optional[SearchCorpusParameters] = OMIT,
        generation: typing.Optional[GenerationParameters] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> QueryFullResponse:
        """
        Query a specific corpus and find relevant results, highlight relevant snippets, and use Retrieval Augmented Generation:

        - Customize your search by specifying the query text (`query`), pagination details (`offset` and `limit`), and metadata filters (`metadata_filter`) to tailor your search results. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#query-definition)
        - Leverage advanced search capabilities like reranking (`reranker`) and Retrieval Augmented Generation (RAG) (`generation`) for enhanced query performance. Generation is opt in by setting the `generation` property. By excluding the property or by setting it to null, the response
          will not include generation. [Learn more](https://docs.vectara.com/docs/learn/grounded-generation/configure-query-summarization).
        - Use hybrid search to achieve optimal results by setting different values for `lexical_interpolation` (e.g., `0.025`). [Learn more](https://docs.vectara.com/docs/learn/hybrid-search)
        - Specify a RAG-specific LLM like Mockingbird (`mockingbird-1.0-2024-07-16`) for the `generation_preset_name`. [Learn more](https://docs.vectara.com/docs/learn/mockingbird-llm)
        - Use advanced summarization options that utilize detailed summarization parameters such as `max_response_characters`, `temperature`, and `frequency_penalty` for generating precise and relevant summaries. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#advanced-summarization-options)

        For more detailed information, see [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string, which is the question the user is asking.

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

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

        search : typing.Optional[SearchCorpusParameters]
            The parameters to search one corpus.

        generation : typing.Optional[GenerationParameters]

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

        Returns
        -------
        QueryFullResponse


        Examples
        --------
        from vectara import Vectara

        client = Vectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )
        client.corpora.query(
            corpus_key="my-corpus",
            query="query",
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "query": query,
                "search": convert_and_respect_annotation_metadata(
                    object_=search, annotation=SearchCorpusParameters, direction="write"
                ),
                "generation": convert_and_respect_annotation_metadata(
                    object_=generation, annotation=GenerationParameters, direction="write"
                ),
                "stream_response": False,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    QueryFullResponse,
                    parse_obj_as(
                        type_=QueryFullResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)


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

    async def list(
        self,
        *,
        limit: typing.Optional[int] = None,
        filter: typing.Optional[str] = None,
        page_key: typing.Optional[str] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> AsyncPager[Corpus]:
        """
        List corpora in the account. The corpus objects that are returned are less
        detailed than the direct corpus retrieval operation.

        Parameters
        ----------
        limit : typing.Optional[int]
            The maximum number of corpora to return at one time.

        filter : typing.Optional[str]
            A regular expression to filter the corpora by their name or summary.

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

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

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

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

        Returns
        -------
        AsyncPager[Corpus]
            List of corpora.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            response = await client.corpora.list()
            async for item in response:
                yield item
            # alternatively, you can paginate page-by-page
            async for page in response.iter_pages():
                yield page


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/corpora",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "limit": limit,
                "filter": filter,
                "page_key": page_key,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                _parsed_response = typing.cast(
                    ListCorporaResponse,
                    parse_obj_as(
                        type_=ListCorporaResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
                _has_next = False
                _get_next = None
                if _parsed_response.metadata is not None:
                    _parsed_next = _parsed_response.metadata.page_key
                    _has_next = _parsed_next is not None and _parsed_next != ""
                    _get_next = lambda: self.list(
                        limit=limit,
                        filter=filter,
                        page_key=_parsed_next,
                        request_timeout=request_timeout,
                        request_timeout_millis=request_timeout_millis,
                        request_options=request_options,
                    )
                _items = _parsed_response.corpora
                return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next)
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def create(
        self,
        *,
        key: CorpusKey,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        queries_are_answers: typing.Optional[bool] = OMIT,
        documents_are_questions: typing.Optional[bool] = OMIT,
        encoder_id: typing.Optional[str] = OMIT,
        encoder_name: typing.Optional[str] = OMIT,
        filter_attributes: typing.Optional[typing.Sequence[FilterAttribute]] = OMIT,
        custom_dimensions: typing.Optional[typing.Sequence[CorpusCustomDimension]] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Create a corpus, which is a container to store documents and associated metadata.

        Parameters
        ----------
        key : CorpusKey

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

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

        name : typing.Optional[str]
            The name for the corpus. This value defaults to the key.

        description : typing.Optional[str]
            Description for the corpus.

        queries_are_answers : typing.Optional[bool]
            Queries made to this corpus are considered answers, and not questions.

        documents_are_questions : typing.Optional[bool]
            Documents inside this corpus are considered questions, and not answers.

        encoder_id : typing.Optional[str]
            *Deprecated*: Use `encoder_name` instead.


        encoder_name : typing.Optional[str]
            The encoder used by the corpus.

        filter_attributes : typing.Optional[typing.Sequence[FilterAttribute]]
            The new filter attributes of the corpus.
            If unset then the corpus will not have filter attributes.


        custom_dimensions : typing.Optional[typing.Sequence[CorpusCustomDimension]]
            A custom dimension is an additional numerical field attached to a document part. You
            can then multiply this numerical field with a query time custom dimension of the same
            name. This allows boosting (or deboosting) document parts for arbitrary reasons.
            This feature is only enabled for Scale customers.


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

        Returns
        -------
        Corpus
            The corpus has been created.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.create(
                key="my-corpus",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            "v2/corpora",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "key": key,
                "name": name,
                "description": description,
                "queries_are_answers": queries_are_answers,
                "documents_are_questions": documents_are_questions,
                "encoder_id": encoder_id,
                "encoder_name": encoder_name,
                "filter_attributes": convert_and_respect_annotation_metadata(
                    object_=filter_attributes, annotation=typing.Sequence[FilterAttribute], direction="write"
                ),
                "custom_dimensions": convert_and_respect_annotation_metadata(
                    object_=custom_dimensions, annotation=typing.Sequence[CorpusCustomDimension], direction="write"
                ),
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def get(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Get metadata about a corpus. This operation is not a method of searching a corpus.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to retrieve.

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

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

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

        Returns
        -------
        Corpus
            A corpus.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.get(
                corpus_key="my-corpus",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def delete(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Delete a corpus and all the data that it contains.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to delete

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

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

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

        Returns
        -------
        None

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.delete(
                corpus_key="my-corpus",
            )


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

    async def update(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        enabled: typing.Optional[bool] = OMIT,
        name: typing.Optional[str] = OMIT,
        description: typing.Optional[str] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> Corpus:
        """
        Enable, disable, or update the name and description of a corpus. This lets you
        manage data availability without deleting the corpus, which is useful for
        maintenance and security purposes. Update the name and description of a corpus
        dynamically to help keep your data aligned with changing business needs.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to update.

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

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

        enabled : typing.Optional[bool]
            Set whether or not the corpus is enabled. If unset then the corpus will remain in the same state.

        name : typing.Optional[str]
            The name for the corpus. If unset or null then the corpus will remain in the same state.

        description : typing.Optional[str]
            Description of the corpus. If unset or null then the corpus will remain in the same state.

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

        Returns
        -------
        Corpus
            Successfully modified the corpus.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.update(
                corpus_key="my-corpus",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}",
            base_url=self._client_wrapper.get_environment().default,
            method="PATCH",
            json={
                "enabled": enabled,
                "name": name,
                "description": description,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    Corpus,
                    parse_obj_as(
                        type_=Corpus,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def reset(
        self,
        corpus_key: CorpusKey,
        *,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> None:
        """
        Resets a corpus, which removes all documents and data from the specified corpus, while keeping the corpus itself.

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to reset.

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

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

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

        Returns
        -------
        None

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.reset(
                corpus_key="my-corpus",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/reset",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def replace_filter_attributes(
        self,
        corpus_key: CorpusKey,
        *,
        filter_attributes: typing.Sequence[FilterAttribute],
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> ReplaceFilterAttributesResponse:
        """
        Replace the filter attributes of a corpus. This does not happen immediately, but
        instead creates a job and will complete when that job completes. Until that
        job completes, using new filter attributes will not work.

        You can monitor the status of the filter change using the returned job id.

        Parameters
        ----------
        corpus_key : CorpusKey
            Key of the corpus to have filters replaced.

        filter_attributes : typing.Sequence[FilterAttribute]
            The new filter attributes.

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

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

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

        Returns
        -------
        ReplaceFilterAttributesResponse
            Successfully created a job that will replace the filter attributes.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara, FilterAttribute

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.replace_filter_attributes(
                corpus_key="my-corpus",
                filter_attributes=[
                    FilterAttribute(
                        name="Title",
                        level="document",
                        type="integer",
                    )
                ],
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/replace_filter_attributes",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "filter_attributes": convert_and_respect_annotation_metadata(
                    object_=filter_attributes, annotation=typing.Sequence[FilterAttribute], direction="write"
                ),
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    ReplaceFilterAttributesResponse,
                    parse_obj_as(
                        type_=ReplaceFilterAttributesResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def search(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        limit: typing.Optional[int] = None,
        offset: typing.Optional[int] = None,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> QueryFullResponse:
        """
        Search a single corpus with a straightforward query request, specifying the corpus key and query parameters.

        - Specify the unique `corpus_key` identifying the corpus to query.
        - Enter the search `query` string for the corpus, which is the question you want to ask.
        - Set the maximum number of results (`limit`) to return. **Default**: 10, **minimum**: 1
        - Define the `offset` position from which to start in the result set.

        For more detailed information, see this [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string for the corpus, which is the question the user is asking.

        limit : typing.Optional[int]
            Maximum number of results to return.

        offset : typing.Optional[int]
            Position from which to start in the result set.

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

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

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

        Returns
        -------
        QueryFullResponse
            A response to a query.

        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.search(
                corpus_key="my-corpus",
                query="query",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="GET",
            params={
                "query": query,
                "limit": limit,
                "offset": offset,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    QueryFullResponse,
                    parse_obj_as(
                        type_=QueryFullResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def query_stream(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        search: typing.Optional[SearchCorpusParameters] = OMIT,
        generation: typing.Optional[GenerationParameters] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> typing.AsyncIterator[QueryStreamedResponse]:
        """
        Query a specific corpus and find relevant results, highlight relevant snippets, and use Retrieval Augmented Generation:

        - Customize your search by specifying the query text (`query`), pagination details (`offset` and `limit`), and metadata filters (`metadata_filter`) to tailor your search results. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#query-definition)
        - Leverage advanced search capabilities like reranking (`reranker`) and Retrieval Augmented Generation (RAG) (`generation`) for enhanced query performance. Generation is opt in by setting the `generation` property. By excluding the property or by setting it to null, the response
          will not include generation. [Learn more](https://docs.vectara.com/docs/learn/grounded-generation/configure-query-summarization).
        - Use hybrid search to achieve optimal results by setting different values for `lexical_interpolation` (e.g., `0.025`). [Learn more](https://docs.vectara.com/docs/learn/hybrid-search)
        - Specify a RAG-specific LLM like Mockingbird (`mockingbird-1.0-2024-07-16`) for the `generation_preset_name`. [Learn more](https://docs.vectara.com/docs/learn/mockingbird-llm)
        - Use advanced summarization options that utilize detailed summarization parameters such as `max_response_characters`, `temperature`, and `frequency_penalty` for generating precise and relevant summaries. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#advanced-summarization-options)

        For more detailed information, see [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string, which is the question the user is asking.

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

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

        search : typing.Optional[SearchCorpusParameters]
            The parameters to search one corpus.

        generation : typing.Optional[GenerationParameters]

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

        Yields
        ------
        typing.AsyncIterator[QueryStreamedResponse]


        Examples
        --------
        import asyncio

        from vectara import (
            AsyncVectara,
            CitationParameters,
            ContextConfiguration,
            CustomerSpecificReranker,
            GenerationParameters,
            ModelParameters,
        )
        from vectara.corpora import SearchCorpusParameters

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            response = await client.corpora.query_stream(
                corpus_key="string",
                request_timeout=1,
                request_timeout_millis=1,
                query="string",
                search=SearchCorpusParameters(
                    custom_dimensions={"string": 1.1},
                    metadata_filter="string",
                    lexical_interpolation=1.1,
                    semantics="default",
                    offset=1,
                    limit=1,
                    context_configuration=ContextConfiguration(
                        characters_before=1,
                        characters_after=1,
                        sentences_before=1,
                        sentences_after=1,
                        start_tag="string",
                        end_tag="string",
                    ),
                    reranker=CustomerSpecificReranker(
                        reranker_id="string",
                        reranker_name="string",
                    ),
                ),
                generation=GenerationParameters(
                    generation_preset_name="string",
                    prompt_name="string",
                    max_used_search_results=1,
                    prompt_template="string",
                    prompt_text="string",
                    max_response_characters=1,
                    response_language="auto",
                    model_parameters=ModelParameters(
                        max_tokens=1,
                        temperature=1.1,
                        frequency_penalty=1.1,
                        presence_penalty=1.1,
                    ),
                    citations=CitationParameters(
                        style="none",
                        url_pattern="string",
                        text_pattern="string",
                    ),
                    enable_factual_consistency_score=True,
                ),
            )
            async for chunk in response:
                yield chunk


        asyncio.run(main())
        """
        async with self._client_wrapper.httpx_client.stream(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "query": query,
                "search": convert_and_respect_annotation_metadata(
                    object_=search, annotation=SearchCorpusParameters, direction="write"
                ),
                "generation": convert_and_respect_annotation_metadata(
                    object_=generation, annotation=GenerationParameters, direction="write"
                ),
                "stream_response": True,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        ) as _response:
            try:
                if 200 <= _response.status_code < 300:
                    async for _text in _response.aiter_lines():
                        try:
                            if len(_text) == 0:
                                continue
                            yield typing.cast(
                                QueryStreamedResponse,
                                parse_obj_as(
                                    type_=QueryStreamedResponse,  # type: ignore
                                    object_=json.loads(_text),
                                ),
                            )
                        except:
                            pass
                    return
                await _response.aread()
                if _response.status_code == 400:
                    raise BadRequestError(
                        typing.cast(
                            BadRequestErrorBody,
                            parse_obj_as(
                                type_=BadRequestErrorBody,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                if _response.status_code == 403:
                    raise ForbiddenError(
                        typing.cast(
                            Error,
                            parse_obj_as(
                                type_=Error,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                if _response.status_code == 404:
                    raise NotFoundError(
                        typing.cast(
                            NotFoundErrorBody,
                            parse_obj_as(
                                type_=NotFoundErrorBody,  # type: ignore
                                object_=_response.json(),
                            ),
                        )
                    )
                _response_json = _response.json()
            except JSONDecodeError:
                raise ApiError(status_code=_response.status_code, body=_response.text)
            raise ApiError(status_code=_response.status_code, body=_response_json)

    async def query(
        self,
        corpus_key: CorpusKey,
        *,
        query: str,
        request_timeout: typing.Optional[int] = None,
        request_timeout_millis: typing.Optional[int] = None,
        search: typing.Optional[SearchCorpusParameters] = OMIT,
        generation: typing.Optional[GenerationParameters] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> QueryFullResponse:
        """
        Query a specific corpus and find relevant results, highlight relevant snippets, and use Retrieval Augmented Generation:

        - Customize your search by specifying the query text (`query`), pagination details (`offset` and `limit`), and metadata filters (`metadata_filter`) to tailor your search results. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#query-definition)
        - Leverage advanced search capabilities like reranking (`reranker`) and Retrieval Augmented Generation (RAG) (`generation`) for enhanced query performance. Generation is opt in by setting the `generation` property. By excluding the property or by setting it to null, the response
          will not include generation. [Learn more](https://docs.vectara.com/docs/learn/grounded-generation/configure-query-summarization).
        - Use hybrid search to achieve optimal results by setting different values for `lexical_interpolation` (e.g., `0.025`). [Learn more](https://docs.vectara.com/docs/learn/hybrid-search)
        - Specify a RAG-specific LLM like Mockingbird (`mockingbird-1.0-2024-07-16`) for the `generation_preset_name`. [Learn more](https://docs.vectara.com/docs/learn/mockingbird-llm)
        - Use advanced summarization options that utilize detailed summarization parameters such as `max_response_characters`, `temperature`, and `frequency_penalty` for generating precise and relevant summaries. [Learn more](https://docs.vectara.com/docs/api-reference/search-apis/search#advanced-summarization-options)

        For more detailed information, see [Query API guide](https://docs.vectara.com/docs/api-reference/search-apis/search).

        Parameters
        ----------
        corpus_key : CorpusKey
            The unique key identifying the corpus to query.

        query : str
            The search query string, which is the question the user is asking.

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

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

        search : typing.Optional[SearchCorpusParameters]
            The parameters to search one corpus.

        generation : typing.Optional[GenerationParameters]

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

        Returns
        -------
        QueryFullResponse


        Examples
        --------
        import asyncio

        from vectara import AsyncVectara

        client = AsyncVectara(
            api_key="YOUR_API_KEY",
            client_id="YOUR_CLIENT_ID",
            client_secret="YOUR_CLIENT_SECRET",
        )


        async def main() -> None:
            await client.corpora.query(
                corpus_key="my-corpus",
                query="query",
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            f"v2/corpora/{jsonable_encoder(corpus_key)}/query",
            base_url=self._client_wrapper.get_environment().default,
            method="POST",
            json={
                "query": query,
                "search": convert_and_respect_annotation_metadata(
                    object_=search, annotation=SearchCorpusParameters, direction="write"
                ),
                "generation": convert_and_respect_annotation_metadata(
                    object_=generation, annotation=GenerationParameters, direction="write"
                ),
                "stream_response": False,
            },
            headers={
                "Request-Timeout": str(request_timeout) if request_timeout is not None else None,
                "Request-Timeout-Millis": str(request_timeout_millis) if request_timeout_millis is not None else None,
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    QueryFullResponse,
                    parse_obj_as(
                        type_=QueryFullResponse,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 400:
                raise BadRequestError(
                    typing.cast(
                        BadRequestErrorBody,
                        parse_obj_as(
                            type_=BadRequestErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 403:
                raise ForbiddenError(
                    typing.cast(
                        Error,
                        parse_obj_as(
                            type_=Error,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            if _response.status_code == 404:
                raise NotFoundError(
                    typing.cast(
                        NotFoundErrorBody,
                        parse_obj_as(
                            type_=NotFoundErrorBody,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)
