diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml deleted file mode 100644 index 5f8f8d1a..00000000 --- a/.github/workflows/build_docs.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Simple workflow for deploying static content to GitHub Pages -name: Build docs based on Sphinx - -on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - build: - environment: - name: build-docs - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set working directory - uses: ./.github/actions/set-working-directory - with: - working_directory: "${{ github.workspace }}" - - - name: Setup python environment - uses: ./.github/actions/setup-python-env - with: - working_directory: "${{ env.WORKING_DIRECTORY }}" - - - name: Build docs - run: | - cd docs - make html - - - uses: EndBug/add-and-commit@v9.1.1 - with: - add: "${{ env.WORKING_DIRECTORY }}/docs/_build/html" - default_author: github_actor - fetch: true - message: "[skip ci] Update docs" - pathspec_error_handling: ignore diff --git a/.github/workflows/static_docs.yml b/.github/workflows/static_docs.yml deleted file mode 100644 index 467f7c6a..00000000 --- a/.github/workflows/static_docs.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Simple workflow for deploying static content to GitHub Pages -name: Deploy docs to GitHub Pages - -on: - # Runs on pushes targeting the default branch - release: - types: [published] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Pages - uses: actions/configure-pages@v5 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - # Upload entire repository - path: './docs/_build/html' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d4bb2cbb..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle deleted file mode 100644 index b5586057..00000000 Binary files a/docs/_build/doctrees/environment.pickle and /dev/null differ diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree deleted file mode 100644 index 6e3a720a..00000000 Binary files a/docs/_build/doctrees/index.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/examples.doctree b/docs/_build/doctrees/source/examples.doctree deleted file mode 100644 index 490fd20d..00000000 Binary files a/docs/_build/doctrees/source/examples.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/intro.doctree b/docs/_build/doctrees/source/intro.doctree deleted file mode 100644 index 0a395c3c..00000000 Binary files a/docs/_build/doctrees/source/intro.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/modules.doctree b/docs/_build/doctrees/source/modules.doctree deleted file mode 100644 index 65a16513..00000000 Binary files a/docs/_build/doctrees/source/modules.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.doctree b/docs/_build/doctrees/source/notdiamond.doctree deleted file mode 100644 index a6bb7356..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.llms.doctree b/docs/_build/doctrees/source/notdiamond.llms.doctree deleted file mode 100644 index 36a4bd29..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.llms.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.metrics.doctree b/docs/_build/doctrees/source/notdiamond.metrics.doctree deleted file mode 100644 index 2b5f464a..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.metrics.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.prompts.doctree b/docs/_build/doctrees/source/notdiamond.prompts.doctree deleted file mode 100644 index ea386fc9..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.prompts.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.toolkit.doctree b/docs/_build/doctrees/source/notdiamond.toolkit.doctree deleted file mode 100644 index a7bb0342..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.toolkit.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.toolkit.litellm.doctree b/docs/_build/doctrees/source/notdiamond.toolkit.litellm.doctree deleted file mode 100644 index 7756d09a..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.toolkit.litellm.doctree and /dev/null differ diff --git a/docs/_build/doctrees/source/notdiamond.toolkit.rag.doctree b/docs/_build/doctrees/source/notdiamond.toolkit.rag.doctree deleted file mode 100644 index 6e46cb95..00000000 Binary files a/docs/_build/doctrees/source/notdiamond.toolkit.rag.doctree and /dev/null differ diff --git a/docs/_build/html/.buildinfo b/docs/_build/html/.buildinfo deleted file mode 100644 index f02d9643..00000000 --- a/docs/_build/html/.buildinfo +++ /dev/null @@ -1,4 +0,0 @@ -# Sphinx build info version 1 -# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 204a5c462923e8038e5918346d8d885f -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_build/html/.doctrees/environment.pickle b/docs/_build/html/.doctrees/environment.pickle deleted file mode 100644 index d514ea30..00000000 Binary files a/docs/_build/html/.doctrees/environment.pickle and /dev/null differ diff --git a/docs/_build/html/.doctrees/index.doctree b/docs/_build/html/.doctrees/index.doctree deleted file mode 100644 index b9d2fedb..00000000 Binary files a/docs/_build/html/.doctrees/index.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/intro.doctree b/docs/_build/html/.doctrees/source/intro.doctree deleted file mode 100644 index 7fd60461..00000000 Binary files a/docs/_build/html/.doctrees/source/intro.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/notdiamond.doctree b/docs/_build/html/.doctrees/source/notdiamond.doctree deleted file mode 100644 index fd2fc794..00000000 Binary files a/docs/_build/html/.doctrees/source/notdiamond.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/notdiamond.llms.doctree b/docs/_build/html/.doctrees/source/notdiamond.llms.doctree deleted file mode 100644 index ad317ab0..00000000 Binary files a/docs/_build/html/.doctrees/source/notdiamond.llms.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/notdiamond.metrics.doctree b/docs/_build/html/.doctrees/source/notdiamond.metrics.doctree deleted file mode 100644 index c6d491fa..00000000 Binary files a/docs/_build/html/.doctrees/source/notdiamond.metrics.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/notdiamond.toolkit.doctree b/docs/_build/html/.doctrees/source/notdiamond.toolkit.doctree deleted file mode 100644 index bd17188b..00000000 Binary files a/docs/_build/html/.doctrees/source/notdiamond.toolkit.doctree and /dev/null differ diff --git a/docs/_build/html/.doctrees/source/notdiamond.toolkit.rag.doctree b/docs/_build/html/.doctrees/source/notdiamond.toolkit.rag.doctree deleted file mode 100644 index 5e35558b..00000000 Binary files a/docs/_build/html/.doctrees/source/notdiamond.toolkit.rag.doctree and /dev/null differ diff --git a/docs/_build/html/.nojekyll b/docs/_build/html/.nojekyll deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/_build/html/_modules/index.html b/docs/_build/html/_modules/index.html deleted file mode 100644 index efe356cf..00000000 --- a/docs/_build/html/_modules/index.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - Overview: module code — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - -
  • -
  • -
-
-
- - -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/_init.html b/docs/_build/html/_modules/notdiamond/_init.html deleted file mode 100644 index 6df7361b..00000000 --- a/docs/_build/html/_modules/notdiamond/_init.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - notdiamond._init — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond._init

-import os
-from typing import Dict, List, Union
-
-from notdiamond.toolkit._retry import (
-    AsyncRetryWrapper,
-    ClientType,
-    ModelType,
-    OpenAIMessagesType,
-    RetryManager,
-    RetryWrapper,
-)
-
-
-
-[docs] -def init( - client: Union[ClientType, List[ClientType]], - models: ModelType, - max_retries: Union[int, Dict[str, int]] = 1, - timeout: Union[float, Dict[str, float]] = 60.0, - model_messages: Dict[str, OpenAIMessagesType] = None, - api_key: Union[str, None] = None, - async_mode: bool = False, - backoff: Union[float, Dict[str, float]] = 2.0, -) -> RetryManager: - """ - Entrypoint for fallback and retry features without changing existing code. - - Add this to existing codebase without other modifications to enable the following capabilities: - - - Fallback to a different model if a model invocation fails. - - If configured, fallback to a different *provider* if a model invocation fails - (eg. azure/gpt-4o fails -> invoke openai/gpt-4o) - - Load-balance between models and providers, if specified. - - Pass timeout and retry configurations to each invoke, optionally configured per model. - - Pass model-specific messages on each retry (prepended to the provided `messages` parameter) - - Parameters: - client (Union[ClientType, List[ClientType]]): Clients to apply retry/fallback logic to. - models (Union[Dict[str, float], List[str]]): - Models to use of the format <provider>/<model>. - Supports two formats: - - List of models, eg. ["openai/gpt-4o", "azure/gpt-4o"]. Models will be prioritized as listed. - - Dict of models to weights for load balancing, eg. {"openai/gpt-4o": 0.9, "azure/gpt-4o": 0.1}. - If a model invocation fails, the next model is selected by sampling using the *remaining* weights. - max_retries (Union[int, Dict[str, int]]): - Maximum number of retries. Can be configured globally or per model. - timeout (Union[float, Dict[str, float]]): - Timeout in seconds per model. Can be configured globally or per model. - model_messages (Dict[str, OpenAIMessagesType]): - Model-specific messages to prepend to `messages` on each invocation, formatted OpenAI-style. Can be - configured using any role which is valid as an initial message (eg. "system" or "user", but not "assistant"). - api_key (Optional[str]): - Not Diamond API key for authentication. Unused for now - will offer logging and metrics in the future. - async_mode (bool): - Whether to manage clients as async. - backoff (Union[float, Dict[str, float]]): - Backoff factor for exponential backoff per each retry. Can be configured globally or per model. - - Returns: - RetryManager: Manager object that handles retries and fallbacks. Not required for usage. - - Model Fallback Prioritization - ----------------------------- - - - If models is a list, the fallback model is selected in order after removing the failed model. - eg. If "openai/gpt-4o" fails for the list: - - ["openai/gpt-4o", "azure/gpt-4o"], "azure/gpt-4o" will be tried next - - ["openai/gpt-4o-mini", "openai/gpt-4o", "azure/gpt-4o"], "openai/gpt-4o-mini" will be tried next. - - If models is a dict, the next model is selected by sampling using the *remaining* weights. - eg. If "openai/gpt-4o" fails for the dict: - - {"openai/gpt-4o": 0.9, "azure/gpt-4o": 0.1}, "azure/gpt-4o" will be invoked 100% of the time - - {"openai/gpt-4o": 0.5, "azure/gpt-4o": 0.25, "openai/gpt-4o-mini": 0.25}, then "azure/gpt-4o" and - "openai/gpt-4o-mini" can be invoked with 50% probability each. - - Usage - ----- - - Please refer to tests/test_init.py for more examples on how to use notdiamond.init. - - .. code-block:: python - - # ...existing workflow code, including client initialization... - openai_client = OpenAI(...) - azure_client = AzureOpenAI(...) - - # Add `notdiamond.init` to the workflow. - notdiamond.init( - [openai_client, azure_client], - models={"openai/gpt-4o": 0.9, "azure/gpt-4o": 0.1}, - max_retries={"openai/gpt-4o": 3, "azure/gpt-4o": 1}, - timeout={"openai/gpt-4o": 10.0, "azure/gpt-4o": 5.0}, - model_messages={ - "openai/gpt-4o": [{"role": "user", "content": "Here is a prompt for OpenAI."}], - "azure/gpt-4o": [{"role": "user", "content": "Here is a prompt for Azure."}], - }, - api_key="sk-...", - backoff=2.0, - ) - - # ...continue existing workflow code... - response = openai_client.chat.completions.create( - model="notdiamond", - messages=[{"role": "user", "content": "Hello!"}] - ) - - """ - api_key = api_key or os.getenv("NOTDIAMOND_API_KEY") - - if async_mode: - wrapper_cls = AsyncRetryWrapper - else: - wrapper_cls = RetryWrapper - - for model in models: - if len(model.split("/")) != 2: - raise ValueError( - f"Model {model} must be in the format <provider>/<model>." - ) - - if not isinstance(client, List): - client_wrappers = [ - wrapper_cls( - client=client, - models=models, - max_retries=max_retries, - timeout=timeout, - model_messages=model_messages, - api_key=api_key, - backoff=backoff, - ) - ] - else: - client_wrappers = [ - wrapper_cls( - client=cc, - models=models, - max_retries=max_retries, - timeout=timeout, - model_messages=model_messages, - api_key=api_key, - backoff=backoff, - ) - for cc in client - ] - retry_manager = RetryManager(models, client_wrappers) - - return retry_manager
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/callbacks.html b/docs/_build/html/_modules/notdiamond/callbacks.html deleted file mode 100644 index c0ec5678..00000000 --- a/docs/_build/html/_modules/notdiamond/callbacks.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - notdiamond.callbacks — NotDiamond 0.2.7-beta - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.callbacks

-from typing import Any
-
-from langchain_core.callbacks.base import BaseCallbackHandler
-
-from notdiamond.llms.provider import NDLLMProvider
-
-
-
-[docs] -class NDLLMBaseCallbackHandler(BaseCallbackHandler): - """ - Base callback handler for NotDiamond LLMs. - Accepts all of the langchain_core callbacks and adds new ones. - """ - -
-[docs] - def on_model_select( - self, model_provider: NDLLMProvider, model_name: str - ) -> Any: - """ - Called when a model is selected. - """
- - -
-[docs] - def on_latency_tracking( - self, - session_id: str, - model_provider: NDLLMProvider, - tokens_per_second: float, - ): - """ - Called when latency tracking is enabled. - """
- - -
-[docs] - def on_api_error(self, error_message: str): - """ - Called when an NDLLM API error occurs. - """
-
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/exceptions.html b/docs/_build/html/_modules/notdiamond/exceptions.html deleted file mode 100644 index 3f484161..00000000 --- a/docs/_build/html/_modules/notdiamond/exceptions.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - notdiamond.exceptions — NotDiamond 0.3.38 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.exceptions

-
-[docs] -class UnsupportedLLMProvider(Exception): - """The exception class for unsupported LLM provider"""
- - - -
-[docs] -class UnsupportedEmbeddingProvider(Exception): - """The exception class for unsupported Embedding provider"""
- - - -
-[docs] -class InvalidApiKey(Exception): - """The exception class for InvalidApiKey"""
- - - -
-[docs] -class MissingApiKey(Exception): - """The exception class for MissingApiKey"""
- - - -
-[docs] -class MissingLLMConfigs(Exception): - """The exception class for empty LLM provider configs array"""
- - - -
-[docs] -class ApiError(Exception): - """The exception class for any ApiError"""
- - - -
-[docs] -class CreateUnavailableError(Exception): - """The exception class raised when `notdiamond[create]` is not available"""
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/llms/client.html b/docs/_build/html/_modules/notdiamond/llms/client.html deleted file mode 100644 index fb0ecfaf..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/client.html +++ /dev/null @@ -1,1912 +0,0 @@ - - - - - - notdiamond.llms.client — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.client

-"""NotDiamond client class"""
-
-
-import inspect
-import logging
-import time
-import warnings
-from enum import Enum
-from typing import (
-    Any,
-    AsyncIterator,
-    Callable,
-    Dict,
-    Iterator,
-    List,
-    Optional,
-    Sequence,
-    Tuple,
-    Type,
-    Union,
-)
-
-# Details: https://python.langchain.com/v0.1/docs/guides/development/pydantic_compatibility/
-from pydantic import BaseModel
-from pydantic_partial import create_partial_model
-
-from notdiamond import settings
-from notdiamond._utils import _module_check, token_counter
-from notdiamond.exceptions import (
-    ApiError,
-    CreateUnavailableError,
-    MissingLLMConfigs,
-)
-from notdiamond.llms.config import LLMConfig
-from notdiamond.llms.providers import is_o1_model
-from notdiamond.llms.request import (
-    amodel_select,
-    create_preference_id,
-    model_select,
-    report_latency,
-)
-from notdiamond.metrics.metric import Metric
-from notdiamond.prompts import (
-    _curly_escape,
-    inject_system_prompt,
-    o1_system_prompt_translate,
-)
-from notdiamond.types import NDApiKeyValidator
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-
-class _NDClientTarget(Enum):
-    ROUTER = "router"
-    INVOKER = "invoker"
-
-
-def _ndllm_factory(import_target: _NDClientTarget = None):
-    _invoke_error_msg_tmpl = (
-        "{fn_name} is not available. `notdiamond` can generate LLM responses after "
-        "installing additional dependencies via `pip install notdiamond[create]`."
-    )
-
-    _default_llm_config_invalid_warning = "The default LLMConfig set is invalid. Defaulting to {provider}/{model}"
-
-    _no_default_llm_config_warning = (
-        "No default LLMConfig set. Defaulting to {provider}/{model}"
-    )
-
-    class _NDRouterClient(BaseModel):
-        api_key: str
-        llm_configs: Optional[List[Union[LLMConfig, str]]]
-        default: Union[LLMConfig, int, str]
-        max_model_depth: Optional[int]
-        latency_tracking: bool
-        hash_content: bool
-        tradeoff: Optional[str]
-        preference_id: Optional[str]
-        tools: Optional[Sequence[Union[Dict[str, Any], Callable]]]
-        callbacks: Optional[List]
-        nd_api_url: Optional[str]
-        user_agent: Union[str, None]
-        max_retries: Optional[int]
-        timeout: Optional[Union[float, int]]
-
-        class Config:
-            arbitrary_types_allowed = True
-
-        def __init__(
-            self,
-            llm_configs: Optional[List[Union[LLMConfig, str]]] = None,
-            api_key: Optional[str] = None,
-            default: Union[LLMConfig, int, str] = 0,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: bool = True,
-            hash_content: bool = False,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            callbacks: Optional[List] = None,
-            tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] = None,
-            nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL,
-            user_agent: Union[str, None] = None,
-            max_retries: Optional[int] = 3,
-            timeout: Optional[Union[float, int]] = 60.0,
-            **kwargs,
-        ):
-            if api_key is None:
-                api_key = settings.NOTDIAMOND_API_KEY
-            NDApiKeyValidator(api_key=api_key)
-
-            if user_agent is None:
-                user_agent = settings.DEFAULT_USER_AGENT
-
-            if llm_configs is not None:
-                llm_configs = self._parse_llm_configs_data(llm_configs)
-
-                if max_model_depth is None:
-                    max_model_depth = len(llm_configs)
-
-                if max_model_depth > len(llm_configs):
-                    LOGGER.warning(
-                        "WARNING: max_model_depth cannot be bigger than the number of LLMs."
-                    )
-                    max_model_depth = len(llm_configs)
-
-            if tradeoff is not None:
-                if tradeoff not in ["cost", "latency"]:
-                    raise ValueError(
-                        "Invalid tradeoff. Accepted values: cost, latency."
-                    )
-
-            if tradeoff is not None:
-                warnings.warn(
-                    "The tradeoff constructor parameter is deprecated and will be removed in a "
-                    "future version. Please specify the tradeoff when using model_select or invocation methods.",
-                    DeprecationWarning,
-                    stacklevel=2,
-                )
-
-            super().__init__(
-                api_key=api_key,
-                llm_configs=llm_configs,
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-                tools=tools,
-                callbacks=callbacks,
-                nd_api_url=nd_api_url,
-                user_agent=user_agent,
-                max_retries=max_retries,
-                timeout=timeout,
-                **kwargs,
-            )
-            self.user_agent = user_agent
-            assert (
-                self.api_key is not None
-            ), "API key is not set. Please set a Not Diamond API key."
-
-        @property
-        def chat(self):
-            return self
-
-        @property
-        def completions(self):
-            return self
-
-        def create_preference_id(self, name: Optional[str] = None) -> str:
-            return create_preference_id(self.api_key, name, self.nd_api_url)
-
-        async def amodel_select(
-            self,
-            messages: List[Dict[str, str]],
-            input: Optional[Dict[str, Any]] = None,
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> tuple[str, Optional[LLMConfig]]:
-            """
-            This function calls the NotDiamond backend to fetch the most suitable model for the given prompt,
-            and leaves the execution of the LLM call to the developer.
-            The function is async, so it's suitable for async codebases.
-
-            Parameters:
-                messages (List[Dict[str, str]]): List of messages, OpenAI style.
-                input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify
-                                                            the values for those variables. Defaults to None, assuming no
-                                                            variables.
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                nd_api_url (Optional[str]): The URL of the NotDiamond API. Defaults to settings.NOTDIAMOND_API_URL.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-            Returns:
-                tuple[str, Optional[LLMConfig]]: returns the session_id and the chosen LLM
-            """
-            if input is None:
-                input = {}
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            best_llm, session_id = await amodel_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-                _user_agent=self.user_agent,
-            )
-
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            return session_id, best_llm
-
-        def model_select(
-            self,
-            messages: List[Dict[str, str]],
-            input: Optional[Dict[str, Any]] = None,
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> tuple[str, Optional[LLMConfig]]:
-            """
-            This function calls the NotDiamond backend to fetch the most suitable model for the given prompt,
-            and leaves the execution of the LLM call to the developer.
-
-            Parameters:
-                messages (List[Dict[str, str]]): List of messages OpenAI style.
-                input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify
-                                                            the values for those variables. Defaults to None, assuming no
-                                                            variables.
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Returns:
-                tuple[str, Optional[LLMConfig]]: returns the session_id and the chosen LLM
-            """
-            if input is None:
-                input = {}
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            best_llm, session_id = model_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-                _user_agent=self.user_agent,
-            )
-
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            return session_id, best_llm
-
-        @staticmethod
-        def _parse_llm_configs_data(
-            llm_configs: list,
-        ) -> List[LLMConfig]:
-            providers = []
-            for llm_config in llm_configs:
-                if isinstance(llm_config, LLMConfig):
-                    providers.append(llm_config)
-                    continue
-                parsed_provider = LLMConfig.from_string(llm_config)
-                providers.append(parsed_provider)
-            return providers
-
-        def validate_params(
-            self,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-        ):
-            self.default = default
-
-            if max_model_depth is not None:
-                self.max_model_depth = max_model_depth
-
-            if self.llm_configs is None or len(self.llm_configs) == 0:
-                raise MissingLLMConfigs(
-                    "No LLM config speficied. Specify at least one."
-                )
-
-            if self.max_model_depth is None:
-                self.max_model_depth = len(self.llm_configs)
-
-            if self.max_model_depth == 0:
-                raise ValueError("max_model_depth has to be bigger than 0.")
-
-            if self.max_model_depth > len(self.llm_configs):
-                LOGGER.warning(
-                    "WARNING: max_model_depth cannot be bigger than the number of LLMs."
-                )
-                self.max_model_depth = len(self.llm_configs)
-
-            if tradeoff is not None:
-                if tradeoff not in ["cost", "latency"]:
-                    raise ValueError(
-                        "Invalid tradeoff. Accepted values: cost, latency."
-                    )
-            self.tradeoff = tradeoff
-
-            if preference_id is not None:
-                self.preference_id = preference_id
-
-            if latency_tracking is not None:
-                self.latency_tracking = latency_tracking
-
-            if hash_content is not None:
-                self.hash_content = hash_content
-
-        def bind_tools(
-            self, tools: Sequence[Union[Dict[str, Any], Callable]]
-        ) -> "NotDiamond":
-            """
-            Bind tools to the LLM object. The tools will be passed to the LLM object when invoking it.
-            Results in the tools being available in the LLM object.
-            You can access the tool_calls in the result via `result.tool_calls`.
-            """
-
-            for provider in self.llm_configs:
-                if provider.model not in settings.PROVIDERS[
-                    provider.provider
-                ].get("support_tools", []):
-                    raise ApiError(
-                        f"{provider.provider}/{provider.model} does not support function calling."
-                    )
-            self.tools = tools
-
-            return self
-
-        def call_callbacks(self, function_name: str, *args, **kwargs) -> None:
-            """
-            Call all callbacks with a specific function name.
-            """
-
-            if self.callbacks is None:
-                return
-
-            for callback in self.callbacks:
-                if hasattr(callback, function_name):
-                    getattr(callback, function_name)(*args, **kwargs)
-
-        def create(*args, **kwargs):
-            format_str = f"`{inspect.stack()[0].function}`"
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(fn_name=format_str)
-            )
-
-        async def acreate(*args, **kwargs):
-            format_str = f"`{inspect.stack()[0].function}`"
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(fn_name=format_str)
-            )
-
-        def invoke(*args, **kwargs):
-            format_str = f"`{inspect.stack()[0].function}`"
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(fn_name=format_str)
-            )
-
-        async def ainvoke(*args, **kwargs):
-            format_str = f"`{inspect.stack()[0].function}`"
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(fn_name=format_str)
-            )
-
-        def stream(*args, **kwargs):
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(
-                    fn_name=inspect.stack()[0].function
-                )
-            )
-
-        async def astream(*args, **kwargs):
-            raise CreateUnavailableError(
-                _invoke_error_msg_tmpl.format(
-                    fn_name=inspect.stack()[0].function
-                )
-            )
-
-        @property
-        def default_llm(self) -> LLMConfig:
-            """
-            Return the default LLM that's set on the NotDiamond client class.
-            """
-            if isinstance(self.default, int):
-                if self.default < len(self.llm_configs):
-                    return self.llm_configs[self.default]
-
-            if isinstance(self.default, str):
-                try:
-                    default = LLMConfig.from_string(self.default)
-                    if default in self.llm_configs:
-                        return default
-                except Exception as e:
-                    LOGGER.debug(f"Error setting default llm: {e}")
-
-            if isinstance(self.default, LLMConfig):
-                return self.default
-
-            default = self.llm_configs[0]
-            if self.default is None:
-                LOGGER.info(
-                    _no_default_llm_config_warning.format(
-                        provider=default.provider, model=default.model
-                    )
-                )
-            else:
-                LOGGER.info(
-                    _default_llm_config_invalid_warning.format(
-                        provider=default.provider, model=default.model
-                    )
-                )
-            return default
-
-    # Do not import from langchain_core directly, as it is now an optional SDK dependency
-    try:
-        LLM = _module_check("langchain_core.language_models.llms", "LLM")
-        BaseMessageChunk = _module_check(
-            "langchain_core.messages", "BaseMessageChunk"
-        )
-        JsonOutputParser = _module_check(
-            "langchain_core.output_parsers", "JsonOutputParser"
-        )
-        ChatPromptTemplate = _module_check(
-            "langchain_core.prompts", "ChatPromptTemplate"
-        )
-    except (ModuleNotFoundError, ImportError) as ierr:
-        msg = _invoke_error_msg_tmpl.format(fn_name="NotDiamond creation")
-        if import_target == _NDClientTarget.INVOKER:
-            msg += " Create was requested, however - raising..."
-            raise ImportError(msg) from ierr
-        else:
-            LOGGER.debug(msg)
-            return _NDRouterClient
-
-    class _NDInvokerClient(_NDRouterClient, LLM):
-        """
-        Implementation of NotDiamond class, the main class responsible for creating and invoking LLM prompts.
-        The class inherits from Langchain's LLM class. Starting reference is from here:
-        https://python.langchain.com/docs/modules/model_io/llms/custom_llm
-
-        It's mandatory to have an API key set. If the api_key is not explicitly specified,
-        it will check for NOTDIAMOND_API_KEY in the .env file.
-
-        Raises:
-            MissingLLMProviders: you must specify at least one LLM provider for the router to work
-            ApiError: error raised when the NotDiamond API call fails.
-                        Ensure to set a default LLM provider to not break the code.
-        """
-
-        api_key: str
-        llm_configs: Optional[List[Union[LLMConfig, str]]]
-        default: Union[LLMConfig, int, str]
-        max_model_depth: Optional[int]
-        latency_tracking: bool
-        hash_content: bool
-        tradeoff: Optional[str]
-        preference_id: Optional[str]
-        tools: Optional[Sequence[Union[Dict[str, Any], Callable]]]
-        callbacks: Optional[List]
-        nd_api_url: Optional[str]
-        user_agent: Union[str, None]
-
-        def __init__(
-            self,
-            llm_configs: Optional[List[Union[LLMConfig, str]]] = None,
-            api_key: Optional[str] = None,
-            default: Union[LLMConfig, int, str] = 0,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: bool = True,
-            hash_content: bool = False,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] = None,
-            callbacks: Optional[List] = None,
-            nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL,
-            user_agent: Union[str, None] = None,
-            timeout: Optional[Union[float, int]] = 60.0,
-            max_retries: Optional[int] = 3,
-            **kwargs,
-        ) -> None:
-            super().__init__(
-                api_key=api_key,
-                llm_configs=llm_configs,
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-                tools=tools,
-                callbacks=callbacks,
-                nd_api_url=nd_api_url,
-                user_agent=user_agent,
-                timeout=timeout,
-                max_retries=max_retries,
-                **kwargs,
-            )
-            if user_agent is None:
-                user_agent = settings.DEFAULT_USER_AGENT
-
-            if tradeoff is not None:
-                warnings.warn(
-                    "The tradeoff constructor parameter is deprecated and will be removed in a "
-                    "future version. Please specify the tradeoff when using model_select or invocation methods.",
-                    DeprecationWarning,
-                    stacklevel=2,
-                )
-
-            self.user_agent = user_agent
-            assert (
-                self.api_key is not None
-            ), "API key is not set. Please set a Not Diamond API key."
-
-        def __repr__(self) -> str:
-            class_name = self.__class__.__name__
-            address = hex(id(self))  # Gets the memory address of the object
-            return f"<{class_name} object at {address}>"
-
-        @property
-        def _llm_type(self) -> str:
-            return "NotDiamond LLM"
-
-        @staticmethod
-        def _inject_model_instruction(messages, parser):
-            format_instructions = parser.get_format_instructions()
-            format_instructions = format_instructions.replace(
-                "{", "{{"
-            ).replace("}", "}}")
-            messages[0]["content"] = (
-                format_instructions + "\n" + messages[0]["content"]
-            )
-            return messages
-
-        def _call(
-            self,
-            prompt: str,
-            stop: Optional[List[str]] = None,
-            run_manager: Optional[Any] = None,
-            **kwargs: Any,
-        ) -> str:
-            if stop is not None:
-                raise ValueError("stop kwargs are not permitted.")
-            return "This function is deprecated for the latest LangChain version, use invoke instead"
-
-        def create(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> tuple[str, str, LLMConfig]:
-            """
-            Function call to invoke the LLM, with the same interface
-            as the OpenAI Python library.
-
-            Parameters:
-                messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass
-                    the messages OpenAI style.
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a
-                                                                dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                nd_api_url (Optional[str]): The URL of the NotDiamond API. Defaults to settings.NOTDIAMOND_API_URL.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Returns:
-                tuple[Union[AIMessage, BaseModel], str, LLMConfig]:
-                    result: response type defined by Langchain, contains the response from the LLM.
-                    or object of the response_model
-                    str: session_id returned by the NotDiamond API
-                    LLMConfig: the best LLM selected by the router
-            """
-
-            return self.invoke(
-                messages=messages,
-                model=model,
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-                metric=metric,
-                previous_session=previous_session,
-                response_model=response_model,
-                timeout=timeout,
-                max_retries=max_retries,
-                **kwargs,
-            )
-
-        async def acreate(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> tuple[str, str, LLMConfig]:
-            """
-            Async function call to invoke the LLM, with the same interface
-            as the OpenAI Python library.
-
-            Parameters:
-                messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass
-                    the messages OpenAI style.
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a
-                                                                dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Returns:
-                tuple[Union[AIMessage, BaseModel], str, LLMConfig]:
-                    result: response type defined by Langchain, contains the response from the LLM.
-                    or object of the response_model
-                    str: session_id returned by the NotDiamond API
-                    LLMConfig: the best LLM selected by the router
-            """
-
-            result = await self.ainvoke(
-                messages=messages,
-                model=model,
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-                metric=metric,
-                previous_session=previous_session,
-                response_model=response_model,
-                timeout=timeout,
-                max_retries=max_retries,
-                **kwargs,
-            )
-            return result
-
-        def invoke(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            input: Optional[Dict[str, Any]] = None,
-            **kwargs,
-        ) -> tuple[str, str, LLMConfig]:
-            """
-            Function to invoke the LLM. Behind the scenes what happens:
-            1. API call to NotDiamond backend to get the most suitable LLM for the given prompt
-            2. Invoke the returned LLM client side
-            3. Return the response
-
-            Parameters:
-                prompt_template (Optional(Union[ NDPromptTemplate, NDChatPromptTemplate, str, ])):
-                    the prompt template defined by the user. It also supports Langchain prompt template types.
-                messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass
-                    the messages OpenAI style.
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a
-                                                                dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify
-                                                            the values for those variables. Defaults to None, assuming no
-                                                            variables.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Returns:
-                tuple[Union[AIMessage, BaseModel], str, LLMConfig]:
-                    result: response type defined by Langchain, contains the response from the LLM.
-                    or object of the response_model
-                    str: session_id returned by the NotDiamond API
-                    LLMConfig: the best LLM selected by the router
-            """
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            # If response_model is present, we will parse the response into the given model
-            # doing this here so that if validation errors occur, we can raise them before making the API call
-            response_model_parser = None
-            if response_model is not None:
-                self.verify_against_response_model()
-                response_model_parser = JsonOutputParser(
-                    pydantic_object=response_model
-                )
-
-            if input is None:
-                input = {}
-
-            best_llm, session_id = model_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-            )
-
-            is_default = False
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-                is_default = True
-
-            if best_llm.system_prompt is not None:
-                messages = inject_system_prompt(
-                    messages, best_llm.system_prompt
-                )
-
-            messages = o1_system_prompt_translate(messages, best_llm)
-
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            llm = self._llm_from_config(best_llm, callbacks=self.callbacks)
-
-            if self.tools and not is_o1_model(best_llm):
-                llm = llm.bind_tools(self.tools)
-
-            if response_model is not None:
-                messages = _NDInvokerClient._inject_model_instruction(
-                    messages, response_model_parser
-                )
-            chain_messages = [
-                (msg["role"], _curly_escape(msg["content"]))
-                for msg in messages
-            ]
-            prompt_template = ChatPromptTemplate.from_messages(chain_messages)
-            chain = prompt_template | llm
-            accepted_errors = _get_accepted_invoke_errors(best_llm.provider)
-
-            try:
-                if self.latency_tracking:
-                    result = self._invoke_with_latency_tracking(
-                        session_id=session_id,
-                        chain=chain,
-                        llm_config=best_llm,
-                        is_default=is_default,
-                        input=input,
-                        **kwargs,
-                    )
-                else:
-                    result = chain.invoke(input, **kwargs)
-            except accepted_errors as e:
-                if best_llm.provider == "google":
-                    LOGGER.warning(
-                        f"Submitted chat messages are violating Google requirements with error {e}. "
-                        "If you see this message, `notdiamond` has returned a Google model as the best option, "
-                        "but the LLM call will fail. If possible, `notdiamond` will fall back to a non-Google model."
-                    )
-
-                    non_google_llm = next(
-                        (
-                            llm_config
-                            for llm_config in self.llm_configs
-                            if llm_config.provider != "google"
-                        ),
-                        None,
-                    )
-
-                    if non_google_llm is not None:
-                        best_llm = non_google_llm
-                        llm = self._llm_from_config(
-                            best_llm, callbacks=self.callbacks
-                        )
-                        if response_model is not None:
-                            messages = (
-                                _NDInvokerClient._inject_model_instruction(
-                                    messages, response_model_parser
-                                )
-                            )
-                        chain_messages = [
-                            (msg["role"], _curly_escape(msg["content"]))
-                            for msg in messages
-                        ]
-                        prompt_template = ChatPromptTemplate.from_messages(
-                            chain_messages
-                        )
-                        chain = prompt_template | llm
-
-                        if self.latency_tracking:
-                            result = self._invoke_with_latency_tracking(
-                                session_id=session_id,
-                                chain=chain,
-                                llm_config=best_llm,
-                                is_default=is_default,
-                                input=input,
-                                **kwargs,
-                            )
-                        else:
-                            result = chain.invoke(input, **kwargs)
-                    else:
-                        raise e
-                else:
-                    raise e
-
-            if response_model is not None:
-                parsed_dict = response_model_parser.parse(result.content)
-                result = response_model.parse_obj(parsed_dict)
-
-            return result, session_id, best_llm
-
-        async def ainvoke(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            input: Optional[Dict[str, Any]] = None,
-            **kwargs,
-        ) -> tuple[str, str, LLMConfig]:
-            """
-            Function to invoke the LLM. Behind the scenes what happens:
-            1. API call to NotDiamond backend to get the most suitable LLM for the given prompt
-            2. Invoke the returned LLM client side
-            3. Return the response
-
-            Parameters:
-                messages (List[Dict[str, str]]): List of messages, OpenAI style
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify
-                                                            the values for those variables. Defaults to None, assuming no
-                                                            variables.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Returns:
-                tuple[Union[AIMessage, BaseModel], str, LLMConfig]:
-                    result: response type defined by Langchain, contains the response from the LLM.
-                    or object of the response_model
-                    str: session_id returned by the NotDiamond API
-                    LLMConfig: the best LLM selected by the router
-            """
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            response_model_parser = None
-            if response_model is not None:
-                self.verify_against_response_model()
-                response_model_parser = JsonOutputParser(
-                    pydantic_object=response_model
-                )
-
-            if input is None:
-                input = {}
-
-            best_llm, session_id = await amodel_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-            )
-
-            is_default = False
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-                is_default = True
-
-            if best_llm.system_prompt is not None:
-                messages = inject_system_prompt(
-                    messages, best_llm.system_prompt
-                )
-
-            messages = o1_system_prompt_translate(messages, best_llm)
-
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            llm = self._llm_from_config(best_llm, callbacks=self.callbacks)
-
-            if self.tools and not is_o1_model(best_llm):
-                llm = llm.bind_tools(self.tools)
-
-            if response_model is not None:
-                messages = _NDInvokerClient._inject_model_instruction(
-                    messages, response_model_parser
-                )
-            chain_messages = [
-                (msg["role"], _curly_escape(msg["content"]))
-                for msg in messages
-            ]
-            prompt_template = ChatPromptTemplate.from_messages(chain_messages)
-            chain = prompt_template | llm
-            accepted_errors = _get_accepted_invoke_errors(best_llm.provider)
-
-            try:
-                if self.latency_tracking:
-                    result = await self._async_invoke_with_latency_tracking(
-                        session_id=session_id,
-                        chain=chain,
-                        llm_config=best_llm,
-                        is_default=is_default,
-                        input=input,
-                        **kwargs,
-                    )
-                else:
-                    result = await chain.ainvoke(input, **kwargs)
-            except accepted_errors as e:
-                if best_llm.provider == "google":
-                    LOGGER.warning(
-                        f"Submitted chat messages are violating Google requirements with error {e}. "
-                        "If you see this message, `notdiamond` has returned a Google model as the best option, "
-                        "but the LLM call will fail. If possible, `notdiamond` will fall back to a non-Google model."
-                    )
-
-                    non_google_llm = next(
-                        (
-                            llm_config
-                            for llm_config in self.llm_configs
-                            if llm_config.provider != "google"
-                        ),
-                        None,
-                    )
-
-                    if non_google_llm is not None:
-                        best_llm = non_google_llm
-                        llm = self._llm_from_config(
-                            best_llm, callbacks=self.callbacks
-                        )
-                        if response_model is not None:
-                            messages = (
-                                _NDInvokerClient._inject_model_instruction(
-                                    messages, response_model_parser
-                                )
-                            )
-                        chain_messages = [
-                            (msg["role"], _curly_escape(msg["content"]))
-                            for msg in messages
-                        ]
-                        prompt_template = ChatPromptTemplate.from_messages(
-                            chain_messages
-                        )
-                        chain = prompt_template | llm
-
-                        if self.latency_tracking:
-                            result = (
-                                await self._async_invoke_with_latency_tracking(
-                                    session_id=session_id,
-                                    chain=chain,
-                                    llm_config=best_llm,
-                                    is_default=is_default,
-                                    input=input,
-                                    **kwargs,
-                                )
-                            )
-                        else:
-                            result = await chain.ainvoke(input, **kwargs)
-                    else:
-                        raise e
-                else:
-                    raise e
-
-            if response_model is not None:
-                parsed_dict = response_model_parser.parse(result.content)
-                result = response_model.parse_obj(parsed_dict)
-
-            return result, session_id, best_llm
-
-        def stream(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> Iterator[Union[BaseMessageChunk, BaseModel]]:
-            """
-            This function calls the NotDiamond backend to fetch the most suitable model for the given prompt,
-            and calls the LLM client side to stream the response.
-
-            Parameters:
-                messages (Optional[List[Dict[str, str]], optional): List of messages, OpenAI style
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a
-                                                                dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Yields:
-                Iterator[Union[BaseMessageChunk, BaseModel]]: returns the response in chunks.
-                    If response_model is present, it will return the partial model object
-            """
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            response_model_parser = None
-            if response_model is not None:
-                self.verify_against_response_model()
-                response_model_parser = JsonOutputParser(
-                    pydantic_object=response_model
-                )
-
-            best_llm, session_id = model_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-            )
-
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-
-            if best_llm.system_prompt is not None:
-                messages = inject_system_prompt(
-                    messages, best_llm.system_prompt
-                )
-
-            if response_model is not None:
-                messages = _NDInvokerClient._inject_model_instruction(
-                    messages, response_model_parser
-                )
-
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            llm = self._llm_from_config(best_llm, callbacks=self.callbacks)
-            if self.tools:
-                llm = llm.bind_tools(self.tools)
-
-            if response_model is not None:
-                chain = llm | response_model_parser
-            else:
-                chain = llm
-
-            for chunk in chain.stream(messages, **kwargs):
-                if response_model is None:
-                    yield chunk
-                else:
-                    partial_model = create_partial_model(response_model)
-                    yield partial_model(**chunk)
-
-        async def astream(
-            self,
-            messages: List[Dict[str, str]],
-            model: Optional[List[LLMConfig]] = None,
-            default: Optional[Union[LLMConfig, int, str]] = None,
-            max_model_depth: Optional[int] = None,
-            latency_tracking: Optional[bool] = None,
-            hash_content: Optional[bool] = None,
-            tradeoff: Optional[str] = None,
-            preference_id: Optional[str] = None,
-            metric: Metric = Metric("accuracy"),
-            previous_session: Optional[str] = None,
-            response_model: Optional[Type[BaseModel]] = None,
-            timeout: Optional[Union[float, int]] = None,
-            max_retries: Optional[int] = None,
-            **kwargs,
-        ) -> AsyncIterator[Union[BaseMessageChunk, BaseModel]]:
-            """
-            This function calls the NotDiamond backend to fetch the most suitable model for the given prompt,
-            and calls the LLM client side to stream the response. The function is async, so it's suitable for async codebases.
-
-            Parameters:
-                messages (Optional[List[Dict[str, str]], optional): List of messages, OpenAI style
-                model (Optional[List[LLMConfig]]): List of models to choose from.
-                default (Optional[Union[LLMConfig, int, str]]): Default LLM.
-                max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth
-                                                    of routing you're willing to go.
-                latency_tracking (Optional[bool]): Latency tracking flag.
-                hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API.
-                tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff
-                                                    for the router to determine the best LLM for a given query.
-                preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard.
-                                                Defaults to None.
-                metric (Metric, optional): Metric used by NotDiamond router to choose the best LLM.
-                                                Defaults to Metric("accuracy").
-                previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests.
-                response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the
-                                                                response into the given model. In which case result will a dict.
-                timeout (int): The number of seconds to wait before terminating the API call to Not Diamond backend.
-                max_retries (int): The number of retries to attempt before giving up.
-                **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through.
-
-            Raises:
-                ApiError: when the NotDiamond API fails
-
-            Yields:
-                AsyncIterator[Union[BaseMessageChunk, BaseModel]]: returns the response in chunks.
-                    If response_model is present, it will return the partial model object
-            """
-
-            if model is not None:
-                llm_configs = self._parse_llm_configs_data(model)
-                self.llm_configs = llm_configs
-
-            self.validate_params(
-                default=default,
-                max_model_depth=max_model_depth,
-                latency_tracking=latency_tracking,
-                hash_content=hash_content,
-                tradeoff=tradeoff,
-                preference_id=preference_id,
-            )
-
-            response_model_parser = None
-            if response_model is not None:
-                self.verify_against_response_model()
-                response_model_parser = JsonOutputParser(
-                    pydantic_object=response_model
-                )
-
-            best_llm, session_id = await amodel_select(
-                messages=messages,
-                llm_configs=self.llm_configs,
-                metric=metric,
-                notdiamond_api_key=self.api_key,
-                max_model_depth=self.max_model_depth,
-                hash_content=self.hash_content,
-                tradeoff=self.tradeoff,
-                preference_id=self.preference_id,
-                tools=self.tools,
-                previous_session=previous_session,
-                timeout=timeout or self.timeout,
-                max_retries=max_retries or self.max_retries,
-                nd_api_url=self.nd_api_url,
-            )
-
-            if not best_llm:
-                LOGGER.warning(
-                    f"ND API error. Falling back to default provider={self.default_llm.provider}/{self.default_llm.model}."
-                )
-                best_llm = self.default_llm
-
-            if best_llm.system_prompt is not None:
-                messages = inject_system_prompt(
-                    messages, best_llm.system_prompt
-                )
-            if response_model is not None:
-                messages = _NDInvokerClient._inject_model_instruction(
-                    messages, response_model_parser
-                )
-
-            self.call_callbacks("on_model_select", best_llm, best_llm.model)
-
-            llm = self._llm_from_config(best_llm, callbacks=self.callbacks)
-            if self.tools:
-                llm = llm.bind_tools(self.tools)
-
-            if response_model is not None:
-                chain = llm | response_model_parser
-            else:
-                chain = llm
-
-            async for chunk in chain.astream(messages, **kwargs):
-                if response_model is None:
-                    yield chunk
-                else:
-                    partial_model = create_partial_model(response_model)
-                    yield partial_model(**chunk)
-
-        async def _async_invoke_with_latency_tracking(
-            self,
-            session_id: str,
-            chain: Any,
-            llm_config: LLMConfig,
-            input: Optional[Dict[str, Any]] = {},
-            is_default: bool = True,
-            **kwargs,
-        ):
-            if session_id in ("NO-SESSION-ID", "") and not is_default:
-                error_message = (
-                    "ND session_id is not valid for latency tracking."
-                    + "Please check the API response."
-                )
-                self.call_callbacks("on_api_error", error_message)
-                raise ApiError(error_message)
-
-            start_time = time.time()
-
-            result = await chain.ainvoke(input, **kwargs)
-
-            end_time = time.time()
-
-            tokens_completed = token_counter(
-                model=llm_config.model,
-                messages=[{"role": "assistant", "content": result.content}],
-            )
-            tokens_per_second = tokens_completed / (end_time - start_time)
-
-            report_latency(
-                session_id=session_id,
-                llm_config=llm_config,
-                tokens_per_second=tokens_per_second,
-                notdiamond_api_key=self.api_key,
-                nd_api_url=self.nd_api_url,
-                _user_agent=self.user_agent,
-            )
-            self.call_callbacks(
-                "on_latency_tracking",
-                session_id,
-                llm_config,
-                tokens_per_second,
-            )
-
-            return result
-
-        def _invoke_with_latency_tracking(
-            self,
-            session_id: str,
-            chain: Any,
-            llm_config: LLMConfig,
-            input: Optional[Dict[str, Any]] = {},
-            is_default: bool = True,
-            **kwargs,
-        ):
-            LOGGER.debug(f"Latency tracking enabled, session_id={session_id}")
-            if session_id in ("NO-SESSION-ID", "") and not is_default:
-                error_message = (
-                    "ND session_id is not valid for latency tracking."
-                    + "Please check the API response."
-                )
-                self.call_callbacks("on_api_error", error_message)
-                raise ApiError(error_message)
-
-            start_time = time.time()
-            result = chain.invoke(input, **kwargs)
-            end_time = time.time()
-
-            tokens_completed = token_counter(
-                model=llm_config.model,
-                messages=[{"role": "assistant", "content": result.content}],
-            )
-            tokens_per_second = tokens_completed / (end_time - start_time)
-
-            report_latency(
-                session_id=session_id,
-                llm_config=llm_config,
-                tokens_per_second=tokens_per_second,
-                notdiamond_api_key=self.api_key,
-                nd_api_url=self.nd_api_url,
-                _user_agent=self.user_agent,
-            )
-            self.call_callbacks(
-                "on_latency_tracking",
-                session_id,
-                llm_config,
-                tokens_per_second,
-            )
-
-            return result
-
-        @staticmethod
-        def _llm_from_config(
-            provider: LLMConfig,
-            callbacks: Optional[List] = None,
-        ) -> Any:
-            default_kwargs = {"max_retries": 5, "timeout": 120}
-            passed_kwargs = {**default_kwargs, **provider.kwargs}
-
-            if provider.provider == "openai":
-                ChatOpenAI = _module_check(
-                    "langchain_openai.chat_models",
-                    "ChatOpenAI",
-                    provider.provider,
-                )
-                if is_o1_model(provider):
-                    passed_kwargs["temperature"] = 1.0
-
-                return ChatOpenAI(
-                    openai_api_key=provider.api_key,
-                    model_name=provider.model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "anthropic":
-                ChatAnthropic = _module_check(
-                    "langchain_anthropic", "ChatAnthropic", provider.provider
-                )
-                return ChatAnthropic(
-                    anthropic_api_key=provider.api_key,
-                    model=provider.model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "google":
-                ChatGoogleGenerativeAI = _module_check(
-                    "langchain_google_genai",
-                    "ChatGoogleGenerativeAI",
-                    provider.provider,
-                )
-                return ChatGoogleGenerativeAI(
-                    google_api_key=provider.api_key,
-                    model=provider.model,
-                    convert_system_message_to_human=True,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "cohere":
-                ChatCohere = _module_check(
-                    "langchain_cohere.chat_models",
-                    "ChatCohere",
-                    provider.provider,
-                )
-                return ChatCohere(
-                    cohere_api_key=provider.api_key,
-                    model=provider.model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "mistral":
-                ChatMistralAI = _module_check(
-                    "langchain_mistralai.chat_models",
-                    "ChatMistralAI",
-                    provider.provider,
-                )
-                return ChatMistralAI(
-                    mistral_api_key=provider.api_key,
-                    model=provider.model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "togetherai":
-                provider_settings = settings.PROVIDERS.get(
-                    provider.provider, None
-                )
-                model_prefixes = provider_settings.get("model_prefix", None)
-                model_prefix = model_prefixes.get(provider.model, None)
-                del passed_kwargs["max_retries"]
-                del passed_kwargs["timeout"]
-
-                if model_prefix is not None:
-                    model = f"{model_prefix}/{provider.model}"
-                ChatTogether = _module_check(
-                    "langchain_together", "ChatTogether", provider.provider
-                )
-                return ChatTogether(
-                    together_api_key=provider.api_key,
-                    model=model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "perplexity":
-                del passed_kwargs["max_retries"]
-                passed_kwargs["request_timeout"] = passed_kwargs["timeout"]
-                del passed_kwargs["timeout"]
-                ChatPerplexity = _module_check(
-                    "langchain_community.chat_models",
-                    "ChatPerplexity",
-                    provider.provider,
-                )
-                return ChatPerplexity(
-                    pplx_api_key=provider.api_key,
-                    model=provider.model,
-                    callbacks=callbacks,
-                    **passed_kwargs,
-                )
-            if provider.provider == "replicate":
-                provider_settings = settings.PROVIDERS.get(
-                    provider.provider, None
-                )
-                model_prefixes = provider_settings.get("model_prefix", None)
-                model_prefix = model_prefixes.get(provider.model, None)
-                passed_kwargs["request_timeout"] = passed_kwargs["timeout"]
-                del passed_kwargs["timeout"]
-
-                if model_prefix is not None:
-                    model = f"replicate/{model_prefix}/{provider.model}"
-                ChatLiteLLM = _module_check(
-                    "langchain_community.chat_models",
-                    "ChatLiteLLM",
-                    provider.provider,
-                )
-                return ChatLiteLLM(
-                    model=model,
-                    callbacks=callbacks,
-                    replicate_api_key=provider.api_key,
-                    **passed_kwargs,
-                )
-            raise ValueError(f"Unsupported provider: {provider.provider}")
-
-        def verify_against_response_model(self) -> bool:
-            """
-            Verify that the LLMs support response modeling.
-            """
-
-            for provider in self.llm_configs:
-                if provider.model not in settings.PROVIDERS[
-                    provider.provider
-                ].get("support_response_model", []):
-                    raise ApiError(
-                        f"{provider.provider}/{provider.model} does not support response modeling."
-                    )
-
-            return True
-
-    if import_target is _NDClientTarget.ROUTER:
-        return _NDRouterClient
-    return _NDInvokerClient
-
-
-_NDClient = _ndllm_factory()
-
-
-
-[docs] -class NotDiamond(_NDClient): - api_key: str - """ - API key required for making calls to NotDiamond. - You can get an API key via our dashboard: https://app.notdiamond.ai - If an API key is not set, it will check for NOTDIAMOND_API_KEY in .env file. - """ - - llm_configs: Optional[List[Union[LLMConfig, str]]] - """The list of LLMs that are available to route between.""" - - default: Union[LLMConfig, int, str] - """ - Set a default LLM, so in case anything goes wrong in the flow, - as for example NotDiamond API call fails, your code won't break and you have - a fallback model. There are various ways to configure a default model: - - - Integer, specifying the index of the default provider from the llm_configs list - - String, similar how you can specify llm_configs, of structure 'provider_name/model_name' - - LLMConfig, just directly specify the object of the provider - - By default, we will set your first LLM in the list as the default. - """ - - max_model_depth: Optional[int] - """ - If your top recommended model is down, specify up to which depth of routing you're willing to go. - If max_model_depth is not set, it defaults to the length of the llm_configs list. - If max_model_depth is set to 0, the init will fail. - If the value is larger than the llm_configs list length, we reset the value to len(llm_configs). - """ - - latency_tracking: bool - """ - Tracking and sending latency of LLM call to NotDiamond server as feedback, so we can improve our router. - By default this is turned on, set it to False to turn off. - """ - - hash_content: bool - """ - Hashing the content before being sent to the NotDiamond API. - By default this is False. - """ - - tradeoff: Optional[str] - """ - [DEPRECATED] The tradeoff constructor parameter is deprecated and will be removed in a future version. - Please specify the tradeoff when using model_select or invocation methods. - - Define tradeoff between "cost" and "latency" for the router to determine the best LLM for a given query. - If None is specified, then the router will not consider either cost or latency. - - The supported values: "cost", "latency" - - Defaults to None. - """ - - preference_id: Optional[str] - """The ID of the router preference that was configured via the Dashboard. Defaults to None.""" - - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] - """Bind tools to the LLM object. The tools will be passed to the LLM object when invoking it.""" - - nd_api_url: Optional[str] - """The URL of the NotDiamond API. Defaults to settings.NOTDIAMOND_API_URL.""" - - user_agent: Union[str, None] - - max_retries: int - """The maximum number of retries to make when calling the Not Diamond API.""" - - timeout: float - """The timeout for the Not Diamond API call.""" - -
-[docs] - class Config: - arbitrary_types_allowed = True
- - - def __init__( - self, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - user_agent: Union[str, None] = settings.DEFAULT_USER_AGENT, - *args, - **kwargs, - ): - super().__init__( - nd_api_url=nd_api_url, user_agent=user_agent, *args, **kwargs - ) - self.nd_api_url = nd_api_url - - if kwargs.get("tradeoff") is not None: - warnings.warn( - "The tradeoff constructor parameter is deprecated and will be removed in a " - "future version. Please specify the tradeoff when using model_select or invocation methods.", - DeprecationWarning, - stacklevel=2, - )
- - - -def _get_accepted_invoke_errors(provider: str) -> Tuple: - if provider == "google": - ChatGoogleGenerativeAIError = _module_check( - "langchain_google_genai.chat_models", - "ChatGoogleGenerativeAIError", - provider, - ) - accepted_errors = (ChatGoogleGenerativeAIError, ValueError) - else: - accepted_errors = (ValueError,) - return accepted_errors -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/llms/config.html b/docs/_build/html/_modules/notdiamond/llms/config.html deleted file mode 100644 index 1d9e55d6..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/config.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - notdiamond.llms.config — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.config

-import logging
-from typing import Optional
-
-from notdiamond import settings
-from notdiamond.exceptions import (
-    UnsupportedEmbeddingProvider,
-    UnsupportedLLMProvider,
-)
-
-POSSIBLE_PROVIDERS = list(settings.PROVIDERS.keys())
-POSSIBLE_MODELS = list(
-    model
-    for provider_values in settings.PROVIDERS.values()
-    for values in provider_values.values()
-    if isinstance(values, list)
-    for model in values
-)
-
-POSSIBLE_EMBEDDING_PROVIDERS = [
-    *list(settings.EMBEDDING_PROVIDERS.keys()),
-    "huggingface",
-]
-POSSIBLE_EMBEDDING_MODELS = list(
-    model
-    for provider_values in settings.EMBEDDING_PROVIDERS.values()
-    for values in provider_values.values()
-    if isinstance(values, list)
-    for model in values
-)
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-
-
-[docs] -class LLMConfig: - """ - A NotDiamond LLM provider config (or LLMConfig) is represented by a combination of provider and model. - Provider refers to the company of the foundational model, such as openai, anthropic, google. - The model represents the model name as defined by the owner company, such as gpt-3.5-turbo - Beside this you can also specify the API key for each provider, specify extra arguments - that are also supported by Langchain (eg. temperature), and a system prmopt to be used - with the provider. If the provider is selected during routing, then the system prompt will - be used, replacing the one in the message array if there are any. - - All supported providers and models can be found in our docs. - - If the API key it's not specified, it will try to pick it up from an .env file before failing. - As example for OpenAI it will look for OPENAI_API_KEY. - - Attributes: - provider (str): The name of the LLM provider (e.g., "openai", "anthropic"). Must be one of the - predefined providers in `POSSIBLE_PROVIDERS`. - model (str): The name of the LLM model to use (e.g., "gpt-3.5-turbo"). - Must be one of the predefined models in `POSSIBLE_MODELS`. - system_prompt (Optional[str], optional): The system prompt to use for the provider. Defaults to None. - api_key (Optional[str], optional): The API key for accessing the LLM provider's services. - Defaults to None, in which case it tries to fetch from the settings. - openrouter_model (str): The OpenRouter model equivalent for this provider / model - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedLLMProvider: If the `provider` or `model` specified is not supported. - """ - -
-[docs] - def __init__( - self, - provider: str, - model: str, - is_custom: bool = False, - system_prompt: Optional[str] = None, - context_length: Optional[int] = None, - input_price: Optional[float] = None, - custom_input_price: Optional[float] = None, - output_price: Optional[float] = None, - custom_output_price: Optional[float] = None, - latency: Optional[float] = None, - custom_latency: Optional[float] = None, - api_key: Optional[str] = None, - **kwargs, - ): - """_summary_ - - Args: - provider (str): The name of the LLM provider (e.g., "openai", "anthropic"). - model (str): The name of the LLM model to use (e.g., "gpt-3.5-turbo"). - is_custom (bool): Whether this is a custom model. Defaults to False. - system_prompt (Optional[str], optional): The system prompt to use for the provider. Defaults to None. - context_length (Optional[int], optional): Custom context window length for the provider/model. - custom_input_price (Optional[float], optional): Custom input price (USD) per million tokens for this - provider/model; will default to public input price if available. - custom_output_price (Optional[float], optional): Custom output price (USD) per million tokens for this - provider/model; will default to public output price if available. - custom_latency (Optional[float], optional): Custom latency (time to first token) for provider/model. - api_key (Optional[str], optional): The API key for accessing the LLM provider's services. - Defaults to None. - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedLLMProvider: If the `provider` or `model` specified is not supported. - """ - if is_custom: - self._openrouter_model = None - self.api_key = api_key - self.default_input_price = custom_input_price or input_price - self.default_output_price = custom_output_price or output_price - else: - if provider not in POSSIBLE_PROVIDERS: - raise UnsupportedLLMProvider( - f"Given LLM provider {provider} is not in the list of supported providers." - ) - if model not in POSSIBLE_MODELS: - raise UnsupportedLLMProvider( - f"Given LLM model {model} is not in the list of supported models." - ) - self._openrouter_model = settings.PROVIDERS[provider][ - "openrouter_identifier" - ].get(model, None) - - self.api_key = ( - api_key - if api_key is not None - else settings.PROVIDERS[provider]["api_key"] - ) - - self.default_input_price = settings.PROVIDERS[provider]["price"][ - model - ]["input"] - self.default_output_price = settings.PROVIDERS[provider]["price"][ - model - ]["output"] - - self.provider = provider - self.model = model - self.system_prompt = system_prompt - - self.is_custom = is_custom - self.context_length = context_length - self.input_price = custom_input_price or input_price - self.output_price = custom_output_price or output_price - self.latency = custom_latency or latency - - self.kwargs = kwargs
- - - def __str__(self) -> str: - return f"{self.provider}/{self.model}" - - def __repr__(self) -> str: - return f"LLMConfig({self.provider}/{self.model})" - - def __eq__(self, other): - if isinstance(other, LLMConfig): - return ( - self.provider == other.provider and self.model == other.model - ) - return False - - def __hash__(self): - return hash(str(self)) - - @property - def openrouter_model(self): - if self._openrouter_model is None: - LOGGER.warning( - f"Configured model {str(self)} is not available via OpenRouter. Please try another model." - ) - return self._openrouter_model - -
-[docs] - def prepare_for_request(self): - """ - Converts the LLMConfig object to a dict in the format accepted by - the NotDiamond API. - - Returns: - dict - """ - return { - "provider": self.provider, - "model": self.model, - "is_custom": self.is_custom, - "context_length": self.context_length, - "input_price": self.input_price, - "output_price": self.output_price, - "latency": self.latency, - }
- - -
-[docs] - def set_api_key(self, api_key: str) -> "LLMConfig": - self.api_key = api_key - - return self
- - -
-[docs] - @classmethod - def from_string(cls, llm_provider: str): - """ - We allow our users to specify LLM providers for NotDiamond in the string format 'provider_name/model_name', - as example 'openai/gpt-3.5-turbo'. Underlying our workflows we want to ensure we use LLMConfig as - the base type, so this class method converts a string specification of an LLM provider into an - LLMConfig object. - - Args: - llm_provider (str): this is the string definition of the LLM provider - - Returns: - LLMConfig: initialized object with correct provider and model - """ - split_items = llm_provider.split("/") - if len(split_items) not in [2, 3]: - raise ValueError( - f"Expected string of format 'provider/model' or 'prefix/provider/model' but got {llm_provider}" - ) - elif len(split_items) == 3: - _, provider, model = split_items - else: - provider = split_items[0] - model = split_items[1] - return cls(provider=provider, model=model)
-
- - - -
-[docs] -class EmbeddingConfig: - """ - A NotDiamond embedding provider config (or EmbeddingConfig) is represented by a combination of provider and model. - Provider refers to the company of the foundational model, such as openai, anthropic, google. - The model represents the model name as defined by the owner company, such as text-embedding-3-large - Beside this you can also specify the API key for each provider or extra arguments - that are also supported by Langchain. - - All supported providers and models can be found in our docs. - - If the API key is not specified, the Config will try to read the key from an .env file before failing. - For example, the Config will look for `OPENAI_API_KEY` to authenticate any OpenAI provider. - - Attributes: - provider (str): The name of the LLM provider (e.g., "openai", "anthropic"). Must be one of the - predefined providers in `POSSIBLE_EMBEDDING_PROVIDERS`. - model (str): The name of the LLM model to use (e.g., "gpt-3.5-turbo"). - Must be one of the predefined models in `POSSIBLE_MODELS`. - api_key (Optional[str], optional): The API key for accessing the LLM provider's services. - Defaults to None, in which case it tries to fetch from the environment. - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedLLMProvider: If the `provider` or `model` specified is not supported. - """ - -
-[docs] - def __init__( - self, - provider: str, - model: str, - api_key: Optional[str] = None, - **kwargs, - ): - """_summary_ - - Args: - provider (str): The name of the embedding provider (e.g., "openai", "anthropic"). - model (str): The name of the embedding model to use (e.g., "text-embedding-3-large"). - api_key (Optional[str], optional): The API key for accessing the embedding provider's services. - Defaults to None. - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedEmbeddingProvider: If the `provider` or `model` specified is not supported. - """ - if provider not in POSSIBLE_EMBEDDING_PROVIDERS: - raise UnsupportedEmbeddingProvider( - f"Given embedding provider {provider} is not in the list of supported providers." - ) - - if ( - model not in POSSIBLE_EMBEDDING_MODELS - and provider != "huggingface" - ): - raise UnsupportedEmbeddingProvider( - f"Given embedding model {model} is not in the list of supported models." - ) - - self.api_key = ( - api_key - if api_key is not None - else settings.PROVIDERS[provider]["api_key"] - ) - - self.provider = provider - self.model = model - self.kwargs = kwargs
- - - def __str__(self) -> str: - return f"{self.provider}/{self.model}" - - def __repr__(self) -> str: - return f"EmbeddingConfig({self.provider}/{self.model})" - - def __eq__(self, other): - if isinstance(other, EmbeddingConfig): - return ( - self.provider == other.provider and self.model == other.model - ) - return False - - def __hash__(self): - return hash(str(self)) - -
-[docs] - def set_api_key(self, api_key: str) -> "EmbeddingConfig": - self.api_key = api_key - - return self
- - -
-[docs] - @classmethod - def from_string(cls, llm_provider: str): - """ - We allow our users to specify LLM providers for NotDiamond in the string format 'provider_name/model_name', - for example 'openai/gpt-3.5-turbo'. Our workflows expect LLMConfig as - the base type, so this class method converts a string specification of an LLM provider into an - LLMConfig object. - - Args: - llm_provider (str): this is the string definition of the LLM provider - - Returns: - LLMConfig: initialized object with correct provider and model - """ - split_items = llm_provider.split("/") - if len(split_items) not in [2, 3]: - raise ValueError( - f"Expected string of format 'provider/model' or 'prefix/provider/model' but got {llm_provider}" - ) - elif len(split_items) == 3: - _, provider, model = split_items - else: - provider = split_items[0] - model = split_items[1] - return cls(provider=provider, model=model)
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/llms/llm.html b/docs/_build/html/_modules/notdiamond/llms/llm.html deleted file mode 100644 index b5ee56e5..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/llm.html +++ /dev/null @@ -1,1599 +0,0 @@ - - - - - - notdiamond.llms.llm — NotDiamond 0.2.7-beta - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.llm

-"""NDLLM Class"""
-
-import time
-from typing import (
-    Any,
-    AsyncIterator,
-    Callable,
-    Dict,
-    Iterator,
-    List,
-    Optional,
-    Sequence,
-    Type,
-    Union,
-)
-
-from langchain.prompts import PromptTemplate
-from langchain_anthropic import ChatAnthropic
-from langchain_cohere.chat_models import ChatCohere
-from langchain_community.chat_models import ChatLiteLLM, ChatPerplexity
-from langchain_core.callbacks.base import BaseCallbackHandler
-from langchain_core.callbacks.manager import CallbackManagerForLLMRun
-from langchain_core.language_models.llms import LLM
-from langchain_core.messages import AIMessage, BaseMessage, BaseMessageChunk
-from langchain_core.output_parsers import JsonOutputParser
-from langchain_core.prompt_values import StringPromptValue
-from langchain_core.prompts import ChatPromptTemplate
-from langchain_google_genai import ChatGoogleGenerativeAI
-from langchain_google_genai.chat_models import ChatGoogleGenerativeAIError
-from langchain_mistralai.chat_models import ChatMistralAI
-from langchain_openai import ChatOpenAI
-from langchain_together import Together
-from litellm import token_counter
-from pydantic import BaseModel
-from pydantic_partial import create_partial_model
-
-from notdiamond import settings
-from notdiamond.callbacks import NDLLMBaseCallbackHandler
-from notdiamond.exceptions import ApiError, MissingLLMProviders
-from notdiamond.llms.provider import NDLLMProvider
-from notdiamond.llms.request import amodel_select, model_select, report_latency
-from notdiamond.metrics.metric import NDMetric
-from notdiamond.prompts.prompt import NDChatPromptTemplate, NDPromptTemplate
-from notdiamond.types import NDApiKeyValidator
-
-
-
-[docs] -class NDLLM(LLM): - """ - Implementation of NDLLM class, the main class responsible for routing. - The class inherits from Langchain's LLM class. Starting reference is from here: - https://python.langchain.com/docs/modules/model_io/llms/custom_llm - - It's mandatory to have an API key set. If the api_key is not explicitly specified, - it will check for NOTDIAMOND_API_KEY in the .env file. - - Raises: - MissingLLMProviders: you must specify at least one LLM provider for the router to work - ApiError: error raised when the NotDiamond API call fails. - Ensure to set a default LLM provider to not break the code. - """ - - api_key: str - """ - API key required for making calls to NotDiamond. - You can get an API key via our dashboard: https://app.notdiamond.ai - If an API key is not set, it will check for NOTDIAMOND_API_KEY in .env file. - """ - - llm_providers: Optional[List[NDLLMProvider]] - """The list of LLM providers that are available to route between.""" - - default: Union[NDLLMProvider, int, str] - """ - Set a default LLM provider, so in case anything goes wrong in the flow, - as for example NotDiamond API call fails, your code won't break and you have - a fallback model. There are various ways to configure a default model: - - - Integer, specifying the index of the default provider from the llm_providers list - - String, similar how you can specify llm_providers, of structure 'provider_name/model_name' - - NDLLMProvider, just directly specify the object of the provider - - By default, we will set your first LLM in the list as the default. - """ - - max_model_depth: Optional[int] - """ - If your top recommended model is down, specify up to which depth of routing you're willing to go. - If max_model_depth is not set, it defaults to the length of the llm_providers list. - If max_model_depth is set to 0, the init will fail. - If the value is larger than the llm_providers list length, we reset the value to len(llm_providers). - """ - - latency_tracking: bool - """ - Tracking and sending latency of LLM call to NotDiamond server as feedback, so we can improve our router. - By default this is turned on, set it to False to turn off. - """ - - hash_content: bool - """ - Hashing the content before being sent to the NotDiamond API. - By default this is False. - """ - - tradeoff: Optional[str] - """ - Define tradeoff between "cost" and "latency" for the router to determine the best LLM for a given query. - If None is specified, then the router will not consider either cost or latency. - - The supported values: "cost", "latency" - - Defaults to None. - """ - - preference_id: Optional[str] - """The ID of the router preference that was configured via the Dashboard. Defaults to None.""" - - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] - """Bind tools to the LLM object. The tools will be passed to the LLM object when invoking it.""" - - callbacks: Optional[ - List[Union[BaseCallbackHandler, NDLLMBaseCallbackHandler]] - ] - """ - Callback handler for the LLM object. It will be passed to the LLM object when invoking it. - Also has custom NDLLM callbacks: - - on_model_select - - on_latency_tracking - - on_api_error - """ - - def __init__( - self, - llm_providers: Optional[List[NDLLMProvider]] = None, - api_key: Optional[str] = None, - default: Union[NDLLMProvider, int, str] = 0, - max_model_depth: Optional[int] = None, - latency_tracking: bool = True, - hash_content: bool = False, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - callbacks: Optional[ - List[Union[BaseCallbackHandler, NDLLMBaseCallbackHandler]] - ] = None, - **kwargs, - ) -> None: - if api_key is None: - api_key = settings.NOTDIAMOND_API_KEY - NDApiKeyValidator(api_key=api_key) - - if llm_providers is not None: - llm_providers = self._parse_llm_providers_data(llm_providers) - - if max_model_depth is None: - max_model_depth = len(llm_providers) - - if max_model_depth > len(llm_providers): - print( - "WARNING: max_model_depth cannot be bigger than the number of LLM providers." - ) - max_model_depth = len(llm_providers) - - if tradeoff is not None: - if tradeoff not in ["cost", "latency"]: - raise ValueError( - "Invalid tradeoff. Accepted values: cost, latency." - ) - - super(NDLLM, self).__init__( - api_key=api_key, - llm_providers=llm_providers, - default=default, - max_model_depth=max_model_depth, - latency_tracking=latency_tracking, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - callbacks=callbacks, - **kwargs, - ) - - def __repr__(self) -> str: - class_name = self.__class__.__name__ - address = hex(id(self)) # Gets the memory address of the object - return f"<{class_name} object at {address}>" - - @property - def chat(self): - return self - - @property - def completions(self): - return self - - @property - def default_llm_provider(self) -> Union[NDLLMProvider, None]: - """ - Return the default LLM provider that's set on the NDLLM class. - """ - if isinstance(self.default, int): - return self.llm_providers[int(self.default)] - if isinstance(self.default, str): - if self.default.isdigit(): - return self.llm_providers[int(self.default)] - return NDLLMProvider.from_string(self.default) - if isinstance(self.default, NDLLMProvider): - return self.default - return self.llm_providers[0] - - @staticmethod - def _parse_llm_providers_data(llm_providers: list) -> List[NDLLMProvider]: - providers = [] - for llm_provider in llm_providers: - if isinstance(llm_provider, NDLLMProvider): - providers.append(llm_provider) - continue - parsed_provider = NDLLMProvider.from_string(llm_provider) - providers.append(parsed_provider) - return providers - - @property - def _llm_type(self) -> str: - return "NotDiamond LLM" - - def _call( - self, - prompt: str, - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> str: - if stop is not None: - raise ValueError("stop kwargs are not permitted.") - return "This function is deprecated for the latest LangChain version, use invoke instead" - -
-[docs] - def create( - self, - messages: List[Dict[str, str]], - model: Optional[List[NDLLMProvider]] = None, - default: Optional[Union[NDLLMProvider, int, str]] = None, - max_model_depth: Optional[int] = None, - latency_tracking: Optional[bool] = None, - hash_content: Optional[bool] = None, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - """ - Function call to invoke the LLM, with the same interface - as the OpenAI Python library. - - Parameters: - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - model (Optional[List[NDLLMProvider]]): List of models to choose from. - default (Optional[Union[NDLLMProvider, int, str]]): Default LLM provider. - max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth - of routing you're willing to go. - latency_tracking (Optional[bool]): Latency tracking flag. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Returns: - tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - result: response type defined by Langchain, contains the response from the LLM. - or object of the response_model - str: session_id returned by the NotDiamond API - NDLLMProvider: the best LLM provider selected by the router - """ - - if model is not None: - llm_providers = self._parse_llm_providers_data(model) - self.llm_providers = llm_providers - - self.validate_params( - default=default, - max_model_depth=max_model_depth, - latency_tracking=latency_tracking, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - ) - - return self.invoke( - messages=messages, - metric=metric, - response_model=response_model, - **kwargs, - )
- - -
-[docs] - async def acreate( - self, - messages: List[Dict[str, str]], - model: Optional[List[NDLLMProvider]] = None, - default: Optional[Union[NDLLMProvider, int, str]] = None, - max_model_depth: Optional[int] = None, - latency_tracking: Optional[bool] = None, - hash_content: Optional[bool] = None, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - """ - Async function call to invoke the LLM, with the same interface - as the OpenAI Python library. - - Parameters: - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - model (Optional[List[NDLLMProvider]]): List of models to choose from. - default (Optional[Union[NDLLMProvider, int, str]]): Default LLM provider. - max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth - of routing you're willing to go. - latency_tracking (Optional[bool]): Latency tracking flag. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Returns: - tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - result: response type defined by Langchain, contains the response from the LLM. - or object of the response_model - str: session_id returned by the NotDiamond API - NDLLMProvider: the best LLM provider selected by the router - """ - if model is not None and len(model) > 0: - llm_providers = self._parse_llm_providers_data(model) - self.llm_providers = llm_providers - - self.validate_params( - default=default, - max_model_depth=max_model_depth, - latency_tracking=latency_tracking, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - ) - - result = await self.ainvoke( - messages=messages, - metric=metric, - response_model=response_model, - **kwargs, - ) - return result
- - -
-[docs] - def invoke( - self, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - messages: Optional[List[Dict[str, str]]] = None, - input: Optional[Dict[str, Any]] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - """ - Function to invoke the LLM. Behind the scenes what happens: - 1. API call to NotDiamond backend to get the most suitable LLM for the given prompt - 2. Invoke the returned LLM client side - 3. Return the response - - Parameters: - prompt_template (Optional(Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ])): - the prompt template defined by the user. It also supports Langchain prompt template types. - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the response into - the given model. In which case result will a dict. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Returns: - tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - result: response type defined by Langchain, contains the response from the LLM. - or object of the response_model - str: session_id returned by the NotDiamond API - NDLLMProvider: the best LLM provider selected by the router - """ - - # If response_model is present, we will parse the response into the given model - # doing this here so that if validation errors occur, we can raise them before making the API call - response_model_parser = None - if response_model is not None: - self.verify_against_response_model() - response_model_parser = JsonOutputParser( - pydantic_object=response_model - ) - - prompt_template = self._prepare_prompt_template( - prompt_template, - messages, - response_model_parser=response_model_parser, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - best_llm, session_id = model_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - is_default = False - if not best_llm: - best_llm = self.default_llm_provider - is_default = True - - if best_llm is None: - error_message = ( - "ND couldn't find a suitable model to call." - + "To avoid disruptions, we recommend setting a default fallback model or increasing max model depth." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - if best_llm.system_prompt is not None: - prompt_template = prompt_template.inject_system_prompt( - best_llm.system_prompt - ) - - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - llm = self._llm_from_provider(best_llm, callbacks=self.callbacks) - - if self.tools: - llm = llm.bind_tools(self.tools) - - chain = prompt_template | llm - - try: - if self.latency_tracking: - result = self._invoke_with_latency_tracking( - session_id=session_id, - chain=chain, - llm_provider=best_llm, - is_default=is_default, - input=input, - **kwargs, - ) - else: - result = chain.invoke(input, **kwargs) - except (ChatGoogleGenerativeAIError, ValueError) as e: - if ( - isinstance(prompt_template, NDChatPromptTemplate) - and best_llm.provider == "google" - ): - print( - f"WARNING: Google model's chat messages are violating requirements with error {e}." - ) - print( - "If you see this message, means the NotDiamond API returned a Google model as the best option," - + "but the LLM call will fail. So we will automatically fall back to a non-Google model, if possible." - ) - - non_google_llm = next( - ( - llm_provider - for llm_provider in self.llm_providers - if llm_provider.provider != "google" - ), - None, - ) - - if non_google_llm is not None: - best_llm = non_google_llm - llm = self._llm_from_provider( - best_llm, callbacks=self.callbacks - ) - chain = prompt_template | llm - - if self.latency_tracking: - result = self._invoke_with_latency_tracking( - session_id=session_id, - chain=chain, - llm_provider=best_llm, - is_default=is_default, - input=input, - **kwargs, - ) - else: - result = chain.invoke(input, **kwargs) - else: - raise e - else: - raise e - - if isinstance(result, str): - result = AIMessage(content=result) - - if response_model is not None: - parsed_dict = response_model_parser.parse(result.content) - result = response_model.parse_obj(parsed_dict) - - return result, session_id, best_llm
- - -
-[docs] - async def ainvoke( - self, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - messages: Optional[List[Dict[str, str]]] = None, - input: Optional[Dict[str, Any]] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - """ - Function to invoke the LLM. Behind the scenes what happens: - 1. API call to NotDiamond backend to get the most suitable LLM for the given prompt - 2. Invoke the returned LLM client side - 3. Return the response - - Parameters: - prompt_template (Optional(Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ])): - the prompt template defined by the user. It also supports Langchain prompt template types. - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the response into - the given model. In which case result will a dict. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Returns: - tuple[Union[AIMessage, BaseModel], str, NDLLMProvider]: - result: response type defined by Langchain, contains the response from the LLM. - or object of the response_model - str: session_id returned by the NotDiamond API - NDLLMProvider: the best LLM provider selected by the router - """ - - response_model_parser = None - if response_model is not None: - self.verify_against_response_model() - response_model_parser = JsonOutputParser( - pydantic_object=response_model - ) - - prompt_template = self._prepare_prompt_template( - prompt_template, - messages, - response_model_parser=response_model_parser, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - best_llm, session_id = await amodel_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - is_default = False - if not best_llm: - best_llm = self.default_llm_provider - is_default = True - - if best_llm is None: - error_message = ( - "ND couldn't find a suitable model to call." - + "To avoid disruptions, we recommend setting a default fallback model or make max depth larger." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - if best_llm.system_prompt is not None: - prompt_template = prompt_template.inject_system_prompt( - best_llm.system_prompt - ) - - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - llm = self._llm_from_provider(best_llm, callbacks=self.callbacks) - - if self.tools: - llm = llm.bind_tools(self.tools) - - chain = prompt_template | llm - - try: - if self.latency_tracking: - result = await self._async_invoke_with_latency_tracking( - session_id=session_id, - chain=chain, - llm_provider=best_llm, - is_default=is_default, - input=input, - **kwargs, - ) - else: - result = await chain.ainvoke(input, **kwargs) - except (ChatGoogleGenerativeAIError, ValueError) as e: - if ( - isinstance(prompt_template, NDChatPromptTemplate) - and best_llm.provider == "google" - ): - print( - f"WARNING: Google model's chat messages are violating requirements with error {e}." - ) - print( - "If you see this message, means the NotDiamond API returned a Google model as the best option," - + "but the LLM call will fail. So we will automatically fall back to a non-Google model, if possible." - ) - - non_google_llm = next( - ( - llm_provider - for llm_provider in self.llm_providers - if llm_provider.provider != "google" - ), - None, - ) - - if non_google_llm is not None: - best_llm = non_google_llm - llm = self._llm_from_provider( - best_llm, callbacks=self.callbacks - ) - chain = prompt_template | llm - - if self.latency_tracking: - result = ( - await self._async_invoke_with_latency_tracking( - session_id=session_id, - chain=chain, - llm_provider=best_llm, - is_default=is_default, - input=input, - **kwargs, - ) - ) - else: - result = await chain.ainvoke(input, **kwargs) - else: - raise e - else: - raise e - - if isinstance(result, str): - result = AIMessage(content=result) - - if response_model is not None: - parsed_dict = response_model_parser.parse(result.content) - result = response_model.parse_obj(parsed_dict) - - return result, session_id, best_llm
- - -
-[docs] - def stream( - self, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - messages: Optional[List[Dict[str, str]]] = None, - input: Optional[Dict[str, Any]] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> Iterator[Union[BaseMessageChunk, BaseModel]]: - """ - This function calls the NotDiamond backend to fetch the most suitable model for the given prompt, - and calls the LLM client side to stream the response. - - Parameters: - prompt_template (Optional(Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ])): - the prompt template defined by the user. It also supports Langchain prompt template types. - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the response into - the given model. In which case result will a dict. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Yields: - Iterator[Union[BaseMessageChunk, BaseModel]]: returns the response in chunks. - If response_model is present, it will return the partial model object - """ - - response_model_parser = None - if response_model is not None: - self.verify_against_response_model() - response_model_parser = JsonOutputParser( - pydantic_object=response_model - ) - - prompt_template = self._prepare_prompt_template( - prompt_template=prompt_template, - messages=messages, - response_model_parser=response_model_parser, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - best_llm, session_id = model_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - if not best_llm: - best_llm = self.default_llm_provider - - if best_llm is None: - error_message = ( - "ND couldn't find a suitable model to call." - + "To avoid disruptions, we recommend setting a default fallback model or make max depth larger." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - if best_llm.system_prompt is not None: - prompt_template = prompt_template.inject_system_prompt( - best_llm.system_prompt - ) - - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - llm = self._llm_from_provider(best_llm, callbacks=self.callbacks) - if self.tools: - llm = llm.bind_tools(self.tools) - - if response_model is not None: - chain = llm | response_model_parser - else: - chain = llm - - for chunk in chain.stream(prompt_template.format(), **kwargs): - if response_model is None: - yield chunk - else: - partial_model = create_partial_model(response_model) - yield partial_model(**chunk)
- - -
-[docs] - async def astream( - self, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - messages: Optional[List[Dict[str, str]]] = None, - input: Optional[Dict[str, Any]] = None, - metric: NDMetric = NDMetric("accuracy"), - response_model: Optional[Type[BaseModel]] = None, - **kwargs, - ) -> AsyncIterator[Union[BaseMessageChunk, BaseModel]]: - """ - This function calls the NotDiamond backend to fetch the most suitable model for the given prompt, - and calls the LLM client side to stream the response. The function is async, so it's suitable for async codebases. - - Parameters: - prompt_template (Optional(Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ])): - the prompt template defined by the user. It also supports Langchain prompt template types. - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - response_model (Optional[Type[BaseModel]], optional): If present, will use JsonOutputParser to parse the response into - the given model. In which case result will a dict. - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Raises: - ApiError: when the NotDiamond API fails - - Yields: - AsyncIterator[Union[BaseMessageChunk, BaseModel]]: returns the response in chunks. - If response_model is present, it will return the partial model object - """ - - response_model_parser = None - if response_model is not None: - self.verify_against_response_model() - response_model_parser = JsonOutputParser( - pydantic_object=response_model - ) - - prompt_template = self._prepare_prompt_template( - prompt_template=prompt_template, - messages=messages, - response_model_parser=response_model_parser, - ) - best_llm, session_id = await amodel_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - if not best_llm: - best_llm = self.default_llm_provider - - if best_llm is None: - error_message = ( - "ND couldn't find a suitable model to call." - + "To avoid disruptions, we recommend setting a default fallback model or make max depth larger." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - if best_llm.system_prompt is not None: - prompt_template = prompt_template.inject_system_prompt( - best_llm.system_prompt - ) - - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - llm = self._llm_from_provider(best_llm, callbacks=self.callbacks) - if self.tools: - llm = llm.bind_tools(self.tools) - - if response_model is not None: - chain = llm | response_model_parser - else: - chain = llm - - async for chunk in chain.astream(prompt_template.format(), **kwargs): - if response_model is None: - yield chunk - else: - partial_model = create_partial_model(response_model) - yield partial_model(**chunk)
- - -
-[docs] - async def amodel_select( - self, - messages: Optional[List[Dict[str, str]]] = None, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - input: Optional[Dict[str, Any]] = None, - model: Optional[List[NDLLMProvider]] = None, - default: Optional[Union[NDLLMProvider, int, str]] = None, - max_model_depth: Optional[int] = None, - latency_tracking: Optional[bool] = None, - hash_content: Optional[bool] = None, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - metric: NDMetric = NDMetric("accuracy"), - **kwargs, - ) -> tuple[str, Optional[NDLLMProvider]]: - """ - This function calls the NotDiamond backend to fetch the most suitable model for the given prompt, - and leaves the execution of the LLM call to the developer. - The function is async, so it's suitable for async codebases. - - Parameters: - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - prompt_template (Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ]): - the prompt template defined by the user. It also supports Langchain prompt template types. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - model (Optional[List[NDLLMProvider]]): List of models to choose from. - default (Optional[Union[NDLLMProvider, int, str]]): Default LLM provider. - max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth - of routing you're willing to go. - latency_tracking (Optional[bool]): Latency tracking flag. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Returns: - tuple[str, Optional[NDLLMProvider]]: returns the session_id and the chosen LLM provider - """ - prompt_template = self._prepare_prompt_template( - prompt_template, - messages, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - if model is not None: - llm_providers = self._parse_llm_providers_data(model) - self.llm_providers = llm_providers - - self.validate_params( - default=default, - max_model_depth=max_model_depth, - latency_tracking=latency_tracking, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - ) - - best_llm, session_id = await amodel_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - if not best_llm and self.default is not None: - print("ND API error. Falling back to default provider.") - best_llm = self.default_llm_provider - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - return session_id, best_llm
- - -
-[docs] - def model_select( - self, - messages: Optional[List[Dict[str, str]]] = None, - prompt_template: Optional[ - Union[ - NDPromptTemplate, - PromptTemplate, - NDChatPromptTemplate, - ChatPromptTemplate, - str, - ] - ] = None, - input: Optional[Dict[str, Any]] = None, - model: Optional[List[NDLLMProvider]] = None, - default: Optional[Union[NDLLMProvider, int, str]] = None, - max_model_depth: Optional[int] = None, - latency_tracking: Optional[bool] = None, - hash_content: Optional[bool] = None, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - metric: NDMetric = NDMetric("accuracy"), - **kwargs, - ) -> tuple[str, Optional[NDLLMProvider]]: - """ - This function calls the NotDiamond backend to fetch the most suitable model for the given prompt, - and leaves the execution of the LLM call to the developer. - - Parameters: - messages (Optional[List[Dict[str, str]], optional): Can be used instead of prompt_template to pass - the messages OpenAI style. - prompt_template (Union[ NDPromptTemplate, PromptTemplate, NDChatPromptTemplate, ChatPromptTemplate, str, ]): - the prompt template defined by the user. It also supports Langchain prompt template types. - input (Optional[Dict[str, Any]], optional): If the prompt_template contains variables, use input to specify - the values for those variables. Defaults to None, assuming no variables. - model (Optional[List[NDLLMProvider]]): List of models to choose from. - default (Optional[Union[NDLLMProvider, int, str]]): Default LLM provider. - max_model_depth (Optional[int]): If your top recommended model is down, specify up to which depth - of routing you're willing to go. - latency_tracking (Optional[bool]): Latency tracking flag. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str]): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - metric (NDMetric, optional): Metric used by NotDiamond router to choose the best LLM. - Defaults to NDMetric("accuracy"). - **kwargs: Any other arguments that are supported by Langchain's invoke method, will be passed through. - - Returns: - tuple[str, Optional[NDLLMProvider]]: returns the session_id and the chosen LLM provider - """ - prompt_template = self._prepare_prompt_template( - prompt_template, - messages, - ) - - if input is None: - input = {} - - prompt_template.partial_variables = { - **prompt_template.partial_variables, - **input, - } - - if model is not None: - llm_providers = self._parse_llm_providers_data(model) - self.llm_providers = llm_providers - - self.validate_params( - default=default, - max_model_depth=max_model_depth, - latency_tracking=latency_tracking, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - ) - - best_llm, session_id = model_select( - prompt_template=prompt_template, - llm_providers=self.llm_providers, - metric=metric, - notdiamond_api_key=self.api_key, - max_model_depth=self.max_model_depth, - hash_content=self.hash_content, - tradeoff=self.tradeoff, - preference_id=self.preference_id, - tools=self.tools, - ) - - if not best_llm and self.default is not None: - print("ND API error. Falling back to default provider.") - best_llm = self.default_llm_provider - self.call_callbacks("on_model_select", best_llm, best_llm.model) - - return session_id, best_llm
- - - async def _async_invoke_with_latency_tracking( - self, - session_id: str, - chain: Any, - llm_provider: NDLLMProvider, - input: Optional[Dict[str, Any]] = {}, - is_default: bool = True, - **kwargs, - ): - if session_id in ("NO-SESSION-ID", "") and not is_default: - error_message = ( - "ND session_id is not valid for latency tracking." - + "Please check the API response." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - start_time = time.time() - - result = await chain.ainvoke(input, **kwargs) - - end_time = time.time() - - if isinstance(result, str): - result = AIMessage(content=result) - - tokens_completed = token_counter( - model=llm_provider.model, - messages=[{"role": "assistant", "content": result.content}], - ) - tokens_per_second = tokens_completed / (end_time - start_time) - - report_latency( - session_id=session_id, - llm_provider=llm_provider, - tokens_per_second=tokens_per_second, - notdiamond_api_key=self.api_key, - ) - self.call_callbacks( - "on_latency_tracking", session_id, llm_provider, tokens_per_second - ) - - return result - - def _invoke_with_latency_tracking( - self, - session_id: str, - chain: Any, - llm_provider: NDLLMProvider, - input: Optional[Dict[str, Any]] = {}, - is_default: bool = True, - **kwargs, - ): - if session_id in ("NO-SESSION-ID", "") and not is_default: - error_message = ( - "ND session_id is not valid for latency tracking." - + "Please check the API response." - ) - self.call_callbacks("on_api_error", error_message) - raise ApiError(error_message) - - start_time = time.time() - result = chain.invoke(input, **kwargs) - end_time = time.time() - - if isinstance(result, str): - result = AIMessage(content=result) - - tokens_completed = token_counter( - model=llm_provider.model, - messages=[{"role": "assistant", "content": result.content}], - ) - tokens_per_second = tokens_completed / (end_time - start_time) - - report_latency( - session_id=session_id, - llm_provider=llm_provider, - tokens_per_second=tokens_per_second, - notdiamond_api_key=self.api_key, - ) - self.call_callbacks( - "on_latency_tracking", session_id, llm_provider, tokens_per_second - ) - - return result - - @staticmethod - def _llm_from_provider( - provider: NDLLMProvider, - callbacks: Optional[ - List[Union[BaseCallbackHandler, NDLLMBaseCallbackHandler]] - ], - ) -> Any: - default_kwargs = {"max_retries": 5, "timeout": 120} - passed_kwargs = {**default_kwargs, **provider.kwargs} - - if provider.provider == "openai": - return ChatOpenAI( - openai_api_key=provider.api_key, - model_name=provider.model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "anthropic": - return ChatAnthropic( - anthropic_api_key=provider.api_key, - model=provider.model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "google": - return ChatGoogleGenerativeAI( - google_api_key=provider.api_key, - model=provider.model, - convert_system_message_to_human=True, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "cohere": - return ChatCohere( - cohere_api_key=provider.api_key, - model=provider.model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "mistral": - return ChatMistralAI( - mistral_api_key=provider.api_key, - model=provider.model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "togetherai": - provider_settings = settings.PROVIDERS.get(provider.provider, None) - model_prefixes = provider_settings.get("model_prefix", None) - model_prefix = model_prefixes.get(provider.model, None) - del passed_kwargs["max_retries"] - del passed_kwargs["timeout"] - - if model_prefix is not None: - model = f"{model_prefix}/{provider.model}" - return Together( - together_api_key=provider.api_key, - model=model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "perplexity": - del passed_kwargs["max_retries"] - passed_kwargs["request_timeout"] = passed_kwargs["timeout"] - del passed_kwargs["timeout"] - return ChatPerplexity( - pplx_api_key=provider.api_key, - model=provider.model, - callbacks=callbacks, - **passed_kwargs, - ) - if provider.provider == "replicate": - provider_settings = settings.PROVIDERS.get(provider.provider, None) - model_prefixes = provider_settings.get("model_prefix", None) - model_prefix = model_prefixes.get(provider.model, None) - passed_kwargs["request_timeout"] = passed_kwargs["timeout"] - del passed_kwargs["timeout"] - - if model_prefix is not None: - model = f"replicate/{model_prefix}/{provider.model}" - return ChatLiteLLM( - model=model, - callbacks=callbacks, - replicate_api_key=provider.api_key, - **passed_kwargs, - ) - raise ValueError(f"Unsupported provider: {provider.provider}") - - @staticmethod - def _prepare_prompt_template( - prompt_template, messages=None, response_model_parser=None - ) -> Union[NDPromptTemplate, NDChatPromptTemplate]: - resulting_prompt_template = None - if prompt_template is not None and messages is not None: - print( - "Warning: prompt_template value is overriding messages value. Set one of those values for optimal performance." - ) - if prompt_template is not None: - if isinstance(prompt_template, NDPromptTemplate) or isinstance( - prompt_template, NDChatPromptTemplate - ): - resulting_prompt_template = prompt_template - elif isinstance(prompt_template, str): - resulting_prompt_template = NDPromptTemplate( - template=prompt_template - ) - elif isinstance(prompt_template, StringPromptValue): - resulting_prompt_template = NDChatPromptTemplate.from_messages( - prompt_template.to_messages() - ) - elif isinstance(prompt_template, PromptTemplate): - resulting_prompt_template = ( - NDPromptTemplate.from_langchain_prompt_template( - prompt_template - ) - ) - elif isinstance(prompt_template, ChatPromptTemplate): - resulting_prompt_template = ( - NDChatPromptTemplate.from_langchain_chat_prompt_template( - prompt_template - ) - ) - elif isinstance(prompt_template, list): - if all(isinstance(pt, BaseMessage) for pt in prompt_template): - resulting_prompt_template = ( - NDChatPromptTemplate.from_messages(prompt_template) - ) - if resulting_prompt_template is None: - raise ValueError( - f"Unsupported prompt_template type {type(prompt_template)}" - ) - if messages is not None: - resulting_prompt_template = ( - NDChatPromptTemplate.from_openai_messages(messages) - ) - - if resulting_prompt_template is None: - raise ValueError("prompt_template or messages must be specified.") - - if response_model_parser is not None: - resulting_prompt_template = ( - resulting_prompt_template.inject_model_instruction( - response_model_parser - ) - ) - - return resulting_prompt_template - -
-[docs] - def bind_tools( - self, - tools: Sequence[Union[Dict[str, Any], Callable]], - ) -> "NDLLM": - """ - Bind tools to the LLM object. The tools will be passed to the LLM object when invoking it. - Results in the tools being available in the LLM object. - You can access the tool_calls in the result via `result.tool_calls`. - """ - - for provider in self.llm_providers: - if provider.model not in settings.PROVIDERS[provider.provider].get( - "support_tools", [] - ): - raise ApiError( - f"{provider.provider}/{provider.model} does not support function calling." - ) - self.tools = tools - - return self
- - -
-[docs] - def call_callbacks(self, function_name: str, *args, **kwargs) -> None: - """ - Call all callbacks with a specific function name. - """ - - if self.callbacks is None: - return - - for callback in self.callbacks: - if hasattr(callback, function_name): - getattr(callback, function_name)(*args, **kwargs)
- - -
-[docs] - def verify_against_response_model(self) -> bool: - """ - Verify that the LLM providers support response modeling. - """ - - for provider in self.llm_providers: - if provider.model not in settings.PROVIDERS[provider.provider].get( - "support_response_model", [] - ): - raise ApiError( - f"{provider.provider}/{provider.model} does not support response modeling." - ) - - return True
- - -
-[docs] - def validate_params( - self, - default: Optional[Union[NDLLMProvider, int, str]] = None, - max_model_depth: Optional[int] = None, - latency_tracking: Optional[bool] = None, - hash_content: Optional[bool] = None, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - ): - if default is not None: - self.default = default - - if max_model_depth is not None: - self.max_model_depth = max_model_depth - - if self.llm_providers is None or len(self.llm_providers) == 0: - raise MissingLLMProviders( - "No LLM provider speficied. Specify at least one." - ) - - if self.max_model_depth is None: - self.max_model_depth = len(self.llm_providers) - - if self.max_model_depth == 0: - raise ValueError("max_model_depth has to be bigger than 0.") - - if self.max_model_depth > len(self.llm_providers): - print( - "WARNING: max_model_depth cannot be bigger than the number of LLM providers." - ) - self.max_model_depth = len(self.llm_providers) - - if tradeoff is not None: - if tradeoff not in ["cost", "latency"]: - raise ValueError( - "Invalid tradeoff. Accepted values: cost, latency." - ) - self.tradeoff = tradeoff - - if preference_id is not None: - self.preference_id = preference_id - - if latency_tracking is not None: - self.latency_tracking = latency_tracking - - if hash_content is not None: - self.hash_content = hash_content
-
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/llms/provider.html b/docs/_build/html/_modules/notdiamond/llms/provider.html deleted file mode 100644 index 08837352..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/provider.html +++ /dev/null @@ -1,253 +0,0 @@ - - - - - - notdiamond.llms.provider — NotDiamond 0.2.7-beta - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.provider

-from typing import Optional
-
-from notdiamond import settings
-from notdiamond.exceptions import UnsupportedLLMProvider
-
-POSSIBLE_PROVIDERS = list(settings.PROVIDERS.keys())
-POSSIBLE_MODELS = list(
-    model
-    for provider_values in settings.PROVIDERS.values()
-    for values in provider_values.values()
-    if isinstance(values, list)
-    for model in values
-)
-
-
-
-[docs] -class NDLLMProvider: - """ - An NDLLM provider is represented by a combination of provider and model. - Provider refers to the company of the foundational model, such as openai, anthropic, google. - The model represents the model name as defined by the owner company, such as gpt-3.5-turbo - Beside this you can also specify the API key for each provider, specify extra arguments - that are also supported by Langchain (eg. temperature), and a system prmopt to be used - with the provider. If the provider is selected during routing, then the system prompt will - be used, replacing the one in the message array if there are any. - - All supported providers and models can be found in our docs. - - If the API key it's not specified, it will try to pick it up from an .env file before failing. - As example for OpenAI it will look for OPENAI_API_KEY. - - Attributes: - provider (str): The name of the LLM provider (e.g., "openai", "anthropic"). Must be one of the - predefined providers in `POSSIBLE_PROVIDERS`. - model (str): The name of the LLM model to use (e.g., "gpt-3.5-turbo"). - Must be one of the predefined models in `POSSIBLE_MODELS`. - system_prompt (Optional[str], optional): The system prompt to use for the provider. Defaults to None. - api_key (Optional[str], optional): The API key for accessing the LLM provider's services. - Defaults to None, in which case it tries to fetch from the settings. - openrouter_model (str): The OpenRouter model equivalent for this provider / model - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedLLMProvider: If the `provider` or `model` specified is not supported. - """ - -
-[docs] - def __init__( - self, - provider: str, - model: str, - system_prompt: Optional[str] = None, - api_key: Optional[str] = None, - **kwargs, - ): - """_summary_ - - Args: - provider (str): The name of the LLM provider (e.g., "openai", "anthropic"). - model (str): The name of the LLM model to use (e.g., "gpt-3.5-turbo"). - system_prompt (Optional[str], optional): The system prompt to use for the provider. Defaults to None. - api_key (Optional[str], optional): The API key for accessing the LLM provider's services. - Defaults to None. - **kwargs: Additional keyword arguments that might be necessary for specific providers or models. - - Raises: - UnsupportedLLMProvider: If the `provider` or `model` specified is not supported. - """ - if provider not in POSSIBLE_PROVIDERS: - raise UnsupportedLLMProvider( - f"Given LLM provider {provider} is not in the list of supported providers." - ) - if model not in POSSIBLE_MODELS: - raise UnsupportedLLMProvider( - f"Given LLM model {model} is not in the list of supported models." - ) - - self.provider = provider - self.model = model - self.system_prompt = system_prompt - self._openrouter_model = settings.PROVIDERS[provider][ - "openrouter_identifier" - ].get(model, None) - self.api_key = ( - api_key - if api_key is not None - else settings.PROVIDERS[provider]["api_key"] - ) - self.kwargs = kwargs
- - - def __repr__(self) -> str: - return f"{self.provider}/{self.model}" - - @property - def openrouter_model(self): - if self._openrouter_model is None: - print("WARNING: this model is not available via OpenRouter") - return self._openrouter_model - -
-[docs] - def prepare_for_request(self): - """ - Converts the NDLLMProvider object to a dict in the format accepted by - the NotDiamond API. - - Returns: - dict - """ - return {"provider": self.provider, "model": self.model}
- - -
-[docs] - def set_api_key(self, api_key: str) -> "NDLLMProvider": - self.api_key = api_key - - return self
- - -
-[docs] - @classmethod - def from_string(cls, llm_provider: str): - """ - We allow our users to specify LLM providers for NDLLM in the string format 'provider_name/model_name', - as example 'openai/gpt-3.5-turbo'. Underlying our workflows we want to ensure we use NDLLMProvider as - the base type, so this class method converts a string specification of an LLM provider into an - NDLLMProvider object. - - Args: - llm_provider (str): this is the string definition of the LLM provider - - Returns: - NDLLMProvider: initialized object with correct provider and model - """ - split_items = llm_provider.split("/") - provider = split_items[0] - model = split_items[1] - return cls(provider=provider, model=model)
-
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/llms/providers.html b/docs/_build/html/_modules/notdiamond/llms/providers.html deleted file mode 100644 index 68a59f5b..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/providers.html +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - notdiamond.llms.providers — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.providers

-from enum import Enum
-
-from notdiamond.llms.config import LLMConfig
-
-
-
-[docs] -class NDLLMProviders(Enum): - """ - NDLLMProviders serves as a registry for the supported LLM models by NotDiamond. - It allows developers to easily specify available LLM providers for the router. - - Attributes: - GPT_3_5_TURBO (NDLLMProvider): refers to 'gpt-3.5-turbo' model by OpenAI - GPT_3_5_TURBO_0125 (NDLLMProvider): refers to 'gpt-3.5-turbo-0125' model by OpenAI - GPT_4 (NDLLMProvider): refers to 'gpt-4' model by OpenAI - GPT_4_0613 (NDLLMProvider): refers to 'gpt-4-0613' model by OpenAI - GPT_4_1106_PREVIEW (NDLLMProvider): refers to 'gpt-4-1106-preview' model by OpenAI - GPT_4_TURBO (NDLLMProvider): refers to 'gpt-4-turbo' model by OpenAI - GPT_4_TURBO_PREVIEW (NDLLMProvider): refers to 'gpt-4-turbo-preview' model by OpenAI - GPT_4_TURBO_2024_04_09 (NDLLMProvider): refers to 'gpt-4-turbo-2024-04-09' model by OpenAI - GPT_4o_2024_05_13 (NDLLMProvider): refers to 'gpt-4o-2024-05-13' model by OpenAI - GPT_4o_2024_08_06 (NDLLMProvider): refers to 'gpt-4o-2024-08-06' model by OpenAI - GPT_4o (NDLLMProvider): refers to 'gpt-4o' model by OpenAI - GPT_4o_MINI_2024_07_18 (NDLLMProvider): refers to 'gpt-4o-mini-2024-07-18' model by OpenAI - GPT_4o_MINI (NDLLMProvider): refers to 'gpt-4o-mini' model by OpenAI - GPT_4_0125_PREVIEW (NDLLMProvider): refers to 'gpt-4-0125-preview' model by OpenAI - GPT_4_1 (NDLLMProvider): refers to 'gpt-4.1' model by OpenAI - GPT_4_1_2025_04_14 (NDLLMProvider): refers to 'gpt-4.1-2025-04-14' model by OpenAI - GPT_4_1_MINI (NDLLMProvider): refers to 'gpt-4.1-mini' model by OpenAI - GPT_4_1_MINI_2025_04_14 (NDLLMProvider): refers to 'gpt-4.1-mini-2025-04-14' model by OpenAI - GPT_4_1_NANO (NDLLMProvider): refers to 'gpt-4.1-nano' model by OpenAI - GPT_4_1_NANO_2025_04_14 (NDLLMProvider): refers to 'gpt-4.1-nano-2025-04-14' model by OpenAI - O1_PREVIEW (NDLLMProvider): refers to 'o1-preview' model by OpenAI - O1_PREVIEW_2024_09_12 (NDLLMProvider): refers to 'o1-preview-2024-09-12' model by OpenAI - O1_MINI (NDLLMProvider): refers to 'o1-mini' model by OpenAI - O1_MINI_2024_09_12 (NDLLMProvider): refers to 'o1-mini-2024-09-12' model by OpenAI - - CLAUDE_2_1 (NDLLMProvider): refers to 'claude-2.1' model by Anthropic - CLAUDE_3_OPUS_20240229 (NDLLMProvider): refers to 'claude-3-opus-20240229' model by Anthropic - CLAUDE_3_SONNET_20240229 (NDLLMProvider): refers to 'claude-3-sonnet-20240229' model by Anthropic - CLAUDE_3_5_SONNET_20240620 (NDLLMProvider): refers to 'claude-3-5-sonnet-20240620' model by Anthropic - CLAUDE_3_7_SONNET_LATEST (NDLLMProvider): refers to 'claude-3-7-sonnet-latest' model by Anthropic - CLAUDE_3_7_SONNET_20250219 (NDLLMProvider): refers to 'claude-3-7-sonnet-20250219' model by Anthropic - CLAUDE_3_5_HAIKU_20241022 (NDLLMProvider): refers to 'claude-3-5-haiku-20241022' model by Anthropic - CLAUDE_3_HAIKU_20240307 (NDLLMProvider): refers to 'claude-3-haiku-20240307' model by Anthropic - CLAUDE_OPUS_4_20250514 (NDLLMProvider): refers to 'claude-opus-4-20250514' model by Anthropic - CLAUDE_SONNET_4_20250514 (NDLLMProvider): refers to 'claude-sonnet-4-20250514' model by Anthropic - CLAUDE_OPUS_4_0 (NDLLMProvider): refers to 'claude-opus-4-0' model by Anthropic - CLAUDE_SONNET_4_0 (NDLLMProvider): refers to 'claude-sonnet-4-0' model by Anthropic - - GEMINI_PRO (NDLLMProvider): refers to 'gemini-pro' model by Google - GEMINI_1_PRO_LATEST (NDLLMProvider): refers to 'gemini-1.0-pro-latest' model by Google - GEMINI_15_PRO_LATEST (NDLLMProvider): refers to 'gemini-1.5-pro-latest' model by Google - GEMINI_15_PRO_EXP_0801 (NDLLMProvider): refers to 'gemini-1.5-pro-exp-0801' model by Google - GEMINI_15_FLASH_LATEST (NDLLMProvider): refers to 'gemini-1.5-flash-latest' model by Google - GEMINI_20_FLASH (NDLLMProvider): refers to 'gemini-20-flash' model by Google - GEMINI_20_FLASH_001 (NDLLMProvider): refers to 'gemini-20-flash-001' model by Google - GEMINI_25_FLASH (NDLLMProvider): refers to 'gemini-25-flash' model by Google - GEMINI_25_PRO (NDLLMProvider): refers to 'gemini-25-pro' model by Google - - COMMAND_R (NDLLMProvider): refers to 'command-r' model by Cohere - COMMAND_R_PLUS (NDLLMProvider): refers to 'command-r-plus' model by Cohere - - MISTRAL_LARGE_LATEST (NDLLMProvider): refers to 'mistral-large-latest' model by Mistral AI - MISTRAL_LARGE_2407 (NDLLMProvider): refers to 'mistral-large-2407' model by Mistral AI - MISTRAL_LARGE_2402 (NDLLMProvider): refers to 'mistral-large-2402' model by Mistral AI - MISTRAL_MEDIUM_LATEST (NDLLMProvider): refers to 'mistral-medium-latest' model by Mistral AI - MISTRAL_SMALL_LATEST (NDLLMProvider): refers to 'mistral-small-latest' model by Mistral AI - OPEN_MISTRAL_7B (NDLLMProvider): refers to 'open-mistral-7b' model by Mistral AI - OPEN_MIXTRAL_8X7B (NDLLMProvider): refers to 'open-mixtral-8x7b' model by Mistral AI - OPEN_MIXTRAL_8X22B (NDLLMProvider): refers to 'open-mixtral-8x22b' model by Mistral AI - OPEN_MISTRAL_NEMO (NDLLMProvider): refers to 'open-mistral-nemo' model by Mistral AI - - TOGETHER_MISTRAL_7B_INSTRUCT_V0_2 (NDLLMProvider): refers to 'Mistral-7B-Instruct-v0.2' model served via TogetherAI - TOGETHER_MIXTRAL_8X7B_INSTRUCT_V0_1 (NDLLMProvider): refers to 'Mixtral-8x7B-Instruct-v0.1' model served via TogetherAI - TOGETHER_MIXTRAL_8X22B_INSTRUCT_V0_1 (NDLLMProvider): refers to 'Mixtral-8x22B-Instruct-v0.1' model served via TogetherAI - TOGETHER_LLAMA_3_70B_CHAT_HF (NDLLMProvider): refers to 'Llama-3-70b-chat-hf' model served via TogetherAI - TOGETHER_LLAMA_3_8B_CHAT_HF (NDLLMProvider): refers to 'Llama-3-8b-chat-hf' model served via TogetherAI - TOGETHER_QWEN2_72B_INSTRUCT (NDLLMProvider): refers to 'Qwen2-72B-Instruct' model served via TogetherAI - TOGETHER_LLAMA_3_1_8B_INSTRUCT_TURBO (NDLLMProvider): refers to 'Meta-Llama-3.1-8B-Instruct-Turbo' - model served via TogetherAI - TOGETHER_LLAMA_3_1_70B_INSTRUCT_TURBO (NDLLMProvider): refers to 'Meta-Llama-3.1-70B-Instruct-Turbo' - model served via TogetherAI - TOGETHER_LLAMA_3_1_405B_INSTRUCT_TURBO (NDLLMProvider): refers to 'Meta-Llama-3.1-405B-Instruct-Turbo' - model served via TogetherAI - TOGETHER_DEEPSEEK_R1 (NDLLMProvider): refers to 'DeepSeek-R1' - model served via TogetherAI - - REPLICATE_MISTRAL_7B_INSTRUCT_V0_2 (NDLLMProvider): refers to "mistral-7b-instruct-v0.2" model served via Replicate - REPLICATE_MIXTRAL_8X7B_INSTRUCT_V0_1 (NDLLMProvider): refers to "mixtral-8x7b-instruct-v0.1" model served via Replicate - REPLICATE_META_LLAMA_3_70B_INSTRUCT (NDLLMProvider): refers to "meta-llama-3-70b-instruct" model served via Replicate - REPLICATE_META_LLAMA_3_8B_INSTRUCT (NDLLMProvider): refers to "meta-llama-3-8b-instruct" model served via Replicate - REPLICATE_META_LLAMA_3_1_405B_INSTRUCT (NDLLMProvider): refers to "meta-llama-3.1-405b-instruct" - model served via Replicate - - SONAR (NDLLMProvider): refers to "sonar" model by Perplexity - """ - - GPT_3_5_TURBO = ("openai", "gpt-3.5-turbo") - GPT_3_5_TURBO_0125 = ("openai", "gpt-3.5-turbo-0125") - GPT_4 = ("openai", "gpt-4") - GPT_4_0613 = ("openai", "gpt-4-0613") - GPT_4_1106_PREVIEW = ("openai", "gpt-4-1106-preview") - GPT_4_TURBO = ("openai", "gpt-4-turbo") - GPT_4_TURBO_PREVIEW = ("openai", "gpt-4-turbo-preview") - GPT_4_TURBO_2024_04_09 = ("openai", "gpt-4-turbo-2024-04-09") - GPT_4o_2024_05_13 = ("openai", "gpt-4o-2024-05-13") - GPT_4o_2024_08_06 = ("openai", "gpt-4o-2024-08-06") - GPT_4o = ("openai", "gpt-4o") - GPT_4o_MINI_2024_07_18 = ("openai", "gpt-4o-mini-2024-07-18") - GPT_4o_MINI = ("openai", "gpt-4o-mini") - GPT_4_0125_PREVIEW = ("openai", "gpt-4-0125-preview") - GPT_4_1 = ("openai", "gpt-4.1") - GPT_4_1_2025_04_14 = ("openai", "gpt-4.1-2025-04-14") - GPT_4_1_MINI = ("openai", "gpt-4.1-mini") - GPT_4_1_MINI_2025_04_14 = ("openai", "gpt-4.1-mini-2025-04-14") - GPT_4_1_NANO = ("openai", "gpt-4.1-nano") - GPT_4_1_NANO_2025_04_14 = ("openai", "gpt-4.1-nano-2025-04-14") - O1_PREVIEW = ("openai", "o1-preview") - O1_PREVIEW_2024_09_12 = ("openai", "o1-preview-2024-09-12") - O1_MINI = ("openai", "o1-mini") - O1_MINI_2024_09_12 = ("openai", "o1-mini-2024-09-12") - CHATGPT_4o_LATEST = ("openai", "chatgpt-4o-latest") - - CLAUDE_2_1 = ("anthropic", "claude-2.1") - CLAUDE_3_OPUS_20240229 = ("anthropic", "claude-3-opus-20240229") - CLAUDE_3_SONNET_20240229 = ("anthropic", "claude-3-sonnet-20240229") - CLAUDE_3_5_SONNET_20240620 = ("anthropic", "claude-3-5-sonnet-20240620") - CLAUDE_3_5_SONNET_20241022 = ("anthropic", "claude-3-5-sonnet-20241022") - CLAUDE_3_5_SONNET_LATEST = ("anthropic", "claude-3-5-sonnet-latest") - CLAUDE_3_7_SONNET_LATEST = ("anthropic", "claude-3-7-sonnet-latest") - CLAUDE_3_7_SONNET_20250219 = ("anthropic", "claude-3-7-sonnet-20250219") - CLAUDE_3_5_HAIKU_20241022 = ("anthropic", "claude-3-5-haiku-20241022") - CLAUDE_3_HAIKU_20240307 = ("anthropic", "claude-3-haiku-20240307") - CLAUDE_OPUS_4_20250514 = ("anthropic", "claude-opus-4-20250514") - CLAUDE_SONNET_4_20250514 = ("anthropic", "claude-sonnet-4-20250514") - CLAUDE_OPUS_4_0 = ("anthropic", "claude-opus-4-0") - CLAUDE_SONNET_4_0 = ("anthropic", "claude-sonnet-4-0") - - GEMINI_PRO = ("google", "gemini-pro") - GEMINI_1_PRO_LATEST = ("google", "gemini-1.0-pro-latest") - GEMINI_15_PRO_LATEST = ("google", "gemini-1.5-pro-latest") - GEMINI_15_PRO_EXP_0801 = ("google", "gemini-1.5-pro-exp-0801") - GEMINI_15_FLASH_LATEST = ("google", "gemini-1.5-flash-latest") - GEMINI_20_FLASH = ("google", "gemini-2.0-flash") - GEMINI_20_FLASH_001 = ("google", "gemini-2.0-flash-001") - GEMINI_25_FLASH = ("google", "gemini-2.5-flash") - GEMINI_25_PRO = ("google", "gemini-2.5-pro") - - COMMAND_R = ("cohere", "command-r") - COMMAND_R_PLUS = ("cohere", "command-r-plus") - - MISTRAL_LARGE_LATEST = ("mistral", "mistral-large-latest") - MISTRAL_LARGE_2407 = ("mistral", "mistral-large-2407") - MISTRAL_LARGE_2402 = ("mistral", "mistral-large-2402") - MISTRAL_MEDIUM_LATEST = ("mistral", "mistral-medium-latest") - MISTRAL_SMALL_LATEST = ("mistral", "mistral-small-latest") - CODESTRAL_LATEST = ("mistral", "codestral-latest") - OPEN_MISTRAL_7B = ("mistral", "open-mistral-7b") - OPEN_MIXTRAL_8X7B = ("mistral", "open-mixtral-8x7b") - OPEN_MIXTRAL_8X22B = ("mistral", "open-mixtral-8x22b") - OPEN_MISTRAL_NEMO = ("mistral", "open-mistral-nemo") - - TOGETHER_MISTRAL_7B_INSTRUCT_V0_2 = ( - "togetherai", - "Mistral-7B-Instruct-v0.2", - ) - TOGETHER_MIXTRAL_8X7B_INSTRUCT_V0_1 = ( - "togetherai", - "Mixtral-8x7B-Instruct-v0.1", - ) - TOGETHER_MIXTRAL_8X22B_INSTRUCT_V0_1 = ( - "togetherai", - "Mixtral-8x22B-Instruct-v0.1", - ) - TOGETHER_LLAMA_3_70B_CHAT_HF = ("togetherai", "Llama-3-70b-chat-hf") - TOGETHER_LLAMA_3_8B_CHAT_HF = ("togetherai", "Llama-3-8b-chat-hf") - TOGETHER_QWEN2_72B_INSTRUCT = ("togetherai", "Qwen2-72B-Instruct") - TOGETHER_LLAMA_3_1_8B_INSTRUCT_TURBO = ( - "togetherai", - "Meta-Llama-3.1-8B-Instruct-Turbo", - ) - TOGETHER_LLAMA_3_1_70B_INSTRUCT_TURBO = ( - "togetherai", - "Meta-Llama-3.1-70B-Instruct-Turbo", - ) - TOGETHER_LLAMA_3_1_405B_INSTRUCT_TURBO = ( - "togetherai", - "Meta-Llama-3.1-405B-Instruct-Turbo", - ) - TOGETHER_DEEPSEEK_R1 = ("togetherai", "DeepSeek-R1") - - SONAR = ( - "perplexity", - "sonar", - ) - - REPLICATE_MISTRAL_7B_INSTRUCT_V0_2 = ( - "replicate", - "mistral-7b-instruct-v0.2", - ) - REPLICATE_MIXTRAL_8X7B_INSTRUCT_V0_1 = ( - "replicate", - "mixtral-8x7b-instruct-v0.1", - ) - REPLICATE_META_LLAMA_3_70B_INSTRUCT = ( - "replicate", - "meta-llama-3-70b-instruct", - ) - REPLICATE_META_LLAMA_3_8B_INSTRUCT = ( - "replicate", - "meta-llama-3-8b-instruct", - ) - REPLICATE_META_LLAMA_3_1_405B_INSTRUCT = ( - "replicate", - "meta-llama-3.1-405b-instruct", - ) - - def __new__(cls, provider, model): - return LLMConfig(provider=provider, model=model)
- - - -
-[docs] -def is_o1_model(llm: LLMConfig): - return llm in ( - NDLLMProviders.O1_PREVIEW, - NDLLMProviders.O1_PREVIEW_2024_09_12, - NDLLMProviders.O1_MINI, - NDLLMProviders.O1_MINI_2024_09_12, - )
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/llms/request.html b/docs/_build/html/_modules/notdiamond/llms/request.html deleted file mode 100644 index 139902a5..00000000 --- a/docs/_build/html/_modules/notdiamond/llms/request.html +++ /dev/null @@ -1,483 +0,0 @@ - - - - - - notdiamond.llms.request — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.llms.request

-import json
-import logging
-from typing import Any, Callable, Dict, List, Optional, Sequence, Union
-
-import aiohttp
-import requests
-
-from notdiamond import settings
-from notdiamond._utils import _default_headers, convert_tool_to_openai_function
-from notdiamond.llms.config import LLMConfig
-from notdiamond.metrics.metric import Metric
-from notdiamond.types import ModelSelectRequestPayload
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-
-
-[docs] -def model_select_prepare( - messages: List[Dict[str, str]], - llm_configs: List[LLMConfig], - metric: Metric, - notdiamond_api_key: str, - max_model_depth: int, - hash_content: bool, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] = [], - previous_session: Optional[str] = None, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -): - """ - This is the core method for the model_select endpoint. - It returns the best fitting LLM to call and a session ID that can be used for feedback. - - Parameters: - messages (List[Dict[str, str]]): list of messages to be used for the LLM call - llm_configs (List[LLMConfig]): a list of available LLMs that the router can decide from - metric (Metric): metric based off which the router makes the decision. As of now only 'accuracy' supported. - notdiamond_api_key (str): API key generated via the NotDiamond dashboard. - max_model_depth (int): if your top recommended model is down, specify up to which depth of routing you're willing to go. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str], optional): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests. - async_mode (bool, optional): whether to run the request in async mode. Defaults to False. - nd_api_url (Optional[str], optional): The URL of the NotDiamond API. Defaults to None. - - Returns: - tuple(url, payload, headers): returns data to be used for the API call of modelSelect - """ - url = f"{nd_api_url}/v2/modelRouter/modelSelect" - tools_dict = get_tools_in_openai_format(tools) - - payload: ModelSelectRequestPayload = { - "messages": messages, - "llm_providers": [ - llm_provider.prepare_for_request() for llm_provider in llm_configs - ], - "metric": metric.metric, - "max_model_depth": max_model_depth, - "hash_content": hash_content, - } - - if tools_dict: - payload["tools"] = tools_dict - if tradeoff is not None: - payload["tradeoff"] = tradeoff - if preference_id is not None: - payload["preference_id"] = preference_id - if previous_session is not None: - payload["previous_session"] = previous_session - - headers = _default_headers(notdiamond_api_key, _user_agent) - - return url, payload, headers
- - - -
-[docs] -def get_tools_in_openai_format( - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]], -): - """ - This function converts the tools list into the format that OpenAI expects. - Does this by using langchains Model that automatically creates the dictionary on bind_tools - - Parameters: - tools (Optional[Sequence[Union[Dict[str, Any], Callable]]]): list of tools to be converted - - Returns: - dict: dictionary of tools in the format that OpenAI expects - """ - if tools: - return [ - { - "type": "function", - "function": convert_tool_to_openai_function(tool), - } - for tool in tools - ] - - return None
- - - -
-[docs] -def model_select_parse(response_code, response_json, llm_configs): - if response_code == 200: - providers = response_json["providers"] - session_id = response_json["session_id"] - - top_provider = providers[0] - - best_llm = list( - filter( - lambda x: (x.model == top_provider["model"]) - & (x.provider == top_provider["provider"]), - llm_configs, - ) - )[0] - return best_llm, session_id - - error_message = response_json["detail"] - LOGGER.error(f"API error: {response_code}. {error_message}") - return None, "NO-SESSION-ID"
- - - -
-[docs] -def model_select( - messages: List[Dict[str, str]], - llm_configs: List[LLMConfig], - metric: Metric, - notdiamond_api_key: str, - max_model_depth: int, - hash_content: bool, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] = [], - previous_session: Optional[str] = None, - timeout: Optional[Union[float, int]] = 60, - max_retries: Optional[int] = 3, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -): - """ - This endpoint receives the prompt and routing settings, and makes a call to the NotDiamond API. - It returns the best fitting LLM to call and a session ID that can be used for feedback. - - Parameters: - messages (List[Dict[str, str]]): list of messages to be used for the LLM call - llm_configs (List[LLMConfig]): a list of available LLMs that the router can decide from - metric (Metric): metric based off which the router makes the decision. As of now only 'accuracy' supported. - notdiamond_api_key (str): API key generated via the NotDiamond dashboard. - max_model_depth (int): if your top recommended model is down, specify up to which depth of routing you're willing to go. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str], optional): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests. - timeout (int, optional): timeout for the request. Defaults to 60. - max_retries (int, optional): The maximum number of retries to make when calling the Not Diamond API. - Defaults to 3. - nd_api_url (Optional[str], optional): The URL of the NotDiamond API. Defaults to None. - Returns: - tuple(LLMConfig, string): returns a tuple of the chosen LLMConfig to call and a session ID string. - In case of an error the LLM defaults to None and the session ID defaults - to 'NO-SESSION-ID'. - """ - url, payload, headers = model_select_prepare( - messages=messages, - llm_configs=llm_configs, - metric=metric, - notdiamond_api_key=notdiamond_api_key, - max_model_depth=max_model_depth, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - tools=tools, - previous_session=previous_session, - nd_api_url=nd_api_url, - _user_agent=_user_agent, - ) - - for n_retry in range(1, max_retries + 1): - try: - response = requests.post( - url, data=json.dumps(payload), headers=headers, timeout=timeout - ) - response_code = response.status_code - response_json = response.json() - break - except Exception as e: - LOGGER.error( - f"Retry {n_retry} of {max_retries}: API error: {e}", - exc_info=True, - ) - if n_retry == max_retries: - return None, "NO-SESSION-ID" - - best_llm, session_id = model_select_parse( - response_code, response_json, llm_configs - ) - - return best_llm, session_id
- - - -
-[docs] -async def amodel_select( - messages: List[Dict[str, str]], - llm_configs: List[LLMConfig], - metric: Metric, - notdiamond_api_key: str, - max_model_depth: int, - hash_content: bool, - tradeoff: Optional[str] = None, - preference_id: Optional[str] = None, - tools: Optional[Sequence[Union[Dict[str, Any], Callable]]] = [], - previous_session: Optional[str] = None, - timeout: Optional[Union[float, int]] = 60, - max_retries: Optional[int] = 3, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -): - """ - This endpoint receives the prompt and routing settings, and makes a call to the NotDiamond API. - It returns the best fitting LLM to call and a session ID that can be used for feedback. - - Parameters: - messages (List[Dict[str, str]]): list of messages to be used for the LLM call - llm_configs (List[LLMConfig]): a list of available LLMs that the router can decide from - metric (Metric): metric based off which the router makes the decision. As of now only 'accuracy' supported. - notdiamond_api_key (str): API key generated via the NotDiamond dashboard. - max_model_depth (int): if your top recommended model is down, specify up to which depth of routing you're willing to go. - hash_content (Optional[bool]): Flag for hashing content before sending to NotDiamond API. - tradeoff (Optional[str], optional): Define the "cost" or "latency" tradeoff - for the router to determine the best LLM for a given query. - preference_id (Optional[str], optional): The ID of the router preference that was configured via the Dashboard. - Defaults to None. - previous_session (Optional[str], optional): The session ID of a previous session, allow you to link requests. - timeout (int, optional): timeout for the request. Defaults to 60. - max_retries (int, optional): The maximum number of retries to make when calling the Not Diamond API. - nd_api_url (Optional[str], optional): The URL of the NotDiamond API. Defaults to None. - Returns: - tuple(LLMConfig, string): returns a tuple of the chosen LLMConfig to call and a session ID string. - In case of an error the LLM defaults to None and the session ID defaults - to 'NO-SESSION-ID'. - """ - url, payload, headers = model_select_prepare( - messages=messages, - llm_configs=llm_configs, - metric=metric, - notdiamond_api_key=notdiamond_api_key, - max_model_depth=max_model_depth, - hash_content=hash_content, - tradeoff=tradeoff, - preference_id=preference_id, - tools=tools, - previous_session=previous_session, - nd_api_url=nd_api_url, - _user_agent=_user_agent, - ) - - for n_retry in range(1, max_retries + 1): - try: - async with aiohttp.ClientSession() as session: - async with session.post( - url, - data=json.dumps(payload), - headers=headers, - timeout=timeout, - ) as response: - response_code = response.status - response_json = await response.json() - break - except Exception as e: - LOGGER.error( - f"Retry {n_retry} of {max_retries}: API error: {e}", - exc_info=True, - ) - if n_retry == max_retries: - return None, "NO-SESSION-ID" - - best_llm, session_id = model_select_parse( - response_code, response_json, llm_configs - ) - - return best_llm, session_id
- - - -
-[docs] -def report_latency( - session_id: str, - llm_config: LLMConfig, - tokens_per_second: float, - notdiamond_api_key: str, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -): - """ - This method makes an API call to the NotDiamond server to report the latency of an LLM call. - It helps fine-tune our model router and ensure we offer recommendations that meet your latency expectation. - - This feature can be disabled on the NDLLM class level by setting `latency_tracking` to False. - - Parameters: - session_id (str): the session ID that was returned from the `invoke` or `model_select` calls, so we know which - router call your latency report refers to. - llm_provider (LLMConfig): specifying the LLM provider for which the latency is reported - tokens_per_second (float): latency of the model call calculated based on time elapsed, input tokens, and output tokens - notdiamond_api_key (str): NotDiamond API call used for authentication - nd_api_url (Optional[str], optional): The URL of the NotDiamond API. Defaults to None. - Returns: - int: status code of the API call, 200 if it's success - - Raises: - ApiError: if the API call to the NotDiamond backend fails, this error is raised - """ - url = f"{nd_api_url}/v2/report/metrics/latency" - - payload = { - "session_id": session_id, - "provider": llm_config.prepare_for_request(), - "feedback": {"tokens_per_second": tokens_per_second}, - } - - headers = _default_headers(notdiamond_api_key, _user_agent) - - try: - response = requests.post(url, json=payload, headers=headers) - except Exception as e: - LOGGER.error( - f"API error for report metrics latency: {e}", exc_info=True - ) - return 500 - - return response.status_code
- - - -
-[docs] -def create_preference_id( - notdiamond_api_key: str, - name: Optional[str] = None, - nd_api_url: Optional[str] = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -) -> str: - """ - Create a preference id with an optional name. The preference name will appear in your - dashboard on Not Diamond. - """ - url = f"{nd_api_url}/v2/preferences/userPreferenceCreate" - headers = _default_headers(notdiamond_api_key, _user_agent) - res = requests.post(url=url, headers=headers, json={"name": name}) - if res.status_code == 200: - preference_id = res.json()["preference_id"] - else: - raise Exception(f"Error creating preference ID: {res.text}") - - return preference_id
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/metrics/metric.html b/docs/_build/html/_modules/notdiamond/metrics/metric.html deleted file mode 100644 index ef71d364..00000000 --- a/docs/_build/html/_modules/notdiamond/metrics/metric.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - notdiamond.metrics.metric — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.metrics.metric

-from typing import Optional
-
-from notdiamond import settings
-from notdiamond.exceptions import ApiError
-from notdiamond.llms.config import LLMConfig
-from notdiamond.metrics.request import feedback_request
-from notdiamond.types import NDApiKeyValidator
-
-
-
-[docs] -class Metric: - def __init__(self, metric: Optional[str] = "accuracy"): - self.metric = metric - - def __call__(self): - return self.metric - -
-[docs] - def feedback( - self, - session_id: str, - llm_config: LLMConfig, - value: int, - notdiamond_api_key: Optional[str] = None, - _user_agent: str = None, - ): - if notdiamond_api_key is None: - notdiamond_api_key = settings.NOTDIAMOND_API_KEY - NDApiKeyValidator(api_key=notdiamond_api_key) - if value not in [0, 1]: - raise ApiError("Invalid feedback value. It must be 0 or 1.") - - return feedback_request( - session_id=session_id, - llm_config=llm_config, - feedback_payload=self.request_payload(value), - notdiamond_api_key=notdiamond_api_key, - _user_agent=_user_agent, - )
- - -
-[docs] - def request_payload(self, value: int): - return {self.metric: value}
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/metrics/request.html b/docs/_build/html/_modules/notdiamond/metrics/request.html deleted file mode 100644 index 08141cc7..00000000 --- a/docs/_build/html/_modules/notdiamond/metrics/request.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - notdiamond.metrics.request — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.metrics.request

-import logging
-from typing import Dict
-
-import requests
-
-from notdiamond import settings
-from notdiamond._utils import _default_headers
-from notdiamond.exceptions import ApiError
-from notdiamond.llms.config import LLMConfig
-from notdiamond.types import FeedbackRequestPayload
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-
-
-[docs] -def feedback_request( - session_id: str, - llm_config: LLMConfig, - feedback_payload: Dict[str, int], - notdiamond_api_key: str, - nd_api_url: str = settings.NOTDIAMOND_API_URL, - _user_agent: str = settings.DEFAULT_USER_AGENT, -) -> bool: - url = f"{nd_api_url}/v2/report/metrics/feedback" - - payload: FeedbackRequestPayload = { - "session_id": session_id, - "provider": llm_config.prepare_for_request(), - "feedback": feedback_payload, - } - - headers = _default_headers(notdiamond_api_key, _user_agent) - - try: - response = requests.post(url, json=payload, headers=headers) - except Exception as e: - raise ApiError(f"ND API error for feedback: {e}") - - if response.status_code != 200: - LOGGER.error( - f"ND API feedback error: failed to report feedback with status {response.status_code}. {response.text}" - ) - return False - - return True
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/prompts.html b/docs/_build/html/_modules/notdiamond/prompts.html deleted file mode 100644 index a7a9c4fb..00000000 --- a/docs/_build/html/_modules/notdiamond/prompts.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - notdiamond.prompts — NotDiamond 0.3.38 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.prompts

-import logging
-import re
-from typing import Dict, List
-
-from notdiamond.llms.config import LLMConfig
-from notdiamond.llms.providers import is_o1_model
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-
-
-[docs] -def inject_system_prompt( - messages: List[Dict[str, str]], system_prompt: str -) -> List[Dict[str, str]]: - """ - Add a system prompt to an OpenAI-style message list. If a system prompt is already present, replace it. - """ - new_messages = [] - found = False - for msg in messages: - # t7: replace the first system prompt with the new one - if msg["role"] == "system" and not found: - new_messages.append({"role": "system", "content": system_prompt}) - found = True - else: - new_messages.append(msg) - if not found: - new_messages.insert(0, {"role": "system", "content": system_prompt}) - return new_messages
- - - -
-[docs] -def _curly_escape(text: str) -> str: - """ - Escape curly braces in the text, but only for single occurrences of alphabetic characters. - This function will not escape double curly braces or non-alphabetic characters. - """ - return re.sub(r"(?<!{){([a-zA-Z])}(?!})", r"{{\1}}", text)
- - - -
-[docs] -def o1_system_prompt_translate( - messages: List[Dict[str, str]], llm: LLMConfig -) -> List[Dict[str, str]]: - if is_o1_model(llm): - translated_messages = [] - for msg in messages: - if msg["role"] == "system": - translated_messages.append( - {"role": "user", "content": msg["content"]} - ) - else: - translated_messages.append(msg) - return translated_messages - return messages
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/prompts/hash.html b/docs/_build/html/_modules/notdiamond/prompts/hash.html deleted file mode 100644 index 4de284b2..00000000 --- a/docs/_build/html/_modules/notdiamond/prompts/hash.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - notdiamond.prompts.hash — NotDiamond 0.2.2-beta - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.prompts.hash

-import ppdeep
-
-
-
-[docs] -def nd_hash(s: str) -> str: - """ - Source of library from: https://github.com/elceef/ppdeep - """ - return ppdeep.hash(s)
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/prompts/prompt.html b/docs/_build/html/_modules/notdiamond/prompts/prompt.html deleted file mode 100644 index a5d931f1..00000000 --- a/docs/_build/html/_modules/notdiamond/prompts/prompt.html +++ /dev/null @@ -1,350 +0,0 @@ - - - - - - notdiamond.prompts.prompt — NotDiamond 0.2.7-beta - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.prompts.prompt

-from typing import Any, Dict, List, Optional, Union
-
-from langchain.prompts import PromptTemplate
-from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
-from langchain_core.output_parsers import JsonOutputParser
-from langchain_core.prompts import ChatPromptTemplate
-from langchain_core.prompts.string import get_template_variables
-
-
-
-[docs] -class NDPromptTemplate(PromptTemplate): - """Custom implementation of NDPromptTemplate - Starting reference is from here: - https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.prompt.PromptTemplate.html - """ - - def __init__( - self, - template: str, - input_variables: Optional[List[str]] = None, - partial_variables: Optional[Dict[str, Any]] = {}, - ): - if input_variables is None: - input_variables = get_template_variables(template, "f-string") - - if partial_variables: - input_variables = [] - - super(NDPromptTemplate, self).__init__( - template=template, - input_variables=input_variables, - partial_variables=partial_variables, - ) - -
-[docs] - @classmethod - def from_langchain_prompt_template(cls, prompt_template: PromptTemplate): - return cls( - template=prompt_template.template, - input_variables=prompt_template.input_variables, - partial_variables=prompt_template.partial_variables, - )
- - -
-[docs] - def format(self, **kwargs: Any) -> str: - """Format the prompt template with the given variables and convert it to NDPromptTemplate.""" - return super(NDPromptTemplate, self).format(**kwargs)
- - -
-[docs] - def optimize(self): - print("Not yet implemented!")
- - -
-[docs] - def prepare_for_request(self): - return [{"role": "user", "content": self.format()}]
- - -
-[docs] - def inject_system_prompt(self, system_prompt: str): - self.template = system_prompt - return self
- - -
-[docs] - def inject_model_instruction(self, parser: JsonOutputParser): - format_instructions = parser.get_format_instructions() - format_instructions = format_instructions.replace("{", "{{").replace( - "}", "}}" - ) - self.template = format_instructions + "\n" + self.template - - return self
-
- - - -
-[docs] -class NDChatPromptTemplate(ChatPromptTemplate): - """ - Starting reference is from - here:https://api.python.langchain.com/en/latest/prompts/langchain_core.prompts.chat.ChatPromptTemplate.html - """ - - def __init__( - self, - messages: Optional[List] = None, - input_variables: Optional[List[str]] = None, - partial_variables: [str, Any] = dict, - ): - if messages is None: - messages = [] - if partial_variables: - input_variables = [] - - super().__init__( - messages=messages, - input_variables=input_variables, - partial_variables=partial_variables, - ) - - @property - def template(self): - message = """ - SYSTEM: {system_prompt} - CONTEXT: {context_prompt} - QUERY: {user_query} - """ - return message - -
-[docs] - @classmethod - def from_langchain_chat_prompt_template( - cls, chat_prompt_template: ChatPromptTemplate - ): - return cls( - messages=chat_prompt_template.messages, - input_variables=chat_prompt_template.input_variables, - partial_variables=chat_prompt_template.partial_variables, - )
- - -
-[docs] - @classmethod - def from_openai_messages(cls, messages: List[Dict[str, str]]): - transformed_messages = [] - for message in messages: - if message["role"] == "system": - transformed_messages.append(SystemMessage(message["content"])) - elif message["role"] == "assistant": - transformed_messages.append(AIMessage(message["content"])) - elif message["role"] == "user": - transformed_messages.append(HumanMessage(message["content"])) - else: - raise ValueError(f"Unsupported role: {message['role']}") - return cls( - messages=transformed_messages, - input_variables=None, - partial_variables={}, - )
- - -
-[docs] - def format(self, **kwargs: Any) -> str: - """Format the prompt template with the given variables. and converts it to NDChatPromptTemplate.""" - return super(NDChatPromptTemplate, self).format(**kwargs)
- - -
-[docs] - def get_last_human_message(self, formated_messages: List) -> str: - for message in reversed(formated_messages): - if isinstance(message, HumanMessage): - return message.content - - raise ValueError("No human message found in the list of messages.")
- - -
-[docs] - def get_role_of_message( - self, message: Union[AIMessage, HumanMessage, SystemMessage] - ) -> str: - if isinstance(message, SystemMessage): - return "system" - if isinstance(message, AIMessage): - return "assistant" - if isinstance(message, HumanMessage): - return "user" - raise ValueError(f"Unsupported message type: {type(message)}")
- - -
-[docs] - def prepare_for_request(self): - formated_messages = self.format_messages(**self.partial_variables) - messages = [] - for message in formated_messages: - if ( - isinstance(message, SystemMessage) - or isinstance(message, AIMessage) - or isinstance(message, HumanMessage) - ): - messages.append( - { - "role": self.get_role_of_message(message), - "content": message.content, - } - ) - - return messages
- - -
-[docs] - def inject_system_prompt(self, system_prompt: str): - messages = self.prepare_for_request() - new_messages = [] - found = False - for msg in messages: - # t7: replace the first system prompt with the new one - if msg["role"] == "system" and not found: - new_messages.append( - {"role": "system", "content": system_prompt} - ) - found = True - else: - new_messages.append(msg) - if not found: - new_messages.insert( - 0, {"role": "system", "content": system_prompt} - ) - return self.from_openai_messages(new_messages)
- - -
-[docs] - def inject_model_instruction(self, parser: JsonOutputParser): - format_instructions = parser.get_format_instructions() - format_instructions = format_instructions.replace("{", "{{").replace( - "}", "}}" - ) - self.messages[0].content = ( - format_instructions + "\n" + self.messages[0].content - ) - - return self
-
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/toolkit/custom_router.html b/docs/_build/html/_modules/notdiamond/toolkit/custom_router.html deleted file mode 100644 index b495b080..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/custom_router.html +++ /dev/null @@ -1,572 +0,0 @@ - - - - - - notdiamond.toolkit.custom_router — NotDiamond 0.4.4 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for notdiamond.toolkit.custom_router

-import json
-import tempfile
-import time
-from collections import OrderedDict
-from typing import Dict, List, Optional, Tuple, Union
-
-import numpy as np
-import pandas as pd
-import requests
-from tqdm import tqdm
-
-from notdiamond.exceptions import ApiError
-from notdiamond.llms.client import NotDiamond
-from notdiamond.llms.config import LLMConfig
-from notdiamond.settings import NOTDIAMOND_API_KEY, NOTDIAMOND_API_URL, VERSION
-from notdiamond.types import NDApiKeyValidator
-from notdiamond._utils import token_counter
-
-
-
-[docs] -class CustomRouter: - """ - Implementation of CustomRouter class, used to train custom routers using custom datasets. - - Attributes: - language (str): The language of the dataset in lowercase. Defaults to "english". - maximize (bool): Whether higher score is better. Defaults to true. - api_key (Optional[str], optional): The NotDiamond API key. If not specified, will try to - find it in the environment variable NOTDIAMOND_API_KEY. - """ - - def __init__( - self, - language: str = "english", - maximize: bool = True, - api_key: Optional[str] = None, - ): - if api_key is None: - api_key = NOTDIAMOND_API_KEY - NDApiKeyValidator(api_key=api_key) - - self.api_key = api_key - self.language = language - self.maximize = maximize - - def _request_train_router( - self, - prompt_column: str, - dataset_file: str, - llm_configs: List[LLMConfig], - preference_id: Optional[str], - nd_api_url: str, - ) -> str: - url = f"{nd_api_url}/v2/pzn/trainCustomRouter" - - files = {"dataset_file": open(dataset_file, "rb")} - - payload = { - "language": self.language, - "llm_providers": json.dumps( - [provider.prepare_for_request() for provider in llm_configs] - ), - "prompt_column": prompt_column, - "maximize": self.maximize, - "preference_id": preference_id, - } - - headers = { - "Authorization": f"Bearer {self.api_key}", - "User-Agent": f"Python-SDK/{VERSION}", - } - - response = requests.post( - url=url, headers=headers, data=payload, files=files - ) - if response.status_code != 200: - raise ApiError( - f"ND backend error status code: {response.status_code}, {response.text}" - ) - - preference_id = response.json()["preference_id"] - return preference_id - - def _prepare_joint_dataset( - self, - dataset: Dict[Union[str, LLMConfig], pd.DataFrame], - prompt_column: str, - response_column: str, - score_column: str, - ) -> Tuple[pd.DataFrame, List[LLMConfig]]: - a_provider = list(dataset.keys())[0] - prompts = dataset[a_provider].get(prompt_column, None) - if prompts is None: - raise ValueError(f"Prompt column {prompt_column} not found in df.") - prompts = prompts.to_list() - - llm_configs = [] - joint_dataset = {prompt_column: prompts} - for provider, df in dataset.items(): - llm_configs.append(provider) - - responses = df.get(response_column, None) - if responses is None: - raise ValueError( - f"Response column {response_column} not found in df." - ) - responses = responses.to_list() - joint_dataset[f"{str(provider)}/response"] = responses - - scores = df.get(score_column, None) - if scores is None: - raise ValueError( - f"Score column {score_column} not found in df." - ) - scores = scores.to_list() - joint_dataset[f"{str(provider)}/score"] = scores - - joint_df = pd.DataFrame(joint_dataset) - - llm_configs = NotDiamond._parse_llm_configs_data(llm_configs) - return joint_df, llm_configs - -
-[docs] - def fit( - self, - dataset: Dict[Union[str, LLMConfig], pd.DataFrame], - prompt_column: str, - response_column: str, - score_column: str, - preference_id: Optional[str] = None, - nd_api_url: Optional[str] = NOTDIAMOND_API_URL, - ) -> str: - """ - Method to train a custom router using provided dataset. - - Parameters: - dataset (Dict[str, pandas.DataFrame]): The dataset to train a custom router. - Each key in the dictionary should be in the form of <provider>/<model>. - prompt_column (str): The column name in each DataFrame corresponding - to the prompts used to evaluate the LLM. - response_column (str): The column name in each DataFrame corresponding - to the response given by the LLM for a given prompt. - score_column (str): The column name in each DataFrame corresponding - to the score given to the response from the LLM. - preference_id (Optional[str], optional): If specified, the custom router - associated with the preference_id will be updated with the provided dataset. - nd_api_url (Optional[str], optional): The URL of the NotDiamond API. Defaults to prod. - - Raises: - ApiError: When the NotDiamond API fails - ValueError: When parsing the provided dataset fails - UnsupportedLLMProvider: When a provider specified in the dataset is not supported. - - Returns: - str: - preference_id: the preference_id associated with the custom router. - Use this preference_id in your routing calls to use the custom router. - """ - - joint_df, llm_configs = self._prepare_joint_dataset( - dataset, prompt_column, response_column, score_column - ) - - with tempfile.NamedTemporaryFile(suffix=".csv") as joint_csv: - joint_df.to_csv(joint_csv.name, index=False) - preference_id = self._request_train_router( - prompt_column, - joint_csv.name, - llm_configs, - preference_id, - nd_api_url, - ) - - return preference_id
- - - def _get_latency(self, llm_config: LLMConfig, prompt: str) -> float: - llm = NotDiamond._llm_from_config(llm_config) - start_time = time.time() - _ = llm.invoke([("human", prompt)]) - end_time = time.time() - return (end_time - start_time) * 1000 # ms - - def _get_cost( - self, llm_config: LLMConfig, prompt: str, response: str - ) -> float: - n_input_tokens = token_counter(model="gpt-4o", text=prompt) - n_output_tokens = token_counter(model="gpt-4o", text=response) - input_price = ( - llm_config.default_input_price - if llm_config.input_price is None - else llm_config.input_price - ) - output_price = ( - llm_config.default_output_price - if llm_config.output_price is None - else llm_config.output_price - ) - return ( - n_input_tokens * input_price + n_output_tokens * output_price - ) / 1e6 - - def _eval_custom_router( - self, - client: NotDiamond, - llm_configs: List[LLMConfig], - joint_df: pd.DataFrame, - prompt_column: str, - include_latency: bool, - ) -> Tuple[pd.DataFrame, pd.DataFrame]: - eval_results = OrderedDict() - eval_results[prompt_column] = [] - eval_results["session_id"] = [] - eval_results["notdiamond/score"] = [] - eval_results["notdiamond/cost"] = [] - eval_results["notdiamond/response"] = [] - eval_results["notdiamond/recommended_provider"] = [] - - if include_latency: - eval_results["notdiamond/latency"] = [] - - for provider in llm_configs: - provider_score_column = ( - f"{provider.provider}/{provider.model}/score" - ) - eval_results[provider_score_column] = [] - - provider_response_column = ( - f"{provider.provider}/{provider.model}/response" - ) - eval_results[provider_response_column] = [] - - provider_cost_column = f"{provider.provider}/{provider.model}/cost" - eval_results[provider_cost_column] = [] - - if include_latency: - provider_latency_column = ( - f"{provider.provider}/{provider.model}/latency" - ) - eval_results[provider_latency_column] = [] - - for _, row in tqdm(joint_df.iterrows(), total=len(joint_df)): - prompt = row[prompt_column] - eval_results[prompt_column].append(prompt) - - session_id, nd_provider = client.chat.completions.model_select( - messages=[{"role": "user", "content": prompt}], timeout=60 - ) - if nd_provider is None: - continue - - eval_results["session_id"].append(session_id) - - provider_matched = False - for provider in llm_configs: - provider_score = row[ - f"{provider.provider}/{provider.model}/score" - ] - eval_results[ - f"{provider.provider}/{provider.model}/score" - ].append(provider_score) - - provider_response = row[ - f"{provider.provider}/{provider.model}/response" - ] - eval_results[ - f"{provider.provider}/{provider.model}/response" - ].append(provider_response) - - provider_cost = self._get_cost( - provider, prompt, provider_response - ) - eval_results[ - f"{provider.provider}/{provider.model}/cost" - ].append(provider_cost) - - if include_latency: - provider_latency = self._get_latency(provider, prompt) - eval_results[ - f"{provider.provider}/{provider.model}/latency" - ].append(provider_latency) - - if ( - not provider_matched - and provider.provider == nd_provider.provider - and provider.model == nd_provider.model - ): - provider_matched = True - eval_results["notdiamond/score"].append(provider_score) - eval_results["notdiamond/cost"].append(provider_cost) - eval_results["notdiamond/response"].append( - provider_response - ) - eval_results["notdiamond/recommended_provider"].append( - f"{nd_provider.provider}/{nd_provider.model}" - ) - if include_latency: - eval_results["notdiamond/latency"].append( - provider_latency - ) - - if not provider_matched: - raise ValueError( - f""" - Custom router returned {nd_provider.provider}/{nd_provider.model} - which is not in the set of models in the test dataset - """ - ) - - eval_results_df = pd.DataFrame(eval_results) - - eval_stats = OrderedDict() - best_average_provider = None - best_average_score = -(2 * int(self.maximize) - 1) * np.inf - - nd_average_score = eval_results_df["notdiamond/score"].mean() - eval_stats["Not Diamond Average Score"] = [nd_average_score] - - nd_average_cost = eval_results_df["notdiamond/cost"].mean() - eval_stats["Not Diamond Average Cost"] = [nd_average_cost] - - if include_latency: - nd_average_latency = eval_results_df["notdiamond/latency"].mean() - eval_stats["Not Diamond Average Latency"] = [nd_average_latency] - - for provider in llm_configs: - provider_avg_score = eval_results_df[ - f"{provider.provider}/{provider.model}/score" - ].mean() - eval_stats[f"{provider.provider}/{provider.model}/avg_score"] = [ - provider_avg_score - ] - - provider_avg_cost = eval_results_df[ - f"{provider.provider}/{provider.model}/cost" - ].mean() - eval_stats[f"{provider.provider}/{provider.model}/avg_cost"] = [ - provider_avg_cost - ] - - if include_latency: - provider_avg_latency = eval_results_df[ - f"{provider.provider}/{provider.model}/latency" - ].mean() - eval_stats[ - f"{provider.provider}/{provider.model}/avg_latency" - ] = [provider_avg_latency] - - if self.maximize: - if provider_avg_score > best_average_score: - best_average_score = provider_avg_score - best_average_cost = provider_avg_cost - best_average_provider = ( - f"{provider.provider}/{provider.model}" - ) - if include_latency: - best_average_latency = provider_avg_latency - else: - if provider_avg_score < best_average_score: - best_average_score = provider_avg_score - best_average_cost = provider_avg_cost - best_average_provider = ( - f"{provider.provider}/{provider.model}" - ) - if include_latency: - best_average_latency = provider_avg_latency - - eval_stats["Best Average Provider"] = [best_average_provider] - eval_stats["Best Provider Average Score"] = [best_average_score] - eval_stats["Best Provider Average Cost"] = [best_average_cost] - - if include_latency: - eval_stats["Best Provider Average Latency"] = [ - best_average_latency - ] - - first_columns = [ - "Best Average Provider", - "Best Provider Average Score", - "Best Provider Average Cost", - "Best Provider Average Latency", - "Not Diamond Average Score", - "Not Diamond Average Cost", - "Not Diamond Average Latency", - ] - else: - first_columns = [ - "Best Average Provider", - "Best Provider Average Score", - "Best Provider Average Cost", - "Not Diamond Average Score", - "Not Diamond Average Cost", - ] - - column_order = first_columns + [ - col for col in eval_stats.keys() if col not in first_columns - ] - ordered_eval_stats = OrderedDict() - for col in column_order: - ordered_eval_stats[col] = eval_stats[col] - - eval_stats_df = pd.DataFrame(ordered_eval_stats) - return eval_results_df, eval_stats_df - -
-[docs] - def eval( - self, - dataset: Dict[Union[str, LLMConfig], pd.DataFrame], - prompt_column: str, - response_column: str, - score_column: str, - preference_id: str, - include_latency: bool = False, - ) -> Tuple[pd.DataFrame, pd.DataFrame]: - """ - Method to evaluate a custom router using provided dataset. - - Parameters: - dataset (Dict[str, pandas.DataFrame]): The dataset to train a custom router. - Each key in the dictionary should be in the form of <provider>/<model>. - prompt_column (str): The column name in each DataFrame corresponding - to the prompts used to evaluate the LLM. - response_column (str): The column name in each DataFrame corresponding - to the response given by the LLM for a given prompt. - score_column (str): The column name in each DataFrame corresponding - to the score given to the response from the LLM. - preference_id (str): The preference_id associated with the custom router - returned from .fit(). - - Raises: - ApiError: When the NotDiamond API fails - ValueError: When parsing the provided dataset fails - UnsupportedLLMProvider: When a provider specified in the dataset is not supported. - - Returns: - Tuple[pandas.DataFrame, pandas.DataFrame]: - eval_results_df: A DataFrame containing all the prompts, responses of each provider - (indicated by column <provider>/<model>/response), scores of each provider - (indicated by column <provider>/<model>/score), and notdiamond custom router - response and score (indicated by column notdiamond/response and notdiamond/score). - eval_stats_df: A DataFrame containing the "Best Average Provider" computed from the - provided dataset, the "Best Provider Average Score" achieved by the "Best Average Provider", - and the "Not Diamond Average Score" achieved through custom router. - """ - - joint_df, llm_configs = self._prepare_joint_dataset( - dataset, prompt_column, response_column, score_column - ) - - client = NotDiamond( - llm_configs=llm_configs, - api_key=self.api_key, - preference_id=preference_id, - ) - - eval_results_df, eval_stats_df = self._eval_custom_router( - client, llm_configs, joint_df, prompt_column, include_latency - ) - return eval_results_df, eval_stats_df
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/toolkit/langchain.html b/docs/_build/html/_modules/notdiamond/toolkit/langchain.html deleted file mode 100644 index bde1235d..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/langchain.html +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - notdiamond.toolkit.langchain — NotDiamond 0.3.25 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.toolkit.langchain

-import os
-from importlib import metadata
-from typing import (
-    Any,
-    AsyncIterator,
-    Dict,
-    Iterator,
-    List,
-    Optional,
-    Sequence,
-    Union,
-)
-
-from langchain.chat_models.base import init_chat_model
-from langchain_community.adapters.openai import convert_message_to_dict
-from langchain_core.language_models import LanguageModelInput
-from langchain_core.messages.utils import convert_to_messages
-from langchain_core.prompt_values import (
-    ChatPromptValue,
-    PromptValue,
-    StringPromptValue,
-)
-from langchain_core.runnables import Runnable, RunnableConfig
-
-import notdiamond as nd
-
-_LANGCHAIN_PROVIDERS = {
-    "openai",
-    "anthropic",
-    "google",
-    "mistral",
-    "togetherai",
-    "cohere",
-}
-
-
-
-[docs] -class NotDiamondRunnable(Runnable[LanguageModelInput, str]): - """ - See Runnable docs for details - https://python.langchain.com/v0.1/docs/expression_language/interface/ - """ - - llm_configs: List - api_key: Optional[str] = os.getenv("NOTDIAMOND_API_KEY") - client: Any - -
-[docs] - def __init__( - self, - nd_llm_configs: Optional[List] = None, - nd_api_key: Optional[str] = None, - nd_client: Optional[Any] = None, - nd_kwargs: Optional[Dict[str, Any]] = None, - ): - """ - Params: - nd_llm_configs: List of LLM configs to use. - nd_api_key: Not Diamond API key. - nd_client: Not Diamond client. - nd_kwargs: Keyword arguments to pass directly to model_select. - """ - if not nd_client: - if not nd_api_key or not nd_llm_configs: - raise ValueError( - "Must provide either client or api_key and llm_configs to " - "instantiate NotDiamondRunnable." - ) - nd_client = nd.NotDiamond( - llm_configs=nd_llm_configs, - api_key=nd_api_key, - ) - elif nd_client.llm_configs: - for llm_config in nd_client.llm_configs: - if isinstance(llm_config, str): - llm_config = nd.LLMConfig.from_string(llm_config) - if llm_config.provider not in _LANGCHAIN_PROVIDERS: - raise ValueError( - f"Requested provider in {llm_config} supported by Not Diamond " - "but not langchain.chat_models.base.init_chat_model. Please " - "remove it from your llm_configs." - ) - - try: - nd_client.user_agent = ( - f"langchain-community/{metadata.version('notdiamond')}" - ) - except AttributeError: - pass - - self.client = nd_client - self.api_key = nd_client.api_key - self.llm_configs = nd_client.llm_configs - self.nd_kwargs = nd_kwargs or dict()
- - - def _model_select(self, input: LanguageModelInput) -> str: - messages = _convert_input_to_message_dicts(input) - _, provider = self.client.chat.completions.model_select( - messages=messages, **self.nd_kwargs - ) - provider_str = _nd_provider_to_langchain_provider(str(provider)) - return provider_str - - async def _amodel_select(self, input: LanguageModelInput) -> str: - messages = _convert_input_to_message_dicts(input) - _, provider = await self.client.chat.completions.amodel_select( - messages=messages, **self.nd_kwargs - ) - provider_str = _nd_provider_to_langchain_provider(str(provider)) - return provider_str - -
-[docs] - def stream( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> Iterator[str]: - yield self._model_select(input)
- - -
-[docs] - def invoke( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - ) -> str: - return self._model_select(input)
- - -
-[docs] - def batch( - self, - inputs: List[LanguageModelInput], - config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None, - **kwargs: Optional[Any], - ) -> List[str]: - return [self._model_select(input) for input in inputs]
- - -
-[docs] - async def astream( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> AsyncIterator[str]: - yield await self._amodel_select(input)
- - -
-[docs] - async def ainvoke( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> str: - return await self._amodel_select(input)
- - -
-[docs] - async def abatch( - self, - inputs: List[LanguageModelInput], - config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None, - **kwargs: Optional[Any], - ) -> List[str]: - return [await self._amodel_select(input) for input in inputs]
-
- - - -
-[docs] -class NotDiamondRoutedRunnable(Runnable[LanguageModelInput, Any]): -
-[docs] - def __init__( - self, - *args: Any, - configurable_fields: Optional[List[str]] = None, - nd_llm_configs: Optional[List] = None, - nd_api_key: Optional[str] = None, - nd_client: Optional[Any] = None, - nd_kwargs: Optional[Dict[str, Any]] = None, - **kwargs: Optional[Dict[Any, Any]], - ) -> None: - """ - Params: - nd_llm_configs: List of LLM configs to use. - nd_api_key: Not Diamond API key. - nd_client: Not Diamond client. - nd_kwargs: Keyword arguments to pass directly to model_select. - """ - _nd_kwargs = { - kw: kwargs[kw] for kw in kwargs.keys() if kw.startswith("nd_") - } - if nd_kwargs: - _nd_kwargs.update(nd_kwargs) - - self._ndrunnable = NotDiamondRunnable( - nd_api_key=nd_api_key, - nd_llm_configs=nd_llm_configs, - nd_client=nd_client, - nd_kwargs=_nd_kwargs, - ) - _routed_fields = ["model", "model_provider"] - if configurable_fields is None: - configurable_fields = [] - self._configurable_fields = _routed_fields + configurable_fields - self._configurable_model = init_chat_model( - *args, - configurable_fields=self._configurable_fields, - config_prefix="nd", - **{kw: kwv for kw, kwv in kwargs.items() if kw not in _nd_kwargs}, # type: ignore[arg-type] - )
- - -
-[docs] - def stream( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> Iterator[Any]: - provider_str = self._ndrunnable._model_select(input) - _config = self._build_model_config(provider_str, config) - yield from self._configurable_model.stream(input, config=_config)
- - -
-[docs] - def invoke( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> Any: - provider_str = self._ndrunnable._model_select(input) - _config = self._build_model_config(provider_str, config) - return self._configurable_model.invoke(input, config=_config)
- - -
-[docs] - def batch( - self, - inputs: List[LanguageModelInput], - config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None, - **kwargs: Optional[Any], - ) -> List[Any]: - config = config or {} - - provider_strs = [ - self._ndrunnable._model_select(input) for input in inputs - ] - if isinstance(config, dict): - _configs = [ - self._build_model_config(ps, config) for ps in provider_strs - ] - else: - _configs = [ - self._build_model_config(ps, config[i]) - for i, ps in enumerate(provider_strs) - ] - - return self._configurable_model.batch(inputs, config=_configs)
- - -
-[docs] - async def astream( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> AsyncIterator[Any]: - provider_str = await self._ndrunnable._amodel_select(input) - _config = self._build_model_config(provider_str, config) - async for chunk in self._configurable_model.astream( - input, config=_config - ): - yield chunk
- - -
-[docs] - async def ainvoke( - self, - input: LanguageModelInput, - config: Optional[RunnableConfig] = None, - **kwargs: Optional[Any], - ) -> Any: - provider_str = await self._ndrunnable._amodel_select(input) - _config = self._build_model_config(provider_str, config) - return await self._configurable_model.ainvoke(input, config=_config)
- - -
-[docs] - async def abatch( - self, - inputs: List[LanguageModelInput], - config: Optional[Union[RunnableConfig, List[RunnableConfig]]] = None, - **kwargs: Optional[Any], - ) -> List[Any]: - config = config or {} - - provider_strs = [ - await self._ndrunnable._amodel_select(input) for input in inputs - ] - if isinstance(config, dict): - _configs = [ - self._build_model_config(ps, config) for ps in provider_strs - ] - else: - _configs = [ - self._build_model_config(ps, config[i]) - for i, ps in enumerate(provider_strs) - ] - - return await self._configurable_model.abatch(inputs, config=_configs)
- - -
-[docs] - def _build_model_config( - self, provider_str: str, config: Optional[RunnableConfig] = None - ) -> RunnableConfig: - """ - Provider string should take the form '{model}/{model_provider}' - """ - config = config or RunnableConfig() - - model_provider, model = provider_str.split("/") - _config = RunnableConfig( - configurable={ - "nd_model": model, - "nd_model_provider": model_provider, - }, - ) - - for k, v in config.items(): - _config["configurable"][f"nd_{k}"] = v - return _config
-
- - - -def _convert_input_to_message_dicts( - input: LanguageModelInput, -) -> List[Dict[str, str]]: - if isinstance(input, PromptValue): - output = input - elif isinstance(input, str): - output = StringPromptValue(text=input) - elif isinstance(input, Sequence): - output = ChatPromptValue(messages=convert_to_messages(input)) - else: - raise ValueError( - f"Invalid input type {type(input)}. " - "Must be a PromptValue, str, or list of BaseMessages." - ) - return [ - convert_message_to_dict(message) for message in output.to_messages() - ] - - -def _nd_provider_to_langchain_provider(llm_config_str: str) -> str: - provider, model = llm_config_str.split("/") - provider = ( - provider.replace("google", "google_genai") - .replace("mistral", "mistralai") - .replace("togetherai", "together") - ) - return f"{provider}/{model}" -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/toolkit/openai.html b/docs/_build/html/_modules/notdiamond/toolkit/openai.html deleted file mode 100644 index 777b32cd..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/openai.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - notdiamond.toolkit.openai — NotDiamond 0.3.38 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.toolkit.openai

-"""
-Tools for working directly with OpenAI's various models.
-"""
-import logging
-from typing import List, Union
-
-from notdiamond import NotDiamond
-from notdiamond.llms.providers import NDLLMProviders
-from notdiamond.settings import NOTDIAMOND_API_KEY, OPENAI_API_KEY
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-_ND_PARAMS = [
-    "llm_configs",
-    "default",
-    "max_model_depth",
-    "latency_tracking",
-    "hash_content",
-    "tradeoff",
-    "preference_id",
-    "tools",
-    "callbacks",
-    "nd_api_url",
-    "nd_api_key",
-    "user_agent",
-]
-_SHARED_PARAMS = ["timeout", "max_retries"]
-
-
-
-[docs] -class _OpenAIBase: - """ - Base class which wraps both an openai client and Not Diamond retry / fallback logic. - """ - - def __init__(self, oai_client_cls, *args, **kwargs): - nd_kwargs = { - k: v for k, v in kwargs.items() if k in _ND_PARAMS + _SHARED_PARAMS - } - - # TODO [a9] remove llm_configs as valid constructor arg for ND client - self._nd_client = NotDiamond( - api_key=nd_kwargs.get("nd_api_key", NOTDIAMOND_API_KEY), - llm_configs=["openai/gpt-3.5-turbo"], - *args, - **nd_kwargs, - ) - - # Create a OpenAI client with a dummy model - will ignore this during routing - oai_kwargs = {k: v for k, v in kwargs.items() if k not in _ND_PARAMS} - self._oai_client = oai_client_cls( - *args, api_key=OPENAI_API_KEY, **oai_kwargs - ) - - def __getattr__(self, name): - return getattr(self._oai_client, name) - - def __call__(self, *args, **kwargs): - return self._oai_client(*args, **kwargs) - - def __dir__(self): - return dir(self._oai_client) - - @property - def chat(self): - class ChatCompletions: - def __init__(self, parent): - self.parent = parent - - @property - def completions(self): - return self - - def create(self, *args, **kwargs): - return self.parent.create(*args, **kwargs) - - return ChatCompletions(self) - - def _create_prep(self, model: Union[str, List], **kwargs): - model = kwargs.get("model", model) - - if model is None: - LOGGER.info( - "No LLM configs provided. Not Diamond will route to all OpenAI models." - ) - llm_configs = [ - str(p) for p in NDLLMProviders if p.provider == "openai" - ] - elif isinstance(model, str): - llm_configs = model.split(",") - elif isinstance(model, list): - llm_configs = self._nd_client._parse_llm_configs_data(model) - - if "messages" not in kwargs: - raise ValueError("'messages' argument is required") - - return llm_configs
- - - -
-[docs] -class OpenAI(_OpenAIBase): - """ - Encapsulating class for an openai.OpenAI client. This supports the same methods as - the openai package, while also supporting routed prompts with calls to `completion`. - """ - - def __init__(self, *args, **kwargs): - from openai import OpenAI as OpenAIClient - - super().__init__(OpenAIClient, *args, **kwargs) - -
-[docs] - def create(self, *args, model: Union[str, List] = None, **kwargs): - """ - Perform chat completion using OpenAI's API, after routing the prompt to a - specific LLM via Not Diamond. - """ - llm_configs = self._create_prep(model, **kwargs) - session_id, best_llm = self._nd_client.model_select( - *args, model=llm_configs, **kwargs - ) - response = self._oai_client.chat.completions.create( - *args, model=str(best_llm.model), **kwargs - ) - LOGGER.info(f"Routed prompt to {best_llm} for session ID {session_id}") - return response
-
- - - -
-[docs] -class AsyncOpenAI(_OpenAIBase): - """ - Encapsulating class for an openai.OpenAI client. This supports the same methods as - the openai package, while also supporting routed prompts with calls to `completion`. - """ - - def __init__(self, *args, **kwargs): - from openai import AsyncOpenAI as OpenAIClient - - super().__init__(OpenAIClient, *args, **kwargs) - -
-[docs] - async def create(self, *args, model: Union[str, List] = None, **kwargs): - """ - Perform async chat completion using OpenAI's API, after routing the prompt to a - specific LLM via Not Diamond. - """ - llm_configs = self._create_prep(model, **kwargs) - session_id, best_llm = await self._nd_client.amodel_select( - *args, model=llm_configs, **kwargs - ) - response = await self._oai_client.chat.completions.create( - *args, model=str(best_llm.model), **kwargs - ) - LOGGER.debug( - f"Routed prompt to {best_llm} for session ID {session_id}" - ) - return response
-
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation.html b/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation.html deleted file mode 100644 index ec664b03..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - notdiamond.toolkit.rag.evaluation — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for notdiamond.toolkit.rag.evaluation

-import logging
-from typing import Dict, List, Optional, Sequence, Tuple, Union
-
-import optuna
-import pandas as pd
-from langchain_core.callbacks import Callbacks
-from langchain_core.embeddings import Embeddings as LangchainEmbeddings
-from langchain_core.language_models import BaseLanguageModel as LangchainLLM
-from langchain_core.prompt_values import StringPromptValue
-from ragas import EvaluationDataset, SingleTurnSample
-from ragas._analytics import track_was_completed
-from ragas.cost import TokenUsageParser
-from ragas.embeddings.base import BaseRagasEmbeddings
-from ragas.evaluation import evaluate as ragas_evaluate
-from ragas.llms import BaseRagasLLM
-from ragas.metrics.base import Metric
-from ragas.run_config import RunConfig
-from tqdm import tqdm
-
-from notdiamond.llms.config import LLMConfig
-from notdiamond.toolkit.rag.evaluation_dataset import (
-    RAGEvaluationDataset,
-    RAGSample,
-)
-from notdiamond.toolkit.rag.llms import get_llm
-from notdiamond.toolkit.rag.workflow import BaseNDRagWorkflow
-
-LOGGER = logging.getLogger(__name__)
-LOGGER.setLevel(logging.INFO)
-
-_DEFAULT_GENERATION_TEMPERATURE = 0.7
-
-
-
-[docs] -def get_eval_dataset( - test_queries: pd.DataFrame, - workflow: BaseNDRagWorkflow, - generation_prompt: str = None, - generator_llm: Union[LLMConfig, str] = None, -): - """ - Create a dataset of RAGSample objects to evaluate the performance of a RAG workflow. - - Args: - test_queries: A pandas DataFrame with schema implied by the method below. - workflow: BaseNDRagWorkflow subclass created by the user. - - Schema for test_queries can be found below - https://docs.ragas.io/en/stable/references/evaluation_schema/#ragas.dataset_schema.SingleTurnSample - """ - samples = [] - for _, row in test_queries.iterrows(): - query = row["user_input"] - reference = row["reference"] - generation_prompt = generation_prompt or row.get("generation_prompt") - if generation_prompt is None: - raise ValueError( - "Provided test queries DataFrame does not include" - " 'generation_prompt' and 'generation_prompt' is not provided." - ) - generator_llm = generator_llm or row.get("generator_llm") - if generator_llm is None: - raise ValueError( - "Provided test queries DataFrame does not include" - " 'generator_llm' and 'generator_llm' is not provided." - ) - - retrieved_contexts = workflow.get_retrieved_context(query) - response = workflow.get_response(query) - - sample = RAGSample( - user_input=query, - retrieved_contexts=retrieved_contexts, - response=response, - reference=reference, - generation_prompt=generation_prompt, - generator_llm=generator_llm, - ) - samples.append(sample) - eval_dataset = RAGEvaluationDataset(samples) - return eval_dataset
- - - -
-[docs] -def auto_optimize(workflow: BaseNDRagWorkflow, n_trials: int): - direction = "maximize" if workflow.objective_maximize else "minimize" - study = optuna.create_study( - study_name=workflow.job_name, direction=direction - ) - study.optimize(workflow._outer_objective, n_trials=n_trials) - workflow._set_param_values(study.best_params) - return {"best_params": study.best_params, "trials": study.trials}
- - - -def _map_to_ragas_samples( - dataset: RAGEvaluationDataset, -) -> Tuple[EvaluationDataset, pd.DataFrame]: - ragas_samples = [] - extra_columns = { - "generation_prompt": [], - } - for sample in dataset: - ragas_sample = SingleTurnSample( - user_input=sample.user_input, - retrieved_contexts=sample.retrieved_contexts, - reference_contexts=sample.reference_contexts, - response=sample.response, - multi_responses=sample.multi_responses, - reference=sample.reference, - rubrics=sample.rubrics, - ) - ragas_samples.append(ragas_sample) - extra_columns["generation_prompt"].append(sample.generation_prompt) - - extra_columns_df = pd.DataFrame.from_dict(extra_columns) - return EvaluationDataset(ragas_samples), extra_columns_df - - -def _evaluate_dataset( - generator_llm: LLMConfig, - dataset: EvaluationDataset, - metrics: Optional[Sequence[Metric]] = None, - llm: Optional[Union[BaseRagasLLM, LangchainLLM]] = None, - embeddings: Optional[ - Union[BaseRagasEmbeddings, LangchainEmbeddings] - ] = None, - callbacks: Callbacks = None, - in_ci: bool = False, - run_config: RunConfig = RunConfig(), - token_usage_parser: Optional[TokenUsageParser] = None, - raise_exceptions: bool = False, - column_map: Optional[Dict[str, str]] = None, - show_progress: bool = True, - batch_size: Optional[int] = None, -) -> pd.DataFrame: - LOGGER.info(f"Evaluating generations from {str(generator_llm)}") - - result = ragas_evaluate( - dataset, - metrics, - llm, - embeddings, - callbacks, - in_ci, - run_config, - token_usage_parser, - raise_exceptions, - column_map, - show_progress, - batch_size, - ) - return result.to_pandas() - - -def _generate_rag_eval_dataset( - generator_llm: LLMConfig, - dataset: RAGEvaluationDataset, - temperature: float = _DEFAULT_GENERATION_TEMPERATURE, -) -> RAGEvaluationDataset: - LOGGER.info(f"Generating responses from {str(generator_llm)}") - - llm = get_llm(generator_llm) - temperature = generator_llm.kwargs.get("temperature", temperature) - - eval_samples = [] - for sample in tqdm(dataset): - response = llm.generate_text( - StringPromptValue(text=sample.generation_prompt), - temperature=temperature, - ) - eval_sample = RAGSample( - user_input=sample.user_input, - retrieved_contexts=sample.retrieved_contexts, - reference_contexts=sample.reference_contexts, - response=response.generations[0][0].text, - multi_responses=sample.multi_responses, - reference=sample.reference, - rubrics=sample.rubrics, - generation_prompt=sample.generation_prompt, - generator_llm=str(generator_llm), - ) - eval_samples.append(eval_sample) - return RAGEvaluationDataset(eval_samples) - - -
-[docs] -@track_was_completed -def evaluate( - dataset: RAGEvaluationDataset, - metrics: Optional[Sequence[Metric]] = None, - llm: Optional[Union[BaseRagasLLM, LangchainLLM]] = None, - embeddings: Optional[ - Union[BaseRagasEmbeddings, LangchainEmbeddings] - ] = None, - callbacks: Callbacks = None, - in_ci: bool = False, - run_config: RunConfig = RunConfig(), - token_usage_parser: Optional[TokenUsageParser] = None, - raise_exceptions: bool = False, - column_map: Optional[Dict[str, str]] = None, - show_progress: bool = True, - batch_size: Optional[int] = None, - generator_llms: List[LLMConfig] = [], -) -> Dict[str, pd.DataFrame]: - dataset_llm_str = dataset[0].generator_llm - dataset_llm_config = LLMConfig.from_string(dataset_llm_str) - - if dataset_llm_config not in generator_llms: - generator_llms.append(dataset_llm_config) - - ragas_dataset, extra_columns = _map_to_ragas_samples(dataset) - - dataset_results = _evaluate_dataset( - dataset_llm_config, - ragas_dataset, - metrics, - llm, - embeddings, - callbacks, - in_ci, - run_config, - token_usage_parser, - raise_exceptions, - column_map, - show_progress, - batch_size, - ) - - evaluation_results = { - str(dataset_llm_config): pd.concat( - [dataset_results, extra_columns], axis=1 - ) - } - - for llm_config in generator_llms: - if str(llm_config) in evaluation_results: - continue - - llm_dataset = _generate_rag_eval_dataset(llm_config, dataset) - ragas_dataset, extra_columns = _map_to_ragas_samples(llm_dataset) - dataset_results = _evaluate_dataset( - llm_config, - ragas_dataset, - metrics, - llm, - embeddings, - callbacks, - in_ci, - run_config, - token_usage_parser, - raise_exceptions, - column_map, - show_progress, - batch_size, - ) - evaluation_results[str(llm_config)] = pd.concat( - [dataset_results, extra_columns], axis=1 - ) - return evaluation_results
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation_dataset.html b/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation_dataset.html deleted file mode 100644 index 0d1c9d4f..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/rag/evaluation_dataset.html +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - notdiamond.toolkit.rag.evaluation_dataset — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for notdiamond.toolkit.rag.evaluation_dataset

-from dataclasses import dataclass
-from typing import Dict, List, Union, overload
-
-from ragas import MultiTurnSample, SingleTurnSample
-from ragas.dataset_schema import RagasDataset
-
-
-
-[docs] -class RAGSample(SingleTurnSample): - """ - Represents RAG evaluation samples. - - Attributes: - user_input (Optional[str]): The input query from the user. - retrieved_contexts (Optional[List[str]]): List of contexts retrieved for the query. - reference_contexts (Optional[List[str]]): List of reference contexts for the query. - response (Optional[str]): The generated response for the query. - generation_prompt (str): The input prompt to the generator LLM. - generator_llm (str): The LLM used to generate the response. - multi_responses (Optional[List[str]]): List of multiple responses generated for the query. - reference (Optional[str]): The reference answer for the query. - rubric (Optional[Dict[str, str]]): Evaluation rubric for the sample. - """ - - generation_prompt: str - generator_llm: str
- - - -
-[docs] -@dataclass -class RAGEvaluationDataset(RagasDataset[RAGSample]): - """ - Represents a dataset of RAG evaluation samples. - - Attributes: - samples (List[BaseSample]): A list of evaluation samples. - - Methods: - validate_samples(samples): Validates that all samples are of the same type. - get_sample_type(): Returns the type of the samples in the dataset. - to_hf_dataset(): Converts the dataset to a Hugging Face Dataset. - to_pandas(): Converts the dataset to a pandas DataFrame. - features(): Returns the features of the samples. - from_list(mapping): Creates an EvaluationDataset from a list of dictionaries. - from_dict(mapping): Creates an EvaluationDataset from a dictionary. - to_csv(path): Converts the dataset to a CSV file. - to_jsonl(path): Converts the dataset to a JSONL file. - from_jsonl(path): Creates an EvaluationDataset from a JSONL file. - """ - - @overload - def __getitem__(self, idx: int) -> RAGSample: - ... - - @overload - def __getitem__(self, idx: slice) -> "RAGEvaluationDataset": - ... - - def __getitem__( - self, idx: Union[int, slice] - ) -> Union[RAGSample, "RAGEvaluationDataset"]: - if isinstance(idx, int): - return self.samples[idx] - elif isinstance(idx, slice): - return type(self)(samples=self.samples[idx]) - else: - raise TypeError("Index must be int or slice") - -
-[docs] - def is_multi_turn(self) -> bool: - return False
- - -
-[docs] - def to_list(self) -> List[Dict]: - rows = [sample.to_dict() for sample in self.samples] - return rows
- - -
-[docs] - @classmethod - def from_list(cls, data: List[Dict]): - samples = [] - if all( - "user_input" in item and isinstance(data[0]["user_input"], list) - for item in data - ): - samples.extend(MultiTurnSample(**sample) for sample in data) - else: - samples.extend(SingleTurnSample(**sample) for sample in data) - return cls(samples=samples)
- - - def __repr__(self) -> str: - return f"RAGEvaluationDataset(features={self.features()}, len={len(self.samples)})"
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/toolkit/rag/llms.html b/docs/_build/html/_modules/notdiamond/toolkit/rag/llms.html deleted file mode 100644 index df3e0938..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/rag/llms.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - - notdiamond.toolkit.rag.llms — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.toolkit.rag.llms

-from typing import Union
-
-from langchain_cohere import CohereEmbeddings
-from langchain_mistralai import MistralAIEmbeddings
-from langchain_openai import OpenAIEmbeddings
-from ragas.embeddings import HuggingfaceEmbeddings, LangchainEmbeddingsWrapper
-from ragas.llms import LangchainLLMWrapper
-
-from ...exceptions import UnsupportedEmbeddingProvider
-from ...llms.client import NotDiamond
-from ...llms.config import EmbeddingConfig, LLMConfig
-
-
-
-[docs] -def get_llm(llm_config_or_str: Union[LLMConfig, str]) -> LangchainLLMWrapper: - """ - Build the LLM object compatible with evaluation metrics. - - Parameters: - llm_config_or_str (Union[LLMConfig, str]): a LLMConfig object or a model string - that specifies the LLM to construct. - """ - if isinstance(llm_config_or_str, str): - llm_config = LLMConfig.from_string(llm_config_or_str) - else: - llm_config = llm_config_or_str - - lc_llm = NotDiamond._llm_from_config(llm_config) - return LangchainLLMWrapper(lc_llm)
- - - -
-[docs] -def get_embedding( - embedding_model_config_or_str: Union[EmbeddingConfig, str] -) -> Union[LangchainEmbeddingsWrapper, HuggingfaceEmbeddings]: - """ - Build the embedding model object compatible with evaluation metrics. - - Parameters: - embedding_model_config_or_str (Union[EmbeddingConfig, str]): an EmbeddingConfig object - or an embedding model string that specifies the embedding model to construct. - """ - if isinstance(embedding_model_config_or_str, str): - embedding_config = EmbeddingConfig.from_string( - embedding_model_config_or_str - ) - else: - embedding_config = embedding_model_config_or_str - - if embedding_config.provider == "openai": - lc_embedding = OpenAIEmbeddings( - model=embedding_config.model, **embedding_config.kwargs - ) - - elif embedding_config.provider == "cohere": - lc_embedding = CohereEmbeddings( - model=embedding_config.model, **embedding_config.kwargs - ) - - elif embedding_config.provider == "mistral": - lc_embedding = MistralAIEmbeddings( - model=embedding_config.model, **embedding_config.kwargs - ) - - elif embedding_config.provider == "huggingface": - return HuggingfaceEmbeddings(model_name=embedding_config.model) - - else: - raise UnsupportedEmbeddingProvider( - f"Embedding model {str(embedding_config)} not supported." - ) - return LangchainEmbeddingsWrapper(lc_embedding)
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/toolkit/rag/testset.html b/docs/_build/html/_modules/notdiamond/toolkit/rag/testset.html deleted file mode 100644 index bd6df292..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/rag/testset.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - - - notdiamond.toolkit.rag.testset — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for notdiamond.toolkit.rag.testset

-from typing import List, Optional, Sequence, Union
-
-import pandas as pd
-from langchain_core.callbacks import Callbacks
-from langchain_core.documents import Document as LCDocument
-from llama_index.core.base.embeddings.base import (
-    BaseEmbedding as LlamaIndexEmbedding,
-)
-from llama_index.core.base.llms.base import BaseLLM as LlamaIndexLLM
-from llama_index.core.schema import Document as LlamaIndexDocument
-from ragas.embeddings import BaseRagasEmbeddings, LlamaIndexEmbeddingsWrapper
-from ragas.llms import BaseRagasLLM, LlamaIndexLLMWrapper
-from ragas.run_config import RunConfig
-from ragas.testset import TestsetGenerator
-from ragas.testset.graph import KnowledgeGraph, Node, NodeType
-from ragas.testset.persona import Persona
-from ragas.testset.synthesizers import QueryDistribution
-from ragas.testset.transforms import (
-    Transforms,
-    apply_transforms,
-    default_transforms,
-)
-
-
-
-[docs] -class TestDataGenerator(TestsetGenerator): -
-[docs] - def __init__( - self, - llm: BaseRagasLLM, - embedding_model: BaseRagasEmbeddings, - knowledge_graph: KnowledgeGraph = KnowledgeGraph(), - persona_list: Optional[List[Persona]] = None, - ): - """ - RAG Test data generator class. - Generates test cases from documents for evaluating RAG workflows. - - Parameters: - llm (BaseRagasLLM): An LLM object inherited from BaseRagasLLM. Obtain this - via the get_llm tool. - embedding_model (BaseRagasEmbeddings): An embedding model object inherited - from BaseRagasEmbeddings. Obtain this via the get_embedding tool. - knowledge_graph (KnowledgeGraph): The knowledge graph to use for the generation - process. Default empty. - """ - super().__init__( - llm=llm, - embedding_model=embedding_model, - knowledge_graph=knowledge_graph, - persona_list=persona_list, - )
- - -
-[docs] - def generate_from_docs( - self, - documents: Union[Sequence[LCDocument], Sequence[LlamaIndexDocument]], - testset_size: int, - transforms: Optional[Transforms] = None, - transforms_llm: Optional[Union[BaseRagasLLM, LlamaIndexLLM]] = None, - transforms_embedding_model: Optional[ - Union[BaseRagasEmbeddings, LlamaIndexEmbedding] - ] = None, - query_distribution: Optional[QueryDistribution] = None, - run_config: Optional[RunConfig] = None, - callbacks: Optional[Callbacks] = None, - with_debugging_logs: bool = False, - raise_exceptions: bool = True, - ) -> pd.DataFrame: - """ - Generates an evaluation dataset based on given Langchain or Llama Index documents and parameters. - - Parameters: - documents : Sequence[LCDocument] - A sequence of Langchain documents to use as source material - testset_size : int - The number of test samples to generate - transforms : Optional[Transforms], optional - Custom transforms to apply to the documents, by default None - transforms_llm : Optional[BaseRagasLLM], optional - LLM to use for transforms if different from instance LLM, by default None - transforms_embedding_model : Optional[BaseRagasEmbeddings], optional - Embedding model to use for transforms if different from instance model, by default None - query_distribution : Optional[QueryDistribution], optional - Distribution of query types to generate, by default None - run_config : Optional[RunConfig], optional - Configuration for the generation run, by default None - callbacks : Optional[Callbacks], optional - Callbacks to use during generation, by default None - with_debugging_logs : bool, optional - Whether to include debug logs, by default False - raise_exceptions : bool, optional - Whether to raise exceptions during generation, by default True - - Returns: - Testset - The generated evaluation dataset - - Raises: - ValueError - If no LLM or embedding model is provided either during initialization or as arguments - """ - assert isinstance( - documents, list - ), "Documents must be a list of langchain or llama-index documents." - - if isinstance(documents[0], LCDocument): - dataset = self.generate_with_langchain_docs( - documents=documents, - testset_size=testset_size, - transforms=transforms, - transforms_llm=transforms_llm, - transforms_embedding_model=transforms_embedding_model, - query_distribution=query_distribution, - run_config=run_config, - callbacks=callbacks, - with_debugging_logs=with_debugging_logs, - raise_exceptions=raise_exceptions, - ) - return dataset.to_pandas() - - elif isinstance(documents[0], LlamaIndexDocument): - dataset = self.generate_with_llamaindex_docs( - documents=documents, - testset_size=testset_size, - transforms=transforms, - transforms_llm=transforms_llm, - transforms_embedding_model=transforms_embedding_model, - query_distribution=query_distribution, - run_config=run_config, - callbacks=callbacks, - with_debugging_logs=with_debugging_logs, - raise_exceptions=raise_exceptions, - ) - return dataset.to_pandas() - - raise ValueError( - "Documents must be a list of langchain or llama-index documents." - )
- - -
-[docs] - def generate_with_llamaindex_docs( - self, - documents: Sequence[LlamaIndexDocument], - testset_size: int, - transforms: Optional[Transforms] = None, - transforms_llm: Optional[LlamaIndexLLM] = None, - transforms_embedding_model: Optional[LlamaIndexEmbedding] = None, - query_distribution: Optional[QueryDistribution] = None, - run_config: Optional[RunConfig] = None, - callbacks: Optional[Callbacks] = None, - with_debugging_logs=False, - raise_exceptions: bool = True, - ): - """ - Generates an evaluation dataset based on given scenarios and parameters. - """ - - run_config = run_config or RunConfig() - - # force the user to provide an llm and embedding client to prevent use of default LLMs - if not self.llm and not transforms_llm: - raise ValueError( - "An llm client was not provided." - " Provide an LLM on init or as an argument to this method." - " Alternatively you can provide your own transforms through the `transforms` parameter." - ) - if not self.embedding_model and not transforms_embedding_model: - raise ValueError( - "An embedding client was not provided." - " Provide an embedding through the transforms_embedding_model parameter." - " Alternatively you can provide your own transforms through the `transforms` parameter." - ) - - if not transforms: - # use TestsetGenerator's LLM and embedding model if no transforms_llm or transforms_embedding_model is provided - if transforms_llm is None: - llm_for_transforms = self.llm - else: - llm_for_transforms = LlamaIndexLLMWrapper(transforms_llm) - if transforms_embedding_model is None: - embedding_model_for_transforms = self.embedding_model - else: - embedding_model_for_transforms = LlamaIndexEmbeddingsWrapper( - transforms_embedding_model - ) - - # create the transforms - transforms = default_transforms( - documents=[ - LCDocument(page_content=doc.text) for doc in documents - ], - llm=llm_for_transforms, - embedding_model=embedding_model_for_transforms, - ) - - # convert the documents to Ragas nodes - nodes = [] - for doc in documents: - if doc.text is not None and doc.text.strip() != "": - node = Node( - type=NodeType.DOCUMENT, - properties={ - "page_content": doc.text, - "document_metadata": doc.metadata, - }, - ) - nodes.append(node) - - kg = KnowledgeGraph(nodes=nodes) - - # apply transforms and update the knowledge graph - apply_transforms(kg, transforms, run_config) - self.knowledge_graph = kg - - return self.generate( - testset_size=testset_size, - query_distribution=query_distribution, - run_config=run_config, - callbacks=callbacks, - with_debugging_logs=with_debugging_logs, - raise_exceptions=raise_exceptions, - )
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/toolkit/rag/workflow.html b/docs/_build/html/_modules/notdiamond/toolkit/rag/workflow.html deleted file mode 100644 index ea56faf0..00000000 --- a/docs/_build/html/_modules/notdiamond/toolkit/rag/workflow.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - - notdiamond.toolkit.rag.workflow — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
    -
  • - - -
  • -
  • -
-
-
-
-
- -

Source code for notdiamond.toolkit.rag.workflow

-from dataclasses import dataclass
-from typing import Any, ClassVar, Dict, List, Optional, Type, Union, get_args
-
-import optuna
-
-from notdiamond.toolkit.rag.evaluation_dataset import RAGEvaluationDataset
-
-
-
-[docs] -@dataclass -class IntValueRange: - """ - A range of int values for an auto-evaluated RAG pipeline. Useful for, eg. RAG context chunk size. - """ - - lo: int - hi: int - step: int - - def __contains__(self, value: int) -> bool: - return ( - self.lo <= value <= self.hi and (value - self.lo) % self.step == 0 - )
- - - -
-[docs] -@dataclass -class FloatValueRange: - """ - A range of float values for an auto-evaluated RAG pipeline. Useful for, eg. LLM temperature. - """ - - lo: float - hi: float - step: float - - def __contains__(self, value: float) -> bool: - return self.lo <= value <= self.hi
- - - -
-[docs] -@dataclass -class CategoricalValueOptions: - """ - A list of categorical values for an auto-evaluated RAG pipeline. Useful for, eg. embedding algorithms. - """ - - values: List[str] - - def __contains__(self, value: str) -> bool: - return value in self.values
- - - -_ALLOWED_TYPES = [IntValueRange, FloatValueRange, CategoricalValueOptions] - - -
-[docs] -class BaseNDRagWorkflow: - """ - A base interface for a RAG workflow to be auto-evaluated by Not Diamond. - - Subclasses should define parameter_specs to type parameters they need to optimize, - by using type annotations with the above dataclasses. For example: - - .. code-block:: python - - class ExampleNDRagWorkflow(BaseNDRagWorkflow): - parameter_specs = { - "chunk_size": (Annotated[int, IntValueRange(1000, 2500, 500)], 1000), - "chunk_overlap": (Annotated[int, IntValueRange(50, 200, 25)], 100), - "top_k": (Annotated[int, IntValueRange(1, 20, 1)], 5), - "algo": ( - Annotated[ - str, - CategoricalValueOptions( - [ - "BM25", - "openai_small", - "openai_large", - "cohere_eng", - "cohere_multi", - ] - ), - ], - "BM25", - ), - "temperature": (Annotated[float, FloatValueRange(0.0, 1.0, 0.1)], 0.9), - } - - """ - - parameter_specs: ClassVar[Dict[str, tuple[Type, Any]]] = {} - -
-[docs] - def __init__( - self, - documents: Any, - test_queries: Optional[Any] = None, - objective_maximize: bool = True, - **kwargs, - ): - """ - Args: - evaluation_dataset: A dataset of RAG evaluation samples. - documents: The documents to use for RAG. - objective_maximize: Whether to maximize or minimize the objective defined below. - """ - if not self.parameter_specs: - raise NotImplementedError( - f"Class {self.__class__.__name__} must define parameter_specs" - ) - - self._param_ranges = {} - self._base_param_types = {} - for param_name, ( - param_type, - default_value, - ) in self.parameter_specs.items(): - type_args = get_args(param_type) - base_type, range_type = type_args - if range_type is None: - raise ValueError( - f"Expected parameter type in {_ALLOWED_TYPES} but received {param_type}" - ) - self._param_ranges[param_name] = range_type - self._base_param_types[param_name] = base_type - if not isinstance(default_value, base_type): - raise ValueError( - f"Expected default value type {base_type} but received {type(default_value)}" - ) - setattr(self, param_name, default_value) - - self.documents = documents - self.test_queries = test_queries - self.objective_maximize = objective_maximize
- - -
-[docs] - def get_parameter_type(self, param_name: str) -> Type: - param_type = self._base_param_types.get(param_name) - if param_type is None: - raise ValueError( - f"Parameter {param_name} not found in parameter_specs" - ) - return param_type
- - -
-[docs] - def get_parameter_range(self, param_name: str) -> Type: - param_range = self._param_ranges.get(param_name) - if param_range is None: - raise ValueError( - f"Parameter {param_name} not found in parameter_specs" - ) - return param_range
- - -
-[docs] - def rag_workflow( - self, documents: Any, test_queries: Optional[List[str]] - ) -> RAGEvaluationDataset: - """ - Define RAG workflow components here by setting instance attrs on `self`. Those attributes will be set - at init-time and available when retrieving context or generating responses. - """ - raise NotImplementedError()
- - -
-[docs] - def objective(self): - """ - Define the objective function for your RAG workflow. The workflow's hyperparameters will be optimized - according to values of this objective. - """ - raise NotImplementedError()
- - - def _outer_objective(self, trial: optuna.Trial): - for param_name in self.parameter_specs.keys(): - param_range = self.get_parameter_range(param_name) - if isinstance(param_range, IntValueRange): - param_value = trial.suggest_int( - param_name, - param_range.lo, - param_range.hi, - step=param_range.step, - ) - elif isinstance(param_range, FloatValueRange): - param_value = trial.suggest_float( - param_name, - param_range.lo, - param_range.hi, - step=param_range.step, - ) - elif isinstance(param_range, CategoricalValueOptions): - param_value = trial.suggest_categorical( - param_name, param_range.values - ) - else: - raise ValueError( - f"Expected parameter type in {_ALLOWED_TYPES} but received unknown parameter type: {param_range}" - ) - setattr(self, param_name, param_value) - - self.evaluation_dataset = self.rag_workflow( - self.documents, self.test_queries - ) - result = self.objective() - self._reset_param_values() - return result - - def _get_default_param_values(self): - return { - param_name: default_value - for param_name, (_, default_value) in self.parameter_specs.items() - } - - def _set_param_values( - self, param_values: Dict[str, Union[int, float, str]] - ): - for param_name in self.parameter_specs.keys(): - param_value = param_values.get(param_name) - param_type = self.get_parameter_type(param_name) - param_range = self.get_parameter_range(param_name) - if param_value is None: - raise ValueError( - f"Best value for {param_name} not found. This should not happen." - ) - elif not isinstance(param_value, param_type): - raise ValueError( - f"Expected parameter type {param_type} but received {type(param_value)}" - ) - elif param_value not in param_range: - raise ValueError( - f"Parameter value {param_value} not in range {param_range}" - ) - setattr(self, param_name, param_value) - - def _reset_param_values(self): - for param_name in self.parameter_specs.keys(): - setattr( - self, param_name, self._get_default_param_values()[param_name] - ) - -
-[docs] - def job_name(self): - raise NotImplementedError()
- - -
-[docs] - def get_retrieved_context(self, query: str) -> List[str]: - raise NotImplementedError()
- - -
-[docs] - def get_response(self, query: str) -> str: - raise NotImplementedError()
-
- -
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/notdiamond/types.html b/docs/_build/html/_modules/notdiamond/types.html deleted file mode 100644 index f94ade39..00000000 --- a/docs/_build/html/_modules/notdiamond/types.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - notdiamond.types — NotDiamond 0.3.38 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for notdiamond.types

-from typing import Any, Dict, List
-
-from pydantic import BaseModel, field_validator
-
-from notdiamond.exceptions import InvalidApiKey, MissingApiKey
-
-
-
-[docs] -class NDApiKeyValidator(BaseModel): - api_key: str - -
-[docs] - @field_validator("api_key", mode="before") - @classmethod - def api_key_must_be_a_string(cls, v) -> str: - if not isinstance(v, str): - raise InvalidApiKey("ND API key should be a string") - return v
- - -
-[docs] - @field_validator("api_key", mode="after") - @classmethod - def string_must_not_be_empty(cls, v): - if len(v) == 0: - raise MissingApiKey("ND API key should be longer than 0") - return v
-
- - - -
-[docs] -class ModelSelectRequestPayload(BaseModel): - prompt_template: str - formatted_prompt: str - components: Dict[str, Dict] - llm_configs: List[Dict] - metric: str - max_model_depth: int
- - - -
-[docs] -class FeedbackRequestPayload(BaseModel): - session_id: str - provider: Dict[str, Any] - feedback: Dict[str, int]
- -
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/pydantic/_internal/_repr.html b/docs/_build/html/_modules/pydantic/_internal/_repr.html deleted file mode 100644 index 4d6a6675..00000000 --- a/docs/_build/html/_modules/pydantic/_internal/_repr.html +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - pydantic._internal._repr — NotDiamond 0.3.25 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for pydantic._internal._repr

-"""Tools to provide pretty/human-readable display of objects."""
-
-from __future__ import annotations as _annotations
-
-import types
-import typing
-from typing import Any
-
-import typing_extensions
-
-from . import _typing_extra
-
-if typing.TYPE_CHECKING:
-    ReprArgs: typing_extensions.TypeAlias = 'typing.Iterable[tuple[str | None, Any]]'
-    RichReprResult: typing_extensions.TypeAlias = (
-        'typing.Iterable[Any | tuple[Any] | tuple[str, Any] | tuple[str, Any, Any]]'
-    )
-
-
-class PlainRepr(str):
-    """String class where repr doesn't include quotes. Useful with Representation when you want to return a string
-    representation of something that is valid (or pseudo-valid) python.
-    """
-
-    def __repr__(self) -> str:
-        return str(self)
-
-
-class Representation:
-    # Mixin to provide `__str__`, `__repr__`, and `__pretty__` and `__rich_repr__` methods.
-    # `__pretty__` is used by [devtools](https://python-devtools.helpmanual.io/).
-    # `__rich_repr__` is used by [rich](https://rich.readthedocs.io/en/stable/pretty.html).
-    # (this is not a docstring to avoid adding a docstring to classes which inherit from Representation)
-
-    # we don't want to use a type annotation here as it can break get_type_hints
-    __slots__ = ()  # type: typing.Collection[str]
-
-    def __repr_args__(self) -> ReprArgs:
-        """Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden.
-
-        Can either return:
-        * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]`
-        * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]`
-        """
-        attrs_names = self.__slots__
-        if not attrs_names and hasattr(self, '__dict__'):
-            attrs_names = self.__dict__.keys()
-        attrs = ((s, getattr(self, s)) for s in attrs_names)
-        return [(a, v) for a, v in attrs if v is not None]
-
-    def __repr_name__(self) -> str:
-        """Name of the instance's class, used in __repr__."""
-        return self.__class__.__name__
-
-    def __repr_str__(self, join_str: str) -> str:
-        return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
-
-    def __pretty__(self, fmt: typing.Callable[[Any], Any], **kwargs: Any) -> typing.Generator[Any, None, None]:
-        """Used by devtools (https://python-devtools.helpmanual.io/) to pretty print objects."""
-        yield self.__repr_name__() + '('
-        yield 1
-        for name, value in self.__repr_args__():
-            if name is not None:
-                yield name + '='
-            yield fmt(value)
-            yield ','
-            yield 0
-        yield -1
-        yield ')'
-
-    def __rich_repr__(self) -> RichReprResult:
-        """Used by Rich (https://rich.readthedocs.io/en/stable/pretty.html) to pretty print objects."""
-        for name, field_repr in self.__repr_args__():
-            if name is None:
-                yield field_repr
-            else:
-                yield name, field_repr
-
-    def __str__(self) -> str:
-        return self.__repr_str__(' ')
-
-    def __repr__(self) -> str:
-        return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
-
-
-def display_as_type(obj: Any) -> str:
-    """Pretty representation of a type, should be as close as possible to the original type definition string.
-
-    Takes some logic from `typing._type_repr`.
-    """
-    if isinstance(obj, types.FunctionType):
-        return obj.__name__
-    elif obj is ...:
-        return '...'
-    elif isinstance(obj, Representation):
-        return repr(obj)
-    elif isinstance(obj, typing_extensions.TypeAliasType):
-        return str(obj)
-
-    if not isinstance(obj, (_typing_extra.typing_base, _typing_extra.WithArgsTypes, type)):
-        obj = obj.__class__
-
-    if _typing_extra.origin_is_union(typing_extensions.get_origin(obj)):
-        args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
-        return f'Union[{args}]'
-    elif isinstance(obj, _typing_extra.WithArgsTypes):
-        if typing_extensions.get_origin(obj) == typing_extensions.Literal:
-            args = ', '.join(map(repr, typing_extensions.get_args(obj)))
-        else:
-            args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
-        try:
-            return f'{obj.__qualname__}[{args}]'
-        except AttributeError:
-            return str(obj)  # handles TypeAliasType in 3.12
-    elif isinstance(obj, type):
-        return obj.__qualname__
-    else:
-        return repr(obj).replace('typing.', '').replace('typing_extensions.', '')
-
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/pydantic/main.html b/docs/_build/html/_modules/pydantic/main.html deleted file mode 100644 index ed0890af..00000000 --- a/docs/_build/html/_modules/pydantic/main.html +++ /dev/null @@ -1,1718 +0,0 @@ - - - - - - pydantic.main — NotDiamond 0.3.25 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for pydantic.main

-"""Logic for creating models."""
-
-from __future__ import annotations as _annotations
-
-import operator
-import sys
-import types
-import typing
-import warnings
-from copy import copy, deepcopy
-from typing import (
-    TYPE_CHECKING,
-    Any,
-    Callable,
-    ClassVar,
-    Dict,
-    Generator,
-    Literal,
-    Mapping,
-    Set,
-    Tuple,
-    TypeVar,
-    Union,
-    cast,
-    overload,
-)
-
-import pydantic_core
-import typing_extensions
-from pydantic_core import PydanticUndefined
-from typing_extensions import Self, TypeAlias, Unpack
-
-from ._internal import (
-    _config,
-    _decorators,
-    _fields,
-    _forward_ref,
-    _generics,
-    _import_utils,
-    _mock_val_ser,
-    _model_construction,
-    _repr,
-    _typing_extra,
-    _utils,
-)
-from ._migration import getattr_migration
-from .aliases import AliasChoices, AliasPath
-from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
-from .config import ConfigDict
-from .errors import PydanticUndefinedAnnotation, PydanticUserError
-from .json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema, JsonSchemaMode, JsonSchemaValue, model_json_schema
-from .plugin._schema_validator import PluggableSchemaValidator
-from .warnings import PydanticDeprecatedSince20
-
-if TYPE_CHECKING:
-    from inspect import Signature
-    from pathlib import Path
-
-    from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator
-
-    from ._internal._utils import AbstractSetIntStr, MappingIntStrAny
-    from .deprecated.parse import Protocol as DeprecatedParseProtocol
-    from .fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr
-else:
-    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
-    # and https://youtrack.jetbrains.com/issue/PY-51428
-    DeprecationWarning = PydanticDeprecatedSince20
-
-__all__ = 'BaseModel', 'create_model'
-
-# Keep these type aliases available at runtime:
-TupleGenerator: TypeAlias = Generator[Tuple[str, Any], None, None]
-# Keep this type alias in sync with the stub definition in `pydantic-core`:
-IncEx: TypeAlias = Union[
-    Set[int], Set[str], Mapping[int, Union['IncEx', Literal[True]]], Mapping[str, Union['IncEx', Literal[True]]]
-]
-
-_object_setattr = _model_construction.object_setattr
-
-
-class BaseModel(metaclass=_model_construction.ModelMetaclass):
-    """Usage docs: https://docs.pydantic.dev/2.9/concepts/models/
-
-    A base class for creating Pydantic models.
-
-    Attributes:
-        __class_vars__: The names of the class variables defined on the model.
-        __private_attributes__: Metadata about the private attributes of the model.
-        __signature__: The synthesized `__init__` [`Signature`][inspect.Signature] of the model.
-
-        __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
-        __pydantic_core_schema__: The core schema of the model.
-        __pydantic_custom_init__: Whether the model has a custom `__init__` function.
-        __pydantic_decorators__: Metadata containing the decorators defined on the model.
-            This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
-        __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
-            __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
-        __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
-        __pydantic_post_init__: The name of the post-init method for the model, if defined.
-        __pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
-        __pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
-        __pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.
-
-        __pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
-            is set to `'allow'`.
-        __pydantic_fields_set__: The names of fields explicitly set during instantiation.
-        __pydantic_private__: Values of private attributes set on the model instance.
-    """
-
-    # Class attributes:
-    # `model_fields` and `__pydantic_decorators__` must be set for
-    # `GenerateSchema.model_schema` to work for a plain `BaseModel` annotation.
-
-    model_config: ClassVar[ConfigDict] = ConfigDict()
-    """
-    Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].
-    """
-
-    # Because `dict` is in the local namespace of the `BaseModel` class, we use `Dict` for annotations.
-    # TODO v3 fallback to `dict` when the deprecated `dict` method gets removed.
-    model_fields: ClassVar[Dict[str, FieldInfo]] = {}  # noqa: UP006
-    """
-    Metadata about the fields defined on the model,
-    mapping of field names to [`FieldInfo`][pydantic.fields.FieldInfo] objects.
-
-    This replaces `Model.__fields__` from Pydantic V1.
-    """
-
-    model_computed_fields: ClassVar[Dict[str, ComputedFieldInfo]] = {}  # noqa: UP006
-    """A dictionary of computed field names and their corresponding `ComputedFieldInfo` objects."""
-
-    __class_vars__: ClassVar[set[str]]
-    """The names of the class variables defined on the model."""
-
-    __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]]  # noqa: UP006
-    """Metadata about the private attributes of the model."""
-
-    __signature__: ClassVar[Signature]
-    """The synthesized `__init__` [`Signature`][inspect.Signature] of the model."""
-
-    __pydantic_complete__: ClassVar[bool] = False
-    """Whether model building is completed, or if there are still undefined fields."""
-
-    __pydantic_core_schema__: ClassVar[CoreSchema]
-    """The core schema of the model."""
-
-    __pydantic_custom_init__: ClassVar[bool]
-    """Whether the model has a custom `__init__` method."""
-
-    __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] = _decorators.DecoratorInfos()
-    """Metadata containing the decorators defined on the model.
-    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1."""
-
-    __pydantic_generic_metadata__: ClassVar[_generics.PydanticGenericMetadata]
-    """Metadata for generic models; contains data used for a similar purpose to
-    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these."""
-
-    __pydantic_parent_namespace__: ClassVar[Dict[str, Any] | None] = None  # noqa: UP006
-    """Parent namespace of the model, used for automatic rebuilding of models."""
-
-    __pydantic_post_init__: ClassVar[None | Literal['model_post_init']]
-    """The name of the post-init method for the model, if defined."""
-
-    __pydantic_root_model__: ClassVar[bool] = False
-    """Whether the model is a [`RootModel`][pydantic.root_model.RootModel]."""
-
-    __pydantic_serializer__: ClassVar[SchemaSerializer]
-    """The `pydantic-core` `SchemaSerializer` used to dump instances of the model."""
-
-    __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator]
-    """The `pydantic-core` `SchemaValidator` used to validate instances of the model."""
-
-    __pydantic_extra__: dict[str, Any] | None = _model_construction.NoInitField(init=False)
-    """A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra] is set to `'allow'`."""
-
-    __pydantic_fields_set__: set[str] = _model_construction.NoInitField(init=False)
-    """The names of fields explicitly set during instantiation."""
-
-    __pydantic_private__: dict[str, Any] | None = _model_construction.NoInitField(init=False)
-    """Values of private attributes set on the model instance."""
-
-    if not TYPE_CHECKING:
-        # Prevent `BaseModel` from being instantiated directly
-        # (defined in an `if not TYPE_CHECKING` block for clarity and to avoid type checking errors):
-        __pydantic_core_schema__ = _mock_val_ser.MockCoreSchema(
-            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
-            code='base-model-instantiated',
-        )
-        __pydantic_validator__ = _mock_val_ser.MockValSer(
-            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
-            val_or_ser='validator',
-            code='base-model-instantiated',
-        )
-        __pydantic_serializer__ = _mock_val_ser.MockValSer(
-            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
-            val_or_ser='serializer',
-            code='base-model-instantiated',
-        )
-
-    __slots__ = '__dict__', '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__'
-
-    def __init__(self, /, **data: Any) -> None:
-        """Create a new model by parsing and validating input data from keyword arguments.
-
-        Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
-        validated to form a valid model.
-
-        `self` is explicitly positional-only to allow `self` as a field name.
-        """
-        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
-        __tracebackhide__ = True
-        validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
-        if self is not validated_self:
-            warnings.warn(
-                'A custom validator is returning a value other than `self`.\n'
-                "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
-                'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
-                category=None,
-            )
-
-    # The following line sets a flag that we use to determine when `__init__` gets overridden by the user
-    __init__.__pydantic_base_init__ = True  # pyright: ignore[reportFunctionMemberAccess]
-
-    @property
-    def model_extra(self) -> dict[str, Any] | None:
-        """Get extra fields set during validation.
-
-        Returns:
-            A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`.
-        """
-        return self.__pydantic_extra__
-
-    @property
-    def model_fields_set(self) -> set[str]:
-        """Returns the set of fields that have been explicitly set on this model instance.
-
-        Returns:
-            A set of strings representing the fields that have been set,
-                i.e. that were not filled from defaults.
-        """
-        return self.__pydantic_fields_set__
-
-    @classmethod
-    def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:  # noqa: C901
-        """Creates a new instance of the `Model` class with validated data.
-
-        Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data.
-        Default values are respected, but no other validation is performed.
-
-        !!! note
-            `model_construct()` generally respects the `model_config.extra` setting on the provided model.
-            That is, if `model_config.extra == 'allow'`, then all extra passed values are added to the model instance's `__dict__`
-            and `__pydantic_extra__` fields. If `model_config.extra == 'ignore'` (the default), then all extra passed values are ignored.
-            Because no validation is performed with a call to `model_construct()`, having `model_config.extra == 'forbid'` does not result in
-            an error if extra values are passed, but they will be ignored.
-
-        Args:
-            _fields_set: A set of field names that were originally explicitly set during instantiation. If provided,
-                this is directly used for the [`model_fields_set`][pydantic.BaseModel.model_fields_set] attribute.
-                Otherwise, the field names from the `values` argument will be used.
-            values: Trusted or pre-validated data dictionary.
-
-        Returns:
-            A new instance of the `Model` class with validated data.
-        """
-        m = cls.__new__(cls)
-        fields_values: dict[str, Any] = {}
-        fields_set = set()
-
-        for name, field in cls.model_fields.items():
-            if field.alias is not None and field.alias in values:
-                fields_values[name] = values.pop(field.alias)
-                fields_set.add(name)
-
-            if (name not in fields_set) and (field.validation_alias is not None):
-                validation_aliases: list[str | AliasPath] = (
-                    field.validation_alias.choices
-                    if isinstance(field.validation_alias, AliasChoices)
-                    else [field.validation_alias]
-                )
-
-                for alias in validation_aliases:
-                    if isinstance(alias, str) and alias in values:
-                        fields_values[name] = values.pop(alias)
-                        fields_set.add(name)
-                        break
-                    elif isinstance(alias, AliasPath):
-                        value = alias.search_dict_for_path(values)
-                        if value is not PydanticUndefined:
-                            fields_values[name] = value
-                            fields_set.add(name)
-                            break
-
-            if name not in fields_set:
-                if name in values:
-                    fields_values[name] = values.pop(name)
-                    fields_set.add(name)
-                elif not field.is_required():
-                    fields_values[name] = field.get_default(call_default_factory=True)
-        if _fields_set is None:
-            _fields_set = fields_set
-
-        _extra: dict[str, Any] | None = values if cls.model_config.get('extra') == 'allow' else None
-        _object_setattr(m, '__dict__', fields_values)
-        _object_setattr(m, '__pydantic_fields_set__', _fields_set)
-        if not cls.__pydantic_root_model__:
-            _object_setattr(m, '__pydantic_extra__', _extra)
-
-        if cls.__pydantic_post_init__:
-            m.model_post_init(None)
-            # update private attributes with values set
-            if hasattr(m, '__pydantic_private__') and m.__pydantic_private__ is not None:
-                for k, v in values.items():
-                    if k in m.__private_attributes__:
-                        m.__pydantic_private__[k] = v
-
-        elif not cls.__pydantic_root_model__:
-            # Note: if there are any private attributes, cls.__pydantic_post_init__ would exist
-            # Since it doesn't, that means that `__pydantic_private__` should be set to None
-            _object_setattr(m, '__pydantic_private__', None)
-
-        return m
-
-    def model_copy(self, *, update: dict[str, Any] | None = None, deep: bool = False) -> Self:
-        """Usage docs: https://docs.pydantic.dev/2.9/concepts/serialization/#model_copy
-
-        Returns a copy of the model.
-
-        Args:
-            update: Values to change/add in the new model. Note: the data is not validated
-                before creating the new model. You should trust this data.
-            deep: Set to `True` to make a deep copy of the model.
-
-        Returns:
-            New model instance.
-        """
-        copied = self.__deepcopy__() if deep else self.__copy__()
-        if update:
-            if self.model_config.get('extra') == 'allow':
-                for k, v in update.items():
-                    if k in self.model_fields:
-                        copied.__dict__[k] = v
-                    else:
-                        if copied.__pydantic_extra__ is None:
-                            copied.__pydantic_extra__ = {}
-                        copied.__pydantic_extra__[k] = v
-            else:
-                copied.__dict__.update(update)
-            copied.__pydantic_fields_set__.update(update.keys())
-        return copied
-
-    def model_dump(
-        self,
-        *,
-        mode: Literal['json', 'python'] | str = 'python',
-        include: IncEx | None = None,
-        exclude: IncEx | None = None,
-        context: Any | None = None,
-        by_alias: bool = False,
-        exclude_unset: bool = False,
-        exclude_defaults: bool = False,
-        exclude_none: bool = False,
-        round_trip: bool = False,
-        warnings: bool | Literal['none', 'warn', 'error'] = True,
-        serialize_as_any: bool = False,
-    ) -> dict[str, Any]:
-        """Usage docs: https://docs.pydantic.dev/2.9/concepts/serialization/#modelmodel_dump
-
-        Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
-
-        Args:
-            mode: The mode in which `to_python` should run.
-                If mode is 'json', the output will only contain JSON serializable types.
-                If mode is 'python', the output may contain non-JSON-serializable Python objects.
-            include: A set of fields to include in the output.
-            exclude: A set of fields to exclude from the output.
-            context: Additional context to pass to the serializer.
-            by_alias: Whether to use the field's alias in the dictionary key if defined.
-            exclude_unset: Whether to exclude fields that have not been explicitly set.
-            exclude_defaults: Whether to exclude fields that are set to their default value.
-            exclude_none: Whether to exclude fields that have a value of `None`.
-            round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
-            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
-                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
-            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
-
-        Returns:
-            A dictionary representation of the model.
-        """
-        return self.__pydantic_serializer__.to_python(
-            self,
-            mode=mode,
-            by_alias=by_alias,
-            include=include,
-            exclude=exclude,
-            context=context,
-            exclude_unset=exclude_unset,
-            exclude_defaults=exclude_defaults,
-            exclude_none=exclude_none,
-            round_trip=round_trip,
-            warnings=warnings,
-            serialize_as_any=serialize_as_any,
-        )
-
-    def model_dump_json(
-        self,
-        *,
-        indent: int | None = None,
-        include: IncEx | None = None,
-        exclude: IncEx | None = None,
-        context: Any | None = None,
-        by_alias: bool = False,
-        exclude_unset: bool = False,
-        exclude_defaults: bool = False,
-        exclude_none: bool = False,
-        round_trip: bool = False,
-        warnings: bool | Literal['none', 'warn', 'error'] = True,
-        serialize_as_any: bool = False,
-    ) -> str:
-        """Usage docs: https://docs.pydantic.dev/2.9/concepts/serialization/#modelmodel_dump_json
-
-        Generates a JSON representation of the model using Pydantic's `to_json` method.
-
-        Args:
-            indent: Indentation to use in the JSON output. If None is passed, the output will be compact.
-            include: Field(s) to include in the JSON output.
-            exclude: Field(s) to exclude from the JSON output.
-            context: Additional context to pass to the serializer.
-            by_alias: Whether to serialize using field aliases.
-            exclude_unset: Whether to exclude fields that have not been explicitly set.
-            exclude_defaults: Whether to exclude fields that are set to their default value.
-            exclude_none: Whether to exclude fields that have a value of `None`.
-            round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
-            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
-                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
-            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
-
-        Returns:
-            A JSON string representation of the model.
-        """
-        return self.__pydantic_serializer__.to_json(
-            self,
-            indent=indent,
-            include=include,
-            exclude=exclude,
-            context=context,
-            by_alias=by_alias,
-            exclude_unset=exclude_unset,
-            exclude_defaults=exclude_defaults,
-            exclude_none=exclude_none,
-            round_trip=round_trip,
-            warnings=warnings,
-            serialize_as_any=serialize_as_any,
-        ).decode()
-
-    @classmethod
-    def model_json_schema(
-        cls,
-        by_alias: bool = True,
-        ref_template: str = DEFAULT_REF_TEMPLATE,
-        schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
-        mode: JsonSchemaMode = 'validation',
-    ) -> dict[str, Any]:
-        """Generates a JSON schema for a model class.
-
-        Args:
-            by_alias: Whether to use attribute aliases or not.
-            ref_template: The reference template.
-            schema_generator: To override the logic used to generate the JSON schema, as a subclass of
-                `GenerateJsonSchema` with your desired modifications
-            mode: The mode in which to generate the schema.
-
-        Returns:
-            The JSON schema for the given model class.
-        """
-        return model_json_schema(
-            cls, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator, mode=mode
-        )
-
-    @classmethod
-    def model_parametrized_name(cls, params: tuple[type[Any], ...]) -> str:
-        """Compute the class name for parametrizations of generic classes.
-
-        This method can be overridden to achieve a custom naming scheme for generic BaseModels.
-
-        Args:
-            params: Tuple of types of the class. Given a generic class
-                `Model` with 2 type variables and a concrete model `Model[str, int]`,
-                the value `(str, int)` would be passed to `params`.
-
-        Returns:
-            String representing the new class where `params` are passed to `cls` as type variables.
-
-        Raises:
-            TypeError: Raised when trying to generate concrete names for non-generic models.
-        """
-        if not issubclass(cls, typing.Generic):
-            raise TypeError('Concrete names should only be generated for generic models.')
-
-        # Any strings received should represent forward references, so we handle them specially below.
-        # If we eventually move toward wrapping them in a ForwardRef in __class_getitem__ in the future,
-        # we may be able to remove this special case.
-        param_names = [param if isinstance(param, str) else _repr.display_as_type(param) for param in params]
-        params_component = ', '.join(param_names)
-        return f'{cls.__name__}[{params_component}]'
-
-    def model_post_init(self, __context: Any) -> None:
-        """Override this method to perform additional initialization after `__init__` and `model_construct`.
-        This is useful if you want to do some validation that requires the entire model to be initialized.
-        """
-        pass
-
-    @classmethod
-    def model_rebuild(
-        cls,
-        *,
-        force: bool = False,
-        raise_errors: bool = True,
-        _parent_namespace_depth: int = 2,
-        _types_namespace: dict[str, Any] | None = None,
-    ) -> bool | None:
-        """Try to rebuild the pydantic-core schema for the model.
-
-        This may be necessary when one of the annotations is a ForwardRef which could not be resolved during
-        the initial attempt to build the schema, and automatic rebuilding fails.
-
-        Args:
-            force: Whether to force the rebuilding of the model schema, defaults to `False`.
-            raise_errors: Whether to raise errors, defaults to `True`.
-            _parent_namespace_depth: The depth level of the parent namespace, defaults to 2.
-            _types_namespace: The types namespace, defaults to `None`.
-
-        Returns:
-            Returns `None` if the schema is already "complete" and rebuilding was not required.
-            If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.
-        """
-        if not force and cls.__pydantic_complete__:
-            return None
-        else:
-            if '__pydantic_core_schema__' in cls.__dict__:
-                delattr(cls, '__pydantic_core_schema__')  # delete cached value to ensure full rebuild happens
-            if _types_namespace is not None:
-                types_namespace: dict[str, Any] | None = _types_namespace.copy()
-            else:
-                if _parent_namespace_depth > 0:
-                    frame_parent_ns = (
-                        _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}
-                    )
-                    cls_parent_ns = (
-                        _model_construction.unpack_lenient_weakvaluedict(cls.__pydantic_parent_namespace__) or {}
-                    )
-                    types_namespace = {**cls_parent_ns, **frame_parent_ns}
-                    cls.__pydantic_parent_namespace__ = _model_construction.build_lenient_weakvaluedict(types_namespace)
-                else:
-                    types_namespace = _model_construction.unpack_lenient_weakvaluedict(
-                        cls.__pydantic_parent_namespace__
-                    )
-
-                types_namespace = _typing_extra.merge_cls_and_parent_ns(cls, types_namespace)
-
-            # manually override defer_build so complete_model_class doesn't skip building the model again
-            config = {**cls.model_config, 'defer_build': False}
-            return _model_construction.complete_model_class(
-                cls,
-                cls.__name__,
-                _config.ConfigWrapper(config, check=False),
-                raise_errors=raise_errors,
-                types_namespace=types_namespace,
-            )
-
-    @classmethod
-    def model_validate(
-        cls,
-        obj: Any,
-        *,
-        strict: bool | None = None,
-        from_attributes: bool | None = None,
-        context: Any | None = None,
-    ) -> Self:
-        """Validate a pydantic model instance.
-
-        Args:
-            obj: The object to validate.
-            strict: Whether to enforce types strictly.
-            from_attributes: Whether to extract data from object attributes.
-            context: Additional context to pass to the validator.
-
-        Raises:
-            ValidationError: If the object could not be validated.
-
-        Returns:
-            The validated model instance.
-        """
-        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
-        __tracebackhide__ = True
-        return cls.__pydantic_validator__.validate_python(
-            obj, strict=strict, from_attributes=from_attributes, context=context
-        )
-
-    @classmethod
-    def model_validate_json(
-        cls,
-        json_data: str | bytes | bytearray,
-        *,
-        strict: bool | None = None,
-        context: Any | None = None,
-    ) -> Self:
-        """Usage docs: https://docs.pydantic.dev/2.9/concepts/json/#json-parsing
-
-        Validate the given JSON data against the Pydantic model.
-
-        Args:
-            json_data: The JSON data to validate.
-            strict: Whether to enforce types strictly.
-            context: Extra variables to pass to the validator.
-
-        Returns:
-            The validated Pydantic model.
-
-        Raises:
-            ValidationError: If `json_data` is not a JSON string or the object could not be validated.
-        """
-        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
-        __tracebackhide__ = True
-        return cls.__pydantic_validator__.validate_json(json_data, strict=strict, context=context)
-
-    @classmethod
-    def model_validate_strings(
-        cls,
-        obj: Any,
-        *,
-        strict: bool | None = None,
-        context: Any | None = None,
-    ) -> Self:
-        """Validate the given object with string data against the Pydantic model.
-
-        Args:
-            obj: The object containing string data to validate.
-            strict: Whether to enforce types strictly.
-            context: Extra variables to pass to the validator.
-
-        Returns:
-            The validated Pydantic model.
-        """
-        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
-        __tracebackhide__ = True
-        return cls.__pydantic_validator__.validate_strings(obj, strict=strict, context=context)
-
-    @classmethod
-    def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreSchemaHandler, /) -> CoreSchema:
-        """Hook into generating the model's CoreSchema.
-
-        Args:
-            source: The class we are generating a schema for.
-                This will generally be the same as the `cls` argument if this is a classmethod.
-            handler: A callable that calls into Pydantic's internal CoreSchema generation logic.
-
-        Returns:
-            A `pydantic-core` `CoreSchema`.
-        """
-        # Only use the cached value from this _exact_ class; we don't want one from a parent class
-        # This is why we check `cls.__dict__` and don't use `cls.__pydantic_core_schema__` or similar.
-        schema = cls.__dict__.get('__pydantic_core_schema__')
-        if schema is not None and not isinstance(schema, _mock_val_ser.MockCoreSchema):
-            # Due to the way generic classes are built, it's possible that an invalid schema may be temporarily
-            # set on generic classes. I think we could resolve this to ensure that we get proper schema caching
-            # for generics, but for simplicity for now, we just always rebuild if the class has a generic origin.
-            if not cls.__pydantic_generic_metadata__['origin']:
-                return cls.__pydantic_core_schema__
-
-        return handler(source)
-
-    @classmethod
-    def __get_pydantic_json_schema__(
-        cls,
-        core_schema: CoreSchema,
-        handler: GetJsonSchemaHandler,
-        /,
-    ) -> JsonSchemaValue:
-        """Hook into generating the model's JSON schema.
-
-        Args:
-            core_schema: A `pydantic-core` CoreSchema.
-                You can ignore this argument and call the handler with a new CoreSchema,
-                wrap this CoreSchema (`{'type': 'nullable', 'schema': current_schema}`),
-                or just call the handler with the original schema.
-            handler: Call into Pydantic's internal JSON schema generation.
-                This will raise a `pydantic.errors.PydanticInvalidForJsonSchema` if JSON schema
-                generation fails.
-                Since this gets called by `BaseModel.model_json_schema` you can override the
-                `schema_generator` argument to that function to change JSON schema generation globally
-                for a type.
-
-        Returns:
-            A JSON schema, as a Python object.
-        """
-        return handler(core_schema)
-
-    @classmethod
-    def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
-        """This is intended to behave just like `__init_subclass__`, but is called by `ModelMetaclass`
-        only after the class is actually fully initialized. In particular, attributes like `model_fields` will
-        be present when this is called.
-
-        This is necessary because `__init_subclass__` will always be called by `type.__new__`,
-        and it would require a prohibitively large refactor to the `ModelMetaclass` to ensure that
-        `type.__new__` was called in such a manner that the class would already be sufficiently initialized.
-
-        This will receive the same `kwargs` that would be passed to the standard `__init_subclass__`, namely,
-        any kwargs passed to the class definition that aren't used internally by pydantic.
-
-        Args:
-            **kwargs: Any keyword arguments passed to the class definition that aren't used internally
-                by pydantic.
-        """
-        pass
-
-    def __class_getitem__(
-        cls, typevar_values: type[Any] | tuple[type[Any], ...]
-    ) -> type[BaseModel] | _forward_ref.PydanticRecursiveRef:
-        cached = _generics.get_cached_generic_type_early(cls, typevar_values)
-        if cached is not None:
-            return cached
-
-        if cls is BaseModel:
-            raise TypeError('Type parameters should be placed on typing.Generic, not BaseModel')
-        if not hasattr(cls, '__parameters__'):
-            raise TypeError(f'{cls} cannot be parametrized because it does not inherit from typing.Generic')
-        if not cls.__pydantic_generic_metadata__['parameters'] and typing.Generic not in cls.__bases__:
-            raise TypeError(f'{cls} is not a generic class')
-
-        if not isinstance(typevar_values, tuple):
-            typevar_values = (typevar_values,)
-        _generics.check_parameters_count(cls, typevar_values)
-
-        # Build map from generic typevars to passed params
-        typevars_map: dict[_typing_extra.TypeVarType, type[Any]] = dict(
-            zip(cls.__pydantic_generic_metadata__['parameters'], typevar_values)
-        )
-
-        if _utils.all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map:
-            submodel = cls  # if arguments are equal to parameters it's the same object
-            _generics.set_cached_generic_type(cls, typevar_values, submodel)
-        else:
-            parent_args = cls.__pydantic_generic_metadata__['args']
-            if not parent_args:
-                args = typevar_values
-            else:
-                args = tuple(_generics.replace_types(arg, typevars_map) for arg in parent_args)
-
-            origin = cls.__pydantic_generic_metadata__['origin'] or cls
-            model_name = origin.model_parametrized_name(args)
-            params = tuple(
-                {param: None for param in _generics.iter_contained_typevars(typevars_map.values())}
-            )  # use dict as ordered set
-
-            with _generics.generic_recursion_self_type(origin, args) as maybe_self_type:
-                if maybe_self_type is not None:
-                    return maybe_self_type
-
-                cached = _generics.get_cached_generic_type_late(cls, typevar_values, origin, args)
-                if cached is not None:
-                    return cached
-
-                # Attempt to rebuild the origin in case new types have been defined
-                try:
-                    # depth 3 gets you above this __class_getitem__ call
-                    origin.model_rebuild(_parent_namespace_depth=3)
-                except PydanticUndefinedAnnotation:
-                    # It's okay if it fails, it just means there are still undefined types
-                    # that could be evaluated later.
-                    # TODO: Make sure validation fails if there are still undefined types, perhaps using MockValidator
-                    pass
-
-                submodel = _generics.create_generic_submodel(model_name, origin, args, params)
-
-                # Update cache
-                _generics.set_cached_generic_type(cls, typevar_values, submodel, origin, args)
-
-        return submodel
-
-    def __copy__(self) -> Self:
-        """Returns a shallow copy of the model."""
-        cls = type(self)
-        m = cls.__new__(cls)
-        _object_setattr(m, '__dict__', copy(self.__dict__))
-        _object_setattr(m, '__pydantic_extra__', copy(self.__pydantic_extra__))
-        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
-
-        if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:
-            _object_setattr(m, '__pydantic_private__', None)
-        else:
-            _object_setattr(
-                m,
-                '__pydantic_private__',
-                {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined},
-            )
-
-        return m
-
-    def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:
-        """Returns a deep copy of the model."""
-        cls = type(self)
-        m = cls.__new__(cls)
-        _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
-        _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo))
-        # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],
-        # and attempting a deepcopy would be marginally slower.
-        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
-
-        if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:
-            _object_setattr(m, '__pydantic_private__', None)
-        else:
-            _object_setattr(
-                m,
-                '__pydantic_private__',
-                deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo),
-            )
-
-        return m
-
-    if not TYPE_CHECKING:
-        # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
-        # The same goes for __setattr__ and __delattr__, see: https://github.com/pydantic/pydantic/issues/8643
-
-        def __getattr__(self, item: str) -> Any:
-            private_attributes = object.__getattribute__(self, '__private_attributes__')
-            if item in private_attributes:
-                attribute = private_attributes[item]
-                if hasattr(attribute, '__get__'):
-                    return attribute.__get__(self, type(self))  # type: ignore
-
-                try:
-                    # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items
-                    return self.__pydantic_private__[item]  # type: ignore
-                except KeyError as exc:
-                    raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc
-            else:
-                # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.
-                # See `BaseModel.__repr_args__` for more details
-                try:
-                    pydantic_extra = object.__getattribute__(self, '__pydantic_extra__')
-                except AttributeError:
-                    pydantic_extra = None
-
-                if pydantic_extra:
-                    try:
-                        return pydantic_extra[item]
-                    except KeyError as exc:
-                        raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc
-                else:
-                    if hasattr(self.__class__, item):
-                        return super().__getattribute__(item)  # Raises AttributeError if appropriate
-                    else:
-                        # this is the current error
-                        raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
-
-        def __setattr__(self, name: str, value: Any) -> None:
-            if name in self.__class_vars__:
-                raise AttributeError(
-                    f'{name!r} is a ClassVar of `{self.__class__.__name__}` and cannot be set on an instance. '
-                    f'If you want to set a value on the class, use `{self.__class__.__name__}.{name} = value`.'
-                )
-            elif not _fields.is_valid_field_name(name):
-                if self.__pydantic_private__ is None or name not in self.__private_attributes__:
-                    _object_setattr(self, name, value)
-                else:
-                    attribute = self.__private_attributes__[name]
-                    if hasattr(attribute, '__set__'):
-                        attribute.__set__(self, value)  # type: ignore
-                    else:
-                        self.__pydantic_private__[name] = value
-                return
-
-            self._check_frozen(name, value)
-
-            attr = getattr(self.__class__, name, None)
-            if isinstance(attr, property):
-                attr.__set__(self, value)
-            elif self.model_config.get('validate_assignment', None):
-                self.__pydantic_validator__.validate_assignment(self, name, value)
-            elif self.model_config.get('extra') != 'allow' and name not in self.model_fields:
-                # TODO - matching error
-                raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"')
-            elif self.model_config.get('extra') == 'allow' and name not in self.model_fields:
-                if self.model_extra and name in self.model_extra:
-                    self.__pydantic_extra__[name] = value  # type: ignore
-                else:
-                    try:
-                        getattr(self, name)
-                    except AttributeError:
-                        # attribute does not already exist on instance, so put it in extra
-                        self.__pydantic_extra__[name] = value  # type: ignore
-                    else:
-                        # attribute _does_ already exist on instance, and was not in extra, so update it
-                        _object_setattr(self, name, value)
-            else:
-                self.__dict__[name] = value
-                self.__pydantic_fields_set__.add(name)
-
-        def __delattr__(self, item: str) -> Any:
-            if item in self.__private_attributes__:
-                attribute = self.__private_attributes__[item]
-                if hasattr(attribute, '__delete__'):
-                    attribute.__delete__(self)  # type: ignore
-                    return
-
-                try:
-                    # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items
-                    del self.__pydantic_private__[item]  # type: ignore
-                    return
-                except KeyError as exc:
-                    raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc
-
-            self._check_frozen(item, None)
-
-            if item in self.model_fields:
-                object.__delattr__(self, item)
-            elif self.__pydantic_extra__ is not None and item in self.__pydantic_extra__:
-                del self.__pydantic_extra__[item]
-            else:
-                try:
-                    object.__delattr__(self, item)
-                except AttributeError:
-                    raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
-
-    def _check_frozen(self, name: str, value: Any) -> None:
-        if self.model_config.get('frozen', None):
-            typ = 'frozen_instance'
-        elif getattr(self.model_fields.get(name), 'frozen', False):
-            typ = 'frozen_field'
-        else:
-            return
-        error: pydantic_core.InitErrorDetails = {
-            'type': typ,
-            'loc': (name,),
-            'input': value,
-        }
-        raise pydantic_core.ValidationError.from_exception_data(self.__class__.__name__, [error])
-
-    def __getstate__(self) -> dict[Any, Any]:
-        private = self.__pydantic_private__
-        if private:
-            private = {k: v for k, v in private.items() if v is not PydanticUndefined}
-        return {
-            '__dict__': self.__dict__,
-            '__pydantic_extra__': self.__pydantic_extra__,
-            '__pydantic_fields_set__': self.__pydantic_fields_set__,
-            '__pydantic_private__': private,
-        }
-
-    def __setstate__(self, state: dict[Any, Any]) -> None:
-        _object_setattr(self, '__pydantic_fields_set__', state.get('__pydantic_fields_set__', {}))
-        _object_setattr(self, '__pydantic_extra__', state.get('__pydantic_extra__', {}))
-        _object_setattr(self, '__pydantic_private__', state.get('__pydantic_private__', {}))
-        _object_setattr(self, '__dict__', state.get('__dict__', {}))
-
-    if not TYPE_CHECKING:
-
-        def __eq__(self, other: Any) -> bool:
-            if isinstance(other, BaseModel):
-                # When comparing instances of generic types for equality, as long as all field values are equal,
-                # only require their generic origin types to be equal, rather than exact type equality.
-                # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1).
-                self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__
-                other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__
-
-                # Perform common checks first
-                if not (
-                    self_type == other_type
-                    and getattr(self, '__pydantic_private__', None) == getattr(other, '__pydantic_private__', None)
-                    and self.__pydantic_extra__ == other.__pydantic_extra__
-                ):
-                    return False
-
-                # We only want to compare pydantic fields but ignoring fields is costly.
-                # We'll perform a fast check first, and fallback only when needed
-                # See GH-7444 and GH-7825 for rationale and a performance benchmark
-
-                # First, do the fast (and sometimes faulty) __dict__ comparison
-                if self.__dict__ == other.__dict__:
-                    # If the check above passes, then pydantic fields are equal, we can return early
-                    return True
-
-                # We don't want to trigger unnecessary costly filtering of __dict__ on all unequal objects, so we return
-                # early if there are no keys to ignore (we would just return False later on anyway)
-                model_fields = type(self).model_fields.keys()
-                if self.__dict__.keys() <= model_fields and other.__dict__.keys() <= model_fields:
-                    return False
-
-                # If we reach here, there are non-pydantic-fields keys, mapped to unequal values, that we need to ignore
-                # Resort to costly filtering of the __dict__ objects
-                # We use operator.itemgetter because it is much faster than dict comprehensions
-                # NOTE: Contrary to standard python class and instances, when the Model class has a default value for an
-                # attribute and the model instance doesn't have a corresponding attribute, accessing the missing attribute
-                # raises an error in BaseModel.__getattr__ instead of returning the class attribute
-                # So we can use operator.itemgetter() instead of operator.attrgetter()
-                getter = operator.itemgetter(*model_fields) if model_fields else lambda _: _utils._SENTINEL
-                try:
-                    return getter(self.__dict__) == getter(other.__dict__)
-                except KeyError:
-                    # In rare cases (such as when using the deprecated BaseModel.copy() method),
-                    # the __dict__ may not contain all model fields, which is how we can get here.
-                    # getter(self.__dict__) is much faster than any 'safe' method that accounts
-                    # for missing keys, and wrapping it in a `try` doesn't slow things down much
-                    # in the common case.
-                    self_fields_proxy = _utils.SafeGetItemProxy(self.__dict__)
-                    other_fields_proxy = _utils.SafeGetItemProxy(other.__dict__)
-                    return getter(self_fields_proxy) == getter(other_fields_proxy)
-
-            # other instance is not a BaseModel
-            else:
-                return NotImplemented  # delegate to the other item in the comparison
-
-    if TYPE_CHECKING:
-        # We put `__init_subclass__` in a TYPE_CHECKING block because, even though we want the type-checking benefits
-        # described in the signature of `__init_subclass__` below, we don't want to modify the default behavior of
-        # subclass initialization.
-
-        def __init_subclass__(cls, **kwargs: Unpack[ConfigDict]):
-            """This signature is included purely to help type-checkers check arguments to class declaration, which
-            provides a way to conveniently set model_config key/value pairs.
-
-            ```py
-            from pydantic import BaseModel
-
-            class MyModel(BaseModel, extra='allow'): ...
-            ```
-
-            However, this may be deceiving, since the _actual_ calls to `__init_subclass__` will not receive any
-            of the config arguments, and will only receive any keyword arguments passed during class initialization
-            that are _not_ expected keys in ConfigDict. (This is due to the way `ModelMetaclass.__new__` works.)
-
-            Args:
-                **kwargs: Keyword arguments passed to the class definition, which set model_config
-
-            Note:
-                You may want to override `__pydantic_init_subclass__` instead, which behaves similarly but is called
-                *after* the class is fully initialized.
-            """
-
-    def __iter__(self) -> TupleGenerator:
-        """So `dict(model)` works."""
-        yield from [(k, v) for (k, v) in self.__dict__.items() if not k.startswith('_')]
-        extra = self.__pydantic_extra__
-        if extra:
-            yield from extra.items()
-
-    def __repr__(self) -> str:
-        return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
-
-    def __repr_args__(self) -> _repr.ReprArgs:
-        for k, v in self.__dict__.items():
-            field = self.model_fields.get(k)
-            if field and field.repr:
-                yield k, v
-
-        # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.
-        # This can happen if a `ValidationError` is raised during initialization and the instance's
-        # repr is generated as part of the exception handling. Therefore, we use `getattr` here
-        # with a fallback, even though the type hints indicate the attribute will always be present.
-        try:
-            pydantic_extra = object.__getattribute__(self, '__pydantic_extra__')
-        except AttributeError:
-            pydantic_extra = None
-
-        if pydantic_extra is not None:
-            yield from ((k, v) for k, v in pydantic_extra.items())
-        yield from ((k, getattr(self, k)) for k, v in self.model_computed_fields.items() if v.repr)
-
-    # take logic from `_repr.Representation` without the side effects of inheritance, see #5740
-    __repr_name__ = _repr.Representation.__repr_name__
-    __repr_str__ = _repr.Representation.__repr_str__
-    __pretty__ = _repr.Representation.__pretty__
-    __rich_repr__ = _repr.Representation.__rich_repr__
-
-    def __str__(self) -> str:
-        return self.__repr_str__(' ')
-
-    # ##### Deprecated methods from v1 #####
-    @property
-    @typing_extensions.deprecated(
-        'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=None
-    )
-    def __fields__(self) -> dict[str, FieldInfo]:
-        warnings.warn(
-            'The `__fields__` attribute is deprecated, use `model_fields` instead.', category=PydanticDeprecatedSince20
-        )
-        return self.model_fields
-
-    @property
-    @typing_extensions.deprecated(
-        'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.',
-        category=None,
-    )
-    def __fields_set__(self) -> set[str]:
-        warnings.warn(
-            'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.',
-            category=PydanticDeprecatedSince20,
-        )
-        return self.__pydantic_fields_set__
-
-    @typing_extensions.deprecated('The `dict` method is deprecated; use `model_dump` instead.', category=None)
-    def dict(  # noqa: D102
-        self,
-        *,
-        include: IncEx | None = None,
-        exclude: IncEx | None = None,
-        by_alias: bool = False,
-        exclude_unset: bool = False,
-        exclude_defaults: bool = False,
-        exclude_none: bool = False,
-    ) -> Dict[str, Any]:  # noqa UP006
-        warnings.warn('The `dict` method is deprecated; use `model_dump` instead.', category=PydanticDeprecatedSince20)
-        return self.model_dump(
-            include=include,
-            exclude=exclude,
-            by_alias=by_alias,
-            exclude_unset=exclude_unset,
-            exclude_defaults=exclude_defaults,
-            exclude_none=exclude_none,
-        )
-
-    @typing_extensions.deprecated('The `json` method is deprecated; use `model_dump_json` instead.', category=None)
-    def json(  # noqa: D102
-        self,
-        *,
-        include: IncEx | None = None,
-        exclude: IncEx | None = None,
-        by_alias: bool = False,
-        exclude_unset: bool = False,
-        exclude_defaults: bool = False,
-        exclude_none: bool = False,
-        encoder: Callable[[Any], Any] | None = PydanticUndefined,  # type: ignore[assignment]
-        models_as_dict: bool = PydanticUndefined,  # type: ignore[assignment]
-        **dumps_kwargs: Any,
-    ) -> str:
-        warnings.warn(
-            'The `json` method is deprecated; use `model_dump_json` instead.', category=PydanticDeprecatedSince20
-        )
-        if encoder is not PydanticUndefined:
-            raise TypeError('The `encoder` argument is no longer supported; use field serializers instead.')
-        if models_as_dict is not PydanticUndefined:
-            raise TypeError('The `models_as_dict` argument is no longer supported; use a model serializer instead.')
-        if dumps_kwargs:
-            raise TypeError('`dumps_kwargs` keyword arguments are no longer supported.')
-        return self.model_dump_json(
-            include=include,
-            exclude=exclude,
-            by_alias=by_alias,
-            exclude_unset=exclude_unset,
-            exclude_defaults=exclude_defaults,
-            exclude_none=exclude_none,
-        )
-
-    @classmethod
-    @typing_extensions.deprecated('The `parse_obj` method is deprecated; use `model_validate` instead.', category=None)
-    def parse_obj(cls, obj: Any) -> Self:  # noqa: D102
-        warnings.warn(
-            'The `parse_obj` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20
-        )
-        return cls.model_validate(obj)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, '
-        'otherwise load the data then use `model_validate` instead.',
-        category=None,
-    )
-    def parse_raw(  # noqa: D102
-        cls,
-        b: str | bytes,
-        *,
-        content_type: str | None = None,
-        encoding: str = 'utf8',
-        proto: DeprecatedParseProtocol | None = None,
-        allow_pickle: bool = False,
-    ) -> Self:  # pragma: no cover
-        warnings.warn(
-            'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, '
-            'otherwise load the data then use `model_validate` instead.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import parse
-
-        try:
-            obj = parse.load_str_bytes(
-                b,
-                proto=proto,
-                content_type=content_type,
-                encoding=encoding,
-                allow_pickle=allow_pickle,
-            )
-        except (ValueError, TypeError) as exc:
-            import json
-
-            # try to match V1
-            if isinstance(exc, UnicodeDecodeError):
-                type_str = 'value_error.unicodedecode'
-            elif isinstance(exc, json.JSONDecodeError):
-                type_str = 'value_error.jsondecode'
-            elif isinstance(exc, ValueError):
-                type_str = 'value_error'
-            else:
-                type_str = 'type_error'
-
-            # ctx is missing here, but since we've added `input` to the error, we're not pretending it's the same
-            error: pydantic_core.InitErrorDetails = {
-                # The type: ignore on the next line is to ignore the requirement of LiteralString
-                'type': pydantic_core.PydanticCustomError(type_str, str(exc)),  # type: ignore
-                'loc': ('__root__',),
-                'input': b,
-            }
-            raise pydantic_core.ValidationError.from_exception_data(cls.__name__, [error])
-        return cls.model_validate(obj)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The `parse_file` method is deprecated; load the data from file, then if your data is JSON '
-        'use `model_validate_json`, otherwise `model_validate` instead.',
-        category=None,
-    )
-    def parse_file(  # noqa: D102
-        cls,
-        path: str | Path,
-        *,
-        content_type: str | None = None,
-        encoding: str = 'utf8',
-        proto: DeprecatedParseProtocol | None = None,
-        allow_pickle: bool = False,
-    ) -> Self:
-        warnings.warn(
-            'The `parse_file` method is deprecated; load the data from file, then if your data is JSON '
-            'use `model_validate_json`, otherwise `model_validate` instead.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import parse
-
-        obj = parse.load_file(
-            path,
-            proto=proto,
-            content_type=content_type,
-            encoding=encoding,
-            allow_pickle=allow_pickle,
-        )
-        return cls.parse_obj(obj)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The `from_orm` method is deprecated; set '
-        "`model_config['from_attributes']=True` and use `model_validate` instead.",
-        category=None,
-    )
-    def from_orm(cls, obj: Any) -> Self:  # noqa: D102
-        warnings.warn(
-            'The `from_orm` method is deprecated; set '
-            "`model_config['from_attributes']=True` and use `model_validate` instead.",
-            category=PydanticDeprecatedSince20,
-        )
-        if not cls.model_config.get('from_attributes', None):
-            raise PydanticUserError(
-                'You must set the config attribute `from_attributes=True` to use from_orm', code=None
-            )
-        return cls.model_validate(obj)
-
-    @classmethod
-    @typing_extensions.deprecated('The `construct` method is deprecated; use `model_construct` instead.', category=None)
-    def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:  # noqa: D102
-        warnings.warn(
-            'The `construct` method is deprecated; use `model_construct` instead.', category=PydanticDeprecatedSince20
-        )
-        return cls.model_construct(_fields_set=_fields_set, **values)
-
-    @typing_extensions.deprecated(
-        'The `copy` method is deprecated; use `model_copy` instead. '
-        'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
-        category=None,
-    )
-    def copy(
-        self,
-        *,
-        include: AbstractSetIntStr | MappingIntStrAny | None = None,
-        exclude: AbstractSetIntStr | MappingIntStrAny | None = None,
-        update: Dict[str, Any] | None = None,  # noqa UP006
-        deep: bool = False,
-    ) -> Self:  # pragma: no cover
-        """Returns a copy of the model.
-
-        !!! warning "Deprecated"
-            This method is now deprecated; use `model_copy` instead.
-
-        If you need `include` or `exclude`, use:
-
-        ```py
-        data = self.model_dump(include=include, exclude=exclude, round_trip=True)
-        data = {**data, **(update or {})}
-        copied = self.model_validate(data)
-        ```
-
-        Args:
-            include: Optional set or mapping specifying which fields to include in the copied model.
-            exclude: Optional set or mapping specifying which fields to exclude in the copied model.
-            update: Optional dictionary of field-value pairs to override field values in the copied model.
-            deep: If True, the values of fields that are Pydantic models will be deep-copied.
-
-        Returns:
-            A copy of the model with included, excluded and updated fields as specified.
-        """
-        warnings.warn(
-            'The `copy` method is deprecated; use `model_copy` instead. '
-            'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import copy_internals
-
-        values = dict(
-            copy_internals._iter(
-                self, to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False
-            ),
-            **(update or {}),
-        )
-        if self.__pydantic_private__ is None:
-            private = None
-        else:
-            private = {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}
-
-        if self.__pydantic_extra__ is None:
-            extra: dict[str, Any] | None = None
-        else:
-            extra = self.__pydantic_extra__.copy()
-            for k in list(self.__pydantic_extra__):
-                if k not in values:  # k was in the exclude
-                    extra.pop(k)
-            for k in list(values):
-                if k in self.__pydantic_extra__:  # k must have come from extra
-                    extra[k] = values.pop(k)
-
-        # new `__pydantic_fields_set__` can have unset optional fields with a set value in `update` kwarg
-        if update:
-            fields_set = self.__pydantic_fields_set__ | update.keys()
-        else:
-            fields_set = set(self.__pydantic_fields_set__)
-
-        # removing excluded fields from `__pydantic_fields_set__`
-        if exclude:
-            fields_set -= set(exclude)
-
-        return copy_internals._copy_and_set_values(self, values, fields_set, extra, private, deep=deep)
-
-    @classmethod
-    @typing_extensions.deprecated('The `schema` method is deprecated; use `model_json_schema` instead.', category=None)
-    def schema(  # noqa: D102
-        cls, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE
-    ) -> Dict[str, Any]:  # noqa UP006
-        warnings.warn(
-            'The `schema` method is deprecated; use `model_json_schema` instead.', category=PydanticDeprecatedSince20
-        )
-        return cls.model_json_schema(by_alias=by_alias, ref_template=ref_template)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.',
-        category=None,
-    )
-    def schema_json(  # noqa: D102
-        cls, *, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE, **dumps_kwargs: Any
-    ) -> str:  # pragma: no cover
-        warnings.warn(
-            'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.',
-            category=PydanticDeprecatedSince20,
-        )
-        import json
-
-        from .deprecated.json import pydantic_encoder
-
-        return json.dumps(
-            cls.model_json_schema(by_alias=by_alias, ref_template=ref_template),
-            default=pydantic_encoder,
-            **dumps_kwargs,
-        )
-
-    @classmethod
-    @typing_extensions.deprecated('The `validate` method is deprecated; use `model_validate` instead.', category=None)
-    def validate(cls, value: Any) -> Self:  # noqa: D102
-        warnings.warn(
-            'The `validate` method is deprecated; use `model_validate` instead.', category=PydanticDeprecatedSince20
-        )
-        return cls.model_validate(value)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.',
-        category=None,
-    )
-    def update_forward_refs(cls, **localns: Any) -> None:  # noqa: D102
-        warnings.warn(
-            'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.',
-            category=PydanticDeprecatedSince20,
-        )
-        if localns:  # pragma: no cover
-            raise TypeError('`localns` arguments are not longer accepted.')
-        cls.model_rebuild(force=True)
-
-    @typing_extensions.deprecated(
-        'The private method `_iter` will be removed and should no longer be used.', category=None
-    )
-    def _iter(self, *args: Any, **kwargs: Any) -> Any:
-        warnings.warn(
-            'The private method `_iter` will be removed and should no longer be used.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import copy_internals
-
-        return copy_internals._iter(self, *args, **kwargs)
-
-    @typing_extensions.deprecated(
-        'The private method `_copy_and_set_values` will be removed and should no longer be used.',
-        category=None,
-    )
-    def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any:
-        warnings.warn(
-            'The private method `_copy_and_set_values` will be removed and should no longer be used.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import copy_internals
-
-        return copy_internals._copy_and_set_values(self, *args, **kwargs)
-
-    @classmethod
-    @typing_extensions.deprecated(
-        'The private method `_get_value` will be removed and should no longer be used.',
-        category=None,
-    )
-    def _get_value(cls, *args: Any, **kwargs: Any) -> Any:
-        warnings.warn(
-            'The private method `_get_value` will be removed and should no longer be used.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import copy_internals
-
-        return copy_internals._get_value(cls, *args, **kwargs)
-
-    @typing_extensions.deprecated(
-        'The private method `_calculate_keys` will be removed and should no longer be used.',
-        category=None,
-    )
-    def _calculate_keys(self, *args: Any, **kwargs: Any) -> Any:
-        warnings.warn(
-            'The private method `_calculate_keys` will be removed and should no longer be used.',
-            category=PydanticDeprecatedSince20,
-        )
-        from .deprecated import copy_internals
-
-        return copy_internals._calculate_keys(self, *args, **kwargs)
-
-
-ModelT = TypeVar('ModelT', bound=BaseModel)
-
-
-@overload
-def create_model(
-    model_name: str,
-    /,
-    *,
-    __config__: ConfigDict | None = None,
-    __doc__: str | None = None,
-    __base__: None = None,
-    __module__: str = __name__,
-    __validators__: dict[str, Callable[..., Any]] | None = None,
-    __cls_kwargs__: dict[str, Any] | None = None,
-    **field_definitions: Any,
-) -> type[BaseModel]: ...
-
-
-@overload
-def create_model(
-    model_name: str,
-    /,
-    *,
-    __config__: ConfigDict | None = None,
-    __doc__: str | None = None,
-    __base__: type[ModelT] | tuple[type[ModelT], ...],
-    __module__: str = __name__,
-    __validators__: dict[str, Callable[..., Any]] | None = None,
-    __cls_kwargs__: dict[str, Any] | None = None,
-    **field_definitions: Any,
-) -> type[ModelT]: ...
-
-
-def create_model(  # noqa: C901
-    model_name: str,
-    /,
-    *,
-    __config__: ConfigDict | None = None,
-    __doc__: str | None = None,
-    __base__: type[ModelT] | tuple[type[ModelT], ...] | None = None,
-    __module__: str | None = None,
-    __validators__: dict[str, Callable[..., Any]] | None = None,
-    __cls_kwargs__: dict[str, Any] | None = None,
-    __slots__: tuple[str, ...] | None = None,
-    **field_definitions: Any,
-) -> type[ModelT]:
-    """Usage docs: https://docs.pydantic.dev/2.9/concepts/models/#dynamic-model-creation
-
-    Dynamically creates and returns a new Pydantic model, in other words, `create_model` dynamically creates a
-    subclass of [`BaseModel`][pydantic.BaseModel].
-
-    Args:
-        model_name: The name of the newly created model.
-        __config__: The configuration of the new model.
-        __doc__: The docstring of the new model.
-        __base__: The base class or classes for the new model.
-        __module__: The name of the module that the model belongs to;
-            if `None`, the value is taken from `sys._getframe(1)`
-        __validators__: A dictionary of methods that validate fields. The keys are the names of the validation methods to
-            be added to the model, and the values are the validation methods themselves. You can read more about functional
-            validators [here](https://docs.pydantic.dev/2.9/concepts/validators/#field-validators).
-        __cls_kwargs__: A dictionary of keyword arguments for class creation, such as `metaclass`.
-        __slots__: Deprecated. Should not be passed to `create_model`.
-        **field_definitions: Attributes of the new model. They should be passed in the format:
-            `<name>=(<type>, <default value>)`, `<name>=(<type>, <FieldInfo>)`, or `typing.Annotated[<type>, <FieldInfo>]`.
-            Any additional metadata in `typing.Annotated[<type>, <FieldInfo>, ...]` will be ignored.
-
-    Returns:
-        The new [model][pydantic.BaseModel].
-
-    Raises:
-        PydanticUserError: If `__base__` and `__config__` are both passed.
-    """
-    if __slots__ is not None:
-        # __slots__ will be ignored from here on
-        warnings.warn('__slots__ should not be passed to create_model', RuntimeWarning)
-
-    if __base__ is not None:
-        if __config__ is not None:
-            raise PydanticUserError(
-                'to avoid confusion `__config__` and `__base__` cannot be used together',
-                code='create-model-config-base',
-            )
-        if not isinstance(__base__, tuple):
-            __base__ = (__base__,)
-    else:
-        __base__ = (cast('type[ModelT]', BaseModel),)
-
-    __cls_kwargs__ = __cls_kwargs__ or {}
-
-    fields = {}
-    annotations = {}
-
-    for f_name, f_def in field_definitions.items():
-        if not _fields.is_valid_field_name(f_name):
-            warnings.warn(f'fields may not start with an underscore, ignoring "{f_name}"', RuntimeWarning)
-        if isinstance(f_def, tuple):
-            f_def = cast('tuple[str, Any]', f_def)
-            try:
-                f_annotation, f_value = f_def
-            except ValueError as e:
-                raise PydanticUserError(
-                    'Field definitions should be a `(<type>, <default>)`.',
-                    code='create-model-field-definitions',
-                ) from e
-
-        elif _typing_extra.is_annotated(f_def):
-            (f_annotation, f_value, *_) = typing_extensions.get_args(
-                f_def
-            )  # first two input are expected from Annotated, refer to https://docs.python.org/3/library/typing.html#typing.Annotated
-            FieldInfo = _import_utils.import_cached_field_info()
-
-            if not isinstance(f_value, FieldInfo):
-                raise PydanticUserError(
-                    'Field definitions should be a Annotated[<type>, <FieldInfo>]',
-                    code='create-model-field-definitions',
-                )
-
-        else:
-            f_annotation, f_value = None, f_def
-
-        if f_annotation:
-            annotations[f_name] = f_annotation
-        fields[f_name] = f_value
-
-    if __module__ is None:
-        f = sys._getframe(1)
-        __module__ = f.f_globals['__name__']
-
-    namespace: dict[str, Any] = {'__annotations__': annotations, '__module__': __module__}
-    if __doc__:
-        namespace.update({'__doc__': __doc__})
-    if __validators__:
-        namespace.update(__validators__)
-    namespace.update(fields)
-    if __config__:
-        namespace['model_config'] = _config.ConfigWrapper(__config__).config_dict
-    resolved_bases = types.resolve_bases(__base__)
-    meta, ns, kwds = types.prepare_class(model_name, resolved_bases, kwds=__cls_kwargs__)
-    if resolved_bases is not __base__:
-        ns['__orig_bases__'] = __base__
-    namespace.update(ns)
-
-    return meta(
-        model_name,
-        resolved_bases,
-        namespace,
-        __pydantic_reset_parent_namespace__=False,
-        _create_model_module=__module__,
-        **kwds,
-    )
-
-
-__getattr__ = getattr_migration(__name__)
-
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_modules/ragas/dataset_schema.html b/docs/_build/html/_modules/ragas/dataset_schema.html deleted file mode 100644 index 3d173bca..00000000 --- a/docs/_build/html/_modules/ragas/dataset_schema.html +++ /dev/null @@ -1,1014 +0,0 @@ - - - - - - ragas.dataset_schema — NotDiamond 0.3.43 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for ragas.dataset_schema

-from __future__ import annotations
-
-import json
-import random
-import typing as t
-from abc import ABC, abstractmethod
-from collections import defaultdict
-from dataclasses import dataclass, field
-from uuid import UUID
-
-import numpy as np
-import requests
-from datasets import Dataset as HFDataset
-from pydantic import BaseModel, field_validator
-
-from ragas._version import __version__
-from ragas.callbacks import ChainRunEncoder, parse_run_traces
-from ragas.cost import CostCallbackHandler
-from ragas.exceptions import UploadException
-from ragas.messages import AIMessage, HumanMessage, ToolCall, ToolMessage
-from ragas.sdk import (
-    RAGAS_API_SOURCE,
-    build_evaluation_app_url,
-    check_api_response,
-    get_api_url,
-    get_app_token,
-    get_app_url,
-    upload_packet,
-)
-from ragas.utils import safe_nanmean
-
-if t.TYPE_CHECKING:
-    from pathlib import Path
-
-    from datasets import Dataset as HFDataset
-    from pandas import DataFrame as PandasDataframe
-
-    from ragas.callbacks import ChainRun
-    from ragas.cost import TokenUsage
-
-
-class BaseSample(BaseModel):
-    """
-    Base class for evaluation samples.
-    """
-
-    def to_dict(self) -> t.Dict:
-        """
-        Get the dictionary representation of the sample without attributes that are None.
-        """
-        return self.model_dump(exclude_none=True)
-
-    def get_features(self) -> t.List[str]:
-        """
-        Get the features of the sample that are not None.
-        """
-        return list(self.to_dict().keys())
-
-    def to_string(self) -> str:
-        """
-        Get the string representation of the sample.
-        """
-        sample_dict = self.to_dict()
-        return "".join(f"\n{key}:\n\t{val}\n" for key, val in sample_dict.items())
-
-
-class SingleTurnSample(BaseSample):
-    """
-    Represents evaluation samples for single-turn interactions.
-
-    Attributes
-    ----------
-    user_input : Optional[str]
-        The input query from the user.
-    retrieved_contexts : Optional[List[str]]
-        List of contexts retrieved for the query.
-    reference_contexts : Optional[List[str]]
-        List of reference contexts for the query.
-    response : Optional[str]
-        The generated response for the query.
-    multi_responses : Optional[List[str]]
-        List of multiple responses generated for the query.
-    reference : Optional[str]
-        The reference answer for the query.
-    rubric : Optional[Dict[str, str]]
-        Evaluation rubric for the sample.
-    """
-
-    user_input: t.Optional[str] = None
-    retrieved_contexts: t.Optional[t.List[str]] = None
-    reference_contexts: t.Optional[t.List[str]] = None
-    response: t.Optional[str] = None
-    multi_responses: t.Optional[t.List[str]] = None
-    reference: t.Optional[str] = None
-    rubrics: t.Optional[t.Dict[str, str]] = None
-
-
-class MultiTurnSample(BaseSample):
-    """
-    Represents evaluation samples for multi-turn interactions.
-
-    Attributes
-    ----------
-    user_input : List[Union[HumanMessage, AIMessage, ToolMessage]]
-        A list of messages representing the conversation turns.
-    reference : Optional[str], optional
-        The reference answer or expected outcome for the conversation.
-    reference_tool_calls : Optional[List[ToolCall]], optional
-        A list of expected tool calls for the conversation.
-    rubrics : Optional[Dict[str, str]], optional
-        Evaluation rubrics for the conversation.
-    reference_topics : Optional[List[str]], optional
-        A list of reference topics for the conversation.
-    """
-
-    user_input: t.List[t.Union[HumanMessage, AIMessage, ToolMessage]]
-    reference: t.Optional[str] = None
-    reference_tool_calls: t.Optional[t.List[ToolCall]] = None
-    rubrics: t.Optional[t.Dict[str, str]] = None
-    reference_topics: t.Optional[t.List[str]] = None
-
-    @field_validator("user_input")
-    @classmethod
-    def validate_user_input(
-        cls,
-        messages: t.List[t.Union[HumanMessage, AIMessage, ToolMessage]],
-    ) -> t.List[t.Union[HumanMessage, AIMessage, ToolMessage]]:
-        """Validates the user input messages."""
-        if not (
-            isinstance(m, (HumanMessage, AIMessage, ToolMessage)) for m in messages
-        ):
-            raise ValueError(
-                "All inputs must be instances of HumanMessage, AIMessage, or ToolMessage."
-            )
-
-        prev_message = None
-        for m in messages:
-            if isinstance(m, ToolMessage):
-                if not isinstance(prev_message, AIMessage):
-                    raise ValueError(
-                        "ToolMessage instances must be preceded by an AIMessage instance."
-                    )
-                if prev_message.tool_calls is None:
-                    raise ValueError(
-                        f"ToolMessage instances must be preceded by an AIMessage instance with tool_calls. Got {prev_message}"
-                    )
-            prev_message = m
-
-        return messages
-
-    def to_messages(self):
-        """Converts the user input messages to a list of dictionaries."""
-        return [m.model_dump() for m in self.user_input]
-
-    def pretty_repr(self):
-        """Returns a pretty string representation of the conversation."""
-        lines = []
-        for m in self.user_input:
-            lines.append(m.pretty_repr())
-
-        return "\n".join(lines)
-
-
-Sample = t.TypeVar("Sample", bound=BaseSample)
-T = t.TypeVar("T", bound="RagasDataset")
-
-
-@dataclass
-class RagasDataset(ABC, t.Generic[Sample]):
-    samples: t.List[Sample]
-
-    def __post_init__(self):
-        self.samples = self.validate_samples(self.samples)
-
-    @abstractmethod
-    def to_list(self) -> t.List[t.Dict]:
-        """Converts the samples to a list of dictionaries."""
-        pass
-
-    @classmethod
-    @abstractmethod
-    def from_list(cls: t.Type[T], data: t.List[t.Dict]) -> T:
-        """Creates an RagasDataset from a list of dictionaries."""
-        pass
-
-    def validate_samples(self, samples: t.List[Sample]) -> t.List[Sample]:
-        """Validates that all samples are of the same type."""
-        if len(samples) == 0:
-            return samples
-
-        first_sample_type = type(samples[0])
-        for i, sample in enumerate(samples):
-            if not isinstance(sample, first_sample_type):
-                raise ValueError(
-                    f"Sample at index {i} is of type {type(sample)}, expected {first_sample_type}"
-                )
-
-        return samples
-
-    def get_sample_type(self) -> t.Type[Sample]:
-        """Returns the type of the samples in the dataset."""
-        return type(self.samples[0])
-
-    def to_hf_dataset(self) -> HFDataset:
-        """Converts the dataset to a Hugging Face Dataset."""
-        try:
-            from datasets import Dataset as HFDataset
-        except ImportError:
-            raise ImportError(
-                "datasets is not installed. Please install it to use this function."
-            )
-
-        return HFDataset.from_list(self.to_list())
-
-    @classmethod
-    def from_hf_dataset(cls: t.Type[T], dataset: HFDataset) -> T:
-        """Creates an EvaluationDataset from a Hugging Face Dataset."""
-        return cls.from_list(dataset.to_list())
-
-    def to_pandas(self) -> PandasDataframe:
-        """Converts the dataset to a pandas DataFrame."""
-        try:
-            import pandas as pd
-        except ImportError:
-            raise ImportError(
-                "pandas is not installed. Please install it to use this function."
-            )
-
-        data = self.to_list()
-        return pd.DataFrame(data)
-
-    @classmethod
-    def from_pandas(cls, dataframe: PandasDataframe):
-        """Creates an EvaluationDataset from a pandas DataFrame."""
-        return cls.from_list(dataframe.to_dict(orient="records"))
-
-    def features(self):
-        """Returns the features of the samples."""
-        return self.samples[0].get_features()
-
-    @classmethod
-    def from_dict(cls: t.Type[T], mapping: t.Dict) -> T:
-        """Creates an EvaluationDataset from a dictionary."""
-        samples = []
-        if all(
-            "user_input" in item and isinstance(mapping[0]["user_input"], list)
-            for item in mapping
-        ):
-            samples.extend(MultiTurnSample(**sample) for sample in mapping)
-        else:
-            samples.extend(SingleTurnSample(**sample) for sample in mapping)
-        return cls(samples=samples)
-
-    def to_csv(self, path: t.Union[str, Path]):
-        """Converts the dataset to a CSV file."""
-        import csv
-
-        data = self.to_list()
-        if not data:
-            return
-
-        fieldnames = data[0].keys()
-
-        with open(path, "w", newline="") as csvfile:
-            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
-            writer.writeheader()
-            for row in data:
-                writer.writerow(row)
-
-    def to_jsonl(self, path: t.Union[str, Path]):
-        """Converts the dataset to a JSONL file."""
-        with open(path, "w") as jsonlfile:
-            for sample in self.to_list():
-                jsonlfile.write(json.dumps(sample, ensure_ascii=False) + "\n")
-
-    @classmethod
-    def from_jsonl(cls: t.Type[T], path: t.Union[str, Path]) -> T:
-        """Creates an EvaluationDataset from a JSONL file."""
-        with open(path, "r") as jsonlfile:
-            data = [json.loads(line) for line in jsonlfile]
-        return cls.from_list(data)
-
-    def __iter__(self) -> t.Iterator[Sample]:  # type: ignore
-        return iter(self.samples)
-
-    def __len__(self) -> int:
-        return len(self.samples)
-
-    def __str__(self) -> str:
-        return f"EvaluationDataset(features={self.features()}, len={len(self.samples)})"
-
-    def __repr__(self) -> str:
-        return self.__str__()
-
-
-SingleTurnSampleOrMultiTurnSample = t.Union[SingleTurnSample, MultiTurnSample]
-
-
-@dataclass
-class EvaluationDataset(RagasDataset[SingleTurnSampleOrMultiTurnSample]):
-    """
-    Represents a dataset of evaluation samples.
-
-    Attributes
-    ----------
-    samples : List[BaseSample]
-        A list of evaluation samples.
-
-    Methods
-    -------
-    validate_samples(samples)
-        Validates that all samples are of the same type.
-    get_sample_type()
-        Returns the type of the samples in the dataset.
-    to_hf_dataset()
-        Converts the dataset to a Hugging Face Dataset.
-    to_pandas()
-        Converts the dataset to a pandas DataFrame.
-    features()
-        Returns the features of the samples.
-    from_list(mapping)
-        Creates an EvaluationDataset from a list of dictionaries.
-    from_dict(mapping)
-        Creates an EvaluationDataset from a dictionary.
-    to_csv(path)
-        Converts the dataset to a CSV file.
-    to_jsonl(path)
-        Converts the dataset to a JSONL file.
-    from_jsonl(path)
-        Creates an EvaluationDataset from a JSONL file.
-    """
-
-    @t.overload
-    def __getitem__(self, idx: int) -> SingleTurnSampleOrMultiTurnSample: ...
-
-    @t.overload
-    def __getitem__(self, idx: slice) -> "EvaluationDataset": ...
-
-    def __getitem__(
-        self, idx: t.Union[int, slice]
-    ) -> t.Union[SingleTurnSampleOrMultiTurnSample, "EvaluationDataset"]:
-        if isinstance(idx, int):
-            return self.samples[idx]
-        elif isinstance(idx, slice):
-            return type(self)(samples=self.samples[idx])
-        else:
-            raise TypeError("Index must be int or slice")
-
-    def is_multi_turn(self) -> bool:
-        return self.get_sample_type() == MultiTurnSample
-
-    def to_list(self) -> t.List[t.Dict]:
-        rows = [sample.to_dict() for sample in self.samples]
-
-        if self.get_sample_type() == MultiTurnSample:
-            for sample in rows:
-                for item in sample["user_input"]:
-                    if not isinstance(item["content"], str):
-                        item["content"] = json.dumps(
-                            item["content"], ensure_ascii=False
-                        )
-
-        return rows
-
-    @classmethod
-    def from_list(cls, data: t.List[t.Dict]) -> EvaluationDataset:
-        samples = []
-        if all(
-            "user_input" in item and isinstance(data[0]["user_input"], list)
-            for item in data
-        ):
-            samples.extend(MultiTurnSample(**sample) for sample in data)
-        else:
-            samples.extend(SingleTurnSample(**sample) for sample in data)
-        return cls(samples=samples)
-
-    def __repr__(self) -> str:
-        return f"EvaluationDataset(features={self.features()}, len={len(self.samples)})"
-
-
-@dataclass
-class EvaluationResult:
-    """
-    A class to store and process the results of the evaluation.
-
-    Attributes
-    ----------
-    scores : Dataset
-        The dataset containing the scores of the evaluation.
-    dataset : Dataset, optional
-        The original dataset used for the evaluation. Default is None.
-    binary_columns : list of str, optional
-        List of columns that are binary metrics. Default is an empty list.
-    cost_cb : CostCallbackHandler, optional
-        The callback handler for cost computation. Default is None.
-    """
-
-    scores: t.List[t.Dict[str, t.Any]]
-    dataset: EvaluationDataset
-    binary_columns: t.List[str] = field(default_factory=list)
-    cost_cb: t.Optional[CostCallbackHandler] = None
-    traces: t.List[t.Dict[str, t.Any]] = field(default_factory=list)
-    ragas_traces: t.Dict[str, ChainRun] = field(default_factory=dict, repr=False)
-    run_id: t.Optional[UUID] = None
-
-    def __post_init__(self):
-        # transform scores from list of dicts to dict of lists
-        self._scores_dict = {
-            k: [d[k] for d in self.scores] for k in self.scores[0].keys()
-        }
-
-        values = []
-        self._repr_dict = {}
-        for metric_name in self._scores_dict.keys():
-            value = safe_nanmean(self._scores_dict[metric_name])
-            self._repr_dict[metric_name] = value
-            if metric_name not in self.binary_columns:
-                value = t.cast(float, value)
-                values.append(value + 1e-10)
-
-        # parse the traces
-        run_id = str(self.run_id) if self.run_id is not None else None
-        self.traces = parse_run_traces(self.ragas_traces, run_id)
-
-    def __repr__(self) -> str:
-        score_strs = [f"'{k}': {v:0.4f}" for k, v in self._repr_dict.items()]
-        return "{" + ", ".join(score_strs) + "}"
-
-    def __getitem__(self, key: str) -> t.List[float]:
-        return self._scores_dict[key]
-
-    def to_pandas(self, batch_size: int | None = None, batched: bool = False):
-        """
-        Convert the result to a pandas DataFrame.
-
-        Parameters
-        ----------
-        batch_size : int, optional
-            The batch size for conversion. Default is None.
-        batched : bool, optional
-            Whether to convert in batches. Default is False.
-
-        Returns
-        -------
-        pandas.DataFrame
-            The result as a pandas DataFrame.
-
-        Raises
-        ------
-        ValueError
-            If the dataset is not provided.
-        """
-        try:
-            import pandas as pd
-        except ImportError:
-            raise ImportError(
-                "pandas is not installed. Please install it to use this function."
-            )
-
-        if self.dataset is None:
-            raise ValueError("dataset is not provided for the results class")
-        assert len(self.scores) == len(self.dataset)
-        # convert both to pandas dataframes and concatenate
-        scores_df = pd.DataFrame(self.scores)
-        dataset_df = self.dataset.to_pandas()
-        return pd.concat([dataset_df, scores_df], axis=1)
-
-    def total_tokens(self) -> t.Union[t.List[TokenUsage], TokenUsage]:
-        """
-        Compute the total tokens used in the evaluation.
-
-        Returns
-        -------
-        list of TokenUsage or TokenUsage
-            The total tokens used.
-
-        Raises
-        ------
-        ValueError
-            If the cost callback handler is not provided.
-        """
-        if self.cost_cb is None:
-            raise ValueError(
-                "The evaluate() run was not configured for computing cost. Please provide a token_usage_parser function to evaluate() to compute cost."
-            )
-        return self.cost_cb.total_tokens()
-
-    def total_cost(
-        self,
-        cost_per_input_token: t.Optional[float] = None,
-        cost_per_output_token: t.Optional[float] = None,
-        per_model_costs: t.Dict[str, t.Tuple[float, float]] = {},
-    ) -> float:
-        """
-        Compute the total cost of the evaluation.
-
-        Parameters
-        ----------
-        cost_per_input_token : float, optional
-            The cost per input token. Default is None.
-        cost_per_output_token : float, optional
-            The cost per output token. Default is None.
-        per_model_costs : dict of str to tuple of float, optional
-            The per model costs. Default is an empty dictionary.
-
-        Returns
-        -------
-        float
-            The total cost of the evaluation.
-
-        Raises
-        ------
-        ValueError
-            If the cost callback handler is not provided.
-        """
-        if self.cost_cb is None:
-            raise ValueError(
-                "The evaluate() run was not configured for computing cost. Please provide a token_usage_parser function to evaluate() to compute cost."
-            )
-        return self.cost_cb.total_cost(
-            cost_per_input_token, cost_per_output_token, per_model_costs
-        )
-
-    def upload(
-        self,
-        verbose: bool = True,
-    ) -> str:
-        from datetime import datetime, timezone
-
-        timestamp = datetime.now(timezone.utc).isoformat()
-        root_trace = [
-            trace for trace in self.ragas_traces.values() if trace.parent_run_id is None
-        ][0]
-        packet = json.dumps(
-            {
-                "run_id": str(root_trace.run_id),
-                "created_at": timestamp,
-                "evaluation_run": [t.model_dump() for t in self.ragas_traces.values()],
-            },
-            cls=ChainRunEncoder,
-        )
-        response = upload_packet(
-            path="/alignment/evaluation",
-            data_json_string=packet,
-        )
-
-        # check status codes
-        app_url = get_app_url()
-        evaluation_app_url = build_evaluation_app_url(app_url, root_trace.run_id)
-        if response.status_code == 409:
-            # this evalution already exists
-            if verbose:
-                print(f"Evaluation run already exists. View at {evaluation_app_url}")
-            return evaluation_app_url
-        elif response.status_code != 200:
-            # any other error
-            raise UploadException(
-                status_code=response.status_code,
-                message=f"Failed to upload results: {response.text}",
-            )
-
-        if verbose:
-            print(f"Evaluation results uploaded! View at {evaluation_app_url}")
-        return evaluation_app_url
-
-
-class PromptAnnotation(BaseModel):
-    prompt_input: t.Dict[str, t.Any]
-    prompt_output: t.Dict[str, t.Any]
-    edited_output: t.Optional[t.Dict[str, t.Any]] = None
-
-    def __getitem__(self, key):
-        return getattr(self, key)
-
-
-class SampleAnnotation(BaseModel):
-    metric_input: t.Dict[str, t.Any]
-    metric_output: float
-    prompts: t.Dict[str, PromptAnnotation]
-    is_accepted: bool
-    target: t.Optional[float] = None
-
-    def __getitem__(self, key):
-        return getattr(self, key)
-
-
-class MetricAnnotation(BaseModel):
-    root: t.Dict[str, t.List[SampleAnnotation]]
-
-    def __getitem__(self, key):
-        return SingleMetricAnnotation(name=key, samples=self.root[key])
-
-    @classmethod
-    def _process_dataset(
-        cls, dataset: dict, metric_name: t.Optional[str]
-    ) -> "MetricAnnotation":
-        """
-        Process raw dataset into MetricAnnotation format
-
-        Parameters
-        ----------
-        dataset : dict
-            Raw dataset to process
-        metric_name : str, optional
-            Name of the specific metric to filter
-
-        Returns
-        -------
-        MetricAnnotation
-            Processed annotation data
-        """
-        if metric_name is not None and metric_name not in dataset:
-            raise ValueError(f"Split {metric_name} not found in the dataset.")
-
-        return cls(
-            root={
-                key: [SampleAnnotation(**sample) for sample in value]
-                for key, value in dataset.items()
-                if metric_name is None or key == metric_name
-            }
-        )
-
-    @classmethod
-    def from_json(cls, path: str, metric_name: t.Optional[str]) -> "MetricAnnotation":
-        """Load annotations from a JSON file"""
-        dataset = json.load(open(path))
-        return cls._process_dataset(dataset, metric_name)
-
-    @classmethod
-    def from_app(
-        cls,
-        run_id: str,
-        metric_name: t.Optional[str] = None,
-    ) -> "MetricAnnotation":
-        """
-        Fetch annotations from a URL using either evaluation result or run_id
-
-        Parameters
-        ----------
-        run_id : str
-            Direct run ID to fetch annotations
-        metric_name : str, optional
-            Name of the specific metric to filter
-
-        Returns
-        -------
-        MetricAnnotation
-            Annotation data from the API
-
-        Raises
-        ------
-        ValueError
-            If run_id is not provided
-        """
-        if run_id is None:
-            raise ValueError("run_id must be provided")
-
-        endpoint = f"/api/v1/alignment/evaluation/annotation/{run_id}"
-
-        app_token = get_app_token()
-        base_url = get_api_url()
-        app_url = get_app_url()
-
-        response = requests.get(
-            f"{base_url}{endpoint}",
-            headers={
-                "Content-Type": "application/json",
-                "x-app-token": app_token,
-                "x-source": RAGAS_API_SOURCE,
-                "x-app-version": __version__,
-            },
-        )
-
-        check_api_response(response)
-        dataset = response.json()["data"]
-
-        if not dataset:
-            evaluation_url = build_evaluation_app_url(app_url, run_id)
-            raise ValueError(
-                f"No annotations found. Please annotate the Evaluation first then run this method. "
-                f"\nNote: you can annotate the evaluations using the Ragas app by going to {evaluation_url}"
-            )
-
-        return cls._process_dataset(dataset, metric_name)
-
-    def __len__(self):
-        return sum(len(value) for value in self.root.values())
-
-
-class SingleMetricAnnotation(BaseModel):
-    name: str
-    samples: t.List[SampleAnnotation]
-
-    def to_evaluation_dataset(self) -> EvaluationDataset:
-        samples = [sample.metric_input for sample in self.samples]
-        return EvaluationDataset.from_list(samples)
-
-    def __getitem__(self, idx):
-        return self.samples[idx]
-
-    def __repr__(self):
-        return f"SingleMetricAnnotation(name={self.name}, len={len(self.samples)})"
-
-    def __iter__(self) -> t.Iterator[SampleAnnotation]:  # type: ignore
-        return iter(self.samples)
-
-    def select(self, indices: t.List[int]) -> "SingleMetricAnnotation":
-        return SingleMetricAnnotation(
-            name=self.name,
-            samples=[self.samples[idx] for idx in indices],
-        )
-
-    @classmethod
-    def from_json(cls, path) -> "SingleMetricAnnotation":
-        dataset = json.load(open(path))
-
-        return cls(
-            name=dataset["name"],
-            samples=[SampleAnnotation(**sample) for sample in dataset["samples"]],
-        )
-
-    def filter(self, function: t.Optional[t.Callable] = None):
-        if function is None:
-            function = lambda x: True  # noqa: E731
-
-        return SingleMetricAnnotation(
-            name=self.name,
-            samples=[sample for sample in self.samples if function(sample)],
-        )
-
-    def __len__(self):
-        return len(self.samples)
-
-    def train_test_split(
-        self,
-        test_size: float = 0.2,
-        seed: int = 42,
-        stratify: t.Optional[t.List[t.Any]] = None,
-    ) -> t.Tuple["SingleMetricAnnotation", "SingleMetricAnnotation"]:
-        """
-        Split the dataset into training and testing sets.
-
-        Parameters:
-            test_size (float): The proportion of the dataset to include in the test split.
-            seed (int): Random seed for reproducibility.
-            stratify (list): The column values to stratify the split on.
-        """
-        raise NotImplementedError
-
-    def sample(
-        self, n: int, stratify_key: t.Optional[str] = None
-    ) -> "SingleMetricAnnotation":
-        """
-        Create a subset of the dataset.
-
-        Parameters:
-            n (int): The number of samples to include in the subset.
-            stratify_key (str): The column to stratify the subset on.
-
-        Returns:
-            SingleMetricAnnotation: A subset of the dataset with `n` samples.
-        """
-        if n > len(self.samples):
-            raise ValueError(
-                "Requested sample size exceeds the number of available samples."
-            )
-
-        if stratify_key is None:
-            # Simple random sampling
-            sampled_indices = random.sample(range(len(self.samples)), n)
-            sampled_samples = [self.samples[i] for i in sampled_indices]
-        else:
-            # Stratified sampling
-            class_groups = defaultdict(list)
-            for idx, sample in enumerate(self.samples):
-                key = sample[stratify_key]
-                class_groups[key].append(idx)
-
-            # Determine the proportion of samples to take from each class
-            total_samples = sum(len(indices) for indices in class_groups.values())
-            proportions = {
-                cls: len(indices) / total_samples
-                for cls, indices in class_groups.items()
-            }
-
-            sampled_indices = []
-            for cls, indices in class_groups.items():
-                cls_sample_count = int(np.round(proportions[cls] * n))
-                cls_sample_count = min(
-                    cls_sample_count, len(indices)
-                )  # Don't oversample
-                sampled_indices.extend(random.sample(indices, cls_sample_count))
-
-            # Handle any rounding discrepancies to ensure exactly `n` samples
-            while len(sampled_indices) < n:
-                remaining_indices = set(range(len(self.samples))) - set(sampled_indices)
-                if not remaining_indices:
-                    break
-                sampled_indices.append(random.choice(list(remaining_indices)))
-
-            sampled_samples = [self.samples[i] for i in sampled_indices]
-
-        return SingleMetricAnnotation(name=self.name, samples=sampled_samples)
-
-    def batch(
-        self,
-        batch_size: int,
-        drop_last_batch: bool = False,
-    ):
-        """
-        Create a batch iterator.
-
-        Parameters:
-            batch_size (int): The number of samples in each batch.
-            stratify (str): The column to stratify the batches on.
-            drop_last_batch (bool): Whether to drop the last batch if it is smaller than the specified batch size.
-        """
-
-        samples = self.samples[:]
-        random.shuffle(samples)
-
-        all_batches = [
-            samples[i : i + batch_size]
-            for i in range(0, len(samples), batch_size)
-            if len(samples[i : i + batch_size]) == batch_size or not drop_last_batch
-        ]
-
-        return all_batches
-
-    def stratified_batches(
-        self,
-        batch_size: int,
-        stratify_key: str,
-        drop_last_batch: bool = False,
-        replace: bool = False,
-    ) -> t.List[t.List[SampleAnnotation]]:
-        """
-        Create stratified batches based on a specified key, ensuring proportional representation.
-
-        Parameters:
-            batch_size (int): Number of samples per batch.
-            stratify_key (str): Key in `metric_input` used for stratification (e.g., class labels).
-            drop_last_batch (bool): If True, drops the last batch if it has fewer samples than `batch_size`.
-            replace (bool): If True, allows reusing samples from the same class to fill a batch if necessary.
-
-        Returns:
-            List[List[SampleAnnotation]]: A list of stratified batches, each batch being a list of SampleAnnotation objects.
-        """
-        # Group samples based on the stratification key
-        class_groups = defaultdict(list)
-        for sample in self.samples:
-            key = sample[stratify_key]
-            class_groups[key].append(sample)
-
-        # Shuffle each class group for randomness
-        for group in class_groups.values():
-            random.shuffle(group)
-
-        # Determine the number of batches required
-        total_samples = len(self.samples)
-        num_batches = (
-            np.ceil(total_samples / batch_size).astype(int)
-            if drop_last_batch
-            else np.floor(total_samples / batch_size).astype(int)
-        )
-        samples_per_class_per_batch = {
-            cls: max(1, len(samples) // num_batches)
-            for cls, samples in class_groups.items()
-        }
-
-        # Create stratified batches
-        all_batches = []
-        while len(all_batches) < num_batches:
-            batch = []
-            for cls, samples in list(class_groups.items()):
-                # Determine the number of samples to take from this class
-                count = min(
-                    samples_per_class_per_batch[cls],
-                    len(samples),
-                    batch_size - len(batch),
-                )
-                if count > 0:
-                    # Add samples from the current class
-                    batch.extend(samples[:count])
-                    class_groups[cls] = samples[count:]  # Remove used samples
-                elif replace and len(batch) < batch_size:
-                    # Reuse samples if `replace` is True
-                    batch.extend(random.choices(samples, k=batch_size - len(batch)))
-
-            # Shuffle the batch to mix classes
-            random.shuffle(batch)
-            if len(batch) == batch_size or not drop_last_batch:
-                all_batches.append(batch)
-
-        return all_batches
-
-    def get_prompt_annotations(self) -> t.Dict[str, t.List[PromptAnnotation]]:
-        """
-        Get all the prompt annotations for each prompt as a list.
-        """
-        prompt_annotations = defaultdict(list)
-        for sample in self.samples:
-            if sample.is_accepted:
-                for prompt_name, prompt_annotation in sample.prompts.items():
-                    prompt_annotations[prompt_name].append(prompt_annotation)
-        return prompt_annotations
-
- -
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/docs/_build/html/_modules/typing.html b/docs/_build/html/_modules/typing.html deleted file mode 100644 index 8110d892..00000000 --- a/docs/_build/html/_modules/typing.html +++ /dev/null @@ -1,3646 +0,0 @@ - - - - - - typing — NotDiamond 0.3.25 - - - - - - - - - - - - - - - - -
- - -
- -
-
-
- -
-
-
-
- -

Source code for typing

-"""
-The typing module: Support for gradual typing as defined by PEP 484 and subsequent PEPs.
-
-Among other things, the module includes the following:
-* Generic, Protocol, and internal machinery to support generic aliases.
-  All subscripted types like X[int], Union[int, str] are generic aliases.
-* Various "special forms" that have unique meanings in type annotations:
-  NoReturn, Never, ClassVar, Self, Concatenate, Unpack, and others.
-* Classes whose instances can be type arguments to generic classes and functions:
-  TypeVar, ParamSpec, TypeVarTuple.
-* Public helper functions: get_type_hints, overload, cast, final, and others.
-* Several protocols to support duck-typing:
-  SupportsFloat, SupportsIndex, SupportsAbs, and others.
-* Special types: NewType, NamedTuple, TypedDict.
-* Deprecated wrapper submodules for re and io related types.
-* Deprecated aliases for builtin types and collections.abc ABCs.
-
-Any name not present in __all__ is an implementation detail
-that may be changed without notice. Use at your own risk!
-"""
-
-from abc import abstractmethod, ABCMeta
-import collections
-from collections import defaultdict
-import collections.abc
-import contextlib
-import functools
-import operator
-import re as stdlib_re  # Avoid confusion with the re we export.
-import sys
-import types
-import warnings
-from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType, GenericAlias
-
-
-try:
-    from _typing import _idfunc
-except ImportError:
-    def _idfunc(_, x):
-        return x
-
-# Please keep __all__ alphabetized within each category.
-__all__ = [
-    # Super-special typing primitives.
-    'Annotated',
-    'Any',
-    'Callable',
-    'ClassVar',
-    'Concatenate',
-    'Final',
-    'ForwardRef',
-    'Generic',
-    'Literal',
-    'Optional',
-    'ParamSpec',
-    'Protocol',
-    'Tuple',
-    'Type',
-    'TypeVar',
-    'TypeVarTuple',
-    'Union',
-
-    # ABCs (from collections.abc).
-    'AbstractSet',  # collections.abc.Set.
-    'ByteString',
-    'Container',
-    'ContextManager',
-    'Hashable',
-    'ItemsView',
-    'Iterable',
-    'Iterator',
-    'KeysView',
-    'Mapping',
-    'MappingView',
-    'MutableMapping',
-    'MutableSequence',
-    'MutableSet',
-    'Sequence',
-    'Sized',
-    'ValuesView',
-    'Awaitable',
-    'AsyncIterator',
-    'AsyncIterable',
-    'Coroutine',
-    'Collection',
-    'AsyncGenerator',
-    'AsyncContextManager',
-
-    # Structural checks, a.k.a. protocols.
-    'Reversible',
-    'SupportsAbs',
-    'SupportsBytes',
-    'SupportsComplex',
-    'SupportsFloat',
-    'SupportsIndex',
-    'SupportsInt',
-    'SupportsRound',
-
-    # Concrete collection types.
-    'ChainMap',
-    'Counter',
-    'Deque',
-    'Dict',
-    'DefaultDict',
-    'List',
-    'OrderedDict',
-    'Set',
-    'FrozenSet',
-    'NamedTuple',  # Not really a type.
-    'TypedDict',  # Not really a type.
-    'Generator',
-
-    # Other concrete types.
-    'BinaryIO',
-    'IO',
-    'Match',
-    'Pattern',
-    'TextIO',
-
-    # One-off things.
-    'AnyStr',
-    'assert_type',
-    'assert_never',
-    'cast',
-    'clear_overloads',
-    'dataclass_transform',
-    'final',
-    'get_args',
-    'get_origin',
-    'get_overloads',
-    'get_type_hints',
-    'is_typeddict',
-    'LiteralString',
-    'Never',
-    'NewType',
-    'no_type_check',
-    'no_type_check_decorator',
-    'NoReturn',
-    'NotRequired',
-    'overload',
-    'ParamSpecArgs',
-    'ParamSpecKwargs',
-    'Required',
-    'reveal_type',
-    'runtime_checkable',
-    'Self',
-    'Text',
-    'TYPE_CHECKING',
-    'TypeAlias',
-    'TypeGuard',
-    'Unpack',
-]
-
-# The pseudo-submodules 're' and 'io' are part of the public
-# namespace, but excluded from __all__ because they might stomp on
-# legitimate imports of those modules.
-
-
-def _type_convert(arg, module=None, *, allow_special_forms=False):
-    """For converting None to type(None), and strings to ForwardRef."""
-    if arg is None:
-        return type(None)
-    if isinstance(arg, str):
-        return ForwardRef(arg, module=module, is_class=allow_special_forms)
-    return arg
-
-
-def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False):
-    """Check that the argument is a type, and return it (internal helper).
-
-    As a special case, accept None and return type(None) instead. Also wrap strings
-    into ForwardRef instances. Consider several corner cases, for example plain
-    special forms like Union are not valid, while Union[int, str] is OK, etc.
-    The msg argument is a human-readable error message, e.g.::
-
-        "Union[arg, ...]: arg should be a type."
-
-    We append the repr() of the actual value (truncated to 100 chars).
-    """
-    invalid_generic_forms = (Generic, Protocol)
-    if not allow_special_forms:
-        invalid_generic_forms += (ClassVar,)
-        if is_argument:
-            invalid_generic_forms += (Final,)
-
-    arg = _type_convert(arg, module=module, allow_special_forms=allow_special_forms)
-    if (isinstance(arg, _GenericAlias) and
-            arg.__origin__ in invalid_generic_forms):
-        raise TypeError(f"{arg} is not valid as type argument")
-    if arg in (Any, LiteralString, NoReturn, Never, Self, TypeAlias):
-        return arg
-    if allow_special_forms and arg in (ClassVar, Final):
-        return arg
-    if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
-        raise TypeError(f"Plain {arg} is not valid as type argument")
-    if type(arg) is tuple:
-        raise TypeError(f"{msg} Got {arg!r:.100}.")
-    return arg
-
-
-def _is_param_expr(arg):
-    return arg is ... or isinstance(arg,
-            (tuple, list, ParamSpec, _ConcatenateGenericAlias))
-
-
-def _should_unflatten_callable_args(typ, args):
-    """Internal helper for munging collections.abc.Callable's __args__.
-
-    The canonical representation for a Callable's __args__ flattens the
-    argument types, see https://github.com/python/cpython/issues/86361.
-
-    For example::
-
-        >>> import collections.abc
-        >>> P = ParamSpec('P')
-        >>> collections.abc.Callable[[int, int], str].__args__ == (int, int, str)
-        True
-        >>> collections.abc.Callable[P, str].__args__ == (P, str)
-        True
-
-    As a result, if we need to reconstruct the Callable from its __args__,
-    we need to unflatten it.
-    """
-    return (
-        typ.__origin__ is collections.abc.Callable
-        and not (len(args) == 2 and _is_param_expr(args[0]))
-    )
-
-
-def _type_repr(obj):
-    """Return the repr() of an object, special-casing types (internal helper).
-
-    If obj is a type, we return a shorter version than the default
-    type.__repr__, based on the module and qualified name, which is
-    typically enough to uniquely identify a type.  For everything
-    else, we fall back on repr(obj).
-    """
-    if isinstance(obj, types.GenericAlias):
-        return repr(obj)
-    if isinstance(obj, type):
-        if obj.__module__ == 'builtins':
-            return obj.__qualname__
-        return f'{obj.__module__}.{obj.__qualname__}'
-    if obj is ...:
-        return('...')
-    if isinstance(obj, types.FunctionType):
-        return obj.__name__
-    return repr(obj)
-
-
-def _collect_parameters(args):
-    """Collect all type variables and parameter specifications in args
-    in order of first appearance (lexicographic order).
-
-    For example::
-
-        >>> P = ParamSpec('P')
-        >>> T = TypeVar('T')
-        >>> _collect_parameters((T, Callable[P, T]))
-        (~T, ~P)
-    """
-    parameters = []
-    for t in args:
-        if isinstance(t, type):
-            # We don't want __parameters__ descriptor of a bare Python class.
-            pass
-        elif isinstance(t, tuple):
-            # `t` might be a tuple, when `ParamSpec` is substituted with
-            # `[T, int]`, or `[int, *Ts]`, etc.
-            for x in t:
-                for collected in _collect_parameters([x]):
-                    if collected not in parameters:
-                        parameters.append(collected)
-        elif hasattr(t, '__typing_subst__'):
-            if t not in parameters:
-                parameters.append(t)
-        else:
-            for x in getattr(t, '__parameters__', ()):
-                if x not in parameters:
-                    parameters.append(x)
-    return tuple(parameters)
-
-
-def _check_generic(cls, parameters, elen):
-    """Check correct count for parameters of a generic cls (internal helper).
-
-    This gives a nice error message in case of count mismatch.
-    """
-    if not elen:
-        raise TypeError(f"{cls} is not a generic class")
-    alen = len(parameters)
-    if alen != elen:
-        raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
-                        f" actual {alen}, expected {elen}")
-
-def _unpack_args(args):
-    newargs = []
-    for arg in args:
-        subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
-        if subargs is not None and not (subargs and subargs[-1] is ...):
-            newargs.extend(subargs)
-        else:
-            newargs.append(arg)
-    return newargs
-
-def _deduplicate(params, *, unhashable_fallback=False):
-    # Weed out strict duplicates, preserving the first of each occurrence.
-    try:
-        return dict.fromkeys(params)
-    except TypeError:
-        if not unhashable_fallback:
-            raise
-        # Happens for cases like `Annotated[dict, {'x': IntValidator()}]`
-        return _deduplicate_unhashable(params)
-
-def _deduplicate_unhashable(unhashable_params):
-    new_unhashable = []
-    for t in unhashable_params:
-        if t not in new_unhashable:
-            new_unhashable.append(t)
-    return new_unhashable
-
-def _compare_args_orderless(first_args, second_args):
-    first_unhashable = _deduplicate_unhashable(first_args)
-    second_unhashable = _deduplicate_unhashable(second_args)
-    t = list(second_unhashable)
-    try:
-        for elem in first_unhashable:
-            t.remove(elem)
-    except ValueError:
-        return False
-    return not t
-
-def _remove_dups_flatten(parameters):
-    """Internal helper for Union creation and substitution.
-
-    Flatten Unions among parameters, then remove duplicates.
-    """
-    # Flatten out Union[Union[...], ...].
-    params = []
-    for p in parameters:
-        if isinstance(p, (_UnionGenericAlias, types.UnionType)):
-            params.extend(p.__args__)
-        else:
-            params.append(p)
-
-    return tuple(_deduplicate(params, unhashable_fallback=True))
-
-
-def _flatten_literal_params(parameters):
-    """Internal helper for Literal creation: flatten Literals among parameters."""
-    params = []
-    for p in parameters:
-        if isinstance(p, _LiteralGenericAlias):
-            params.extend(p.__args__)
-        else:
-            params.append(p)
-    return tuple(params)
-
-
-_cleanups = []
-
-
-def _tp_cache(func=None, /, *, typed=False):
-    """Internal wrapper caching __getitem__ of generic types.
-
-    For non-hashable arguments, the original function is used as a fallback.
-    """
-    def decorator(func):
-        cached = functools.lru_cache(typed=typed)(func)
-        _cleanups.append(cached.cache_clear)
-
-        @functools.wraps(func)
-        def inner(*args, **kwds):
-            try:
-                return cached(*args, **kwds)
-            except TypeError:
-                pass  # All real errors (not unhashable args) are raised below.
-            return func(*args, **kwds)
-        return inner
-
-    if func is not None:
-        return decorator(func)
-
-    return decorator
-
-def _eval_type(t, globalns, localns, recursive_guard=frozenset()):
-    """Evaluate all forward references in the given type t.
-
-    For use of globalns and localns see the docstring for get_type_hints().
-    recursive_guard is used to prevent infinite recursion with a recursive
-    ForwardRef.
-    """
-    if isinstance(t, ForwardRef):
-        return t._evaluate(globalns, localns, recursive_guard)
-    if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)):
-        if isinstance(t, GenericAlias):
-            args = tuple(
-                ForwardRef(arg) if isinstance(arg, str) else arg
-                for arg in t.__args__
-            )
-            is_unpacked = t.__unpacked__
-            if _should_unflatten_callable_args(t, args):
-                t = t.__origin__[(args[:-1], args[-1])]
-            else:
-                t = t.__origin__[args]
-            if is_unpacked:
-                t = Unpack[t]
-        ev_args = tuple(_eval_type(a, globalns, localns, recursive_guard) for a in t.__args__)
-        if ev_args == t.__args__:
-            return t
-        if isinstance(t, GenericAlias):
-            return GenericAlias(t.__origin__, ev_args)
-        if isinstance(t, types.UnionType):
-            return functools.reduce(operator.or_, ev_args)
-        else:
-            return t.copy_with(ev_args)
-    return t
-
-
-class _Final:
-    """Mixin to prohibit subclassing."""
-
-    __slots__ = ('__weakref__',)
-
-    def __init_subclass__(cls, /, *args, **kwds):
-        if '_root' not in kwds:
-            raise TypeError("Cannot subclass special typing classes")
-
-class _Immutable:
-    """Mixin to indicate that object should not be copied."""
-
-    __slots__ = ()
-
-    def __copy__(self):
-        return self
-
-    def __deepcopy__(self, memo):
-        return self
-
-
-class _NotIterable:
-    """Mixin to prevent iteration, without being compatible with Iterable.
-
-    That is, we could do::
-
-        def __iter__(self): raise TypeError()
-
-    But this would make users of this mixin duck type-compatible with
-    collections.abc.Iterable - isinstance(foo, Iterable) would be True.
-
-    Luckily, we can instead prevent iteration by setting __iter__ to None, which
-    is treated specially.
-    """
-
-    __slots__ = ()
-    __iter__ = None
-
-
-# Internal indicator of special typing constructs.
-# See __doc__ instance attribute for specific docs.
-class _SpecialForm(_Final, _NotIterable, _root=True):
-    __slots__ = ('_name', '__doc__', '_getitem')
-
-    def __init__(self, getitem):
-        self._getitem = getitem
-        self._name = getitem.__name__
-        self.__doc__ = getitem.__doc__
-
-    def __getattr__(self, item):
-        if item in {'__name__', '__qualname__'}:
-            return self._name
-
-        raise AttributeError(item)
-
-    def __mro_entries__(self, bases):
-        raise TypeError(f"Cannot subclass {self!r}")
-
-    def __repr__(self):
-        return 'typing.' + self._name
-
-    def __reduce__(self):
-        return self._name
-
-    def __call__(self, *args, **kwds):
-        raise TypeError(f"Cannot instantiate {self!r}")
-
-    def __or__(self, other):
-        return Union[self, other]
-
-    def __ror__(self, other):
-        return Union[other, self]
-
-    def __instancecheck__(self, obj):
-        raise TypeError(f"{self} cannot be used with isinstance()")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError(f"{self} cannot be used with issubclass()")
-
-    @_tp_cache
-    def __getitem__(self, parameters):
-        return self._getitem(self, parameters)
-
-
-class _LiteralSpecialForm(_SpecialForm, _root=True):
-    def __getitem__(self, parameters):
-        if not isinstance(parameters, tuple):
-            parameters = (parameters,)
-        return self._getitem(self, *parameters)
-
-
-class _AnyMeta(type):
-    def __instancecheck__(self, obj):
-        if self is Any:
-            raise TypeError("typing.Any cannot be used with isinstance()")
-        return super().__instancecheck__(obj)
-
-    def __repr__(self):
-        if self is Any:
-            return "typing.Any"
-        return super().__repr__()  # respect to subclasses
-
-
-class Any(metaclass=_AnyMeta):
-    """Special type indicating an unconstrained type.
-
-    - Any is compatible with every type.
-    - Any assumed to have all methods.
-    - All values assumed to be instances of Any.
-
-    Note that all the above statements are true from the point of view of
-    static type checkers. At runtime, Any should not be used with instance
-    checks.
-    """
-
-    def __new__(cls, *args, **kwargs):
-        if cls is Any:
-            raise TypeError("Any cannot be instantiated")
-        return super().__new__(cls)
-
-
-@_SpecialForm
-def NoReturn(self, parameters):
-    """Special type indicating functions that never return.
-
-    Example::
-
-        from typing import NoReturn
-
-        def stop() -> NoReturn:
-            raise Exception('no way')
-
-    NoReturn can also be used as a bottom type, a type that
-    has no values. Starting in Python 3.11, the Never type should
-    be used for this concept instead. Type checkers should treat the two
-    equivalently.
-    """
-    raise TypeError(f"{self} is not subscriptable")
-
-# This is semantically identical to NoReturn, but it is implemented
-# separately so that type checkers can distinguish between the two
-# if they want.
-@_SpecialForm
-def Never(self, parameters):
-    """The bottom type, a type that has no members.
-
-    This can be used to define a function that should never be
-    called, or a function that never returns::
-
-        from typing import Never
-
-        def never_call_me(arg: Never) -> None:
-            pass
-
-        def int_or_str(arg: int | str) -> None:
-            never_call_me(arg)  # type checker error
-            match arg:
-                case int():
-                    print("It's an int")
-                case str():
-                    print("It's a str")
-                case _:
-                    never_call_me(arg)  # OK, arg is of type Never
-    """
-    raise TypeError(f"{self} is not subscriptable")
-
-
-@_SpecialForm
-def Self(self, parameters):
-    """Used to spell the type of "self" in classes.
-
-    Example::
-
-        from typing import Self
-
-        class Foo:
-            def return_self(self) -> Self:
-                ...
-                return self
-
-    This is especially useful for:
-        - classmethods that are used as alternative constructors
-        - annotating an `__enter__` method which returns self
-    """
-    raise TypeError(f"{self} is not subscriptable")
-
-
-@_SpecialForm
-def LiteralString(self, parameters):
-    """Represents an arbitrary literal string.
-
-    Example::
-
-        from typing import LiteralString
-
-        def run_query(sql: LiteralString) -> None:
-            ...
-
-        def caller(arbitrary_string: str, literal_string: LiteralString) -> None:
-            run_query("SELECT * FROM students")  # OK
-            run_query(literal_string)  # OK
-            run_query("SELECT * FROM " + literal_string)  # OK
-            run_query(arbitrary_string)  # type checker error
-            run_query(  # type checker error
-                f"SELECT * FROM students WHERE name = {arbitrary_string}"
-            )
-
-    Only string literals and other LiteralStrings are compatible
-    with LiteralString. This provides a tool to help prevent
-    security issues such as SQL injection.
-    """
-    raise TypeError(f"{self} is not subscriptable")
-
-
-@_SpecialForm
-def ClassVar(self, parameters):
-    """Special type construct to mark class variables.
-
-    An annotation wrapped in ClassVar indicates that a given
-    attribute is intended to be used as a class variable and
-    should not be set on instances of that class.
-
-    Usage::
-
-        class Starship:
-            stats: ClassVar[dict[str, int]] = {} # class variable
-            damage: int = 10                     # instance variable
-
-    ClassVar accepts only types and cannot be further subscribed.
-
-    Note that ClassVar is not a class itself, and should not
-    be used with isinstance() or issubclass().
-    """
-    item = _type_check(parameters, f'{self} accepts only single type.')
-    return _GenericAlias(self, (item,))
-
-@_SpecialForm
-def Final(self, parameters):
-    """Special typing construct to indicate final names to type checkers.
-
-    A final name cannot be re-assigned or overridden in a subclass.
-
-    For example::
-
-        MAX_SIZE: Final = 9000
-        MAX_SIZE += 1  # Error reported by type checker
-
-        class Connection:
-            TIMEOUT: Final[int] = 10
-
-        class FastConnector(Connection):
-            TIMEOUT = 1  # Error reported by type checker
-
-    There is no runtime checking of these properties.
-    """
-    item = _type_check(parameters, f'{self} accepts only single type.')
-    return _GenericAlias(self, (item,))
-
-@_SpecialForm
-def Union(self, parameters):
-    """Union type; Union[X, Y] means either X or Y.
-
-    On Python 3.10 and higher, the | operator
-    can also be used to denote unions;
-    X | Y means the same thing to the type checker as Union[X, Y].
-
-    To define a union, use e.g. Union[int, str]. Details:
-    - The arguments must be types and there must be at least one.
-    - None as an argument is a special case and is replaced by
-      type(None).
-    - Unions of unions are flattened, e.g.::
-
-        assert Union[Union[int, str], float] == Union[int, str, float]
-
-    - Unions of a single argument vanish, e.g.::
-
-        assert Union[int] == int  # The constructor actually returns int
-
-    - Redundant arguments are skipped, e.g.::
-
-        assert Union[int, str, int] == Union[int, str]
-
-    - When comparing unions, the argument order is ignored, e.g.::
-
-        assert Union[int, str] == Union[str, int]
-
-    - You cannot subclass or instantiate a union.
-    - You can use Optional[X] as a shorthand for Union[X, None].
-    """
-    if parameters == ():
-        raise TypeError("Cannot take a Union of no types.")
-    if not isinstance(parameters, tuple):
-        parameters = (parameters,)
-    msg = "Union[arg, ...]: each arg must be a type."
-    parameters = tuple(_type_check(p, msg) for p in parameters)
-    parameters = _remove_dups_flatten(parameters)
-    if len(parameters) == 1:
-        return parameters[0]
-    if len(parameters) == 2 and type(None) in parameters:
-        return _UnionGenericAlias(self, parameters, name="Optional")
-    return _UnionGenericAlias(self, parameters)
-
-@_SpecialForm
-def Optional(self, parameters):
-    """Optional[X] is equivalent to Union[X, None]."""
-    arg = _type_check(parameters, f"{self} requires a single type.")
-    return Union[arg, type(None)]
-
-@_LiteralSpecialForm
-@_tp_cache(typed=True)
-def Literal(self, *parameters):
-    """Special typing form to define literal types (a.k.a. value types).
-
-    This form can be used to indicate to type checkers that the corresponding
-    variable or function parameter has a value equivalent to the provided
-    literal (or one of several literals)::
-
-        def validate_simple(data: Any) -> Literal[True]:  # always returns True
-            ...
-
-        MODE = Literal['r', 'rb', 'w', 'wb']
-        def open_helper(file: str, mode: MODE) -> str:
-            ...
-
-        open_helper('/some/path', 'r')  # Passes type check
-        open_helper('/other/path', 'typo')  # Error in type checker
-
-    Literal[...] cannot be subclassed. At runtime, an arbitrary value
-    is allowed as type argument to Literal[...], but type checkers may
-    impose restrictions.
-    """
-    # There is no '_type_check' call because arguments to Literal[...] are
-    # values, not types.
-    parameters = _flatten_literal_params(parameters)
-
-    try:
-        parameters = tuple(p for p, _ in _deduplicate(list(_value_and_type_iter(parameters))))
-    except TypeError:  # unhashable parameters
-        pass
-
-    return _LiteralGenericAlias(self, parameters)
-
-
-@_SpecialForm
-def TypeAlias(self, parameters):
-    """Special form for marking type aliases.
-
-    Use TypeAlias to indicate that an assignment should
-    be recognized as a proper type alias definition by type
-    checkers.
-
-    For example::
-
-        Predicate: TypeAlias = Callable[..., bool]
-
-    It's invalid when used anywhere except as in the example above.
-    """
-    raise TypeError(f"{self} is not subscriptable")
-
-
-@_SpecialForm
-def Concatenate(self, parameters):
-    """Special form for annotating higher-order functions.
-
-    ``Concatenate`` can be used in conjunction with ``ParamSpec`` and
-    ``Callable`` to represent a higher-order function which adds, removes or
-    transforms the parameters of a callable.
-
-    For example::
-
-        Callable[Concatenate[int, P], int]
-
-    See PEP 612 for detailed information.
-    """
-    if parameters == ():
-        raise TypeError("Cannot take a Concatenate of no types.")
-    if not isinstance(parameters, tuple):
-        parameters = (parameters,)
-    if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)):
-        raise TypeError("The last parameter to Concatenate should be a "
-                        "ParamSpec variable or ellipsis.")
-    msg = "Concatenate[arg, ...]: each arg must be a type."
-    parameters = (*(_type_check(p, msg) for p in parameters[:-1]), parameters[-1])
-    return _ConcatenateGenericAlias(self, parameters,
-                                    _paramspec_tvars=True)
-
-
-@_SpecialForm
-def TypeGuard(self, parameters):
-    """Special typing construct for marking user-defined type guard functions.
-
-    ``TypeGuard`` can be used to annotate the return type of a user-defined
-    type guard function.  ``TypeGuard`` only accepts a single type argument.
-    At runtime, functions marked this way should return a boolean.
-
-    ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-    type checkers to determine a more precise type of an expression within a
-    program's code flow.  Usually type narrowing is done by analyzing
-    conditional code flow and applying the narrowing to a block of code.  The
-    conditional expression here is sometimes referred to as a "type guard".
-
-    Sometimes it would be convenient to use a user-defined boolean function
-    as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-    return type to alert static type checkers to this intention.
-
-    Using  ``-> TypeGuard`` tells the static type checker that for a given
-    function:
-
-    1. The return value is a boolean.
-    2. If the return value is ``True``, the type of its argument
-       is the type inside ``TypeGuard``.
-
-       For example::
-
-           def is_str(val: Union[str, float]):
-               # "isinstance" type guard
-               if isinstance(val, str):
-                   # Type of ``val`` is narrowed to ``str``
-                   ...
-               else:
-                   # Else, type of ``val`` is narrowed to ``float``.
-                   ...
-
-    Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-    form of ``TypeA`` (it can even be a wider form) and this may lead to
-    type-unsafe results.  The main reason is to allow for things like
-    narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-    a subtype of the former, since ``List`` is invariant.  The responsibility of
-    writing type-safe type guards is left to the user.
-
-    ``TypeGuard`` also works with type variables.  For more information, see
-    PEP 647 (User-Defined Type Guards).
-    """
-    item = _type_check(parameters, f'{self} accepts only single type.')
-    return _GenericAlias(self, (item,))
-
-
-class ForwardRef(_Final, _root=True):
-    """Internal wrapper to hold a forward reference."""
-
-    __slots__ = ('__forward_arg__', '__forward_code__',
-                 '__forward_evaluated__', '__forward_value__',
-                 '__forward_is_argument__', '__forward_is_class__',
-                 '__forward_module__')
-
-    def __init__(self, arg, is_argument=True, module=None, *, is_class=False):
-        if not isinstance(arg, str):
-            raise TypeError(f"Forward reference must be a string -- got {arg!r}")
-
-        # If we do `def f(*args: *Ts)`, then we'll have `arg = '*Ts'`.
-        # Unfortunately, this isn't a valid expression on its own, so we
-        # do the unpacking manually.
-        if arg.startswith('*'):
-            arg_to_compile = f'({arg},)[0]'  # E.g. (*Ts,)[0] or (*tuple[int, int],)[0]
-        else:
-            arg_to_compile = arg
-        try:
-            code = compile(arg_to_compile, '<string>', 'eval')
-        except SyntaxError:
-            raise SyntaxError(f"Forward reference must be an expression -- got {arg!r}")
-
-        self.__forward_arg__ = arg
-        self.__forward_code__ = code
-        self.__forward_evaluated__ = False
-        self.__forward_value__ = None
-        self.__forward_is_argument__ = is_argument
-        self.__forward_is_class__ = is_class
-        self.__forward_module__ = module
-
-    def _evaluate(self, globalns, localns, recursive_guard):
-        if self.__forward_arg__ in recursive_guard:
-            return self
-        if not self.__forward_evaluated__ or localns is not globalns:
-            if globalns is None and localns is None:
-                globalns = localns = {}
-            elif globalns is None:
-                globalns = localns
-            elif localns is None:
-                localns = globalns
-            if self.__forward_module__ is not None:
-                globalns = getattr(
-                    sys.modules.get(self.__forward_module__, None), '__dict__', globalns
-                )
-            type_ = _type_check(
-                eval(self.__forward_code__, globalns, localns),
-                "Forward references must evaluate to types.",
-                is_argument=self.__forward_is_argument__,
-                allow_special_forms=self.__forward_is_class__,
-            )
-            self.__forward_value__ = _eval_type(
-                type_, globalns, localns, recursive_guard | {self.__forward_arg__}
-            )
-            self.__forward_evaluated__ = True
-        return self.__forward_value__
-
-    def __eq__(self, other):
-        if not isinstance(other, ForwardRef):
-            return NotImplemented
-        if self.__forward_evaluated__ and other.__forward_evaluated__:
-            return (self.__forward_arg__ == other.__forward_arg__ and
-                    self.__forward_value__ == other.__forward_value__)
-        return (self.__forward_arg__ == other.__forward_arg__ and
-                self.__forward_module__ == other.__forward_module__)
-
-    def __hash__(self):
-        return hash((self.__forward_arg__, self.__forward_module__))
-
-    def __or__(self, other):
-        return Union[self, other]
-
-    def __ror__(self, other):
-        return Union[other, self]
-
-    def __repr__(self):
-        if self.__forward_module__ is None:
-            module_repr = ''
-        else:
-            module_repr = f', module={self.__forward_module__!r}'
-        return f'ForwardRef({self.__forward_arg__!r}{module_repr})'
-
-
-def _is_unpacked_typevartuple(x: Any) -> bool:
-    return ((not isinstance(x, type)) and
-            getattr(x, '__typing_is_unpacked_typevartuple__', False))
-
-
-def _is_typevar_like(x: Any) -> bool:
-    return isinstance(x, (TypeVar, ParamSpec)) or _is_unpacked_typevartuple(x)
-
-
-class _PickleUsingNameMixin:
-    """Mixin enabling pickling based on self.__name__."""
-
-    def __reduce__(self):
-        return self.__name__
-
-
-class _BoundVarianceMixin:
-    """Mixin giving __init__ bound and variance arguments.
-
-    This is used by TypeVar and ParamSpec, which both employ the notions of
-    a type 'bound' (restricting type arguments to be a subtype of some
-    specified type) and type 'variance' (determining subtype relations between
-    generic types).
-    """
-    def __init__(self, bound, covariant, contravariant):
-        """Used to setup TypeVars and ParamSpec's bound, covariant and
-        contravariant attributes.
-        """
-        if covariant and contravariant:
-            raise ValueError("Bivariant types are not supported.")
-        self.__covariant__ = bool(covariant)
-        self.__contravariant__ = bool(contravariant)
-        if bound:
-            self.__bound__ = _type_check(bound, "Bound must be a type.")
-        else:
-            self.__bound__ = None
-
-    def __or__(self, right):
-        return Union[self, right]
-
-    def __ror__(self, left):
-        return Union[left, self]
-
-    def __repr__(self):
-        if self.__covariant__:
-            prefix = '+'
-        elif self.__contravariant__:
-            prefix = '-'
-        else:
-            prefix = '~'
-        return prefix + self.__name__
-
-
-class TypeVar(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin,
-              _root=True):
-    """Type variable.
-
-    Usage::
-
-      T = TypeVar('T')  # Can be anything
-      A = TypeVar('A', str, bytes)  # Must be str or bytes
-
-    Type variables exist primarily for the benefit of static type
-    checkers.  They serve as the parameters for generic types as well
-    as for generic function definitions.  See class Generic for more
-    information on generic types.  Generic functions work as follows:
-
-      def repeat(x: T, n: int) -> List[T]:
-          '''Return a list containing n references to x.'''
-          return [x]*n
-
-      def longest(x: A, y: A) -> A:
-          '''Return the longest of two strings.'''
-          return x if len(x) >= len(y) else y
-
-    The latter example's signature is essentially the overloading
-    of (str, str) -> str and (bytes, bytes) -> bytes.  Also note
-    that if the arguments are instances of some subclass of str,
-    the return type is still plain str.
-
-    At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError.
-
-    Type variables defined with covariant=True or contravariant=True
-    can be used to declare covariant or contravariant generic types.
-    See PEP 484 for more details. By default generic types are invariant
-    in all type variables.
-
-    Type variables can be introspected. e.g.:
-
-      T.__name__ == 'T'
-      T.__constraints__ == ()
-      T.__covariant__ == False
-      T.__contravariant__ = False
-      A.__constraints__ == (str, bytes)
-
-    Note that only type variables defined in global scope can be pickled.
-    """
-
-    def __init__(self, name, *constraints, bound=None,
-                 covariant=False, contravariant=False):
-        self.__name__ = name
-        super().__init__(bound, covariant, contravariant)
-        if constraints and bound is not None:
-            raise TypeError("Constraints cannot be combined with bound=...")
-        if constraints and len(constraints) == 1:
-            raise TypeError("A single constraint is not allowed")
-        msg = "TypeVar(name, constraint, ...): constraints must be types."
-        self.__constraints__ = tuple(_type_check(t, msg) for t in constraints)
-        def_mod = _caller()
-        if def_mod != 'typing':
-            self.__module__ = def_mod
-
-    def __typing_subst__(self, arg):
-        msg = "Parameters to generic types must be types."
-        arg = _type_check(arg, msg, is_argument=True)
-        if ((isinstance(arg, _GenericAlias) and arg.__origin__ is Unpack) or
-            (isinstance(arg, GenericAlias) and getattr(arg, '__unpacked__', False))):
-            raise TypeError(f"{arg} is not valid as type argument")
-        return arg
-
-
-class TypeVarTuple(_Final, _Immutable, _PickleUsingNameMixin, _root=True):
-    """Type variable tuple.
-
-    Usage:
-
-      Ts = TypeVarTuple('Ts')  # Can be given any name
-
-    Just as a TypeVar (type variable) is a placeholder for a single type,
-    a TypeVarTuple is a placeholder for an *arbitrary* number of types. For
-    example, if we define a generic class using a TypeVarTuple:
-
-      class C(Generic[*Ts]): ...
-
-    Then we can parameterize that class with an arbitrary number of type
-    arguments:
-
-      C[int]       # Fine
-      C[int, str]  # Also fine
-      C[()]        # Even this is fine
-
-    For more details, see PEP 646.
-
-    Note that only TypeVarTuples defined in global scope can be pickled.
-    """
-
-    def __init__(self, name):
-        self.__name__ = name
-
-        # Used for pickling.
-        def_mod = _caller()
-        if def_mod != 'typing':
-            self.__module__ = def_mod
-
-    def __iter__(self):
-        yield Unpack[self]
-
-    def __repr__(self):
-        return self.__name__
-
-    def __typing_subst__(self, arg):
-        raise TypeError("Substitution of bare TypeVarTuple is not supported")
-
-    def __typing_prepare_subst__(self, alias, args):
-        params = alias.__parameters__
-        typevartuple_index = params.index(self)
-        for param in params[typevartuple_index + 1:]:
-            if isinstance(param, TypeVarTuple):
-                raise TypeError(f"More than one TypeVarTuple parameter in {alias}")
-
-        alen = len(args)
-        plen = len(params)
-        left = typevartuple_index
-        right = plen - typevartuple_index - 1
-        var_tuple_index = None
-        fillarg = None
-        for k, arg in enumerate(args):
-            if not isinstance(arg, type):
-                subargs = getattr(arg, '__typing_unpacked_tuple_args__', None)
-                if subargs and len(subargs) == 2 and subargs[-1] is ...:
-                    if var_tuple_index is not None:
-                        raise TypeError("More than one unpacked arbitrary-length tuple argument")
-                    var_tuple_index = k
-                    fillarg = subargs[0]
-        if var_tuple_index is not None:
-            left = min(left, var_tuple_index)
-            right = min(right, alen - var_tuple_index - 1)
-        elif left + right > alen:
-            raise TypeError(f"Too few arguments for {alias};"
-                            f" actual {alen}, expected at least {plen-1}")
-
-        return (
-            *args[:left],
-            *([fillarg]*(typevartuple_index - left)),
-            tuple(args[left: alen - right]),
-            *([fillarg]*(plen - right - left - typevartuple_index - 1)),
-            *args[alen - right:],
-        )
-
-
-class ParamSpecArgs(_Final, _Immutable, _root=True):
-    """The args for a ParamSpec object.
-
-    Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
-
-    ParamSpecArgs objects have a reference back to their ParamSpec:
-
-       P.args.__origin__ is P
-
-    This type is meant for runtime introspection and has no special meaning to
-    static type checkers.
-    """
-    def __init__(self, origin):
-        self.__origin__ = origin
-
-    def __repr__(self):
-        return f"{self.__origin__.__name__}.args"
-
-    def __eq__(self, other):
-        if not isinstance(other, ParamSpecArgs):
-            return NotImplemented
-        return self.__origin__ == other.__origin__
-
-
-class ParamSpecKwargs(_Final, _Immutable, _root=True):
-    """The kwargs for a ParamSpec object.
-
-    Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
-
-    ParamSpecKwargs objects have a reference back to their ParamSpec:
-
-       P.kwargs.__origin__ is P
-
-    This type is meant for runtime introspection and has no special meaning to
-    static type checkers.
-    """
-    def __init__(self, origin):
-        self.__origin__ = origin
-
-    def __repr__(self):
-        return f"{self.__origin__.__name__}.kwargs"
-
-    def __eq__(self, other):
-        if not isinstance(other, ParamSpecKwargs):
-            return NotImplemented
-        return self.__origin__ == other.__origin__
-
-
-class ParamSpec(_Final, _Immutable, _BoundVarianceMixin, _PickleUsingNameMixin,
-                _root=True):
-    """Parameter specification variable.
-
-    Usage::
-
-       P = ParamSpec('P')
-
-    Parameter specification variables exist primarily for the benefit of static
-    type checkers.  They are used to forward the parameter types of one
-    callable to another callable, a pattern commonly found in higher order
-    functions and decorators.  They are only valid when used in ``Concatenate``,
-    or as the first argument to ``Callable``, or as parameters for user-defined
-    Generics.  See class Generic for more information on generic types.  An
-    example for annotating a decorator::
-
-       T = TypeVar('T')
-       P = ParamSpec('P')
-
-       def add_logging(f: Callable[P, T]) -> Callable[P, T]:
-           '''A type-safe decorator to add logging to a function.'''
-           def inner(*args: P.args, **kwargs: P.kwargs) -> T:
-               logging.info(f'{f.__name__} was called')
-               return f(*args, **kwargs)
-           return inner
-
-       @add_logging
-       def add_two(x: float, y: float) -> float:
-           '''Add two numbers together.'''
-           return x + y
-
-    Parameter specification variables can be introspected. e.g.:
-
-       P.__name__ == 'P'
-
-    Note that only parameter specification variables defined in global scope can
-    be pickled.
-    """
-
-    @property
-    def args(self):
-        return ParamSpecArgs(self)
-
-    @property
-    def kwargs(self):
-        return ParamSpecKwargs(self)
-
-    def __init__(self, name, *, bound=None, covariant=False, contravariant=False):
-        self.__name__ = name
-        super().__init__(bound, covariant, contravariant)
-        def_mod = _caller()
-        if def_mod != 'typing':
-            self.__module__ = def_mod
-
-    def __typing_subst__(self, arg):
-        if isinstance(arg, (list, tuple)):
-            arg = tuple(_type_check(a, "Expected a type.") for a in arg)
-        elif not _is_param_expr(arg):
-            raise TypeError(f"Expected a list of types, an ellipsis, "
-                            f"ParamSpec, or Concatenate. Got {arg}")
-        return arg
-
-    def __typing_prepare_subst__(self, alias, args):
-        params = alias.__parameters__
-        i = params.index(self)
-        if i >= len(args):
-            raise TypeError(f"Too few arguments for {alias}")
-        # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.
-        if len(params) == 1 and not _is_param_expr(args[0]):
-            assert i == 0
-            args = (args,)
-        # Convert lists to tuples to help other libraries cache the results.
-        elif isinstance(args[i], list):
-            args = (*args[:i], tuple(args[i]), *args[i+1:])
-        return args
-
-def _is_dunder(attr):
-    return attr.startswith('__') and attr.endswith('__')
-
-class _BaseGenericAlias(_Final, _root=True):
-    """The central part of the internal API.
-
-    This represents a generic version of type 'origin' with type arguments 'params'.
-    There are two kind of these aliases: user defined and special. The special ones
-    are wrappers around builtin collections and ABCs in collections.abc. These must
-    have 'name' always set. If 'inst' is False, then the alias can't be instantiated;
-    this is used by e.g. typing.List and typing.Dict.
-    """
-
-    def __init__(self, origin, *, inst=True, name=None):
-        self._inst = inst
-        self._name = name
-        self.__origin__ = origin
-        self.__slots__ = None  # This is not documented.
-
-    def __call__(self, *args, **kwargs):
-        if not self._inst:
-            raise TypeError(f"Type {self._name} cannot be instantiated; "
-                            f"use {self.__origin__.__name__}() instead")
-        result = self.__origin__(*args, **kwargs)
-        try:
-            result.__orig_class__ = self
-        # Some objects raise TypeError (or something even more exotic)
-        # if you try to set attributes on them; we guard against that here
-        except Exception:
-            pass
-        return result
-
-    def __mro_entries__(self, bases):
-        res = []
-        if self.__origin__ not in bases:
-            res.append(self.__origin__)
-        i = bases.index(self)
-        for b in bases[i+1:]:
-            if isinstance(b, _BaseGenericAlias) or issubclass(b, Generic):
-                break
-        else:
-            res.append(Generic)
-        return tuple(res)
-
-    def __getattr__(self, attr):
-        if attr in {'__name__', '__qualname__'}:
-            return self._name or self.__origin__.__name__
-
-        # We are careful for copy and pickle.
-        # Also for simplicity we don't relay any dunder names
-        if '__origin__' in self.__dict__ and not _is_dunder(attr):
-            return getattr(self.__origin__, attr)
-        raise AttributeError(attr)
-
-    def __setattr__(self, attr, val):
-        if _is_dunder(attr) or attr in {'_name', '_inst', '_nparams',
-                                        '_paramspec_tvars'}:
-            super().__setattr__(attr, val)
-        else:
-            setattr(self.__origin__, attr, val)
-
-    def __instancecheck__(self, obj):
-        return self.__subclasscheck__(type(obj))
-
-    def __subclasscheck__(self, cls):
-        raise TypeError("Subscripted generics cannot be used with"
-                        " class and instance checks")
-
-    def __dir__(self):
-        return list(set(super().__dir__()
-                + [attr for attr in dir(self.__origin__) if not _is_dunder(attr)]))
-
-
-# Special typing constructs Union, Optional, Generic, Callable and Tuple
-# use three special attributes for internal bookkeeping of generic types:
-# * __parameters__ is a tuple of unique free type parameters of a generic
-#   type, for example, Dict[T, T].__parameters__ == (T,);
-# * __origin__ keeps a reference to a type that was subscripted,
-#   e.g., Union[T, int].__origin__ == Union, or the non-generic version of
-#   the type.
-# * __args__ is a tuple of all arguments used in subscripting,
-#   e.g., Dict[T, int].__args__ == (T, int).
-
-
-class _GenericAlias(_BaseGenericAlias, _root=True):
-    # The type of parameterized generics.
-    #
-    # That is, for example, `type(List[int])` is `_GenericAlias`.
-    #
-    # Objects which are instances of this class include:
-    # * Parameterized container types, e.g. `Tuple[int]`, `List[int]`.
-    #  * Note that native container types, e.g. `tuple`, `list`, use
-    #    `types.GenericAlias` instead.
-    # * Parameterized classes:
-    #     T = TypeVar('T')
-    #     class C(Generic[T]): pass
-    #     # C[int] is a _GenericAlias
-    # * `Callable` aliases, generic `Callable` aliases, and
-    #   parameterized `Callable` aliases:
-    #     T = TypeVar('T')
-    #     # _CallableGenericAlias inherits from _GenericAlias.
-    #     A = Callable[[], None]  # _CallableGenericAlias
-    #     B = Callable[[T], None]  # _CallableGenericAlias
-    #     C = B[int]  # _CallableGenericAlias
-    # * Parameterized `Final`, `ClassVar` and `TypeGuard`:
-    #     # All _GenericAlias
-    #     Final[int]
-    #     ClassVar[float]
-    #     TypeVar[bool]
-
-    def __init__(self, origin, args, *, inst=True, name=None,
-                 _paramspec_tvars=False):
-        super().__init__(origin, inst=inst, name=name)
-        if not isinstance(args, tuple):
-            args = (args,)
-        self.__args__ = tuple(... if a is _TypingEllipsis else
-                              a for a in args)
-        self.__parameters__ = _collect_parameters(args)
-        self._paramspec_tvars = _paramspec_tvars
-        if not name:
-            self.__module__ = origin.__module__
-
-    def __eq__(self, other):
-        if not isinstance(other, _GenericAlias):
-            return NotImplemented
-        return (self.__origin__ == other.__origin__
-                and self.__args__ == other.__args__)
-
-    def __hash__(self):
-        return hash((self.__origin__, self.__args__))
-
-    def __or__(self, right):
-        return Union[self, right]
-
-    def __ror__(self, left):
-        return Union[left, self]
-
-    @_tp_cache
-    def __getitem__(self, args):
-        # Parameterizes an already-parameterized object.
-        #
-        # For example, we arrive here doing something like:
-        #   T1 = TypeVar('T1')
-        #   T2 = TypeVar('T2')
-        #   T3 = TypeVar('T3')
-        #   class A(Generic[T1]): pass
-        #   B = A[T2]  # B is a _GenericAlias
-        #   C = B[T3]  # Invokes _GenericAlias.__getitem__
-        #
-        # We also arrive here when parameterizing a generic `Callable` alias:
-        #   T = TypeVar('T')
-        #   C = Callable[[T], None]
-        #   C[int]  # Invokes _GenericAlias.__getitem__
-
-        if self.__origin__ in (Generic, Protocol):
-            # Can't subscript Generic[...] or Protocol[...].
-            raise TypeError(f"Cannot subscript already-subscripted {self}")
-        if not self.__parameters__:
-            raise TypeError(f"{self} is not a generic class")
-
-        # Preprocess `args`.
-        if not isinstance(args, tuple):
-            args = (args,)
-        args = tuple(_type_convert(p) for p in args)
-        args = _unpack_args(args)
-        new_args = self._determine_new_args(args)
-        r = self.copy_with(new_args)
-        return r
-
-    def _determine_new_args(self, args):
-        # Determines new __args__ for __getitem__.
-        #
-        # For example, suppose we had:
-        #   T1 = TypeVar('T1')
-        #   T2 = TypeVar('T2')
-        #   class A(Generic[T1, T2]): pass
-        #   T3 = TypeVar('T3')
-        #   B = A[int, T3]
-        #   C = B[str]
-        # `B.__args__` is `(int, T3)`, so `C.__args__` should be `(int, str)`.
-        # Unfortunately, this is harder than it looks, because if `T3` is
-        # anything more exotic than a plain `TypeVar`, we need to consider
-        # edge cases.
-
-        params = self.__parameters__
-        # In the example above, this would be {T3: str}
-        for param in params:
-            prepare = getattr(param, '__typing_prepare_subst__', None)
-            if prepare is not None:
-                args = prepare(self, args)
-        alen = len(args)
-        plen = len(params)
-        if alen != plen:
-            raise TypeError(f"Too {'many' if alen > plen else 'few'} arguments for {self};"
-                            f" actual {alen}, expected {plen}")
-        new_arg_by_param = dict(zip(params, args))
-        return tuple(self._make_substitution(self.__args__, new_arg_by_param))
-
-    def _make_substitution(self, args, new_arg_by_param):
-        """Create a list of new type arguments."""
-        new_args = []
-        for old_arg in args:
-            if isinstance(old_arg, type):
-                new_args.append(old_arg)
-                continue
-
-            substfunc = getattr(old_arg, '__typing_subst__', None)
-            if substfunc:
-                new_arg = substfunc(new_arg_by_param[old_arg])
-            else:
-                subparams = getattr(old_arg, '__parameters__', ())
-                if not subparams:
-                    new_arg = old_arg
-                else:
-                    subargs = []
-                    for x in subparams:
-                        if isinstance(x, TypeVarTuple):
-                            subargs.extend(new_arg_by_param[x])
-                        else:
-                            subargs.append(new_arg_by_param[x])
-                    new_arg = old_arg[tuple(subargs)]
-
-            if self.__origin__ == collections.abc.Callable and isinstance(new_arg, tuple):
-                # Consider the following `Callable`.
-                #   C = Callable[[int], str]
-                # Here, `C.__args__` should be (int, str) - NOT ([int], str).
-                # That means that if we had something like...
-                #   P = ParamSpec('P')
-                #   T = TypeVar('T')
-                #   C = Callable[P, T]
-                #   D = C[[int, str], float]
-                # ...we need to be careful; `new_args` should end up as
-                # `(int, str, float)` rather than `([int, str], float)`.
-                new_args.extend(new_arg)
-            elif _is_unpacked_typevartuple(old_arg):
-                # Consider the following `_GenericAlias`, `B`:
-                #   class A(Generic[*Ts]): ...
-                #   B = A[T, *Ts]
-                # If we then do:
-                #   B[float, int, str]
-                # The `new_arg` corresponding to `T` will be `float`, and the
-                # `new_arg` corresponding to `*Ts` will be `(int, str)`. We
-                # should join all these types together in a flat list
-                # `(float, int, str)` - so again, we should `extend`.
-                new_args.extend(new_arg)
-            elif isinstance(old_arg, tuple):
-                # Corner case:
-                #    P = ParamSpec('P')
-                #    T = TypeVar('T')
-                #    class Base(Generic[P]): ...
-                # Can be substituted like this:
-                #    X = Base[[int, T]]
-                # In this case, `old_arg` will be a tuple:
-                new_args.append(
-                    tuple(self._make_substitution(old_arg, new_arg_by_param)),
-                )
-            else:
-                new_args.append(new_arg)
-        return new_args
-
-    def copy_with(self, args):
-        return self.__class__(self.__origin__, args, name=self._name, inst=self._inst,
-                              _paramspec_tvars=self._paramspec_tvars)
-
-    def __repr__(self):
-        if self._name:
-            name = 'typing.' + self._name
-        else:
-            name = _type_repr(self.__origin__)
-        if self.__args__:
-            args = ", ".join([_type_repr(a) for a in self.__args__])
-        else:
-            # To ensure the repr is eval-able.
-            args = "()"
-        return f'{name}[{args}]'
-
-    def __reduce__(self):
-        if self._name:
-            origin = globals()[self._name]
-        else:
-            origin = self.__origin__
-        args = tuple(self.__args__)
-        if len(args) == 1 and not isinstance(args[0], tuple):
-            args, = args
-        return operator.getitem, (origin, args)
-
-    def __mro_entries__(self, bases):
-        if isinstance(self.__origin__, _SpecialForm):
-            raise TypeError(f"Cannot subclass {self!r}")
-
-        if self._name:  # generic version of an ABC or built-in class
-            return super().__mro_entries__(bases)
-        if self.__origin__ is Generic:
-            if Protocol in bases:
-                return ()
-            i = bases.index(self)
-            for b in bases[i+1:]:
-                if isinstance(b, _BaseGenericAlias) and b is not self:
-                    return ()
-        return (self.__origin__,)
-
-    def __iter__(self):
-        yield Unpack[self]
-
-
-# _nparams is the number of accepted parameters, e.g. 0 for Hashable,
-# 1 for List and 2 for Dict.  It may be -1 if variable number of
-# parameters are accepted (needs custom __getitem__).
-
-class _SpecialGenericAlias(_NotIterable, _BaseGenericAlias, _root=True):
-    def __init__(self, origin, nparams, *, inst=True, name=None):
-        if name is None:
-            name = origin.__name__
-        super().__init__(origin, inst=inst, name=name)
-        self._nparams = nparams
-        if origin.__module__ == 'builtins':
-            self.__doc__ = f'A generic version of {origin.__qualname__}.'
-        else:
-            self.__doc__ = f'A generic version of {origin.__module__}.{origin.__qualname__}.'
-
-    @_tp_cache
-    def __getitem__(self, params):
-        if not isinstance(params, tuple):
-            params = (params,)
-        msg = "Parameters to generic types must be types."
-        params = tuple(_type_check(p, msg) for p in params)
-        _check_generic(self, params, self._nparams)
-        return self.copy_with(params)
-
-    def copy_with(self, params):
-        return _GenericAlias(self.__origin__, params,
-                             name=self._name, inst=self._inst)
-
-    def __repr__(self):
-        return 'typing.' + self._name
-
-    def __subclasscheck__(self, cls):
-        if isinstance(cls, _SpecialGenericAlias):
-            return issubclass(cls.__origin__, self.__origin__)
-        if not isinstance(cls, _GenericAlias):
-            return issubclass(cls, self.__origin__)
-        return super().__subclasscheck__(cls)
-
-    def __reduce__(self):
-        return self._name
-
-    def __or__(self, right):
-        return Union[self, right]
-
-    def __ror__(self, left):
-        return Union[left, self]
-
-class _CallableGenericAlias(_NotIterable, _GenericAlias, _root=True):
-    def __repr__(self):
-        assert self._name == 'Callable'
-        args = self.__args__
-        if len(args) == 2 and _is_param_expr(args[0]):
-            return super().__repr__()
-        return (f'typing.Callable'
-                f'[[{", ".join([_type_repr(a) for a in args[:-1]])}], '
-                f'{_type_repr(args[-1])}]')
-
-    def __reduce__(self):
-        args = self.__args__
-        if not (len(args) == 2 and _is_param_expr(args[0])):
-            args = list(args[:-1]), args[-1]
-        return operator.getitem, (Callable, args)
-
-
-class _CallableType(_SpecialGenericAlias, _root=True):
-    def copy_with(self, params):
-        return _CallableGenericAlias(self.__origin__, params,
-                                     name=self._name, inst=self._inst,
-                                     _paramspec_tvars=True)
-
-    def __getitem__(self, params):
-        if not isinstance(params, tuple) or len(params) != 2:
-            raise TypeError("Callable must be used as "
-                            "Callable[[arg, ...], result].")
-        args, result = params
-        # This relaxes what args can be on purpose to allow things like
-        # PEP 612 ParamSpec.  Responsibility for whether a user is using
-        # Callable[...] properly is deferred to static type checkers.
-        if isinstance(args, list):
-            params = (tuple(args), result)
-        else:
-            params = (args, result)
-        return self.__getitem_inner__(params)
-
-    @_tp_cache
-    def __getitem_inner__(self, params):
-        args, result = params
-        msg = "Callable[args, result]: result must be a type."
-        result = _type_check(result, msg)
-        if args is Ellipsis:
-            return self.copy_with((_TypingEllipsis, result))
-        if not isinstance(args, tuple):
-            args = (args,)
-        args = tuple(_type_convert(arg) for arg in args)
-        params = args + (result,)
-        return self.copy_with(params)
-
-
-class _TupleType(_SpecialGenericAlias, _root=True):
-    @_tp_cache
-    def __getitem__(self, params):
-        if not isinstance(params, tuple):
-            params = (params,)
-        if len(params) >= 2 and params[-1] is ...:
-            msg = "Tuple[t, ...]: t must be a type."
-            params = tuple(_type_check(p, msg) for p in params[:-1])
-            return self.copy_with((*params, _TypingEllipsis))
-        msg = "Tuple[t0, t1, ...]: each t must be a type."
-        params = tuple(_type_check(p, msg) for p in params)
-        return self.copy_with(params)
-
-
-class _UnionGenericAlias(_NotIterable, _GenericAlias, _root=True):
-    def copy_with(self, params):
-        return Union[params]
-
-    def __eq__(self, other):
-        if not isinstance(other, (_UnionGenericAlias, types.UnionType)):
-            return NotImplemented
-        try:  # fast path
-            return set(self.__args__) == set(other.__args__)
-        except TypeError:  # not hashable, slow path
-            return _compare_args_orderless(self.__args__, other.__args__)
-
-    def __hash__(self):
-        return hash(frozenset(self.__args__))
-
-    def __repr__(self):
-        args = self.__args__
-        if len(args) == 2:
-            if args[0] is type(None):
-                return f'typing.Optional[{_type_repr(args[1])}]'
-            elif args[1] is type(None):
-                return f'typing.Optional[{_type_repr(args[0])}]'
-        return super().__repr__()
-
-    def __instancecheck__(self, obj):
-        return self.__subclasscheck__(type(obj))
-
-    def __subclasscheck__(self, cls):
-        for arg in self.__args__:
-            if issubclass(cls, arg):
-                return True
-
-    def __reduce__(self):
-        func, (origin, args) = super().__reduce__()
-        return func, (Union, args)
-
-
-def _value_and_type_iter(parameters):
-    return ((p, type(p)) for p in parameters)
-
-
-class _LiteralGenericAlias(_GenericAlias, _root=True):
-    def __eq__(self, other):
-        if not isinstance(other, _LiteralGenericAlias):
-            return NotImplemented
-
-        return set(_value_and_type_iter(self.__args__)) == set(_value_and_type_iter(other.__args__))
-
-    def __hash__(self):
-        return hash(frozenset(_value_and_type_iter(self.__args__)))
-
-
-class _ConcatenateGenericAlias(_GenericAlias, _root=True):
-    def copy_with(self, params):
-        if isinstance(params[-1], (list, tuple)):
-            return (*params[:-1], *params[-1])
-        if isinstance(params[-1], _ConcatenateGenericAlias):
-            params = (*params[:-1], *params[-1].__args__)
-        return super().copy_with(params)
-
-
-@_SpecialForm
-def Unpack(self, parameters):
-    """Type unpack operator.
-
-    The type unpack operator takes the child types from some container type,
-    such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'.
-
-    For example::
-
-        # For some generic class `Foo`:
-        Foo[Unpack[tuple[int, str]]]  # Equivalent to Foo[int, str]
-
-        Ts = TypeVarTuple('Ts')
-        # Specifies that `Bar` is generic in an arbitrary number of types.
-        # (Think of `Ts` as a tuple of an arbitrary number of individual
-        #  `TypeVar`s, which the `Unpack` is 'pulling out' directly into the
-        #  `Generic[]`.)
-        class Bar(Generic[Unpack[Ts]]): ...
-        Bar[int]  # Valid
-        Bar[int, str]  # Also valid
-
-    From Python 3.11, this can also be done using the `*` operator::
-
-        Foo[*tuple[int, str]]
-        class Bar(Generic[*Ts]): ...
-
-    Note that there is only some runtime checking of this operator. Not
-    everything the runtime allows may be accepted by static type checkers.
-
-    For more information, see PEP 646.
-    """
-    item = _type_check(parameters, f'{self} accepts only single type.')
-    return _UnpackGenericAlias(origin=self, args=(item,))
-
-
-class _UnpackGenericAlias(_GenericAlias, _root=True):
-    def __repr__(self):
-        # `Unpack` only takes one argument, so __args__ should contain only
-        # a single item.
-        return '*' + repr(self.__args__[0])
-
-    def __getitem__(self, args):
-        if self.__typing_is_unpacked_typevartuple__:
-            return args
-        return super().__getitem__(args)
-
-    @property
-    def __typing_unpacked_tuple_args__(self):
-        assert self.__origin__ is Unpack
-        assert len(self.__args__) == 1
-        arg, = self.__args__
-        if isinstance(arg, _GenericAlias):
-            assert arg.__origin__ is tuple
-            return arg.__args__
-        return None
-
-    @property
-    def __typing_is_unpacked_typevartuple__(self):
-        assert self.__origin__ is Unpack
-        assert len(self.__args__) == 1
-        return isinstance(self.__args__[0], TypeVarTuple)
-
-
-class Generic:
-    """Abstract base class for generic types.
-
-    A generic type is typically declared by inheriting from
-    this class parameterized with one or more type variables.
-    For example, a generic mapping type might be defined as::
-
-      class Mapping(Generic[KT, VT]):
-          def __getitem__(self, key: KT) -> VT:
-              ...
-          # Etc.
-
-    This class can then be used as follows::
-
-      def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
-          try:
-              return mapping[key]
-          except KeyError:
-              return default
-    """
-    __slots__ = ()
-    _is_protocol = False
-
-    @_tp_cache
-    def __class_getitem__(cls, params):
-        """Parameterizes a generic class.
-
-        At least, parameterizing a generic class is the *main* thing this method
-        does. For example, for some generic class `Foo`, this is called when we
-        do `Foo[int]` - there, with `cls=Foo` and `params=int`.
-
-        However, note that this method is also called when defining generic
-        classes in the first place with `class Foo(Generic[T]): ...`.
-        """
-        if not isinstance(params, tuple):
-            params = (params,)
-
-        params = tuple(_type_convert(p) for p in params)
-        if cls in (Generic, Protocol):
-            # Generic and Protocol can only be subscripted with unique type variables.
-            if not params:
-                raise TypeError(
-                    f"Parameter list to {cls.__qualname__}[...] cannot be empty"
-                )
-            if not all(_is_typevar_like(p) for p in params):
-                raise TypeError(
-                    f"Parameters to {cls.__name__}[...] must all be type variables "
-                    f"or parameter specification variables.")
-            if len(set(params)) != len(params):
-                raise TypeError(
-                    f"Parameters to {cls.__name__}[...] must all be unique")
-        else:
-            # Subscripting a regular Generic subclass.
-            for param in cls.__parameters__:
-                prepare = getattr(param, '__typing_prepare_subst__', None)
-                if prepare is not None:
-                    params = prepare(cls, params)
-            _check_generic(cls, params, len(cls.__parameters__))
-
-            new_args = []
-            for param, new_arg in zip(cls.__parameters__, params):
-                if isinstance(param, TypeVarTuple):
-                    new_args.extend(new_arg)
-                else:
-                    new_args.append(new_arg)
-            params = tuple(new_args)
-
-        return _GenericAlias(cls, params,
-                             _paramspec_tvars=True)
-
-    def __init_subclass__(cls, *args, **kwargs):
-        super().__init_subclass__(*args, **kwargs)
-        tvars = []
-        if '__orig_bases__' in cls.__dict__:
-            error = Generic in cls.__orig_bases__
-        else:
-            error = (Generic in cls.__bases__ and
-                        cls.__name__ != 'Protocol' and
-                        type(cls) != _TypedDictMeta)
-        if error:
-            raise TypeError("Cannot inherit from plain Generic")
-        if '__orig_bases__' in cls.__dict__:
-            tvars = _collect_parameters(cls.__orig_bases__)
-            # Look for Generic[T1, ..., Tn].
-            # If found, tvars must be a subset of it.
-            # If not found, tvars is it.
-            # Also check for and reject plain Generic,
-            # and reject multiple Generic[...].
-            gvars = None
-            for base in cls.__orig_bases__:
-                if (isinstance(base, _GenericAlias) and
-                        base.__origin__ is Generic):
-                    if gvars is not None:
-                        raise TypeError(
-                            "Cannot inherit from Generic[...] multiple times.")
-                    gvars = base.__parameters__
-            if gvars is not None:
-                tvarset = set(tvars)
-                gvarset = set(gvars)
-                if not tvarset <= gvarset:
-                    s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
-                    s_args = ', '.join(str(g) for g in gvars)
-                    raise TypeError(f"Some type variables ({s_vars}) are"
-                                    f" not listed in Generic[{s_args}]")
-                tvars = gvars
-        cls.__parameters__ = tuple(tvars)
-
-
-class _TypingEllipsis:
-    """Internal placeholder for ... (ellipsis)."""
-
-
-_TYPING_INTERNALS = ['__parameters__', '__orig_bases__',  '__orig_class__',
-                     '_is_protocol', '_is_runtime_protocol', '__final__']
-
-_SPECIAL_NAMES = ['__abstractmethods__', '__annotations__', '__dict__', '__doc__',
-                  '__init__', '__module__', '__new__', '__slots__',
-                  '__subclasshook__', '__weakref__', '__class_getitem__']
-
-# These special attributes will be not collected as protocol members.
-EXCLUDED_ATTRIBUTES = _TYPING_INTERNALS + _SPECIAL_NAMES + ['_MutableMapping__marker']
-
-
-def _get_protocol_attrs(cls):
-    """Collect protocol members from a protocol class objects.
-
-    This includes names actually defined in the class dictionary, as well
-    as names that appear in annotations. Special names (above) are skipped.
-    """
-    attrs = set()
-    for base in cls.__mro__[:-1]:  # without object
-        if base.__name__ in ('Protocol', 'Generic'):
-            continue
-        annotations = getattr(base, '__annotations__', {})
-        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
-            if not attr.startswith('_abc_') and attr not in EXCLUDED_ATTRIBUTES:
-                attrs.add(attr)
-    return attrs
-
-
-def _is_callable_members_only(cls):
-    # PEP 544 prohibits using issubclass() with protocols that have non-method members.
-    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
-
-
-def _no_init_or_replace_init(self, *args, **kwargs):
-    cls = type(self)
-
-    if cls._is_protocol:
-        raise TypeError('Protocols cannot be instantiated')
-
-    # Already using a custom `__init__`. No need to calculate correct
-    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
-    if cls.__init__ is not _no_init_or_replace_init:
-        return
-
-    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
-    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
-    # searches for a proper new `__init__` in the MRO. The new `__init__`
-    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
-    # instantiation of the protocol subclass will thus use the new
-    # `__init__` and no longer call `_no_init_or_replace_init`.
-    for base in cls.__mro__:
-        init = base.__dict__.get('__init__', _no_init_or_replace_init)
-        if init is not _no_init_or_replace_init:
-            cls.__init__ = init
-            break
-    else:
-        # should not happen
-        cls.__init__ = object.__init__
-
-    cls.__init__(self, *args, **kwargs)
-
-
-def _caller(depth=1, default='__main__'):
-    try:
-        return sys._getframe(depth + 1).f_globals.get('__name__', default)
-    except (AttributeError, ValueError):  # For platforms without _getframe()
-        return None
-
-
-def _allow_reckless_class_checks(depth=3):
-    """Allow instance and class checks for special stdlib modules.
-
-    The abc and functools modules indiscriminately call isinstance() and
-    issubclass() on the whole MRO of a user class, which may contain protocols.
-    """
-    return _caller(depth) in {'abc', 'functools', None}
-
-
-_PROTO_ALLOWLIST = {
-    'collections.abc': [
-        'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable',
-        'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
-    ],
-    'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'],
-}
-
-
-class _ProtocolMeta(ABCMeta):
-    # This metaclass is really unfortunate and exists only because of
-    # the lack of __instancehook__.
-    def __instancecheck__(cls, instance):
-        # We need this method for situations where attributes are
-        # assigned in __init__.
-        if (
-            getattr(cls, '_is_protocol', False) and
-            not getattr(cls, '_is_runtime_protocol', False) and
-            not _allow_reckless_class_checks(depth=2)
-        ):
-            raise TypeError("Instance and class checks can only be used with"
-                            " @runtime_checkable protocols")
-
-        if ((not getattr(cls, '_is_protocol', False) or
-                _is_callable_members_only(cls)) and
-                issubclass(instance.__class__, cls)):
-            return True
-        if cls._is_protocol:
-            if all(hasattr(instance, attr) and
-                    # All *methods* can be blocked by setting them to None.
-                    (not callable(getattr(cls, attr, None)) or
-                     getattr(instance, attr) is not None)
-                    for attr in _get_protocol_attrs(cls)):
-                return True
-        return super().__instancecheck__(instance)
-
-
-class Protocol(Generic, metaclass=_ProtocolMeta):
-    """Base class for protocol classes.
-
-    Protocol classes are defined as::
-
-        class Proto(Protocol):
-            def meth(self) -> int:
-                ...
-
-    Such classes are primarily used with static type checkers that recognize
-    structural subtyping (static duck-typing).
-
-    For example::
-
-        class C:
-            def meth(self) -> int:
-                return 0
-
-        def func(x: Proto) -> int:
-            return x.meth()
-
-        func(C())  # Passes static type check
-
-    See PEP 544 for details. Protocol classes decorated with
-    @typing.runtime_checkable act as simple-minded runtime protocols that check
-    only the presence of given attributes, ignoring their type signatures.
-    Protocol classes can be generic, they are defined as::
-
-        class GenProto(Protocol[T]):
-            def meth(self) -> T:
-                ...
-    """
-
-    __slots__ = ()
-    _is_protocol = True
-    _is_runtime_protocol = False
-
-    def __init_subclass__(cls, *args, **kwargs):
-        super().__init_subclass__(*args, **kwargs)
-
-        # Determine if this is a protocol or a concrete subclass.
-        if not cls.__dict__.get('_is_protocol', False):
-            cls._is_protocol = any(b is Protocol for b in cls.__bases__)
-
-        # Set (or override) the protocol subclass hook.
-        def _proto_hook(other):
-            if not cls.__dict__.get('_is_protocol', False):
-                return NotImplemented
-
-            # First, perform various sanity checks.
-            if not getattr(cls, '_is_runtime_protocol', False):
-                if _allow_reckless_class_checks():
-                    return NotImplemented
-                raise TypeError("Instance and class checks can only be used with"
-                                " @runtime_checkable protocols")
-            if not _is_callable_members_only(cls):
-                if _allow_reckless_class_checks():
-                    return NotImplemented
-                raise TypeError("Protocols with non-method members"
-                                " don't support issubclass()")
-            if not isinstance(other, type):
-                # Same error message as for issubclass(1, int).
-                raise TypeError('issubclass() arg 1 must be a class')
-
-            # Second, perform the actual structural compatibility check.
-            for attr in _get_protocol_attrs(cls):
-                for base in other.__mro__:
-                    # Check if the members appears in the class dictionary...
-                    if attr in base.__dict__:
-                        if base.__dict__[attr] is None:
-                            return NotImplemented
-                        break
-
-                    # ...or in annotations, if it is a sub-protocol.
-                    annotations = getattr(base, '__annotations__', {})
-                    if (isinstance(annotations, collections.abc.Mapping) and
-                            attr in annotations and
-                            issubclass(other, Generic) and other._is_protocol):
-                        break
-                else:
-                    return NotImplemented
-            return True
-
-        if '__subclasshook__' not in cls.__dict__:
-            cls.__subclasshook__ = _proto_hook
-
-        # We have nothing more to do for non-protocols...
-        if not cls._is_protocol:
-            return
-
-        # ... otherwise check consistency of bases, and prohibit instantiation.
-        for base in cls.__bases__:
-            if not (base in (object, Generic) or
-                    base.__module__ in _PROTO_ALLOWLIST and
-                    base.__name__ in _PROTO_ALLOWLIST[base.__module__] or
-                    issubclass(base, Generic) and base._is_protocol):
-                raise TypeError('Protocols can only inherit from other'
-                                ' protocols, got %r' % base)
-        if cls.__init__ is Protocol.__init__:
-            cls.__init__ = _no_init_or_replace_init
-
-
-class _AnnotatedAlias(_NotIterable, _GenericAlias, _root=True):
-    """Runtime representation of an annotated type.
-
-    At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
-    with extra annotations. The alias behaves like a normal typing alias.
-    Instantiating is the same as instantiating the underlying type; binding
-    it to types is also the same.
-
-    The metadata itself is stored in a '__metadata__' attribute as a tuple.
-    """
-
-    def __init__(self, origin, metadata):
-        if isinstance(origin, _AnnotatedAlias):
-            metadata = origin.__metadata__ + metadata
-            origin = origin.__origin__
-        super().__init__(origin, origin)
-        self.__metadata__ = metadata
-
-    def copy_with(self, params):
-        assert len(params) == 1
-        new_type = params[0]
-        return _AnnotatedAlias(new_type, self.__metadata__)
-
-    def __repr__(self):
-        return "typing.Annotated[{}, {}]".format(
-            _type_repr(self.__origin__),
-            ", ".join(repr(a) for a in self.__metadata__)
-        )
-
-    def __reduce__(self):
-        return operator.getitem, (
-            Annotated, (self.__origin__,) + self.__metadata__
-        )
-
-    def __eq__(self, other):
-        if not isinstance(other, _AnnotatedAlias):
-            return NotImplemented
-        return (self.__origin__ == other.__origin__
-                and self.__metadata__ == other.__metadata__)
-
-    def __hash__(self):
-        return hash((self.__origin__, self.__metadata__))
-
-    def __getattr__(self, attr):
-        if attr in {'__name__', '__qualname__'}:
-            return 'Annotated'
-        return super().__getattr__(attr)
-
-
-class Annotated:
-    """Add context-specific metadata to a type.
-
-    Example: Annotated[int, runtime_check.Unsigned] indicates to the
-    hypothetical runtime_check module that this type is an unsigned int.
-    Every other consumer of this type can ignore this metadata and treat
-    this type as int.
-
-    The first argument to Annotated must be a valid type.
-
-    Details:
-
-    - It's an error to call `Annotated` with less than two arguments.
-    - Access the metadata via the ``__metadata__`` attribute::
-
-        assert Annotated[int, '$'].__metadata__ == ('$',)
-
-    - Nested Annotated types are flattened::
-
-        assert Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
-
-    - Instantiating an annotated type is equivalent to instantiating the
-    underlying type::
-
-        assert Annotated[C, Ann1](5) == C(5)
-
-    - Annotated can be used as a generic type alias::
-
-        Optimized: TypeAlias = Annotated[T, runtime.Optimize()]
-        assert Optimized[int] == Annotated[int, runtime.Optimize()]
-
-        OptimizedList: TypeAlias = Annotated[list[T], runtime.Optimize()]
-        assert OptimizedList[int] == Annotated[list[int], runtime.Optimize()]
-
-    - Annotated cannot be used with an unpacked TypeVarTuple::
-
-        Variadic: TypeAlias = Annotated[*Ts, Ann1]  # NOT valid
-
-      This would be equivalent to::
-
-        Annotated[T1, T2, T3, ..., Ann1]
-
-      where T1, T2 etc. are TypeVars, which would be invalid, because
-      only one type should be passed to Annotated.
-    """
-
-    __slots__ = ()
-
-    def __new__(cls, *args, **kwargs):
-        raise TypeError("Type Annotated cannot be instantiated.")
-
-    def __class_getitem__(cls, params):
-        if not isinstance(params, tuple):
-            params = (params,)
-        return cls._class_getitem_inner(cls, *params)
-
-    @_tp_cache(typed=True)
-    def _class_getitem_inner(cls, *params):
-        if len(params) < 2:
-            raise TypeError("Annotated[...] should be used "
-                            "with at least two arguments (a type and an "
-                            "annotation).")
-        if _is_unpacked_typevartuple(params[0]):
-            raise TypeError("Annotated[...] should not be used with an "
-                            "unpacked TypeVarTuple")
-        msg = "Annotated[t, ...]: t must be a type."
-        origin = _type_check(params[0], msg, allow_special_forms=True)
-        metadata = tuple(params[1:])
-        return _AnnotatedAlias(origin, metadata)
-
-    def __init_subclass__(cls, *args, **kwargs):
-        raise TypeError(
-            "Cannot subclass {}.Annotated".format(cls.__module__)
-        )
-
-
-def runtime_checkable(cls):
-    """Mark a protocol class as a runtime protocol.
-
-    Such protocol can be used with isinstance() and issubclass().
-    Raise TypeError if applied to a non-protocol class.
-    This allows a simple-minded structural check very similar to
-    one trick ponies in collections.abc such as Iterable.
-
-    For example::
-
-        @runtime_checkable
-        class Closable(Protocol):
-            def close(self): ...
-
-        assert isinstance(open('/some/file'), Closable)
-
-    Warning: this will check only the presence of the required methods,
-    not their type signatures!
-    """
-    if not issubclass(cls, Generic) or not cls._is_protocol:
-        raise TypeError('@runtime_checkable can be only applied to protocol classes,'
-                        ' got %r' % cls)
-    cls._is_runtime_protocol = True
-    return cls
-
-
-def cast(typ, val):
-    """Cast a value to a type.
-
-    This returns the value unchanged.  To the type checker this
-    signals that the return value has the designated type, but at
-    runtime we intentionally don't check anything (we want this
-    to be as fast as possible).
-    """
-    return val
-
-
-def assert_type(val, typ, /):
-    """Ask a static type checker to confirm that the value is of the given type.
-
-    At runtime this does nothing: it returns the first argument unchanged with no
-    checks or side effects, no matter the actual type of the argument.
-
-    When a static type checker encounters a call to assert_type(), it
-    emits an error if the value is not of the specified type::
-
-        def greet(name: str) -> None:
-            assert_type(name, str)  # OK
-            assert_type(name, int)  # type checker error
-    """
-    return val
-
-
-_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
-                  types.MethodType, types.ModuleType,
-                  WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
-
-
-def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
-    """Return type hints for an object.
-
-    This is often the same as obj.__annotations__, but it handles
-    forward references encoded as string literals and recursively replaces all
-    'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
-
-    The argument may be a module, class, method, or function. The annotations
-    are returned as a dictionary. For classes, annotations include also
-    inherited members.
-
-    TypeError is raised if the argument is not of a type that can contain
-    annotations, and an empty dictionary is returned if no annotations are
-    present.
-
-    BEWARE -- the behavior of globalns and localns is counterintuitive
-    (unless you are familiar with how eval() and exec() work).  The
-    search order is locals first, then globals.
-
-    - If no dict arguments are passed, an attempt is made to use the
-      globals from obj (or the respective module's globals for classes),
-      and these are also used as the locals.  If the object does not appear
-      to have globals, an empty dictionary is used.  For classes, the search
-      order is globals first then locals.
-
-    - If one dict argument is passed, it is used for both globals and
-      locals.
-
-    - If two dict arguments are passed, they specify globals and
-      locals, respectively.
-    """
-    if getattr(obj, '__no_type_check__', None):
-        return {}
-    # Classes require a special treatment.
-    if isinstance(obj, type):
-        hints = {}
-        for base in reversed(obj.__mro__):
-            if globalns is None:
-                base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
-            else:
-                base_globals = globalns
-            ann = base.__dict__.get('__annotations__', {})
-            if isinstance(ann, types.GetSetDescriptorType):
-                ann = {}
-            base_locals = dict(vars(base)) if localns is None else localns
-            if localns is None and globalns is None:
-                # This is surprising, but required.  Before Python 3.10,
-                # get_type_hints only evaluated the globalns of
-                # a class.  To maintain backwards compatibility, we reverse
-                # the globalns and localns order so that eval() looks into
-                # *base_globals* first rather than *base_locals*.
-                # This only affects ForwardRefs.
-                base_globals, base_locals = base_locals, base_globals
-            for name, value in ann.items():
-                if value is None:
-                    value = type(None)
-                if isinstance(value, str):
-                    value = ForwardRef(value, is_argument=False, is_class=True)
-                value = _eval_type(value, base_globals, base_locals)
-                hints[name] = value
-        return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}
-
-    if globalns is None:
-        if isinstance(obj, types.ModuleType):
-            globalns = obj.__dict__
-        else:
-            nsobj = obj
-            # Find globalns for the unwrapped object.
-            while hasattr(nsobj, '__wrapped__'):
-                nsobj = nsobj.__wrapped__
-            globalns = getattr(nsobj, '__globals__', {})
-        if localns is None:
-            localns = globalns
-    elif localns is None:
-        localns = globalns
-    hints = getattr(obj, '__annotations__', None)
-    if hints is None:
-        # Return empty annotations for something that _could_ have them.
-        if isinstance(obj, _allowed_types):
-            return {}
-        else:
-            raise TypeError('{!r} is not a module, class, method, '
-                            'or function.'.format(obj))
-    hints = dict(hints)
-    for name, value in hints.items():
-        if value is None:
-            value = type(None)
-        if isinstance(value, str):
-            # class-level forward refs were handled above, this must be either
-            # a module-level annotation or a function argument annotation
-            value = ForwardRef(
-                value,
-                is_argument=not isinstance(obj, types.ModuleType),
-                is_class=False,
-            )
-        hints[name] = _eval_type(value, globalns, localns)
-    return hints if include_extras else {k: _strip_annotations(t) for k, t in hints.items()}
-
-
-def _strip_annotations(t):
-    """Strip the annotations from a given type."""
-    if isinstance(t, _AnnotatedAlias):
-        return _strip_annotations(t.__origin__)
-    if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
-        return _strip_annotations(t.__args__[0])
-    if isinstance(t, _GenericAlias):
-        stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
-        if stripped_args == t.__args__:
-            return t
-        return t.copy_with(stripped_args)
-    if isinstance(t, GenericAlias):
-        stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
-        if stripped_args == t.__args__:
-            return t
-        return GenericAlias(t.__origin__, stripped_args)
-    if isinstance(t, types.UnionType):
-        stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
-        if stripped_args == t.__args__:
-            return t
-        return functools.reduce(operator.or_, stripped_args)
-
-    return t
-
-
-def get_origin(tp):
-    """Get the unsubscripted version of a type.
-
-    This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar,
-    Annotated, and others. Return None for unsupported types.
-
-    Examples::
-
-        >>> P = ParamSpec('P')
-        >>> assert get_origin(Literal[42]) is Literal
-        >>> assert get_origin(int) is None
-        >>> assert get_origin(ClassVar[int]) is ClassVar
-        >>> assert get_origin(Generic) is Generic
-        >>> assert get_origin(Generic[T]) is Generic
-        >>> assert get_origin(Union[T, int]) is Union
-        >>> assert get_origin(List[Tuple[T, T]][int]) is list
-        >>> assert get_origin(P.args) is P
-    """
-    if isinstance(tp, _AnnotatedAlias):
-        return Annotated
-    if isinstance(tp, (_BaseGenericAlias, GenericAlias,
-                       ParamSpecArgs, ParamSpecKwargs)):
-        return tp.__origin__
-    if tp is Generic:
-        return Generic
-    if isinstance(tp, types.UnionType):
-        return types.UnionType
-    return None
-
-
-def get_args(tp):
-    """Get type arguments with all substitutions performed.
-
-    For unions, basic simplifications used by Union constructor are performed.
-
-    Examples::
-
-        >>> T = TypeVar('T')
-        >>> assert get_args(Dict[str, int]) == (str, int)
-        >>> assert get_args(int) == ()
-        >>> assert get_args(Union[int, Union[T, int], str][int]) == (int, str)
-        >>> assert get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
-        >>> assert get_args(Callable[[], T][int]) == ([], int)
-    """
-    if isinstance(tp, _AnnotatedAlias):
-        return (tp.__origin__,) + tp.__metadata__
-    if isinstance(tp, (_GenericAlias, GenericAlias)):
-        res = tp.__args__
-        if _should_unflatten_callable_args(tp, res):
-            res = (list(res[:-1]), res[-1])
-        return res
-    if isinstance(tp, types.UnionType):
-        return tp.__args__
-    return ()
-
-
-def is_typeddict(tp):
-    """Check if an annotation is a TypedDict class.
-
-    For example::
-
-        >>> from typing import TypedDict
-        >>> class Film(TypedDict):
-        ...     title: str
-        ...     year: int
-        ...
-        >>> is_typeddict(Film)
-        True
-        >>> is_typeddict(dict)
-        False
-    """
-    return isinstance(tp, _TypedDictMeta)
-
-
-_ASSERT_NEVER_REPR_MAX_LENGTH = 100
-
-
-def assert_never(arg: Never, /) -> Never:
-    """Statically assert that a line of code is unreachable.
-
-    Example::
-
-        def int_or_str(arg: int | str) -> None:
-            match arg:
-                case int():
-                    print("It's an int")
-                case str():
-                    print("It's a str")
-                case _:
-                    assert_never(arg)
-
-    If a type checker finds that a call to assert_never() is
-    reachable, it will emit an error.
-
-    At runtime, this throws an exception when called.
-    """
-    value = repr(arg)
-    if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH:
-        value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...'
-    raise AssertionError(f"Expected code to be unreachable, but got: {value}")
-
-
-def no_type_check(arg):
-    """Decorator to indicate that annotations are not type hints.
-
-    The argument must be a class or function; if it is a class, it
-    applies recursively to all methods and classes defined in that class
-    (but not to methods defined in its superclasses or subclasses).
-
-    This mutates the function(s) or class(es) in place.
-    """
-    if isinstance(arg, type):
-        for key in dir(arg):
-            obj = getattr(arg, key)
-            if (
-                not hasattr(obj, '__qualname__')
-                or obj.__qualname__ != f'{arg.__qualname__}.{obj.__name__}'
-                or getattr(obj, '__module__', None) != arg.__module__
-            ):
-                # We only modify objects that are defined in this type directly.
-                # If classes / methods are nested in multiple layers,
-                # we will modify them when processing their direct holders.
-                continue
-            # Instance, class, and static methods:
-            if isinstance(obj, types.FunctionType):
-                obj.__no_type_check__ = True
-            if isinstance(obj, types.MethodType):
-                obj.__func__.__no_type_check__ = True
-            # Nested types:
-            if isinstance(obj, type):
-                no_type_check(obj)
-    try:
-        arg.__no_type_check__ = True
-    except TypeError:  # built-in classes
-        pass
-    return arg
-
-
-def no_type_check_decorator(decorator):
-    """Decorator to give another decorator the @no_type_check effect.
-
-    This wraps the decorator with something that wraps the decorated
-    function in @no_type_check.
-    """
-    @functools.wraps(decorator)
-    def wrapped_decorator(*args, **kwds):
-        func = decorator(*args, **kwds)
-        func = no_type_check(func)
-        return func
-
-    return wrapped_decorator
-
-
-def _overload_dummy(*args, **kwds):
-    """Helper for @overload to raise when called."""
-    raise NotImplementedError(
-        "You should not call an overloaded function. "
-        "A series of @overload-decorated functions "
-        "outside a stub module should always be followed "
-        "by an implementation that is not @overload-ed.")
-
-
-# {module: {qualname: {firstlineno: func}}}
-_overload_registry = defaultdict(functools.partial(defaultdict, dict))
-
-
-def overload(func):
-    """Decorator for overloaded functions/methods.
-
-    In a stub file, place two or more stub definitions for the same
-    function in a row, each decorated with @overload.
-
-    For example::
-
-        @overload
-        def utf8(value: None) -> None: ...
-        @overload
-        def utf8(value: bytes) -> bytes: ...
-        @overload
-        def utf8(value: str) -> bytes: ...
-
-    In a non-stub file (i.e. a regular .py file), do the same but
-    follow it with an implementation.  The implementation should *not*
-    be decorated with @overload::
-
-        @overload
-        def utf8(value: None) -> None: ...
-        @overload
-        def utf8(value: bytes) -> bytes: ...
-        @overload
-        def utf8(value: str) -> bytes: ...
-        def utf8(value):
-            ...  # implementation goes here
-
-    The overloads for a function can be retrieved at runtime using the
-    get_overloads() function.
-    """
-    # classmethod and staticmethod
-    f = getattr(func, "__func__", func)
-    try:
-        _overload_registry[f.__module__][f.__qualname__][f.__code__.co_firstlineno] = func
-    except AttributeError:
-        # Not a normal function; ignore.
-        pass
-    return _overload_dummy
-
-
-def get_overloads(func):
-    """Return all defined overloads for *func* as a sequence."""
-    # classmethod and staticmethod
-    f = getattr(func, "__func__", func)
-    if f.__module__ not in _overload_registry:
-        return []
-    mod_dict = _overload_registry[f.__module__]
-    if f.__qualname__ not in mod_dict:
-        return []
-    return list(mod_dict[f.__qualname__].values())
-
-
-def clear_overloads():
-    """Clear all overloads in the registry."""
-    _overload_registry.clear()
-
-
-def final(f):
-    """Decorator to indicate final methods and final classes.
-
-    Use this decorator to indicate to type checkers that the decorated
-    method cannot be overridden, and decorated class cannot be subclassed.
-
-    For example::
-
-        class Base:
-            @final
-            def done(self) -> None:
-                ...
-        class Sub(Base):
-            def done(self) -> None:  # Error reported by type checker
-                ...
-
-        @final
-        class Leaf:
-            ...
-        class Other(Leaf):  # Error reported by type checker
-            ...
-
-    There is no runtime checking of these properties. The decorator
-    attempts to set the ``__final__`` attribute to ``True`` on the decorated
-    object to allow runtime introspection.
-    """
-    try:
-        f.__final__ = True
-    except (AttributeError, TypeError):
-        # Skip the attribute silently if it is not writable.
-        # AttributeError happens if the object has __slots__ or a
-        # read-only property, TypeError if it's a builtin class.
-        pass
-    return f
-
-
-# Some unconstrained type variables.  These are used by the container types.
-# (These are not for export.)
-T = TypeVar('T')  # Any type.
-KT = TypeVar('KT')  # Key type.
-VT = TypeVar('VT')  # Value type.
-T_co = TypeVar('T_co', covariant=True)  # Any type covariant containers.
-V_co = TypeVar('V_co', covariant=True)  # Any type covariant containers.
-VT_co = TypeVar('VT_co', covariant=True)  # Value type covariant containers.
-T_contra = TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
-# Internal type variable used for Type[].
-CT_co = TypeVar('CT_co', covariant=True, bound=type)
-
-# A useful type variable with constraints.  This represents string types.
-# (This one *is* for export!)
-AnyStr = TypeVar('AnyStr', bytes, str)
-
-
-# Various ABCs mimicking those in collections.abc.
-_alias = _SpecialGenericAlias
-
-Hashable = _alias(collections.abc.Hashable, 0)  # Not generic.
-Awaitable = _alias(collections.abc.Awaitable, 1)
-Coroutine = _alias(collections.abc.Coroutine, 3)
-AsyncIterable = _alias(collections.abc.AsyncIterable, 1)
-AsyncIterator = _alias(collections.abc.AsyncIterator, 1)
-Iterable = _alias(collections.abc.Iterable, 1)
-Iterator = _alias(collections.abc.Iterator, 1)
-Reversible = _alias(collections.abc.Reversible, 1)
-Sized = _alias(collections.abc.Sized, 0)  # Not generic.
-Container = _alias(collections.abc.Container, 1)
-Collection = _alias(collections.abc.Collection, 1)
-Callable = _CallableType(collections.abc.Callable, 2)
-Callable.__doc__ = \
-    """Deprecated alias to collections.abc.Callable.
-
-    Callable[[int], str] signifies a function that takes a single
-    parameter of type int and returns a str.
-
-    The subscription syntax must always be used with exactly two
-    values: the argument list and the return type.
-    The argument list must be a list of types, a ParamSpec,
-    Concatenate or ellipsis. The return type must be a single type.
-
-    There is no syntax to indicate optional or keyword arguments;
-    such function types are rarely used as callback types.
-    """
-AbstractSet = _alias(collections.abc.Set, 1, name='AbstractSet')
-MutableSet = _alias(collections.abc.MutableSet, 1)
-# NOTE: Mapping is only covariant in the value type.
-Mapping = _alias(collections.abc.Mapping, 2)
-MutableMapping = _alias(collections.abc.MutableMapping, 2)
-Sequence = _alias(collections.abc.Sequence, 1)
-MutableSequence = _alias(collections.abc.MutableSequence, 1)
-ByteString = _alias(collections.abc.ByteString, 0)  # Not generic
-# Tuple accepts variable number of parameters.
-Tuple = _TupleType(tuple, -1, inst=False, name='Tuple')
-Tuple.__doc__ = \
-    """Deprecated alias to builtins.tuple.
-
-    Tuple[X, Y] is the cross-product type of X and Y.
-
-    Example: Tuple[T1, T2] is a tuple of two elements corresponding
-    to type variables T1 and T2.  Tuple[int, float, str] is a tuple
-    of an int, a float and a string.
-
-    To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
-    """
-List = _alias(list, 1, inst=False, name='List')
-Deque = _alias(collections.deque, 1, name='Deque')
-Set = _alias(set, 1, inst=False, name='Set')
-FrozenSet = _alias(frozenset, 1, inst=False, name='FrozenSet')
-MappingView = _alias(collections.abc.MappingView, 1)
-KeysView = _alias(collections.abc.KeysView, 1)
-ItemsView = _alias(collections.abc.ItemsView, 2)
-ValuesView = _alias(collections.abc.ValuesView, 1)
-ContextManager = _alias(contextlib.AbstractContextManager, 1, name='ContextManager')
-AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, 1, name='AsyncContextManager')
-Dict = _alias(dict, 2, inst=False, name='Dict')
-DefaultDict = _alias(collections.defaultdict, 2, name='DefaultDict')
-OrderedDict = _alias(collections.OrderedDict, 2)
-Counter = _alias(collections.Counter, 1)
-ChainMap = _alias(collections.ChainMap, 2)
-Generator = _alias(collections.abc.Generator, 3)
-AsyncGenerator = _alias(collections.abc.AsyncGenerator, 2)
-Type = _alias(type, 1, inst=False, name='Type')
-Type.__doc__ = \
-    """Deprecated alias to builtins.type.
-
-    builtins.type or typing.Type can be used to annotate class objects.
-    For example, suppose we have the following classes::
-
-        class User: ...  # Abstract base for User classes
-        class BasicUser(User): ...
-        class ProUser(User): ...
-        class TeamUser(User): ...
-
-    And a function that takes a class argument that's a subclass of
-    User and returns an instance of the corresponding class::
-
-        U = TypeVar('U', bound=User)
-        def new_user(user_class: Type[U]) -> U:
-            user = user_class()
-            # (Here we could write the user object to a database)
-            return user
-
-        joe = new_user(BasicUser)
-
-    At this point the type checker knows that joe has type BasicUser.
-    """
-
-
-@runtime_checkable
-class SupportsInt(Protocol):
-    """An ABC with one abstract method __int__."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __int__(self) -> int:
-        pass
-
-
-@runtime_checkable
-class SupportsFloat(Protocol):
-    """An ABC with one abstract method __float__."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __float__(self) -> float:
-        pass
-
-
-@runtime_checkable
-class SupportsComplex(Protocol):
-    """An ABC with one abstract method __complex__."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __complex__(self) -> complex:
-        pass
-
-
-@runtime_checkable
-class SupportsBytes(Protocol):
-    """An ABC with one abstract method __bytes__."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __bytes__(self) -> bytes:
-        pass
-
-
-@runtime_checkable
-class SupportsIndex(Protocol):
-    """An ABC with one abstract method __index__."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __index__(self) -> int:
-        pass
-
-
-@runtime_checkable
-class SupportsAbs(Protocol[T_co]):
-    """An ABC with one abstract method __abs__ that is covariant in its return type."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __abs__(self) -> T_co:
-        pass
-
-
-@runtime_checkable
-class SupportsRound(Protocol[T_co]):
-    """An ABC with one abstract method __round__ that is covariant in its return type."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def __round__(self, ndigits: int = 0) -> T_co:
-        pass
-
-
-def _make_nmtuple(name, types, module, defaults = ()):
-    fields = [n for n, t in types]
-    types = {n: _type_check(t, f"field {n} annotation must be a type")
-             for n, t in types}
-    nm_tpl = collections.namedtuple(name, fields,
-                                    defaults=defaults, module=module)
-    nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = types
-    return nm_tpl
-
-
-# attributes prohibited to set in NamedTuple class syntax
-_prohibited = frozenset({'__new__', '__init__', '__slots__', '__getnewargs__',
-                         '_fields', '_field_defaults',
-                         '_make', '_replace', '_asdict', '_source'})
-
-_special = frozenset({'__module__', '__name__', '__annotations__'})
-
-
-class NamedTupleMeta(type):
-    def __new__(cls, typename, bases, ns):
-        assert _NamedTuple in bases
-        for base in bases:
-            if base is not _NamedTuple and base is not Generic:
-                raise TypeError(
-                    'can only inherit from a NamedTuple type and Generic')
-        bases = tuple(tuple if base is _NamedTuple else base for base in bases)
-        types = ns.get('__annotations__', {})
-        default_names = []
-        for field_name in types:
-            if field_name in ns:
-                default_names.append(field_name)
-            elif default_names:
-                raise TypeError(f"Non-default namedtuple field {field_name} "
-                                f"cannot follow default field"
-                                f"{'s' if len(default_names) > 1 else ''} "
-                                f"{', '.join(default_names)}")
-        nm_tpl = _make_nmtuple(typename, types.items(),
-                               defaults=[ns[n] for n in default_names],
-                               module=ns['__module__'])
-        nm_tpl.__bases__ = bases
-        if Generic in bases:
-            class_getitem = Generic.__class_getitem__.__func__
-            nm_tpl.__class_getitem__ = classmethod(class_getitem)
-        # update from user namespace without overriding special namedtuple attributes
-        for key in ns:
-            if key in _prohibited:
-                raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
-            elif key not in _special and key not in nm_tpl._fields:
-                setattr(nm_tpl, key, ns[key])
-        if Generic in bases:
-            nm_tpl.__init_subclass__()
-        return nm_tpl
-
-
-def NamedTuple(typename, fields=None, /, **kwargs):
-    """Typed version of namedtuple.
-
-    Usage::
-
-        class Employee(NamedTuple):
-            name: str
-            id: int
-
-    This is equivalent to::
-
-        Employee = collections.namedtuple('Employee', ['name', 'id'])
-
-    The resulting class has an extra __annotations__ attribute, giving a
-    dict that maps field names to types.  (The field names are also in
-    the _fields attribute, which is part of the namedtuple API.)
-    An alternative equivalent functional syntax is also accepted::
-
-        Employee = NamedTuple('Employee', [('name', str), ('id', int)])
-    """
-    if fields is None:
-        fields = kwargs.items()
-    elif kwargs:
-        raise TypeError("Either list of fields or keywords"
-                        " can be provided to NamedTuple, not both")
-    return _make_nmtuple(typename, fields, module=_caller())
-
-_NamedTuple = type.__new__(NamedTupleMeta, 'NamedTuple', (), {})
-
-def _namedtuple_mro_entries(bases):
-    assert NamedTuple in bases
-    return (_NamedTuple,)
-
-NamedTuple.__mro_entries__ = _namedtuple_mro_entries
-
-
-class _TypedDictMeta(type):
-    def __new__(cls, name, bases, ns, total=True):
-        """Create a new typed dict class object.
-
-        This method is called when TypedDict is subclassed,
-        or when TypedDict is instantiated. This way
-        TypedDict supports all three syntax forms described in its docstring.
-        Subclasses and instances of TypedDict return actual dictionaries.
-        """
-        for base in bases:
-            if type(base) is not _TypedDictMeta and base is not Generic:
-                raise TypeError('cannot inherit from both a TypedDict type '
-                                'and a non-TypedDict base class')
-
-        if any(issubclass(b, Generic) for b in bases):
-            generic_base = (Generic,)
-        else:
-            generic_base = ()
-
-        tp_dict = type.__new__(_TypedDictMeta, name, (*generic_base, dict), ns)
-
-        annotations = {}
-        own_annotations = ns.get('__annotations__', {})
-        msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
-        own_annotations = {
-            n: _type_check(tp, msg, module=tp_dict.__module__)
-            for n, tp in own_annotations.items()
-        }
-        required_keys = set()
-        optional_keys = set()
-
-        for base in bases:
-            annotations.update(base.__dict__.get('__annotations__', {}))
-
-            base_required = base.__dict__.get('__required_keys__', set())
-            required_keys |= base_required
-            optional_keys -= base_required
-
-            base_optional = base.__dict__.get('__optional_keys__', set())
-            required_keys -= base_optional
-            optional_keys |= base_optional
-
-        annotations.update(own_annotations)
-        for annotation_key, annotation_type in own_annotations.items():
-            annotation_origin = get_origin(annotation_type)
-            if annotation_origin is Annotated:
-                annotation_args = get_args(annotation_type)
-                if annotation_args:
-                    annotation_type = annotation_args[0]
-                    annotation_origin = get_origin(annotation_type)
-
-            if annotation_origin is Required:
-                is_required = True
-            elif annotation_origin is NotRequired:
-                is_required = False
-            else:
-                is_required = total
-
-            if is_required:
-                required_keys.add(annotation_key)
-                optional_keys.discard(annotation_key)
-            else:
-                optional_keys.add(annotation_key)
-                required_keys.discard(annotation_key)
-
-        assert required_keys.isdisjoint(optional_keys), (
-            f"Required keys overlap with optional keys in {name}:"
-            f" {required_keys=}, {optional_keys=}"
-        )
-        tp_dict.__annotations__ = annotations
-        tp_dict.__required_keys__ = frozenset(required_keys)
-        tp_dict.__optional_keys__ = frozenset(optional_keys)
-        if not hasattr(tp_dict, '__total__'):
-            tp_dict.__total__ = total
-        return tp_dict
-
-    __call__ = dict  # static method
-
-    def __subclasscheck__(cls, other):
-        # Typed dicts are only for static structural subtyping.
-        raise TypeError('TypedDict does not support instance and class checks')
-
-    __instancecheck__ = __subclasscheck__
-
-
-def TypedDict(typename, fields=None, /, *, total=True, **kwargs):
-    """A simple typed namespace. At runtime it is equivalent to a plain dict.
-
-    TypedDict creates a dictionary type such that a type checker will expect all
-    instances to have a certain set of keys, where each key is
-    associated with a value of a consistent type. This expectation
-    is not checked at runtime.
-
-    Usage::
-
-        >>> class Point2D(TypedDict):
-        ...     x: int
-        ...     y: int
-        ...     label: str
-        ...
-        >>> a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
-        >>> b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
-        >>> Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
-        True
-
-    The type info can be accessed via the Point2D.__annotations__ dict, and
-    the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
-    TypedDict supports an additional equivalent form::
-
-        Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
-
-    By default, all keys must be present in a TypedDict. It is possible
-    to override this by specifying totality::
-
-        class Point2D(TypedDict, total=False):
-            x: int
-            y: int
-
-    This means that a Point2D TypedDict can have any of the keys omitted. A type
-    checker is only expected to support a literal False or True as the value of
-    the total argument. True is the default, and makes all items defined in the
-    class body be required.
-
-    The Required and NotRequired special forms can also be used to mark
-    individual keys as being required or not required::
-
-        class Point2D(TypedDict):
-            x: int               # the "x" key must always be present (Required is the default)
-            y: NotRequired[int]  # the "y" key can be omitted
-
-    See PEP 655 for more details on Required and NotRequired.
-    """
-    if fields is None:
-        fields = kwargs
-    elif kwargs:
-        raise TypeError("TypedDict takes either a dict or keyword arguments,"
-                        " but not both")
-    if kwargs:
-        warnings.warn(
-            "The kwargs-based syntax for TypedDict definitions is deprecated "
-            "in Python 3.11, will be removed in Python 3.13, and may not be "
-            "understood by third-party type checkers.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-    ns = {'__annotations__': dict(fields)}
-    module = _caller()
-    if module is not None:
-        # Setting correct module is necessary to make typed dict classes pickleable.
-        ns['__module__'] = module
-
-    return _TypedDictMeta(typename, (), ns, total=total)
-
-_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
-TypedDict.__mro_entries__ = lambda bases: (_TypedDict,)
-
-
-@_SpecialForm
-def Required(self, parameters):
-    """Special typing construct to mark a TypedDict key as required.
-
-    This is mainly useful for total=False TypedDicts.
-
-    For example::
-
-        class Movie(TypedDict, total=False):
-            title: Required[str]
-            year: int
-
-        m = Movie(
-            title='The Matrix',  # typechecker error if key is omitted
-            year=1999,
-        )
-
-    There is no runtime checking that a required key is actually provided
-    when instantiating a related TypedDict.
-    """
-    item = _type_check(parameters, f'{self._name} accepts only a single type.')
-    return _GenericAlias(self, (item,))
-
-
-@_SpecialForm
-def NotRequired(self, parameters):
-    """Special typing construct to mark a TypedDict key as potentially missing.
-
-    For example::
-
-        class Movie(TypedDict):
-            title: str
-            year: NotRequired[int]
-
-        m = Movie(
-            title='The Matrix',  # typechecker error if key is omitted
-            year=1999,
-        )
-    """
-    item = _type_check(parameters, f'{self._name} accepts only a single type.')
-    return _GenericAlias(self, (item,))
-
-
-class NewType:
-    """NewType creates simple unique types with almost zero runtime overhead.
-
-    NewType(name, tp) is considered a subtype of tp
-    by static type checkers. At runtime, NewType(name, tp) returns
-    a dummy callable that simply returns its argument.
-
-    Usage::
-
-        UserId = NewType('UserId', int)
-
-        def name_by_id(user_id: UserId) -> str:
-            ...
-
-        UserId('user')          # Fails type check
-
-        name_by_id(42)          # Fails type check
-        name_by_id(UserId(42))  # OK
-
-        num = UserId(5) + 1     # type: int
-    """
-
-    __call__ = _idfunc
-
-    def __init__(self, name, tp):
-        self.__qualname__ = name
-        if '.' in name:
-            name = name.rpartition('.')[-1]
-        self.__name__ = name
-        self.__supertype__ = tp
-        def_mod = _caller()
-        if def_mod != 'typing':
-            self.__module__ = def_mod
-
-    def __mro_entries__(self, bases):
-        # We defined __mro_entries__ to get a better error message
-        # if a user attempts to subclass a NewType instance. bpo-46170
-        superclass_name = self.__name__
-
-        class Dummy:
-            def __init_subclass__(cls):
-                subclass_name = cls.__name__
-                raise TypeError(
-                    f"Cannot subclass an instance of NewType. Perhaps you were looking for: "
-                    f"`{subclass_name} = NewType({subclass_name!r}, {superclass_name})`"
-                )
-
-        return (Dummy,)
-
-    def __repr__(self):
-        return f'{self.__module__}.{self.__qualname__}'
-
-    def __reduce__(self):
-        return self.__qualname__
-
-    def __or__(self, other):
-        return Union[self, other]
-
-    def __ror__(self, other):
-        return Union[other, self]
-
-
-# Python-version-specific alias (Python 2: unicode; Python 3: str)
-Text = str
-
-
-# Constant that's True when type checking, but False here.
-TYPE_CHECKING = False
-
-
-class IO(Generic[AnyStr]):
-    """Generic base class for TextIO and BinaryIO.
-
-    This is an abstract, generic version of the return of open().
-
-    NOTE: This does not distinguish between the different possible
-    classes (text vs. binary, read vs. write vs. read/write,
-    append-only, unbuffered).  The TextIO and BinaryIO subclasses
-    below capture the distinctions between text vs. binary, which is
-    pervasive in the interface; however we currently do not offer a
-    way to track the other distinctions in the type system.
-    """
-
-    __slots__ = ()
-
-    @property
-    @abstractmethod
-    def mode(self) -> str:
-        pass
-
-    @property
-    @abstractmethod
-    def name(self) -> str:
-        pass
-
-    @abstractmethod
-    def close(self) -> None:
-        pass
-
-    @property
-    @abstractmethod
-    def closed(self) -> bool:
-        pass
-
-    @abstractmethod
-    def fileno(self) -> int:
-        pass
-
-    @abstractmethod
-    def flush(self) -> None:
-        pass
-
-    @abstractmethod
-    def isatty(self) -> bool:
-        pass
-
-    @abstractmethod
-    def read(self, n: int = -1) -> AnyStr:
-        pass
-
-    @abstractmethod
-    def readable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def readline(self, limit: int = -1) -> AnyStr:
-        pass
-
-    @abstractmethod
-    def readlines(self, hint: int = -1) -> List[AnyStr]:
-        pass
-
-    @abstractmethod
-    def seek(self, offset: int, whence: int = 0) -> int:
-        pass
-
-    @abstractmethod
-    def seekable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def tell(self) -> int:
-        pass
-
-    @abstractmethod
-    def truncate(self, size: int = None) -> int:
-        pass
-
-    @abstractmethod
-    def writable(self) -> bool:
-        pass
-
-    @abstractmethod
-    def write(self, s: AnyStr) -> int:
-        pass
-
-    @abstractmethod
-    def writelines(self, lines: List[AnyStr]) -> None:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'IO[AnyStr]':
-        pass
-
-    @abstractmethod
-    def __exit__(self, type, value, traceback) -> None:
-        pass
-
-
-class BinaryIO(IO[bytes]):
-    """Typed version of the return of open() in binary mode."""
-
-    __slots__ = ()
-
-    @abstractmethod
-    def write(self, s: Union[bytes, bytearray]) -> int:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'BinaryIO':
-        pass
-
-
-class TextIO(IO[str]):
-    """Typed version of the return of open() in text mode."""
-
-    __slots__ = ()
-
-    @property
-    @abstractmethod
-    def buffer(self) -> BinaryIO:
-        pass
-
-    @property
-    @abstractmethod
-    def encoding(self) -> str:
-        pass
-
-    @property
-    @abstractmethod
-    def errors(self) -> Optional[str]:
-        pass
-
-    @property
-    @abstractmethod
-    def line_buffering(self) -> bool:
-        pass
-
-    @property
-    @abstractmethod
-    def newlines(self) -> Any:
-        pass
-
-    @abstractmethod
-    def __enter__(self) -> 'TextIO':
-        pass
-
-
-class _DeprecatedType(type):
-    def __getattribute__(cls, name):
-        if name not in {"__dict__", "__module__", "__doc__"} and name in cls.__dict__:
-            warnings.warn(
-                f"{cls.__name__} is deprecated, import directly "
-                f"from typing instead. {cls.__name__} will be removed "
-                "in Python 3.12.",
-                DeprecationWarning,
-                stacklevel=2,
-            )
-        return super().__getattribute__(name)
-
-
-class io(metaclass=_DeprecatedType):
-    """Wrapper namespace for IO generic classes."""
-
-    __all__ = ['IO', 'TextIO', 'BinaryIO']
-    IO = IO
-    TextIO = TextIO
-    BinaryIO = BinaryIO
-
-
-io.__name__ = __name__ + '.io'
-sys.modules[io.__name__] = io
-
-Pattern = _alias(stdlib_re.Pattern, 1)
-Match = _alias(stdlib_re.Match, 1)
-
-class re(metaclass=_DeprecatedType):
-    """Wrapper namespace for re type aliases."""
-
-    __all__ = ['Pattern', 'Match']
-    Pattern = Pattern
-    Match = Match
-
-
-re.__name__ = __name__ + '.re'
-sys.modules[re.__name__] = re
-
-
-def reveal_type(obj: T, /) -> T:
-    """Ask a static type checker to reveal the inferred type of an expression.
-
-    When a static type checker encounters a call to ``reveal_type()``,
-    it will emit the inferred type of the argument::
-
-        x: int = 1
-        reveal_type(x)
-
-    Running a static type checker (e.g., mypy) on this example
-    will produce output similar to 'Revealed type is "builtins.int"'.
-
-    At runtime, the function prints the runtime type of the
-    argument and returns the argument unchanged.
-    """
-    print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr)
-    return obj
-
-
-def dataclass_transform(
-    *,
-    eq_default: bool = True,
-    order_default: bool = False,
-    kw_only_default: bool = False,
-    field_specifiers: tuple[type[Any] | Callable[..., Any], ...] = (),
-    **kwargs: Any,
-) -> Callable[[T], T]:
-    """Decorator to mark an object as providing dataclass-like behaviour.
-
-    The decorator can be applied to a function, class, or metaclass.
-
-    Example usage with a decorator function::
-
-        T = TypeVar("T")
-
-        @dataclass_transform()
-        def create_model(cls: type[T]) -> type[T]:
-            ...
-            return cls
-
-        @create_model
-        class CustomerModel:
-            id: int
-            name: str
-
-    On a base class::
-
-        @dataclass_transform()
-        class ModelBase: ...
-
-        class CustomerModel(ModelBase):
-            id: int
-            name: str
-
-    On a metaclass::
-
-        @dataclass_transform()
-        class ModelMeta(type): ...
-
-        class ModelBase(metaclass=ModelMeta): ...
-
-        class CustomerModel(ModelBase):
-            id: int
-            name: str
-
-    The ``CustomerModel`` classes defined above will
-    be treated by type checkers similarly to classes created with
-    ``@dataclasses.dataclass``.
-    For example, type checkers will assume these classes have
-    ``__init__`` methods that accept ``id`` and ``name``.
-
-    The arguments to this decorator can be used to customize this behavior:
-    - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
-        ``True`` or ``False`` if it is omitted by the caller.
-    - ``order_default`` indicates whether the ``order`` parameter is
-        assumed to be True or False if it is omitted by the caller.
-    - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
-        assumed to be True or False if it is omitted by the caller.
-    - ``field_specifiers`` specifies a static list of supported classes
-        or functions that describe fields, similar to ``dataclasses.field()``.
-    - Arbitrary other keyword arguments are accepted in order to allow for
-        possible future extensions.
-
-    At runtime, this decorator records its arguments in the
-    ``__dataclass_transform__`` attribute on the decorated object.
-    It has no other runtime effect.
-
-    See PEP 681 for more details.
-    """
-    def decorator(cls_or_fn):
-        cls_or_fn.__dataclass_transform__ = {
-            "eq_default": eq_default,
-            "order_default": order_default,
-            "kw_only_default": kw_only_default,
-            "field_specifiers": field_specifiers,
-            "kwargs": kwargs,
-        }
-        return cls_or_fn
-    return decorator
-
- -
-
- -
-
-
-
- - - - diff --git a/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js b/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js deleted file mode 100644 index 81415803..00000000 --- a/docs/_build/html/_static/_sphinx_javascript_frameworks_compat.js +++ /dev/null @@ -1,123 +0,0 @@ -/* Compatability shim for jQuery and underscores.js. - * - * Copyright Sphinx contributors - * Released under the two clause BSD licence - */ - -/** - * small helper function to urldecode strings - * - * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL - */ -jQuery.urldecode = function(x) { - if (!x) { - return x - } - return decodeURIComponent(x.replace(/\+/g, ' ')); -}; - -/** - * small helper function to urlencode strings - */ -jQuery.urlencode = encodeURIComponent; - -/** - * This function returns the parsed url parameters of the - * current request. Multiple values per key are supported, - * it will always return arrays of strings for the value parts. - */ -jQuery.getQueryParameters = function(s) { - if (typeof s === 'undefined') - s = document.location.search; - var parts = s.substr(s.indexOf('?') + 1).split('&'); - var result = {}; - for (var i = 0; i < parts.length; i++) { - var tmp = parts[i].split('=', 2); - var key = jQuery.urldecode(tmp[0]); - var value = jQuery.urldecode(tmp[1]); - if (key in result) - result[key].push(value); - else - result[key] = [value]; - } - return result; -}; - -/** - * highlight a given string on a jquery object by wrapping it in - * span elements with the given class name. - */ -jQuery.fn.highlightText = function(text, className) { - function highlight(node, addItems) { - if (node.nodeType === 3) { - var val = node.nodeValue; - var pos = val.toLowerCase().indexOf(text); - if (pos >= 0 && - !jQuery(node.parentNode).hasClass(className) && - !jQuery(node.parentNode).hasClass("nohighlight")) { - var span; - var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); - if (isInSVG) { - span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); - } else { - span = document.createElement("span"); - span.className = className; - } - span.appendChild(document.createTextNode(val.substr(pos, text.length))); - node.parentNode.insertBefore(span, node.parentNode.insertBefore( - document.createTextNode(val.substr(pos + text.length)), - node.nextSibling)); - node.nodeValue = val.substr(0, pos); - if (isInSVG) { - var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); - var bbox = node.parentElement.getBBox(); - rect.x.baseVal.value = bbox.x; - rect.y.baseVal.value = bbox.y; - rect.width.baseVal.value = bbox.width; - rect.height.baseVal.value = bbox.height; - rect.setAttribute('class', className); - addItems.push({ - "parent": node.parentNode, - "target": rect}); - } - } - } - else if (!jQuery(node).is("button, select, textarea")) { - jQuery.each(node.childNodes, function() { - highlight(this, addItems); - }); - } - } - var addItems = []; - var result = this.each(function() { - highlight(this, addItems); - }); - for (var i = 0; i < addItems.length; ++i) { - jQuery(addItems[i].parent).before(addItems[i].target); - } - return result; -}; - -/* - * backward compatibility for jQuery.browser - * This will be supported until firefox bug is fixed. - */ -if (!jQuery.browser) { - jQuery.uaMatch = function(ua) { - ua = ua.toLowerCase(); - - var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || - /(webkit)[ \/]([\w.]+)/.exec(ua) || - /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || - /(msie) ([\w.]+)/.exec(ua) || - ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || - []; - - return { - browser: match[ 1 ] || "", - version: match[ 2 ] || "0" - }; - }; - jQuery.browser = {}; - jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; -} diff --git a/docs/_build/html/_static/basic.css b/docs/_build/html/_static/basic.css deleted file mode 100644 index f316efcb..00000000 --- a/docs/_build/html/_static/basic.css +++ /dev/null @@ -1,925 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ - -/* -- main layout ----------------------------------------------------------- */ - -div.clearer { - clear: both; -} - -div.section::after { - display: block; - content: ''; - clear: left; -} - -/* -- relbar ---------------------------------------------------------------- */ - -div.related { - width: 100%; - font-size: 90%; -} - -div.related h3 { - display: none; -} - -div.related ul { - margin: 0; - padding: 0 0 0 10px; - list-style: none; -} - -div.related li { - display: inline; -} - -div.related li.right { - float: right; - margin-right: 5px; -} - -/* -- sidebar --------------------------------------------------------------- */ - -div.sphinxsidebarwrapper { - padding: 10px 5px 0 10px; -} - -div.sphinxsidebar { - float: left; - width: 230px; - margin-left: -100%; - font-size: 90%; - word-wrap: break-word; - overflow-wrap : break-word; -} - -div.sphinxsidebar ul { - list-style: none; -} - -div.sphinxsidebar ul ul, -div.sphinxsidebar ul.want-points { - margin-left: 20px; - list-style: square; -} - -div.sphinxsidebar ul ul { - margin-top: 0; - margin-bottom: 0; -} - -div.sphinxsidebar form { - margin-top: 10px; -} - -div.sphinxsidebar input { - border: 1px solid #98dbcc; - font-family: sans-serif; - font-size: 1em; -} - -div.sphinxsidebar #searchbox form.search { - overflow: hidden; -} - -div.sphinxsidebar #searchbox input[type="text"] { - float: left; - width: 80%; - padding: 0.25em; - box-sizing: border-box; -} - -div.sphinxsidebar #searchbox input[type="submit"] { - float: left; - width: 20%; - border-left: none; - padding: 0.25em; - box-sizing: border-box; -} - - -img { - border: 0; - max-width: 100%; -} - -/* -- search page ----------------------------------------------------------- */ - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li p.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} - -/* -- index page ------------------------------------------------------------ */ - -table.contentstable { - width: 90%; - margin-left: auto; - margin-right: auto; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -/* -- general index --------------------------------------------------------- */ - -table.indextable { - width: 100%; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable ul { - margin-top: 0; - margin-bottom: 0; - list-style-type: none; -} - -table.indextable > tbody > tr > td > ul { - padding-left: 0em; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -div.modindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -div.genindex-jumpbox { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 1em 0 1em 0; - padding: 0.4em; -} - -/* -- domain module index --------------------------------------------------- */ - -table.modindextable td { - padding: 2px; - border-collapse: collapse; -} - -/* -- general body styles --------------------------------------------------- */ - -div.body { - min-width: 360px; - max-width: 800px; -} - -div.body p, div.body dd, div.body li, div.body blockquote { - -moz-hyphens: auto; - -ms-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; -} - -a.headerlink { - visibility: hidden; -} - -a:visited { - color: #551A8B; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink, -caption:hover > a.headerlink, -p.caption:hover > a.headerlink, -div.code-block-caption:hover > a.headerlink { - visibility: visible; -} - -div.body p.caption { - text-align: inherit; -} - -div.body td { - text-align: left; -} - -.first { - margin-top: 0 !important; -} - -p.rubric { - margin-top: 30px; - font-weight: bold; -} - -img.align-left, figure.align-left, .figure.align-left, object.align-left { - clear: left; - float: left; - margin-right: 1em; -} - -img.align-right, figure.align-right, .figure.align-right, object.align-right { - clear: right; - float: right; - margin-left: 1em; -} - -img.align-center, figure.align-center, .figure.align-center, object.align-center { - display: block; - margin-left: auto; - margin-right: auto; -} - -img.align-default, figure.align-default, .figure.align-default { - display: block; - margin-left: auto; - margin-right: auto; -} - -.align-left { - text-align: left; -} - -.align-center { - text-align: center; -} - -.align-default { - text-align: center; -} - -.align-right { - text-align: right; -} - -/* -- sidebars -------------------------------------------------------------- */ - -div.sidebar, -aside.sidebar { - margin: 0 0 0.5em 1em; - border: 1px solid #ddb; - padding: 7px; - background-color: #ffe; - width: 40%; - float: right; - clear: right; - overflow-x: auto; -} - -p.sidebar-title { - font-weight: bold; -} - -nav.contents, -aside.topic, -div.admonition, div.topic, blockquote { - clear: left; -} - -/* -- topics ---------------------------------------------------------------- */ - -nav.contents, -aside.topic, -div.topic { - border: 1px solid #ccc; - padding: 7px; - margin: 10px 0 10px 0; -} - -p.topic-title { - font-size: 1.1em; - font-weight: bold; - margin-top: 10px; -} - -/* -- admonitions ----------------------------------------------------------- */ - -div.admonition { - margin-top: 10px; - margin-bottom: 10px; - padding: 7px; -} - -div.admonition dt { - font-weight: bold; -} - -p.admonition-title { - margin: 0px 10px 5px 0px; - font-weight: bold; -} - -div.body p.centered { - text-align: center; - margin-top: 25px; -} - -/* -- content of sidebars/topics/admonitions -------------------------------- */ - -div.sidebar > :last-child, -aside.sidebar > :last-child, -nav.contents > :last-child, -aside.topic > :last-child, -div.topic > :last-child, -div.admonition > :last-child { - margin-bottom: 0; -} - -div.sidebar::after, -aside.sidebar::after, -nav.contents::after, -aside.topic::after, -div.topic::after, -div.admonition::after, -blockquote::after { - display: block; - content: ''; - clear: both; -} - -/* -- tables ---------------------------------------------------------------- */ - -table.docutils { - margin-top: 10px; - margin-bottom: 10px; - border: 0; - border-collapse: collapse; -} - -table.align-center { - margin-left: auto; - margin-right: auto; -} - -table.align-default { - margin-left: auto; - margin-right: auto; -} - -table caption span.caption-number { - font-style: italic; -} - -table caption span.caption-text { -} - -table.docutils td, table.docutils th { - padding: 1px 8px 1px 5px; - border-top: 0; - border-left: 0; - border-right: 0; - border-bottom: 1px solid #aaa; -} - -th { - text-align: left; - padding-right: 5px; -} - -table.citation { - border-left: solid 1px gray; - margin-left: 1px; -} - -table.citation td { - border-bottom: none; -} - -th > :first-child, -td > :first-child { - margin-top: 0px; -} - -th > :last-child, -td > :last-child { - margin-bottom: 0px; -} - -/* -- figures --------------------------------------------------------------- */ - -div.figure, figure { - margin: 0.5em; - padding: 0.5em; -} - -div.figure p.caption, figcaption { - padding: 0.3em; -} - -div.figure p.caption span.caption-number, -figcaption span.caption-number { - font-style: italic; -} - -div.figure p.caption span.caption-text, -figcaption span.caption-text { -} - -/* -- field list styles ----------------------------------------------------- */ - -table.field-list td, table.field-list th { - border: 0 !important; -} - -.field-list ul { - margin: 0; - padding-left: 1em; -} - -.field-list p { - margin: 0; -} - -.field-name { - -moz-hyphens: manual; - -ms-hyphens: manual; - -webkit-hyphens: manual; - hyphens: manual; -} - -/* -- hlist styles ---------------------------------------------------------- */ - -table.hlist { - margin: 1em 0; -} - -table.hlist td { - vertical-align: top; -} - -/* -- object description styles --------------------------------------------- */ - -.sig { - font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; -} - -.sig-name, code.descname { - background-color: transparent; - font-weight: bold; -} - -.sig-name { - font-size: 1.1em; -} - -code.descname { - font-size: 1.2em; -} - -.sig-prename, code.descclassname { - background-color: transparent; -} - -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - -.sig-param.n { - font-style: italic; -} - -/* C++ specific styling */ - -.sig-inline.c-texpr, -.sig-inline.cpp-texpr { - font-family: unset; -} - -.sig.c .k, .sig.c .kt, -.sig.cpp .k, .sig.cpp .kt { - color: #0033B3; -} - -.sig.c .m, -.sig.cpp .m { - color: #1750EB; -} - -.sig.c .s, .sig.c .sc, -.sig.cpp .s, .sig.cpp .sc { - color: #067D17; -} - - -/* -- other body styles ----------------------------------------------------- */ - -ol.arabic { - list-style: decimal; -} - -ol.loweralpha { - list-style: lower-alpha; -} - -ol.upperalpha { - list-style: upper-alpha; -} - -ol.lowerroman { - list-style: lower-roman; -} - -ol.upperroman { - list-style: upper-roman; -} - -:not(li) > ol > li:first-child > :first-child, -:not(li) > ul > li:first-child > :first-child { - margin-top: 0px; -} - -:not(li) > ol > li:last-child > :last-child, -:not(li) > ul > li:last-child > :last-child { - margin-bottom: 0px; -} - -ol.simple ol p, -ol.simple ul p, -ul.simple ol p, -ul.simple ul p { - margin-top: 0; -} - -ol.simple > li:not(:first-child) > p, -ul.simple > li:not(:first-child) > p { - margin-top: 0; -} - -ol.simple p, -ul.simple p { - margin-bottom: 0; -} - -aside.footnote > span, -div.citation > span { - float: left; -} -aside.footnote > span:last-of-type, -div.citation > span:last-of-type { - padding-right: 0.5em; -} -aside.footnote > p { - margin-left: 2em; -} -div.citation > p { - margin-left: 4em; -} -aside.footnote > p:last-of-type, -div.citation > p:last-of-type { - margin-bottom: 0em; -} -aside.footnote > p:last-of-type:after, -div.citation > p:last-of-type:after { - content: ""; - clear: both; -} - -dl.field-list { - display: grid; - grid-template-columns: fit-content(30%) auto; -} - -dl.field-list > dt { - font-weight: bold; - word-break: break-word; - padding-left: 0.5em; - padding-right: 5px; -} - -dl.field-list > dd { - padding-left: 0.5em; - margin-top: 0em; - margin-left: 0em; - margin-bottom: 0em; -} - -dl { - margin-bottom: 15px; -} - -dd > :first-child { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.sig dd { - margin-top: 0px; - margin-bottom: 0px; -} - -.sig dl { - margin-top: 0px; - margin-bottom: 0px; -} - -dl > dd:last-child, -dl > dd:last-child > :last-child { - margin-bottom: 0; -} - -dt:target, span.highlighted { - background-color: #fbe54e; -} - -rect.highlighted { - fill: #fbe54e; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -.versionmodified { - font-style: italic; -} - -.system-message { - background-color: #fda; - padding: 5px; - border: 3px solid red; -} - -.footnote:target { - background-color: #ffa; -} - -.line-block { - display: block; - margin-top: 1em; - margin-bottom: 1em; -} - -.line-block .line-block { - margin-top: 0; - margin-bottom: 0; - margin-left: 1.5em; -} - -.guilabel, .menuselection { - font-family: sans-serif; -} - -.accelerator { - text-decoration: underline; -} - -.classifier { - font-style: oblique; -} - -.classifier:before { - font-style: normal; - margin: 0 0.5em; - content: ":"; - display: inline-block; -} - -abbr, acronym { - border-bottom: dotted 1px; - cursor: help; -} - -.translated { - background-color: rgba(207, 255, 207, 0.2) -} - -.untranslated { - background-color: rgba(255, 207, 207, 0.2) -} - -/* -- code displays --------------------------------------------------------- */ - -pre { - overflow: auto; - overflow-y: hidden; /* fixes display issues on Chrome browsers */ -} - -pre, div[class*="highlight-"] { - clear: both; -} - -span.pre { - -moz-hyphens: none; - -ms-hyphens: none; - -webkit-hyphens: none; - hyphens: none; - white-space: nowrap; -} - -div[class*="highlight-"] { - margin: 1em 0; -} - -td.linenos pre { - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - display: block; -} - -table.highlighttable tbody { - display: block; -} - -table.highlighttable tr { - display: flex; -} - -table.highlighttable td { - margin: 0; - padding: 0; -} - -table.highlighttable td.linenos { - padding-right: 0.5em; -} - -table.highlighttable td.code { - flex: 1; - overflow: hidden; -} - -.highlight .hll { - display: block; -} - -div.highlight pre, -table.highlighttable pre { - margin: 0; -} - -div.code-block-caption + div { - margin-top: 0; -} - -div.code-block-caption { - margin-top: 1em; - padding: 2px 5px; - font-size: small; -} - -div.code-block-caption code { - background-color: transparent; -} - -table.highlighttable td.linenos, -span.linenos, -div.highlight span.gp { /* gp: Generic.Prompt */ - user-select: none; - -webkit-user-select: text; /* Safari fallback only */ - -webkit-user-select: none; /* Chrome/Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+ */ -} - -div.code-block-caption span.caption-number { - padding: 0.1em 0.3em; - font-style: italic; -} - -div.code-block-caption span.caption-text { -} - -div.literal-block-wrapper { - margin: 1em 0; -} - -code.xref, a code { - background-color: transparent; - font-weight: bold; -} - -h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { - background-color: transparent; -} - -.viewcode-link { - float: right; -} - -.viewcode-back { - float: right; - font-family: sans-serif; -} - -div.viewcode-block:target { - margin: -1px -10px; - padding: 0 10px; -} - -/* -- math display ---------------------------------------------------------- */ - -img.math { - vertical-align: middle; -} - -div.body div.math p { - text-align: center; -} - -span.eqno { - float: right; -} - -span.eqno a.headerlink { - position: absolute; - z-index: 1; -} - -div.math:hover a.headerlink { - visibility: visible; -} - -/* -- printout stylesheet --------------------------------------------------- */ - -@media print { - div.document, - div.documentwrapper, - div.bodywrapper { - margin: 0 !important; - width: 100%; - } - - div.sphinxsidebar, - div.related, - div.footer, - #top-link { - display: none; - } -} \ No newline at end of file diff --git a/docs/_build/html/_static/css/badge_only.css b/docs/_build/html/_static/css/badge_only.css deleted file mode 100644 index c718cee4..00000000 --- a/docs/_build/html/_static/css/badge_only.css +++ /dev/null @@ -1 +0,0 @@ -.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff b/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff deleted file mode 100644 index 6cb60000..00000000 Binary files a/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 b/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 deleted file mode 100644 index 7059e231..00000000 Binary files a/docs/_build/html/_static/css/fonts/Roboto-Slab-Bold.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff b/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff deleted file mode 100644 index f815f63f..00000000 Binary files a/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 b/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 deleted file mode 100644 index f2c76e5b..00000000 Binary files a/docs/_build/html/_static/css/fonts/Roboto-Slab-Regular.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot b/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca9..00000000 Binary files a/docs/_build/html/_static/css/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/fontawesome-webfont.svg b/docs/_build/html/_static/css/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845e..00000000 --- a/docs/_build/html/_static/css/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf b/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2f..00000000 Binary files a/docs/_build/html/_static/css/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff b/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a4..00000000 Binary files a/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 b/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc60..00000000 Binary files a/docs/_build/html/_static/css/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-bold-italic.woff b/docs/_build/html/_static/css/fonts/lato-bold-italic.woff deleted file mode 100644 index 88ad05b9..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-bold-italic.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 b/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 deleted file mode 100644 index c4e3d804..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-bold-italic.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-bold.woff b/docs/_build/html/_static/css/fonts/lato-bold.woff deleted file mode 100644 index c6dff51f..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-bold.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-bold.woff2 b/docs/_build/html/_static/css/fonts/lato-bold.woff2 deleted file mode 100644 index bb195043..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-bold.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-normal-italic.woff b/docs/_build/html/_static/css/fonts/lato-normal-italic.woff deleted file mode 100644 index 76114bc0..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-normal-italic.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 b/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 deleted file mode 100644 index 3404f37e..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-normal-italic.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-normal.woff b/docs/_build/html/_static/css/fonts/lato-normal.woff deleted file mode 100644 index ae1307ff..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-normal.woff and /dev/null differ diff --git a/docs/_build/html/_static/css/fonts/lato-normal.woff2 b/docs/_build/html/_static/css/fonts/lato-normal.woff2 deleted file mode 100644 index 3bf98433..00000000 Binary files a/docs/_build/html/_static/css/fonts/lato-normal.woff2 and /dev/null differ diff --git a/docs/_build/html/_static/css/theme.css b/docs/_build/html/_static/css/theme.css deleted file mode 100644 index 19a446a0..00000000 --- a/docs/_build/html/_static/css/theme.css +++ /dev/null @@ -1,4 +0,0 @@ -html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/docs/_build/html/_static/css/vendor/bootstrap.min.css b/docs/_build/html/_static/css/vendor/bootstrap.min.css deleted file mode 100644 index 7c4b8576..00000000 --- a/docs/_build/html/_static/css/vendor/bootstrap.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v4.3.1 (https://getbootstrap.com/) - * Copyright 2011-2019 The Bootstrap Authors - * Copyright 2011-2019 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} diff --git a/docs/_build/html/_static/doctools.js b/docs/_build/html/_static/doctools.js deleted file mode 100644 index 4d67807d..00000000 --- a/docs/_build/html/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - */ -"use strict"; - -const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ - "TEXTAREA", - "INPUT", - "SELECT", - "BUTTON", -]); - -const _ready = (callback) => { - if (document.readyState !== "loading") { - callback(); - } else { - document.addEventListener("DOMContentLoaded", callback); - } -}; - -/** - * Small JavaScript module for the documentation. - */ -const Documentation = { - init: () => { - Documentation.initDomainIndexTable(); - Documentation.initOnKeyListeners(); - }, - - /** - * i18n support - */ - TRANSLATIONS: {}, - PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), - LOCALE: "unknown", - - // gettext and ngettext don't access this so that the functions - // can safely bound to a different name (_ = Documentation.gettext) - gettext: (string) => { - const translated = Documentation.TRANSLATIONS[string]; - switch (typeof translated) { - case "undefined": - return string; // no translation - case "string": - return translated; // translation exists - default: - return translated[0]; // (singular, plural) translation tuple exists - } - }, - - ngettext: (singular, plural, n) => { - const translated = Documentation.TRANSLATIONS[singular]; - if (typeof translated !== "undefined") - return translated[Documentation.PLURAL_EXPR(n)]; - return n === 1 ? singular : plural; - }, - - addTranslations: (catalog) => { - Object.assign(Documentation.TRANSLATIONS, catalog.messages); - Documentation.PLURAL_EXPR = new Function( - "n", - `return (${catalog.plural_expr})` - ); - Documentation.LOCALE = catalog.locale; - }, - - /** - * helper function to focus on search bar - */ - focusSearchBar: () => { - document.querySelectorAll("input[name=q]")[0]?.focus(); - }, - - /** - * Initialise the domain index toggle buttons - */ - initDomainIndexTable: () => { - const toggler = (el) => { - const idNumber = el.id.substr(7); - const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); - if (el.src.substr(-9) === "minus.png") { - el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; - toggledRows.forEach((el) => (el.style.display = "none")); - } else { - el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; - toggledRows.forEach((el) => (el.style.display = "")); - } - }; - - const togglerElements = document.querySelectorAll("img.toggler"); - togglerElements.forEach((el) => - el.addEventListener("click", (event) => toggler(event.currentTarget)) - ); - togglerElements.forEach((el) => (el.style.display = "")); - if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); - }, - - initOnKeyListeners: () => { - // only install a listener if it is really needed - if ( - !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && - !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS - ) - return; - - document.addEventListener("keydown", (event) => { - // bail for input elements - if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; - // bail with special keys - if (event.altKey || event.ctrlKey || event.metaKey) return; - - if (!event.shiftKey) { - switch (event.key) { - case "ArrowLeft": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const prevLink = document.querySelector('link[rel="prev"]'); - if (prevLink && prevLink.href) { - window.location.href = prevLink.href; - event.preventDefault(); - } - break; - case "ArrowRight": - if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; - - const nextLink = document.querySelector('link[rel="next"]'); - if (nextLink && nextLink.href) { - window.location.href = nextLink.href; - event.preventDefault(); - } - break; - } - } - - // some keyboard layouts may need Shift to get / - switch (event.key) { - case "/": - if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; - Documentation.focusSearchBar(); - event.preventDefault(); - } - }); - }, -}; - -// quick alias for translations -const _ = Documentation.gettext; - -_ready(Documentation.init); diff --git a/docs/_build/html/_static/documentation_options.js b/docs/_build/html/_static/documentation_options.js deleted file mode 100644 index 67977745..00000000 --- a/docs/_build/html/_static/documentation_options.js +++ /dev/null @@ -1,13 +0,0 @@ -const DOCUMENTATION_OPTIONS = { - VERSION: '0.4.4', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: false, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/docs/_build/html/_static/file.png b/docs/_build/html/_static/file.png deleted file mode 100644 index a858a410..00000000 Binary files a/docs/_build/html/_static/file.png and /dev/null differ diff --git a/docs/_build/html/_static/jquery.js b/docs/_build/html/_static/jquery.js deleted file mode 100644 index c4c6022f..00000000 --- a/docs/_build/html/_static/jquery.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/_build/html/_static/js/html5shiv.min.js b/docs/_build/html/_static/js/html5shiv.min.js deleted file mode 100644 index cd1c674f..00000000 --- a/docs/_build/html/_static/js/html5shiv.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/** -* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed -*/ -!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/docs/_build/html/_static/js/searchtools.js b/docs/_build/html/_static/js/searchtools.js deleted file mode 100644 index 0d4ca232..00000000 --- a/docs/_build/html/_static/js/searchtools.js +++ /dev/null @@ -1,595 +0,0 @@ -/* - * searchtools.js - * ~~~~~~~~~~~~~~~~ - * - * Sphinx JavaScript utilities for the full-text search. - * - * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. - * :license: BSD, see LICENSE for details. - * - * CHANGELOG: - * - Removes ajax call to get context for each result - * - Adjusts Search.query to remove duplicates in search results. - * - Adjusts Scorer to rank objects higher. - * - Adds Search._total_non_object_results to limit the number of search non - * object results. Object results do not perform another GET resquest, so they - * are cheap to display. - */ - -if (!Scorer) { - /** - * Simple result scoring code. - */ - var Scorer = { - // Implement the following function to further tweak the score for each result - // The function takes a result array [filename, title, anchor, descr, score] - // and returns the new score. - /* - score: function(result) { - return result[4]; - }, - */ - - // query matches the full name of an object - objNameMatch: 15, - // or matches in the last dotted part of the object name - objPartialMatch: 15, - // Additive scores depending on the priority of the object - objPrio: { - 0: 15, // used to be importantResults - 1: 5, // used to be objectResults - 2: -5 - }, // used to be unimportantResults - // Used when the priority is not in the mapping. - objPrioDefault: 0, - - // query found in title - title: 15, - partialTitle: 7, - // query found in terms - term: 10, - partialTerm: 2 - }; -} - -if (!splitQuery) { - function splitQuery(query) { - return query.split(/\s+/); - } -} - -/** - * Search Module - */ -var Search = { - _index: null, - _queued_query: null, - _pulse_status: -1, - _total_non_object_results: 10, - - htmlToText: function (htmlString) { - var htmlString = htmlString.replace(//g, ""); - var htmlElement = document.createElement("span"); - htmlElement.innerHTML = htmlString; - $(htmlElement) - .find(".headerlink") - .remove(); - docContent = $(htmlElement).find("[role=main]")[0]; - return docContent.textContent || docContent.innerText; - }, - - init: function () { - var params = $.getQueryParameters(); - if (params.q) { - var query = params.q[0]; - $('input[name="q"]')[0].value = query; - this.performSearch(query); - } - }, - - loadIndex: function (url) { - $.ajax({ - type: "GET", - url: url, - data: null, - dataType: "script", - cache: true, - complete: function (jqxhr, textstatus) { - if (textstatus != "success") { - document.getElementById("searchindexloader").src = url; - } - } - }); - }, - - setIndex: function (index) { - var q; - this._index = index; - if ((q = this._queued_query) !== null) { - this._queued_query = null; - Search.query(q); - } - }, - - hasIndex: function () { - return this._index !== null; - }, - - deferQuery: function (query) { - this._queued_query = query; - }, - - stopPulse: function () { - this._pulse_status = 0; - }, - - startPulse: function () { - if (this._pulse_status >= 0) return; - function pulse() { - var i; - Search._pulse_status = (Search._pulse_status + 1) % 4; - var dotString = ""; - for (i = 0; i < Search._pulse_status; i++) dotString += "."; - Search.dots.text(dotString); - if (Search._pulse_status > -1) window.setTimeout(pulse, 500); - } - pulse(); - }, - - /** - * perform a search for something (or wait until index is loaded) - */ - performSearch: function (query) { - // create the required interface elements - this.out = $("#search-results"); - this.title = $("

" + _("Searching") + "

").appendTo(this.out); - this.dots = $("").appendTo(this.title); - this.status = $('

 

').appendTo(this.out); - this.output = $('