diff --git a/.buildinfo b/.buildinfo deleted file mode 100644 index 378590710..000000000 --- a/.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: 06e8a676855577b99a70bcb467f3a087 -tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.doctrees/about_us.doctree b/.doctrees/about_us.doctree deleted file mode 100644 index 867136272..000000000 Binary files a/.doctrees/about_us.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.conditions.service.doctree b/.doctrees/apiref/chatsky.conditions.service.doctree deleted file mode 100644 index f49d2309d..000000000 Binary files a/.doctrees/apiref/chatsky.conditions.service.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.conditions.slots.doctree b/.doctrees/apiref/chatsky.conditions.slots.doctree deleted file mode 100644 index 8135b9675..000000000 Binary files a/.doctrees/apiref/chatsky.conditions.slots.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.conditions.standard.doctree b/.doctrees/apiref/chatsky.conditions.standard.doctree deleted file mode 100644 index eec1c7393..000000000 Binary files a/.doctrees/apiref/chatsky.conditions.standard.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.database.doctree b/.doctrees/apiref/chatsky.context_storages.database.doctree deleted file mode 100644 index 268b0475c..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.database.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.json.doctree b/.doctrees/apiref/chatsky.context_storages.json.doctree deleted file mode 100644 index 6b467ce73..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.json.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.mongo.doctree b/.doctrees/apiref/chatsky.context_storages.mongo.doctree deleted file mode 100644 index e0b4bb60a..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.mongo.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.pickle.doctree b/.doctrees/apiref/chatsky.context_storages.pickle.doctree deleted file mode 100644 index 26914662f..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.pickle.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.protocol.doctree b/.doctrees/apiref/chatsky.context_storages.protocol.doctree deleted file mode 100644 index 1257dfbf7..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.protocol.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.redis.doctree b/.doctrees/apiref/chatsky.context_storages.redis.doctree deleted file mode 100644 index 9e33a6218..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.redis.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.shelve.doctree b/.doctrees/apiref/chatsky.context_storages.shelve.doctree deleted file mode 100644 index 1d5a00cf9..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.shelve.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.sql.doctree b/.doctrees/apiref/chatsky.context_storages.sql.doctree deleted file mode 100644 index 47f0e7361..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.sql.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.context_storages.ydb.doctree b/.doctrees/apiref/chatsky.context_storages.ydb.doctree deleted file mode 100644 index 8b64eeba3..000000000 Binary files a/.doctrees/apiref/chatsky.context_storages.ydb.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.context.doctree b/.doctrees/apiref/chatsky.core.context.doctree deleted file mode 100644 index 6aabf9936..000000000 Binary files a/.doctrees/apiref/chatsky.core.context.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.message.doctree b/.doctrees/apiref/chatsky.core.message.doctree deleted file mode 100644 index 89729d457..000000000 Binary files a/.doctrees/apiref/chatsky.core.message.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.node_label.doctree b/.doctrees/apiref/chatsky.core.node_label.doctree deleted file mode 100644 index 2865a78f6..000000000 Binary files a/.doctrees/apiref/chatsky.core.node_label.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.pipeline.doctree b/.doctrees/apiref/chatsky.core.pipeline.doctree deleted file mode 100644 index 82084a66f..000000000 Binary files a/.doctrees/apiref/chatsky.core.pipeline.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.script.doctree b/.doctrees/apiref/chatsky.core.script.doctree deleted file mode 100644 index 646890b7d..000000000 Binary files a/.doctrees/apiref/chatsky.core.script.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.script_function.doctree b/.doctrees/apiref/chatsky.core.script_function.doctree deleted file mode 100644 index 55c3dbf12..000000000 Binary files a/.doctrees/apiref/chatsky.core.script_function.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.script_parsing.doctree b/.doctrees/apiref/chatsky.core.script_parsing.doctree deleted file mode 100644 index 2a2b6ade8..000000000 Binary files a/.doctrees/apiref/chatsky.core.script_parsing.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.actor.doctree b/.doctrees/apiref/chatsky.core.service.actor.doctree deleted file mode 100644 index c912793f9..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.actor.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.component.doctree b/.doctrees/apiref/chatsky.core.service.component.doctree deleted file mode 100644 index 8f70c0cb5..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.component.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.extra.doctree b/.doctrees/apiref/chatsky.core.service.extra.doctree deleted file mode 100644 index 1e717b28f..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.extra.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.group.doctree b/.doctrees/apiref/chatsky.core.service.group.doctree deleted file mode 100644 index 740af2d1c..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.group.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.service.doctree b/.doctrees/apiref/chatsky.core.service.service.doctree deleted file mode 100644 index 201aa7387..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.service.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.service.types.doctree b/.doctrees/apiref/chatsky.core.service.types.doctree deleted file mode 100644 index 37cef83d3..000000000 Binary files a/.doctrees/apiref/chatsky.core.service.types.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.transition.doctree b/.doctrees/apiref/chatsky.core.transition.doctree deleted file mode 100644 index 50344c2b7..000000000 Binary files a/.doctrees/apiref/chatsky.core.transition.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.core.utils.doctree b/.doctrees/apiref/chatsky.core.utils.doctree deleted file mode 100644 index 6b11be46c..000000000 Binary files a/.doctrees/apiref/chatsky.core.utils.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.destinations.standard.doctree b/.doctrees/apiref/chatsky.destinations.standard.doctree deleted file mode 100644 index 1d6342fc8..000000000 Binary files a/.doctrees/apiref/chatsky.destinations.standard.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.messengers.common.interface.doctree b/.doctrees/apiref/chatsky.messengers.common.interface.doctree deleted file mode 100644 index c92d8043d..000000000 Binary files a/.doctrees/apiref/chatsky.messengers.common.interface.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.messengers.common.types.doctree b/.doctrees/apiref/chatsky.messengers.common.types.doctree deleted file mode 100644 index 8c41f496a..000000000 Binary files a/.doctrees/apiref/chatsky.messengers.common.types.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.messengers.console.doctree b/.doctrees/apiref/chatsky.messengers.console.doctree deleted file mode 100644 index 2b2781e1d..000000000 Binary files a/.doctrees/apiref/chatsky.messengers.console.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.messengers.telegram.abstract.doctree b/.doctrees/apiref/chatsky.messengers.telegram.abstract.doctree deleted file mode 100644 index a4cf7c64d..000000000 Binary files a/.doctrees/apiref/chatsky.messengers.telegram.abstract.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.messengers.telegram.interface.doctree b/.doctrees/apiref/chatsky.messengers.telegram.interface.doctree deleted file mode 100644 index b42c38fd6..000000000 Binary files a/.doctrees/apiref/chatsky.messengers.telegram.interface.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.processing.slots.doctree b/.doctrees/apiref/chatsky.processing.slots.doctree deleted file mode 100644 index 1b52e2ae2..000000000 Binary files a/.doctrees/apiref/chatsky.processing.slots.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.processing.standard.doctree b/.doctrees/apiref/chatsky.processing.standard.doctree deleted file mode 100644 index 39b917511..000000000 Binary files a/.doctrees/apiref/chatsky.processing.standard.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.responses.slots.doctree b/.doctrees/apiref/chatsky.responses.slots.doctree deleted file mode 100644 index 037782b4e..000000000 Binary files a/.doctrees/apiref/chatsky.responses.slots.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.responses.standard.doctree b/.doctrees/apiref/chatsky.responses.standard.doctree deleted file mode 100644 index d447d301d..000000000 Binary files a/.doctrees/apiref/chatsky.responses.standard.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.slots.slots.doctree b/.doctrees/apiref/chatsky.slots.slots.doctree deleted file mode 100644 index 71a7a1b22..000000000 Binary files a/.doctrees/apiref/chatsky.slots.slots.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.stats.cli.doctree b/.doctrees/apiref/chatsky.stats.cli.doctree deleted file mode 100644 index e68fdc179..000000000 Binary files a/.doctrees/apiref/chatsky.stats.cli.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.stats.default_extractors.doctree b/.doctrees/apiref/chatsky.stats.default_extractors.doctree deleted file mode 100644 index 17c483a51..000000000 Binary files a/.doctrees/apiref/chatsky.stats.default_extractors.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.stats.instrumentor.doctree b/.doctrees/apiref/chatsky.stats.instrumentor.doctree deleted file mode 100644 index c526aed4a..000000000 Binary files a/.doctrees/apiref/chatsky.stats.instrumentor.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.stats.utils.doctree b/.doctrees/apiref/chatsky.stats.utils.doctree deleted file mode 100644 index df106bbae..000000000 Binary files a/.doctrees/apiref/chatsky.stats.utils.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.db_benchmark.basic_config.doctree b/.doctrees/apiref/chatsky.utils.db_benchmark.basic_config.doctree deleted file mode 100644 index 8e37f41d1..000000000 Binary files a/.doctrees/apiref/chatsky.utils.db_benchmark.basic_config.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.db_benchmark.benchmark.doctree b/.doctrees/apiref/chatsky.utils.db_benchmark.benchmark.doctree deleted file mode 100644 index 5578feccd..000000000 Binary files a/.doctrees/apiref/chatsky.utils.db_benchmark.benchmark.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.db_benchmark.report.doctree b/.doctrees/apiref/chatsky.utils.db_benchmark.report.doctree deleted file mode 100644 index 5865c5b3e..000000000 Binary files a/.doctrees/apiref/chatsky.utils.db_benchmark.report.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.devel.async_helpers.doctree b/.doctrees/apiref/chatsky.utils.devel.async_helpers.doctree deleted file mode 100644 index 3a66f36f7..000000000 Binary files a/.doctrees/apiref/chatsky.utils.devel.async_helpers.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.devel.extra_field_helpers.doctree b/.doctrees/apiref/chatsky.utils.devel.extra_field_helpers.doctree deleted file mode 100644 index e81a61156..000000000 Binary files a/.doctrees/apiref/chatsky.utils.devel.extra_field_helpers.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.devel.json_serialization.doctree b/.doctrees/apiref/chatsky.utils.devel.json_serialization.doctree deleted file mode 100644 index 12e099399..000000000 Binary files a/.doctrees/apiref/chatsky.utils.devel.json_serialization.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.testing.cleanup_db.doctree b/.doctrees/apiref/chatsky.utils.testing.cleanup_db.doctree deleted file mode 100644 index 9f8f5889c..000000000 Binary files a/.doctrees/apiref/chatsky.utils.testing.cleanup_db.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.testing.common.doctree b/.doctrees/apiref/chatsky.utils.testing.common.doctree deleted file mode 100644 index 9a932cc32..000000000 Binary files a/.doctrees/apiref/chatsky.utils.testing.common.doctree and /dev/null differ diff --git a/.doctrees/apiref/chatsky.utils.testing.toy_script.doctree b/.doctrees/apiref/chatsky.utils.testing.toy_script.doctree deleted file mode 100644 index f0aebd0a6..000000000 Binary files a/.doctrees/apiref/chatsky.utils.testing.toy_script.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_conditions.doctree b/.doctrees/apiref/index_conditions.doctree deleted file mode 100644 index a83a95a2a..000000000 Binary files a/.doctrees/apiref/index_conditions.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_context_storages.doctree b/.doctrees/apiref/index_context_storages.doctree deleted file mode 100644 index 3306ddf3b..000000000 Binary files a/.doctrees/apiref/index_context_storages.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_core.doctree b/.doctrees/apiref/index_core.doctree deleted file mode 100644 index 361070d2a..000000000 Binary files a/.doctrees/apiref/index_core.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_core.service.doctree b/.doctrees/apiref/index_core.service.doctree deleted file mode 100644 index 349332cf7..000000000 Binary files a/.doctrees/apiref/index_core.service.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_db_benchmark.doctree b/.doctrees/apiref/index_db_benchmark.doctree deleted file mode 100644 index ad52efb40..000000000 Binary files a/.doctrees/apiref/index_db_benchmark.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_destinations.doctree b/.doctrees/apiref/index_destinations.doctree deleted file mode 100644 index 971ca2a34..000000000 Binary files a/.doctrees/apiref/index_destinations.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_development_utils.doctree b/.doctrees/apiref/index_development_utils.doctree deleted file mode 100644 index 5a2738bf6..000000000 Binary files a/.doctrees/apiref/index_development_utils.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_messenger_interfaces.doctree b/.doctrees/apiref/index_messenger_interfaces.doctree deleted file mode 100644 index 815f23e28..000000000 Binary files a/.doctrees/apiref/index_messenger_interfaces.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_processing.doctree b/.doctrees/apiref/index_processing.doctree deleted file mode 100644 index 1acdf3873..000000000 Binary files a/.doctrees/apiref/index_processing.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_responses.doctree b/.doctrees/apiref/index_responses.doctree deleted file mode 100644 index 03d8e9e33..000000000 Binary files a/.doctrees/apiref/index_responses.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_slots.doctree b/.doctrees/apiref/index_slots.doctree deleted file mode 100644 index 5626f6532..000000000 Binary files a/.doctrees/apiref/index_slots.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_stats.doctree b/.doctrees/apiref/index_stats.doctree deleted file mode 100644 index 78616a479..000000000 Binary files a/.doctrees/apiref/index_stats.doctree and /dev/null differ diff --git a/.doctrees/apiref/index_testing_utils.doctree b/.doctrees/apiref/index_testing_utils.doctree deleted file mode 100644 index a0c9f591d..000000000 Binary files a/.doctrees/apiref/index_testing_utils.doctree and /dev/null differ diff --git a/.doctrees/community.doctree b/.doctrees/community.doctree deleted file mode 100644 index aeb4504a4..000000000 Binary files a/.doctrees/community.doctree and /dev/null differ diff --git a/.doctrees/development.doctree b/.doctrees/development.doctree deleted file mode 100644 index 55b432be8..000000000 Binary files a/.doctrees/development.doctree and /dev/null differ diff --git a/.doctrees/environment.pickle b/.doctrees/environment.pickle deleted file mode 100644 index ec2cea786..000000000 Binary files a/.doctrees/environment.pickle and /dev/null differ diff --git a/.doctrees/examples.doctree b/.doctrees/examples.doctree deleted file mode 100644 index 5fd4ac1ce..000000000 Binary files a/.doctrees/examples.doctree and /dev/null differ diff --git a/.doctrees/get_started.doctree b/.doctrees/get_started.doctree deleted file mode 100644 index fbde0559e..000000000 Binary files a/.doctrees/get_started.doctree and /dev/null differ diff --git a/.doctrees/index.doctree b/.doctrees/index.doctree deleted file mode 100644 index f9150c9ca..000000000 Binary files a/.doctrees/index.doctree and /dev/null differ diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.1_basics.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.1_basics.ipynb deleted file mode 100644 index 71caf1678..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.1_basics.ipynb +++ /dev/null @@ -1,129 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "88f2aac4", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 1. Basics\n", - "\n", - "The following tutorial shows the basic use of the database connection.\n", - "\n", - "See [context_storage_factory](../apiref/chatsky.context_storages.database.rst#chatsky.context_storages.database.context_storage_factory) function\n", - "for creating a context storage by path.\n", - "\n", - "In this example JSON file is used as a storage." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "e9e3323b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:52.257413Z", - "iopub.status.busy": "2024-10-10T12:16:52.256818Z", - "iopub.status.idle": "2024-10-10T12:16:53.972706Z", - "shell.execute_reply": "2024-10-10T12:16:53.971880Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[json,pickle]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b676fc2d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:53.975470Z", - "iopub.status.busy": "2024-10-10T12:16:53.974870Z", - "iopub.status.idle": "2024-10-10T12:16:55.218739Z", - "shell.execute_reply": "2024-10-10T12:16:55.217978Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "import pathlib\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH\n", - "\n", - "pathlib.Path(\"dbs\").mkdir(exist_ok=True)\n", - "db = context_storage_factory(\"json://dbs/file.json\")\n", - "# db = context_storage_factory(\"pickle://dbs/file.pkl\")\n", - "# db = context_storage_factory(\"shelve://dbs/file\")\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " # a function for automatic tutorial running (testing) with HAPPY_PATH\n", - "\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.2_postgresql.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.2_postgresql.ipynb deleted file mode 100644 index a11e1b223..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.2_postgresql.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "4179df0d", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 2. PostgreSQL\n", - "\n", - "This is a tutorial on using PostgreSQL.\n", - "\n", - "See [SQLContextStorage](../apiref/chatsky.context_storages.sql.rst#chatsky.context_storages.sql.SQLContextStorage) class\n", - "for storing your users' contexts in SQL databases.\n", - "\n", - "Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/)\n", - "and [asyncpg](https://magicstack.github.io/asyncpg/current/)\n", - "libraries for asynchronous access to PostgreSQL DB." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "bd2a25cb", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:56.928758Z", - "iopub.status.busy": "2024-10-10T12:16:56.928515Z", - "iopub.status.idle": "2024-10-10T12:16:58.140458Z", - "shell.execute_reply": "2024-10-10T12:16:58.139608Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[postgresql]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "8c8d4b07", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:58.143149Z", - "iopub.status.busy": "2024-10-10T12:16:58.142726Z", - "iopub.status.idle": "2024-10-10T12:16:59.343734Z", - "shell.execute_reply": "2024-10-10T12:16:59.342940Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "9e9a4896", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:59.346467Z", - "iopub.status.busy": "2024-10-10T12:16:59.345965Z", - "iopub.status.idle": "2024-10-10T12:16:59.489742Z", - "shell.execute_reply": "2024-10-10T12:16:59.489080Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "db_uri = \"postgresql+asyncpg://{}:{}@localhost:5432/{}\".format(\n", - " os.environ[\"POSTGRES_USERNAME\"],\n", - " os.environ[\"POSTGRES_PASSWORD\"],\n", - " os.environ[\"POSTGRES_DB\"],\n", - ")\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "e20e71f4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:16:59.492456Z", - "iopub.status.busy": "2024-10-10T12:16:59.491838Z", - "iopub.status.idle": "2024-10-10T12:16:59.524516Z", - "shell.execute_reply": "2024-10-10T12:16:59.523809Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.3_mongodb.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.3_mongodb.ipynb deleted file mode 100644 index 90f3ca3d6..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.3_mongodb.ipynb +++ /dev/null @@ -1,162 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "22fff33b", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 3. MongoDB\n", - "\n", - "This is a tutorial on using MongoDB.\n", - "\n", - "See [MongoContextStorage](../apiref/chatsky.context_storages.mongo.rst#chatsky.context_storages.mongo.MongoContextStorage) class\n", - "for storing you users' contexts in Mongo database.\n", - "\n", - "Chatsky uses [motor](https://motor.readthedocs.io/en/stable/)\n", - "library for asynchronous access to MongoDB." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "11a8bb2f", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:01.283736Z", - "iopub.status.busy": "2024-10-10T12:17:01.283519Z", - "iopub.status.idle": "2024-10-10T12:17:02.484155Z", - "shell.execute_reply": "2024-10-10T12:17:02.483375Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[mongodb]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "46309a90", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:02.486504Z", - "iopub.status.busy": "2024-10-10T12:17:02.486217Z", - "iopub.status.idle": "2024-10-10T12:17:03.671380Z", - "shell.execute_reply": "2024-10-10T12:17:03.670241Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "4c648df9", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:03.675147Z", - "iopub.status.busy": "2024-10-10T12:17:03.674373Z", - "iopub.status.idle": "2024-10-10T12:17:03.714681Z", - "shell.execute_reply": "2024-10-10T12:17:03.713869Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "db_uri = \"mongodb://{}:{}@localhost:27017/{}\".format(\n", - " os.environ[\"MONGO_INITDB_ROOT_USERNAME\"],\n", - " os.environ[\"MONGO_INITDB_ROOT_PASSWORD\"],\n", - " os.environ[\"MONGO_INITDB_ROOT_USERNAME\"],\n", - ")\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "5eb6bc96", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:03.717769Z", - "iopub.status.busy": "2024-10-10T12:17:03.717510Z", - "iopub.status.idle": "2024-10-10T12:17:03.761228Z", - "shell.execute_reply": "2024-10-10T12:17:03.760489Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.4_redis.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.4_redis.ipynb deleted file mode 100644 index 1e95b4417..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.4_redis.ipynb +++ /dev/null @@ -1,161 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "305872da", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 4. Redis\n", - "\n", - "This is a tutorial on using Redis.\n", - "\n", - "See [RedisContextStorage](../apiref/chatsky.context_storages.redis.rst#chatsky.context_storages.redis.RedisContextStorage) class\n", - "for storing you users' contexts in Redis database.\n", - "\n", - "Chatsky uses [redis.asyncio](https://redis.readthedocs.io/en/latest/)\n", - "library for asynchronous access to Redis DB." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a6bfa4a4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:05.949005Z", - "iopub.status.busy": "2024-10-10T12:17:05.948511Z", - "iopub.status.idle": "2024-10-10T12:17:07.262152Z", - "shell.execute_reply": "2024-10-10T12:17:07.261337Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[redis]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "42a80e74", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:07.264788Z", - "iopub.status.busy": "2024-10-10T12:17:07.264472Z", - "iopub.status.idle": "2024-10-10T12:17:08.455019Z", - "shell.execute_reply": "2024-10-10T12:17:08.454335Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "488e71f4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:08.457910Z", - "iopub.status.busy": "2024-10-10T12:17:08.457320Z", - "iopub.status.idle": "2024-10-10T12:17:08.462013Z", - "shell.execute_reply": "2024-10-10T12:17:08.461448Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "db_uri = \"redis://{}:{}@localhost:6379/{}\".format(\n", - " \"\", os.environ[\"REDIS_PASSWORD\"], \"0\"\n", - ")\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "dddbeba5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:08.464311Z", - "iopub.status.busy": "2024-10-10T12:17:08.463878Z", - "iopub.status.idle": "2024-10-10T12:17:08.481222Z", - "shell.execute_reply": "2024-10-10T12:17:08.480512Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.5_mysql.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.5_mysql.ipynb deleted file mode 100644 index 4a79ff260..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.5_mysql.ipynb +++ /dev/null @@ -1,164 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "5c86b801", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 5. MySQL\n", - "\n", - "This is a tutorial on using MySQL.\n", - "\n", - "See [SQLContextStorage](../apiref/chatsky.context_storages.sql.rst#chatsky.context_storages.sql.SQLContextStorage) class\n", - "for storing you users' contexts in SQL databases.\n", - "\n", - "Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/)\n", - "and [asyncmy](https://github.com/long2ice/asyncmy)\n", - "libraries for asynchronous access to MySQL DB." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "44d45b4e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:10.012219Z", - "iopub.status.busy": "2024-10-10T12:17:10.011966Z", - "iopub.status.idle": "2024-10-10T12:17:11.244802Z", - "shell.execute_reply": "2024-10-10T12:17:11.243880Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[mysql]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "3278adbe", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:11.247429Z", - "iopub.status.busy": "2024-10-10T12:17:11.246952Z", - "iopub.status.idle": "2024-10-10T12:17:12.552462Z", - "shell.execute_reply": "2024-10-10T12:17:12.551682Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "7576db26", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:12.555322Z", - "iopub.status.busy": "2024-10-10T12:17:12.554741Z", - "iopub.status.idle": "2024-10-10T12:17:12.633164Z", - "shell.execute_reply": "2024-10-10T12:17:12.632408Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "db_uri = \"mysql+asyncmy://{}:{}@localhost:3307/{}\".format(\n", - " os.environ[\"MYSQL_USERNAME\"],\n", - " os.environ[\"MYSQL_PASSWORD\"],\n", - " os.environ[\"MYSQL_DATABASE\"],\n", - ")\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "865fe64c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:12.635943Z", - "iopub.status.busy": "2024-10-10T12:17:12.635396Z", - "iopub.status.idle": "2024-10-10T12:17:12.673801Z", - "shell.execute_reply": "2024-10-10T12:17:12.673052Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.6_sqlite.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.6_sqlite.ipynb deleted file mode 100644 index 5930b6bd6..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.6_sqlite.ipynb +++ /dev/null @@ -1,168 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "77b70fa8", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 6. SQLite\n", - "\n", - "This is a tutorial on using SQLite.\n", - "\n", - "See [SQLContextStorage](../apiref/chatsky.context_storages.sql.rst#chatsky.context_storages.sql.SQLContextStorage) class\n", - "for storing you users' contexts in SQL databases.\n", - "\n", - "Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/)\n", - "and [aiosqlite](https://readthedocs.org/projects/aiosqlite/)\n", - "libraries for asynchronous access to SQLite DB.\n", - "\n", - "Note that protocol separator for windows differs from one for linux." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "4bfd7c5a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:14.425602Z", - "iopub.status.busy": "2024-10-10T12:17:14.425093Z", - "iopub.status.idle": "2024-10-10T12:17:15.660949Z", - "shell.execute_reply": "2024-10-10T12:17:15.660114Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[sqlite]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b849ff33", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:15.663546Z", - "iopub.status.busy": "2024-10-10T12:17:15.663114Z", - "iopub.status.idle": "2024-10-10T12:17:16.816046Z", - "shell.execute_reply": "2024-10-10T12:17:16.815403Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import pathlib\n", - "from platform import system\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "207e802a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:16.818954Z", - "iopub.status.busy": "2024-10-10T12:17:16.818478Z", - "iopub.status.idle": "2024-10-10T12:17:16.855671Z", - "shell.execute_reply": "2024-10-10T12:17:16.855024Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "pathlib.Path(\"dbs\").mkdir(exist_ok=True)\n", - "db_file = pathlib.Path(\"dbs/sqlite.db\")\n", - "db_file.touch(exist_ok=True)\n", - "\n", - "separator = \"///\" if system() == \"Windows\" else \"////\"\n", - "db_uri = f\"sqlite+aiosqlite:{separator}{db_file.absolute()}\"\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "e5f3607a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:16.858466Z", - "iopub.status.busy": "2024-10-10T12:17:16.857918Z", - "iopub.status.idle": "2024-10-10T12:17:16.897451Z", - "shell.execute_reply": "2024-10-10T12:17:16.896692Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.7_yandex_database.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.7_yandex_database.ipynb deleted file mode 100644 index 669bbd801..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.7_yandex_database.ipynb +++ /dev/null @@ -1,169 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "5b6888f6", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 7. Yandex DataBase\n", - "\n", - "This is a tutorial on how to use Yandex DataBase.\n", - "\n", - "See [YDBContextStorage](../apiref/chatsky.context_storages.ydb.rst#chatsky.context_storages.ydb.YDBContextStorage) class\n", - "for storing you users' contexts in Yandex database.\n", - "\n", - "Chatsky uses [ydb.aio](https://ydb.tech/en/docs/)\n", - "library for asynchronous access to Yandex DB." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "e7fd24e5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:18.538684Z", - "iopub.status.busy": "2024-10-10T12:17:18.538428Z", - "iopub.status.idle": "2024-10-10T12:17:19.760313Z", - "shell.execute_reply": "2024-10-10T12:17:19.759509Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[ydb]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b8400656", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:19.763036Z", - "iopub.status.busy": "2024-10-10T12:17:19.762597Z", - "iopub.status.idle": "2024-10-10T12:17:21.002212Z", - "shell.execute_reply": "2024-10-10T12:17:21.001371Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from chatsky.context_storages import context_storage_factory\n", - "\n", - "from chatsky import Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "0a6b9eb7", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:21.005154Z", - "iopub.status.busy": "2024-10-10T12:17:21.004561Z", - "iopub.status.idle": "2024-10-10T12:17:21.039326Z", - "shell.execute_reply": "2024-10-10T12:17:21.038536Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# ##### Connecting to yandex cloud\n", - "# https://github.com/zinal/ydb-python-sdk/blob/ex_basic-example_p1/examples/basic_example_v1/README.md\n", - "# export YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS=$HOME/key-ydb-sa-0.json\n", - "# export YDB_ENDPOINT=grpcs://ydb.serverless.yandexcloud.net:2135\n", - "# export YDB_DATABASE=/ru-central1/qwertyuiopasdfgh/123456789qwertyui\n", - "# ##### or use local-ydb with variables from .env_file\n", - "# db_uri=\"grpc://localhost:2136/local\"\n", - "\n", - "db_uri = \"{}{}\".format(\n", - " os.environ[\"YDB_ENDPOINT\"],\n", - " os.environ[\"YDB_DATABASE\"],\n", - ")\n", - "db = context_storage_factory(db_uri)\n", - "\n", - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4983a891", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:21.042427Z", - "iopub.status.busy": "2024-10-10T12:17:21.041835Z", - "iopub.status.idle": "2024-10-10T12:17:21.095106Z", - "shell.execute_reply": "2024-10-10T12:17:21.094372Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.8_db_benchmarking.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.context_storages.8_db_benchmarking.ipynb deleted file mode 100644 index e81631c67..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.context_storages.8_db_benchmarking.ipynb +++ /dev/null @@ -1,5739 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b25d5946", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 8. Context storage benchmarking\n", - "\n", - "This tutorial shows how to benchmark context storages.\n", - "\n", - "For more info see [API reference](../apiref/chatsky.utils.db_benchmark.benchmark.rst)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "da6393b4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:22.668266Z", - "iopub.status.busy": "2024-10-10T12:17:22.667469Z", - "iopub.status.idle": "2024-10-10T12:17:23.927949Z", - "shell.execute_reply": "2024-10-10T12:17:23.927202Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[benchmark,postgresql,mongodb,redis,mysql,sqlite,ydb]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ec103b12", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:23.930399Z", - "iopub.status.busy": "2024-10-10T12:17:23.929992Z", - "iopub.status.idle": "2024-10-10T12:17:25.351419Z", - "shell.execute_reply": "2024-10-10T12:17:25.350577Z" - } - }, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "from platform import system\n", - "import tempfile\n", - "\n", - "import chatsky.utils.db_benchmark as benchmark" - ] - }, - { - "cell_type": "markdown", - "id": "0cf675b4", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "## Context storage setup" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "6c3c0f5d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:25.355840Z", - "iopub.status.busy": "2024-10-10T12:17:25.355110Z", - "iopub.status.idle": "2024-10-10T12:17:25.360942Z", - "shell.execute_reply": "2024-10-10T12:17:25.360069Z" - } - }, - "outputs": [], - "source": [ - "# this cell is only required for shelve and sqlite databases\n", - "tutorial_dir = Path(tempfile.mkdtemp())\n", - "db_path = tutorial_dir / \"dbs\"\n", - "db_path.mkdir()\n", - "sqlite_file = db_path / \"sqlite.db\"\n", - "sqlite_file.touch(exist_ok=True)\n", - "sqlite_separator = \"///\" if system() == \"Windows\" else \"////\"" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "6f388276", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:25.365614Z", - "iopub.status.busy": "2024-10-10T12:17:25.364487Z", - "iopub.status.idle": "2024-10-10T12:17:25.370232Z", - "shell.execute_reply": "2024-10-10T12:17:25.369494Z" - } - }, - "outputs": [], - "source": [ - "storages = {\n", - " \"Shelve\": f\"shelve://{db_path}/shelve\",\n", - " \"PostgreSQL\": \"postgresql+asyncpg://postgres:pass@localhost:5432/test\",\n", - " \"MongoDB\": \"mongodb://admin:pass@localhost:27017/admin\",\n", - " \"Redis\": \"redis://:pass@localhost:6379/0\",\n", - " \"MySQL\": \"mysql+asyncmy://root:pass@localhost:3307/test\",\n", - " \"SQLite\": f\"sqlite+aiosqlite:{sqlite_separator}{sqlite_file.absolute()}\",\n", - " \"YDB\": \"grpc://localhost:2136/local\",\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "aea0c289", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "## Saving benchmark results to a file\n", - "\n", - "Benchmark results are saved to files.\n", - "\n", - "For that there exist two functions:\n", - "[benchmark_all](../apiref/chatsky.utils.db_benchmark.benchmark.rst#chatsky.utils.db_benchmark.benchmark.benchmark_all)\n", - "and\n", - "[save_results_to_file](../apiref/chatsky.utils.db_benchmark.benchmark.rst#chatsky.utils.db_benchmark.benchmark.save_results_to_file).\n", - "\n", - "Note: context storages passed into these functions will be cleared.\n", - "\n", - "Once the benchmark results are saved to a file,\n", - "you can view and analyze them using two methods:\n", - "\n", - "* [Using the Report Function](#Using-the-report-function): This function\n", - " can display specified information from a given file.\n", - " By default, it prints the name and average metrics for each benchmark case.\n", - "\n", - "* [Using the Streamlit App](#Using-Streamlit-app): A Streamlit app\n", - " is available for viewing and comparing benchmark results.\n", - " You can upload benchmark result files using the app's \"Benchmark sets\" tab,\n", - " inspect individual results in the \"View\" tab,\n", - " and compare metrics in the \"Compare\" tab.\n", - "\n", - "Benchmark results are saved according to a specific schema,\n", - "which can be found in the benchmark schema documentation.\n", - "Each database being benchmarked will have its own result file.\n", - "\n", - "### Configuration\n", - "\n", - "The first one is a higher-level wrapper of the second one.\n", - "The first function accepts\n", - "[BenchmarkCase](../apiref/chatsky.utils.db_benchmark.benchmark.rst#chatsky.utils.db_benchmark.benchmark.BenchmarkCase)\n", - "which configures databases to be benchmarked and parameters of the benchmarks.\n", - "The second function accepts only a single URI for the database\n", - "and several benchmark configurations. So, the second function is simpler to use,\n", - "while the first function allows for more configuration\n", - "(e.g. having different databases benchmarked in a single file).\n", - "\n", - "Both function use\n", - "[BenchmarkConfig](../apiref/chatsky.utils.db_benchmark.benchmark.rst#chatsky.utils.db_benchmark.benchmark.BenchmarkConfig)\n", - "to configure benchmark behaviour.\n", - "`BenchmarkConfig` is only an interface for benchmark configurations.\n", - "Its most basic implementation is\n", - "[BasicBenchmarkConfig](../apiref/chatsky.utils.db_benchmark.basic_config.rst#chatsky.utils.db_benchmark.basic_config.BasicBenchmarkConfig).\n", - "\n", - "Chatsky provides configuration presets in the\n", - "[basic_config](../apiref/chatsky.utils.db_benchmark.basic_config.rst) module,\n", - "covering various contexts, messages, and edge cases.\n", - "You can use these presets by passing them to the benchmark functions or create\n", - "your own configuration.\n", - "\n", - "To learn more about using presets see\n", - "[Configuration presets](#Configuration-presets)\n", - "\n", - "Benchmark configs have several parameters:\n", - "\n", - "Setting `context_num` to 50 means that we'll run fifty cycles of\n", - "writing and reading context. This way we'll be able to get a more accurate\n", - "average read/write time as well as check if read/write times are dependent\n", - "on the number of contexts in the storage.\n", - "\n", - "You can also configure the `dialog_len`, `message_dimensions` and\n", - "`misc_dimensions` parameters. This allows you to set the contexts\n", - "you want your database to be benchmarked with.\n", - "\n", - "### File structure\n", - "\n", - "The files are saved according to [the schema](\n", - "../_misc/benchmark_schema.json\n", - ")." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "2f39c787", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:25.374806Z", - "iopub.status.busy": "2024-10-10T12:17:25.373680Z", - "iopub.status.idle": "2024-10-10T12:17:31.267962Z", - "shell.execute_reply": "2024-10-10T12:17:31.267070Z" - } - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "208346ace1804e9583ec9e450e11a5be", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - " 0%| | 0/1 [00:00\n", - "\n", - "Note\n", - "\n", - "You can also import `LongpollingInterface`\n", - "under the alias of `TelegramInterface` from `chatsky.messengers`:\n", - "\n", - "```python\n", - "from chatsky.messengers import TelegramInterface\n", - "```\n", - "\n", - "" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "da9c6ae2", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:35.985351Z", - "iopub.status.busy": "2024-10-10T12:17:35.984572Z", - "iopub.status.idle": "2024-10-10T12:17:35.989498Z", - "shell.execute_reply": "2024-10-10T12:17:35.988792Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "script = {\n", - " \"greeting_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [\n", - " Tr(dst=\"greeting_node\", cnd=cnd.ExactMatch(\"/start\"))\n", - " ],\n", - " },\n", - " \"greeting_node\": {\n", - " RESPONSE: \"Hi\",\n", - " TRANSITIONS: [Tr(dst=dst.Current())],\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: \"Please, repeat the request\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"greeting_node\", cnd=cnd.ExactMatch(\"/start\"))\n", - " ],\n", - " },\n", - " }\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "0abec332", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:35.992069Z", - "iopub.status.busy": "2024-10-10T12:17:35.991576Z", - "iopub.status.idle": "2024-10-10T12:17:36.054503Z", - "shell.execute_reply": "2024-10-10T12:17:36.053774Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "interface = LongpollingInterface(token=os.environ[\"TG_BOT_TOKEN\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c911ac9d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:36.057245Z", - "iopub.status.busy": "2024-10-10T12:17:36.056803Z", - "iopub.status.idle": "2024-10-10T12:17:36.061576Z", - "shell.execute_reply": "2024-10-10T12:17:36.060977Z" - } - }, - "outputs": [], - "source": [ - "pipeline = Pipeline(\n", - " script=script,\n", - " start_label=(\"greeting_flow\", \"start_node\"),\n", - " fallback_label=(\"greeting_flow\", \"fallback_node\"),\n", - " messenger_interface=interface,\n", - " # The interface can be passed as a pipeline argument.\n", - ")\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " if is_interactive_mode():\n", - " # prevent run during doc building\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.2_attachments.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.2_attachments.ipynb deleted file mode 100644 index 3102cb311..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.2_attachments.ipynb +++ /dev/null @@ -1,417 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "90c632c5", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Telegram: 2. Attachments\n", - "\n", - "The following tutorial shows how to send different attachments using\n", - "telegram interfaces.\n", - "\n", - "Here, [LongpollingInterface](../apiref/chatsky.messengers.telegram.interface.rst#chatsky.messengers.telegram.interface.LongpollingInterface)\n", - "class and [python-telegram-bot](https://docs.python-telegram-bot.org/)\n", - "library are used for accessing telegram API in polling mode.\n", - "\n", - "Telegram API token is required to access telegram API." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "f6fb0625", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:37.991591Z", - "iopub.status.busy": "2024-10-10T12:17:37.991380Z", - "iopub.status.idle": "2024-10-10T12:17:39.264645Z", - "shell.execute_reply": "2024-10-10T12:17:39.263764Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[telegram]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "635be29b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:39.267388Z", - "iopub.status.busy": "2024-10-10T12:17:39.266936Z", - "iopub.status.idle": "2024-10-10T12:17:40.528191Z", - "shell.execute_reply": "2024-10-10T12:17:40.527373Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "from pydantic import HttpUrl\n", - "\n", - "from chatsky import (\n", - " GLOBAL,\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " Message,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - ")\n", - "from chatsky.messengers.telegram import LongpollingInterface\n", - "from chatsky.core.message import (\n", - " Animation,\n", - " Audio,\n", - " Contact,\n", - " Document,\n", - " Location,\n", - " Image,\n", - " MediaGroup,\n", - " Poll,\n", - " PollOption,\n", - " Sticker,\n", - " Video,\n", - " VideoMessage,\n", - " VoiceMessage,\n", - ")\n", - "from chatsky.utils.testing.common import is_interactive_mode" - ] - }, - { - "cell_type": "markdown", - "id": "a5881423", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Example attachment data is specified below in form of dictionaries.\n", - "List of attachments that telegram messenger interface can send can\n", - "be found here:\n", - "[supported_request_attachment_types](../apiref/chatsky.messengers.telegram.abstract.rst#chatsky.messengers.telegram.abstract._AbstractTelegramInterface.supported_request_attachment_types)." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "bd52f133", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:40.531342Z", - "iopub.status.busy": "2024-10-10T12:17:40.530651Z", - "iopub.status.idle": "2024-10-10T12:17:40.537863Z", - "shell.execute_reply": "2024-10-10T12:17:40.537293Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "\n", - "EXAMPLE_ATTACHMENT_SOURCE = (\n", - " \"https://github.com/deeppavlov/chatsky/wiki/example_attachments\"\n", - ")\n", - "\n", - "location_data = {\"latitude\": 50.65, \"longitude\": 3.916667}\n", - "\n", - "contact_data = {\n", - " \"phone_number\": \"8-900-555-35-35\",\n", - " \"first_name\": \"Hope\",\n", - " \"last_name\": \"Credit\",\n", - "}\n", - "\n", - "sticker_data = {\n", - " \"id\": (\n", - " \"CAACAgIAAxkBAAErAAFXZibO5ksphCKS\"\n", - " + \"XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE\"\n", - " ),\n", - " \"caption\": \"A sticker I've just found\",\n", - "}\n", - "\n", - "audio_data = {\n", - " \"source\": HttpUrl(\n", - " f\"{EXAMPLE_ATTACHMENT_SOURCE}/separation-william-king.mp3\"\n", - " ),\n", - " \"caption\": \"Separation melody by William King\",\n", - " \"filename\": \"separation-william-king.mp3\",\n", - "}\n", - "\n", - "video_data = {\n", - " \"source\": HttpUrl(\n", - " f\"{EXAMPLE_ATTACHMENT_SOURCE}/crownfall-lags-nkognit0.mp4\"\n", - " ),\n", - " \"caption\": \"Epic Dota2 gameplay by Nkognit0\",\n", - " \"filename\": \"crownfall-lags-nkognit0.mp4\",\n", - "}\n", - "\n", - "animation_data = {\n", - " # For some reason, if we don't define filename explicitly,\n", - " # animation is sent as file.\n", - " \"source\": HttpUrl(\n", - " f\"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.gif\"\n", - " ),\n", - " \"caption\": \"Hong Kong skyscraper views by Simplyart4794\",\n", - " \"filename\": \"hong-kong-simplyart4794.gif\",\n", - "}\n", - "\n", - "image_data = {\n", - " \"source\": HttpUrl(f\"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png\"),\n", - " \"caption\": \"DeepPavlov logo\",\n", - " \"filename\": \"deeppavlov.png\",\n", - "}\n", - "\n", - "document_data = {\n", - " \"source\": HttpUrl(f\"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf\"),\n", - " \"caption\": \"DeepPavlov article\",\n", - " \"filename\": \"deeppavlov-article.pdf\",\n", - "}\n", - "\n", - "ATTACHMENTS = [\n", - " \"location\",\n", - " \"contact\",\n", - " \"poll\",\n", - " \"sticker\",\n", - " \"audio\",\n", - " \"video\",\n", - " \"animation\",\n", - " \"image\",\n", - " \"document\",\n", - " \"voice_message\",\n", - " \"video_message\",\n", - " \"media_group\",\n", - "]\n", - "\n", - "QUOTED_ATTACHMENTS = [f'\"{attachment}\"' for attachment in ATTACHMENTS]" - ] - }, - { - "cell_type": "markdown", - "id": "3078835c", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "The bot below sends different attachments on request.\n", - "\n", - "[Here](../apiref/chatsky.core.message.rst) you can find\n", - "all the attachment options available." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "2bab81d6", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:40.540062Z", - "iopub.status.busy": "2024-10-10T12:17:40.539833Z", - "iopub.status.idle": "2024-10-10T12:17:40.553595Z", - "shell.execute_reply": "2024-10-10T12:17:40.552993Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "script = {\n", - " GLOBAL: {\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"main_flow\", f\"{attachment}_node\"),\n", - " cnd=cnd.ExactMatch(attachment),\n", - " )\n", - " for attachment in ATTACHMENTS\n", - " ]\n", - " },\n", - " \"main_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [Tr(dst=\"intro_node\", cnd=cnd.ExactMatch(\"/start\"))],\n", - " },\n", - " \"intro_node\": {\n", - " RESPONSE: f'Type {\", \".join(QUOTED_ATTACHMENTS[:-1])}'\n", - " f\" or {QUOTED_ATTACHMENTS[-1]}\"\n", - " f\" to receive a corresponding attachment!\",\n", - " },\n", - " \"location_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your location!\",\n", - " attachments=[Location(**location_data)],\n", - " ),\n", - " },\n", - " \"contact_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your contact!\",\n", - " attachments=[Contact(**contact_data)],\n", - " ),\n", - " },\n", - " \"poll_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your poll!\",\n", - " attachments=[\n", - " Poll(\n", - " question=\"What is the poll question?\",\n", - " options=[\n", - " PollOption(text=\"This one!\"),\n", - " PollOption(text=\"Not this one :(\"),\n", - " ],\n", - " ),\n", - " ],\n", - " ),\n", - " },\n", - " \"sticker_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your sticker!\",\n", - " attachments=[Sticker(**sticker_data)],\n", - " ),\n", - " },\n", - " \"audio_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your audio!\",\n", - " attachments=[Audio(**audio_data)],\n", - " ),\n", - " },\n", - " \"video_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your video!\",\n", - " attachments=[Video(**video_data)],\n", - " ),\n", - " },\n", - " \"animation_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your animation!\",\n", - " attachments=[Animation(**animation_data)],\n", - " ),\n", - " },\n", - " \"image_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your image!\",\n", - " attachments=[Image(**image_data)],\n", - " ),\n", - " },\n", - " \"document_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your document!\",\n", - " attachments=[Document(**document_data)],\n", - " ),\n", - " },\n", - " \"voice_message_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your voice message!\",\n", - " attachments=[VoiceMessage(source=audio_data[\"source\"])],\n", - " ),\n", - " },\n", - " \"video_message_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your video message!\",\n", - " attachments=[VideoMessage(source=video_data[\"source\"])],\n", - " ),\n", - " },\n", - " \"media_group_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your media group!\",\n", - " attachments=[\n", - " MediaGroup(\n", - " group=[\n", - " Image(**image_data),\n", - " Video(**video_data),\n", - " ],\n", - " )\n", - " ],\n", - " ),\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: f\"Unknown attachment type, try again! \"\n", - " f\"Supported attachments are: \"\n", - " f'{\", \".join(QUOTED_ATTACHMENTS[:-1])} '\n", - " f\"and {QUOTED_ATTACHMENTS[-1]}.\",\n", - " },\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "ecbced0f", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:40.555811Z", - "iopub.status.busy": "2024-10-10T12:17:40.555421Z", - "iopub.status.idle": "2024-10-10T12:17:40.634846Z", - "shell.execute_reply": "2024-10-10T12:17:40.633985Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "interface = LongpollingInterface(token=os.environ[\"TG_BOT_TOKEN\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "99f81a72", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:40.637989Z", - "iopub.status.busy": "2024-10-10T12:17:40.637330Z", - "iopub.status.idle": "2024-10-10T12:17:40.644286Z", - "shell.execute_reply": "2024-10-10T12:17:40.643322Z" - } - }, - "outputs": [], - "source": [ - "pipeline = Pipeline(\n", - " script=script,\n", - " start_label=(\"main_flow\", \"start_node\"),\n", - " fallback_label=(\"main_flow\", \"fallback_node\"),\n", - " messenger_interface=interface,\n", - " # The interface can be passed as a pipeline argument.\n", - ")\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " if is_interactive_mode():\n", - " # prevent run during doc building\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.3_advanced.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.3_advanced.ipynb deleted file mode 100644 index a017b2d54..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.telegram.3_advanced.ipynb +++ /dev/null @@ -1,420 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ca629070", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Telegram: 3. Advanced\n", - "\n", - "The following tutorial shows several advanced cases of user-to-bot interaction.\n", - "\n", - "Here, [LongpollingInterface](../apiref/chatsky.messengers.telegram.interface.rst#chatsky.messengers.telegram.interface.LongpollingInterface)\n", - "class and [python-telegram-bot](https://docs.python-telegram-bot.org/)\n", - "library are used for accessing telegram API in polling mode.\n", - "\n", - "Telegram API token is required to access telegram API." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "4ca898c7", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:42.614644Z", - "iopub.status.busy": "2024-10-10T12:17:42.614126Z", - "iopub.status.idle": "2024-10-10T12:17:43.808917Z", - "shell.execute_reply": "2024-10-10T12:17:43.808049Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages." - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[telegram]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "2c6decb9", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:43.811375Z", - "iopub.status.busy": "2024-10-10T12:17:43.811136Z", - "iopub.status.idle": "2024-10-10T12:17:45.125957Z", - "shell.execute_reply": "2024-10-10T12:17:45.125290Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import os\n", - "from hashlib import sha256\n", - "from urllib.request import urlopen\n", - "\n", - "from pydantic import HttpUrl\n", - "from telegram import InlineKeyboardButton, InlineKeyboardMarkup\n", - "from telegram.constants import ParseMode\n", - "\n", - "from chatsky import (\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " LOCAL,\n", - " Message,\n", - " Pipeline,\n", - " BaseResponse,\n", - " Context,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - ")\n", - "from chatsky.messengers.telegram import LongpollingInterface\n", - "from chatsky.core.message import (\n", - " DataAttachment,\n", - " Document,\n", - " Image,\n", - " Location,\n", - " Sticker,\n", - " MessageInitTypes,\n", - ")\n", - "from chatsky.utils.testing.common import is_interactive_mode" - ] - }, - { - "cell_type": "markdown", - "id": "46221329", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "This bot shows different special telegram messenger interface use cases,\n", - "such as:\n", - "\n", - "1. Interactive keyboard with buttons.\n", - "2. Text formatted with Markdown V2.\n", - "3. Multiple attachments of different kind handling.\n", - "4. Image with a spoiler.\n", - "5. Document with a thumbnail.\n", - "6. Attachment bytes hash.\n", - "\n", - "Last option (\"Raw attachments!\") button might be especially interesting,\n", - "because it shows how bot percepts different telegram attachments sent by user\n", - "in terms and datastructures of Chatsky.\n", - "\n", - "
\n", - "\n", - "Tip\n", - "\n", - "Check out\n", - "[this](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot)\n", - "class for information about different arguments\n", - "for sending attachments, `send_...` methods.\n", - "\n", - "For example, documentation for `Image` extra fields can be found in the\n", - "[send_photo](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot.send_photo)\n", - "method.\n", - "\n", - "The `Message` class also supports extra keywords as described in\n", - "[send_message](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot.send_message).\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "89501c25", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:45.128856Z", - "iopub.status.busy": "2024-10-10T12:17:45.128316Z", - "iopub.status.idle": "2024-10-10T12:17:45.263362Z", - "shell.execute_reply": "2024-10-10T12:17:45.262711Z" - } - }, - "outputs": [], - "source": [ - "\n", - "EXAMPLE_ATTACHMENT_SOURCE = (\n", - " \"https://github.com/deeppavlov/chatsky/wiki/example_attachments\"\n", - ")\n", - "\n", - "image_url = HttpUrl(f\"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png\")\n", - "\n", - "formatted_text = \"\"\"\n", - "Visit [this link](https://core.telegram.org/bots/api#formatting-options)\n", - "for more information about formatting options in telegram\\.\n", - "\"\"\" # noqa: W605\n", - "\n", - "location_data = {\"latitude\": 59.9386, \"longitude\": 30.3141}\n", - "\n", - "sticker_data = {\n", - " \"id\": (\n", - " \"CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v\"\n", - " + \"gp1wACRygAAiSjCUtLa7RHZy76ezQE\"\n", - " ),\n", - "}\n", - "\n", - "image_data = {\n", - " \"source\": image_url,\n", - " \"caption\": \"DeepPavlov logo\",\n", - " \"has_spoiler\": True,\n", - " \"filename\": \"deeppavlov_logo.png\",\n", - "}\n", - "# last 3 fields are extra keywords passed directly to the\n", - "# telegram.Bot.send_photo method\n", - "\n", - "document_data = {\n", - " \"source\": HttpUrl(f\"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf\"),\n", - " \"caption\": \"DeepPavlov article\",\n", - " \"filename\": \"deeppavlov_article.pdf\",\n", - " \"thumbnail\": urlopen(str(image_url)).read(),\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b59ec6f4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:45.266218Z", - "iopub.status.busy": "2024-10-10T12:17:45.265786Z", - "iopub.status.idle": "2024-10-10T12:17:45.271407Z", - "shell.execute_reply": "2024-10-10T12:17:45.270798Z" - } - }, - "outputs": [], - "source": [ - "class DataAttachmentHash(BaseResponse):\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " attachment = [\n", - " a\n", - " for a in ctx.last_request.attachments\n", - " if isinstance(a, DataAttachment)\n", - " ]\n", - " if len(attachment) > 0:\n", - " attachment_bytes = await attachment[0].get_bytes(\n", - " ctx.pipeline.messenger_interface\n", - " )\n", - " attachment_hash = sha256(attachment_bytes).hexdigest()\n", - " response = (\n", - " \"Here's your previous request first attachment sha256 hash:\\n\"\n", - " f\"```\\n{attachment_hash}\\n```\"\n", - " )\n", - " return Message(text=response, parse_mode=ParseMode.MARKDOWN_V2)\n", - " else:\n", - " return \"Last request did not contain any data attachment!\"" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "0cccd6ea", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:45.273969Z", - "iopub.status.busy": "2024-10-10T12:17:45.273406Z", - "iopub.status.idle": "2024-10-10T12:17:45.286151Z", - "shell.execute_reply": "2024-10-10T12:17:45.285489Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "script = {\n", - " \"main_flow\": {\n", - " LOCAL: {\n", - " TRANSITIONS: [\n", - " Tr(dst=\"main_node\", cnd=cnd.ExactMatch(\"/start\")),\n", - " Tr(dst=\"formatted_node\", cnd=cnd.HasCallbackQuery(\"formatted\")),\n", - " Tr(\n", - " dst=\"attachments_node\",\n", - " cnd=cnd.HasCallbackQuery(\"attachments\"),\n", - " ),\n", - " Tr(dst=\"secret_node\", cnd=cnd.HasCallbackQuery(\"secret\")),\n", - " Tr(dst=\"thumbnail_node\", cnd=cnd.HasCallbackQuery(\"thumbnail\")),\n", - " Tr(dst=\"hash_init_node\", cnd=cnd.HasCallbackQuery(\"hash\")),\n", - " Tr(dst=\"main_node\", cnd=cnd.HasCallbackQuery(\"restart\")),\n", - " ]\n", - " },\n", - " \"start_node\": {},\n", - " \"main_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Welcome! Choose what you want to receive.\",\n", - " reply_markup=InlineKeyboardMarkup(\n", - " [\n", - " [\n", - " InlineKeyboardButton(\n", - " \"Cute formatted text!\",\n", - " callback_data=\"formatted\",\n", - " ),\n", - " ],\n", - " [\n", - " InlineKeyboardButton(\n", - " \"Multiple attachments!\",\n", - " callback_data=\"attachments\",\n", - " ),\n", - " InlineKeyboardButton(\n", - " \"Secret image!\", callback_data=\"secret\"\n", - " ),\n", - " InlineKeyboardButton(\n", - " \"Document with thumbnail!\",\n", - " callback_data=\"thumbnail\",\n", - " ),\n", - " ],\n", - " [\n", - " InlineKeyboardButton(\n", - " \"First attachment bytes hash!\",\n", - " callback_data=\"hash\",\n", - " ),\n", - " ],\n", - " [\n", - " InlineKeyboardButton(\n", - " \"Restart!\", callback_data=\"restart\"\n", - " ),\n", - " ],\n", - " ],\n", - " ),\n", - " # you can add extra fields to the message itself;\n", - " # they are passed to telegram.Bot.send_message\n", - " ),\n", - " },\n", - " \"formatted_node\": {\n", - " RESPONSE: Message(\n", - " text=formatted_text, parse_mode=ParseMode.MARKDOWN_V2\n", - " ),\n", - " },\n", - " \"attachments_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your message with multiple attachments \"\n", - " + \"(a location and a sticker)!\",\n", - " attachments=[\n", - " Location(**location_data),\n", - " Sticker(**sticker_data),\n", - " ],\n", - " ),\n", - " },\n", - " \"secret_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your secret image!\",\n", - " attachments=[Image(**image_data)],\n", - " ),\n", - " },\n", - " \"thumbnail_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Here's your document with thumbnail!\",\n", - " attachments=[Document(**document_data)],\n", - " ),\n", - " },\n", - " \"hash_init_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Alright! Now send me a message with data attachment \"\n", - " + \"(audio, video, animation, image, sticker or document)!\"\n", - " ),\n", - " TRANSITIONS: [Tr(dst=\"hash_request_node\")],\n", - " },\n", - " \"hash_request_node\": {\n", - " RESPONSE: DataAttachmentHash(),\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: Message(\n", - " text=\"Bot has entered unrecoverable state:\"\n", - " + \"\\nRun /start command again to restart.\"\n", - " ),\n", - " },\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "a5fc0e0c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:45.288901Z", - "iopub.status.busy": "2024-10-10T12:17:45.288336Z", - "iopub.status.idle": "2024-10-10T12:17:45.352708Z", - "shell.execute_reply": "2024-10-10T12:17:45.352027Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "interface = LongpollingInterface(token=os.environ[\"TG_BOT_TOKEN\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "e29b2cc3", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:45.355354Z", - "iopub.status.busy": "2024-10-10T12:17:45.354977Z", - "iopub.status.idle": "2024-10-10T12:17:45.360154Z", - "shell.execute_reply": "2024-10-10T12:17:45.359421Z" - } - }, - "outputs": [], - "source": [ - "pipeline = Pipeline(\n", - " script=script,\n", - " start_label=(\"main_flow\", \"start_node\"),\n", - " fallback_label=(\"main_flow\", \"fallback_node\"),\n", - " messenger_interface=interface,\n", - " # The interface can be passed as a pipeline argument.\n", - ")\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " if is_interactive_mode():\n", - " # prevent run during doc building\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.1_fastapi.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.1_fastapi.ipynb deleted file mode 100644 index 8ae31aeca..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.1_fastapi.ipynb +++ /dev/null @@ -1,231 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "135d65cc", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 0 - }, - "source": [ - "# Web API: 1. FastAPI\n", - "\n", - "This tutorial shows how to create an API for Chatsky using FastAPI and\n", - "introduces messenger interfaces.\n", - "\n", - "You can see the result at http://127.0.0.1:8000/docs.\n", - "\n", - "Here, [CallbackMessengerInterface](../apiref/chatsky.messengers.common.interface.rst#chatsky.messengers.common.interface.CallbackMessengerInterface)\n", - "is used to process requests.\n", - "\n", - "[Message](../apiref/chatsky.core.message.rst#chatsky.core.message.Message)\n", - "is used in creating a JSON Schema for the endpoint." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "f8b884d7", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:47.160244Z", - "iopub.status.busy": "2024-10-10T12:17:47.159724Z", - "iopub.status.idle": "2024-10-10T12:17:48.332673Z", - "shell.execute_reply": "2024-10-10T12:17:48.331876Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky uvicorn fastapi" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "1a2d8dd6", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:48.335007Z", - "iopub.status.busy": "2024-10-10T12:17:48.334790Z", - "iopub.status.idle": "2024-10-10T12:17:49.927503Z", - "shell.execute_reply": "2024-10-10T12:17:49.926783Z" - } - }, - "outputs": [], - "source": [ - "from chatsky.messengers.common.interface import CallbackMessengerInterface\n", - "from chatsky import Message, Pipeline\n", - "from chatsky.utils.testing import TOY_SCRIPT_KWARGS, is_interactive_mode\n", - "\n", - "import uvicorn\n", - "from fastapi import FastAPI" - ] - }, - { - "cell_type": "markdown", - "id": "18ad9859", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Messenger interfaces establish communication between users and the pipeline.\n", - "They manage message channel initialization and termination\n", - "as well as pipeline execution on every user request.\n", - "There are two built-in messenger interface types that can be extended\n", - "through inheritance:\n", - "\n", - "* `PollingMessengerInterface` - Starts polling for user requests\n", - " in a loop upon initialization,\n", - " it has following methods:\n", - "\n", - " * `_request()` - Method that is used to retrieve user requests\n", - " from a messenger, should return list of tuples:\n", - " (user request, unique dialog id).\n", - " * `_respond(responses)` - Method that sends responses generated by pipeline\n", - " to users through a messenger,\n", - " accepts list of dialog `Contexts`.\n", - " * `_on_exception(e)` - Method that is called when a critical exception\n", - " occurs. I.e. exception from context storage or messenger interface,\n", - " not a service exception.\n", - "\n", - " Such exceptions lead to the termination of the loop.\n", - " * `connect(pipeline_runner, loop, timeout)` -\n", - " Method that starts the polling loop.\n", - "\n", - " This method is called inside `pipeline.run` method.\n", - "\n", - " It accepts 3 arguments:\n", - "\n", - " * a callback that runs pipeline,\n", - " * a function that should return True to continue polling,\n", - " * and time to wait between loop executions.\n", - "\n", - "* `CallbackMessengerInterface` - Creates message channel\n", - " and provides a callback for pipeline execution,\n", - " it has following methods:\n", - "\n", - " * `on_request(request, ctx_id)` or `on_request_async(request, ctx_id)` -\n", - " Method that should be called each time\n", - " user provides new input to pipeline,\n", - " returns dialog Context.\n", - " * `connect(pipeline_runner)` - Method that sets `pipeline_runner` as\n", - " a function to be called inside `on_request`.\n", - "\n", - " This method is called inside `pipeline.run` method.\n", - "\n", - "You can find API reference for these classes\n", - "[here](../apiref/chatsky.messengers.common.interface.rst).\n", - "\n", - "Here the default `CallbackMessengerInterface` is used to setup\n", - "communication between the pipeline on the server side and the messenger client." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "725f9c81", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:49.931816Z", - "iopub.status.busy": "2024-10-10T12:17:49.931064Z", - "iopub.status.idle": "2024-10-10T12:17:49.936728Z", - "shell.execute_reply": "2024-10-10T12:17:49.935797Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "messenger_interface = CallbackMessengerInterface()\n", - "# CallbackMessengerInterface instantiating the dedicated messenger interface\n", - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS, messenger_interface=messenger_interface\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "692083ef", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:49.939602Z", - "iopub.status.busy": "2024-10-10T12:17:49.939307Z", - "iopub.status.idle": "2024-10-10T12:17:49.961047Z", - "shell.execute_reply": "2024-10-10T12:17:49.960106Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "app = FastAPI()\n", - "\n", - "\n", - "@app.post(\"/chat\", response_model=Message)\n", - "async def respond(\n", - " user_id: str,\n", - " user_message: Message,\n", - "):\n", - " context = await messenger_interface.on_request_async(user_message, user_id)\n", - " return context.last_response" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "b3e2c9cf", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:49.965787Z", - "iopub.status.busy": "2024-10-10T12:17:49.965391Z", - "iopub.status.idle": "2024-10-10T12:17:49.970341Z", - "shell.execute_reply": "2024-10-10T12:17:49.969306Z" - } - }, - "outputs": [], - "source": [ - "if __name__ == \"__main__\":\n", - " if is_interactive_mode(): # do not run this during doc building\n", - " pipeline.run() # runs the messenger_interface.connect method\n", - " uvicorn.run(\n", - " app,\n", - " host=\"127.0.0.1\",\n", - " port=8000,\n", - " )" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.ipynb deleted file mode 100644 index b42329324..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.ipynb +++ /dev/null @@ -1,231 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "f5592e19", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Web API: 2. WebSocket Chat\n", - "\n", - "This tutorial shows how to create a Web chat on FastAPI using websockets.\n", - "\n", - "You can see the result at http://127.0.0.1:8000/.\n", - "\n", - "This tutorial is a modified version of the FastAPI tutorial on WebSockets:\n", - "https://fastapi.tiangolo.com/advanced/websockets/.\n", - "\n", - "As mentioned in that tutorial,\n", - "\n", - "> ... for this example, we'll use a very simple HTML document\n", - "> with some JavaScript, all inside a long string.\n", - "> This, of course, is not optimal and you wouldn't use it for production.\n", - "\n", - "Here, [CallbackMessengerInterface](../apiref/chatsky.messengers.common.interface.rst#chatsky.messengers.common.interface.CallbackMessengerInterface)\n", - "is used to process requests.\n", - "\n", - "[Message](../apiref/chatsky.core.message.rst#chatsky.core.message.Message) is used to represent text messages." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "0092933a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:51.713359Z", - "iopub.status.busy": "2024-10-10T12:17:51.712815Z", - "iopub.status.idle": "2024-10-10T12:17:52.909714Z", - "shell.execute_reply": "2024-10-10T12:17:52.908889Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky uvicorn fastapi" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "3ad54569", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:52.912152Z", - "iopub.status.busy": "2024-10-10T12:17:52.911888Z", - "iopub.status.idle": "2024-10-10T12:17:54.315506Z", - "shell.execute_reply": "2024-10-10T12:17:54.314873Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky.messengers.common.interface import CallbackMessengerInterface\n", - "from chatsky import Message, Pipeline\n", - "from chatsky.utils.testing import TOY_SCRIPT_KWARGS, is_interactive_mode\n", - "\n", - "import uvicorn\n", - "from fastapi import FastAPI, WebSocket, WebSocketDisconnect\n", - "from fastapi.responses import HTMLResponse" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "ea376396", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:54.318293Z", - "iopub.status.busy": "2024-10-10T12:17:54.317742Z", - "iopub.status.idle": "2024-10-10T12:17:54.321870Z", - "shell.execute_reply": "2024-10-10T12:17:54.321231Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "messenger_interface = CallbackMessengerInterface()\n", - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS, messenger_interface=messenger_interface\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "d2aeab2f", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:54.324038Z", - "iopub.status.busy": "2024-10-10T12:17:54.323673Z", - "iopub.status.idle": "2024-10-10T12:17:54.331119Z", - "shell.execute_reply": "2024-10-10T12:17:54.330447Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "app = FastAPI()\n", - "PORT = 8000\n", - "\n", - "html = f\"\"\"\n", - "\n", - "\n", - " \n", - " Chat\n", - " \n", - " \n", - "

WebSocket Chat

\n", - "
\n", - " \n", - " \n", - "
\n", - " \n", - " \n", - " \n", - "\n", - "\"\"\"\n", - "\n", - "\n", - "@app.get(\"/\")\n", - "async def get():\n", - " return HTMLResponse(html)\n", - "\n", - "\n", - "@app.websocket(\"/ws/{client_id}\")\n", - "async def websocket_endpoint(websocket: WebSocket, client_id: int):\n", - " await websocket.accept()\n", - " try:\n", - " while True:\n", - " data = await websocket.receive_text()\n", - " await websocket.send_text(f\"User: {data}\")\n", - " request = Message(data)\n", - " context = await messenger_interface.on_request_async(\n", - " request, client_id\n", - " )\n", - " response = context.last_response.text\n", - " if response is not None:\n", - " await websocket.send_text(f\"Bot: {response}\")\n", - " else:\n", - " await websocket.send_text(\"Bot did not return text.\")\n", - " except WebSocketDisconnect: # ignore disconnections\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "8e86f124", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:54.333428Z", - "iopub.status.busy": "2024-10-10T12:17:54.332924Z", - "iopub.status.idle": "2024-10-10T12:17:54.336524Z", - "shell.execute_reply": "2024-10-10T12:17:54.335962Z" - } - }, - "outputs": [], - "source": [ - "if __name__ == \"__main__\":\n", - " if is_interactive_mode(): # do not run this during doc building\n", - " pipeline.run()\n", - " uvicorn.run(\n", - " app,\n", - " host=\"127.0.0.1\",\n", - " port=PORT,\n", - " )" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.ipynb deleted file mode 100644 index 8d3966542..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.ipynb +++ /dev/null @@ -1,285 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d3dbf035", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Web API: 3. Load testing with Locust\n", - "\n", - "This tutorial shows how to use an API endpoint\n", - "created in the FastAPI tutorial in load testing." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "1e556c82", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:55.989169Z", - "iopub.status.busy": "2024-10-10T12:17:55.988836Z", - "iopub.status.idle": "2024-10-10T12:17:57.233040Z", - "shell.execute_reply": "2024-10-10T12:17:57.232143Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky locust" - ] - }, - { - "cell_type": "markdown", - "id": "135f340f", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "## Running Locust\n", - "\n", - "1. Run this file directly:\n", - " ```bash\n", - " python {file_name}\n", - " ```\n", - "2. Run locust targeting this file:\n", - " ```bash\n", - " locust -f {file_name}\n", - " ```\n", - "3. Run from python:\n", - " ```python\n", - " import sys\n", - " from locust import main\n", - "\n", - " sys.argv = [\"locust\", \"-f\", {file_name}]\n", - " main.main()\n", - " ```\n", - "\n", - "You should see the result at http://127.0.0.1:8089.\n", - "\n", - "Make sure that your POST endpoint is also running (run the FastAPI tutorial).\n", - "\n", - "If using the FastAPI tutorial, set \"Host\" to `http://127.0.0.1:8000`,\n", - "when prompted by Locust." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b465dd56", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:57.235784Z", - "iopub.status.busy": "2024-10-10T12:17:57.235356Z", - "iopub.status.idle": "2024-10-10T12:17:57.732656Z", - "shell.execute_reply": "2024-10-10T12:17:57.724504Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "################################################################################\n", - "# this patch is only needed to run this file in IPython kernel\n", - "# and can be safely removed\n", - "import gevent.monkey\n", - "\n", - "gevent.monkey.patch_all()\n", - "################################################################################" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "c0613b4b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:57.735404Z", - "iopub.status.busy": "2024-10-10T12:17:57.734940Z", - "iopub.status.idle": "2024-10-10T12:17:59.275705Z", - "shell.execute_reply": "2024-10-10T12:17:59.234512Z" - } - }, - "outputs": [], - "source": [ - "import uuid\n", - "import time\n", - "import sys\n", - "\n", - "from locust import FastHttpUser, task, constant, main\n", - "\n", - "from chatsky import Message\n", - "from chatsky.utils.testing import HAPPY_PATH, is_interactive_mode" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "e376b436", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:59.278563Z", - "iopub.status.busy": "2024-10-10T12:17:59.277966Z", - "iopub.status.idle": "2024-10-10T12:17:59.368497Z", - "shell.execute_reply": "2024-10-10T12:17:59.337715Z" - } - }, - "outputs": [], - "source": [ - "class ChatskyUser(FastHttpUser):\n", - " wait_time = constant(1)\n", - "\n", - " def check_happy_path(self, happy_path):\n", - " \"\"\"\n", - " Check a happy path.\n", - " For each `(request, response)` pair in `happy_path`:\n", - " 1. Send request to the API endpoint and catch its response.\n", - " 2. Compare API response with the `response`.\n", - " If they do not match, fail the request.\n", - "\n", - " :param happy_path:\n", - " An iterable of tuples of\n", - " `(Message, Message | Callable(Message->str|None) | None)`.\n", - "\n", - " If the second element is `Message`,\n", - " check that API response matches it.\n", - "\n", - " If the second element is `None`,\n", - " do not check the API response.\n", - "\n", - " If the second element is a `Callable`,\n", - " call it with the API response as its argument.\n", - " If the function returns a string,\n", - " that string is considered an error message.\n", - " If the function returns `None`,\n", - " the API response is considered correct.\n", - " \"\"\"\n", - " user_id = str(uuid.uuid4())\n", - "\n", - " for request, response in happy_path:\n", - " request = Message.model_validate(request)\n", - " with self.client.post(\n", - " f\"/chat?user_id={user_id}\",\n", - " headers={\n", - " \"accept\": \"application/json\",\n", - " \"Content-Type\": \"application/json\",\n", - " },\n", - " # Name is the displayed name of the request.\n", - " name=f\"/chat?user_message={request.model_dump_json()}\",\n", - " data=request.model_dump_json(),\n", - " catch_response=True,\n", - " ) as candidate_response:\n", - " candidate_response.raise_for_status()\n", - " text_response = Message.model_validate(\n", - " candidate_response.json()\n", - " )\n", - "\n", - " if response is not None:\n", - " if callable(response):\n", - " error_message = response(text_response)\n", - " if error_message is not None:\n", - " candidate_response.failure(error_message)\n", - " elif text_response != Message.model_validate(response):\n", - " candidate_response.failure(\n", - " f\"Expected: {response.model_dump_json()}\\n\"\n", - " f\"Got: {text_response.model_dump_json()}\"\n", - " )\n", - "\n", - " time.sleep(self.wait_time())\n", - "\n", - " @task(3) # <- this task is 3 times more likely than the other\n", - " def dialog_1(self):\n", - " self.check_happy_path(HAPPY_PATH)\n", - "\n", - " @task\n", - " def dialog_2(self):\n", - " def check_first_message(msg: Message) -> str | None:\n", - " if msg.text is None:\n", - " return f\"Message does not contain text: {msg.model_dump_json()}\"\n", - " if \"Hi\" not in msg.text:\n", - " return (\n", - " f'\"Hi\" is not in the response message: '\n", - " f\"{msg.model_dump_json()}\"\n", - " )\n", - " return None\n", - "\n", - " self.check_happy_path(\n", - " [\n", - " # a function can be used to check the return message\n", - " (\"Hi\", check_first_message),\n", - " # a None is used if return message should not be checked\n", - " (\"i'm fine, how are you?\", None),\n", - " # this should fail\n", - " (\"Hi\", check_first_message),\n", - " ]\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c0268c36", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:17:59.371149Z", - "iopub.status.busy": "2024-10-10T12:17:59.370897Z", - "iopub.status.idle": "2024-10-10T12:17:59.503484Z", - "shell.execute_reply": "2024-10-10T12:17:59.436369Z" - } - }, - "outputs": [], - "source": [ - "if __name__ == \"__main__\":\n", - " if is_interactive_mode():\n", - " sys.argv = [\"locust\", \"-f\", __file__]\n", - " main.main()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.ipynb deleted file mode 100644 index 15790e1c8..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.ipynb +++ /dev/null @@ -1,630 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "6aab3eda", - "metadata": {}, - "source": [ - "# Web API: 4. Streamlit chat interface\n", - "\n", - "This tutorial shows how to use an API endpoint created in the FastAPI tutorial\n", - "in a Streamlit chat.\n", - "\n", - "A demonstration of the chat:\n", - "![demo](https://user-images.githubusercontent.com/61429541/238721597-ef88261d-e9e6-497d-ba68-0bcc9a765808.png)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b8985683", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:01.463178Z", - "iopub.status.busy": "2024-10-10T12:18:01.462954Z", - "iopub.status.idle": "2024-10-10T12:18:02.685954Z", - "shell.execute_reply": "2024-10-10T12:18:02.685109Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky streamlit streamlit-chat" - ] - }, - { - "cell_type": "markdown", - "id": "a0089551", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## Running Streamlit:\n", - "\n", - "```bash\n", - "streamlit run {file_name}\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "66cdf29e", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## Module and package import" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "5b10e665", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:02.689019Z", - "iopub.status.busy": "2024-10-10T12:18:02.688387Z", - "iopub.status.idle": "2024-10-10T12:18:02.692403Z", - "shell.execute_reply": "2024-10-10T12:18:02.691823Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "###########################################################\n", - "# This patch is only needed to import Message from chatsky.\n", - "# Streamlit Chat interface can be written without using it.\n", - "import asyncio\n", - "\n", - "loop = asyncio.new_event_loop()\n", - "asyncio.set_event_loop(loop)\n", - "###########################################################" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "c3d9fdf9", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:02.694419Z", - "iopub.status.busy": "2024-10-10T12:18:02.694208Z", - "iopub.status.idle": "2024-10-10T12:18:04.403125Z", - "shell.execute_reply": "2024-10-10T12:18:04.402319Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:03.160 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - } - ], - "source": [ - "import uuid\n", - "import itertools\n", - "\n", - "import requests\n", - "import streamlit as st\n", - "from streamlit_chat import message\n", - "import streamlit.components.v1 as components\n", - "from chatsky import Message" - ] - }, - { - "cell_type": "markdown", - "id": "7d77e3a6", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## API configuration\n", - "\n", - "Here we define methods to contact the API endpoint." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "a5fb3033", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.406750Z", - "iopub.status.busy": "2024-10-10T12:18:04.406035Z", - "iopub.status.idle": "2024-10-10T12:18:04.410861Z", - "shell.execute_reply": "2024-10-10T12:18:04.410149Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "API_URL = \"http://localhost:8000/chat\"\n", - "\n", - "\n", - "def query(payload, user_id) -> requests.Response:\n", - " response = requests.post(\n", - " API_URL + f\"?user_id={user_id}\",\n", - " headers={\n", - " \"accept\": \"application/json\",\n", - " \"Content-Type\": \"application/json\",\n", - " },\n", - " json=payload,\n", - " )\n", - " return response" - ] - }, - { - "cell_type": "markdown", - "id": "b0fdee46", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## Streamlit configuration\n", - "\n", - "Here we configure Streamlit page and initialize some session variables:\n", - "\n", - "1. `user_id` -- stores user_id to be used in pipeline.\n", - "2. `bot_responses` -- a list of bot responses.\n", - "3. `user_requests` -- a list of user requests." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "415807b5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.413440Z", - "iopub.status.busy": "2024-10-10T12:18:04.412983Z", - "iopub.status.idle": "2024-10-10T12:18:04.812542Z", - "shell.execute_reply": "2024-10-10T12:18:04.811888Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.688 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.690 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.801 \n", - " \u001b[33m\u001b[1mWarning:\u001b[0m to view this Streamlit app on a browser, run it with the following\n", - " command:\n", - "\n", - " streamlit run /home/runner/.cache/pypoetry/virtualenvs/chatsky-T4DtYLUW-py3.9/lib/python3.9/site-packages/ipykernel_launcher.py [ARGUMENTS]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.802 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.803 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.803 Session state does not function when running a script without `streamlit run`\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.804 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.804 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.805 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.807 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.807 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.808 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.809 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.809 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - } - ], - "source": [ - "st.set_page_config(page_title=\"Streamlit Chatsky Chat\", page_icon=\":robot:\")\n", - "\n", - "st.header(\"Streamlit Chatsky Chat\")\n", - "\n", - "if \"user_id\" not in st.session_state:\n", - " st.session_state[\"user_id\"] = str(uuid.uuid4())\n", - "\n", - "if \"bot_responses\" not in st.session_state:\n", - " st.session_state[\"bot_responses\"] = []\n", - "\n", - "if \"user_requests\" not in st.session_state:\n", - " st.session_state[\"user_requests\"] = []" - ] - }, - { - "cell_type": "markdown", - "id": "b43a08e1", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "## UI setup\n", - "\n", - "Here we configure elements that will be used\n", - "in Streamlit to interact with the API.\n", - "\n", - "First we define a text input field which\n", - "a user is supposed to type his requests into.\n", - "Then we define a button that sends a query\n", - "to the API, logs requests and responses,\n", - "and clears the text field." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "33a7194c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.815495Z", - "iopub.status.busy": "2024-10-10T12:18:04.814774Z", - "iopub.status.idle": "2024-10-10T12:18:04.820432Z", - "shell.execute_reply": "2024-10-10T12:18:04.819690Z" - } - }, - "outputs": [], - "source": [ - "def send_and_receive():\n", - " \"\"\"\n", - " Send text inside the input field. Receive response from API endpoint.\n", - "\n", - " Add both the request and response to `user_requests` and `bot_responses`.\n", - "\n", - " We do not call this function inside the `text_input.on_change` because then\n", - " we'd call it whenever the text field loses focus\n", - " (e.g. when a browser tab is switched).\n", - " \"\"\"\n", - " user_request = st.session_state[\"input\"]\n", - "\n", - " if user_request == \"\":\n", - " return\n", - "\n", - " st.session_state[\"user_requests\"].append(user_request)\n", - "\n", - " bot_response = query(\n", - " Message(user_request).model_dump(),\n", - " user_id=st.session_state[\"user_id\"],\n", - " )\n", - " bot_response.raise_for_status()\n", - "\n", - " bot_message = Message.model_validate(bot_response.json()).text\n", - "\n", - " # # Implementation without using Message:\n", - " # bot_response = query(\n", - " # {\"text\": user_request},\n", - " # user_id=st.session_state[\"user_id\"]\n", - " # )\n", - " # bot_response.raise_for_status()\n", - " #\n", - " # bot_message = bot_response.json()[\"response\"][\"text\"]\n", - "\n", - " st.session_state[\"bot_responses\"].append(bot_message)\n", - "\n", - " st.session_state[\"input\"] = \"\"" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "9538e903", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.822984Z", - "iopub.status.busy": "2024-10-10T12:18:04.822530Z", - "iopub.status.idle": "2024-10-10T12:18:04.836879Z", - "shell.execute_reply": "2024-10-10T12:18:04.836176Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.824 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.824 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.825 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.825 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.826 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.827 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.827 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.828 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.828 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "st.text_input(\"You: \", key=\"input\")\n", - "st.button(\"Send\", on_click=send_and_receive)" - ] - }, - { - "cell_type": "markdown", - "id": "502b04f6", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "### Component patch\n", - "\n", - "Here we add a component that presses the\n", - "`Send` button whenever user presses the `Enter` key." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "569939c4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.839588Z", - "iopub.status.busy": "2024-10-10T12:18:04.839130Z", - "iopub.status.idle": "2024-10-10T12:18:04.847003Z", - "shell.execute_reply": "2024-10-10T12:18:04.846322Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.840 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.841 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "data": { - "text/plain": [ - "DeltaGenerator()" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "components.html(\n", - " \"\"\"\n", - "\n", - "\"\"\",\n", - " height=0,\n", - " width=0,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "6bc58229", - "metadata": { - "lines_to_next_cell": 2 - }, - "source": [ - "### Message display\n", - "\n", - "Here we use the `streamlit-chat` package to\n", - "display user requests and bot responses." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "4d69c576", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:04.849619Z", - "iopub.status.busy": "2024-10-10T12:18:04.849217Z", - "iopub.status.idle": "2024-10-10T12:18:04.854897Z", - "shell.execute_reply": "2024-10-10T12:18:04.854216Z" - } - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.851 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-10-10 12:18:04.852 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" - ] - } - ], - "source": [ - "for i, bot_response, user_request in zip(\n", - " itertools.count(0),\n", - " st.session_state.get(\"bot_responses\", []),\n", - " st.session_state.get(\"user_requests\", []),\n", - "):\n", - " message(user_request, key=f\"{i}_user\", is_user=True)\n", - " message(bot_response, key=f\"{i}_bot\")" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.1_basics.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.1_basics.ipynb deleted file mode 100644 index 84d838e55..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.1_basics.ipynb +++ /dev/null @@ -1,317 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ba0b1e80", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 1. Basics\n", - "\n", - "This notebook shows a basic example of creating a simple dialog bot (agent).\n", - "\n", - "Here, basic usage of [Pipeline](../apiref/chatsky.core.pipeline.rst#chatsky.core.pipeline.Pipeline) is shown.\n", - "\n", - "Additionally, function [check_happy_path](../apiref/chatsky.utils.testing.common.rst#chatsky.utils.testing.common.check_happy_path)\n", - "that can be used for Pipeline testing is presented.\n", - "\n", - "Let's do all the necessary imports from Chatsky:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a1523dd1", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:07.108485Z", - "iopub.status.busy": "2024-10-10T12:18:07.108157Z", - "iopub.status.idle": "2024-10-10T12:18:08.264415Z", - "shell.execute_reply": "2024-10-10T12:18:08.263607Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "1cb0e5f5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:08.267075Z", - "iopub.status.busy": "2024-10-10T12:18:08.266660Z", - "iopub.status.idle": "2024-10-10T12:18:09.431644Z", - "shell.execute_reply": "2024-10-10T12:18:09.430877Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - " # all the aliases used in tutorials are available for direct import\n", - " # e.g. you can do `from chatsky import Tr` instead\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "dc3c854d", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "First of all, to create a dialog agent, we need to create a dialog script.\n", - "Below script means a dialog script.\n", - "\n", - "A script is a dictionary, where the keys are the names of the flows.\n", - "A script can contain multiple scripts, which is needed in order to divide\n", - "a dialog into sub-dialogs and process them separately.\n", - "\n", - "For example, the separation can be tied to the topic of the dialog.\n", - "In this tutorial there is one flow called `greeting_flow`.\n", - "\n", - "Flow describes a sub-dialog using linked nodes.\n", - "Each node has the keywords `RESPONSE` and `TRANSITIONS`.\n", - "\n", - "* `RESPONSE` contains the response\n", - " that the agent will return from the current node.\n", - "* `TRANSITIONS` is a list of [Transition](../apiref/chatsky.core.transition.rst#chatsky.core.transition.Transition)s\n", - " that describes possible transitions from the current node as well as their\n", - " conditions and priorities." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "7899486e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:09.434866Z", - "iopub.status.busy": "2024-10-10T12:18:09.434139Z", - "iopub.status.idle": "2024-10-10T12:18:09.441586Z", - "shell.execute_reply": "2024-10-10T12:18:09.441023Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"greeting_flow\": {\n", - " \"start_node\": { # This is the initial node,\n", - " # it doesn't contain a `RESPONSE`.\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " # This transition means that the next node would be \"node1\"\n", - " # if user's message is \"Hi\"\n", - " },\n", - " \"node1\": {\n", - " RESPONSE: \"Hi, how are you?\",\n", - " # When the bot enters node1,\n", - " # return \"Hi, how are you?\".\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node2\", cnd=cnd.ExactMatch(\"I'm fine, how are you?\"))\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node3\", cnd=cnd.ExactMatch(\"Let's talk about music.\"))\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"Sorry, I can not talk about music now.\",\n", - " TRANSITIONS: [Tr(dst=\"node4\", cnd=cnd.ExactMatch(\"Ok, goodbye.\"))],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"Bye\",\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " \"fallback_node\": {\n", - " # We get to this node if the conditions\n", - " # for switching to other nodes are not performed.\n", - " RESPONSE: \"Ooops\",\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " }\n", - "}\n", - "\n", - "\n", - "happy_path = (\n", - " (\n", - " \"Hi\",\n", - " \"Hi, how are you?\",\n", - " ), # start_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"Bye\"), # node3 -> node4\n", - " (\"Hi\", \"Hi, how are you?\"), # node4 -> node1\n", - " (\"stop\", \"Ooops\"), # node1 -> fallback_node\n", - " (\n", - " \"stop\",\n", - " \"Ooops\",\n", - " ), # fallback_node -> fallback_node\n", - " (\n", - " \"Hi\",\n", - " \"Hi, how are you?\",\n", - " ), # fallback_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"Bye\"), # node3 -> node4\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "9e89dee9", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "`Pipeline` is an object that processes user\n", - "inputs and produces responses.\n", - "\n", - "To create the pipeline you need to pass the script (`script`),\n", - "initial node (`start_label`) and\n", - "the node to which the default transition will take place\n", - "if none of the current conditions are met (`fallback_label`).\n", - "\n", - "If `fallback_label` is not set, it defaults to `start_label`.\n", - "\n", - "Roughly, the process is as follows:\n", - "\n", - "1. Pipeline receives a user request.\n", - "2. The next node is determined with the help of `TRANSITIONS`.\n", - "3. Response of the chosen node is sent to the user.\n", - "\n", - "For a more detailed description, see [here](\n", - "../apiref/chatsky.core.pipeline.rst#chatsky.core.pipeline.Pipeline._run_pipeline\n", - ")." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "a0877a20", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:09.443809Z", - "iopub.status.busy": "2024-10-10T12:18:09.443434Z", - "iopub.status.idle": "2024-10-10T12:18:09.461696Z", - "shell.execute_reply": "2024-10-10T12:18:09.461050Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='Bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='Bye'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"greeting_flow\", \"start_node\"),\n", - " fallback_label=(\"greeting_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(\n", - " pipeline,\n", - " happy_path,\n", - " printout=True,\n", - " ) # This is a function for automatic tutorial\n", - " # running (testing tutorial) with `happy_path`.\n", - "\n", - " if is_interactive_mode():\n", - " pipeline.run()\n", - " # this method runs the pipeline with the preconfigured interface\n", - " # which is CLI by default: it allows chatting with the bot\n", - " # via command line" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.2_conditions.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.2_conditions.ipynb deleted file mode 100644 index 29bbf3cd0..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.2_conditions.ipynb +++ /dev/null @@ -1,390 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "46c64065", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 2. Conditions\n", - "\n", - "This tutorial shows different options for\n", - "setting transition conditions from one node to another.\n", - "\n", - "Here, [conditions](../apiref/chatsky.conditions.standard.rst)\n", - "for script transitions are shown." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "cfe92092", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:11.215846Z", - "iopub.status.busy": "2024-10-10T12:18:11.215215Z", - "iopub.status.idle": "2024-10-10T12:18:12.512885Z", - "shell.execute_reply": "2024-10-10T12:18:12.512093Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "bfb68422", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:12.515500Z", - "iopub.status.busy": "2024-10-10T12:18:12.515060Z", - "iopub.status.idle": "2024-10-10T12:18:13.683873Z", - "shell.execute_reply": "2024-10-10T12:18:13.683238Z" - } - }, - "outputs": [], - "source": [ - "import re\n", - "\n", - "from chatsky import (\n", - " Context,\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Message,\n", - " Pipeline,\n", - " BaseCondition,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "612c480f", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "The transition condition is determined by\n", - "[BaseCondition](../apiref/chatsky.core.script_function.rst#chatsky.core.script_function.BaseCondition).\n", - "\n", - "If this function returns `True`,\n", - "then the corresponding transition is considered possible.\n", - "\n", - "Condition functions have signature\n", - "\n", - " class MyCondition(BaseCondition):\n", - " async def call(self, ctx: Context) -> bool:\n", - "\n", - "This script covers the following pre-defined conditions:\n", - "\n", - "- `ExactMatch` returns `True` if the user's request completely\n", - " matches the value passed to the function.\n", - "- `Regexp` returns `True` if the pattern matches the user's request.\n", - " `Regexp` has same signature as `re.compile` function.\n", - "- `Any` returns `True` if one element of input sequence of conditions is `True`.\n", - "- `All` returns `True` if All elements of input\n", - " sequence of conditions are `True`.\n", - "\n", - "For a full list of available conditions see\n", - "[here](../apiref/chatsky.conditions.standard.rst).\n", - "\n", - "The `cnd` field of `Transition` may also be a constant bool value." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "08528b99", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:13.686460Z", - "iopub.status.busy": "2024-10-10T12:18:13.686146Z", - "iopub.status.idle": "2024-10-10T12:18:13.690446Z", - "shell.execute_reply": "2024-10-10T12:18:13.689857Z" - } - }, - "outputs": [], - "source": [ - "class HiLowerCase(BaseCondition):\n", - " \"\"\"\n", - " Return True if `hi` in both uppercase and lowercase\n", - " letters is contained in the user request.\n", - " \"\"\"\n", - "\n", - " async def call(self, ctx: Context) -> bool:\n", - " request = ctx.last_request\n", - " return \"hi\" in request.text.lower()" - ] - }, - { - "cell_type": "markdown", - "id": "46d0c6a4", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Conditions are subclasses of `pydantic.BaseModel`.\n", - "\n", - "You can define custom fields to make them more customizable:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "98746501", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:13.692586Z", - "iopub.status.busy": "2024-10-10T12:18:13.692204Z", - "iopub.status.idle": "2024-10-10T12:18:13.696657Z", - "shell.execute_reply": "2024-10-10T12:18:13.696113Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "class ComplexUserAnswer(BaseCondition):\n", - " \"\"\"\n", - " Checks if the misc field of the last message is of a certain value.\n", - "\n", - " Messages are more complex than just strings.\n", - " The misc field can be used to store metadata about the message.\n", - " More on that in the next tutorial.\n", - " \"\"\"\n", - "\n", - " value: dict\n", - "\n", - " async def call(self, ctx: Context) -> bool:\n", - " request = ctx.last_request\n", - " return request.misc == self.value\n", - "\n", - "\n", - "customized_condition = ComplexUserAnswer(value={\"some_key\": \"some_value\"})" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "0ec39e82", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:13.698690Z", - "iopub.status.busy": "2024-10-10T12:18:13.698331Z", - "iopub.status.idle": "2024-10-10T12:18:13.706714Z", - "shell.execute_reply": "2024-10-10T12:18:13.706168Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"greeting_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " # If \"Hi\" == request of user then we make the transition\n", - " },\n", - " \"node1\": {\n", - " RESPONSE: \"Hi, how are you?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=\"node2\",\n", - " cnd=cnd.Regexp(r\".*how are you\", flags=re.IGNORECASE),\n", - " )\n", - " ],\n", - " # pattern matching\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=\"node3\",\n", - " cnd=cnd.All(\n", - " cnd.Regexp(r\"talk\"), cnd.Regexp(r\"about.*music\")\n", - " ),\n", - " )\n", - " ],\n", - " # Combine sequences of conditions with `cnd.All`\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"Sorry, I can not talk about music now.\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node4\", cnd=cnd.Regexp(re.compile(r\"Ok, goodbye.\")))\n", - " ],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"bye\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=\"node1\",\n", - " cnd=cnd.Any(\n", - " HiLowerCase(),\n", - " cnd.ExactMatch(\"hello\"),\n", - " ),\n", - " )\n", - " ],\n", - " # Combine sequences of conditions with `cnd.Any`\n", - " },\n", - " \"fallback_node\": { # We get to this node\n", - " # if no suitable transition was found\n", - " RESPONSE: \"Ooops\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node1\", cnd=customized_condition),\n", - " # use a previously instantiated condition here\n", - " Tr(dst=\"start_node\", cnd=False),\n", - " # This transition will never be made\n", - " Tr(dst=\"fallback_node\"),\n", - " # `True` is the default value of `cnd`\n", - " # this transition will always be valid\n", - " ],\n", - " },\n", - " }\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\n", - " \"Hi\",\n", - " \"Hi, how are you?\",\n", - " ), # start_node -> node1\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"bye\"), # node3 -> node4\n", - " (\"Hi\", \"Hi, how are you?\"), # node4 -> node1\n", - " (\"stop\", \"Ooops\"), # node1 -> fallback_node\n", - " (\n", - " \"one\",\n", - " \"Ooops\",\n", - " ), # fallback_node -> fallback_node\n", - " (\n", - " \"help\",\n", - " \"Ooops\",\n", - " ), # fallback_node -> fallback_node\n", - " (\n", - " \"nope\",\n", - " \"Ooops\",\n", - " ), # fallback_node -> fallback_node\n", - " (\n", - " Message(misc={\"some_key\": \"some_value\"}),\n", - " \"Hi, how are you?\",\n", - " ), # fallback_node -> node1\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"bye\"), # node3 -> node4\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "cb9fce1a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:13.708772Z", - "iopub.status.busy": "2024-10-10T12:18:13.708462Z", - "iopub.status.idle": "2024-10-10T12:18:13.732053Z", - "shell.execute_reply": "2024-10-10T12:18:13.731447Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='one'\n", - "BOT : text='Ooops'\n", - "USER: text='help'\n", - "BOT : text='Ooops'\n", - "USER: text='nope'\n", - "BOT : text='Ooops'\n", - "USER: misc='{'some_key': 'some_value'}'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"greeting_flow\", \"start_node\"),\n", - " fallback_label=(\"greeting_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.3_responses.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.3_responses.ipynb deleted file mode 100644 index 585da1f9f..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.3_responses.ipynb +++ /dev/null @@ -1,407 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cd28076f", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 3. Responses\n", - "\n", - "This tutorial shows different options for setting responses.\n", - "\n", - "Here, [responses](../apiref/chatsky.responses.standard.rst)\n", - "that allow giving custom answers to users are shown." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "e83fd66a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:15.288908Z", - "iopub.status.busy": "2024-10-10T12:18:15.288481Z", - "iopub.status.idle": "2024-10-10T12:18:16.524548Z", - "shell.execute_reply": "2024-10-10T12:18:16.523762Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "66b026a0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:16.527238Z", - "iopub.status.busy": "2024-10-10T12:18:16.526677Z", - "iopub.status.idle": "2024-10-10T12:18:17.858320Z", - "shell.execute_reply": "2024-10-10T12:18:17.857647Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import re\n", - "import random\n", - "from typing import Union\n", - "\n", - "from chatsky import (\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Context,\n", - " Message,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - " responses as rsp,\n", - " destinations as dst,\n", - " BaseResponse,\n", - " MessageInitTypes,\n", - " AnyResponse,\n", - " AbsoluteNodeLabel,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "d2170274", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Response of a node is determined by\n", - "[BaseResponse](../apiref/chatsky.core.script_function.rst#chatsky.core.script_function.BaseResponse).\n", - "\n", - "Response can be constant in which case it is an instance\n", - "of [Message](../apiref/chatsky.core.message.rst#chatsky.core.message.Message).\n", - "\n", - "`Message` has an option to be instantiated from a string\n", - "which is what we've been using so far.\n", - "Under the hood `RESPONSE: \"text\"` is converted into\n", - "`RESPONSE: Message(text=\"text\")`.\n", - "This class should be used over simple strings when\n", - "some additional information needs to be sent such as images/metadata.\n", - "\n", - "More information on that can be found in the [media tutorial](\n", - "../tutorials/tutorials.script.responses.1_media.py\n", - ").\n", - "\n", - "Instances of this class are returned by\n", - "[last_request](../apiref/chatsky.core.context.rst#chatsky.core.context.Context.last_request) and\n", - "[last_response](../apiref/chatsky.core.context.rst#chatsky.core.context.Context.last_response).\n", - "In the previous tutorial we showed how to access fields of messages\n", - "to build custom conditions.\n", - "\n", - "Node `RESPONSE` can also be set to a custom function.\n", - "This is demonstrated below:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "2ae61226", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:17.861482Z", - "iopub.status.busy": "2024-10-10T12:18:17.860714Z", - "iopub.status.idle": "2024-10-10T12:18:17.875819Z", - "shell.execute_reply": "2024-10-10T12:18:17.875205Z" - } - }, - "outputs": [], - "source": [ - "class CannotTalkAboutTopic(BaseResponse):\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " request = ctx.last_request\n", - " if request.text is None:\n", - " topic = None\n", - " else:\n", - " topic_pattern = re.compile(r\"(.*talk about )(.*)\\.\")\n", - " topic = topic_pattern.findall(request.text)\n", - " topic = topic and topic[0] and topic[0][-1]\n", - " if topic:\n", - " return f\"Sorry, I can not talk about {topic} now.\"\n", - " else:\n", - " return \"Sorry, I can not talk about that now.\"\n", - "\n", - "\n", - "class UpperCase(BaseResponse):\n", - " response: AnyResponse # either const response or another BaseResponse\n", - "\n", - " def __init__(self, response: Union[MessageInitTypes, BaseResponse]):\n", - " # defining this allows passing response as a positional argument\n", - " # and allows to make a more detailed type annotation:\n", - " # AnyResponse cannot be a string but can be initialized from it,\n", - " # so MessageInitTypes annotates that we can init from a string\n", - " super().__init__(response=response)\n", - "\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " response = await self.response(ctx)\n", - " # const response is converted to BaseResponse,\n", - " # so we call it regardless of the response type\n", - "\n", - " if response.text is not None:\n", - " response.text = response.text.upper()\n", - " return response\n", - "\n", - "\n", - "class FallbackTrace(BaseResponse):\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " return Message(\n", - " misc={\n", - " \"previous_node\": await dst.Previous()(ctx),\n", - " \"last_request\": ctx.last_request,\n", - " }\n", - " )" - ] - }, - { - "cell_type": "markdown", - "id": "5301b2a0", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "Chatsky provides one basic response as part of\n", - "the [standard](../apiref/chatsky.responses.standard.rst) module:\n", - "\n", - "- `RandomChoice` randomly chooses a message out of the ones passed to it." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "cc701a71", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:17.878242Z", - "iopub.status.busy": "2024-10-10T12:18:17.877753Z", - "iopub.status.idle": "2024-10-10T12:18:17.887024Z", - "shell.execute_reply": "2024-10-10T12:18:17.886359Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"greeting_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " \"node1\": {\n", - " RESPONSE: rsp.RandomChoice(\n", - " \"Hi, what is up?\",\n", - " \"Hello, how are you?\",\n", - " ),\n", - " # Random choice from candidate list.\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node2\", cnd=cnd.ExactMatch(\"I'm fine, how are you?\"))\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node3\", cnd=cnd.ExactMatch(\"Let's talk about music.\"))\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: CannotTalkAboutTopic(),\n", - " TRANSITIONS: [Tr(dst=\"node4\", cnd=cnd.ExactMatch(\"Ok, goodbye.\"))],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: UpperCase(\"bye\"),\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: FallbackTrace(),\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " }\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\n", - " \"Hi\",\n", - " \"Hello, how are you?\",\n", - " ), # start_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"BYE\"), # node3 -> node4\n", - " (\"Hi\", \"Hello, how are you?\"), # node4 -> node1\n", - " (\n", - " \"stop\",\n", - " Message(\n", - " misc={\n", - " \"previous_node\": AbsoluteNodeLabel(\n", - " flow_name=\"greeting_flow\", node_name=\"node1\"\n", - " ),\n", - " \"last_request\": Message(\"stop\"),\n", - " }\n", - " ),\n", - " ),\n", - " # node1 -> fallback_node\n", - " (\n", - " \"one\",\n", - " Message(\n", - " misc={\n", - " \"previous_node\": AbsoluteNodeLabel(\n", - " flow_name=\"greeting_flow\", node_name=\"fallback_node\"\n", - " ),\n", - " \"last_request\": Message(\"one\"),\n", - " }\n", - " ),\n", - " ), # f_n->f_n\n", - " (\n", - " \"help\",\n", - " Message(\n", - " misc={\n", - " \"previous_node\": AbsoluteNodeLabel(\n", - " flow_name=\"greeting_flow\", node_name=\"fallback_node\"\n", - " ),\n", - " \"last_request\": Message(\"help\"),\n", - " }\n", - " ),\n", - " ), # f_n->f_n\n", - " (\n", - " \"nope\",\n", - " Message(\n", - " misc={\n", - " \"previous_node\": AbsoluteNodeLabel(\n", - " flow_name=\"greeting_flow\", node_name=\"fallback_node\"\n", - " ),\n", - " \"last_request\": Message(\"nope\"),\n", - " }\n", - " ),\n", - " ), # f_n->f_n\n", - " (\n", - " \"Hi\",\n", - " \"Hi, what is up?\",\n", - " ), # fallback_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about music now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"BYE\"), # node3 -> node4\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "1df7093c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:17.889233Z", - "iopub.status.busy": "2024-10-10T12:18:17.888836Z", - "iopub.status.idle": "2024-10-10T12:18:17.910960Z", - "shell.execute_reply": "2024-10-10T12:18:17.910295Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hello, how are you?'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='BYE'\n", - "USER: text='Hi'\n", - "BOT : text='Hello, how are you?'\n", - "USER: text='stop'\n", - "BOT : misc='{'previous_node': {'flow_name': 'greeting_flow', 'node_name': 'node1'}, 'last_request': {'text': 'stop'}}'\n", - "USER: text='one'\n", - "BOT : misc='{'previous_node': {'flow_name': 'greeting_flow', 'node_name': 'fallback_node'}, 'last_request': {'text': 'one'}}'\n", - "USER: text='help'\n", - "BOT : misc='{'previous_node': {'flow_name': 'greeting_flow', 'node_name': 'fallback_node'}, 'last_request': {'text': 'help'}}'\n", - "USER: text='nope'\n", - "BOT : misc='{'previous_node': {'flow_name': 'greeting_flow', 'node_name': 'fallback_node'}, 'last_request': {'text': 'nope'}}'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, what is up?'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='BYE'\n" - ] - } - ], - "source": [ - "random.seed(31415) # predestination of choice\n", - "\n", - "\n", - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"greeting_flow\", \"start_node\"),\n", - " fallback_label=(\"greeting_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.4_transitions.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.4_transitions.ipynb deleted file mode 100644 index 0ae12528d..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.4_transitions.ipynb +++ /dev/null @@ -1,517 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "56ab8b91", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "# Core: 4. Transitions\n", - "\n", - "This tutorial shows settings for transitions between flows and nodes.\n", - "\n", - "Here, [conditions](../apiref/chatsky.conditions.standard.rst)\n", - "for transition between many different script steps are shown.\n", - "\n", - "Some of the destination steps can be set using\n", - "[destinations](../apiref/chatsky.destinations.standard.rst)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "54ff229a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:19.772176Z", - "iopub.status.busy": "2024-10-10T12:18:19.771893Z", - "iopub.status.idle": "2024-10-10T12:18:20.994161Z", - "shell.execute_reply": "2024-10-10T12:18:20.993343Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "d33b1ba0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:20.996730Z", - "iopub.status.busy": "2024-10-10T12:18:20.996344Z", - "iopub.status.idle": "2024-10-10T12:18:22.220654Z", - "shell.execute_reply": "2024-10-10T12:18:22.219862Z" - } - }, - "outputs": [], - "source": [ - "import re\n", - "\n", - "from chatsky import (\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Context,\n", - " NodeLabelInitTypes,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " BaseDestination,\n", - " conditions as cnd,\n", - " destinations as dst,\n", - ")\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "ddb275aa", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "The `TRANSITIONS` keyword is used to determine a list of transitions from\n", - "the current node. After receiving user request, Pipeline will choose the\n", - "next node relying on that list.\n", - "If no transition in the list is suitable, transition will be made\n", - "to the fallback node.\n", - "\n", - "Each transition is represented by the [Transition](../apiref/chatsky.core.transition.rst#chatsky.core.transition.Transition)\n", - "class.\n", - "\n", - "It has three main fields:\n", - "\n", - "- dst: Destination determines the node to which the transition is made.\n", - "- cnd: Condition determines if the transition is allowed.\n", - "- priority: Allows choosing one of the transitions if several are allowed.\n", - " Higher priority transitions will be chosen over the rest.\n", - " If priority is not set,\n", - " [default_priority](../apiref/chatsky.core.pipeline.rst#chatsky.core.pipeline.Pipeline.default_priority)\n", - " is used instead.\n", - " Default priority is 1 by default (but may be set via Pipeline).\n", - "\n", - "For more details on how the next node is chosen see\n", - "[here](../apiref/chatsky.core.transition.rst#chatsky.core.transition.get_next_label).\n", - "\n", - "Like conditions, all of these fields can be either constant values or\n", - "custom functions ([BaseDestination](../apiref/chatsky.core.script_function.rst#chatsky.core.script_function.BaseDestination),\n", - "[BaseCondition](../apiref/chatsky.core.script_function.rst#chatsky.core.script_function.BaseCondition),\n", - "[BasePriority](../apiref/chatsky.core.script_function.rst#chatsky.core.script_function.BasePriority))." - ] - }, - { - "cell_type": "markdown", - "id": "07312899", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "## Destinations\n", - "\n", - "Destination node is specified with a [NodeLabel](../apiref/chatsky.core.node_label.rst#chatsky.core.node_label.NodeLabel)\n", - "class.\n", - "\n", - "It contains two field:\n", - "\n", - "- \"flow_name\": Name of the flow the node belongs to.\n", - " Optional; if not set, will use the flow of the current node.\n", - "- \"node_name\": Name of the node inside the flow.\n", - "\n", - "Instances of this class can be initialized from a tuple of two strings\n", - "(flow name and node name) or a single string (node name; relative flow name).\n", - "This happens automatically for return values of `BaseDestination`\n", - "and for the `dst` field of `Transition`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "ab6956c0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:22.223415Z", - "iopub.status.busy": "2024-10-10T12:18:22.223050Z", - "iopub.status.idle": "2024-10-10T12:18:22.227442Z", - "shell.execute_reply": "2024-10-10T12:18:22.226841Z" - } - }, - "outputs": [], - "source": [ - "class GreetingFlowNode2(BaseDestination):\n", - " async def call(self, ctx: Context) -> NodeLabelInitTypes:\n", - " return \"greeting_flow\", \"node2\"" - ] - }, - { - "cell_type": "markdown", - "id": "dc21777e", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Chatsky provides several basic transitions as part of\n", - "the [standard](../apiref/chatsky.destinations.standard.rst) module:\n", - "\n", - "- `FromHistory` returns a node from label history.\n", - " `Current` and `Previous` are subclasses of it that return specific nodes\n", - " (current node and previous node respectively).\n", - "- `Start` returns the start node.\n", - "- `Fallback` returns the fallback node.\n", - "- `Forward` returns the next node (in order of definition)\n", - " in the current flow relative to the current node.\n", - "- `Backward` returns the previous node (in order of definition)\n", - " in the current flow relative to the current node." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4a7b2a04", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:22.229777Z", - "iopub.status.busy": "2024-10-10T12:18:22.229394Z", - "iopub.status.idle": "2024-10-10T12:18:22.246399Z", - "shell.execute_reply": "2024-10-10T12:18:22.245804Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"global_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"music_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"talk about music\"),\n", - " # this condition is checked first.\n", - " # if it fails, pipeline will try the next transition\n", - " ),\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"hi|hello\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=\"fallback_node\",\n", - " # a single string references a node in the same flow\n", - " ),\n", - " # this transition will only be made if previous ones fail\n", - " ]\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: \"Ooops\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"music_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"talk about music\"),\n", - " ),\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"hi|hello\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=dst.Previous(),\n", - " cnd=cnd.Regexp(r\"previous\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=dst.Current(), # this goes to the current node\n", - " # i.e. fallback node\n", - " ),\n", - " ],\n", - " },\n", - " },\n", - " \"greeting_flow\": {\n", - " \"node1\": {\n", - " RESPONSE: \"Hi, how are you?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"global_flow\", \"fallback_node\"),\n", - " priority=0.1,\n", - " ), # due to low priority (default priority is 1)\n", - " # this transition will be made if the next one fails\n", - " Tr(dst=\"node2\", cnd=cnd.Regexp(r\"how are you\")),\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Fallback(),\n", - " priority=0.1,\n", - " ),\n", - " # there is no need to specify such transition:\n", - " # For any node if all transitions fail,\n", - " # fallback node becomes the next node.\n", - " # Here, this transition exists for demonstration purposes.\n", - " Tr(\n", - " dst=dst.Forward(), # i.e. \"node3\" of this flow\n", - " cnd=cnd.Regexp(r\"talk about\"),\n", - " priority=0.5,\n", - " ), # this transition is the third candidate\n", - " Tr(\n", - " dst=(\"music_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"talk about music\"),\n", - " ), # this transition is the first candidate\n", - " Tr(\n", - " dst=dst.Previous(),\n", - " cnd=cnd.Regexp(r\"previous\", flags=re.IGNORECASE),\n", - " ), # this transition is the second candidate\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"Sorry, I can not talk about that now.\",\n", - " TRANSITIONS: [Tr(dst=dst.Forward(), cnd=cnd.Regexp(r\"bye\"))],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"Bye\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=\"node1\",\n", - " cnd=cnd.Regexp(r\"hi|hello\", flags=re.IGNORECASE),\n", - " )\n", - " ],\n", - " },\n", - " },\n", - " \"music_flow\": {\n", - " \"node1\": {\n", - " RESPONSE: \"I love `System of a Down` group, \"\n", - " \"would you like to talk about it?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.Regexp(r\"yes|yep|ok\", flags=re.IGNORECASE),\n", - " )\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"System of a Down is an Armenian-American \"\n", - " \"heavy metal band formed in 1994.\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.Regexp(r\"next\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=dst.Current(),\n", - " cnd=cnd.Regexp(r\"repeat\", flags=re.IGNORECASE),\n", - " ),\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.Regexp(r\"next\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=dst.Backward(),\n", - " cnd=cnd.Regexp(r\"back\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=dst.Current(),\n", - " cnd=cnd.Regexp(r\"repeat\", flags=re.IGNORECASE),\n", - " ),\n", - " ],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"That's all I know.\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=GreetingFlowNode2(),\n", - " cnd=cnd.Regexp(r\"next\", flags=re.IGNORECASE),\n", - " ),\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node4\"),\n", - " cnd=cnd.Regexp(r\"next time\", flags=re.IGNORECASE),\n", - " priority=2,\n", - " ), # \"next\" is contained in \"next_time\" so we need higher\n", - " # priority here.\n", - " # Otherwise, this transition will never be made\n", - " ],\n", - " },\n", - " },\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\"hi\", \"Hi, how are you?\"),\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\n", - " \"talk about music.\",\n", - " \"I love `System of a Down` group, \" \"would you like to talk about it?\",\n", - " ),\n", - " (\n", - " \"yes\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"next\",\n", - " \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " ),\n", - " (\n", - " \"back\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"repeat\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"next\",\n", - " \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " ),\n", - " (\"next\", \"That's all I know.\"),\n", - " (\n", - " \"next\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\"previous\", \"That's all I know.\"),\n", - " (\"next time\", \"Bye\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"previous\", \"Bye\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"nope\", \"Ooops\"),\n", - " (\"hi\", \"Hi, how are you?\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"previous\", \"Hi, how are you?\"),\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\n", - " \"let's talk about something.\",\n", - " \"Sorry, I can not talk about that now.\",\n", - " ),\n", - " (\"Ok, goodbye.\", \"Bye\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "49734729", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:22.248693Z", - "iopub.status.busy": "2024-10-10T12:18:22.248345Z", - "iopub.status.idle": "2024-10-10T12:18:22.283581Z", - "shell.execute_reply": "2024-10-10T12:18:22.282807Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='talk about music.'\n", - "BOT : text='I love `System of a Down` group, would you like to talk about it?'\n", - "USER: text='yes'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='next'\n", - "BOT : text='The band achieved commercial success with the release of five studio albums.'\n", - "USER: text='back'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='repeat'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='next'\n", - "BOT : text='The band achieved commercial success with the release of five studio albums.'\n", - "USER: text='next'\n", - "BOT : text='That's all I know.'\n", - "USER: text='next'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='previous'\n", - "BOT : text='That's all I know.'\n", - "USER: text='next time'\n", - "BOT : text='Bye'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='previous'\n", - "BOT : text='Bye'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='nope'\n", - "BOT : text='Ooops'\n", - "USER: text='hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='previous'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='let's talk about something.'\n", - "BOT : text='Sorry, I can not talk about that now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='Bye'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"global_flow\", \"start_node\"),\n", - " fallback_label=(\"global_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.5_global_local.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.5_global_local.ipynb deleted file mode 100644 index d588152e2..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.5_global_local.ipynb +++ /dev/null @@ -1,411 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "937eeb83", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "# Core: 5. Global and Local nodes" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7c448f0d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:24.172122Z", - "iopub.status.busy": "2024-10-10T12:18:24.171345Z", - "iopub.status.idle": "2024-10-10T12:18:25.385803Z", - "shell.execute_reply": "2024-10-10T12:18:25.384978Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "b001a822", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:25.388638Z", - "iopub.status.busy": "2024-10-10T12:18:25.388018Z", - "iopub.status.idle": "2024-10-10T12:18:26.634163Z", - "shell.execute_reply": "2024-10-10T12:18:26.633413Z" - } - }, - "outputs": [], - "source": [ - "import re\n", - "\n", - "from chatsky import (\n", - " GLOBAL,\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - " destinations as dst,\n", - ")\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "fda08ef6", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Keywords `GLOBAL` and `LOCAL` are used to define global and local nodes\n", - "respectively. Global node is defined at the script level (along with flows)\n", - "and local node is defined at the flow level (along with nodes inside a flow).\n", - "\n", - "Every local node inherits properties from the global node.\n", - "Every node inherits properties from the local node (of its flow).\n", - "\n", - "For example, if we are to set list `A` as transitions for the\n", - "local node of a flow, then every node of that flow would effectively\n", - "have the `A` list extended with its own transitions.\n", - "\n", - "
\n", - "\n", - "To sum up transition priorities:\n", - "\n", - "Transition A is of higher priority compared to Transition B:\n", - "\n", - "1. If A.priority > B.priority; OR\n", - "2. If A is a node transition and B is a local or global transition;\n", - " or A is a local transition and B is a global transition; OR\n", - "3. If A is defined in the transition list earlier than B.\n", - "\n", - "
\n", - "\n", - "For more information on node inheritance, see [here](\n", - "../apiref/chatsky.core.script.rst#chatsky.core.script.Script.get_inherited_node\n", - ").\n", - "\n", - "
\n", - "\n", - "Note\n", - "\n", - "Property [current_node](../apiref/chatsky.core.context.rst#chatsky.core.context.Context.current_node) does not return\n", - "the current node as is. Instead it returns a node that is modified\n", - "by the global and local nodes.\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "507f17cf", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:26.637098Z", - "iopub.status.busy": "2024-10-10T12:18:26.636722Z", - "iopub.status.idle": "2024-10-10T12:18:26.653031Z", - "shell.execute_reply": "2024-10-10T12:18:26.652279Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " GLOBAL: {\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"\\b(hi|hello)\\b\", flags=re.I),\n", - " priority=1.1,\n", - " ),\n", - " Tr(\n", - " dst=(\"music_flow\", \"node1\"),\n", - " cnd=cnd.Regexp(r\"talk about music\"),\n", - " priority=1.1,\n", - " ),\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.All(\n", - " cnd.Regexp(r\"next\\b\"),\n", - " cnd.CheckLastLabels(\n", - " labels=[(\"music_flow\", i) for i in [\"node2\", \"node3\"]]\n", - " ), # this checks if the current node is\n", - " # music_flow.node2 or music_flow.node3\n", - " ),\n", - " ),\n", - " Tr(\n", - " dst=dst.Current(),\n", - " cnd=cnd.All(\n", - " cnd.Regexp(r\"repeat\", flags=re.I),\n", - " cnd.Negation(\n", - " cnd.CheckLastLabels(flow_labels=[\"global_flow\"])\n", - " ),\n", - " ),\n", - " priority=0.2,\n", - " ),\n", - " ],\n", - " },\n", - " \"global_flow\": {\n", - " \"start_node\": {},\n", - " \"fallback_node\": {\n", - " RESPONSE: \"Ooops\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Previous(),\n", - " cnd=cnd.Regexp(r\"previous\", flags=re.I),\n", - " )\n", - " ],\n", - " },\n", - " },\n", - " \"greeting_flow\": {\n", - " \"node1\": {\n", - " RESPONSE: \"Hi, how are you?\",\n", - " TRANSITIONS: [Tr(dst=\"node2\", cnd=cnd.Regexp(r\"how are you\"))],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.Regexp(r\"talk about\"),\n", - " priority=0.5,\n", - " ),\n", - " Tr(\n", - " dst=dst.Previous(),\n", - " cnd=cnd.Regexp(r\"previous\", flags=re.I),\n", - " ),\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"Sorry, I can not talk about that now.\",\n", - " TRANSITIONS: [Tr(dst=dst.Forward(), cnd=cnd.Regexp(r\"bye\"))],\n", - " },\n", - " \"node4\": {RESPONSE: \"bye\"},\n", - " # This node does not define its own transitions.\n", - " # It will use global transitions only.\n", - " },\n", - " \"music_flow\": {\n", - " \"node1\": {\n", - " RESPONSE: \"I love `System of a Down` group, \"\n", - " \"would you like to talk about it?\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Forward(),\n", - " cnd=cnd.Regexp(r\"yes|yep|ok\", flags=re.IGNORECASE),\n", - " )\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"System of a Down is an Armenian-American \"\n", - " \"heavy metal band formed in 1994.\",\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=dst.Backward(),\n", - " cnd=cnd.Regexp(r\"back\", flags=re.IGNORECASE),\n", - " ),\n", - " ],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"That's all I know.\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node4\"),\n", - " cnd=cnd.Regexp(r\"next time\", flags=re.I),\n", - " ),\n", - " Tr(\n", - " dst=(\"greeting_flow\", \"node2\"),\n", - " cnd=cnd.Regexp(r\"next\", flags=re.I),\n", - " ),\n", - " ],\n", - " },\n", - " },\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\"hi\", \"Hi, how are you?\"),\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\n", - " \"talk about music.\",\n", - " \"I love `System of a Down` group, \" \"would you like to talk about it?\",\n", - " ),\n", - " (\n", - " \"yes\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"next\",\n", - " \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " ),\n", - " (\n", - " \"back\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"repeat\",\n", - " \"System of a Down is \"\n", - " \"an Armenian-American heavy metal band formed in 1994.\",\n", - " ),\n", - " (\n", - " \"next\",\n", - " \"The band achieved commercial success \"\n", - " \"with the release of five studio albums.\",\n", - " ),\n", - " (\"next\", \"That's all I know.\"),\n", - " (\n", - " \"next\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\"previous\", \"That's all I know.\"),\n", - " (\"next time\", \"bye\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"previous\", \"bye\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"nope\", \"Ooops\"),\n", - " (\"hi\", \"Hi, how are you?\"),\n", - " (\"stop\", \"Ooops\"),\n", - " (\"previous\", \"Hi, how are you?\"),\n", - " (\n", - " \"i'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ),\n", - " (\n", - " \"let's talk about something.\",\n", - " \"Sorry, I can not talk about that now.\",\n", - " ),\n", - " (\"Ok, goodbye.\", \"bye\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "a6ce2b0e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:26.655431Z", - "iopub.status.busy": "2024-10-10T12:18:26.655026Z", - "iopub.status.idle": "2024-10-10T12:18:26.692626Z", - "shell.execute_reply": "2024-10-10T12:18:26.691898Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='talk about music.'\n", - "BOT : text='I love `System of a Down` group, would you like to talk about it?'\n", - "USER: text='yes'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='next'\n", - "BOT : text='The band achieved commercial success with the release of five studio albums.'\n", - "USER: text='back'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='repeat'\n", - "BOT : text='System of a Down is an Armenian-American heavy metal band formed in 1994.'\n", - "USER: text='next'\n", - "BOT : text='The band achieved commercial success with the release of five studio albums.'\n", - "USER: text='next'\n", - "BOT : text='That's all I know.'\n", - "USER: text='next'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='previous'\n", - "BOT : text='That's all I know.'\n", - "USER: text='next time'\n", - "BOT : text='bye'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='previous'\n", - "BOT : text='bye'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='nope'\n", - "BOT : text='Ooops'\n", - "USER: text='hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='previous'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='let's talk about something.'\n", - "BOT : text='Sorry, I can not talk about that now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"global_flow\", \"start_node\"),\n", - " fallback_label=(\"global_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.6_context_serialization.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.6_context_serialization.ipynb deleted file mode 100644 index 76040b37b..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.6_context_serialization.ipynb +++ /dev/null @@ -1,230 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "48aa246a", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 6. Context serialization" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "757ea925", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:28.382440Z", - "iopub.status.busy": "2024-10-10T12:18:28.382220Z", - "iopub.status.idle": "2024-10-10T12:18:29.545998Z", - "shell.execute_reply": "2024-10-10T12:18:29.545134Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "46e41c61", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:29.548425Z", - "iopub.status.busy": "2024-10-10T12:18:29.548205Z", - "iopub.status.idle": "2024-10-10T12:18:30.721759Z", - "shell.execute_reply": "2024-10-10T12:18:30.721153Z" - } - }, - "outputs": [], - "source": [ - "import logging\n", - "\n", - "from chatsky import (\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Context,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " BaseResponse,\n", - " MessageInitTypes,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "f0e3dcc5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:30.724444Z", - "iopub.status.busy": "2024-10-10T12:18:30.723981Z", - "iopub.status.idle": "2024-10-10T12:18:30.728152Z", - "shell.execute_reply": "2024-10-10T12:18:30.727555Z" - } - }, - "outputs": [], - "source": [ - "class RequestCounter(BaseResponse):\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " return f\"answer {len(ctx.requests)}\"" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "62330733", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:30.730216Z", - "iopub.status.busy": "2024-10-10T12:18:30.729824Z", - "iopub.status.idle": "2024-10-10T12:18:30.733562Z", - "shell.execute_reply": "2024-10-10T12:18:30.733011Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"flow_start\": {\n", - " \"node_start\": {\n", - " RESPONSE: RequestCounter(),\n", - " TRANSITIONS: [Tr(dst=(\"flow_start\", \"node_start\"))],\n", - " }\n", - " }\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\"hi\", \"answer 1\"),\n", - " (\"how are you?\", \"answer 2\"),\n", - " (\"ok\", \"answer 3\"),\n", - " (\"good\", \"answer 4\"),\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "658198d8", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Draft function that performs serialization." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c7d6e47c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:30.735753Z", - "iopub.status.busy": "2024-10-10T12:18:30.735340Z", - "iopub.status.idle": "2024-10-10T12:18:30.739605Z", - "shell.execute_reply": "2024-10-10T12:18:30.739019Z" - } - }, - "outputs": [], - "source": [ - "def process_response(ctx: Context):\n", - " ctx_json = ctx.model_dump_json()\n", - " if isinstance(ctx_json, str):\n", - " logging.info(\"context serialized to json str\")\n", - " else:\n", - " raise Exception(f\"ctx={ctx_json} has to be serialized to json string\")\n", - "\n", - " ctx_dict = ctx.model_dump()\n", - " if isinstance(ctx_dict, dict):\n", - " logging.info(\"context serialized to dict\")\n", - " else:\n", - " raise Exception(f\"ctx={ctx_dict} has to be serialized to dict\")\n", - "\n", - " if not isinstance(ctx, Context):\n", - " raise Exception(f\"ctx={ctx} has to have Context type\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "d8b6985a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:30.741909Z", - "iopub.status.busy": "2024-10-10T12:18:30.741519Z", - "iopub.status.idle": "2024-10-10T12:18:30.753652Z", - "shell.execute_reply": "2024-10-10T12:18:30.753049Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='hi'\n", - "BOT : text='answer 1'\n", - "USER: text='how are you?'\n", - "BOT : text='answer 2'\n", - "USER: text='ok'\n", - "BOT : text='answer 3'\n", - "USER: text='good'\n", - "BOT : text='answer 4'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"flow_start\", \"node_start\"),\n", - " post_services=[process_response],\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.7_pre_response_processing.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.7_pre_response_processing.ipynb deleted file mode 100644 index 5665f8268..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.7_pre_response_processing.ipynb +++ /dev/null @@ -1,356 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "57ef2918", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 7. Pre-response processing\n", - "\n", - "Here, [PRE_RESPONSE](../apiref/chatsky.core.script.rst#chatsky.core.script.PRE_RESPONSE)\n", - "is demonstrated which can be used for\n", - "additional context processing before response handlers." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "633336a7", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:32.436714Z", - "iopub.status.busy": "2024-10-10T12:18:32.436496Z", - "iopub.status.idle": "2024-10-10T12:18:33.693720Z", - "shell.execute_reply": "2024-10-10T12:18:33.692887Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "34b50f81", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:33.696552Z", - "iopub.status.busy": "2024-10-10T12:18:33.695945Z", - "iopub.status.idle": "2024-10-10T12:18:34.903703Z", - "shell.execute_reply": "2024-10-10T12:18:34.903038Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " GLOBAL,\n", - " LOCAL,\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " PRE_RESPONSE,\n", - " Context,\n", - " Message,\n", - " MessageInitTypes,\n", - " BaseResponse,\n", - " Transition as Tr,\n", - " Pipeline,\n", - " destinations as dst,\n", - " processing as proc,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "16444815", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "Processing functions have the same signature as\n", - "conditions, responses or destinations\n", - "except they don't return anything:\n", - "\n", - ".. python:\n", - "\n", - " class MyProcessing(BaseProcessing):\n", - " async def call(self, ctx: Context) -> None:\n", - " ...\n", - "\n", - "\n", - "The main way for processing functions to interact with the script\n", - "is modifying `ctx.current_node`, which is used by pipeline\n", - "to store a copy of the current node in script.\n", - "Any of its attributes can be safely edited, and these changes will\n", - "only have an effect during the current turn of the current context." - ] - }, - { - "cell_type": "markdown", - "id": "cf56b4aa", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "In this tutorial we'll subclass\n", - "[ModifyResponse](../apiref/chatsky.processing.standard.rst#chatsky.processing.standard.ModifyResponse)\n", - "processing function so that it would modify response\n", - "of the current node to include a prefix." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "4c1bc2b3", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:34.906603Z", - "iopub.status.busy": "2024-10-10T12:18:34.906049Z", - "iopub.status.idle": "2024-10-10T12:18:34.911373Z", - "shell.execute_reply": "2024-10-10T12:18:34.910742Z" - } - }, - "outputs": [], - "source": [ - "class AddPrefix(proc.ModifyResponse):\n", - " prefix: str\n", - "\n", - " def __init__(self, prefix: str):\n", - " # basemodel does not allow positional arguments by default\n", - " super().__init__(prefix=prefix)\n", - "\n", - " async def modified_response(\n", - " self, original_response: BaseResponse, ctx: Context\n", - " ) -> MessageInitTypes:\n", - " result = await original_response(ctx)\n", - "\n", - " if result.text is not None:\n", - " result.text = f\"{self.prefix}: {result.text}\"\n", - " return result" - ] - }, - { - "cell_type": "markdown", - "id": "4a95cad1", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "
\n", - "\n", - "Tip\n", - "\n", - "You can use `ModifyResponse` to catch exceptions in response functions:\n", - "\n", - ".. python:\n", - "\n", - " class ExceptionHandler(proc.ModifyResponse):\n", - " async def modified_response(self, original_response, ctx):\n", - " try:\n", - " return await original_response(ctx)\n", - " except Exception as exc:\n", - " return str(exc)\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b90940ac", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:34.913812Z", - "iopub.status.busy": "2024-10-10T12:18:34.913339Z", - "iopub.status.idle": "2024-10-10T12:18:34.919213Z", - "shell.execute_reply": "2024-10-10T12:18:34.918636Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"root\": {\n", - " \"start\": {\n", - " TRANSITIONS: [Tr(dst=(\"flow\", \"step_0\"))],\n", - " },\n", - " \"fallback\": {RESPONSE: \"the end\"},\n", - " },\n", - " GLOBAL: {\n", - " PRE_RESPONSE: {\n", - " \"proc_name_1\": AddPrefix(\"l1_global\"),\n", - " \"proc_name_2\": AddPrefix(\"l2_global\"),\n", - " }\n", - " },\n", - " \"flow\": {\n", - " LOCAL: {\n", - " PRE_RESPONSE: {\n", - " \"proc_name_2\": AddPrefix(\"l2_local\"),\n", - " \"proc_name_3\": AddPrefix(\"l3_local\"),\n", - " },\n", - " TRANSITIONS: [Tr(dst=dst.Forward(loop=True))],\n", - " },\n", - " \"step_0\": {\n", - " RESPONSE: \"first\",\n", - " },\n", - " \"step_1\": {\n", - " PRE_RESPONSE: {\"proc_name_1\": AddPrefix(\"l1_step_1\")},\n", - " RESPONSE: \"second\",\n", - " },\n", - " \"step_2\": {\n", - " PRE_RESPONSE: {\"proc_name_2\": AddPrefix(\"l2_step_2\")},\n", - " RESPONSE: \"third\",\n", - " },\n", - " \"step_3\": {\n", - " PRE_RESPONSE: {\"proc_name_3\": AddPrefix(\"l3_step_3\")},\n", - " RESPONSE: \"fourth\",\n", - " },\n", - " \"step_4\": {\n", - " PRE_RESPONSE: {\"proc_name_4\": AddPrefix(\"l4_step_4\")},\n", - " RESPONSE: \"fifth\",\n", - " },\n", - " },\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "368d7557", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "The order of execution for processing functions is as follows:\n", - "\n", - "1. All node-specific functions are executed in the order of definition;\n", - "2. All local functions are executed in the order of definition except those with\n", - " keys matching to previously executed functions;\n", - "3. All global functions are executed in the order of definition\n", - " except those with keys matching to previously executed functions.\n", - "\n", - "That means that if both global and local nodes\n", - "define a processing function with key \"processing_name\",\n", - "only the one inside the local node will be executed.\n", - "\n", - "This demonstrated in the happy path below\n", - "(the first prefix in the text is the last one to execute):" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "ae2ffd70", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:34.921380Z", - "iopub.status.busy": "2024-10-10T12:18:34.920972Z", - "iopub.status.idle": "2024-10-10T12:18:34.924681Z", - "shell.execute_reply": "2024-10-10T12:18:34.924097Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# testing\n", - "happy_path = (\n", - " (Message(), \"l1_global: l3_local: l2_local: first\"),\n", - " (Message(), \"l3_local: l2_local: l1_step_1: second\"),\n", - " (Message(), \"l1_global: l3_local: l2_step_2: third\"),\n", - " (Message(), \"l1_global: l2_local: l3_step_3: fourth\"),\n", - " (Message(), \"l1_global: l3_local: l2_local: l4_step_4: fifth\"),\n", - " (Message(), \"l1_global: l3_local: l2_local: first\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "71b6b9dd", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:34.926602Z", - "iopub.status.busy": "2024-10-10T12:18:34.926406Z", - "iopub.status.idle": "2024-10-10T12:18:34.951448Z", - "shell.execute_reply": "2024-10-10T12:18:34.950749Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: \n", - "BOT : text='l1_global: l3_local: l2_local: first'\n", - "USER: \n", - "BOT : text='l3_local: l2_local: l1_step_1: second'\n", - "USER: \n", - "BOT : text='l1_global: l3_local: l2_step_2: third'\n", - "USER: \n", - "BOT : text='l1_global: l2_local: l3_step_3: fourth'\n", - "USER: \n", - "BOT : text='l1_global: l3_local: l2_local: l4_step_4: fifth'\n", - "USER: \n", - "BOT : text='l1_global: l3_local: l2_local: first'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"root\", \"start\"),\n", - " fallback_label=(\"root\", \"fallback\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.8_misc.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.8_misc.ipynb deleted file mode 100644 index 03b26a228..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.8_misc.ipynb +++ /dev/null @@ -1,266 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "647fd618", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 8. Misc\n", - "\n", - "This tutorial shows `MISC` (miscellaneous) keyword usage.\n", - "\n", - "See [MISC](../apiref/chatsky.core.script.rst#chatsky.core.script.MISC)\n", - "for more information." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "04229782", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:36.688374Z", - "iopub.status.busy": "2024-10-10T12:18:36.688169Z", - "iopub.status.idle": "2024-10-10T12:18:37.999377Z", - "shell.execute_reply": "2024-10-10T12:18:37.998553Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "2e4d115a", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:38.002448Z", - "iopub.status.busy": "2024-10-10T12:18:38.002128Z", - "iopub.status.idle": "2024-10-10T12:18:39.221397Z", - "shell.execute_reply": "2024-10-10T12:18:39.220730Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " GLOBAL,\n", - " LOCAL,\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " MISC,\n", - " Context,\n", - " Message,\n", - " Pipeline,\n", - " MessageInitTypes,\n", - " BaseResponse,\n", - " Transition as Tr,\n", - " destinations as dst,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "640afd8f", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "`MISC` is used to store custom node data.\n", - "It can be accessed via `ctx.current_node.misc`." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "6b5af009", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:39.224263Z", - "iopub.status.busy": "2024-10-10T12:18:39.223731Z", - "iopub.status.idle": "2024-10-10T12:18:39.227836Z", - "shell.execute_reply": "2024-10-10T12:18:39.227196Z" - } - }, - "outputs": [], - "source": [ - "class CustomResponse(BaseResponse):\n", - " async def call(self, ctx: Context) -> MessageInitTypes:\n", - " return (\n", - " f\"node_name={ctx.last_label.node_name}: \"\n", - " f\"current_node.misc={ctx.current_node.misc}\"\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "a786962b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:39.229887Z", - "iopub.status.busy": "2024-10-10T12:18:39.229490Z", - "iopub.status.idle": "2024-10-10T12:18:39.235854Z", - "shell.execute_reply": "2024-10-10T12:18:39.235284Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"root\": {\n", - " \"start\": {\n", - " TRANSITIONS: [Tr(dst=(\"flow\", \"step_0\"))],\n", - " },\n", - " \"fallback\": {RESPONSE: \"the end\"},\n", - " },\n", - " GLOBAL: {\n", - " MISC: {\n", - " \"var1\": \"global_data\",\n", - " \"var2\": \"global_data\",\n", - " \"var3\": \"global_data\",\n", - " }\n", - " },\n", - " \"flow\": {\n", - " LOCAL: {\n", - " MISC: {\n", - " \"var2\": \"global data is overwritten by local\",\n", - " \"var3\": \"global data is overwritten by local\",\n", - " },\n", - " TRANSITIONS: [Tr(dst=dst.Forward(loop=True))],\n", - " },\n", - " \"step_0\": {\n", - " MISC: {\"var3\": \"this overwrites local values - step_0\"},\n", - " RESPONSE: CustomResponse(),\n", - " },\n", - " \"step_1\": {\n", - " MISC: {\"var3\": \"this overwrites local values - step_1\"},\n", - " RESPONSE: CustomResponse(),\n", - " },\n", - " \"step_2\": {\n", - " MISC: {\"var3\": \"this overwrites local values - step_2\"},\n", - " RESPONSE: CustomResponse(),\n", - " },\n", - " },\n", - "}\n", - "\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\n", - " Message(),\n", - " \"node_name=step_0: current_node.misc=\"\n", - " \"{'var3': 'this overwrites local values - step_0', \"\n", - " \"'var2': 'global data is overwritten by local', \"\n", - " \"'var1': 'global_data'}\",\n", - " ),\n", - " (\n", - " Message(),\n", - " \"node_name=step_1: current_node.misc=\"\n", - " \"{'var3': 'this overwrites local values - step_1', \"\n", - " \"'var2': 'global data is overwritten by local', \"\n", - " \"'var1': 'global_data'}\",\n", - " ),\n", - " (\n", - " Message(),\n", - " \"node_name=step_2: current_node.misc=\"\n", - " \"{'var3': 'this overwrites local values - step_2', \"\n", - " \"'var2': 'global data is overwritten by local', \"\n", - " \"'var1': 'global_data'}\",\n", - " ),\n", - " (\n", - " Message(),\n", - " \"node_name=step_0: current_node.misc=\"\n", - " \"{'var3': 'this overwrites local values - step_0', \"\n", - " \"'var2': 'global data is overwritten by local', \"\n", - " \"'var1': 'global_data'}\",\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "de2a0d91", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:39.238148Z", - "iopub.status.busy": "2024-10-10T12:18:39.237710Z", - "iopub.status.idle": "2024-10-10T12:18:39.248722Z", - "shell.execute_reply": "2024-10-10T12:18:39.248119Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: \n", - "BOT : text='node_name=step_0: current_node.misc={'var3': 'this overwrites local values - step_0', 'var2': 'global data is overwritten by local', 'var1': 'global_data'}'\n", - "USER: \n", - "BOT : text='node_name=step_1: current_node.misc={'var3': 'this overwrites local values - step_1', 'var2': 'global data is overwritten by local', 'var1': 'global_data'}'\n", - "USER: \n", - "BOT : text='node_name=step_2: current_node.misc={'var3': 'this overwrites local values - step_2', 'var2': 'global data is overwritten by local', 'var1': 'global_data'}'\n", - "USER: \n", - "BOT : text='node_name=step_0: current_node.misc={'var3': 'this overwrites local values - step_0', 'var2': 'global data is overwritten by local', 'var1': 'global_data'}'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"root\", \"start\"),\n", - " fallback_label=(\"root\", \"fallback\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.core.9_pre_transition_processing.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.core.9_pre_transition_processing.ipynb deleted file mode 100644 index 2b13c25b5..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.core.9_pre_transition_processing.ipynb +++ /dev/null @@ -1,279 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "2f0158af", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Core: 9. Pre-transition processing\n", - "\n", - "Here, [PRE_TRANSITION](../apiref/chatsky.core.script.rst#chatsky.core.script.PRE_TRANSITION)\n", - "is demonstrated which can be used for additional context\n", - "processing before transitioning to the next step." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "5b524e85", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:41.039964Z", - "iopub.status.busy": "2024-10-10T12:18:41.039381Z", - "iopub.status.idle": "2024-10-10T12:18:42.219388Z", - "shell.execute_reply": "2024-10-10T12:18:42.218423Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "e308e824", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:42.222300Z", - "iopub.status.busy": "2024-10-10T12:18:42.222052Z", - "iopub.status.idle": "2024-10-10T12:18:43.355629Z", - "shell.execute_reply": "2024-10-10T12:18:43.355009Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " GLOBAL,\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " PRE_RESPONSE,\n", - " PRE_TRANSITION,\n", - " Context,\n", - " Pipeline,\n", - " BaseProcessing,\n", - " BaseResponse,\n", - " MessageInitTypes,\n", - " Transition as Tr,\n", - " destinations as dst,\n", - " processing as proc,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "2982409e", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "Processing functions can be used at two stages:\n", - "\n", - "1. Pre-transition. Triggers after response is received but before\n", - " the next node is considered.\n", - "2. Pre-response. Triggers after transition is chosen and current node is\n", - " changed but before response of that node is calculated.\n", - "\n", - "In this tutorial we'll save the response function of the current node\n", - "during pre-transition and extract it during pre-response\n", - "(at which point current node is already changed)." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "6257ebbc", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:43.358493Z", - "iopub.status.busy": "2024-10-10T12:18:43.357960Z", - "iopub.status.idle": "2024-10-10T12:18:43.364044Z", - "shell.execute_reply": "2024-10-10T12:18:43.363475Z" - } - }, - "outputs": [], - "source": [ - "class SavePreviousNodeResponse(BaseProcessing):\n", - " async def call(self, ctx: Context) -> None:\n", - " if ctx.current_node.response is not None:\n", - " ctx.misc[\"previous_node_response\"] = ctx.current_node.response\n", - " # This function is called as Pre-transition\n", - " # so current node is going to be the previous one\n", - " # when we reach the Pre-response step\n", - "\n", - "\n", - "class PrependPreviousNodeResponse(proc.ModifyResponse):\n", - " async def modified_response(\n", - " self, original_response: BaseResponse, ctx: Context\n", - " ) -> MessageInitTypes:\n", - " result = await original_response(ctx)\n", - "\n", - " previous_node_response = ctx.misc.get(\"previous_node_response\")\n", - " if previous_node_response is None:\n", - " return result\n", - " else:\n", - " previous_result = await previous_node_response(ctx)\n", - " return f\"previous={previous_result.text}: current={result.text}\"" - ] - }, - { - "cell_type": "markdown", - "id": "d70ab34d", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "
\n", - "\n", - "Note\n", - "\n", - "Previous node can be accessed another way.\n", - "\n", - "Instead of storing the node response in misc,\n", - "one can obtain previous label\n", - "with `dst.Previous()(ctx)` and then get the node from the\n", - "[Script](../apiref/chatsky.core.script.rst#chatsky.core.script.Script) object:\n", - "\n", - "```python\n", - "ctx.pipeline.script.get_inherited_node(dst.Previous()(ctx))\n", - "```\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "bd563c93", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:43.366275Z", - "iopub.status.busy": "2024-10-10T12:18:43.365884Z", - "iopub.status.idle": "2024-10-10T12:18:43.371255Z", - "shell.execute_reply": "2024-10-10T12:18:43.370594Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# a dialog script\n", - "toy_script = {\n", - " \"root\": {\n", - " \"start\": {\n", - " TRANSITIONS: [Tr(dst=(\"flow\", \"step_0\"))],\n", - " },\n", - " \"fallback\": {RESPONSE: \"the end\"},\n", - " },\n", - " GLOBAL: {\n", - " PRE_RESPONSE: {\"proc_name_1\": PrependPreviousNodeResponse()},\n", - " PRE_TRANSITION: {\"proc_name_1\": SavePreviousNodeResponse()},\n", - " TRANSITIONS: [Tr(dst=dst.Forward(loop=True))],\n", - " },\n", - " \"flow\": {\n", - " \"step_0\": {RESPONSE: \"first\"},\n", - " \"step_1\": {RESPONSE: \"second\"},\n", - " \"step_2\": {RESPONSE: \"third\"},\n", - " \"step_3\": {RESPONSE: \"fourth\"},\n", - " \"step_4\": {RESPONSE: \"fifth\"},\n", - " },\n", - "}\n", - "\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\"1\", \"first\"),\n", - " (\"2\", \"previous=first: current=second\"),\n", - " (\"3\", \"previous=second: current=third\"),\n", - " (\"4\", \"previous=third: current=fourth\"),\n", - " (\"5\", \"previous=fourth: current=fifth\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "93c12e86", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:43.373574Z", - "iopub.status.busy": "2024-10-10T12:18:43.373066Z", - "iopub.status.idle": "2024-10-10T12:18:43.389345Z", - "shell.execute_reply": "2024-10-10T12:18:43.388756Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='1'\n", - "BOT : text='first'\n", - "USER: text='2'\n", - "BOT : text='previous=first: current=second'\n", - "USER: text='3'\n", - "BOT : text='previous=second: current=third'\n", - "USER: text='4'\n", - "BOT : text='previous=third: current=fourth'\n", - "USER: text='5'\n", - "BOT : text='previous=fourth: current=fifth'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"root\", \"start\"),\n", - " fallback_label=(\"root\", \"fallback\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.responses.1_media.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.responses.1_media.ipynb deleted file mode 100644 index 0ad4422c5..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.responses.1_media.ipynb +++ /dev/null @@ -1,234 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ebccfdcd", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Responses: 1. Media\n", - "\n", - "Here, [Attachment](../apiref/chatsky.core.message.rst#chatsky.core.message.Attachment) class is shown.\n", - "Attachments can be used for attaching different media elements\n", - "(such as [Image](../apiref/chatsky.core.message.rst#chatsky.core.message.Image),\n", - "[Document](../apiref/chatsky.core.message.rst#chatsky.core.message.Document)\n", - "or [Audio](../apiref/chatsky.core.message.rst#chatsky.core.message.Audio)).\n", - "\n", - "They can be attached to any message but will only work if the chosen\n", - "[messenger interface](../apiref/index_messenger_interfaces.rst) supports them." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a5d8ab76", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:45.042194Z", - "iopub.status.busy": "2024-10-10T12:18:45.041983Z", - "iopub.status.idle": "2024-10-10T12:18:46.238957Z", - "shell.execute_reply": "2024-10-10T12:18:46.238115Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "5d37caf2", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:46.241588Z", - "iopub.status.busy": "2024-10-10T12:18:46.241179Z", - "iopub.status.idle": "2024-10-10T12:18:47.403198Z", - "shell.execute_reply": "2024-10-10T12:18:47.402538Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " Message,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - " destinations as dst,\n", - ")\n", - "from chatsky.core.message import Image\n", - "\n", - "from chatsky.utils.testing import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e9d6932e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:47.406169Z", - "iopub.status.busy": "2024-10-10T12:18:47.405636Z", - "iopub.status.idle": "2024-10-10T12:18:47.413988Z", - "shell.execute_reply": "2024-10-10T12:18:47.413421Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "img_url = \"https://www.python.org/static/img/python-logo.png\"\n", - "toy_script = {\n", - " \"root\": {\n", - " \"start\": {\n", - " TRANSITIONS: [Tr(dst=(\"pics\", \"ask_picture\"))],\n", - " },\n", - " \"fallback\": {\n", - " RESPONSE: \"Final node reached, send any message to restart.\",\n", - " TRANSITIONS: [Tr(dst=(\"pics\", \"ask_picture\"))],\n", - " },\n", - " },\n", - " \"pics\": {\n", - " \"ask_picture\": {\n", - " RESPONSE: \"Please, send me a picture url\",\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"pics\", \"send_one\"),\n", - " cnd=cnd.Regexp(r\"^http.+\\.png$\"),\n", - " ),\n", - " Tr(\n", - " dst=(\"pics\", \"send_many\"),\n", - " cnd=cnd.Regexp(f\"{img_url} repeat 10 times\"),\n", - " ),\n", - " Tr(\n", - " dst=dst.Current(),\n", - " ),\n", - " ],\n", - " },\n", - " \"send_one\": {\n", - " RESPONSE: Message( # need to use the Message class to send images\n", - " text=\"here's my picture!\",\n", - " attachments=[Image(source=img_url)],\n", - " ),\n", - " },\n", - " \"send_many\": {\n", - " RESPONSE: Message(\n", - " text=\"Look at my pictures\",\n", - " attachments=[Image(source=img_url)] * 10,\n", - " ),\n", - " },\n", - " },\n", - "}\n", - "\n", - "happy_path = (\n", - " (\"Hi\", \"Please, send me a picture url\"),\n", - " (\"no\", \"Please, send me a picture url\"),\n", - " (\n", - " img_url,\n", - " Message(\n", - " text=\"here's my picture!\",\n", - " attachments=[Image(source=img_url)],\n", - " ),\n", - " ),\n", - " (\"ok\", \"Final node reached, send any message to restart.\"),\n", - " (\"ok\", \"Please, send me a picture url\"),\n", - " (\n", - " f\"{img_url} repeat 10 times\",\n", - " Message(\n", - " text=\"Look at my pictures\",\n", - " attachments=[Image(source=img_url)] * 10,\n", - " ),\n", - " ),\n", - " (\"ok\", \"Final node reached, send any message to restart.\"),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "282a735d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:47.416173Z", - "iopub.status.busy": "2024-10-10T12:18:47.415752Z", - "iopub.status.idle": "2024-10-10T12:18:47.432093Z", - "shell.execute_reply": "2024-10-10T12:18:47.431411Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Please, send me a picture url'\n", - "USER: text='no'\n", - "BOT : text='Please, send me a picture url'\n", - "USER: text='https://www.python.org/static/img/python-logo.png'\n", - "BOT : text='here's my picture!' attachments='[{'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}]'\n", - "USER: text='ok'\n", - "BOT : text='Final node reached, send any message to restart.'\n", - "USER: text='ok'\n", - "BOT : text='Please, send me a picture url'\n", - "USER: text='https://www.python.org/static/img/python-logo.png repeat 10 times'\n", - "BOT : text='Look at my pictures' attachments='[{'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}, {'chatsky_attachment_type': 'image', 'source': Url('https://www.python.org/static/img/python-logo.png'), 'use_cache': True}]'\n", - "USER: text='ok'\n", - "BOT : text='Final node reached, send any message to restart.'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"root\", \"start\"),\n", - " fallback_label=(\"root\", \"fallback\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.script.responses.2_multi_message.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.script.responses.2_multi_message.ipynb deleted file mode 100644 index be4f81490..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.script.responses.2_multi_message.ipynb +++ /dev/null @@ -1,290 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "e2193333", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# Responses: 2. Multi Message\n", - "\n", - "This tutorial shows how to store several messages inside a single one.\n", - "This might be useful if you want Chatsky Pipeline to send `response` candidates\n", - "to the messenger interface instead of a final response." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "727e480e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:48.912691Z", - "iopub.status.busy": "2024-10-10T12:18:48.912447Z", - "iopub.status.idle": "2024-10-10T12:18:50.143820Z", - "shell.execute_reply": "2024-10-10T12:18:50.142855Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "8495984c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:50.146502Z", - "iopub.status.busy": "2024-10-10T12:18:50.146082Z", - "iopub.status.idle": "2024-10-10T12:18:51.292660Z", - "shell.execute_reply": "2024-10-10T12:18:51.292000Z" - } - }, - "outputs": [], - "source": [ - "\n", - "from chatsky import (\n", - " TRANSITIONS,\n", - " RESPONSE,\n", - " Message,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - ")\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "1607822b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:51.295423Z", - "iopub.status.busy": "2024-10-10T12:18:51.294922Z", - "iopub.status.idle": "2024-10-10T12:18:51.305380Z", - "shell.execute_reply": "2024-10-10T12:18:51.304834Z" - } - }, - "outputs": [], - "source": [ - "toy_script = {\n", - " \"greeting_flow\": {\n", - " \"start_node\": {\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " \"node1\": {\n", - " RESPONSE: Message(\n", - " misc={\n", - " \"messages\": [\n", - " Message(\n", - " text=\"Hi, what is up?\", misc={\"confidences\": 0.85}\n", - " ),\n", - " Message(\n", - " text=\"Hello, how are you?\",\n", - " misc={\"confidences\": 0.9},\n", - " ),\n", - " ]\n", - " }\n", - " ),\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node2\", cnd=cnd.ExactMatch(\"I'm fine, how are you?\"))\n", - " ],\n", - " },\n", - " \"node2\": {\n", - " RESPONSE: \"Good. What do you want to talk about?\",\n", - " TRANSITIONS: [\n", - " Tr(dst=\"node3\", cnd=cnd.ExactMatch(\"Let's talk about music.\"))\n", - " ],\n", - " },\n", - " \"node3\": {\n", - " RESPONSE: \"Sorry, I can not talk about that now.\",\n", - " TRANSITIONS: [Tr(dst=\"node4\", cnd=cnd.ExactMatch(\"Ok, goodbye.\"))],\n", - " },\n", - " \"node4\": {\n", - " RESPONSE: \"bye\",\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " \"fallback_node\": {\n", - " RESPONSE: \"Ooops\",\n", - " TRANSITIONS: [Tr(dst=\"node1\", cnd=cnd.ExactMatch(\"Hi\"))],\n", - " },\n", - " }\n", - "}\n", - "\n", - "# testing\n", - "happy_path = (\n", - " (\n", - " \"Hi\",\n", - " Message(\n", - " misc={\n", - " \"messages\": [\n", - " Message(\"Hi, what is up?\", misc={\"confidences\": 0.85}),\n", - " Message(\"Hello, how are you?\", misc={\"confidences\": 0.9}),\n", - " ]\n", - " }\n", - " ),\n", - " ), # start_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about that now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"bye\"), # node3 -> node4\n", - " (\n", - " \"Hi\",\n", - " Message(\n", - " misc={\n", - " \"messages\": [\n", - " Message(\"Hi, what is up?\", misc={\"confidences\": 0.85}),\n", - " Message(\"Hello, how are you?\", misc={\"confidences\": 0.9}),\n", - " ]\n", - " }\n", - " ),\n", - " ), # node4 -> node1\n", - " (\n", - " \"stop\",\n", - " \"Ooops\",\n", - " ),\n", - " # node1 -> fallback_node\n", - " (\n", - " \"one\",\n", - " \"Ooops\",\n", - " ), # f_n->f_n\n", - " (\n", - " \"help\",\n", - " \"Ooops\",\n", - " ), # f_n->f_n\n", - " (\n", - " \"nope\",\n", - " \"Ooops\",\n", - " ), # f_n->f_n\n", - " (\n", - " \"Hi\",\n", - " Message(\n", - " misc={\n", - " \"messages\": [\n", - " Message(\"Hi, what is up?\", misc={\"confidences\": 0.85}),\n", - " Message(\"Hello, how are you?\", misc={\"confidences\": 0.9}),\n", - " ]\n", - " }\n", - " ),\n", - " ), # fallback_node -> node1\n", - " (\n", - " \"I'm fine, how are you?\",\n", - " \"Good. What do you want to talk about?\",\n", - " ), # node1 -> node2\n", - " (\n", - " \"Let's talk about music.\",\n", - " \"Sorry, I can not talk about that now.\",\n", - " ), # node2 -> node3\n", - " (\"Ok, goodbye.\", \"bye\"), # node3 -> node4\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "85a01e15", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:51.307573Z", - "iopub.status.busy": "2024-10-10T12:18:51.307151Z", - "iopub.status.idle": "2024-10-10T12:18:51.326772Z", - "shell.execute_reply": "2024-10-10T12:18:51.326209Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : misc='{'messages': [{'text': 'Hi, what is up?', 'misc': {'confidences': 0.85}}, {'text': 'Hello, how are you?', 'misc': {'confidences': 0.9}}]}'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about that now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : misc='{'messages': [{'text': 'Hi, what is up?', 'misc': {'confidences': 0.85}}, {'text': 'Hello, how are you?', 'misc': {'confidences': 0.9}}]}'\n", - "USER: text='stop'\n", - "BOT : text='Ooops'\n", - "USER: text='one'\n", - "BOT : text='Ooops'\n", - "USER: text='help'\n", - "BOT : text='Ooops'\n", - "USER: text='nope'\n", - "BOT : text='Ooops'\n", - "USER: text='Hi'\n", - "BOT : misc='{'messages': [{'text': 'Hi, what is up?', 'misc': {'confidences': 0.85}}, {'text': 'Hello, how are you?', 'misc': {'confidences': 0.9}}]}'\n", - "USER: text='I'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about that now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n" - ] - } - ], - "source": [ - "\n", - "pipeline = Pipeline(\n", - " script=toy_script,\n", - " start_label=(\"greeting_flow\", \"start_node\"),\n", - " fallback_label=(\"greeting_flow\", \"fallback_node\"),\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, happy_path, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.service.1_basics.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.service.1_basics.ipynb deleted file mode 100644 index 2fa1d09bd..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.service.1_basics.ipynb +++ /dev/null @@ -1,220 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "3abe8a48", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 1. Basic services\n", - "\n", - "This tutorial shows basics of using services.\n", - "\n", - "Here, [misc](../apiref/chatsky.core.context.rst#chatsky.core.context.Context.misc)\n", - "dictionary of context is used for storing additional data." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "ce1d2d70", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:52.989931Z", - "iopub.status.busy": "2024-10-10T12:18:52.989192Z", - "iopub.status.idle": "2024-10-10T12:18:54.129891Z", - "shell.execute_reply": "2024-10-10T12:18:54.129101Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "9daf2789", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:54.132157Z", - "iopub.status.busy": "2024-10-10T12:18:54.131915Z", - "iopub.status.idle": "2024-10-10T12:18:55.355878Z", - "shell.execute_reply": "2024-10-10T12:18:55.354837Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "import logging\n", - "import sys\n", - "from importlib import reload\n", - "\n", - "from chatsky import Context, Pipeline\n", - "\n", - "from chatsky.utils.testing import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - " HAPPY_PATH,\n", - " TOY_SCRIPT_KWARGS,\n", - ")\n", - "\n", - "reload(logging)\n", - "logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=\"\")\n", - "# fix jupyter logs display\n", - "\n", - "logger = logging.getLogger(__name__)" - ] - }, - { - "cell_type": "markdown", - "id": "a307a65d", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "When Pipeline is created, additional pre-\n", - "and post-services can be defined. These run before and after\n", - "`Actor` respectively.\n", - "\n", - "
\n", - "\n", - "Reminder\n", - "\n", - "`Actor` is a Pipeline component that processes user request, determines\n", - "the next node and generates a response from that node.\n", - "\n", - "
\n", - "\n", - "Services can be used to access external APIs, annotate user input, etc.\n", - "\n", - "Service callables only take one parameter: `ctx` (Context) and have no return.\n", - "\n", - "Here a pre-service (\"ping\") and\n", - "a post-service (\"pong\") are added to the pipeline." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "af3de212", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:55.359837Z", - "iopub.status.busy": "2024-10-10T12:18:55.358911Z", - "iopub.status.idle": "2024-10-10T12:18:55.367731Z", - "shell.execute_reply": "2024-10-10T12:18:55.366835Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "def ping_processor(ctx: Context):\n", - " logger.info(\"ping - ...\")\n", - " ctx.misc[\"ping\"] = True\n", - "\n", - "\n", - "# services can be both sync and async\n", - "async def pong_processor(ctx: Context):\n", - " ping_pong = ctx.misc.get(\"ping\", False)\n", - " logger.info(\"... - pong\")\n", - " logger.info(\n", - " f\"Ping-pong exchange: \" f\"{'completed' if ping_pong else 'failed'}.\"\n", - " )\n", - "\n", - "\n", - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS, # contains script, start and fallback labels\n", - " pre_services=[ping_processor],\n", - " post_services=[pong_processor],\n", - " # To add a service simply add it to the corresponding service list\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "82d4118c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:55.370405Z", - "iopub.status.busy": "2024-10-10T12:18:55.369997Z", - "iopub.status.idle": "2024-10-10T12:18:55.380974Z", - "shell.execute_reply": "2024-10-10T12:18:55.380254Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "ping - ...\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "... - pong\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Ping-pong exchange: completed.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH[:1], printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.service.2_advanced.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.service.2_advanced.ipynb deleted file mode 100644 index 05504d1a7..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.service.2_advanced.ipynb +++ /dev/null @@ -1,290 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a3bbf1c8", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 2. Advanced services\n", - "\n", - "This tutorial demonstrates various configuration options for services.\n", - "\n", - "For more information, see\n", - "[API ref](../apiref/chatsky.core.service.service.rst#chatsky.core.service.service.Service)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "1be56566", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:57.072436Z", - "iopub.status.busy": "2024-10-10T12:18:57.071569Z", - "iopub.status.idle": "2024-10-10T12:18:58.251630Z", - "shell.execute_reply": "2024-10-10T12:18:58.250906Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f8ddd566", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:58.254119Z", - "iopub.status.busy": "2024-10-10T12:18:58.253691Z", - "iopub.status.idle": "2024-10-10T12:18:59.377593Z", - "shell.execute_reply": "2024-10-10T12:18:59.376967Z" - } - }, - "outputs": [], - "source": [ - "import logging\n", - "import sys\n", - "from importlib import reload\n", - "\n", - "from chatsky import Context, Pipeline, BaseProcessing\n", - "from chatsky.core.service import Service\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH\n", - "\n", - "reload(logging)\n", - "logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=\"\")\n", - "logger = logging.getLogger(__name__)" - ] - }, - { - "cell_type": "markdown", - "id": "5b810ec1", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "## Intro\n", - "\n", - "In the previous tutorial we used a function as a Service.\n", - "Under the hood a function is converted to a `Service` object\n", - "with the function as its `handler` argument.\n", - "\n", - "The `Service` model has other arguments that modify its execution.\n", - "\n", - "## Service Arguments\n", - "\n", - "* `handler` - Function or `BaseProcessing`.\n", - "* `before_handler` - a list of functions that run before the service.\n", - " You can read more about the handlers in this [tutorial](\n", - " ../tutorials/tutorials.service.5_extra_handlers.py\n", - " ).\n", - "* `after_handler` - a list of functions that run after the service.\n", - " You can read more about the handlers in this [tutorial](\n", - " ../tutorials/tutorials.service.5_extra_handlers.py\n", - " ).\n", - "* `timeout` - service timeout.\n", - "* `concurrent` - whether this service can run concurrently,\n", - " see [tutorial 3](\n", - " ../tutorials/tutorials.service.3_groups.py).\n", - "* `start_condition` - service start condition, see [tutorial 4](\n", - " ../tutorials/tutorials.service.4_conditions_and_paths.py).\n", - "* `name` - name of the service,\n", - " see [tutorial 4](\n", - " ../tutorials/tutorials.service.4_conditions_and_paths.py).\n", - "\n", - "## Service subclassing\n", - "\n", - "Services can also be defined as subclasses of `Service`,\n", - "allowing access to all the fields described above via `self`.\n", - "\n", - "To do this, derive your class from `Service`,\n", - "then implement an async `call` method which will\n", - "now replace the `handler` (see the `PreProcess` example below).\n", - "\n", - "
\n", - "\n", - "Tip\n", - "\n", - "When defining a service as a subclass of `Service`, you can also change\n", - "default parameters such as `timeout` or `start_condition`.\n", - "\n", - "
\n", - "\n", - "## Code explanation\n", - "\n", - "In this example, pipeline contains three services,\n", - "defined in three different ways.\n", - "\n", - "The first is defined as a Service with a function handler.\n", - "\n", - "The second derives from the `Service` class.\n", - "\n", - "The third is defined as a Service with a processing handler." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "f4f187c2", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:59.380349Z", - "iopub.status.busy": "2024-10-10T12:18:59.379787Z", - "iopub.status.idle": "2024-10-10T12:18:59.390242Z", - "shell.execute_reply": "2024-10-10T12:18:59.389593Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "async def function_handler(ctx: Context):\n", - " logger.info(\n", - " \"function_handler running:\\n\"\n", - " \"timeout of this service cannot be determined\"\n", - " )\n", - "\n", - "\n", - "class ServiceSubclass(Service):\n", - " async def call(self, ctx: Context):\n", - " logger.info(\n", - " f\"{self.name or self.computed_name} running:\\n\"\n", - " f\"timeout: {self.timeout}\"\n", - " )\n", - "\n", - " timeout: float = 1.0\n", - " # this overrides the default `None` timeout,\n", - " # but can still be overridden in class instances\n", - "\n", - "\n", - "class ProcessingService(BaseProcessing):\n", - " async def call(self, ctx: Context) -> None:\n", - " try:\n", - " logger.info(self.timeout)\n", - " except AttributeError:\n", - " # this is BaseProcessing not Service so there's no `timeout` field\n", - " logger.info(\n", - " \"ProcessingService running:\\n\"\n", - " \"timeout of this service cannot be determined\"\n", - " )\n", - "\n", - "\n", - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS,\n", - " pre_services=[\n", - " Service(\n", - " handler=function_handler,\n", - " timeout=0.5,\n", - " ),\n", - " ServiceSubclass(name=\"ServiceSubclassWithCustomName\", timeout=100),\n", - " ServiceSubclass(),\n", - " Service(handler=ProcessingService(), timeout=4),\n", - " ],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "c441eb15", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:18:59.392375Z", - "iopub.status.busy": "2024-10-10T12:18:59.391886Z", - "iopub.status.idle": "2024-10-10T12:18:59.401534Z", - "shell.execute_reply": "2024-10-10T12:18:59.400984Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "function_handler running:\n", - "timeout of this service cannot be determined\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ServiceSubclassWithCustomName running:\n", - "timeout: 100.0\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ServiceSubclass running:\n", - "timeout: 1.0\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ProcessingService running:\n", - "timeout of this service cannot be determined\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH[:1], printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.service.3_groups.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.service.3_groups.ipynb deleted file mode 100644 index 982b32c4e..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.service.3_groups.ipynb +++ /dev/null @@ -1,313 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "e73a7ac6", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 3. Service Groups\n", - "\n", - "The following tutorial shows how to group multiple services.\n", - "\n", - "For more information, see\n", - "[API ref](../apiref/chatsky.core.service.group.rst#chatsky.core.service.group.ServiceGroup)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "230de831", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:00.975000Z", - "iopub.status.busy": "2024-10-10T12:19:00.974783Z", - "iopub.status.idle": "2024-10-10T12:19:02.168141Z", - "shell.execute_reply": "2024-10-10T12:19:02.167404Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "bf11ec37", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:02.170648Z", - "iopub.status.busy": "2024-10-10T12:19:02.170228Z", - "iopub.status.idle": "2024-10-10T12:19:03.355473Z", - "shell.execute_reply": "2024-10-10T12:19:03.354841Z" - } - }, - "outputs": [], - "source": [ - "import asyncio\n", - "import logging\n", - "import sys\n", - "from importlib import reload\n", - "\n", - "from chatsky.core import Context, Pipeline\n", - "from chatsky.core.service import ServiceGroup\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS\n", - "\n", - "reload(logging)\n", - "logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=\"\")\n", - "logger = logging.getLogger(__name__)" - ] - }, - { - "cell_type": "markdown", - "id": "f2dfd9a1", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "## Intro\n", - "\n", - "Service groups are used to combine several services\n", - "(or service groups) into one.\n", - "\n", - "Both services and service groups inherit interface from `PipelineComponent`\n", - "class which defines all the fields described in the [previous tutorial](\n", - "../tutorials/tutorials.service.2_advanced.py)\n", - "except `handler`.\n", - "\n", - "Instead of `handler` service group defines `components`:\n", - "a list of services or service groups.\n", - "\n", - "Pipeline pre-services and post-services are actually service groups\n", - "and you can pass a ServiceGroup instead of a list when initializing Pipeline.\n", - "\n", - "## Component execution\n", - "\n", - "Components inside a service group are executed sequentially, except for\n", - "components with the `concurrent` attribute set to `True`:\n", - "Continuous sequences of concurrent components are executed concurrently\n", - "(via `asyncio.gather`).\n", - "\n", - "For example, if components are `[1, 1, 0, 0, 1, 1, 1, 0]` where\n", - "\"1\" indicates a concurrent component, the components are executed as follows:\n", - "\n", - "1. Components 1 and 2 (concurrently);\n", - "2. Component 3;\n", - "3. Component 4;\n", - "4. Components 5, 6 and 7 (concurrently);\n", - "5. Component 8.\n", - "\n", - "
\n", - "\n", - "Note\n", - "\n", - "Components processing different contexts are always executed independently\n", - "of each other.\n", - "\n", - "
\n", - "\n", - "### Fully concurrent flag\n", - "\n", - "Service groups have a `fully_concurrent` flag which makes it treat\n", - "every component inside it as concurrent,\n", - "running all components simultaneously.\n", - "\n", - "This is convenient if you have a bunch of functions,\n", - "that you want to run simultaneously,\n", - "but don't want to make a service for each of them.\n", - "\n", - "## Code explanation\n", - "\n", - "In this example, we define `pre_services` as a `ServiceGroup` instead of a list.\n", - "This allows us to set the `fully_concurrent` flag to `True`.\n", - "The service group consists of 10 services that sleep 0.01 seconds each.\n", - "But since they are executed concurrently, the entire service group\n", - "takes much less time than 0.1 seconds.\n", - "\n", - "To further demonstrate ServiceGroup's execution logic,\n", - "`post_services` is a ServiceGroup with concurrent components 'A' and 'B',\n", - "which execute simultaneously, and also one regular component 'C' at the end.\n", - "\n", - "If 'A' and 'B' weren't concurrent, all steps for component 'A' would complete\n", - "before component 'B' begins its execution, but instead they start\n", - "at the same time. Only after both of them have finished,\n", - "does component 'C' start working." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "97128432", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:03.358270Z", - "iopub.status.busy": "2024-10-10T12:19:03.357753Z", - "iopub.status.idle": "2024-10-10T12:19:03.365994Z", - "shell.execute_reply": "2024-10-10T12:19:03.365407Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "async def time_consuming_service(_):\n", - " await asyncio.sleep(0.01)\n", - "\n", - "\n", - "def interact(stage: str, service: str):\n", - " async def slow_service(_: Context):\n", - " logger.info(f\"{stage} with service {service}\")\n", - " await asyncio.sleep(0.1)\n", - "\n", - " return slow_service\n", - "\n", - "\n", - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS,\n", - " pre_services=ServiceGroup(\n", - " components=[time_consuming_service for _ in range(0, 10)],\n", - " fully_concurrent=True,\n", - " ),\n", - " post_services=[\n", - " ServiceGroup(\n", - " name=\"InteractWithServiceA\",\n", - " components=[\n", - " interact(\"Starting interaction\", \"A\"),\n", - " interact(\"Finishing interaction\", \"A\"),\n", - " ],\n", - " concurrent=True,\n", - " ),\n", - " ServiceGroup(\n", - " name=\"InteractWithServiceB\",\n", - " components=[\n", - " interact(\"Starting interaction\", \"B\"),\n", - " interact(\"Finishing interaction\", \"B\"),\n", - " ],\n", - " concurrent=True,\n", - " ),\n", - " ServiceGroup(\n", - " name=\"InteractWithServiceC\",\n", - " components=[\n", - " interact(\"Starting interaction\", \"C\"),\n", - " interact(\"Finishing interaction\", \"C\"),\n", - " ],\n", - " concurrent=False,\n", - " ),\n", - " ],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "284841c5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:03.368057Z", - "iopub.status.busy": "2024-10-10T12:19:03.367737Z", - "iopub.status.idle": "2024-10-10T12:19:03.791227Z", - "shell.execute_reply": "2024-10-10T12:19:03.790473Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "Starting interaction with service A\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting interaction with service B\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finishing interaction with service A\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finishing interaction with service B\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Starting interaction with service C\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Finishing interaction with service C\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH[:1], printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.service.4_conditions_and_paths.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.service.4_conditions_and_paths.ipynb deleted file mode 100644 index e2ea59474..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.service.4_conditions_and_paths.ipynb +++ /dev/null @@ -1,383 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "6375d29f", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 4. Conditions and paths\n", - "\n", - "This tutorial explains how a unique path is generated for each component\n", - "and how to add a condition for component execution.\n", - "\n", - "[API ref for service conditions](../apiref/chatsky.conditions.service.rst)" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "5ea9e090", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:05.447270Z", - "iopub.status.busy": "2024-10-10T12:19:05.447050Z", - "iopub.status.idle": "2024-10-10T12:19:06.802743Z", - "shell.execute_reply": "2024-10-10T12:19:06.801744Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7e75c4ce", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:06.805973Z", - "iopub.status.busy": "2024-10-10T12:19:06.805641Z", - "iopub.status.idle": "2024-10-10T12:19:08.005302Z", - "shell.execute_reply": "2024-10-10T12:19:08.004583Z" - } - }, - "outputs": [], - "source": [ - "import logging\n", - "import sys\n", - "from importlib import reload\n", - "\n", - "from chatsky.conditions import Not, All, ServiceFinished\n", - "from chatsky.core.service import Service, ServiceGroup\n", - "from chatsky import Pipeline, Context, AnyCondition\n", - "\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS\n", - "\n", - "reload(logging)\n", - "logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=\"\")\n", - "logger = logging.getLogger(__name__)" - ] - }, - { - "cell_type": "markdown", - "id": "90337999", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "## Component paths\n", - "\n", - "Each component has a unique path that can be used as an ID for that component.\n", - "\n", - "A path of a component is a \".\" concatenation of names of service groups this\n", - "component is in and the name of this component.\n", - "\n", - "For example, if component \"c\" is in group \"b\" which itself is in group \"a\"\n", - "then path of \"c\" is \"a.b.c\".\n", - "\n", - "
\n", - "\n", - "Naming services\n", - "\n", - "When choosing a name for a component, keep in mind that\n", - "it should be unique among other components in the same service group.\n", - "\n", - "
\n", - "\n", - "### Computed names\n", - "\n", - "If a component does not have a name, one is provided to it:\n", - "\n", - "1. Each component has `computed_name` property which returns a default name.\n", - " All service groups have \"service_group\" as their computed name.\n", - " For services the computed name is the name of the handler **or** of the\n", - " service class if handler is not present.\n", - "\n", - " E.g. A service with handler `my_func` has computed name \"my_func\";\n", - " and if service `MyService` is a subclass of `Service` and does not\n", - " define `handler` its computed name is \"MyService\".\n", - "2. If there are two or more components in a service group with the same\n", - " computed name, they are assigned names with an incrementing postfix.\n", - " For example, if there are two service groups with no name in pre-services\n", - " they will be named \"service_group#0\" and \"service_group#1\".\n", - "\n", - "### Names of basic components\n", - "\n", - "* Main component (pipeline) has an empty name;\n", - "* Name of the pre services group is \"pre\";\n", - "* Name of the post services group is \"post\";\n", - "* Name of the actor service is \"actor\".\n", - "\n", - "For example, if you have `pre_services=[my_func]`, the full path of the\n", - "`my_func` component is \".pre.my_func\".\n", - "\n", - "## Component start condition\n", - "\n", - "Any component (service or service group) can have a `start_condition`.\n", - "\n", - "Start condition is a `BaseCondition` that determines whether the component\n", - "should be executed.\n", - "\n", - "For more information about conditions, see the [condition tutorial](\n", - "../tutorials/tutorials.script.core.2_conditions.py).\n", - "\n", - "### Component status\n", - "\n", - "At any time each component has a certain status:\n", - "\n", - "* `NOT_RUN` - Component hasn't bee executed yet or\n", - " start condition returned False.\n", - "* `RUNNING` - Component is currently being executed.\n", - "* `FINISHED` - Component finished successfully.\n", - "* `FAILED` - Component execution failed.\n", - "\n", - "For more information, see\n", - "[service_states](../apiref/chatsky.core.context.rst#chatsky.core.context.FrameworkData.service_states).\n", - "\n", - "### ServiceFinished condition\n", - "\n", - "`ServiceFinished` is a condition that returns `True` if another service\n", - "has the `FINISHED` status.\n", - "\n", - "`ServiceFinished` accepts the following constructor parameters:\n", - "\n", - "* `path` (required) - a path of another component.\n", - "* `wait` - whether it should wait for the component to complete.\n", - " This means that the component status cannot be\n", - " `NOT_RUN` or `RUNNING` at the time of the check.\n", - " Defaults to `False`.\n", - "\n", - "
\n", - "\n", - "Warning!\n", - "\n", - "It is possible for the pipeline to get stuck in infinite waiting\n", - "with the `ServiceFinished` condition.\n", - "\n", - "Either disable `wait` or set a timeout for the service.\n", - "\n", - "
\n", - "\n", - "For more information about `ServiceFinished`, see [API ref](\n", - "../apiref/chatsky.conditions.service.rst#chatsky.conditions.service.ServiceFinished).\n", - "\n", - "## Code explanation\n", - "\n", - "In this example, two conditionally executed services are illustrated.\n", - "\n", - "The service named `running_service` is executed\n", - "only if both `SimpleServices` in pre service group\n", - "have finished successfully.\n", - "\n", - "`never_running_service` is executed only if `running_service` is not finished,\n", - "which should never happen.\n", - "\n", - "Lastly, `context_printing_service` prints pipeline runtime information,\n", - "that contains execution state of all previously run services." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "35f03690", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:08.008203Z", - "iopub.status.busy": "2024-10-10T12:19:08.007534Z", - "iopub.status.idle": "2024-10-10T12:19:08.022056Z", - "shell.execute_reply": "2024-10-10T12:19:08.021422Z" - } - }, - "outputs": [], - "source": [ - "class SimpleService(Service):\n", - " async def call(self, _: Context):\n", - " logger.info(f\"Service '{self.name}' is running...\")\n", - "\n", - "\n", - "class NeverRunningService(Service):\n", - " async def call(self, _: Context):\n", - " raise Exception(f\"Oh no! The '{self.name}' service is running!\")\n", - "\n", - " start_condition: AnyCondition = Not(\n", - " ServiceFinished(\".post.named_group.running_service\", wait=True)\n", - " )\n", - "\n", - "\n", - "class RuntimeInfoPrintingService(Service):\n", - " async def call(self, _: Context):\n", - " logger.info(\n", - " f\"Service '{self.name}' runtime execution info:\"\n", - " f\"{self.model_dump_json(indent=4)}\"\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b00a4268", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:08.024462Z", - "iopub.status.busy": "2024-10-10T12:19:08.023927Z", - "iopub.status.idle": "2024-10-10T12:19:08.029194Z", - "shell.execute_reply": "2024-10-10T12:19:08.028560Z" - } - }, - "outputs": [], - "source": [ - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS,\n", - " pre_services=[\n", - " SimpleService(),\n", - " # This service will be named \"SimpleService#0\"\n", - " SimpleService(),\n", - " # This service will be named \"SimpleService#1\"\n", - " ],\n", - " # this group is named \"pre\"\n", - " post_services=[\n", - " ServiceGroup(\n", - " name=\"named_group\",\n", - " components=[\n", - " SimpleService(\n", - " start_condition=All(\n", - " ServiceFinished(\".pre.SimpleService#0\"),\n", - " ServiceFinished(\".pre.SimpleService#1\"),\n", - " ),\n", - " # Alternative:\n", - " # ServiceFinished(\".pre\")\n", - " name=\"running_service\",\n", - " ),\n", - " # This simple service is named \"running_service\"\n", - " NeverRunningService(),\n", - " # this service will be named \"NeverRunningService\"\n", - " ],\n", - " fully_concurrent=True,\n", - " # Makes components in the group run asynchronously,\n", - " # unless one is waiting for another to complete,\n", - " # which is what happens with NeverRunningService.\n", - " ),\n", - " RuntimeInfoPrintingService(),\n", - " ],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "b1ba3af0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:08.031271Z", - "iopub.status.busy": "2024-10-10T12:19:08.030883Z", - "iopub.status.idle": "2024-10-10T12:19:08.040634Z", - "shell.execute_reply": "2024-10-10T12:19:08.039994Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "Service 'SimpleService#0' is running...\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Service 'SimpleService#1' is running...\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Service 'running_service' is running...\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Service 'RuntimeInfoPrintingService' runtime execution info:{\n", - " \"before_handler\": {\n", - " \"functions\": [],\n", - " \"timeout\": null,\n", - " \"concurrent\": false\n", - " },\n", - " \"after_handler\": {\n", - " \"functions\": [],\n", - " \"timeout\": null,\n", - " \"concurrent\": false\n", - " },\n", - " \"timeout\": null,\n", - " \"concurrent\": false,\n", - " \"start_condition\": {\n", - " \"root\": true\n", - " },\n", - " \"name\": \"RuntimeInfoPrintingService\",\n", - " \"path\": \".post.RuntimeInfoPrintingService\",\n", - " \"handler\": null\n", - "}\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH[:1], printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.service.5_extra_handlers.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.service.5_extra_handlers.ipynb deleted file mode 100644 index 7b72b1533..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.service.5_extra_handlers.ipynb +++ /dev/null @@ -1,284 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ef1c50c1", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 5. Extra handlers\n", - "\n", - "This tutorial shows usage of extra handlers:\n", - "functions that run before/after components.\n", - "\n", - "For API ref, see:\n", - "\n", - "* [BeforeHandler](../apiref/chatsky.core.service.extra.rst#chatsky.core.service.extra.BeforeHandler)\n", - "* [AfterHandler](../apiref/chatsky.core.service.extra.rst#chatsky.core.service.extra.AfterHandler)." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a11d7d2c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:10.124788Z", - "iopub.status.busy": "2024-10-10T12:19:10.124383Z", - "iopub.status.idle": "2024-10-10T12:19:11.358591Z", - "shell.execute_reply": "2024-10-10T12:19:11.357762Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "73b3007d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:11.361165Z", - "iopub.status.busy": "2024-10-10T12:19:11.360745Z", - "iopub.status.idle": "2024-10-10T12:19:12.606244Z", - "shell.execute_reply": "2024-10-10T12:19:12.605624Z" - } - }, - "outputs": [], - "source": [ - "import asyncio\n", - "import json\n", - "import random\n", - "import logging\n", - "import sys\n", - "from importlib import reload\n", - "from datetime import datetime\n", - "\n", - "from chatsky.core.service import (\n", - " ServiceGroup,\n", - " ExtraHandlerRuntimeInfo,\n", - ")\n", - "from chatsky import Context, Pipeline\n", - "from chatsky.utils.testing.common import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")\n", - "from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS\n", - "\n", - "reload(logging)\n", - "logging.basicConfig(stream=sys.stdout, level=logging.INFO, format=\"\")\n", - "logger = logging.getLogger(__name__)\n", - "\n", - "random.seed(0)" - ] - }, - { - "cell_type": "markdown", - "id": "d45be6dd", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "## Intro\n", - "\n", - "Extra handlers are additional function\n", - "lists (before-functions and/or after-functions)\n", - "that can be added to any pipeline components (service and service groups).\n", - "\n", - "Extra handlers main purpose should be statistics collection.\n", - "\n", - "## Usage\n", - "\n", - "Extra handlers can be attached to pipeline component using\n", - "`before_handler` and `after_handler` constructor parameter.\n", - "\n", - "Extra handler callable signature can be one of the following:\n", - "`[ctx]` or `[ctx, info]`, where:\n", - "\n", - "* `ctx` - `Context` of the current dialog.\n", - "* `info` - Dictionary, containing information about current extra handler\n", - " and the pipeline component that called this.\n", - "\n", - " For example, `info.stage` will tell you if this Extra Handler is a\n", - " BeforeHandler or AfterHandler; `info.component.name` will give\n", - " you the component's name; `info.component.get_state(ctx)` will\n", - " return the component's execution state (which is `NOT_RUN` for\n", - " before handlers and `FINISHED` for after handlers).\n", - "\n", - "### Extra Handler configuration\n", - "\n", - "Instead of passing a list of functions as extra handler you can pass an instance\n", - "of either [BeforeHandler](../apiref/chatsky.core.service.extra.rst#chatsky.core.service.extra.BeforeHandler) or\n", - "[AfterHandler](../apiref/chatsky.core.service.extra.rst#chatsky.core.service.extra.AfterHandler).\n", - "\n", - "This allows changing the `timeout` and `concurrent` options to change\n", - "the way extra handlers are executed.\n", - "\n", - "### Mass extra handler addition\n", - "\n", - "You can use [add_extra_handler](../apiref/chatsky.core.service.group.rst#chatsky.core.service.group.ServiceGroup.add_extra_handler)\n", - "to add a function as an extra handler to a service group and if you pass\n", - "a `condition` function it will also add the extra handler to all its\n", - "subcomponents that satisfy the condition function.\n", - "\n", - "## Code explanation\n", - "\n", - "Here 5 `heavy_service`s are run in a single concurrent service group.\n", - "Each of them sleeps for random a amount of seconds (between 0 and 0.05).\n", - "\n", - "To each of them (as well as the group)\n", - "time measurement extra handler is attached,\n", - "that writes execution time to `ctx.misc`.\n", - "\n", - "In the end `ctx.misc` is logged to info channel." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "4948d48d", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:12.609113Z", - "iopub.status.busy": "2024-10-10T12:19:12.608544Z", - "iopub.status.idle": "2024-10-10T12:19:12.613593Z", - "shell.execute_reply": "2024-10-10T12:19:12.612954Z" - } - }, - "outputs": [], - "source": [ - "def collect_timestamp_before(ctx: Context, info: ExtraHandlerRuntimeInfo):\n", - " ctx.misc.update({f\"{info.component.path}\": datetime.now()})\n", - "\n", - "\n", - "def collect_timestamp_after(ctx: Context, info: ExtraHandlerRuntimeInfo):\n", - " ctx.misc.update(\n", - " {\n", - " f\"{info.component.path}\": datetime.now()\n", - " - ctx.misc[f\"{info.component.path}\"]\n", - " }\n", - " )\n", - "\n", - "\n", - "async def heavy_service(_):\n", - " await asyncio.sleep(random.randint(0, 5) / 100)\n", - "\n", - "\n", - "def logging_service(ctx: Context):\n", - " logger.info(f\"Context misc: {json.dumps(ctx.misc, indent=4, default=str)}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4855cd43", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:12.615732Z", - "iopub.status.busy": "2024-10-10T12:19:12.615296Z", - "iopub.status.idle": "2024-10-10T12:19:12.620028Z", - "shell.execute_reply": "2024-10-10T12:19:12.619428Z" - } - }, - "outputs": [], - "source": [ - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS,\n", - " pre_services=ServiceGroup(\n", - " before_handler=[collect_timestamp_before],\n", - " after_handler=[collect_timestamp_after],\n", - " components=[\n", - " {\n", - " \"handler\": heavy_service,\n", - " \"before_handler\": [collect_timestamp_before],\n", - " \"after_handler\": [collect_timestamp_after],\n", - " }\n", - " ]\n", - " * 5,\n", - " ),\n", - " post_services=[logging_service],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "d5386319", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:12.622049Z", - "iopub.status.busy": "2024-10-10T12:19:12.621679Z", - "iopub.status.idle": "2024-10-10T12:19:12.750915Z", - "shell.execute_reply": "2024-10-10T12:19:12.750280Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "Context misc: {\n", - " \".pre\": \"0:00:00.121890\",\n", - " \".pre.heavy_service#0\": \"0:00:00.030262\",\n", - " \".pre.heavy_service#1\": \"0:00:00.030243\",\n", - " \".pre.heavy_service#2\": \"0:00:00.000111\",\n", - " \".pre.heavy_service#3\": \"0:00:00.020226\",\n", - " \".pre.heavy_service#4\": \"0:00:00.040351\"\n", - "}\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH[:1], printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.slots.1_basic_example.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.slots.1_basic_example.ipynb deleted file mode 100644 index 3c6f13dda..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.slots.1_basic_example.ipynb +++ /dev/null @@ -1,369 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "9cb61450", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 1. Basic Example\n", - "\n", - "The following tutorial shows basic usage of slots extraction\n", - "module packaged with `chatsky`." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "f72d2bba", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:14.329167Z", - "iopub.status.busy": "2024-10-10T12:19:14.328955Z", - "iopub.status.idle": "2024-10-10T12:19:15.468596Z", - "shell.execute_reply": "2024-10-10T12:19:15.467789Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "8fe0155b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:15.471160Z", - "iopub.status.busy": "2024-10-10T12:19:15.470605Z", - "iopub.status.idle": "2024-10-10T12:19:16.601266Z", - "shell.execute_reply": "2024-10-10T12:19:16.600648Z" - } - }, - "outputs": [], - "source": [ - "from chatsky import (\n", - " RESPONSE,\n", - " TRANSITIONS,\n", - " PRE_TRANSITION,\n", - " PRE_RESPONSE,\n", - " GLOBAL,\n", - " LOCAL,\n", - " Pipeline,\n", - " Transition as Tr,\n", - " conditions as cnd,\n", - " processing as proc,\n", - " responses as rsp,\n", - ")\n", - "\n", - "from chatsky.slots import RegexpSlot\n", - "\n", - "from chatsky.utils.testing import (\n", - " check_happy_path,\n", - " is_interactive_mode,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "2df4048c", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "The slots fall into the following category groups:\n", - "\n", - "- Value slots can be used to extract slot values from user utterances.\n", - "- Group slots can be used to split value slots into groups\n", - " with an arbitrary level of nesting.\n", - "\n", - "You can build the slot tree by passing the child slot instances as extra fields\n", - "of the parent slot. In the following cell, we define two slot groups:\n", - "\n", - " Group 1: person.username, person.email\n", - " Group 2: friend.first_name, friend.last_name\n", - "\n", - "Currently there are two types of value slots:\n", - "\n", - "- [RegexpSlot](../apiref/chatsky.slots.slots.rst#chatsky.slots.slots.RegexpSlot):\n", - " Extracts slot values via regexp.\n", - "- [FunctionSlot](../apiref/chatsky.slots.slots.rst#chatsky.slots.slots.FunctionSlot):\n", - " Extracts slot values with the help of a user-defined function." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e4764140", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:16.604199Z", - "iopub.status.busy": "2024-10-10T12:19:16.603567Z", - "iopub.status.idle": "2024-10-10T12:19:16.607752Z", - "shell.execute_reply": "2024-10-10T12:19:16.607105Z" - } - }, - "outputs": [], - "source": [ - "SLOTS = {\n", - " \"person\": {\n", - " \"username\": RegexpSlot(\n", - " regexp=r\"username is ([a-zA-Z]+)\",\n", - " match_group_idx=1,\n", - " ),\n", - " \"email\": RegexpSlot(\n", - " regexp=r\"email is ([a-z@\\.A-Z]+)\",\n", - " match_group_idx=1,\n", - " ),\n", - " },\n", - " \"friend\": {\n", - " \"first_name\": RegexpSlot(regexp=r\"^[A-Z][a-z]+?(?= )\"),\n", - " \"last_name\": RegexpSlot(regexp=r\"(?<= )[A-Z][a-z]+\"),\n", - " },\n", - "}" - ] - }, - { - "cell_type": "markdown", - "id": "0f046a51", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "The slots module provides several functions for managing slots in-script:\n", - "\n", - "- [SlotsExtracted](../apiref/chatsky.conditions.slots.rst#chatsky.conditions.slots.SlotsExtracted):\n", - " Condition for checking if specified slots are extracted.\n", - "- [Extract](../apiref/chatsky.processing.slots.rst#chatsky.processing.slots.Extract):\n", - " A processing function that extracts specified slots.\n", - "- [Unset](../apiref/chatsky.processing.slots.rst#chatsky.processing.slots.Unset):\n", - " A processing function that marks specified slots as not extracted,\n", - " effectively resetting their state.\n", - "- [UnsetAll](../apiref/chatsky.processing.slots.rst#chatsky.processing.slots.UnsetAll):\n", - " A processing function that marks all slots as not extracted.\n", - "- [FillTemplate](../apiref/chatsky.processing.slots.rst#chatsky.processing.slots.FillTemplate):\n", - " A processing function that fills the `response`\n", - " Message text with extracted slot values.\n", - "- [FilledTemplate](../apiref/chatsky.responses.slots.rst#chatsky.responses.slots.FilledTemplate):\n", - " A response function that takes a Message with a\n", - " format-string text and returns Message\n", - " with its text string filled with extracted slot values.\n", - "\n", - "The usage of all the above functions is shown in the following script:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "00120f31", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:16.610079Z", - "iopub.status.busy": "2024-10-10T12:19:16.609587Z", - "iopub.status.idle": "2024-10-10T12:19:16.619739Z", - "shell.execute_reply": "2024-10-10T12:19:16.619060Z" - } - }, - "outputs": [], - "source": [ - "script = {\n", - " GLOBAL: {\n", - " TRANSITIONS: [\n", - " Tr(dst=(\"username_flow\", \"ask\"), cnd=cnd.Regexp(r\"^[sS]tart\"))\n", - " ]\n", - " },\n", - " \"username_flow\": {\n", - " LOCAL: {\n", - " PRE_TRANSITION: {\"get_slot\": proc.Extract(\"person.username\")},\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"email_flow\", \"ask\"),\n", - " cnd=cnd.SlotsExtracted(\"person.username\"),\n", - " priority=1.2,\n", - " ),\n", - " Tr(dst=(\"username_flow\", \"repeat_question\"), priority=0.8),\n", - " ],\n", - " },\n", - " \"ask\": {\n", - " RESPONSE: \"Write your username (my username is ...):\",\n", - " },\n", - " \"repeat_question\": {\n", - " RESPONSE: \"Please, type your username again (my username is ...):\",\n", - " },\n", - " },\n", - " \"email_flow\": {\n", - " LOCAL: {\n", - " PRE_TRANSITION: {\"get_slot\": proc.Extract(\"person.email\")},\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"friend_flow\", \"ask\"),\n", - " cnd=cnd.SlotsExtracted(\"person.username\", \"person.email\"),\n", - " priority=1.2,\n", - " ),\n", - " Tr(dst=(\"email_flow\", \"repeat_question\"), priority=0.8),\n", - " ],\n", - " },\n", - " \"ask\": {\n", - " RESPONSE: \"Write your email (my email is ...):\",\n", - " },\n", - " \"repeat_question\": {\n", - " RESPONSE: \"Please, write your email again (my email is ...):\",\n", - " },\n", - " },\n", - " \"friend_flow\": {\n", - " LOCAL: {\n", - " PRE_TRANSITION: {\"get_slots\": proc.Extract(\"friend\")},\n", - " TRANSITIONS: [\n", - " Tr(\n", - " dst=(\"root\", \"utter\"),\n", - " cnd=cnd.SlotsExtracted(\n", - " \"friend.first_name\", \"friend.last_name\", mode=\"any\"\n", - " ),\n", - " priority=1.2,\n", - " ),\n", - " Tr(dst=(\"friend_flow\", \"repeat_question\"), priority=0.8),\n", - " ],\n", - " },\n", - " \"ask\": {RESPONSE: \"Please, name me one of your friends: (John Doe)\"},\n", - " \"repeat_question\": {\n", - " RESPONSE: \"Please, name me one of your friends again: (John Doe)\"\n", - " },\n", - " },\n", - " \"root\": {\n", - " \"start\": {\n", - " TRANSITIONS: [Tr(dst=(\"username_flow\", \"ask\"))],\n", - " },\n", - " \"fallback\": {\n", - " RESPONSE: \"Finishing query\",\n", - " TRANSITIONS: [Tr(dst=(\"username_flow\", \"ask\"))],\n", - " },\n", - " \"utter\": {\n", - " RESPONSE: rsp.FilledTemplate(\n", - " \"Your friend is {friend.first_name} {friend.last_name}\"\n", - " ),\n", - " TRANSITIONS: [Tr(dst=(\"root\", \"utter_alternative\"))],\n", - " },\n", - " \"utter_alternative\": {\n", - " RESPONSE: \"Your username is {person.username}. \"\n", - " \"Your email is {person.email}.\",\n", - " PRE_RESPONSE: {\"fill\": proc.FillTemplate()},\n", - " },\n", - " },\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f715382f", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:16.621935Z", - "iopub.status.busy": "2024-10-10T12:19:16.621462Z", - "iopub.status.idle": "2024-10-10T12:19:16.624981Z", - "shell.execute_reply": "2024-10-10T12:19:16.624364Z" - } - }, - "outputs": [], - "source": [ - "HAPPY_PATH = [\n", - " (\"hi\", \"Write your username (my username is ...):\"),\n", - " (\"my username is groot\", \"Write your email (my email is ...):\"),\n", - " (\n", - " \"my email is groot@gmail.com\",\n", - " \"Please, name me one of your friends: (John Doe)\",\n", - " ),\n", - " (\"Bob Page\", \"Your friend is Bob Page\"),\n", - " (\"ok\", \"Your username is groot. Your email is groot@gmail.com.\"),\n", - " (\"ok\", \"Finishing query\"),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "f6d16cf5", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:16.627044Z", - "iopub.status.busy": "2024-10-10T12:19:16.626640Z", - "iopub.status.idle": "2024-10-10T12:19:16.642342Z", - "shell.execute_reply": "2024-10-10T12:19:16.641725Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='hi'\n", - "BOT : text='Write your username (my username is ...):'\n", - "USER: text='my username is groot'\n", - "BOT : text='Write your email (my email is ...):'\n", - "USER: text='my email is groot@gmail.com'\n", - "BOT : text='Please, name me one of your friends: (John Doe)'\n", - "USER: text='Bob Page'\n", - "BOT : text='Your friend is Bob Page'\n", - "USER: text='ok'\n", - "BOT : text='Your username is groot. Your email is groot@gmail.com.'\n", - "USER: text='ok'\n", - "BOT : text='Finishing query'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " script=script,\n", - " start_label=(\"root\", \"start\"),\n", - " fallback_label=(\"root\", \"fallback\"),\n", - " slots=SLOTS,\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(\n", - " pipeline, HAPPY_PATH, printout=True\n", - " ) # This is a function for automatic tutorial running\n", - " # (testing) with HAPPY_PATH\n", - "\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.stats.1_extractor_functions.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.stats.1_extractor_functions.ipynb deleted file mode 100644 index 5f33145b7..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.stats.1_extractor_functions.ipynb +++ /dev/null @@ -1,278 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cb30cf28", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 1. Extractor Functions\n", - "\n", - "The following example covers the basics of using the `stats` module.\n", - "\n", - "Statistics are collected from pipeline services by extractor functions\n", - "that report the state of one or more pipeline components. The `stats` module\n", - "provides several default extractors, but users are free to define their own\n", - "extractor functions. You can find API reference for default extractors\n", - "[here](../apiref/chatsky.stats.default_extractors.rst).\n", - "\n", - "It is a preferred practice to define extractors as asynchronous functions.\n", - "Extractors need to have the following uniform signature:\n", - "the expected arguments are always `Context`, and `ExtraHandlerRuntimeInfo`,\n", - "while the expected return value is an arbitrary `dict` or a `None`.\n", - "The returned value gets persisted to Clickhouse as JSON\n", - "which is why it can contain arbitrarily nested dictionaries,\n", - "but it cannot contain any complex objects that cannot be trivially serialized.\n", - "\n", - "The output of these functions will be captured by an OpenTelemetry\n", - "instrumentor and directed to the Opentelemetry collector server which\n", - "in its turn batches and persists data to Clickhouse or other OLAP storages.\n", - "\n", - "
\n", - "\n", - "Both the Opentelemetry collector and the Clickhouse instance must be running\n", - "during statistics collection.\n", - "If you cloned the Chatsky repo, launch them using `docker compose`:\n", - "```bash\n", - "docker compose --profile stats up\n", - "```\n", - "\n", - "
\n", - "\n", - "For more information on OpenTelemetry instrumentation,\n", - "refer to the body of this tutorial as well as [OpenTelemetry documentation](\n", - "https://opentelemetry.io/docs/instrumentation/python/manual/\n", - ")." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a4e779aa", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:18.277701Z", - "iopub.status.busy": "2024-10-10T12:19:18.277274Z", - "iopub.status.idle": "2024-10-10T12:19:19.459584Z", - "shell.execute_reply": "2024-10-10T12:19:19.458876Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[stats]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "5f450f9f", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:19.462250Z", - "iopub.status.busy": "2024-10-10T12:19:19.461840Z", - "iopub.status.idle": "2024-10-10T12:19:20.734334Z", - "shell.execute_reply": "2024-10-10T12:19:20.733633Z" - } - }, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "from chatsky.core.service import (\n", - " ExtraHandlerRuntimeInfo,\n", - " ExtraHandlerType,\n", - " to_service,\n", - ")\n", - "from chatsky import Context, Pipeline\n", - "from chatsky.stats import OtelInstrumentor, default_extractors\n", - "from chatsky.utils.testing import is_interactive_mode, check_happy_path\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "markdown", - "id": "e00d8705", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "The cells below configure log export with the help of OTLP instrumentation.\n", - "\n", - "* The initial step is to configure the export destination.\n", - "`from_url` method of the `OtelInstrumentor` class simplifies this task\n", - "allowing you to only pass the url of the OTLP Collector server.\n", - "\n", - "* Alternatively, you can use the utility functions\n", - "provided by the `stats` module:\n", - "`set_logger_destination`, `set_tracer_destination`, or `set_meter_destination`.\n", - "These accept an appropriate Opentelemetry exporter instance\n", - "and bind it to provider classes.\n", - "\n", - "* Nextly, the `OtelInstrumentor` class should be constructed to log the output\n", - "of extractor functions. Custom extractors need to be decorated\n", - "with the `OtelInstrumentor` instance. Default extractors are instrumented\n", - "by calling the `instrument` method.\n", - "\n", - "* The entirety of the process is illustrated in the example below." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "145604e4", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:20.737209Z", - "iopub.status.busy": "2024-10-10T12:19:20.736597Z", - "iopub.status.idle": "2024-10-10T12:19:20.743767Z", - "shell.execute_reply": "2024-10-10T12:19:20.743206Z" - } - }, - "outputs": [], - "source": [ - "chatsky_instrumentor = OtelInstrumentor.from_url(\"grpc://localhost:4317\")\n", - "chatsky_instrumentor.instrument()" - ] - }, - { - "cell_type": "markdown", - "id": "206e4da8", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 2 - }, - "source": [ - "The following cell shows a custom extractor function. The data obtained from\n", - "the context and the runtime information gets shaped as a dict and returned\n", - "from the function body. The `chatsky_instrumentor` decorator then ensures\n", - "that the output is logged by OpenTelemetry." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "9df46ff7", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:20.745922Z", - "iopub.status.busy": "2024-10-10T12:19:20.745528Z", - "iopub.status.idle": "2024-10-10T12:19:20.749068Z", - "shell.execute_reply": "2024-10-10T12:19:20.748474Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# decorated by an OTLP Instrumentor instance\n", - "@chatsky_instrumentor\n", - "async def get_service_state(ctx: Context, info: ExtraHandlerRuntimeInfo):\n", - " # extract the execution state of a target service\n", - " data = {\n", - " \"execution_state\": info.component.get_state(ctx),\n", - " }\n", - " # return the state as an arbitrary dict for further logging\n", - " return data" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "bacb3e0c", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:20.751118Z", - "iopub.status.busy": "2024-10-10T12:19:20.750724Z", - "iopub.status.idle": "2024-10-10T12:19:20.754322Z", - "shell.execute_reply": "2024-10-10T12:19:20.753742Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# configure `get_service_state` to run after the `heavy_service`\n", - "@to_service(after_handler=[get_service_state])\n", - "async def heavy_service(ctx: Context):\n", - " _ = ctx # get something from ctx if needed\n", - " await asyncio.sleep(0.02)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "854bbbdd", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:20.756252Z", - "iopub.status.busy": "2024-10-10T12:19:20.756024Z", - "iopub.status.idle": "2024-10-10T12:19:20.869683Z", - "shell.execute_reply": "2024-10-10T12:19:20.869125Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(**TOY_SCRIPT_KWARGS, pre_services=[heavy_service])\n", - "\n", - "pipeline.actor.add_extra_handler(\n", - " ExtraHandlerType.BEFORE, default_extractors.get_current_label\n", - ")\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/nbsphinx/tutorials/tutorials.stats.2_pipeline_integration.ipynb b/.doctrees/nbsphinx/tutorials/tutorials.stats.2_pipeline_integration.ipynb deleted file mode 100644 index 7f8260da4..000000000 --- a/.doctrees/nbsphinx/tutorials/tutorials.stats.2_pipeline_integration.ipynb +++ /dev/null @@ -1,279 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ce85673d", - "metadata": { - "cell_marker": "\"\"\"" - }, - "source": [ - "# 2. Pipeline Integration\n", - "\n", - "In the Chatsky ecosystem, extractor functions act as regular extra handlers (\n", - "[see the extra handlers tutorial](\n", - "../tutorials/tutorials.service.5_extra_handlers.py\n", - ")\n", - ").\n", - "Hence, you can decorate any part of your pipeline, including services,\n", - "service groups and the pipeline as a whole, to obtain the statistics\n", - "specific for that component. Some examples of this functionality\n", - "are showcased in this tutorial.\n", - "\n", - "
\n", - "\n", - "Both the Opentelemetry collector and the Clickhouse instance must be running\n", - "during statistics collection.\n", - "If you cloned the Chatsky repo, launch them using `docker compose`:\n", - "```bash\n", - "docker compose --profile stats up\n", - "```\n", - "\n", - "
" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "3a2a31b0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:22.692484Z", - "iopub.status.busy": "2024-10-10T12:19:22.691855Z", - "iopub.status.idle": "2024-10-10T12:19:23.890846Z", - "shell.execute_reply": "2024-10-10T12:19:23.890070Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "# installing dependencies\n", - "%pip install -q chatsky[stats]" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "d4c4886b", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:23.893290Z", - "iopub.status.busy": "2024-10-10T12:19:23.892902Z", - "iopub.status.idle": "2024-10-10T12:19:25.201094Z", - "shell.execute_reply": "2024-10-10T12:19:25.200449Z" - } - }, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "from chatsky.core.service import (\n", - " ExtraHandlerRuntimeInfo,\n", - " ServiceGroup,\n", - " ExtraHandlerType,\n", - ")\n", - "from chatsky import Context, Pipeline\n", - "from chatsky.stats import OTLPLogExporter, OTLPSpanExporter\n", - "from chatsky.stats import (\n", - " OtelInstrumentor,\n", - " set_logger_destination,\n", - " set_tracer_destination,\n", - ")\n", - "from chatsky.stats import default_extractors\n", - "from chatsky.utils.testing import is_interactive_mode, check_happy_path\n", - "from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "58c13de0", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:25.203951Z", - "iopub.status.busy": "2024-10-10T12:19:25.203321Z", - "iopub.status.idle": "2024-10-10T12:19:25.211358Z", - "shell.execute_reply": "2024-10-10T12:19:25.210702Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "set_logger_destination(OTLPLogExporter(\"grpc://localhost:4317\", insecure=True))\n", - "set_tracer_destination(OTLPSpanExporter(\"grpc://localhost:4317\", insecure=True))\n", - "chatsky_instrumentor = OtelInstrumentor()\n", - "chatsky_instrumentor.instrument()\n", - "\n", - "\n", - "# example extractor function\n", - "@chatsky_instrumentor\n", - "async def get_service_state(ctx: Context, info: ExtraHandlerRuntimeInfo):\n", - " # extract execution state of service from info\n", - " data = {\n", - " \"execution_state\": info.component.get_state(ctx),\n", - " }\n", - " # return a record to save into connected database\n", - " return data" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "64437061", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:25.213629Z", - "iopub.status.busy": "2024-10-10T12:19:25.213145Z", - "iopub.status.idle": "2024-10-10T12:19:25.216611Z", - "shell.execute_reply": "2024-10-10T12:19:25.215868Z" - }, - "lines_to_next_cell": 2 - }, - "outputs": [], - "source": [ - "# example service\n", - "async def heavy_service(ctx: Context):\n", - " _ = ctx # get something from ctx if needed\n", - " await asyncio.sleep(0.02)" - ] - }, - { - "cell_type": "markdown", - "id": "02cad09d", - "metadata": { - "cell_marker": "\"\"\"", - "lines_to_next_cell": 0 - }, - "source": [ - "\n", - "The many ways in which you can use extractor functions are shown in\n", - "the following pipeline definition. The functions are used to obtain\n", - "statistics from respective components:\n", - "\n", - "* A service group of two `heavy_service` instances.\n", - "* An `Actor` service.\n", - "* The pipeline as a whole.\n", - "\n", - "As is the case with the regular extra handler functions,\n", - "you can wire the extractors to run either before or after the target service.\n", - "As a result, you can compare the pre-service and post-service states\n", - "of the context to measure the performance of various components, etc.\n", - "\n", - "Some extractors, like `get_current_label`, have restrictions in terms of their\n", - "run stage: for instance, `get_current_label` needs to only be used as an\n", - "`after_handler` to function correctly." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "b1ec971e", - "metadata": { - "execution": { - "iopub.execute_input": "2024-10-10T12:19:25.218665Z", - "iopub.status.busy": "2024-10-10T12:19:25.218295Z", - "iopub.status.idle": "2024-10-10T12:19:25.440197Z", - "shell.execute_reply": "2024-10-10T12:19:25.439615Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "USER: text='Hi'\n", - "BOT : text='Hi, how are you?'\n", - "USER: text='i'm fine, how are you?'\n", - "BOT : text='Good. What do you want to talk about?'\n", - "USER: text='Let's talk about music.'\n", - "BOT : text='Sorry, I can not talk about music now.'\n", - "USER: text='Ok, goodbye.'\n", - "BOT : text='bye'\n", - "USER: text='Hi'\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "BOT : text='Hi, how are you?'\n" - ] - } - ], - "source": [ - "pipeline = Pipeline(\n", - " **TOY_SCRIPT_KWARGS,\n", - " pre_services=ServiceGroup(\n", - " before_handler=[default_extractors.get_timing_before],\n", - " after_handler=[\n", - " get_service_state,\n", - " default_extractors.get_timing_after,\n", - " ],\n", - " components=[\n", - " heavy_service,\n", - " heavy_service,\n", - " ],\n", - " ),\n", - ")\n", - "# These are Extra Handlers for Actor.\n", - "pipeline.actor.add_extra_handler(\n", - " ExtraHandlerType.BEFORE, default_extractors.get_timing_before\n", - ")\n", - "pipeline.actor.add_extra_handler(ExtraHandlerType.AFTER, get_service_state)\n", - "pipeline.actor.add_extra_handler(\n", - " ExtraHandlerType.AFTER, default_extractors.get_current_label\n", - ")\n", - "pipeline.actor.add_extra_handler(\n", - " ExtraHandlerType.AFTER, default_extractors.get_timing_after\n", - ")\n", - "\n", - "# These are global Extra Handlers for Pipeline service\n", - "pipeline.services_pipeline.add_extra_handler(\n", - " ExtraHandlerType.BEFORE, default_extractors.get_timing_before\n", - ")\n", - "pipeline.services_pipeline.add_extra_handler(\n", - " ExtraHandlerType.AFTER, default_extractors.get_timing_after\n", - ")\n", - "pipeline.services_pipeline.add_extra_handler(\n", - " ExtraHandlerType.AFTER, get_service_state\n", - ")\n", - "\n", - "if __name__ == \"__main__\":\n", - " check_happy_path(pipeline, HAPPY_PATH, printout=True)\n", - " if is_interactive_mode():\n", - " pipeline.run()" - ] - } - ], - "metadata": { - "jupytext": { - "cell_metadata_filter": "-all", - "main_language": "python", - "notebook_metadata_filter": "-all", - "text_representation": { - "extension": ".py", - "format_name": "percent" - } - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.20" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.doctrees/reference.doctree b/.doctrees/reference.doctree deleted file mode 100644 index c948e2b54..000000000 Binary files a/.doctrees/reference.doctree and /dev/null differ diff --git a/.doctrees/tutorials.doctree b/.doctrees/tutorials.doctree deleted file mode 100644 index 990224c17..000000000 Binary files a/.doctrees/tutorials.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_context_storages.doctree b/.doctrees/tutorials/index_context_storages.doctree deleted file mode 100644 index 06b926554..000000000 Binary files a/.doctrees/tutorials/index_context_storages.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_interfaces.doctree b/.doctrees/tutorials/index_interfaces.doctree deleted file mode 100644 index d4ebcfaff..000000000 Binary files a/.doctrees/tutorials/index_interfaces.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_script.doctree b/.doctrees/tutorials/index_script.doctree deleted file mode 100644 index 46a44c8a1..000000000 Binary files a/.doctrees/tutorials/index_script.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_service.doctree b/.doctrees/tutorials/index_service.doctree deleted file mode 100644 index ccbd8499b..000000000 Binary files a/.doctrees/tutorials/index_service.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_slots.doctree b/.doctrees/tutorials/index_slots.doctree deleted file mode 100644 index b5ee25a13..000000000 Binary files a/.doctrees/tutorials/index_slots.doctree and /dev/null differ diff --git a/.doctrees/tutorials/index_stats.doctree b/.doctrees/tutorials/index_stats.doctree deleted file mode 100644 index 020d1183a..000000000 Binary files a/.doctrees/tutorials/index_stats.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.1_basics.doctree b/.doctrees/tutorials/tutorials.context_storages.1_basics.doctree deleted file mode 100644 index 4e685e860..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.1_basics.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.2_postgresql.doctree b/.doctrees/tutorials/tutorials.context_storages.2_postgresql.doctree deleted file mode 100644 index cde477da7..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.2_postgresql.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.3_mongodb.doctree b/.doctrees/tutorials/tutorials.context_storages.3_mongodb.doctree deleted file mode 100644 index 7289e4874..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.3_mongodb.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.4_redis.doctree b/.doctrees/tutorials/tutorials.context_storages.4_redis.doctree deleted file mode 100644 index 00a399534..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.4_redis.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.5_mysql.doctree b/.doctrees/tutorials/tutorials.context_storages.5_mysql.doctree deleted file mode 100644 index 6c6bcbe95..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.5_mysql.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.6_sqlite.doctree b/.doctrees/tutorials/tutorials.context_storages.6_sqlite.doctree deleted file mode 100644 index 7e7d30b4a..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.6_sqlite.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.7_yandex_database.doctree b/.doctrees/tutorials/tutorials.context_storages.7_yandex_database.doctree deleted file mode 100644 index 42b51b4d5..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.7_yandex_database.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.context_storages.8_db_benchmarking.doctree b/.doctrees/tutorials/tutorials.context_storages.8_db_benchmarking.doctree deleted file mode 100644 index e4f397469..000000000 Binary files a/.doctrees/tutorials/tutorials.context_storages.8_db_benchmarking.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.telegram.1_basic.doctree b/.doctrees/tutorials/tutorials.messengers.telegram.1_basic.doctree deleted file mode 100644 index e700ffde5..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.telegram.1_basic.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.telegram.2_attachments.doctree b/.doctrees/tutorials/tutorials.messengers.telegram.2_attachments.doctree deleted file mode 100644 index 31991f169..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.telegram.2_attachments.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.telegram.3_advanced.doctree b/.doctrees/tutorials/tutorials.messengers.telegram.3_advanced.doctree deleted file mode 100644 index a74d77ae7..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.telegram.3_advanced.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.web_api_interface.1_fastapi.doctree b/.doctrees/tutorials/tutorials.messengers.web_api_interface.1_fastapi.doctree deleted file mode 100644 index 9fbe222d0..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.web_api_interface.1_fastapi.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.doctree b/.doctrees/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.doctree deleted file mode 100644 index eb57b4e18..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.doctree b/.doctrees/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.doctree deleted file mode 100644 index 611f0cbe2..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.doctree b/.doctrees/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.doctree deleted file mode 100644 index 37526f4e0..000000000 Binary files a/.doctrees/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.1_basics.doctree b/.doctrees/tutorials/tutorials.script.core.1_basics.doctree deleted file mode 100644 index 8cc3ced37..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.1_basics.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.2_conditions.doctree b/.doctrees/tutorials/tutorials.script.core.2_conditions.doctree deleted file mode 100644 index 2d6fe158c..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.2_conditions.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.3_responses.doctree b/.doctrees/tutorials/tutorials.script.core.3_responses.doctree deleted file mode 100644 index 79c1f231b..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.3_responses.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.4_transitions.doctree b/.doctrees/tutorials/tutorials.script.core.4_transitions.doctree deleted file mode 100644 index 355cbf8b6..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.4_transitions.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.5_global_local.doctree b/.doctrees/tutorials/tutorials.script.core.5_global_local.doctree deleted file mode 100644 index 24ad2131b..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.5_global_local.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.6_context_serialization.doctree b/.doctrees/tutorials/tutorials.script.core.6_context_serialization.doctree deleted file mode 100644 index cc9d97818..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.6_context_serialization.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.7_pre_response_processing.doctree b/.doctrees/tutorials/tutorials.script.core.7_pre_response_processing.doctree deleted file mode 100644 index 666f99f87..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.7_pre_response_processing.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.8_misc.doctree b/.doctrees/tutorials/tutorials.script.core.8_misc.doctree deleted file mode 100644 index 6602aace7..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.8_misc.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.core.9_pre_transition_processing.doctree b/.doctrees/tutorials/tutorials.script.core.9_pre_transition_processing.doctree deleted file mode 100644 index e79ddd24a..000000000 Binary files a/.doctrees/tutorials/tutorials.script.core.9_pre_transition_processing.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.responses.1_media.doctree b/.doctrees/tutorials/tutorials.script.responses.1_media.doctree deleted file mode 100644 index 2e54eb412..000000000 Binary files a/.doctrees/tutorials/tutorials.script.responses.1_media.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.script.responses.2_multi_message.doctree b/.doctrees/tutorials/tutorials.script.responses.2_multi_message.doctree deleted file mode 100644 index c9f14387d..000000000 Binary files a/.doctrees/tutorials/tutorials.script.responses.2_multi_message.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.service.1_basics.doctree b/.doctrees/tutorials/tutorials.service.1_basics.doctree deleted file mode 100644 index d9cfb15c4..000000000 Binary files a/.doctrees/tutorials/tutorials.service.1_basics.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.service.2_advanced.doctree b/.doctrees/tutorials/tutorials.service.2_advanced.doctree deleted file mode 100644 index f767d9bee..000000000 Binary files a/.doctrees/tutorials/tutorials.service.2_advanced.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.service.3_groups.doctree b/.doctrees/tutorials/tutorials.service.3_groups.doctree deleted file mode 100644 index 1a23c5f28..000000000 Binary files a/.doctrees/tutorials/tutorials.service.3_groups.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.service.4_conditions_and_paths.doctree b/.doctrees/tutorials/tutorials.service.4_conditions_and_paths.doctree deleted file mode 100644 index 7ec4d1755..000000000 Binary files a/.doctrees/tutorials/tutorials.service.4_conditions_and_paths.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.service.5_extra_handlers.doctree b/.doctrees/tutorials/tutorials.service.5_extra_handlers.doctree deleted file mode 100644 index 07c222373..000000000 Binary files a/.doctrees/tutorials/tutorials.service.5_extra_handlers.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.slots.1_basic_example.doctree b/.doctrees/tutorials/tutorials.slots.1_basic_example.doctree deleted file mode 100644 index c6672b161..000000000 Binary files a/.doctrees/tutorials/tutorials.slots.1_basic_example.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.stats.1_extractor_functions.doctree b/.doctrees/tutorials/tutorials.stats.1_extractor_functions.doctree deleted file mode 100644 index 1e121f5ab..000000000 Binary files a/.doctrees/tutorials/tutorials.stats.1_extractor_functions.doctree and /dev/null differ diff --git a/.doctrees/tutorials/tutorials.stats.2_pipeline_integration.doctree b/.doctrees/tutorials/tutorials.stats.2_pipeline_integration.doctree deleted file mode 100644 index 15cfed654..000000000 Binary files a/.doctrees/tutorials/tutorials.stats.2_pipeline_integration.doctree and /dev/null differ diff --git a/.doctrees/user_guides.doctree b/.doctrees/user_guides.doctree deleted file mode 100644 index b60bbafc7..000000000 Binary files a/.doctrees/user_guides.doctree and /dev/null differ diff --git a/.doctrees/user_guides/basic_conceptions.doctree b/.doctrees/user_guides/basic_conceptions.doctree deleted file mode 100644 index 0c478af00..000000000 Binary files a/.doctrees/user_guides/basic_conceptions.doctree and /dev/null differ diff --git a/.doctrees/user_guides/context_guide.doctree b/.doctrees/user_guides/context_guide.doctree deleted file mode 100644 index 15ab8f01a..000000000 Binary files a/.doctrees/user_guides/context_guide.doctree and /dev/null differ diff --git a/.doctrees/user_guides/optimization_guide.doctree b/.doctrees/user_guides/optimization_guide.doctree deleted file mode 100644 index 7490707e8..000000000 Binary files a/.doctrees/user_guides/optimization_guide.doctree and /dev/null differ diff --git a/.doctrees/user_guides/pipeline_import.doctree b/.doctrees/user_guides/pipeline_import.doctree deleted file mode 100644 index dad63bc6a..000000000 Binary files a/.doctrees/user_guides/pipeline_import.doctree and /dev/null differ diff --git a/.doctrees/user_guides/slot_extraction.doctree b/.doctrees/user_guides/slot_extraction.doctree deleted file mode 100644 index 731033c39..000000000 Binary files a/.doctrees/user_guides/slot_extraction.doctree and /dev/null differ diff --git a/.doctrees/user_guides/superset_guide.doctree b/.doctrees/user_guides/superset_guide.doctree deleted file mode 100644 index 6623f7ec3..000000000 Binary files a/.doctrees/user_guides/superset_guide.doctree and /dev/null differ diff --git a/404.html b/404.html new file mode 100644 index 000000000..80bce47f7 --- /dev/null +++ b/404.html @@ -0,0 +1,37 @@ + + + + + + Error 404 :( + + + + + + Page not found + + + diff --git a/_images/annotations.png b/_images/annotations.png deleted file mode 100644 index b1e524890..000000000 Binary files a/_images/annotations.png and /dev/null differ diff --git a/_images/benchmark_compare.png b/_images/benchmark_compare.png deleted file mode 100644 index 8765ed48c..000000000 Binary files a/_images/benchmark_compare.png and /dev/null differ diff --git a/_images/benchmark_mass_compare.png b/_images/benchmark_mass_compare.png deleted file mode 100644 index 71911c554..000000000 Binary files a/_images/benchmark_mass_compare.png and /dev/null differ diff --git a/_images/benchmark_sets.png b/_images/benchmark_sets.png deleted file mode 100644 index 4eadd2873..000000000 Binary files a/_images/benchmark_sets.png and /dev/null differ diff --git a/_images/benchmark_view.png b/_images/benchmark_view.png deleted file mode 100644 index c8a5afc81..000000000 Binary files a/_images/benchmark_view.png and /dev/null differ diff --git a/_images/databases.png b/_images/databases.png deleted file mode 100644 index 167c4ce61..000000000 Binary files a/_images/databases.png and /dev/null differ diff --git a/_images/general_stats.png b/_images/general_stats.png deleted file mode 100644 index 660f2ed89..000000000 Binary files a/_images/general_stats.png and /dev/null differ diff --git a/_images/overview.png b/_images/overview.png deleted file mode 100644 index 5bd0fb6a1..000000000 Binary files a/_images/overview.png and /dev/null differ diff --git a/_images/service_stats.png b/_images/service_stats.png deleted file mode 100644 index 7ce724286..000000000 Binary files a/_images/service_stats.png and /dev/null differ diff --git a/_misc/benchmark_schema.json b/_misc/benchmark_schema.json deleted file mode 100644 index f4ecfcf40..000000000 --- a/_misc/benchmark_schema.json +++ /dev/null @@ -1,184 +0,0 @@ -{ - "title": "Benchmark set", - "description": "A structure containing results of multiple benchmarks", - "type": "object", - "properties": { - "name": { - "description": "Name of the benchmark set", - "type": "string" - }, - "description": { - "description": "Description of the benchmark set", - "type": "string" - }, - "uuid": { - "description": "Unique id of the benchmark set", - "type": "string" - }, - "benchmarks": { - "description": "A list of benchmarks in the set with results", - "type": "array", - "items": { - "title": "Benchmark", - "description": "A singular benchmark with results", - "type": "object", - "properties": { - "name": { - "description": "Name of the benchmark", - "type": "string" - }, - "description": { - "description": "Description of the benchmark", - "type": "string" - }, - "uuid": { - "description": "Unique id of the benchmark", - "type": "string" - }, - "success": { - "description": "Whether benchmark process did not raise an exception", - "type": "boolean" - }, - "db_factory": { - "description": "Configuration of the context storage that was benchmarked", - "type": "object", - "properties": { - "uri": { - "description": "URI of the context storage", - "type": "string" - }, - "factory_module": { - "description": "A module containing context storage factory", - "type": "string" - }, - "factory": { - "description": "Name of a context storage factory inside the module", - "type": "string" - } - }, - "required": ["uri", "factory_module", "factory"] - }, - "benchmark_config": { - "description": "Configuration of the benchmark", - "type": "object" - }, - "result": { - "description": "Raw benchmark results or exception info", - "oneOf": [ - { - "type": "object", - "properties": { - "write_times": { - "description": "List of write times; list index corresponds to context_num", - "type": "array", - "items": {"type": "number", "minimum": 0} - }, - "read_times": { - "description": "List of read times w.r.t. dialog_len; list index corresponds to context_num", - "type": "array", - "items": { - "type": "object", - "description": "Dictionary in which keys are equal to dialog_len of a context and values to read time of a context" - } - }, - "update_times": { - "description": "List of update times w.r.t. dialog_len; list index corresponds to context_num", - "type": "array", - "items": { - "type": "object", - "description": "Dictionary in which keys are equal to dialog_len of a context and values to update time of a context" - } - } - }, - "required": ["write_times", "read_times", "update_times"] - }, - { - "type": "object", - "properties": { - "type": { - "description": "Class name of the exception", - "type": "string" - }, - "msg": { - "description": "Exception message", - "type": "string" - }, - "traceback": { - "description": "String representation of exception traceback", - "type": "string" - } - }, - "required": ["type", "msg", "traceback"] - } - ] - - }, - "average_results": { - "description": "Calculated average statistics for benchmark results", - "type": "object", - "properties": { - "average_write_time": {"type": "number"}, - "average_read_time": {"type": "number"}, - "average_update_time": {"type": "number"}, - "read_times_grouped_by_context_num": { - "description": "List of read times w.r.t. context_num (each number is an average read time for that context_num)", - "type": "array", - "items": {"type": "number", "minimum": 0} - }, - "read_times_grouped_by_dialog_len": { - "description": "Mapping from dialog_len to read times (each value is an average read time for that dialog_len)", - "type": "object", - "items": {"type": "number", "minimum": 0} - }, - "update_times_grouped_by_context_num": { - "description": "List of update times w.r.t. context_num (each number is an average update time for that context_num)", - "type": "array", - "items": {"type": "number", "minimum": 0} - }, - "update_times_grouped_by_dialog_len": { - "description": "Mapping from dialog_len to update times (each value is an average update time for that dialog_len)", - "type": "object", - "items": {"type": "number", "minimum": 0} - }, - "pretty_write": { - "description": "Average write time with 3 significant digits", - "type": "number", - "minimum": 0 - }, - "pretty_read": { - "description": "Average read time with 3 significant digits", - "type": "number", - "minimum": 0 - }, - "pretty_update": { - "description": "Average update time with 3 significant digits", - "type": "number", - "minimum": 0 - }, - "pretty_read+update": { - "description": "Sum of average read and update times with 3 significant digits", - "type": "number", - "minimum": 0 - } - }, - "required": [ - "average_write_time", - "average_read_time", - "average_update_time", - "read_times_grouped_by_context_num", - "read_times_grouped_by_dialog_len", - "update_times_grouped_by_context_num", - "update_times_grouped_by_dialog_len", - "pretty_write", - "pretty_read", - "pretty_update", - "pretty_read+update" - ] - } - }, - "required": ["name", "description", "uuid", "success", "db_factory", "benchmark_config", "result"] - } - } - }, - "required": ["name", "description", "uuid", "benchmarks"] -} \ No newline at end of file diff --git a/_misc/benchmark_streamlit.py b/_misc/benchmark_streamlit.py deleted file mode 100644 index 3b760c8b1..000000000 --- a/_misc/benchmark_streamlit.py +++ /dev/null @@ -1,406 +0,0 @@ -""" -Benchmark Streamlit -------------------- -This module provides a streamlit interface to view benchmark results. -The interface has 4 tabs: - -- "Benchmark sets" -- Here you can add or remove benchmark set files (generated by `save_results_to_file` function). -- "View" -- Here you can view various stats for each benchmark in the set (as well as add benchmarks to compare tab). -- "Compare" -- Here you can compare benchmarks added via "View" tab. -- "Mass compare" -- This tab lets you compare all benchmarks in a particular set. - -Run this file with `streamlit run {path/to/this/file}`. - -When run, this module will search for a file named "benchmark_results_files.json" in the directory -where the command above was executed. -If the file does not exist there, it will be created. -The file is used to store paths to benchmark result files. - -Benchmark result files added via this module are not changed (only read). - -You can install all the dependencies of this module with -``` -pip install chatsky[benchmark] -``` -""" - -import json -from pathlib import Path -from uuid import uuid4 - -import pandas as pd -from pympler import asizeof -from humanize import naturalsize -import altair as alt -import streamlit as st - - -st.set_page_config( - page_title="DB benchmark", - layout="wide", - initial_sidebar_state="expanded", -) - -BENCHMARK_RESULTS_FILES = Path("benchmark_results_files.json") -# This file stores links to benchmark set files generated by `save_results_to_file`. - -UPLOAD_FILES_DIR = Path("uploaded_benchmarks") -# This directory stores all the benchmarks uploaded via the streamlit interface - -UPLOAD_FILES_DIR.mkdir(exist_ok=True) - - -if not BENCHMARK_RESULTS_FILES.exists(): - with open(BENCHMARK_RESULTS_FILES, "w", encoding="utf-8") as fd: - json.dump([], fd) - -if "benchmark_files" not in st.session_state: - with open(BENCHMARK_RESULTS_FILES, "r", encoding="utf-8") as fd: - st.session_state["benchmark_files"] = json.load(fd) - -if "benchmarks" not in st.session_state: - st.session_state["benchmarks"] = {} - - for file in st.session_state["benchmark_files"]: - with open(file, "r", encoding="utf-8") as fd: - st.session_state["benchmarks"][file] = json.load(fd) - -if "compare" not in st.session_state: - st.session_state["compare"] = [] - - -def add_metrics(container, value_benchmark): - write, read, update, read_update = container.columns(4) - column_names = ("write", "read", "update", "read+update") - - if not value_benchmark["success"]: - values = {key: "-" for key in column_names} - else: - values = {key: value_benchmark["average_results"][f"pretty_{key}"] for key in column_names} - - columns = { - "write": write, - "read": read, - "update": update, - "read+update": read_update, - } - - metric_help = { - "write": "Average write time for a context with from_dialog_len turns into a clean context storage.", - "read": "Average read time (dialog_len ranges between from_dialog_len and to_dialog_len).", - "update": "Average update time (dialog_len ranges between from_dialog_len and to_dialog_len).", - "read+update": "Sum of average read and update times." - " This metric is the time context_storage interface takes during each of the dialog turns.", - } - - for column_name, column in columns.items(): - column.metric(column_name.title(), values[column_name], help=metric_help[column_name]) - - -st.sidebar.text(f"Benchmarks take {naturalsize(asizeof.asizeof(st.session_state['benchmarks']))} RAM") - -add_tab, view_tab, compare_tab, mass_compare_tab = st.tabs(["Benchmark sets", "View", "Compare", "Mass compare"]) - - -############################################################################### -# Benchmark file manipulation tab -# Allows adding and deleting benchmark files -############################################################################### - -with add_tab: - benchmark_list = [] - - for file, benchmark_set in st.session_state["benchmarks"].items(): - benchmark_list.append( - { - "file": file, - "name": benchmark_set["name"], - "description": benchmark_set["description"], - "uuid": benchmark_set["uuid"], - "delete": False, - } - ) - - benchmark_list_df = pd.DataFrame(data=benchmark_list) - - df_container = st.container() - - df_container.info("In the table below you can view all your files with benchmark results as well as delete them.") - - def edit_name_desc(): - edited_rows = st.session_state["result_df"]["edited_rows"] - - for row, edits in edited_rows.items(): - for column, column_value in edits.items(): - if column in ("name", "description"): - edited_file = benchmark_list_df.iat[row, 0] - st.session_state["benchmarks"][edited_file][column] = column_value - - with open(edited_file, "w", encoding="utf-8") as edited_fd: - json.dump(st.session_state["benchmarks"][edited_file], edited_fd) - - df_container.text(f"row {row}: changed {column} to '{column_value}'") - - edited_df = df_container.data_editor( - benchmark_list_df, - key="result_df", - disabled=("file", "uuid"), - on_change=edit_name_desc, - ) - - delete_container = st.container() - - def delete_benchmarks(): - deleted_sets = [ - f"{name} ({uuid})" for name, uuid in edited_df.loc[edited_df["delete"]][["name", "uuid"]].values - ] - - st.session_state["compare"] = [ - item for item in st.session_state["compare"] if item["benchmark_set"] not in deleted_sets - ] - - files_to_delete = edited_df.loc[edited_df["delete"]]["file"] - for file in files_to_delete: - st.session_state["benchmark_files"].remove(file) - del st.session_state["benchmarks"][file] - Path(file).unlink() - delete_container.text(f"Deleted {file}") - - with open(BENCHMARK_RESULTS_FILES, "w", encoding="utf-8") as fd: - json.dump(list(st.session_state["benchmark_files"]), fd) - - delete_container.button(label="Delete selected benchmark sets", on_click=delete_benchmarks) - - def _add_benchmark(benchmark_file, container): - benchmark_file = str(benchmark_file) - - if benchmark_file == "": - return - - if benchmark_file in st.session_state["benchmark_files"]: - container.warning(f"Benchmark file already added: {benchmark_file}") - return - - if not Path(benchmark_file).exists(): - container.warning(f"File does not exists: {benchmark_file}") - return - - with open(benchmark_file, "r", encoding="utf-8") as fd: - file_contents = json.load(fd) - - for benchmark in st.session_state["benchmarks"].values(): - if file_contents["uuid"] == benchmark["uuid"]: - container.warning(f"Benchmark with the same uuid already exists: {benchmark_file}") - return - - st.session_state["benchmark_files"].append(benchmark_file) - with open(BENCHMARK_RESULTS_FILES, "w", encoding="utf-8") as fd: - json.dump(list(st.session_state["benchmark_files"]), fd) - st.session_state["benchmarks"][benchmark_file] = file_contents - - container.text(f"Added {benchmark_file}") - - st.divider() - - st.info("Below you can upload your benchmark files.") - - upload_container = st.container() - - def process_uploaded_files(): - uploaded_files = st.session_state["benchmark_file_uploader"] - if uploaded_files is not None: - if len(uploaded_files) > 0: - new_uploaded_file_dir = UPLOAD_FILES_DIR / str(uuid4()) - new_uploaded_file_dir.mkdir() - - for file in uploaded_files: - file_path = new_uploaded_file_dir / file.name - with open(file_path, "wb") as uploaded_file_descriptor: - uploaded_file_descriptor.write(file.read()) - - _add_benchmark(file_path, upload_container) - - with upload_container.form("upload_form", clear_on_submit=True): - st.file_uploader( - "Upload benchmark results", accept_multiple_files=True, type="json", key="benchmark_file_uploader" - ) - st.form_submit_button("Submit", on_click=process_uploaded_files) - - -############################################################################### -# View tab -# Allows viewing existing benchmarks -############################################################################### - -with view_tab: - set_choice, benchmark_choice, compare = st.columns([3, 3, 1]) - - sets = { - f"{benchmark['name']} ({benchmark['uuid']})": benchmark for benchmark in st.session_state["benchmarks"].values() - } - benchmark_set = set_choice.selectbox("Benchmark set", sets.keys()) - - if benchmark_set is None: - set_choice.warning("No benchmark sets available") - st.stop() - - selected_set = sets[benchmark_set] - - set_choice.text("Set description:") - set_choice.markdown(selected_set["description"]) - - benchmarks = {f"{benchmark['name']} ({benchmark['uuid']})": benchmark for benchmark in selected_set["benchmarks"]} - - benchmark = benchmark_choice.selectbox("Benchmark", benchmarks.keys()) - - if benchmark is None: - benchmark_choice.warning("No benchmarks in the set") - st.stop() - - selected_benchmark = benchmarks[benchmark] - - benchmark_choice.text("Benchmark description:") - benchmark_choice.markdown(selected_benchmark["description"]) - - with st.expander("Benchmark stats"): - benchmark_stats = { - stat: selected_benchmark[stat] - for stat in ( - "db_factory", - "benchmark_config", - ) - } - - st.json(benchmark_stats) - - if not selected_benchmark["success"]: - exc_info = selected_benchmark["result"] - - st.warning(f"**{exc_info['type']}**: {exc_info['msg']}\n\nTraceback:\n\n```\n{exc_info['traceback']}\n```") - else: - add_metrics(st.container(), selected_benchmark) - - compare_item = { - "benchmark_set": benchmark_set, - "benchmark": benchmark, - "write": selected_benchmark["average_results"]["pretty_write"], - "read": selected_benchmark["average_results"]["pretty_read"], - "update": selected_benchmark["average_results"]["pretty_update"], - "read+update": selected_benchmark["average_results"]["pretty_read+update"], - } - - def add_results_to_compare_tab(): - if compare_item not in st.session_state["compare"]: - st.session_state["compare"].append(compare_item) - else: - st.session_state["compare"].remove(compare_item) - - item_in_compare = compare_item not in st.session_state["compare"] - - compare.button( - "Add to Compare" if item_in_compare else "Remove from Compare", - on_click=add_results_to_compare_tab, - help=( - "Add current benchmark to the 'Compare' tab." - if item_in_compare - else "Remove current benchmark from the 'Compare' tab." - ), - ) - - select_graph, graph = st.columns([1, 3]) - - average_results = selected_benchmark["average_results"] - - graphs = { - "Write": selected_benchmark["result"]["write_times"], - "Read (grouped by contex_num)": average_results["read_times_grouped_by_context_num"], - "Read (grouped by dialog_len)": average_results["read_times_grouped_by_dialog_len"], - "Update (grouped by contex_num)": average_results["update_times_grouped_by_context_num"], - "Update (grouped by dialog_len)": average_results["update_times_grouped_by_dialog_len"], - } - - selected_graph = select_graph.selectbox("Select graph to display", graphs.keys()) - - graph_data = graphs[selected_graph] - - if isinstance(graph_data, dict): - data = pd.DataFrame({"dialog_len": graph_data.keys(), "time": graph_data.values()}) - else: - data = pd.DataFrame({"context_num": range(len(graph_data)), "time": graph_data}) - - chart = ( - alt.Chart(data) - .mark_circle() - .encode( - x=alt.X( - "dialog_len:Q" if isinstance(graph_data, dict) else "context_num:Q", scale=alt.Scale(zero=False) - ), - y="time:Q", - ) - .interactive() - ) - - graph.altair_chart(chart, use_container_width=True) - - -############################################################################### -# Compare tab -# Allows viewing existing benchmarks -############################################################################### - -with compare_tab: - df = pd.DataFrame(st.session_state["compare"]) - - st.info("Here you can compare metrics of different benchmarks. Add them here via the 'View' tab.") - - if not df.empty: - st.dataframe( - df.style.highlight_min( - axis=0, subset=["write", "read", "update", "read+update"], props="background-color:green;" - ).highlight_max(axis=0, subset=["write", "read", "update", "read+update"], props="background-color:red;") - ) - else: - st.warning("Currently, there are no benchmarks to compare.") - -############################################################################### -# Mass compare tab -# Allows comparing all benchmarks inside a single set -############################################################################### - -with mass_compare_tab: - st.info("Here you can compare benchmarks inside of a specific set.") - - sets = { - f"{benchmark_set['name']} ({benchmark_set['uuid']})": benchmark_set - for benchmark_set in st.session_state["benchmarks"].values() - } - benchmark_set = st.selectbox("Benchmark set", sets.keys(), key="mass_compare_selectbox") - - if benchmark_set is None: - st.warning("No benchmark sets available") - st.stop() - - selected_set = sets[benchmark_set] - - compare_items = [] - - for selected_benchmark in selected_set["benchmarks"]: - if selected_benchmark["success"]: - compare_items.append( - { - "benchmark": f"{selected_benchmark['name']} ({selected_benchmark['uuid']})", - "write": selected_benchmark["average_results"]["pretty_write"], - "read": selected_benchmark["average_results"]["pretty_read"], - "update": selected_benchmark["average_results"]["pretty_update"], - "read+update": selected_benchmark["average_results"]["pretty_read+update"], - } - ) - - df = pd.DataFrame(compare_items) - - if not df.empty: - st.dataframe( - df.style.highlight_min( - axis=0, subset=["write", "read", "update", "read+update"], props="background-color:green;" - ).highlight_max(axis=0, subset=["write", "read", "update", "read+update"], props="background-color:red;") - ) diff --git a/_modules/chatsky/conditions/service.html b/_modules/chatsky/conditions/service.html deleted file mode 100644 index 7b87c142d..000000000 --- a/_modules/chatsky/conditions/service.html +++ /dev/null @@ -1,629 +0,0 @@ - - - - - - - - - - chatsky.conditions.service — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.conditions.service

-"""
-Service Conditions
-------------------
-Provides service-related conditions
-"""
-
-from __future__ import annotations
-
-from chatsky.core.context import Context
-from chatsky.core.script_function import BaseCondition
-
-from chatsky.core.service.types import (
-    ComponentExecutionState,
-)
-
-
-
[docs]class ServiceFinished(BaseCondition): - """ - Check if a :py:class:`~chatsky.core.service.service.Service` was executed successfully. - """ - - path: str - """The path of the condition pipeline component.""" - wait: bool = False - """ - Whether to wait for the service to be finished. - - This eliminates possible service states ``NOT_RUN`` and ``RUNNING``. - """ - - def __init__(self, path: str, *, wait: bool = False): - super().__init__(path=path, wait=wait) - -
[docs] async def call(self, ctx: Context) -> bool: - if self.wait: - await ctx.framework_data.service_states[self.path].finished_event.wait() - - state = ctx.framework_data.service_states[self.path].execution_status - - return ComponentExecutionState[state] == ComponentExecutionState.FINISHED
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/conditions/slots.html b/_modules/chatsky/conditions/slots.html deleted file mode 100644 index 1e896315a..000000000 --- a/_modules/chatsky/conditions/slots.html +++ /dev/null @@ -1,627 +0,0 @@ - - - - - - - - - - chatsky.conditions.slots — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.conditions.slots

-"""
-Slot Conditions
----------------------------
-Provides slot-related conditions.
-"""
-
-from __future__ import annotations
-from typing import Literal, List
-
-from chatsky.core import Context, BaseCondition
-from chatsky.slots.slots import SlotName
-
-
-
[docs]class SlotsExtracted(BaseCondition): - """ - Check if :py:attr:`.slots` are extracted. - - :param mode: Whether to check if all slots are extracted or any slot is extracted. - """ - - slots: List[SlotName] - """ - Names of the slots that need to be checked. - """ - mode: Literal["any", "all"] = "all" - """ - Whether to check if all slots are extracted or any slot is extracted. - """ - - def __init__(self, *slots: SlotName, mode: Literal["any", "all"] = "all"): - super().__init__(slots=slots, mode=mode) - -
[docs] async def call(self, ctx: Context) -> bool: - manager = ctx.framework_data.slot_manager - if self.mode == "all": - return all(manager.is_slot_extracted(slot) for slot in self.slots) - elif self.mode == "any": - return any(manager.is_slot_extracted(slot) for slot in self.slots)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/conditions/standard.html b/_modules/chatsky/conditions/standard.html deleted file mode 100644 index 889919b81..000000000 --- a/_modules/chatsky/conditions/standard.html +++ /dev/null @@ -1,823 +0,0 @@ - - - - - - - - - - chatsky.conditions.standard — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.conditions.standard

-"""
-Standard Conditions
--------------------
-This module provides basic conditions.
-
-- :py:class:`.Any`, :py:class:`.All` and :py:class:`.Negation` are meta-conditions.
-- :py:class:`.HasText`, :py:class:`.Regexp`, :py:class:`.HasCallbackQuery` are last-request-based conditions.
-- :py:class:`.CheckLastLabels` is a label-based condition.
-"""
-
-import asyncio
-from typing import Pattern, Union, List, Optional
-import logging
-import re
-from functools import cached_property
-
-from pydantic import Field, computed_field
-
-from chatsky.core import BaseCondition, Context
-from chatsky.core.message import Message, MessageInitTypes, CallbackQuery
-from chatsky.core.node_label import AbsoluteNodeLabel, AbsoluteNodeLabelInitTypes
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class ExactMatch(BaseCondition): - """ - Check if :py:attr:`~.Context.last_request` matches :py:attr:`.match`. - - If :py:attr:`.skip_none`, will not compare ``None`` fields of :py:attr:`.match`. - """ - - match: Message - """ - Message to compare last request with. - - Is initialized according to :py:data:`~.MessageInitTypes`. - """ - skip_none: bool = True - """ - Whether fields set to ``None`` in :py:attr:`.match` should not be compared. - """ - - def __init__(self, match: MessageInitTypes, *, skip_none=True): - super().__init__(match=match, skip_none=skip_none) - -
[docs] async def call(self, ctx: Context) -> bool: - request = ctx.last_request - for field in self.match.model_fields: - match_value = self.match.__getattribute__(field) - if self.skip_none and match_value is None: - continue - if field in request.model_fields.keys(): - if request.__getattribute__(field) != self.match.__getattribute__(field): - return False - else: - return False - return True
- - -
[docs]class HasText(BaseCondition): - """ - Check if the :py:attr:`~.Message.text` attribute of :py:attr:`~.Context.last_request` - contains :py:attr:`.text`. - """ - - text: str - """ - Text to search for in the last request. - """ - - def __init__(self, text: str): - super().__init__(text=text) - -
[docs] async def call(self, ctx: Context) -> bool: - request = ctx.last_request - if request.text is None: - return False - return self.text in request.text
- - -
[docs]class Regexp(BaseCondition): - """ - Check if the :py:attr:`~.Message.text` attribute of :py:attr:`~.Context.last_request` - contains :py:attr:`.pattern`. - """ - - pattern: Union[str, Pattern] - """ - The `RegExp` pattern to search for in the last request. - """ - flags: Union[int, re.RegexFlag] = 0 - """ - Flags to pass to ``re.compile``. - """ - - def __init__(self, pattern: Union[str, Pattern], *, flags: Union[int, re.RegexFlag] = 0): - super().__init__(pattern=pattern, flags=flags) - - @computed_field - @cached_property - def re_object(self) -> Pattern: - """Compiled pattern.""" - return re.compile(self.pattern, self.flags) - -
[docs] async def call(self, ctx: Context) -> bool: - request = ctx.last_request - if request.text is None: - return False - return bool(self.re_object.search(request.text))
- - -
[docs]class Any(BaseCondition): - """ - Check if any condition from the :py:attr:`.conditions` list is True. - """ - - conditions: List[BaseCondition] - """ - List of conditions. - """ - - def __init__(self, *conditions: BaseCondition): - super().__init__(conditions=list(conditions)) - -
[docs] async def call(self, ctx: Context) -> bool: - return any(await asyncio.gather(*(cnd.is_true(ctx) for cnd in self.conditions)))
- - -
[docs]class All(BaseCondition): - """ - Check if all conditions from the :py:attr:`.conditions` list is True. - """ - - conditions: List[BaseCondition] - """ - List of conditions. - """ - - def __init__(self, *conditions: BaseCondition): - super().__init__(conditions=list(conditions)) - -
[docs] async def call(self, ctx: Context) -> bool: - return all(await asyncio.gather(*(cnd.is_true(ctx) for cnd in self.conditions)))
- - -
[docs]class Negation(BaseCondition): - """ - Return the negation of the result of :py:attr:`~.Negation.condition`. - """ - - condition: BaseCondition - """ - Condition to negate. - """ - - def __init__(self, condition: BaseCondition): - super().__init__(condition=condition) - -
[docs] async def call(self, ctx: Context) -> bool: - return not await self.condition.is_true(ctx)
- - -Not = Negation -""" -:py:class:`.Not` is an alias for :py:class:`.Negation`. -""" - - -
[docs]class CheckLastLabels(BaseCondition): - """ - Check if any label in the last :py:attr:`.last_n_indices` of :py:attr:`.Context.labels` is in - :py:attr:`.labels` or if its :py:attr:`~.AbsoluteNodeLabel.flow_name` is in :py:attr:`.flow_labels`. - """ - - flow_labels: List[str] = Field(default_factory=list) - """ - List of flow names to find in the last labels. - """ - labels: List[AbsoluteNodeLabel] = Field(default_factory=list) - """ - List of labels to find in the last labels. - - Is initialized according to :py:data:`~.AbsoluteNodeLabelInitTypes`. - """ - last_n_indices: int = Field(default=1, ge=1) - """ - Number of labels to check. - """ - - def __init__( - self, - *, - flow_labels: Optional[List[str]] = None, - labels: Optional[List[AbsoluteNodeLabelInitTypes]] = None, - last_n_indices: int = 1 - ): - if flow_labels is None: - flow_labels = [] - if labels is None: - labels = [] - super().__init__(flow_labels=flow_labels, labels=labels, last_n_indices=last_n_indices) - -
[docs] async def call(self, ctx: Context) -> bool: - labels = list(ctx.labels.values())[-self.last_n_indices :] # noqa: E203 - for label in labels: - if label.flow_name in self.flow_labels or label in self.labels: - return True - return False
- - -
[docs]class HasCallbackQuery(BaseCondition): - """ - Check if :py:attr:`~.Context.last_request` contains a :py:class:`.CallbackQuery` attachment - with :py:attr:`.CallbackQuery.query_string` matching :py:attr:`.HasCallbackQuery.query_string`. - """ - - query_string: str - """ - Query string to find in last request's attachments. - """ - - def __init__(self, query_string: str): - super().__init__(query_string=query_string) - -
[docs] async def call(self, ctx: Context) -> bool: - last_request = ctx.last_request - if last_request.attachments is None: - return False - for attachment in last_request.attachments: - if isinstance(attachment, CallbackQuery): - if attachment.query_string == self.query_string: - return True - return False
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/database.html b/_modules/chatsky/context_storages/database.html deleted file mode 100644 index e31260996..000000000 --- a/_modules/chatsky/context_storages/database.html +++ /dev/null @@ -1,817 +0,0 @@ - - - - - - - - - - chatsky.context_storages.database — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.database

-"""
-Database
---------
-The `Database` module provides classes for managing the context storage of a dialog system.
-The module can be used to store information such as the current state of the conversation
-and other data. This module includes the intermediate class (:py:class:`.DBContextStorage`) is a class
-that developers can inherit from in order to create their own context storage solutions.
-This class implements the basic functionality and can be extended to add additional features as needed.
-"""
-
-import asyncio
-import importlib
-import threading
-from functools import wraps
-from abc import ABC, abstractmethod
-from typing import Callable, Hashable, Optional
-
-from .protocol import PROTOCOLS
-from chatsky.core import Context
-
-
-
[docs]class DBContextStorage(ABC): - r""" - An abstract interface for `chatsky` DB context storages. - It includes the most essential methods of the python `dict` class. - Can not be instantiated. - - :param path: Parameter `path` should be set with the URI of the database. - It includes a prefix and the required connection credentials. - Example: postgresql+asyncpg://user:password@host:port/database - In the case of classes that save data to hard drive instead of external databases - you need to specify the location of the file, like you do in sqlite. - Keep in mind that in Windows you will have to use double backslashes '\\' - instead of forward slashes '/' when defining the file path. - - """ - - def __init__(self, path: str): - _, _, file_path = path.partition("://") - self.full_path = path - """Full path to access the context storage, as it was provided by user.""" - self.path = file_path - """`full_path` without a prefix defining db used""" - self._lock = threading.Lock() - """Threading for methods that require single thread access.""" - - def __getitem__(self, key: Hashable) -> Context: - """ - Synchronous method for accessing stored Context. - - :param key: Hashable key used to store Context instance. - :return: The stored context, associated with the given key. - """ - return asyncio.run(self.get_item_async(key)) - -
[docs] @abstractmethod - async def get_item_async(self, key: Hashable) -> Context: - """ - Asynchronous method for accessing stored Context. - - :param key: Hashable key used to store Context instance. - :return: The stored context, associated with the given key. - """ - raise NotImplementedError
- - def __setitem__(self, key: Hashable, value: Context): - """ - Synchronous method for storing Context. - - :param key: Hashable key used to store Context instance. - :param value: Context to store. - """ - return asyncio.run(self.set_item_async(key, value)) - -
[docs] @abstractmethod - async def set_item_async(self, key: Hashable, value: Context): - """ - Asynchronous method for storing Context. - - :param key: Hashable key used to store Context instance. - :param value: Context to store. - """ - raise NotImplementedError
- - def __delitem__(self, key: Hashable): - """ - Synchronous method for removing stored Context. - - :param key: Hashable key used to identify Context instance for deletion. - """ - return asyncio.run(self.del_item_async(key)) - -
[docs] @abstractmethod - async def del_item_async(self, key: Hashable): - """ - Asynchronous method for removing stored Context. - - :param key: Hashable key used to identify Context instance for deletion. - """ - raise NotImplementedError
- - def __contains__(self, key: Hashable) -> bool: - """ - Synchronous method for finding whether any Context is stored with given key. - - :param key: Hashable key used to check if Context instance is stored. - :return: True if there is Context accessible by given key, False otherwise. - """ - return asyncio.run(self.contains_async(key)) - -
[docs] @abstractmethod - async def contains_async(self, key: Hashable) -> bool: - """ - Asynchronous method for finding whether any Context is stored with given key. - - :param key: Hashable key used to check if Context instance is stored. - :return: True if there is Context accessible by given key, False otherwise. - """ - raise NotImplementedError
- - def __len__(self) -> int: - """ - Synchronous method for retrieving number of stored Contexts. - - :return: The number of stored Contexts. - """ - return asyncio.run(self.len_async()) - -
[docs] @abstractmethod - async def len_async(self) -> int: - """ - Asynchronous method for retrieving number of stored Contexts. - - :return: The number of stored Contexts. - """ - raise NotImplementedError
- -
[docs] def get(self, key: Hashable, default: Optional[Context] = None) -> Context: - """ - Synchronous method for accessing stored Context, returning default if no Context is stored with the given key. - - :param key: Hashable key used to store Context instance. - :param default: Optional default value to be returned if no Context is found. - :return: The stored context, associated with the given key or default value. - """ - return asyncio.run(self.get_async(key, default))
- -
[docs] async def get_async(self, key: Hashable, default: Optional[Context] = None) -> Context: - """ - Asynchronous method for accessing stored Context, returning default if no Context is stored with the given key. - - :param key: Hashable key used to store Context instance. - :param default: Optional default value to be returned if no Context is found. - :return: The stored context, associated with the given key or default value. - """ - try: - return await self.get_item_async(str(key)) - except KeyError: - return default
- -
[docs] def clear(self): - """ - Synchronous method for clearing context storage, removing all the stored Contexts. - """ - return asyncio.run(self.clear_async())
- -
[docs] @abstractmethod - async def clear_async(self): - """ - Asynchronous method for clearing context storage, removing all the stored Contexts. - """ - raise NotImplementedError
- - -
[docs]def threadsafe_method(func: Callable): - """ - A decorator that makes sure methods of an object instance are threadsafe. - """ - - @wraps(func) - def _synchronized(self, *args, **kwargs): - with self._lock: - return func(self, *args, **kwargs) - - return _synchronized
- - -
[docs]def context_storage_factory(path: str, **kwargs) -> DBContextStorage: - """ - Use context_storage_factory to lazy import context storage types and instantiate them. - The function takes a database connection URI or its equivalent. It should be prefixed with database name, - followed by the symbol triplet '://'. - - Then, you should list the connection parameters like this: user:password@host:port/database - The whole URI will then look like this: - - - shelve://path_to_the_file/file_name - - json://path_to_the_file/file_name - - pickle://path_to_the_file/file_name - - sqlite+aiosqlite://path_to_the_file/file_name - - redis://:pass@localhost:6378/0 - - mongodb://admin:pass@localhost:27016/admin - - mysql+asyncmy://root:pass@localhost:3306/test - - postgresql+asyncpg://postgres:pass@localhost:5430/test - - grpc://localhost:2134/local - - grpcs://localhost:2134/local - - For context storages that write to local files, the function expects a file path instead of connection params: - json://file.json - When using sqlite backend your prefix should contain three slashes if you use Windows, or four in other cases: - sqlite:////file.db - If you want to use additional parameters in class constructors, you can pass them to this function as kwargs. - - :param path: Path to the file. - """ - prefix, _, _ = path.partition("://") - if "sql" in prefix: - prefix = prefix.split("+")[0] # this takes care of alternative sql drivers - assert ( - prefix in PROTOCOLS - ), f""" - URI path should be prefixed with one of the following:\n - {", ".join(PROTOCOLS.keys())}.\n - For more information, see the function doc:\n{context_storage_factory.__doc__} - """ - _class, module = PROTOCOLS[prefix]["class"], PROTOCOLS[prefix]["module"] - target_class = getattr(importlib.import_module(f".{module}", package="chatsky.context_storages"), _class) - return target_class(path, **kwargs)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/json.html b/_modules/chatsky/context_storages/json.html deleted file mode 100644 index 55ed074b8..000000000 --- a/_modules/chatsky/context_storages/json.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - - - chatsky.context_storages.json — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.json

-"""
-JSON
-----
-The JSON module provides a json-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data in a JSON. It allows Chatsky to easily
-store and retrieve context data.
-"""
-
-import asyncio
-from typing import Hashable, Dict
-
-try:
-    import aiofiles
-    import aiofiles.os
-
-    json_available = True
-except ImportError:
-    json_available = False
-
-from pydantic import BaseModel
-
-from .database import DBContextStorage, threadsafe_method
-from chatsky.core import Context
-
-
-
[docs]class SerializableStorage(BaseModel, extra="allow"): - __pydantic_extra__: Dict[str, Context]
- - -
[docs]class JSONContextStorage(DBContextStorage): - """ - Implements :py:class:`.DBContextStorage` with `json` as the storage format. - - :param path: Target file URI. Example: `json://file.json`. - """ - - def __init__(self, path: str): - DBContextStorage.__init__(self, path) - asyncio.run(self._load()) - -
[docs] @threadsafe_method - async def len_async(self) -> int: - return len(self.storage.model_extra)
- -
[docs] @threadsafe_method - async def set_item_async(self, key: Hashable, value: Context): - self.storage.model_extra.__setitem__(str(key), value) - await self._save()
- -
[docs] @threadsafe_method - async def get_item_async(self, key: Hashable) -> Context: - await self._load() - return Context.model_validate(self.storage.model_extra.__getitem__(str(key)))
- -
[docs] @threadsafe_method - async def del_item_async(self, key: Hashable): - self.storage.model_extra.__delitem__(str(key)) - await self._save()
- -
[docs] @threadsafe_method - async def contains_async(self, key: Hashable) -> bool: - await self._load() - return self.storage.model_extra.__contains__(str(key))
- -
[docs] @threadsafe_method - async def clear_async(self): - self.storage.model_extra.clear() - await self._save()
- -
[docs] async def _save(self): - async with aiofiles.open(self.path, "w+", encoding="utf-8") as file_stream: - await file_stream.write(self.storage.model_dump_json())
- -
[docs] async def _load(self): - if not await aiofiles.os.path.isfile(self.path) or (await aiofiles.os.stat(self.path)).st_size == 0: - self.storage = SerializableStorage() - await self._save() - else: - async with aiofiles.open(self.path, "r", encoding="utf-8") as file_stream: - self.storage = SerializableStorage.model_validate_json(await file_stream.read())
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/mongo.html b/_modules/chatsky/context_storages/mongo.html deleted file mode 100644 index d526a20d3..000000000 --- a/_modules/chatsky/context_storages/mongo.html +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - - - - - chatsky.context_storages.mongo — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.mongo

-"""
-Mongo
------
-The Mongo module provides a MongoDB-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data in a MongoDB.
-It allows Chatsky to easily store and retrieve context data in a format that is highly scalable
-and easy to work with.
-
-MongoDB is a widely-used, open-source NoSQL database that is known for its scalability and performance.
-It stores data in a format similar to JSON, making it easy to work with the data in a variety of programming languages
-and environments. Additionally, MongoDB is highly scalable and can handle large amounts of data
-and high levels of read and write traffic.
-"""
-
-from typing import Hashable, Dict, Any
-
-try:
-    from motor.motor_asyncio import AsyncIOMotorClient
-    from bson.objectid import ObjectId
-
-    mongo_available = True
-except ImportError:
-    mongo_available = False
-    AsyncIOMotorClient = None
-    ObjectId = Any
-
-import json
-
-from chatsky.core import Context
-
-from .database import DBContextStorage, threadsafe_method
-from .protocol import get_protocol_install_suggestion
-
-
-
[docs]class MongoContextStorage(DBContextStorage): - """ - Implements :py:class:`.DBContextStorage` with `mongodb` as the database backend. - - :param path: Database URI. Example: `mongodb://user:password@host:port/dbname`. - :param collection: Name of the collection to store the data in. - """ - - def __init__(self, path: str, collection: str = "context_collection"): - DBContextStorage.__init__(self, path) - if not mongo_available: - install_suggestion = get_protocol_install_suggestion("mongodb") - raise ImportError("`mongodb` package is missing.\n" + install_suggestion) - self._mongo = AsyncIOMotorClient(self.full_path) - db = self._mongo.get_default_database() - self.collection = db[collection] - -
[docs] @staticmethod - def _adjust_key(key: Hashable) -> Dict[str, ObjectId]: - """Convert a n-digit context id to a 24-digit mongo id""" - new_key = hex(int.from_bytes(str.encode(str(key)), "big", signed=False))[3:] - new_key = (new_key * (24 // len(new_key) + 1))[:24] - assert len(new_key) == 24 - return {"_id": ObjectId(new_key)}
- -
[docs] @threadsafe_method - async def set_item_async(self, key: Hashable, value: Context): - new_key = self._adjust_key(key) - value = Context.model_validate(value) - document = json.loads(value.model_dump_json()) - - document.update(new_key) - await self.collection.replace_one(new_key, document, upsert=True)
- -
[docs] @threadsafe_method - async def get_item_async(self, key: Hashable) -> Context: - adjust_key = self._adjust_key(key) - document = await self.collection.find_one(adjust_key) - if document: - document.pop("_id") - ctx = Context.model_validate(document) - return ctx - raise KeyError
- -
[docs] @threadsafe_method - async def del_item_async(self, key: Hashable): - adjust_key = self._adjust_key(key) - await self.collection.delete_one(adjust_key)
- -
[docs] @threadsafe_method - async def contains_async(self, key: Hashable) -> bool: - adjust_key = self._adjust_key(key) - return bool(await self.collection.find_one(adjust_key))
- -
[docs] @threadsafe_method - async def len_async(self) -> int: - return await self.collection.estimated_document_count()
- -
[docs] @threadsafe_method - async def clear_async(self): - await self.collection.delete_many(dict())
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/pickle.html b/_modules/chatsky/context_storages/pickle.html deleted file mode 100644 index 362146284..000000000 --- a/_modules/chatsky/context_storages/pickle.html +++ /dev/null @@ -1,669 +0,0 @@ - - - - - - - - - - chatsky.context_storages.pickle — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.pickle

-"""
-Pickle
-------
-The Pickle module provides a pickle-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data in a pickle format.
-It allows Chatsky to easily store and retrieve context data in a format that is efficient
-for serialization and deserialization and can be easily used in python.
-
-Pickle is a python library that allows to serialize and deserialize python objects.
-It is efficient and fast, but it is not recommended to use it to transfer data across
-different languages or platforms because it's not cross-language compatible.
-"""
-
-import asyncio
-import pickle
-from typing import Hashable
-
-try:
-    import aiofiles
-    import aiofiles.os
-
-    pickle_available = True
-except ImportError:
-    pickle_available = False
-
-from .database import DBContextStorage, threadsafe_method
-from chatsky.core import Context
-
-
-
[docs]class PickleContextStorage(DBContextStorage): - """ - Implements :py:class:`.DBContextStorage` with `pickle` as driver. - - :param path: Target file URI. Example: 'pickle://file.pkl'. - """ - - def __init__(self, path: str): - DBContextStorage.__init__(self, path) - asyncio.run(self._load()) - -
[docs] @threadsafe_method - async def len_async(self) -> int: - return len(self.dict)
- -
[docs] @threadsafe_method - async def set_item_async(self, key: Hashable, value: Context): - self.dict.__setitem__(str(key), value) - await self._save()
- -
[docs] @threadsafe_method - async def get_item_async(self, key: Hashable) -> Context: - await self._load() - return Context.model_validate(self.dict.__getitem__(str(key)))
- -
[docs] @threadsafe_method - async def del_item_async(self, key: Hashable): - self.dict.__delitem__(str(key)) - await self._save()
- -
[docs] @threadsafe_method - async def contains_async(self, key: Hashable) -> bool: - await self._load() - return self.dict.__contains__(str(key))
- -
[docs] @threadsafe_method - async def clear_async(self): - self.dict.clear() - await self._save()
- -
[docs] async def _save(self): - async with aiofiles.open(self.path, "wb+") as file: - await file.write(pickle.dumps(self.dict))
- -
[docs] async def _load(self): - if not await aiofiles.os.path.isfile(self.path) or (await aiofiles.os.stat(self.path)).st_size == 0: - self.dict = dict() - await self._save() - else: - async with aiofiles.open(self.path, "rb") as file: - self.dict = pickle.loads(await file.read())
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/protocol.html b/_modules/chatsky/context_storages/protocol.html deleted file mode 100644 index a8123dae4..000000000 --- a/_modules/chatsky/context_storages/protocol.html +++ /dev/null @@ -1,624 +0,0 @@ - - - - - - - - - - chatsky.context_storages.protocol — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.protocol

-"""
-Protocol
---------
-The Protocol module contains the base code for the different communication protocols used in Chatsky.
-It defines the :py:data:`.PROTOCOLS` constant, which lists all the supported protocols in Chatsky.
-
-The module also includes a function :py:func:`.get_protocol_install_suggestion()` that is used to provide
-suggestions for installing the necessary dependencies for a specific protocol.
-This function takes the name of the desired protocol as an argument and returns
-a string containing the necessary installation commands for that protocol.
-
-Chatsky supports a variety of communication protocols,
-which allows it to communicate with different types of databases.
-"""
-
-import json
-import pathlib
-
-with open(pathlib.Path(__file__).parent / "protocols.json", "r", encoding="utf-8") as protocols:
-    PROTOCOLS = json.load(protocols)
-_prtocol_keys = {"module", "class", "slug", "uri_example"}
-assert all(set(proc.keys()) == _prtocol_keys for proc in PROTOCOLS.values()), "Protocols are incomplete"
-
-
-
[docs]def get_protocol_install_suggestion(protocol_name: str) -> str: - """ - Provide suggestions for installing the necessary dependencies for a specific protocol. - - :param protocol_name: Protocol name. - """ - protocol = PROTOCOLS.get(protocol_name, {}) - slug = protocol.get("slug") - if slug: - return f"Try to run `pip install chatsky[{slug}]`" - return ""
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/redis.html b/_modules/chatsky/context_storages/redis.html deleted file mode 100644 index 0d606237b..000000000 --- a/_modules/chatsky/context_storages/redis.html +++ /dev/null @@ -1,662 +0,0 @@ - - - - - - - - - - chatsky.context_storages.redis — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.redis

-"""
-Redis
------
-The Redis module provides a Redis-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data in a Redis.
-It allows Chatsky to easily store and retrieve context data in a format that is highly scalable
-and easy to work with.
-
-Redis is an open-source, in-memory data structure store that is known for its
-high performance and scalability. It stores data in key-value pairs and supports a variety of data
-structures such as strings, hashes, lists, sets, and more.
-Additionally, Redis can be used as a cache, message broker, and database, making it a versatile
-and powerful choice for data storage and management.
-"""
-
-import json
-from typing import Hashable
-
-try:
-    from redis.asyncio import Redis
-
-    redis_available = True
-except ImportError:
-    redis_available = False
-
-from chatsky.core import Context
-
-from .database import DBContextStorage, threadsafe_method
-from .protocol import get_protocol_install_suggestion
-
-
-
[docs]class RedisContextStorage(DBContextStorage): - """ - Implements :py:class:`.DBContextStorage` with `redis` as the database backend. - - :param path: Database URI string. Example: `redis://user:password@host:port`. - """ - - def __init__(self, path: str): - DBContextStorage.__init__(self, path) - if not redis_available: - install_suggestion = get_protocol_install_suggestion("redis") - raise ImportError("`redis` package is missing.\n" + install_suggestion) - self._redis = Redis.from_url(self.full_path) - -
[docs] @threadsafe_method - async def contains_async(self, key: Hashable) -> bool: - return bool(await self._redis.exists(str(key)))
- -
[docs] @threadsafe_method - async def set_item_async(self, key: Hashable, value: Context): - value = Context.model_validate(value) - await self._redis.set(str(key), value.model_dump_json())
- -
[docs] @threadsafe_method - async def get_item_async(self, key: Hashable) -> Context: - result = await self._redis.get(str(key)) - if result: - result_dict = json.loads(result.decode("utf-8")) - return Context.model_validate(result_dict) - raise KeyError(f"No entry for key {key}.")
- -
[docs] @threadsafe_method - async def del_item_async(self, key: Hashable): - await self._redis.delete(str(key))
- -
[docs] @threadsafe_method - async def len_async(self) -> int: - return await self._redis.dbsize()
- -
[docs] @threadsafe_method - async def clear_async(self): - await self._redis.flushdb()
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/shelve.html b/_modules/chatsky/context_storages/shelve.html deleted file mode 100644 index 7e40a89cf..000000000 --- a/_modules/chatsky/context_storages/shelve.html +++ /dev/null @@ -1,641 +0,0 @@ - - - - - - - - - - chatsky.context_storages.shelve — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.shelve

-"""
-Shelve
-------
-The Shelve module provides a shelve-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data in a shelve format.
-It allows Chatsky to easily store and retrieve context data in a format that is efficient
-for serialization and deserialization and can be easily used in python.
-
-Shelve is a python library that allows to store and retrieve python objects.
-It is efficient and fast, but it is not recommended to use it to transfer data across different languages
-or platforms because it's not cross-language compatible.
-It stores data in a dbm-style format in the file system, which is not as fast as the other serialization
-libraries like pickle or JSON.
-"""
-
-import pickle
-from shelve import DbfilenameShelf
-from typing import Hashable
-
-from chatsky.core import Context
-
-from .database import DBContextStorage
-
-
-
[docs]class ShelveContextStorage(DBContextStorage): - """ - Implements :py:class:`.DBContextStorage` with `shelve` as the driver. - - :param path: Target file URI. Example: `shelve://file.db`. - """ - - def __init__(self, path: str): - DBContextStorage.__init__(self, path) - self.shelve_db = DbfilenameShelf(filename=self.path, protocol=pickle.HIGHEST_PROTOCOL) - -
[docs] async def get_item_async(self, key: Hashable) -> Context: - return self.shelve_db[str(key)]
- -
[docs] async def set_item_async(self, key: Hashable, value: Context): - self.shelve_db.__setitem__(str(key), value)
- -
[docs] async def del_item_async(self, key: Hashable): - self.shelve_db.__delitem__(str(key))
- -
[docs] async def contains_async(self, key: Hashable) -> bool: - return self.shelve_db.__contains__(str(key))
- -
[docs] async def len_async(self) -> int: - return self.shelve_db.__len__()
- -
[docs] async def clear_async(self): - self.shelve_db.clear()
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/sql.html b/_modules/chatsky/context_storages/sql.html deleted file mode 100644 index aec69d542..000000000 --- a/_modules/chatsky/context_storages/sql.html +++ /dev/null @@ -1,781 +0,0 @@ - - - - - - - - - - chatsky.context_storages.sql — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.sql

-"""
-SQL
----
-The SQL module provides a SQL-based version of the :py:class:`.DBContextStorage` class.
-This class is used to store and retrieve context data from SQL databases.
-It allows Chatsky to easily store and retrieve context data in a format that is highly scalable
-and easy to work with.
-
-The SQL module provides the ability to choose the backend of your choice from
-MySQL, PostgreSQL, or SQLite. You can choose the one that is most suitable for your use case and environment.
-MySQL and PostgreSQL are widely used open-source relational databases that are known for their
-reliability and scalability. SQLite is a self-contained, high-reliability, embedded, full-featured,
-public-domain, SQL database engine.
-"""
-
-import asyncio
-import importlib
-import json
-from typing import Hashable
-
-from chatsky.core import Context
-
-from .database import DBContextStorage, threadsafe_method
-from .protocol import get_protocol_install_suggestion
-
-try:
-    from sqlalchemy import Table, MetaData, Column, JSON, String, inspect, select, delete, func
-    from sqlalchemy.ext.asyncio import create_async_engine
-
-    sqlalchemy_available = True
-except (ImportError, ModuleNotFoundError):
-    sqlalchemy_available = False
-
-postgres_available = sqlite_available = mysql_available = False
-
-try:
-    import asyncpg
-
-    _ = asyncpg
-
-    postgres_available = True
-except (ImportError, ModuleNotFoundError):
-    pass
-
-try:
-    import asyncmy
-
-    _ = asyncmy
-
-    mysql_available = True
-except (ImportError, ModuleNotFoundError):
-    pass
-
-try:
-    import aiosqlite
-
-    _ = aiosqlite
-
-    sqlite_available = True
-except (ImportError, ModuleNotFoundError):
-    pass
-
-if not sqlalchemy_available:
-    postgres_available = sqlite_available = mysql_available = False
-
-
-
[docs]def import_insert_for_dialect(dialect: str): - """ - Imports the insert function into global scope depending on the chosen sqlalchemy dialect. - - :param dialect: Chosen sqlalchemy dialect. - """ - global insert - insert = getattr( - importlib.import_module(f"sqlalchemy.dialects.{dialect}"), - "insert", - )
- - -
[docs]class SQLContextStorage(DBContextStorage): - """ - | SQL-based version of the :py:class:`.DBContextStorage`. - | Compatible with MySQL, Postgresql, Sqlite. - - :param path: Standard sqlalchemy URI string. - When using sqlite backend in Windows, keep in mind that you have to use double backslashes '\\' - instead of forward slashes '/' in the file path. - :param table_name: The name of the table to use. - :param custom_driver: If you intend to use some other database driver instead of the recommended ones, - set this parameter to `True` to bypass the import checks. - """ - - def __init__(self, path: str, table_name: str = "contexts", custom_driver: bool = False): - DBContextStorage.__init__(self, path) - - self._check_availability(custom_driver) - self.engine = create_async_engine(self.full_path, pool_pre_ping=True) - self.dialect: str = self.engine.dialect.name - - id_column_args = {"primary_key": True} - if self.dialect == "sqlite": - id_column_args["sqlite_on_conflict_primary_key"] = "REPLACE" - - self.metadata = MetaData() - self.table = Table( - table_name, - self.metadata, - Column("id", String(36), **id_column_args), - Column("context", JSON), # column for storing serialized contexts - ) - - asyncio.run(self._create_self_table()) - - import_insert_for_dialect(self.dialect) - -
[docs] @threadsafe_method - async def set_item_async(self, key: Hashable, value: Context): - value = Context.model_validate(value) - value = json.loads(value.model_dump_json()) - - insert_stmt = insert(self.table).values(id=str(key), context=value) - update_stmt = await self._get_update_stmt(insert_stmt) - - async with self.engine.connect() as conn: - await conn.execute(update_stmt) - await conn.commit()
- -
[docs] @threadsafe_method - async def get_item_async(self, key: Hashable) -> Context: - stmt = select(self.table.c.context).where(self.table.c.id == str(key)) - async with self.engine.connect() as conn: - result = await conn.execute(stmt) - row = result.fetchone() - if row: - return Context.model_validate(row[0]) - raise KeyError
- -
[docs] @threadsafe_method - async def del_item_async(self, key: Hashable): - stmt = delete(self.table).where(self.table.c.id == str(key)) - async with self.engine.connect() as conn: - await conn.execute(stmt) - await conn.commit()
- -
[docs] @threadsafe_method - async def contains_async(self, key: Hashable) -> bool: - stmt = select(self.table.c.context).where(self.table.c.id == str(key)) - async with self.engine.connect() as conn: - result = await conn.execute(stmt) - return bool(result.fetchone())
- -
[docs] @threadsafe_method - async def len_async(self) -> int: - stmt = select(func.count()).select_from(self.table) - async with self.engine.connect() as conn: - result = await conn.execute(stmt) - return result.fetchone()[0]
- -
[docs] @threadsafe_method - async def clear_async(self): - stmt = delete(self.table) - async with self.engine.connect() as conn: - await conn.execute(stmt) - await conn.commit()
- -
[docs] async def _create_self_table(self): - async with self.engine.begin() as conn: - if not await conn.run_sync(lambda sync_conn: inspect(sync_conn).has_table(self.table.name)): - await conn.run_sync(self.table.create, self.engine)
- -
[docs] async def _get_update_stmt(self, insert_stmt): - if self.dialect == "sqlite": - return insert_stmt - elif self.dialect == "mysql": - update_stmt = insert_stmt.on_duplicate_key_update(context=insert_stmt.inserted.context) - else: - update_stmt = insert_stmt.on_conflict_do_update( - index_elements=["id"], set_=dict(context=insert_stmt.excluded.context) - ) - return update_stmt
- -
[docs] def _check_availability(self, custom_driver: bool): - if not custom_driver: - if self.full_path.startswith("postgresql") and not postgres_available: - install_suggestion = get_protocol_install_suggestion("postgresql") - raise ImportError("Packages `sqlalchemy` and/or `asyncpg` are missing.\n" + install_suggestion) - elif self.full_path.startswith("mysql") and not mysql_available: - install_suggestion = get_protocol_install_suggestion("mysql") - raise ImportError("Packages `sqlalchemy` and/or `asyncmy` are missing.\n" + install_suggestion) - elif self.full_path.startswith("sqlite") and not sqlite_available: - install_suggestion = get_protocol_install_suggestion("sqlite") - raise ImportError("Package `sqlalchemy` and/or `aiosqlite` is missing.\n" + install_suggestion)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/context_storages/ydb.html b/_modules/chatsky/context_storages/ydb.html deleted file mode 100644 index e6f1c33c1..000000000 --- a/_modules/chatsky/context_storages/ydb.html +++ /dev/null @@ -1,829 +0,0 @@ - - - - - - - - - - chatsky.context_storages.ydb — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.context_storages.ydb

-"""
-Yandex DB
----------
-The Yandex DB module provides a version of the :py:class:`.DBContextStorage` class that designed to work with
-Yandex and other databases. Yandex DataBase is a fully-managed cloud-native SQL service that makes it easy to set up,
-operate, and scale high-performance and high-availability databases for your applications.
-
-The Yandex DB module uses the Yandex Cloud SDK, which is a python library that allows you to work
-with Yandex Cloud services using python. This allows Chatsky to easily integrate with the Yandex DataBase and
-take advantage of the scalability and high-availability features provided by the service.
-"""
-
-import asyncio
-import os
-from typing import Hashable
-from urllib.parse import urlsplit
-
-
-from chatsky.core import Context
-
-from .database import DBContextStorage
-from .protocol import get_protocol_install_suggestion
-
-try:
-    import ydb
-    import ydb.aio
-
-    ydb_available = True
-except ImportError:
-    ydb_available = False
-
-
-
[docs]class YDBContextStorage(DBContextStorage): - """ - Version of the :py:class:`.DBContextStorage` for YDB. - - :param path: Standard sqlalchemy URI string. - When using sqlite backend in Windows, keep in mind that you have to use double backslashes '\\' - instead of forward slashes '/' in the file path. - :param table_name: The name of the table to use. - """ - - def __init__(self, path: str, table_name: str = "contexts", timeout=5): - DBContextStorage.__init__(self, path) - protocol, netloc, self.database, _, _ = urlsplit(path) - self.endpoint = "{}://{}".format(protocol, netloc) - self.table_name = table_name - if not ydb_available: - install_suggestion = get_protocol_install_suggestion("grpc") - raise ImportError("`ydb` package is missing.\n" + install_suggestion) - self.driver, self.pool = asyncio.run(_init_drive(timeout, self.endpoint, self.database, self.table_name)) - -
[docs] async def set_item_async(self, key: Hashable, value: Context): - value = Context.model_validate(value) - - async def callee(session): - query = """ - PRAGMA TablePathPrefix("{}"); - DECLARE $queryId AS Utf8; - DECLARE $queryContext AS Json; - UPSERT INTO {} - ( - id, - context - ) - VALUES - ( - $queryId, - $queryContext - ); - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - {"$queryId": str(key), "$queryContext": value.model_dump_json()}, - commit_tx=True, - ) - - return await self.pool.retry_operation(callee)
- -
[docs] async def get_item_async(self, key: Hashable) -> Context: - async def callee(session): - query = """ - PRAGMA TablePathPrefix("{}"); - DECLARE $queryId AS Utf8; - SELECT - id, - context - FROM {} - WHERE id = $queryId; - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - result_sets = await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - { - "$queryId": str(key), - }, - commit_tx=True, - ) - if result_sets[0].rows: - return Context.model_validate_json(result_sets[0].rows[0].context) - else: - raise KeyError - - return await self.pool.retry_operation(callee)
- -
[docs] async def del_item_async(self, key: Hashable): - async def callee(session): - query = """ - PRAGMA TablePathPrefix("{}"); - DECLARE $queryId AS Utf8; - DELETE - FROM {} - WHERE - id = $queryId - ; - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - {"$queryId": str(key)}, - commit_tx=True, - ) - - return await self.pool.retry_operation(callee)
- -
[docs] async def contains_async(self, key: Hashable) -> bool: - async def callee(session): - # new transaction in serializable read write mode - # if query successfully completed you will get result sets. - # otherwise exception will be raised - query = """ - PRAGMA TablePathPrefix("{}"); - DECLARE $queryId AS Utf8; - SELECT - id, - context - FROM {} - WHERE id = $queryId; - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - result_sets = await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - { - "$queryId": str(key), - }, - commit_tx=True, - ) - return len(result_sets[0].rows) > 0 - - return await self.pool.retry_operation(callee)
- -
[docs] async def len_async(self) -> int: - async def callee(session): - query = """ - PRAGMA TablePathPrefix("{}"); - SELECT - COUNT(*) as cnt - FROM {} - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - result_sets = await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - commit_tx=True, - ) - return result_sets[0].rows[0].cnt - - return await self.pool.retry_operation(callee)
- -
[docs] async def clear_async(self): - async def callee(session): - query = """ - PRAGMA TablePathPrefix("{}"); - DECLARE $queryId AS Utf8; - DELETE - FROM {} - ; - """.format( - self.database, self.table_name - ) - prepared_query = await session.prepare(query) - - await session.transaction(ydb.SerializableReadWrite()).execute( - prepared_query, - {}, - commit_tx=True, - ) - - return await self.pool.retry_operation(callee)
- - -
[docs]async def _init_drive(timeout: int, endpoint: str, database: str, table_name: str): - driver = ydb.aio.Driver(endpoint=endpoint, database=database) - await driver.wait(fail_fast=True, timeout=timeout) - - pool = ydb.aio.SessionPool(driver, size=10) - - if not await _is_table_exists(pool, database, table_name): # create table if it does not exist - await _create_table(pool, database, table_name) - return driver, pool
- - -
[docs]async def _is_table_exists(pool, path, table_name) -> bool: - try: - - async def callee(session): - await session.describe_table(os.path.join(path, table_name)) - - await pool.retry_operation(callee) - return True - except ydb.SchemeError: - return False
- - -
[docs]async def _create_table(pool, path, table_name): - async def callee(session): - await session.create_table( - "/".join([path, table_name]), - ydb.TableDescription() - .with_column(ydb.Column("id", ydb.OptionalType(ydb.PrimitiveType.Utf8))) - .with_column(ydb.Column("context", ydb.OptionalType(ydb.PrimitiveType.Json))) - .with_primary_key("id"), - ) - - return await pool.retry_operation(callee)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/context.html b/_modules/chatsky/core/context.html deleted file mode 100644 index b725d8981..000000000 --- a/_modules/chatsky/core/context.html +++ /dev/null @@ -1,835 +0,0 @@ - - - - - - - - - - chatsky.core.context — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.context

-"""
-Context
--------
-Context is a data structure that is used to store information about the current state of a conversation.
-
-It is used to keep track of the user's input, the current stage of the conversation, and any other
-information that is relevant to the current context of a dialog.
-
-The Context data structure provides several key features to make working with data easier.
-Developers can use the context to store any information that is relevant to the current conversation,
-such as user data, session data, conversation history, e.t.c.
-This allows developers to easily access and use this data throughout the conversation flow.
-
-Another important feature of the context is data serialization.
-The context can be easily serialized to a format that can be stored or transmitted, such as JSON.
-This allows developers to save the context data and resume the conversation later.
-"""
-
-from __future__ import annotations
-import logging
-import asyncio
-from uuid import UUID, uuid4
-from typing import Any, Optional, Union, Dict, TYPE_CHECKING
-
-from pydantic import BaseModel, Field
-
-from chatsky.core.message import Message, MessageInitTypes
-from chatsky.slots.slots import SlotManager
-from chatsky.core.node_label import AbsoluteNodeLabel, AbsoluteNodeLabelInitTypes
-
-if TYPE_CHECKING:
-    from chatsky.core.service import ComponentExecutionState
-    from chatsky.core.script import Node
-    from chatsky.core.pipeline import Pipeline
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]def get_last_index(dictionary: dict) -> int: - """ - Obtain the last index from the `dictionary`. - - :param dictionary: Dictionary with unsorted keys. - :return: Last index from the `dictionary`. - :raises ValueError: If the dictionary is empty. - """ - if len(dictionary) == 0: - raise ValueError("Dictionary is empty.") - indices = list(dictionary) - return max(indices)
- - -
[docs]class ContextError(Exception): - """Raised when context methods are not used correctly."""
- - -
[docs]class ServiceState(BaseModel, arbitrary_types_allowed=True): - execution_status: ComponentExecutionState = Field(default="NOT_RUN") - """ - :py:class:`.ComponentExecutionState` of this pipeline service. - Cleared at the end of every turn. - """ - finished_event: asyncio.Event = Field(default_factory=asyncio.Event) - """ - Asyncio `Event` which can be awaited until this service finishes. - Cleared at the end of every turn. - """
- - -
[docs]class FrameworkData(BaseModel, arbitrary_types_allowed=True): - """ - Framework uses this to store data related to any of its modules. - """ - - service_states: Dict[str, ServiceState] = Field(default_factory=dict, exclude=True) - """ - Dictionary containing :py:class:`.ServiceState` of all the pipeline components. - Cleared at the end of every turn. - """ - current_node: Optional[Node] = Field(default=None, exclude=True) - """ - A copy of the current node provided by :py:meth:`~chatsky.core.script.Script.get_inherited_node`. - This node can be safely modified by Processing functions to alter current node fields. - """ - pipeline: Optional[Pipeline] = Field(default=None, exclude=True) - """ - Instance of the pipeline that manages this context. - Can be used to obtain run configuration such as script or fallback label. - """ - stats: Dict[str, Any] = Field(default_factory=dict) - "Enables complex stats collection across multiple turns." - slot_manager: SlotManager = Field(default_factory=SlotManager) - "Stores extracted slots."
- - -
[docs]class Context(BaseModel): - """ - A structure that is used to store data about the context of a dialog. - """ - - id: Union[UUID, int, str] = Field(default_factory=uuid4) - """ - ``id`` is the unique context identifier. By default, randomly generated using ``uuid4``. - ``id`` can be used to trace the user behavior, e.g while collecting the statistical data. - """ - labels: Dict[int, AbsoluteNodeLabel] = Field(default_factory=dict) - """ - ``labels`` stores the history of labels for all passed nodes. - - - key - ``id`` of the turn. - - value - ``label`` of this turn. - - Start label is stored at key ``0``. - IDs go up by ``1`` after that. - """ - requests: Dict[int, Message] = Field(default_factory=dict) - """ - ``requests`` stores the history of all requests received by the pipeline. - - - key - ``id`` of the turn. - - value - ``request`` of this turn. - - First request is stored at key ``1``. - IDs go up by ``1`` after that. - """ - responses: Dict[int, Message] = Field(default_factory=dict) - """ - ``responses`` stores the history of all responses produced by the pipeline. - - - key - ``id`` of the turn. - - value - ``response`` of this turn. - - First response is stored at key ``1``. - IDs go up by ``1`` after that. - """ - misc: Dict[str, Any] = Field(default_factory=dict) - """ - ``misc`` stores any custom data. The framework doesn't use this dictionary, - so storage of any data won't reflect on the work of the internal Chatsky functions. - - - key - Arbitrary data name. - - value - Arbitrary data. - """ - framework_data: FrameworkData = Field(default_factory=FrameworkData) - """ - This attribute is used for storing custom data required for pipeline execution. - It is meant to be used by the framework only. Accessing it may result in pipeline breakage. - """ - -
[docs] @classmethod - def init(cls, start_label: AbsoluteNodeLabelInitTypes, id: Optional[Union[UUID, int, str]] = None): - """Initialize new context from ``start_label`` and, optionally, context ``id``.""" - init_kwargs = { - "labels": {0: AbsoluteNodeLabel.model_validate(start_label)}, - } - if id is None: - return cls(**init_kwargs) - else: - return cls(**init_kwargs, id=id)
- -
[docs] def add_request(self, request: MessageInitTypes): - """ - Add a new ``request`` to the context. - """ - request_message = Message.model_validate(request) - if len(self.requests) == 0: - self.requests[1] = request_message - else: - last_index = get_last_index(self.requests) - self.requests[last_index + 1] = request_message
- -
[docs] def add_response(self, response: MessageInitTypes): - """ - Add a new ``response`` to the context. - """ - response_message = Message.model_validate(response) - if len(self.responses) == 0: - self.responses[1] = response_message - else: - last_index = get_last_index(self.responses) - self.responses[last_index + 1] = response_message
- -
[docs] def add_label(self, label: AbsoluteNodeLabelInitTypes): - """ - Add a new :py:class:`~.AbsoluteNodeLabel` to the context. - - :raises ContextError: If :py:attr:`labels` is empty. - """ - label = AbsoluteNodeLabel.model_validate(label) - if len(self.labels) == 0: - raise ContextError("Labels are empty. Use `Context.init` to initialize context with labels.") - last_index = get_last_index(self.labels) - self.labels[last_index + 1] = label
- - @property - def last_label(self) -> AbsoluteNodeLabel: - """ - Return the last :py:class:`~.AbsoluteNodeLabel` of - the :py:class:`~.Context`. - - :raises ContextError: If :py:attr:`labels` is empty. - """ - if len(self.labels) == 0: - raise ContextError("Labels are empty. Use `Context.init` to initialize context with labels.") - last_index = get_last_index(self.labels) - return self.labels[last_index] - - @property - def last_response(self) -> Optional[Message]: - """ - Return the last response of the current :py:class:`~.Context`. - Return ``None`` if no responses have been added yet. - """ - if len(self.responses) == 0: - return None - last_index = get_last_index(self.responses) - response = self.responses[last_index] - return response - - @property - def last_request(self) -> Message: - """ - Return the last request of the current :py:class:`~.Context`. - - :raises ContextError: If :py:attr:`responses` is empty. - """ - if len(self.requests) == 0: - raise ContextError("No requests have been added.") - last_index = get_last_index(self.requests) - return self.requests[last_index] - - @property - def pipeline(self) -> Pipeline: - """Return :py:attr:`.FrameworkData.pipeline`.""" - pipeline = self.framework_data.pipeline - if pipeline is None: - raise ContextError("Pipeline is not set.") - return pipeline - - @property - def current_node(self) -> Node: - """Return :py:attr:`.FrameworkData.current_node`.""" - node = self.framework_data.current_node - if node is None: - raise ContextError("Current node is not set.") - return node
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/message.html b/_modules/chatsky/core/message.html deleted file mode 100644 index c6aa31dbc..000000000 --- a/_modules/chatsky/core/message.html +++ /dev/null @@ -1,980 +0,0 @@ - - - - - - - - - - chatsky.core.message — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.message

-"""
-Message
--------
-The Message class is a universal data model for representing a message.
-
-It only contains types and properties that are compatible with most messaging services.
-"""
-
-from __future__ import annotations
-from typing import Literal, Optional, List, Union, Dict, Any, TYPE_CHECKING
-from typing_extensions import TypeAlias, Annotated
-from pathlib import Path
-from urllib.request import urlopen
-import uuid
-import abc
-
-from pydantic import Field, FilePath, HttpUrl, model_validator, field_validator, field_serializer
-from pydantic_core import Url
-
-from chatsky.utils.devel import (
-    json_pickle_validator,
-    json_pickle_serializer,
-    pickle_serializer,
-    pickle_validator,
-    JSONSerializableExtras,
-)
-
-if TYPE_CHECKING:
-    from chatsky.messengers.common.interface import MessengerInterfaceWithAttachments
-
-
-
[docs]class DataModel(JSONSerializableExtras): - """ - This class is a Pydantic BaseModel that can have any type and number of extras. - """ - - pass
- - -
[docs]class Attachment(DataModel, abc.ABC): - """ - Chatsky Message attachment base class. - It is capable of serializing and validating all the model fields to JSON. - """ - - chatsky_attachment_type: str
- - -
[docs]class CallbackQuery(Attachment): - """ - This class is a data model that represents a callback query attachment. - It is sent as a response to non-message events, e.g. keyboard UI interactions. - It has query string attribute, that represents the response data string. - """ - - query_string: str - chatsky_attachment_type: Literal["callback_query"] = "callback_query"
- - -
[docs]class Location(Attachment): - """ - This class is a data model that represents a geographical - location on the Earth's surface. - It has two attributes, longitude and latitude, both of which are float values. - If the absolute difference between the latitude and longitude values of the two - locations is less than 0.00004, they are considered equal. - """ - - longitude: float - latitude: float - chatsky_attachment_type: Literal["location"] = "location"
- - -
[docs]class Contact(Attachment): - """ - This class is a data model that represents a contact. - It includes phone number, and user first and last name. - """ - - phone_number: str - first_name: str - last_name: Optional[str] - chatsky_attachment_type: Literal["contact"] = "contact"
- - -
[docs]class Invoice(Attachment): - """ - This class is a data model that represents an invoice. - It includes title, description, currency name and amount. - """ - - title: str - description: str - currency: str - amount: int - chatsky_attachment_type: Literal["invoice"] = "invoice"
- - -
[docs]class PollOption(DataModel): - """ - This class is a data model that represents a poll option. - It includes the option name and votes number. - """ - - text: str - votes: int = Field(default=0) - chatsky_attachment_type: Literal["poll_option"] = "poll_option"
- - -
[docs]class Poll(Attachment): - """ - This class is a data model that represents a poll. - It includes a list of poll options. - """ - - question: str - options: List[PollOption] - chatsky_attachment_type: Literal["poll"] = "poll"
- - -
[docs]class DataAttachment(Attachment): - """ - This class represents an attachment that can be either - a local file, a URL to a file or a ID of a file on a certain server (such as telegram). - This attachment can also be optionally cached for future use. - """ - - source: Optional[Union[HttpUrl, FilePath]] = None - """Attachment source -- either a URL to a file or a local filepath.""" - use_cache: bool = True - """ - Whether to cache the file (only for URL and ID files). - Disable this if you want to always respond with the most up-to-date version of the file. - """ - cached_filename: Optional[Path] = None - """ - This field is used to store a path to cached version of this file (retrieved from id or URL). - This field is managed by framework. - """ - id: Optional[str] = None - """ - ID of the file on a file server (e.g. file_id for telegram attachments). - :py:meth:`~.MessengerInterfaceWithAttachments.get_attachment_bytes` is used to retrieve bytes from ID. - """ - -
[docs] async def _cache_attachment(self, data: bytes, directory: Path) -> None: - """ - Cache attachment, save bytes into a file. - File has a UUID name based on its `self.source` or `self.id`. - - :param data: attachment data bytes. - :param directory: cache directory where attachment will be saved. - """ - - filename = str(uuid.uuid5(uuid.NAMESPACE_URL, str(self.source or self.id))) - self.cached_filename = directory / filename - self.cached_filename.write_bytes(data)
- -
[docs] async def get_bytes(self, from_interface: MessengerInterfaceWithAttachments) -> Optional[bytes]: - """ - Retrieve attachment bytes. - If the attachment is represented by URL or saved in a file, - it will be downloaded or read automatically. - If cache use is allowed and the attachment is cached, cached file will be used. - Otherwise, a :py:meth:`~.MessengerInterfaceWithAttachments.get_attachment_bytes` - will be used for receiving attachment bytes via ID. - - If cache use is allowed and the attachment is a URL or an ID, bytes will be cached locally. - - :param from_interface: messenger interface the attachment was received from. - """ - - if isinstance(self.source, Path): - with open(self.source, "rb") as file: - return file.read() - elif self.use_cache and self.cached_filename is not None and self.cached_filename.exists(): - with open(self.cached_filename, "rb") as file: - return file.read() - elif isinstance(self.source, Url): - with urlopen(self.source.unicode_string()) as url: - attachment_data = url.read() - else: - attachment_data = await from_interface.get_attachment_bytes(self.id) - if self.use_cache: - await self._cache_attachment(attachment_data, from_interface.attachments_directory) - return attachment_data
- -
[docs] @model_validator(mode="before") - @classmethod - def validate_source_or_id(cls, values: dict): - if not isinstance(values, dict): - raise AssertionError(f"Invalid constructor parameters: {str(values)}") - if bool(values.get("source")) == bool(values.get("id")): - raise AssertionError("Attachment type requires exactly one parameter, `source` or `id`, to be set.") - return values
- - -
[docs]class Audio(DataAttachment): - """Represents an audio file attachment.""" - - chatsky_attachment_type: Literal["audio"] = "audio"
- - -
[docs]class Video(DataAttachment): - """Represents a video file attachment.""" - - chatsky_attachment_type: Literal["video"] = "video"
- - -
[docs]class Animation(DataAttachment): - """Represents an animation file attachment.""" - - chatsky_attachment_type: Literal["animation"] = "animation"
- - -
[docs]class Image(DataAttachment): - """Represents an image file attachment.""" - - chatsky_attachment_type: Literal["image"] = "image"
- - -
[docs]class Sticker(DataAttachment): - """Represents a sticker as a file attachment.""" - - chatsky_attachment_type: Literal["sticker"] = "sticker"
- - -
[docs]class Document(DataAttachment): - """Represents a document file attachment.""" - - chatsky_attachment_type: Literal["document"] = "document"
- - -
[docs]class VoiceMessage(DataAttachment): - """Represents a voice message.""" - - chatsky_attachment_type: Literal["voice_message"] = "voice_message"
- - -
[docs]class VideoMessage(DataAttachment): - """Represents a video message.""" - - chatsky_attachment_type: Literal["video_message"] = "video_message"
- - -
[docs]class MediaGroup(Attachment): - """ - Represents a group of media attachments. - Without this class attachments are sent one-by-one. - - Be mindful of limitations that certain services apply - (e.g. Telegram does not allow audio or document files to be mixed with other types when using media groups, - so you should send them separately by putting them directly in :py:attr:`~.Message.attachments`). - """ - - group: List[Union[Audio, Video, Image, Document, DataAttachment]] = Field(default_factory=list) - chatsky_attachment_type: Literal["media_group"] = "media_group"
- - -
[docs]class Message(DataModel): - """ - Class representing a message and contains several - class level variables to store message information. - - It includes message text, list of attachments, annotations, - MISC dictionary (that consists of user-defined parameters) - and original message field that represents - the update received from messenger interface API. - """ - - text: Optional[str] = None - attachments: Optional[ - List[ - Union[ - CallbackQuery, - Location, - Contact, - Invoice, - Poll, - Audio, - Video, - Animation, - Image, - Sticker, - Document, - VoiceMessage, - VideoMessage, - MediaGroup, - DataModel, - ] - ] - ] = None - annotations: Optional[Dict[str, Any]] = None - misc: Optional[Dict[str, Any]] = None - original_message: Optional[Any] = None - - def __init__( # this allows initializing Message with string as positional argument - self, - text: Optional[str] = None, - *, - attachments: Optional[ - List[ - Union[ - CallbackQuery, - Location, - Contact, - Invoice, - Poll, - Audio, - Video, - Animation, - Image, - Sticker, - Document, - VoiceMessage, - VideoMessage, - MediaGroup, - ] - ] - ] = None, - annotations: Optional[Dict[str, Any]] = None, - misc: Optional[Dict[str, Any]] = None, - original_message: Optional[Any] = None, - **kwargs, - ): - super().__init__( - text=text, - attachments=attachments, - annotations=annotations, - misc=misc, - original_message=original_message, - **kwargs, - ) - -
[docs] @field_serializer("annotations", "misc", when_used="json") - def pickle_serialize_dicts(self, value): - """ - Serialize values that are not json-serializable via pickle. - Allows storing arbitrary data in misc/annotations when using context storages. - """ - if isinstance(value, dict): - return json_pickle_serializer(value) - return value
- -
[docs] @field_validator("annotations", "misc", mode="before") - @classmethod - def pickle_validate_dicts(cls, value): - """Restore values serialized with :py:meth:`pickle_serialize_dicts`.""" - if isinstance(value, dict): - return json_pickle_validator(value) - return value
- -
[docs] @field_serializer("original_message", when_used="json") - def pickle_serialize_original_message(self, value): - """ - Cast :py:attr:`original_message` to string via pickle. - Allows storing arbitrary data in this field when using context storages. - """ - if value is not None: - return pickle_serializer(value) - return value
- -
[docs] @field_validator("original_message", mode="before") - @classmethod - def pickle_validate_original_message(cls, value): - """ - Restore :py:attr:`original_message` after being processed with - :py:meth:`pickle_serialize_original_message`. - """ - if value is not None: - return pickle_validator(value) - return value
- - def __str__(self) -> str: - return " ".join([f"{key}='{value}'" for key, value in self.model_dump(exclude_none=True).items()]) - -
[docs] @model_validator(mode="before") - @classmethod - def validate_from_str(cls, data): - """ - Allow instantiating this class from a single string which becomes :py:attr:`Message.text` - """ - if isinstance(data, str): - return {"text": data} - return data
- - -MessageInitTypes: TypeAlias = Union[ - Message, Annotated[dict, "dict following the Message data model"], Annotated[str, "message text"] -] -"""Types that :py:class:`~.Message` can be validated from.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/node_label.html b/_modules/chatsky/core/node_label.html deleted file mode 100644 index f2e255fad..000000000 --- a/_modules/chatsky/core/node_label.html +++ /dev/null @@ -1,722 +0,0 @@ - - - - - - - - - - chatsky.core.node_label — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.node_label

-"""
-Node Label
-----------
-This module defines classes for addressing nodes.
-"""
-
-from __future__ import annotations
-
-from typing import Optional, Union, Tuple, List, TYPE_CHECKING
-from typing_extensions import TypeAlias, Annotated
-
-from pydantic import BaseModel, model_validator, ValidationInfo
-
-if TYPE_CHECKING:
-    from chatsky.core.context import Context
-
-
-
[docs]def _get_current_flow_name(ctx: Context) -> str: - """Get flow name of the current node from context.""" - current_node = ctx.last_label - return current_node.flow_name
- - -
[docs]class NodeLabel(BaseModel, frozen=True): - """ - A label for a node. (a way to address a specific node in the script) - - Can be relative if :py:attr:`flow_name` is ``None``: - such ``NodeLabel`` will reference a node with the name :py:attr:`node_name` - in the current flow. - """ - - flow_name: Optional[str] = None - """ - Name of the flow in the script. - Can be ``None`` in which case this is inherited from the :py:attr:`.Context.current_node`. - """ - node_name: str - """ - Name of the node in the flow. - """ - -
[docs] @model_validator(mode="before") - @classmethod - def validate_from_str_or_tuple(cls, data, info: ValidationInfo): - """ - Allow instantiating of this class from: - - - A single string (node name). Also attempt to get the current flow name from context. - - A tuple or list of two strings (flow and node name). - """ - if isinstance(data, str): - flow_name = None - context = info.context - if isinstance(context, dict): - flow_name = _get_current_flow_name(context.get("ctx")) - return {"flow_name": flow_name, "node_name": data} - elif isinstance(data, (tuple, list)): - if len(data) == 2 and isinstance(data[0], str) and isinstance(data[1], str): - return {"flow_name": data[0], "node_name": data[1]} - else: - raise ValueError( - f"Cannot validate NodeLabel from {data!r}: {type(data).__name__} should contain 2 strings." - ) - return data
- - -NodeLabelInitTypes: TypeAlias = Union[ - NodeLabel, - Annotated[str, "node_name, flow name equal to current flow's name"], - Tuple[Annotated[str, "flow_name"], Annotated[str, "node_name"]], - Annotated[List[str], "list of two strings (flow_name and node_name)"], - Annotated[dict, "dict following the NodeLabel data model"], -] -"""Types that :py:class:`~.NodeLabel` can be validated from.""" - - -
[docs]class AbsoluteNodeLabel(NodeLabel): - """ - A label for a node. (a way to address a specific node in the script) - """ - - flow_name: str - """ - Name of the flow in the script. - """ - node_name: str - """ - Name of the node in the flow. - """ - -
[docs] @model_validator(mode="before") - @classmethod - def validate_from_node_label(cls, data, info: ValidationInfo): - """ - Allow instantiating of this class from :py:class:`NodeLabel`. - - Attempt to get the current flow name from context if :py:attr:`NodeLabel.flow_name` is empty. - """ - if isinstance(data, NodeLabel): - flow_name = data.flow_name - if flow_name is None: - context = info.context - if isinstance(context, dict): - flow_name = _get_current_flow_name(context.get("ctx")) - return {"flow_name": flow_name, "node_name": data.node_name} - return data
- -
[docs] @model_validator(mode="after") - def check_node_exists(self, info: ValidationInfo): - """ - Validate node exists in the script. - """ - context = info.context - if isinstance(context, dict): - ctx: Context = info.context.get("ctx") - if ctx is not None: - script = ctx.pipeline.script - - node = script.get_node(self) - if node is None: - raise ValueError(f"Cannot find node {self!r} in script.") - return self
- - -AbsoluteNodeLabelInitTypes: TypeAlias = Union[ - AbsoluteNodeLabel, - NodeLabel, - Tuple[Annotated[str, "flow_name"], Annotated[str, "node_name"]], - Annotated[List[str], "list of two strings (flow_name and node_name)"], - Annotated[dict, "dict following the AbsoluteNodeLabel data model"], -] -"""Types that :py:class:`~.AbsoluteNodeLabel` can be validated from.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/pipeline.html b/_modules/chatsky/core/pipeline.html deleted file mode 100644 index b6b9dc8a3..000000000 --- a/_modules/chatsky/core/pipeline.html +++ /dev/null @@ -1,887 +0,0 @@ - - - - - - - - - - chatsky.core.pipeline — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.pipeline

-"""
-Pipeline
---------
-Pipeline is the main element of the Chatsky framework.
-
-Pipeline is responsible for managing and executing the various components
-(:py:class:`~chatsky.core.service.component.PipelineComponent`)
-including :py:class:`.Actor`.
-"""
-
-import asyncio
-import logging
-from functools import cached_property
-from typing import Union, List, Dict, Optional, Hashable
-from pydantic import BaseModel, Field, model_validator, computed_field
-
-from chatsky.context_storages import DBContextStorage
-from chatsky.core.script import Script
-from chatsky.core.context import Context
-from chatsky.core.message import Message
-
-from chatsky.messengers.console import CLIMessengerInterface
-from chatsky.messengers.common import MessengerInterface
-from chatsky.slots.slots import GroupSlot
-from chatsky.core.service.group import ServiceGroup, ServiceGroupInitTypes
-from chatsky.core.service.extra import ComponentExtraHandlerInitTypes, BeforeHandler, AfterHandler
-from .service import Service
-from .utils import finalize_service_group, initialize_service_states
-from chatsky.core.service.actor import Actor
-from chatsky.core.node_label import AbsoluteNodeLabel, AbsoluteNodeLabelInitTypes
-from chatsky.core.script_parsing import JSONImporter, Path
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class PipelineServiceGroup(ServiceGroup): - """A service group that allows actor inside.""" - - components: List[Union[Actor, Service, ServiceGroup]]
- - -
[docs]class Pipeline(BaseModel, extra="forbid", arbitrary_types_allowed=True): - """ - Class that automates service execution and creates service pipeline. - """ - - pre_services: ServiceGroup = Field(default_factory=list, validate_default=True) - """ - :py:class:`~.ServiceGroup` that will be executed before Actor. - """ - post_services: ServiceGroup = Field(default_factory=list, validate_default=True) - """ - :py:class:`~.ServiceGroup` that will be executed after :py:class:`~.Actor`. - """ - script: Script - """ - (required) A :py:class:`~.Script` instance (object or dict). - """ - start_label: AbsoluteNodeLabel - """ - (required) The first node of every context. - """ - fallback_label: AbsoluteNodeLabel - """ - Node which will is used if :py:class:`Actor` cannot find the next node. - - This most commonly happens when there are not suitable transitions. - - Defaults to :py:attr:`start_label`. - """ - default_priority: float = 1.0 - """ - Default priority value for :py:class:`~chatsky.core.transition.Transition`. - - Defaults to ``1.0``. - """ - slots: GroupSlot = Field(default_factory=GroupSlot) - """ - Slots configuration. - """ - messenger_interface: MessengerInterface = Field(default_factory=CLIMessengerInterface) - """ - A `MessengerInterface` instance for this pipeline. - - It handles connections to interfaces that provide user requests and accept bot responses. - """ - context_storage: Union[DBContextStorage, Dict] = Field(default_factory=dict) - """ - A :py:class:`~.DBContextStorage` instance for this pipeline or - a dict to store dialog :py:class:`~.Context`. - """ - before_handler: BeforeHandler = Field(default_factory=list, validate_default=True) - """ - :py:class:`~.BeforeHandler` to add to the pipeline service. - """ - after_handler: AfterHandler = Field(default_factory=list, validate_default=True) - """ - :py:class:`~.AfterHandler` to add to the pipeline service. - """ - timeout: Optional[float] = None - """ - Timeout to add to pipeline root service group. - """ - parallelize_processing: bool = False - """ - This flag determines whether or not the functions - defined in the ``PRE_RESPONSE_PROCESSING`` and ``PRE_TRANSITIONS_PROCESSING`` sections - of the script should be parallelized over respective groups. - """ - - def __init__( - self, - script: Union[Script, dict], - start_label: AbsoluteNodeLabelInitTypes, - fallback_label: AbsoluteNodeLabelInitTypes = None, - *, - default_priority: float = None, - slots: GroupSlot = None, - messenger_interface: MessengerInterface = None, - context_storage: Union[DBContextStorage, dict] = None, - pre_services: ServiceGroupInitTypes = None, - post_services: ServiceGroupInitTypes = None, - before_handler: ComponentExtraHandlerInitTypes = None, - after_handler: ComponentExtraHandlerInitTypes = None, - timeout: float = None, - parallelize_processing: bool = None, - ): - if fallback_label is None: - fallback_label = start_label - init_dict = { - "script": script, - "start_label": start_label, - "fallback_label": fallback_label, - "default_priority": default_priority, - "slots": slots, - "messenger_interface": messenger_interface, - "context_storage": context_storage, - "pre_services": pre_services, - "post_services": post_services, - "before_handler": before_handler, - "after_handler": after_handler, - "timeout": timeout, - "parallelize_processing": parallelize_processing, - } - empty_fields = set() - for k, v in init_dict.items(): - if k not in self.model_fields: - raise NotImplementedError("Init method contains a field not in model fields.") - if v is None: - empty_fields.add(k) - for field in empty_fields: - del init_dict[field] - super().__init__(**init_dict) - self.services_pipeline # cache services - -
[docs] @classmethod - def from_file( - cls, - file: Union[str, Path], - custom_dir: Union[str, Path] = "custom", - **overrides, - ) -> "Pipeline": - """ - Create Pipeline by importing it from a file. - A file (json or yaml) should contain a dictionary with keys being a subset of pipeline init parameters. - - See :py:meth:`.JSONImporter.import_pipeline_file` for more information. - - :param file: Path to a file containing pipeline init parameters. - :param custom_dir: Path to a directory containing custom code. - Defaults to "./custom". - If ``file`` does not use custom code, this parameter will not have any effect. - :param overrides: You can pass init parameters to override those imported from the ``file``. - """ - pipeline = JSONImporter(custom_dir=custom_dir).import_pipeline_file(file) - - pipeline.update(overrides) - - return cls(**pipeline)
- - @computed_field - @cached_property - def actor(self) -> Actor: - """An actor instance of the pipeline.""" - return Actor() - - @computed_field - @cached_property - def services_pipeline(self) -> PipelineServiceGroup: - """ - A group containing :py:attr:`.Pipeline.pre_services`, :py:class:`~.Actor` - and :py:attr:`.Pipeline.post_services`. - It has :py:attr:`.Pipeline.before_handler` and :py:attr:`.Pipeline.after_handler` applied to it. - """ - components = [self.pre_services, self.actor, self.post_services] - self.pre_services.name = "pre" - self.post_services.name = "post" - services_pipeline = PipelineServiceGroup( - components=components, - before_handler=self.before_handler, - after_handler=self.after_handler, - timeout=self.timeout, - ) - services_pipeline.name = "" - services_pipeline.path = "" - - finalize_service_group(services_pipeline, path=services_pipeline.path) - - return services_pipeline - -
[docs] @model_validator(mode="after") - def validate_start_label(self): - """Validate :py:attr:`start_label` is in :py:attr:`script`.""" - if self.script.get_node(self.start_label) is None: - raise ValueError(f"Unknown start_label={self.start_label}") - return self
- -
[docs] @model_validator(mode="after") - def validate_fallback_label(self): - """Validate :py:attr:`fallback_label` is in :py:attr:`script`.""" - if self.script.get_node(self.fallback_label) is None: - raise ValueError(f"Unknown fallback_label={self.fallback_label}") - return self
- -
[docs] async def _run_pipeline( - self, request: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: - """ - Method that should be invoked on user input. - This method has the same signature as :py:class:`~chatsky.core.service.types.PipelineRunnerFunction`. - - This method does: - - 1. Retrieve from :py:attr:`context_storage` or initialize context ``ctx_id``. - 2. Update :py:attr:`.Context.misc` with ``update_ctx_misc``. - 3. Set up :py:attr:`.Context.framework_data` fields. - 4. Add ``request`` to the context. - 5. Execute :py:attr:`services_pipeline`. - This includes :py:class:`.Actor` (read :py:meth:`.Actor.run_component` for more information). - 6. Save context in the :py:attr:`context_storage`. - - :return: Modified context ``ctx_id``. - """ - logger.info(f"Running pipeline for context {ctx_id}.") - logger.debug(f"Received request: {request}.") - if ctx_id is None: - ctx = Context.init(self.start_label) - elif isinstance(self.context_storage, DBContextStorage): - ctx = await self.context_storage.get_async(ctx_id, Context.init(self.start_label, id=ctx_id)) - else: - ctx = self.context_storage.get(ctx_id, Context.init(self.start_label, id=ctx_id)) - - if update_ctx_misc is not None: - ctx.misc.update(update_ctx_misc) - - if self.slots is not None: - ctx.framework_data.slot_manager.set_root_slot(self.slots) - - ctx.framework_data.pipeline = self - initialize_service_states(ctx, self.services_pipeline) - - ctx.add_request(request) - await self.services_pipeline(ctx) - - ctx.framework_data.service_states.clear() - ctx.framework_data.pipeline = None - - if isinstance(self.context_storage, DBContextStorage): - await self.context_storage.set_item_async(ctx_id, ctx) - else: - self.context_storage[ctx_id] = ctx - - return ctx
- -
[docs] def run(self): - """ - Method that starts a pipeline and connects to :py:attr:`messenger_interface`. - - It passes :py:meth:`_run_pipeline` to :py:attr:`messenger_interface` as a callback, - so every time user request is received, :py:meth:`_run_pipeline` will be called. - - This method can be both blocking and non-blocking. It depends on current :py:attr:`messenger_interface` nature. - Message interfaces that run in a loop block current thread. - """ - logger.info("Pipeline is accepting requests.") - asyncio.run(self.messenger_interface.connect(self._run_pipeline))
- -
[docs] def __call__( - self, request: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: - """ - Method that executes pipeline once. - Basically, it is a shortcut for :py:meth:`_run_pipeline`. - NB! When pipeline is executed this way, :py:attr:`messenger_interface` won't be initiated nor connected. - - This method has the same signature as :py:class:`~chatsky.core.service.types.PipelineRunnerFunction`. - """ - return asyncio.run(self._run_pipeline(request, ctx_id, update_ctx_misc))
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/script.html b/_modules/chatsky/core/script.html deleted file mode 100644 index 045d52d39..000000000 --- a/_modules/chatsky/core/script.html +++ /dev/null @@ -1,790 +0,0 @@ - - - - - - - - - - chatsky.core.script — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.script

-"""
-Script
-------
-The Script module provides a set of `pydantic` models for representing the dialog graph.
-
-These models are used by :py:class:`~chatsky.core.service.Actor` to define the conversation flow,
-and to determine the appropriate response based on the user's input and the current state of the conversation.
-"""
-
-# %%
-from __future__ import annotations
-import logging
-from typing import List, Optional, Dict
-
-from pydantic import BaseModel, Field, AliasChoices
-
-from chatsky.core.script_function import AnyResponse, BaseProcessing
-from chatsky.core.node_label import AbsoluteNodeLabel
-from chatsky.core.transition import Transition
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class Node(BaseModel, extra="forbid"): - """ - Node is a basic element of the dialog graph. - - Usually used to represent a specific state of a conversation. - """ - - transitions: List[Transition] = Field( - validation_alias=AliasChoices("transitions", "TRANSITIONS"), default_factory=list - ) - """List of transitions possible from this node.""" - response: Optional[AnyResponse] = Field(validation_alias=AliasChoices("response", "RESPONSE"), default=None) - """Response produced when this node is entered.""" - pre_transition: Dict[str, BaseProcessing] = Field( - validation_alias=AliasChoices("pre_transition", "PRE_TRANSITION"), default_factory=dict - ) - """ - A dictionary of :py:class:`.BaseProcessing` functions that are executed before transitions are processed. - Keys of the dictionary act as names for the processing functions. - """ - pre_response: Dict[str, BaseProcessing] = Field( - validation_alias=AliasChoices("pre_response", "PRE_RESPONSE"), default_factory=dict - ) - """ - A dictionary of :py:class:`.BaseProcessing` functions that are executed before response is processed. - Keys of the dictionary act as names for the processing functions. - """ - misc: dict = Field(validation_alias=AliasChoices("misc", "MISC"), default_factory=dict) - """ - A dictionary that is used to store metadata about the node. - - Can be accessed at runtime via :py:attr:`~chatsky.core.context.Context.current_node`. - """ - -
[docs] def inherit_from_other(self, other: Node): - """ - Inherit properties from another node into this one: - - - Extend ``self.transitions`` with :py:attr:`transitions` of the other node; - - Replace response with ``other.response`` if ``self.response`` is ``None``; - - Dictionaries (:py:attr:`pre_transition`, :py:attr:`pre_response` and :py:attr:`misc`) - are appended to this node's dictionaries except for the repeating keys. - For example, ``inherit_from_other({1: 1, 3: 3}, {1: 0, 2: 2}) == {1: 1, 3: 3, 2: 2}``. - - Basically, only non-conflicting properties of ``other`` are inherited. - """ - - def merge_dicts(first: dict, second: dict): - first.update({k: v for k, v in second.items() if k not in first}) - - self.transitions.extend(other.transitions) - if self.response is None: - self.response = other.response - merge_dicts(self.pre_transition, other.pre_transition) - merge_dicts(self.pre_response, other.pre_response) - merge_dicts(self.misc, other.misc) - return self
- - -
[docs]class Flow(BaseModel, extra="allow"): - """ - Flow is a collection of nodes. - This is used to group them by a specific purpose. - """ - - local_node: Node = Field( - validation_alias=AliasChoices("local", "LOCAL", "local_node", "LOCAL_NODE"), default_factory=Node - ) - """ - Node from which all other nodes in this Flow inherit properties - according to :py:meth:`Node.inherit_from_other`. - """ - __pydantic_extra__: Dict[str, Node] - - @property - def nodes(self) -> Dict[str, Node]: - """ - A dictionary of all non-local nodes in this flow. - - Keys in the dictionary acts as names for the nodes. - """ - return self.__pydantic_extra__ - -
[docs] def get_node(self, name: str) -> Optional[Node]: - """ - Get node with the ``name``. - - :return: Node or ``None`` if it doesn't exist. - """ - return self.nodes.get(name)
- - -
[docs]class Script(BaseModel, extra="allow"): - """ - A script is a collection of nodes. - It represents an entire dialog graph. - """ - - global_node: Node = Field( - validation_alias=AliasChoices("global", "GLOBAL", "global_node", "GLOBAL_NODE"), default_factory=Node - ) - """ - Node from which all other nodes in this Script inherit properties - according to :py:meth:`Node.inherit_from_other`. - """ - __pydantic_extra__: Dict[str, Flow] - - @property - def flows(self) -> Dict[str, Flow]: - """ - A dictionary of all flows in this script. - - Keys in the dictionary acts as names for the flows. - """ - return self.__pydantic_extra__ - -
[docs] def get_flow(self, name: str) -> Optional[Flow]: - """ - Get flow with the ``name``. - - :return: Flow or ``None`` if it doesn't exist. - """ - return self.flows.get(name)
- -
[docs] def get_node(self, label: AbsoluteNodeLabel) -> Optional[Node]: - """ - Get node with the ``label``. - - :return: Node or ``None`` if it doesn't exist. - """ - flow = self.get_flow(label.flow_name) - if flow is None: - return None - return flow.get_node(label.node_name)
- -
[docs] def get_inherited_node(self, label: AbsoluteNodeLabel) -> Optional[Node]: - """ - Return a new node that inherits (using :py:meth:`Node.inherit_from_other`) - properties from :py:class:`Node`, :py:attr:`Flow.local_node` - and :py:attr:`Script.global_node` (in that order). - - Flow and node are determined by ``label``. - - This is essentially a copy of the node specified by ``label``, - that inherits properties from ``local_node`` and ``global_node``. - - :return: A new node or ``None`` if it doesn't exist. - """ - flow = self.get_flow(label.flow_name) - if flow is None: - return None - node = flow.get_node(label.node_name) - if node is None: - return None - - inheritant_node = Node() - - return ( - inheritant_node.inherit_from_other(node) - .inherit_from_other(flow.local_node) - .inherit_from_other(self.global_node) - )
- - -GLOBAL = "GLOBAL" -"""Key for :py:attr:`~chatsky.core.script.Script.global_node`.""" -LOCAL = "LOCAL" -"""Key for :py:attr:`~chatsky.core.script.Flow.local_node`.""" -TRANSITIONS = "TRANSITIONS" -"""Key for :py:attr:`~chatsky.core.script.Node.transitions`.""" -RESPONSE = "RESPONSE" -"""Key for :py:attr:`~chatsky.core.script.Node.response`.""" -MISC = "MISC" -"""Key for :py:attr:`~chatsky.core.script.Node.misc`.""" -PRE_RESPONSE = "PRE_RESPONSE" -"""Key for :py:attr:`~chatsky.core.script.Node.pre_response`.""" -PRE_TRANSITION = "PRE_TRANSITION" -"""Key for :py:attr:`~chatsky.core.script.Node.pre_transition`.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/script_function.html b/_modules/chatsky/core/script_function.html deleted file mode 100644 index f61ee1714..000000000 --- a/_modules/chatsky/core/script_function.html +++ /dev/null @@ -1,840 +0,0 @@ - - - - - - - - - - chatsky.core.script_function — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.script_function

-"""
-Script Function
----------------
-This module provides base classes for functions used in :py:class:`~chatsky.core.script.Script` instances.
-
-These functions allow dynamic script configuration and are essential to the scripting process.
-"""
-
-from __future__ import annotations
-
-from typing import Union, Tuple, ClassVar, Optional
-from typing_extensions import Annotated
-from abc import abstractmethod, ABC
-import logging
-
-from pydantic import BaseModel, model_validator, Field
-
-from chatsky.utils.devel import wrap_sync_function_in_async
-from chatsky.core.context import Context
-from chatsky.core.message import Message, MessageInitTypes
-from chatsky.core.node_label import NodeLabel, NodeLabelInitTypes, AbsoluteNodeLabel
-
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class BaseScriptFunc(BaseModel, ABC, frozen=True): # generic doesn't work well with sphinx autosummary - """ - Base class for any script function. - - Defines :py:meth:`wrapped_call` that wraps :py:meth:`call` and handles exceptions and types conversions. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] - """Return type of the script function.""" - -
[docs] @abstractmethod - async def call(self, ctx: Context): - """Implement this to create a custom function.""" - raise NotImplementedError()
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = ""): - """ - Exception-safe wrapper for :py:meth:`__call__`. - - :return: An instance of :py:attr:`return_type` if possible. - Otherwise, an ``Exception`` instance detailing what went wrong. - """ - try: - result = await self(ctx) - logger.debug(f"Function {self.__class__.__name__} returned {result!r}. {info}") - return result - except Exception as exc: - logger.error(f"An exception occurred in {self.__class__.__name__}. {info}", exc_info=exc) - return exc
- -
[docs] async def __call__(self, ctx: Context): - """ - Handle :py:meth:`call`: - - - Call it (regardless of whether it is async); - - Cast returned value to :py:attr:`return_type`. - - :return: An instance of :py:attr:`return_type`. - :raises TypeError: If :py:meth:`call` returned value of incorrect type. - """ - result = await wrap_sync_function_in_async(self.call, ctx) - if not isinstance(self.return_type, tuple) and issubclass(self.return_type, BaseModel): - result = self.return_type.model_validate(result, context={"ctx": ctx}).model_copy(deep=True) - if not isinstance(result, self.return_type): - raise TypeError( - f"Function `call` of {self.__class__.__name__} should return {self.return_type!r}. " - f"Got instead: {result!r}" - ) - return result
- - -
[docs]class ConstScriptFunc(BaseScriptFunc): - """ - Base class for script functions that return a constant value. - """ - - root: None - """Value to return.""" - -
[docs] async def call(self, ctx: Context): - return self.root
- -
[docs] @model_validator(mode="before") - @classmethod - def validate_value(cls, data): - """Allow instantiating this class from its root value.""" - return {"root": data}
- - -
[docs]class BaseCondition(BaseScriptFunc, ABC): - """ - Base class for condition functions. - - These are used in :py:attr:`chatsky.core.transition.Transition.cnd`. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] = bool - -
[docs] @abstractmethod - async def call(self, ctx: Context) -> bool: - raise NotImplementedError
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = "") -> Union[bool, Exception]: - return await super().wrapped_call(ctx, info=info)
- -
[docs] async def __call__(self, ctx: Context) -> bool: - return await super().__call__(ctx)
- -
[docs] async def is_true(self, ctx: Context, *, info: str = "") -> bool: - """Same as :py:meth:`wrapped_call` but instead of exceptions return ``False``.""" - result = await self.wrapped_call(ctx, info=info) - if isinstance(result, Exception): - return False - return result
- - -
[docs]class ConstCondition(ConstScriptFunc, BaseCondition): - root: bool
- - -AnyCondition = Annotated[Union[ConstCondition, BaseCondition], Field(union_mode="left_to_right")] -""" -A type annotation that allows accepting both :py:class:`ConstCondition` and :py:class:`BaseCondition` -while validating :py:class:`ConstCondition` if possible. -""" - - -
[docs]class BaseResponse(BaseScriptFunc, ABC): - """ - Base class for response functions. - - These are used in :py:attr:`chatsky.core.script.Node.response`. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] = Message - -
[docs] @abstractmethod - async def call(self, ctx: Context) -> MessageInitTypes: - raise NotImplementedError
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = "") -> Union[Message, Exception]: - return await super().wrapped_call(ctx, info=info)
- -
[docs] async def __call__(self, ctx: Context) -> Message: - return await super().__call__(ctx)
- - -
[docs]class ConstResponse(ConstScriptFunc, BaseResponse): - root: Message
- - -AnyResponse = Annotated[Union[ConstResponse, BaseResponse], Field(union_mode="left_to_right")] -""" -A type annotation that allows accepting both :py:class:`ConstResponse` and :py:class:`BaseResponse` -while validating :py:class:`ConstResponse` if possible. -""" - - -
[docs]class BaseDestination(BaseScriptFunc, ABC): - """ - Base class for destination functions. - - These are used in :py:attr:`chatsky.core.transition.Transition.dst`. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] = AbsoluteNodeLabel - -
[docs] @abstractmethod - async def call(self, ctx: Context) -> NodeLabelInitTypes: - raise NotImplementedError
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = "") -> Union[AbsoluteNodeLabel, Exception]: - return await super().wrapped_call(ctx, info=info)
- -
[docs] async def __call__(self, ctx: Context) -> AbsoluteNodeLabel: - return await super().__call__(ctx)
- - -
[docs]class ConstDestination(ConstScriptFunc, BaseDestination): - root: NodeLabel
- - -AnyDestination = Annotated[Union[ConstDestination, BaseDestination], Field(union_mode="left_to_right")] -""" -A type annotation that allows accepting both :py:class:`ConstDestination` and :py:class:`BaseDestination` -while validating :py:class:`ConstDestination` if possible. -""" - - -
[docs]class BaseProcessing(BaseScriptFunc, ABC): - """ - Base class for processing functions. - - These are used in :py:attr:`chatsky.core.script.Node.pre_transition` - and :py:attr:`chatsky.core.script.Node.pre_response`. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] = type(None) - -
[docs] @abstractmethod - async def call(self, ctx: Context) -> None: - raise NotImplementedError
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = "") -> Union[None, Exception]: - return await super().wrapped_call(ctx, info=info)
- -
[docs] async def __call__(self, ctx: Context) -> None: - return await super().__call__(ctx)
- - -
[docs]class BasePriority(BaseScriptFunc, ABC): - """ - Base class for priority functions. - - These are used in :py:attr:`chatsky.core.transition.Transition.priority`. - - Has several possible return types: - - - ``float``: Transition successful with the corresponding priority; - - ``True`` or ``None``: Transition successful with the :py:attr:`~chatsky.core.pipeline.Pipeline.default_priority`; - - ``False``: Transition unsuccessful. - """ - - return_type: ClassVar[Union[type, Tuple[type, ...]]] = (float, type(None), bool) - -
[docs] @abstractmethod - async def call(self, ctx: Context) -> Union[float, bool, None]: - raise NotImplementedError
- -
[docs] async def wrapped_call(self, ctx: Context, *, info: str = "") -> Union[float, bool, None, Exception]: - return await super().wrapped_call(ctx, info=info)
- -
[docs] async def __call__(self, ctx: Context) -> Union[float, bool, None]: - return await super().__call__(ctx)
- - -
[docs]class ConstPriority(ConstScriptFunc, BasePriority): - root: Optional[float]
- - -AnyPriority = Annotated[Union[ConstPriority, BasePriority], Field(union_mode="left_to_right")] -""" -A type annotation that allows accepting both :py:class:`ConstPriority` and :py:class:`BasePriority` -while validating :py:class:`ConstPriority` if possible. -""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/script_parsing.html b/_modules/chatsky/core/script_parsing.html deleted file mode 100644 index a7cbffdd9..000000000 --- a/_modules/chatsky/core/script_parsing.html +++ /dev/null @@ -1,900 +0,0 @@ - - - - - - - - - - chatsky.core.script_parsing — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.script_parsing

-"""
-Pipeline File Import
---------------------
-This module introduces tools that allow importing Pipeline objects from
-json/yaml files.
-
-- :py:class:`JSONImporter` is a class that imports pipeline from files
-- :py:func:`get_chatsky_objects` is a function that provides an index of objects commonly used in a Pipeline definition.
-"""
-
-from typing import Union, Optional, Any, List, Tuple
-import importlib
-import importlib.util
-import importlib.machinery
-import sys
-import logging
-from pathlib import Path
-import json
-from inspect import ismodule
-from functools import reduce
-from contextlib import contextmanager
-
-from pydantic import JsonValue
-
-try:
-    import yaml
-
-    yaml_available = True
-except ImportError:
-    yaml_available = False
-
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class JSONImportError(Exception): - """An exception for incorrect usage of :py:class:`JSONImporter`.""" - - __notes__ = [ - "Read the guide on Pipeline import from file: " - "https://deeppavlov.github.io/chatsky/user_guides/pipeline_import.html" - ]
- - -
[docs]class JSONImporter: - """ - Enables pipeline import from file. - - Since Pipeline and all its components are already pydantic ``BaseModel``, - the only purpose of this class is to allow importing and instantiating arbitrary objects. - - Import is done by replacing strings of certain patterns with corresponding objects. - This process is implemented in :py:meth:`resolve_string_reference`. - - Instantiating is done by replacing dictionaries where a single key is an imported object - with an initialized object where arguments are specified by the dictionary values. - This process is implemented in :py:meth:`replace_resolvable_objects` and - :py:meth:`parse_args`. - - :param custom_dir: Path to the directory containing custom code available for import under the - :py:attr:`CUSTOM_DIR_NAMESPACE_PREFIX`. - """ - - CHATSKY_NAMESPACE_PREFIX: str = "chatsky." - """ - Prefix that indicates an import from the `chatsky` library. - - This class variable can be changed to allow using a different prefix. - """ - CUSTOM_DIR_NAMESPACE_PREFIX: str = "custom." - """ - Prefix that indicates an import from the custom directory. - - This class variable can be changed to allow using a different prefix. - """ - EXTERNAL_LIB_NAMESPACE_PREFIX: str = "external:" - """ - Prefix that indicates an import from any library. - - This class variable can be changed to allow using a different prefix. - """ - - def __init__(self, custom_dir: Union[str, Path]): - self.custom_dir: Path = Path(custom_dir).absolute() - self.custom_dir_location: str = str(self.custom_dir.parent) - self.custom_dir_stem: str = str(self.custom_dir.stem) - -
[docs] @staticmethod - def is_resolvable(value: str) -> bool: - """ - Check if ``value`` starts with any of the namespace prefixes: - - - :py:attr:`CHATSKY_NAMESPACE_PREFIX`; - - :py:attr:`CUSTOM_DIR_NAMESPACE_PREFIX`; - - :py:attr:`EXTERNAL_LIB_NAMESPACE_PREFIX`. - - :return: Whether the value should be resolved (starts with a namespace prefix). - """ - return ( - value.startswith(JSONImporter.CHATSKY_NAMESPACE_PREFIX) - or value.startswith(JSONImporter.CUSTOM_DIR_NAMESPACE_PREFIX) - or value.startswith(JSONImporter.EXTERNAL_LIB_NAMESPACE_PREFIX) - )
- -
[docs] @staticmethod - @contextmanager - def sys_path_append(path): - """ - Append ``path`` to ``sys.path`` before yielding and - restore ``sys.path`` to initial state after returning. - """ - sys_path = sys.path.copy() - sys.path.append(path) - yield - sys.path = sys_path
- -
[docs] @staticmethod - def replace_prefix(string, old_prefix, new_prefix) -> str: - """ - Replace ``old_prefix`` in ``string`` with ``new_prefix``. - - :raises ValueError: If the ``string`` does not begin with ``old_prefix``. - :return: A new string with a new prefix. - """ - if not string.startswith(old_prefix): - raise ValueError(f"String {string!r} does not start with {old_prefix!r}") - return new_prefix + string[len(old_prefix) :] # noqa: E203
- -
[docs] def resolve_string_reference(self, obj: str) -> Any: - """ - Import an object indicated by ``obj``. - - First, ``obj`` is pre-processed -- prefixes are replaced to allow import: - - - :py:attr:`CUSTOM_DIR_NAMESPACE_PREFIX` is replaced ``{stem}.`` where `stem` is the stem of the custom dir; - - :py:attr:`CHATSKY_NAMESPACE_PREFIX` is replaced with ``chatsky.``; - - :py:attr:`EXTERNAL_LIB_NAMESPACE_PREFIX` is removed. - - Next the resulting string is imported: - If the string is ``a.b.c.d``, the following is tried in order: - - 1. ``from a import b; return b.c.d`` - 2. ``from a.b import c; return c.d`` - 3. ``from a.b.c import d; return d`` - - For custom dir imports; parent of the custom dir is appended to ``sys.path`` via :py:meth:`sys_path_append`. - - :return: An imported object. - :raises ValueError: If ``obj`` does not begin with any of the prefixes (is not :py:meth:`is_resolvable`). - :raises JSONImportError: If a string could not be imported. Includes exceptions raised on every import attempt. - """ - # prepare obj string - if obj.startswith(self.CUSTOM_DIR_NAMESPACE_PREFIX): - if not self.custom_dir.exists(): - raise JSONImportError(f"Could not find directory {self.custom_dir}") - obj = self.replace_prefix(obj, self.CUSTOM_DIR_NAMESPACE_PREFIX, self.custom_dir_stem + ".") - - elif obj.startswith(self.CHATSKY_NAMESPACE_PREFIX): - obj = self.replace_prefix(obj, self.CHATSKY_NAMESPACE_PREFIX, "chatsky.") - - elif obj.startswith(self.EXTERNAL_LIB_NAMESPACE_PREFIX): - obj = self.replace_prefix(obj, self.EXTERNAL_LIB_NAMESPACE_PREFIX, "") - - else: - raise ValueError(f"Could not find a namespace prefix: {obj}") - - # import obj - split = obj.split(".") - exceptions: List[Exception] = [] - - for module_split in range(1, len(split)): - module_name = ".".join(split[:module_split]) - object_name = split[module_split:] - try: - with self.sys_path_append(self.custom_dir_location): - module = importlib.import_module(module_name) - return reduce(getattr, [module, *object_name]) - except Exception as exc: - exceptions.append(exc) - logger.debug(f"Exception attempting to import {object_name} from {module_name!r}", exc_info=exc) - raise JSONImportError(f"Could not import {obj}") from Exception(exceptions)
- -
[docs] def parse_args(self, value: JsonValue) -> Tuple[list, dict]: - """ - Parse ``value`` into args and kwargs: - - - If ``value`` is a dictionary, it is returned as kwargs; - - If ``value`` is a list, it is returned as args; - - If ``value`` is ``None``, both args and kwargs are empty; - - If ``value`` is anything else, it is returned as the only arg. - - :return: A tuple of args and kwargs. - """ - args = [] - kwargs = {} - value = self.replace_resolvable_objects(value) - if isinstance(value, dict): - kwargs = value - elif isinstance(value, list): - args = value - elif value is not None: # none is used when no argument is passed: e.g. `dst.Previous:` does not accept args - args = [value] - - return args, kwargs
- -
[docs] def replace_resolvable_objects(self, obj: JsonValue) -> Any: - """ - Replace any resolvable objects inside ``obj`` with their resolved versions and - initialize any that are the only key of a dictionary. - - This method iterates over every value inside ``obj`` (which is ``JsonValue``). - Any string that :py:meth:`is_resolvable` is replaced with an object return from - :py:meth:`resolve_string_reference`. - This is done only once (i.e. if a string is resolved to another resolvable string, - that string is not resolved). - - Any dictionaries that contain only one resolvable key are replaced with a result of - ``resolve_string_reference(key)(*args, **kwargs)`` (the object is initialized) - where ``args`` and ``kwargs`` is the result of :py:meth:`parse_args` - on the value of the dictionary. - - :return: A new object with replaced resolvable strings and dictionaries. - """ - if isinstance(obj, dict): - keys = obj.keys() - if len(keys) == 1: - key = keys.__iter__().__next__() - if self.is_resolvable(key): - args, kwargs = self.parse_args(obj[key]) - return self.resolve_string_reference(key)(*args, **kwargs) - - return {k: (self.replace_resolvable_objects(v)) for k, v in obj.items()} - elif isinstance(obj, list): - return [self.replace_resolvable_objects(item) for item in obj] - elif isinstance(obj, str): - if self.is_resolvable(obj): - return self.resolve_string_reference(obj) - return obj
- -
[docs] def import_pipeline_file(self, file: Union[str, Path]) -> dict: - """ - Import a dictionary from a json/yaml file and replace resolvable objects in it. - - :return: A result of :py:meth:`replace_resolvable_objects` on the dictionary. - :raises JSONImportError: If a file does not have a correct file extension. - :raises JSONImportError: If an imported object from file is not a dictionary. - """ - file = Path(file).absolute() - - with open(file, "r", encoding="utf-8") as fd: - if file.suffix == ".json": - pipeline = json.load(fd) - elif file.suffix in (".yaml", ".yml"): - if not yaml_available: - raise ImportError("`pyyaml` package is missing.\nRun `pip install chatsky[yaml]`.") - pipeline = yaml.safe_load(fd) - else: - raise JSONImportError("File should have a `.json`, `.yaml` or `.yml` extension") - if not isinstance(pipeline, dict): - raise JSONImportError("File should contain a dict") - - logger.info(f"Loaded file {file}") - return self.replace_resolvable_objects(pipeline)
- - -
[docs]def get_chatsky_objects(): - """ - Return an index of most commonly used ``chatsky`` objects (in the context of pipeline initialization). - - :return: A dictionary where keys are names of the objects (e.g. ``chatsky.core.Message``) and values - are the objects. - The items in the dictionary are all the objects from the ``__init__`` files of the following modules: - - - "chatsky.cnd"; - - "chatsky.rsp"; - - "chatsky.dst"; - - "chatsky.proc"; - - "chatsky.core"; - - "chatsky.core.service"; - - "chatsky.slots"; - - "chatsky.context_storages"; - - "chatsky.messengers". - """ - json_importer = JSONImporter(custom_dir="none") - - def get_objects_from_submodule(submodule_name: str, alias: Optional[str] = None): - module = json_importer.resolve_string_reference(submodule_name) - - return { - ".".join([alias or submodule_name, name]): obj - for name, obj in module.__dict__.items() - if not name.startswith("_") and not ismodule(obj) - } - - return { - k: v - for module in ( - "chatsky.cnd", - "chatsky.rsp", - "chatsky.dst", - "chatsky.proc", - "chatsky.core", - "chatsky.core.service", - "chatsky.slots", - "chatsky.context_storages", - "chatsky.messengers", - # "chatsky.stats", - # "chatsky.utils", - ) - for k, v in get_objects_from_submodule(module).items() - }
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/actor.html b/_modules/chatsky/core/service/actor.html deleted file mode 100644 index 7e20a21f8..000000000 --- a/_modules/chatsky/core/service/actor.html +++ /dev/null @@ -1,717 +0,0 @@ - - - - - - - - - - chatsky.core.service.actor — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.actor

-"""
-Actor
------
-Actor is a component of :py:class:`.Pipeline`, that processes the :py:class:`.Script`.
-
-It is responsible for determining the next node and getting response from it.
-
-The actor acts as a bridge between the user's input and the dialog graph,
-making sure that the conversation follows the expected flow.
-
-More details on the processing can be found in the documentation for
-:py:meth:`Actor.run_component`.
-"""
-
-from __future__ import annotations
-import logging
-import asyncio
-from typing import Dict
-
-from chatsky.core.service.component import PipelineComponent
-from chatsky.core.transition import get_next_label
-from chatsky.core.message import Message
-
-from chatsky.core.context import Context
-from chatsky.core.script_function import BaseProcessing
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class Actor(PipelineComponent): - """ - The class which is used to process :py:class:`~chatsky.core.context.Context` - according to the :py:class:`~chatsky.core.script.Script`. - """ - - @property - def computed_name(self) -> str: - """ - "actor" - """ - return "actor" - -
[docs] async def run_component(self, ctx: Context) -> None: - """ - Process the context in the following way: - - 1. Run pre-transition of the :py:attr:`.Context.current_node`. - 2. Determine and save the next node based on :py:attr:`~chatsky.core.script.Node.transitions` - of the :py:attr:`.Context.current_node`. - 3. Run pre-response of the :py:attr:`.Context.current_node`. - 4. Determine and save the response of the :py:attr:`.Context.current_node` - """ - next_label = ctx.pipeline.fallback_label - - try: - ctx.framework_data.current_node = ctx.pipeline.script.get_inherited_node(ctx.last_label) - - logger.debug("Running pre_transition") - await self._run_processing(ctx.current_node.pre_transition, ctx) - - logger.debug("Running transitions") - - destination_result = await get_next_label(ctx, ctx.current_node.transitions, ctx.pipeline.default_priority) - if destination_result is not None: - next_label = destination_result - except Exception as exc: - logger.exception("Exception occurred during transition processing.", exc_info=exc) - - logger.debug(f"Next label: {next_label}") - - ctx.add_label(next_label) - - response = Message() - - try: - ctx.framework_data.current_node = ctx.pipeline.script.get_inherited_node(next_label) - - logger.debug("Running pre_response") - await self._run_processing(ctx.current_node.pre_response, ctx) - - node_response = ctx.current_node.response - if node_response is not None: - response_result = await node_response.wrapped_call(ctx) - if isinstance(response_result, Message): - response = response_result - logger.debug(f"Produced response {response}.") - else: - logger.debug("Response was not produced.") - else: - logger.debug("Node has empty response.") - except Exception as exc: - logger.exception("Exception occurred during response processing.", exc_info=exc) - - ctx.add_response(response)
- -
[docs] @staticmethod - async def _run_processing_parallel(processing: Dict[str, BaseProcessing], ctx: Context) -> None: - """ - Execute :py:class:`.BaseProcessing` functions simultaneously, independent of the order. - - Picked depending on the value of the :py:class:`.Pipeline`'s `parallelize_processing` flag. - """ - await asyncio.gather( - *[func.wrapped_call(ctx, info=f"processing_name={name!r}") for name, func in processing.items()] - )
- -
[docs] @staticmethod - async def _run_processing_sequential(processing: Dict[str, BaseProcessing], ctx: Context) -> None: - """ - Execute :py:class:`.BaseProcessing` functions in-order. - - Picked depending on the value of the :py:class:`.Pipeline`'s `parallelize_processing` flag. - """ - for name, func in processing.items(): - await func.wrapped_call(ctx, info=f"processing_name={name!r}")
- -
[docs] @staticmethod - async def _run_processing(processing: Dict[str, BaseProcessing], ctx: Context) -> None: - """ - Run :py:class:`.BaseProcessing` functions. - - The execution order depends on the value of the :py:class:`.Pipeline`'s - `parallelize_processing` flag. - """ - if ctx.pipeline.parallelize_processing: - await Actor._run_processing_parallel(processing, ctx) - else: - await Actor._run_processing_sequential(processing, ctx)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/component.html b/_modules/chatsky/core/service/component.html deleted file mode 100644 index bf5801f0e..000000000 --- a/_modules/chatsky/core/service/component.html +++ /dev/null @@ -1,772 +0,0 @@ - - - - - - - - - - chatsky.core.service.component — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.component

-"""
-Component
----------
-The Component module defines a :py:class:`.PipelineComponent` class.
-
-This is a base class for pipeline processing and is responsible for performing a specific task.
-"""
-
-from __future__ import annotations
-
-import logging
-import abc
-import asyncio
-from typing import Optional, TYPE_CHECKING
-from pydantic import BaseModel, Field, field_validator
-
-from chatsky.core.service.extra import BeforeHandler, AfterHandler
-from chatsky.core.script_function import AnyCondition
-from chatsky.core.service.types import (
-    ComponentExecutionState,
-    ExtraHandlerType,
-    ExtraHandlerFunction,
-)
-
-logger = logging.getLogger(__name__)
-
-if TYPE_CHECKING:
-    from chatsky.core.context import Context
-
-
-
[docs]class PipelineComponent(abc.ABC, BaseModel, extra="forbid", arbitrary_types_allowed=True): - """ - Base class for a single task processed by :py:class:`.Pipeline`. - """ - - before_handler: BeforeHandler = Field(default_factory=BeforeHandler) - """ - :py:class:`~.BeforeHandler`, associated with this component. - """ - after_handler: AfterHandler = Field(default_factory=AfterHandler) - """ - :py:class:`~.AfterHandler`, associated with this component. - """ - timeout: Optional[float] = None - """ - Maximum component execution time (in seconds), - if it exceeds this time, it is interrupted. - """ - concurrent: bool = False - """ - Optional flag that indicates whether this component - should be executed concurrently with adjacent concurrent components. - """ - start_condition: AnyCondition = Field(default=True, validate_default=True) - """ - :py:data:`~.AnyCondition` that is invoked before each component execution; - component is executed only if it returns ``True``. - """ - name: Optional[str] = None - """ - Name of the component. Defaults to :py:attr:`.computed_name` potentially modified by - :py:func:`~chatsky.core.utils.rename_component_incrementing`. - - See :py:meth:`.validate_name` for rules. - """ - path: Optional[str] = None - """ - Separated by dots path to component, is universally unique. - """ - -
[docs] @field_validator("name") - @classmethod - def validate_name(cls, name: str): - """ - Validate this component's name: - - Name cannot be empty or contain "." or "#". - - :raises ValueError: If name failed validation. - """ - if name is not None: - if name == "": - raise ValueError("Name cannot be blank.") - if "." in name: - raise ValueError(f"Name cannot contain '.': {name!r}.") - if "#" in name: - raise ValueError(f"Name cannot contain '#': {name!r}.") - - return name
- -
[docs] def _set_state(self, ctx: Context, value: ComponentExecutionState): - """ - Method for component runtime state setting, state is preserved in :py:attr:`.Context.framework_data`. - - :param ctx: :py:class:`.Context` to keep state in. - :param value: State to set. - """ - ctx.framework_data.service_states[self.path].execution_status = value
- -
[docs] def get_state(self, ctx: Context) -> ComponentExecutionState: - """ - Method for component runtime state getting, state is preserved in :py:attr:`.Context.framework_data`. - - :param ctx: :py:class:`~.Context` to get state from. - :return: :py:class:`.ComponentExecutionState` of this service. - """ - return ctx.framework_data.service_states[self.path].execution_status
- -
[docs] @abc.abstractmethod - async def run_component(self, ctx: Context) -> Optional[ComponentExecutionState]: - """ - Run this component. - - :param ctx: Current dialog :py:class:`~.Context`. - """ - raise NotImplementedError
- - @property - def computed_name(self) -> str: - """ - Default name that is used if :py:attr:`~.PipelineComponent.name` is not defined. - - In case two components in a :py:class:`~chatsky.core.service.group.ServiceGroup` have the same - :py:attr:`.computed_name` an incrementing number is appended to the name. - """ - return "noname_service" - -
[docs] async def _run(self, ctx: Context) -> None: - """ - A method for running a pipeline component. Executes extra handlers before and after execution, - launches :py:meth:`.run_component` method. This method is run after the component's timeout is set (if needed). - - :param ctx: Current dialog :py:class:`~.Context`. - """ - - async def _inner_run(): - if await self.start_condition(ctx): - await self.before_handler(ctx, self) - - self._set_state(ctx, ComponentExecutionState.RUNNING) - logger.debug(f"Running component {self.path!r}") - result = await self.run_component(ctx) - if isinstance(result, ComponentExecutionState): - self._set_state(ctx, result) - else: - self._set_state(ctx, ComponentExecutionState.FINISHED) - - await self.after_handler(ctx, self) - else: - self._set_state(ctx, ComponentExecutionState.NOT_RUN) - - try: - await asyncio.wait_for(_inner_run(), timeout=self.timeout) - except Exception as exc: - self._set_state(ctx, ComponentExecutionState.FAILED) - logger.error(f"Service '{self.name}' execution failed!", exc_info=exc) - finally: - ctx.framework_data.service_states[self.path].finished_event.set()
- -
[docs] async def __call__(self, ctx: Context) -> None: - """ - A method for calling pipeline components. - It sets up timeout and executes it using :py:meth:`_run` method. - - :param ctx: Current dialog :py:class:`~.Context`. - :return: ``None`` - """ - await self._run(ctx)
- -
[docs] def add_extra_handler(self, extra_handler_type: ExtraHandlerType, extra_handler: ExtraHandlerFunction): - """ - Add extra handler to this component. - - :param extra_handler_type: A type of extra handler to add (before or after). - :param extra_handler: Function to add to the component as an extra handler. - """ - if extra_handler_type == ExtraHandlerType.BEFORE: - target = self.before_handler - elif extra_handler_type == ExtraHandlerType.AFTER: - target = self.after_handler - else: - raise ValueError(f"Unrecognized ExtraHandlerType: {extra_handler_type}") - target.functions.append(extra_handler)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/extra.html b/_modules/chatsky/core/service/extra.html deleted file mode 100644 index 47365cb39..000000000 --- a/_modules/chatsky/core/service/extra.html +++ /dev/null @@ -1,748 +0,0 @@ - - - - - - - - - - chatsky.core.service.extra — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.extra

-"""
-Extra Handler
--------------
-Extra handlers are functions that are executed before or after a
-:py:class:`~chatsky.core.service.component.PipelineComponent`.
-"""
-
-from __future__ import annotations
-import asyncio
-import logging
-import inspect
-import abc
-from typing import Optional, List, Any, ClassVar, Union, Callable, TYPE_CHECKING
-from typing_extensions import Annotated, TypeAlias
-
-from pydantic import BaseModel, model_validator, Field
-
-from chatsky.core.context import Context
-
-from chatsky.utils.devel.async_helpers import wrap_sync_function_in_async
-from chatsky.core.service.types import (
-    ExtraHandlerType,
-    ExtraHandlerFunction,
-    ExtraHandlerRuntimeInfo,
-)
-
-logger = logging.getLogger(__name__)
-
-if TYPE_CHECKING:
-    from chatsky.core.service import PipelineComponent
-
-
-
[docs]class ComponentExtraHandler(BaseModel, abc.ABC, extra="forbid", arbitrary_types_allowed=True): - """ - Class, representing an extra handler for pipeline components. - - A component extra handler is a set of functions, attached to pipeline component (before or after it). - Extra handlers should execute supportive tasks (like time or resources measurement, minor data transformations). - """ - - functions: List[ExtraHandlerFunction] = Field(default_factory=list) - """ - A list or instance of :py:data:`~.ExtraHandlerFunction`. - """ - stage: ClassVar[ExtraHandlerType] - """ - An :py:class:`~.ExtraHandlerType`, specifying whether this handler will - be executed before or after pipeline component. - """ - timeout: Optional[float] = None - """ - Maximum component execution time (in seconds), - if it exceeds this time, it is interrupted. - """ - concurrent: bool = False - """ - A flag that indicates whether the extra handler's functions - should be executed concurrently. False by default. - """ - -
[docs] @model_validator(mode="before") - @classmethod - def functions_validator(cls, data: Any): - """ - Add support for initializing from a `Callable` or List[`Callable`]. - Casts `functions` to `list` if it's not already. - """ - if isinstance(data, list): - result = {"functions": data} - elif callable(data): - result = {"functions": [data]} - else: - result = data - - if isinstance(result, dict): - if ("functions" in result) and (not isinstance(result["functions"], list)): - result["functions"] = [result["functions"]] - return result
- -
[docs] async def _run_function(self, func: ExtraHandlerFunction, ctx: Context, component: PipelineComponent): - handler_params = len(inspect.signature(func).parameters) - if handler_params == 1: - await wrap_sync_function_in_async(func, ctx) - elif handler_params == 2: - extra_handler_runtime_info = ExtraHandlerRuntimeInfo(stage=self.stage, component=component) - await wrap_sync_function_in_async(func, ctx, extra_handler_runtime_info) - else: - raise Exception( - f"Too many parameters required for component {component.name} {self.stage}" - f" wrapper handler '{func.__name__}': {handler_params}!" - )
- -
[docs] async def _run(self, ctx: Context, component_info: PipelineComponent): - """ - Method for executing one of the extra handler functions (before or after). - If the function is not set, nothing happens. - - :param ctx: current dialog context. - :param component_info: associated component's `self` object. - :return: `None` - """ - - if self.concurrent: - await asyncio.gather(*[self._run_function(func, ctx, component_info) for func in self.functions]) - else: - for func in self.functions: - await self._run_function(func, ctx, component_info)
- -
[docs] async def __call__(self, ctx: Context, component_info: PipelineComponent): - """ - A method for calling an extra handler. - It sets up a timeout and executes it using `_run` method. - - :param ctx: (required) Current dialog `Context`. - :param component_info: associated component's `self` object. - """ - task = asyncio.create_task(self._run(ctx, component_info)) - await asyncio.wait_for(task, timeout=self.timeout)
- - -
[docs]class BeforeHandler(ComponentExtraHandler): - """ - A handler for extra functions that are executed before the component's main function. - - :param functions: A list of callables that will be executed - before the component's main function. - :type functions: List[ExtraHandlerFunction] - :param timeout: Optional timeout for the execution of the extra functions, in - seconds. - :param concurrent: Optional flag that indicates whether the extra functions - should be executed concurrently. False by default. - """ - - stage: ClassVar[ExtraHandlerType] = ExtraHandlerType.BEFORE
- - -
[docs]class AfterHandler(ComponentExtraHandler): - """ - A handler for extra functions that are executed after the component's main function. - - :param functions: A list of callables that will be executed - after the component's main function. - :type functions: List[ExtraHandlerFunction] - :param timeout: Optional timeout for the execution of the extra functions, in - seconds. - :param concurrent: Optional flag that indicates whether the extra functions - should be executed concurrently. False by default. - """ - - stage: ClassVar[ExtraHandlerType] = ExtraHandlerType.AFTER
- - -ComponentExtraHandlerInitTypes: TypeAlias = Union[ - ComponentExtraHandler, - Annotated[dict, "dict following the ComponentExtraHandler data model"], - Annotated[Callable, "a singular function for the extra handler"], - Annotated[List[Callable], "functions for the extra handler"], -] -"""Types that :py:class:`~.ComponentExtraHandler` can be validated from.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/group.html b/_modules/chatsky/core/service/group.html deleted file mode 100644 index 19fa5791f..000000000 --- a/_modules/chatsky/core/service/group.html +++ /dev/null @@ -1,741 +0,0 @@ - - - - - - - - - - chatsky.core.service.group — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.group

-"""
-Service Group
--------------
-The Service Group module contains the ServiceGroup class, which is used to represent a group of related services.
-
-This class provides a way to organize and manage multiple services as a single unit,
-allowing for easier management and organization of the services within the pipeline.
-"""
-
-from __future__ import annotations
-import asyncio
-import logging
-from typing import List, Union, Any, Optional
-from typing_extensions import TypeAlias, Annotated
-
-from pydantic import model_validator, Field
-
-from chatsky.core.service.extra import BeforeHandler, AfterHandler
-from chatsky.core.script_function import AnyCondition
-from chatsky.core.context import Context
-from chatsky.core.service.actor import Actor
-from chatsky.core.service.component import PipelineComponent
-from chatsky.core.service.types import (
-    ComponentExecutionState,
-    ExtraHandlerType,
-    ExtraHandlerConditionFunction,
-    ExtraHandlerFunction,
-)
-from .service import Service, ServiceInitTypes
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class ServiceGroup(PipelineComponent): - """ - A service group class. - """ - - components: List[ - Union[ - Service, - ServiceGroup, - ] - ] - """ - A :py:class:`~.ServiceGroup` object, that will be added to the group. - """ - fully_concurrent: bool = False - """ - Whether this should run all components inside it concurrently - (regardless of their `concurrent` attribute. - This is not recursive (applies only to first level components). - Default value is False. - """ - # Repeating inherited fields for better documentation. - before_handler: BeforeHandler = Field(default_factory=BeforeHandler) - after_handler: AfterHandler = Field(default_factory=AfterHandler) - timeout: Optional[float] = None - concurrent: bool = False - start_condition: AnyCondition = Field(default=True, validate_default=True) - name: Optional[str] = None - path: Optional[str] = None - -
[docs] @model_validator(mode="before") - @classmethod - def components_validator(cls, data: Any): - """ - Add support for initializing from a list of :py:class:`~.PipelineComponent`. - """ - if isinstance(data, list): - result = {"components": data} - else: - result = data - - if isinstance(result, dict): - if ("components" in result) and (not isinstance(result["components"], list)): - result["components"] = [result["components"]] - return result
- -
[docs] async def run_component(self, ctx: Context) -> Optional[ComponentExecutionState]: - """ - Run components of this service group. - - If :py:attr:`.fully_concurrent` flag is set to True, all :py:attr:`.components` will run concurrently - (via ``asyncio.gather``). - - Otherwise, all non-concurrent components execute one after another - while consecutive concurrent components are run concurrently (via ``asyncio.gather``). - - :param ctx: Current dialog context. - :return: :py:attr:`.ComponentExecutionState.FAILED` if any component failed. - """ - if self.fully_concurrent: - await asyncio.gather(*[service(ctx) for service in self.components]) - else: - current_subgroup = [] - for component in self.components: - if component.concurrent: - current_subgroup.append(component) - else: - await asyncio.gather(*[service(ctx) for service in current_subgroup]) - await component(ctx) - current_subgroup = [] - await asyncio.gather(*[service(ctx) for service in current_subgroup]) - - failed = any([service.get_state(ctx) == ComponentExecutionState.FAILED for service in self.components]) - if failed: - return ComponentExecutionState.FAILED
- -
[docs] def add_extra_handler( - self, - extra_handler_type: ExtraHandlerType, - extra_handler: ExtraHandlerFunction, - condition: ExtraHandlerConditionFunction = lambda path: False, - ): - """ - Add extra handler to this group. - - For every component in the group, ``condition`` is called with the path of that component - to determine whether to add extra handler to that component. - - :param extra_handler_type: Extra handler type (before or after). - :param extra_handler: Function to add as an extra handler. - :type extra_handler: :py:data:`.ExtraHandlerFunction` - :param condition: Condition function to determine if extra handler should be added to specific subcomponents. - Defaults to a function returning False. - :type condition: :py:data:`.ExtraHandlerConditionFunction` - """ - super().add_extra_handler(extra_handler_type, extra_handler) - for service in self.components: - if not condition(service.path): - continue - if isinstance(service, ServiceGroup): - service.add_extra_handler(extra_handler_type, extra_handler, condition) - else: - service.add_extra_handler(extra_handler_type, extra_handler)
- - @property - def computed_name(self) -> str: - """ - "service_group" - """ - return "service_group"
- - -ServiceGroupInitTypes: TypeAlias = Union[ - ServiceGroup, - Annotated[List[Union[Actor, ServiceInitTypes, "ServiceGroupInitTypes"]], "list of components"], - Annotated[Union[Actor, ServiceInitTypes, "ServiceGroupInitTypes"], "single component of the group"], - Annotated[dict, "dict following the ServiceGroup data model"], -] -"""Types that :py:class:`~.ServiceGroup` can be validated from.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/service.html b/_modules/chatsky/core/service/service.html deleted file mode 100644 index c45c52c4d..000000000 --- a/_modules/chatsky/core/service/service.html +++ /dev/null @@ -1,714 +0,0 @@ - - - - - - - - - - chatsky.core.service.service — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.service

-"""
-Service
--------
-The Service module contains the :py:class:`.Service` class which represents a single task.
-
-Pipeline consists of services and service groups.
-Service is an atomic part of a pipeline.
-"""
-
-from __future__ import annotations
-import logging
-import inspect
-from typing import Any, Optional, Callable, Union
-from typing_extensions import TypeAlias, Annotated
-from pydantic import model_validator, Field
-
-from chatsky.core.context import Context
-from chatsky.core.script_function import BaseProcessing, AnyCondition
-from chatsky.core.service.component import PipelineComponent
-from .extra import BeforeHandler, AfterHandler
-from chatsky.utils.devel import wrap_sync_function_in_async
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class Service(PipelineComponent): - """ - This class represents a service. - """ - - handler: Union[BaseProcessing, Callable[[Context], None]] = None - """ - Function that represents this service. - """ - # Repeating inherited fields for better documentation. - before_handler: BeforeHandler = Field(default_factory=BeforeHandler) - after_handler: AfterHandler = Field(default_factory=AfterHandler) - timeout: Optional[float] = None - concurrent: bool = False - start_condition: AnyCondition = Field(default=True, validate_default=True) - name: Optional[str] = None - path: Optional[str] = None - -
[docs] @model_validator(mode="before") - @classmethod - def handler_validator(cls, data: Any): - """ - Add support for initializing from a `Callable` or `BaseProcessing`. - """ - if inspect.isfunction(data) or isinstance(data, BaseProcessing): - return {"handler": data} - return data
- -
[docs] async def call(self, ctx: Context) -> None: - """ - A placeholder method which the user can redefine in their own derivative of :py:class:`.Service`. - This allows direct access to the ``self`` object and all its fields. - - :param ctx: Current dialog context. - """ - if self.handler is None: - raise NotImplementedError( - f"Received {self.__class__.__name__} object, which has it's 'handler' == 'None'," - f" while also not defining it's own 'call()' method." - ) - await wrap_sync_function_in_async(self.handler, ctx)
- -
[docs] async def run_component(self, ctx: Context) -> None: - """ - Method for running this service. - - :param ctx: Current dialog context. - :return: `None` - """ - await wrap_sync_function_in_async(self.call, ctx)
- - @property - def computed_name(self) -> str: - """ - Return name of the handler or name of this class if handler is empty. - """ - if inspect.isfunction(self.handler): - return self.handler.__name__ - elif self.handler is None: - return self.__class__.__name__ - else: - return self.handler.__class__.__name__
- - -
[docs]def to_service( - before_handler: BeforeHandler = None, - after_handler: AfterHandler = None, - timeout: Optional[int] = None, - concurrent: bool = False, - start_condition: AnyCondition = True, - name: Optional[str] = None, -): - """ - Return a function decorator that returns a :py:class:`Service` with the - given parameters. - """ - before_handler = BeforeHandler() if before_handler is None else before_handler - after_handler = AfterHandler() if after_handler is None else after_handler - - def inner(handler: Union[BaseProcessing, Callable]) -> Service: - return Service( - handler=handler, - before_handler=before_handler, - after_handler=after_handler, - timeout=timeout, - concurrent=concurrent, - start_condition=start_condition, - name=name, - ) - - return inner
- - -ServiceInitTypes: TypeAlias = Union[ - Service, - Annotated[dict, "dict following the Service data model"], - Annotated[Callable, "handler for the service"], - Annotated[BaseProcessing, "handler for the service"], -] -"""Types that :py:class:`~.Service` can be validated from.""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/service/types.html b/_modules/chatsky/core/service/types.html deleted file mode 100644 index 7e740a2fc..000000000 --- a/_modules/chatsky/core/service/types.html +++ /dev/null @@ -1,703 +0,0 @@ - - - - - - - - - - chatsky.core.service.types — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.service.types

-"""
-Types
------
-This module defines type aliases used throughout the ``Core.Service`` module.
-
-The classes and special types in this module can include data models,
-data structures, and other types that are defined for type hinting.
-"""
-
-from __future__ import annotations
-from enum import unique, Enum
-from typing import Callable, Union, Awaitable, Optional, Any, Protocol, Hashable, TYPE_CHECKING
-from typing_extensions import TypeAlias
-from pydantic import BaseModel
-
-
-if TYPE_CHECKING:
-    from chatsky.core import Context, Message
-    from chatsky.core.service import PipelineComponent
-
-
-
[docs]class PipelineRunnerFunction(Protocol): - """ - Protocol for pipeline running. - """ - -
[docs] def __call__( - self, message: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Awaitable[Context]: - """ - :param message: User request for pipeline to process. - :param ctx_id: - ID of the context that the new request belongs to. - Optional, None by default. - If set to `None`, a new context will be created with `message` being the first request. - :param update_ctx_misc: - Dictionary to be passed as an argument to `ctx.misc.update`. - This argument can be used to store values in the `misc` dictionary before anything else runs. - Optional; None by default. - If set to `None`, `ctx.misc.update` will not be called. - :return: - Context instance that pipeline processed. - The context instance has the id of `ctx_id`. - If `ctx_id` is `None`, context instance has an id generated with `uuid.uuid4`. - """
- - -
[docs]@unique -class ComponentExecutionState(str, Enum): - """ - Enum, representing pipeline component execution state. - These states are stored in :py:attr:`~chatsky.core.context.FrameworkData.service_states`, - that should always be requested with ``NOT_RUN`` being default fallback. - Following states are supported: - - - NOT_RUN: component has not been executed yet (the default one), - - RUNNING: component is currently being executed, - - FINISHED: component executed successfully, - - FAILED: component execution failed. - """ - - NOT_RUN = "NOT_RUN" - RUNNING = "RUNNING" - FINISHED = "FINISHED" - FAILED = "FAILED"
- - -
[docs]@unique -class ExtraHandlerType(str, Enum): - """ - Enum, representing extra handler execution stage: before or after the wrapped function. - The following types are supported: - - - BEFORE: extra handler function called before component, - - AFTER: extra handler function called after component. - """ - - BEFORE = "BEFORE" - AFTER = "AFTER"
- - -ExtraHandlerConditionFunction: TypeAlias = Callable[[str], bool] -""" -A function type used when adding an extra handler to a service group to determine -whether extra handler should be added to components of the service group. - -Accepts a single argument - path of a subcomponent in a group. -Return bool - whether to add extra handler to the subcomponent. -""" - - -ExtraHandlerFunction: TypeAlias = Union[ - Callable[["Context"], Any], - Callable[["Context", "ExtraHandlerRuntimeInfo"], Any], -] -""" -A function type for creating extra handler (before and after functions). -Can accept current dialog context and current extra handler info. -""" - - -
[docs]class ExtraHandlerRuntimeInfo(BaseModel): - """ - Information passed to :py:data:`.ExtraHandlerFunction`. - """ - - stage: ExtraHandlerType - """ - :py:class:`.ExtraHandlerType` -- either "BEFORE" or "AFTER". - """ - component: PipelineComponent - """ - Component object that called the extra handler. - """
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/transition.html b/_modules/chatsky/core/transition.html deleted file mode 100644 index b3e9e8f79..000000000 --- a/_modules/chatsky/core/transition.html +++ /dev/null @@ -1,691 +0,0 @@ - - - - - - - - - - chatsky.core.transition — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.transition

-"""
-Transition
-----------
-This module defines a transition class that is used to
-specify conditions and destinations for transitions to nodes.
-"""
-
-from __future__ import annotations
-
-from typing import Union, List, TYPE_CHECKING, Optional, Tuple
-import logging
-import asyncio
-
-from pydantic import BaseModel, Field
-
-from chatsky.core.script_function import AnyCondition, AnyDestination, AnyPriority
-from chatsky.core.script_function import BaseCondition, BaseDestination, BasePriority
-from chatsky.core.node_label import AbsoluteNodeLabel, NodeLabelInitTypes
-
-if TYPE_CHECKING:
-    from chatsky.core.context import Context
-
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class Transition(BaseModel): - """ - A basic class for a transition to a node. - """ - - cnd: AnyCondition = Field(default=True, validate_default=True) - """A condition that determines if transition is allowed to happen.""" - dst: AnyDestination - """Destination node of the transition.""" - priority: AnyPriority = Field(default=None, validate_default=True) - """Priority of the transition. Higher priority transitions are resolved first.""" - - def __init__( - self, - *, - cnd: Union[bool, BaseCondition] = True, - dst: Union[NodeLabelInitTypes, BaseDestination], - priority: Union[Optional[float], BasePriority] = None, - ): - super().__init__(cnd=cnd, dst=dst, priority=priority)
- - -
[docs]async def get_next_label( - ctx: Context, transitions: List[Transition], default_priority: float -) -> Optional[AbsoluteNodeLabel]: - """ - Determine the next node based on ``transitions`` and ``ctx``. - - The process is as follows: - - 1. Condition result is calculated for every transition. - 2. Transitions are filtered by the calculated condition. - 3. Priority result is calculated for every transition that is left. - ``default_priority`` is used for priorities that return ``True`` or ``None`` - as per :py:class:`.BasePriority`. - Those that return ``False`` are filtered out. - 4. Destination result is calculated for every transition that is left. - 5. The highest priority transition is chosen. - If there are multiple transition of the higher priority, - choose the first one of that priority in the ``transitions`` list. - Order of ``transitions`` is as follows: - ``node transitions, local transitions, global transitions``. - - If at any point any :py:class:`.BaseCondition`, :py:class:`.BaseDestination` or :py:class:`.BasePriority` - produces an exception, the corresponding transition is filtered out. - - :return: Label of the next node or ``None`` if no transition is left by the end of the process. - """ - filtered_transitions: List[Transition] = transitions.copy() - condition_results = await asyncio.gather(*[transition.cnd.wrapped_call(ctx) for transition in filtered_transitions]) - - filtered_transitions = [ - transition for transition, condition in zip(filtered_transitions, condition_results) if condition is True - ] - - priority_results = await asyncio.gather( - *[transition.priority.wrapped_call(ctx) for transition in filtered_transitions] - ) - - transitions_with_priorities: List[Tuple[Transition, float]] = [ - (transition, (priority_result if isinstance(priority_result, float) else default_priority)) - for transition, priority_result in zip(filtered_transitions, priority_results) - if (priority_result is True or priority_result is None or isinstance(priority_result, float)) - ] - logger.debug(f"Possible transitions: {transitions_with_priorities!r}") - - transitions_with_priorities = sorted(transitions_with_priorities, key=lambda x: x[1], reverse=True) - - destination_results = await asyncio.gather( - *[transition.dst.wrapped_call(ctx) for transition, _ in transitions_with_priorities] - ) - - for destination in destination_results: - if isinstance(destination, AbsoluteNodeLabel): - return destination - return None
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/core/utils.html b/_modules/chatsky/core/utils.html deleted file mode 100644 index c64103723..000000000 --- a/_modules/chatsky/core/utils.html +++ /dev/null @@ -1,667 +0,0 @@ - - - - - - - - - - chatsky.core.utils — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.core.utils

-"""
-Utils
------
-The Utils module contains functions used to provide names to nameless pipeline components inside of a group.
-"""
-
-import collections
-from typing import List
-
-from chatsky.core import Context
-from chatsky.core.context import ServiceState
-from .service.component import PipelineComponent
-from .service.group import ServiceGroup
-
-
-
[docs]def rename_component_incrementing(index: int, computed_names: List[str]) -> str: - """ - Function for generating new name for a pipeline component, - that has similar name with other components in the same group. - - The name is generated according to these rules: - - 1. Base name is :py:attr:`.PipelineComponent.computed_name`; - 2. If there are multiple components with the same name, ``#[NUMBER]`` is added to the resulting name, - where ``NUMBER`` is the number of components with the same name in current service group. - - :param index: Index of the component to be renamed. - :param computed_names: A list of component names (or computed names). - :return: Name for the component. - """ - base_name = computed_names[index] - if computed_names.count(base_name) == 1: - return base_name - else: - return f"{base_name}#{computed_names[:index].count(base_name)}"
- - -
[docs]def finalize_service_group(service_group: ServiceGroup, path: str = "") -> None: - """ - Function that iterates through a service group (and all its subgroups), - finalizing component's names and paths in it. - Components are renamed only if user didn't set a name for them. Their paths are also generated here. - - :param service_group: Service group to resolve name collisions in. - :param path: - A prefix for component paths -- path of `component` is equal to `{path}.{component.name}`. - Defaults to "". - :raises ValueError: If multiple components have the same name assigned to them. - """ - computed_names = [ - component.name if component.name is not None else component.computed_name - for component in service_group.components - ] - for idx, component in enumerate(service_group.components): - if component.name is None: - component.name = rename_component_incrementing(idx, computed_names) - component.path = f"{path}.{component.name}" - - new_names_counter = collections.Counter([component.name for component in service_group.components]) - for k, v in new_names_counter.items(): - if v != 1: - raise ValueError("Found multiple components with the same name: {path}.{k}") - - for component in service_group.components: - if isinstance(component, ServiceGroup): - finalize_service_group(component, f"{path}.{component.name}")
- - -
[docs]def initialize_service_states(ctx: Context, service: PipelineComponent) -> None: - """ - Reset :py:class:`.ServiceState` of `service`. - - Called at the beginning of every turn for the pipeline service group. - """ - ctx.framework_data.service_states[service.path] = ServiceState() - if isinstance(service, ServiceGroup): - for component in service.components: - initialize_service_states(ctx, component)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/destinations/standard.html b/_modules/chatsky/destinations/standard.html deleted file mode 100644 index d602ba729..000000000 --- a/_modules/chatsky/destinations/standard.html +++ /dev/null @@ -1,732 +0,0 @@ - - - - - - - - - - chatsky.destinations.standard — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.destinations.standard

-"""
-Standard Destinations
----------------------
-This module provides basic destinations.
-
-- :py:class:`FromHistory`, :py:class:`Current` and :py:class:`Previous` -- history-based destinations;
-- :py:class:`Start` and :py:class:`Fallback` -- config-based destinations;
-- :py:class:`Forward` and :py:class:`Backward` -- script-based destinations.
-"""
-
-from __future__ import annotations
-
-from pydantic import Field
-
-from chatsky.core.context import get_last_index, Context
-from chatsky.core.node_label import NodeLabelInitTypes, AbsoluteNodeLabel
-from chatsky.core.script_function import BaseDestination
-
-
-
[docs]class FromHistory(BaseDestination): - """ - Return label of the node located at a certain position in the label history. - """ - - position: int = Field(le=-1) - """ - Position of the label in label history. - - Should be negative: - - - ``-1`` refers to the current node (same as ``ctx.last_label``); - - ``-2`` -- to the previous node. - """ - -
[docs] async def call(self, ctx: Context) -> NodeLabelInitTypes: - index = get_last_index(ctx.labels) - shifted_index = index + self.position + 1 - result = ctx.labels.get(shifted_index) - if result is None: - raise KeyError( - f"No label with index {shifted_index!r}. " - f"Current label index: {index!r}; FromHistory.position: {self.position!r}." - ) - return result
- - -
[docs]class Current(FromHistory): - """ - Return label of the current node. - """ - - position: int = -1 - """Position is set to ``-1`` to get the last node."""
- - - - - -
[docs]class Start(BaseDestination): - """ - Return :py:attr:`~chatsky.core.pipeline.Pipeline.start_label`. - """ - -
[docs] async def call(self, ctx: Context) -> NodeLabelInitTypes: - return ctx.pipeline.start_label
- - -
[docs]class Fallback(BaseDestination): - """ - Return :py:attr:`~chatsky.core.pipeline.Pipeline.fallback_label`. - """ - -
[docs] async def call(self, ctx: Context) -> NodeLabelInitTypes: - return ctx.pipeline.fallback_label
- - -
[docs]def get_next_node_in_flow( - node_label: AbsoluteNodeLabel, - ctx: Context, - *, - increment: bool = True, - loop: bool = False, -) -> AbsoluteNodeLabel: - """ - Function that returns node label of a node in the same flow after shifting the index. - - :param node_label: Label of the node to shift from. - :param ctx: Dialog context. - :param increment: If it is `True`, label index is incremented by `1`, - otherwise it is decreased by `1`. - :param loop: If it is `True` the iteration over the label list is going cyclically - (i.e. Backward in the first node returns the last node). - :return: The tuple that consists of `(flow_label, label, priority)`. - If fallback is executed `(flow_fallback_label, fallback_label, priority)` are returned. - """ - node_label = AbsoluteNodeLabel.model_validate(node_label, context={"ctx": ctx}) - node_keys = list(ctx.pipeline.script.get_flow(node_label.flow_name).nodes.keys()) - - node_index = node_keys.index(node_label.node_name) - node_index = node_index + 1 if increment else node_index - 1 - if not (loop or (0 <= node_index < len(node_keys))): - raise IndexError( - f"Node index {node_index!r} out of range for node_keys: {node_keys!r}. Consider using the `loop` flag." - ) - node_index %= len(node_keys) - - return AbsoluteNodeLabel(flow_name=node_label.flow_name, node_name=node_keys[node_index])
- - -
[docs]class Forward(BaseDestination): - """ - Return the next node relative to the current node in the current flow. - """ - - loop: bool = False - """ - Whether to return the first node of the flow if the current node is the last one. - Otherwise and exception is raised (and transition is considered unsuccessful). - """ - -
[docs] async def call(self, ctx: Context) -> NodeLabelInitTypes: - return get_next_node_in_flow(ctx.last_label, ctx, increment=True, loop=self.loop)
- - -
[docs]class Backward(BaseDestination): - """ - Return the previous node relative to the current node in the current flow. - """ - - loop: bool = False - """ - Whether to return the last node of the flow if the current node is the first one. - Otherwise and exception is raised (and transition is considered unsuccessful). - """ - -
[docs] async def call(self, ctx: Context) -> NodeLabelInitTypes: - return get_next_node_in_flow(ctx.last_label, ctx, increment=False, loop=self.loop)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/messengers/common/interface.html b/_modules/chatsky/messengers/common/interface.html deleted file mode 100644 index 2f741ea99..000000000 --- a/_modules/chatsky/messengers/common/interface.html +++ /dev/null @@ -1,783 +0,0 @@ - - - - - - - - - - chatsky.messengers.common.interface — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.messengers.common.interface

-"""
-Message Interfaces
-------------------
-The Message Interfaces module contains several basic classes that define the message interfaces.
-These classes provide a way to define the structure of the messengers that are used to communicate with Chatsky.
-"""
-
-from __future__ import annotations
-import abc
-import asyncio
-import logging
-from pathlib import Path
-from tempfile import gettempdir
-from typing import Optional, Any, List, Tuple, Hashable, TYPE_CHECKING, Type
-
-if TYPE_CHECKING:
-    from chatsky.core import Context
-    from chatsky.core.service.types import PipelineRunnerFunction
-    from chatsky.messengers.common.types import PollingInterfaceLoopFunction
-    from chatsky.core.message import Message, Attachment
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class MessengerInterface(abc.ABC): - """ - Class that represents a message interface used for communication between pipeline and users. - It is responsible for connection between user and pipeline, as well as for request-response transactions. - """ - -
[docs] @abc.abstractmethod - async def connect(self, pipeline_runner: PipelineRunnerFunction): - """ - Method invoked when message interface is instantiated and connection is established. - May be used for sending an introduction message or displaying general bot information. - - :param pipeline_runner: A function that should process user request and return context; - usually it's a :py:meth:`~chatsky.core.pipeline.Pipeline._run_pipeline` function. - """ - raise NotImplementedError
- - -
[docs]class MessengerInterfaceWithAttachments(MessengerInterface, abc.ABC): - """ - MessengerInterface subclass that has methods for attachment handling. - - :param attachments_directory: Directory where attachments will be stored. - If not specified, the temporary directory will be used. - """ - - supported_request_attachment_types: set[Type[Attachment]] = set() - """ - Types of attachment that this messenger interface can receive. - Attachments not in this list will be neglected. - """ - - supported_response_attachment_types: set[Type[Attachment]] = set() - """ - Types of attachment that this messenger interface can send. - Attachments not in this list will be neglected. - """ - - def __init__(self, attachments_directory: Optional[Path] = None) -> None: - tempdir = gettempdir() - if attachments_directory is not None and not str(attachments_directory.absolute()).startswith(tempdir): - self.attachments_directory = attachments_directory - else: - warning_start = f"Attachments directory for {type(self).__name__} messenger interface" - warning_end = "attachment data won't be cached locally!" - if attachments_directory is None: - self.attachments_directory = Path(tempdir) / f"chatsky-cache-{type(self).__name__}" - logger.info(f"{warning_start} is None, so will be set to tempdir and {warning_end}") - else: - self.attachments_directory = attachments_directory - logger.info(f"{warning_start} is in tempdir, so {warning_end}") - self.attachments_directory.mkdir(parents=True, exist_ok=True) - -
[docs] @abc.abstractmethod - async def get_attachment_bytes(self, source: str) -> bytes: - """ - Get attachment bytes from file source. - - E.g. if a file attachment consists of a URL of the file uploaded to the messenger servers, - this method is the right place to call the messenger API for the file downloading. - - :param source: Identifying string for the file. - :return: The attachment bytes. - """ - raise NotImplementedError
- - -
[docs]class PollingMessengerInterface(MessengerInterface): - """ - Polling message interface runs in a loop, constantly asking users for a new input. - """ - -
[docs] @abc.abstractmethod - def _request(self) -> List[Tuple[Message, Hashable]]: - """ - Method used for sending users request for their input. - - :return: A list of tuples: user inputs and context ids (any user ids) associated with the inputs. - """ - raise NotImplementedError
- -
[docs] @abc.abstractmethod - def _respond(self, responses: List[Context]): - """ - Method used for sending users responses for their last input. - - :param responses: A list of contexts, representing dialogs with the users; - `last_response`, `id` and some dialog info can be extracted from there. - """ - raise NotImplementedError
- -
[docs] def _on_exception(self, e: BaseException): - """ - Method that is called on polling cycle exceptions, in some cases it should show users the exception. - By default, it logs all exit exceptions to `info` log and all non-exit exceptions to `error`. - - :param e: The exception. - """ - if isinstance(e, Exception): - logger.error(f"Exception in {type(self).__name__} loop!", exc_info=e) - else: - logger.info(f"{type(self).__name__} has stopped polling.")
- -
[docs] async def _polling_loop( - self, - pipeline_runner: PipelineRunnerFunction, - timeout: float = 0, - ): - """ - Method running the request - response cycle once. - """ - user_updates = self._request() - responses = [await pipeline_runner(request, ctx_id) for request, ctx_id in user_updates] - self._respond(responses) - await asyncio.sleep(timeout)
- -
[docs] async def connect( - self, - pipeline_runner: PipelineRunnerFunction, - loop: PollingInterfaceLoopFunction = lambda: True, - timeout: float = 0, - ): - """ - Method, running a request - response cycle in a loop. - The looping behavior is determined by `loop` and `timeout`, - for most cases the loop itself shouldn't be overridden. - - :param pipeline_runner: A function that should process user request and return context; - usually it's a :py:meth:`~chatsky.core.pipeline.Pipeline._run_pipeline` function. - :param loop: a function that determines whether polling should be continued; - called in each cycle, should return `True` to continue polling or `False` to stop. - :param timeout: a time interval between polls (in seconds). - """ - while loop(): - try: - await self._polling_loop(pipeline_runner, timeout) - - except BaseException as e: - self._on_exception(e) - break
- - -
[docs]class CallbackMessengerInterface(MessengerInterface): - """ - Callback message interface is waiting for user input and answers once it gets one. - """ - - def __init__(self) -> None: - self._pipeline_runner: Optional[PipelineRunnerFunction] = None - -
[docs] async def connect(self, pipeline_runner: PipelineRunnerFunction): - self._pipeline_runner = pipeline_runner
- -
[docs] async def on_request_async( - self, request: Message, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: - """ - Method that should be invoked on user input. - This method has the same signature as :py:class:`~chatsky.core.service.types.PipelineRunnerFunction`. - """ - return await self._pipeline_runner(request, ctx_id, update_ctx_misc)
- -
[docs] def on_request( - self, request: Any, ctx_id: Optional[Hashable] = None, update_ctx_misc: Optional[dict] = None - ) -> Context: - """ - Method that should be invoked on user input. - This method has the same signature as :py:class:`~chatsky.core.service.types.PipelineRunnerFunction`. - """ - return asyncio.run(self.on_request_async(request, ctx_id, update_ctx_misc))
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/messengers/console.html b/_modules/chatsky/messengers/console.html deleted file mode 100644 index 4a8656509..000000000 --- a/_modules/chatsky/messengers/console.html +++ /dev/null @@ -1,638 +0,0 @@ - - - - - - - - - - chatsky.messengers.console — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.messengers.console

-from typing import Any, Hashable, List, Optional, TextIO, Tuple
-from uuid import uuid4
-from chatsky.messengers.common.interface import PollingMessengerInterface
-from chatsky.core.service.types import PipelineRunnerFunction
-from chatsky.core.context import Context
-from chatsky.core.message import Message
-
-
-
[docs]class CLIMessengerInterface(PollingMessengerInterface): - """ - Command line message interface is the default message interface, communicating with user via `STDIN/STDOUT`. - This message interface can maintain dialog with one user at a time only. - """ - - supported_request_attachment_types = set() - supported_response_attachment_types = set() - - def __init__( - self, - intro: Optional[str] = None, - prompt_request: str = "request: ", - prompt_response: str = "response: ", - out_descriptor: Optional[TextIO] = None, - ): - super().__init__() - self._ctx_id: Optional[Hashable] = None - self._intro: Optional[str] = intro - self._prompt_request: str = prompt_request - self._prompt_response: str = prompt_response - self._descriptor: Optional[TextIO] = out_descriptor - -
[docs] def _request(self) -> List[Tuple[Message, Any]]: - return [(Message(text=input(self._prompt_request)), self._ctx_id)]
- -
[docs] def _respond(self, responses: List[Context]): - print(f"{self._prompt_response}{responses[0].last_response}", file=self._descriptor)
- -
[docs] async def connect(self, pipeline_runner: PipelineRunnerFunction, **kwargs): - """ - The CLIProvider generates new dialog id used to user identification on each `connect` call. - - :param pipeline_runner: A function that should process user request and return context; - usually it's a :py:meth:`~chatsky.core.pipeline.Pipeline._run_pipeline` function. - :param \\**kwargs: argument, added for compatibility with super class, it shouldn't be used normally. - """ - self._ctx_id = uuid4() - if self._intro is not None: - print(self._intro) - await super().connect(pipeline_runner, **kwargs)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/messengers/telegram/abstract.html b/_modules/chatsky/messengers/telegram/abstract.html deleted file mode 100644 index 36a52015c..000000000 --- a/_modules/chatsky/messengers/telegram/abstract.html +++ /dev/null @@ -1,1249 +0,0 @@ - - - - - - - - - - chatsky.messengers.telegram.abstract — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.messengers.telegram.abstract

-"""
-Telegram Base
--------------
-This module implements a base interface for interactions with the
-Telegram API.
-"""
-
-from pathlib import Path
-from typing import Any, Callable, Optional
-
-from chatsky.utils.devel.extra_field_helpers import grab_extra_fields
-
-from chatsky.messengers.common import MessengerInterfaceWithAttachments
-from chatsky.core.service.types import PipelineRunnerFunction
-from chatsky.core.message import (
-    Animation,
-    Audio,
-    CallbackQuery,
-    Contact,
-    Document,
-    Image,
-    Invoice,
-    Location,
-    Message,
-    Poll,
-    PollOption,
-    Sticker,
-    Video,
-    VideoMessage,
-    VoiceMessage,
-    MediaGroup,
-)
-
-try:
-    from telegram import (
-        InputMediaAnimation,
-        InputMediaAudio,
-        InputMediaDocument,
-        InputMediaPhoto,
-        InputMediaVideo,
-        Update,
-        Message as TelegramMessage,
-    )
-    from telegram.ext import Application, ExtBot, MessageHandler, CallbackQueryHandler
-    from telegram.ext.filters import ALL
-
-    telegram_available = True
-except ImportError:
-    ExtBot = Any
-    Update = Any
-    TelegramMessage = Any
-
-    telegram_available = False
-
-
-
[docs]class _AbstractTelegramInterface(MessengerInterfaceWithAttachments): - """ - Messenger interface mixin for Telegram API usage. - """ - - supported_request_attachment_types = { - Location, - Contact, - Poll, - Sticker, - Audio, - Video, - Animation, - Image, - Document, - VoiceMessage, - VideoMessage, - Invoice, - } - supported_response_attachment_types = { - Location, - Contact, - Poll, - Sticker, - Audio, - Video, - Animation, - Image, - Document, - VoiceMessage, - VideoMessage, - MediaGroup, - } - - def __init__(self, token: str, attachments_directory: Optional[Path] = None) -> None: - super().__init__(attachments_directory) - if not telegram_available: - raise ImportError("`python-telegram-bot` package is missing.\nTry to run `pip install chatsky[telegram]`.") - - self.application = Application.builder().token(token).build() - self.application.add_handler(MessageHandler(ALL, self.on_message)) - self.application.add_handler(CallbackQueryHandler(self.on_callback)) - -
[docs] async def get_attachment_bytes(self, source: str) -> bytes: - file = await self.application.bot.get_file(source) - data = await file.download_as_bytearray() - return bytes(data)
- -
[docs] def extract_message_from_telegram(self, update: TelegramMessage) -> Message: - """ - Convert Telegram update to Chatsky message. - Extract text and supported attachments. - - :param update: Telegram update object. - :return: Chatsky message object. - """ - - message = Message() - message.attachments = list() - - message.text = update.text or update.caption - if update.location is not None: - message.attachments += [Location(latitude=update.location.latitude, longitude=update.location.longitude)] - if update.contact is not None: - message.attachments += [ - Contact( - phone_number=update.contact.phone_number, - first_name=update.contact.first_name, - last_name=update.contact.last_name, - user_id=update.contact.user_id, - ) - ] - if update.invoice is not None: - message.attachments += [ - Invoice( - title=update.invoice.title, - description=update.invoice.description, - currency=update.invoice.currency, - amount=update.invoice.total_amount, - ) - ] - if update.poll is not None: - message.attachments += [ - Poll( - question=update.poll.question, - options=[PollOption(text=option.text, votes=option.voter_count) for option in update.poll.options], - is_closed=update.poll.is_closed, - is_anonymous=update.poll.is_anonymous, - type=update.poll.type, - multiple_answers=update.poll.allows_multiple_answers, - correct_option_id=update.poll.correct_option_id, - explanation=update.poll.explanation, - open_period=update.poll.open_period, - ) - ] - if update.sticker is not None: - message.attachments += [ - Sticker( - id=update.sticker.file_id, - is_animated=update.sticker.is_animated, - is_video=update.sticker.is_video, - type=update.sticker.type, - ) - ] - if update.audio is not None: - thumbnail = ( - Image(id=update.audio.thumbnail.file_id, file_unique_id=update.audio.thumbnail.file_unique_id) - if update.audio.thumbnail is not None - else None - ) - message.attachments += [ - Audio( - id=update.audio.file_id, - file_unique_id=update.audio.file_unique_id, - duration=update.audio.duration, - performer=update.audio.performer, - file_name=update.audio.file_name, - mime_type=update.audio.mime_type, - thumbnail=thumbnail, - ) - ] - if update.video is not None: - thumbnail = ( - Image(id=update.video.thumbnail.file_id, file_unique_id=update.video.thumbnail.file_unique_id) - if update.video.thumbnail is not None - else None - ) - message.attachments += [ - Video( - id=update.video.file_id, - file_unique_id=update.video.file_unique_id, - width=update.video.width, - height=update.video.height, - duration=update.video.duration, - file_name=update.video.file_name, - mime_type=update.video.mime_type, - thumbnail=thumbnail, - ) - ] - if update.animation is not None: - thumbnail = ( - Image(id=update.animation.thumbnail.file_id, file_unique_id=update.animation.thumbnail.file_unique_id) - if update.animation.thumbnail is not None - else None - ) - message.attachments += [ - Animation( - id=update.animation.file_id, - file_unique_id=update.animation.file_unique_id, - width=update.animation.width, - height=update.animation.height, - duration=update.animation.duration, - file_name=update.animation.file_name, - mime_type=update.animation.mime_type, - thumbnail=thumbnail, - ) - ] - if len(update.photo) > 0: - message.attachments += [ - Image( - id=picture.file_id, - file_unique_id=picture.file_unique_id, - width=picture.width, - height=picture.height, - ) - for picture in update.photo - ] - if update.document is not None: - thumbnail = ( - Image(id=update.document.thumbnail.file_id, file_unique_id=update.document.thumbnail.file_unique_id) - if update.document.thumbnail is not None - else None - ) - message.attachments += [ - Document( - id=update.document.file_id, - file_unique_id=update.document.file_unique_id, - file_name=update.document.file_name, - mime_type=update.document.mime_type, - thumbnail=thumbnail, - ) - ] - if update.voice is not None: - message.attachments += [ - VoiceMessage( - id=update.voice.file_id, - file_unique_id=update.voice.file_unique_id, - mime_type=update.voice.mime_type, - ) - ] - if update.video_note is not None: - thumbnail = ( - Image(id=update.video_note.thumbnail.file_id, file_unique_id=update.video_note.thumbnail.file_unique_id) - if update.video_note.thumbnail is not None - else None - ) - message.attachments += [ - VideoMessage( - id=update.video_note.file_id, - file_unique_id=update.video_note.file_unique_id, - thumbnail=thumbnail, - ) - ] - - return message
- -
[docs] async def cast_message_to_telegram_and_send(self, bot: ExtBot, chat_id: int, message: Message) -> None: - """ - Send Chatsky message to Telegram. - Sometimes, if several attachments included into message can not be sent as one update, - several Telegram updates will be produced. - Sometimes, if no text and none of the supported attachments are included, - nothing will happen. - - :param bot: Telegram bot, that is used for connection to Telegram API. - :param chat_id: Telegram dialog ID that the message will be sent to. - :param message: Chatsky message that will be processed into Telegram updates. - """ - - if message.text is not None: - await bot.send_message( - chat_id, - message.text, - **grab_extra_fields( - message, - [ - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - "disable_web_page_preview", - ], - ), - ) - if message.attachments is not None: - for attachment in message.attachments: - if isinstance(attachment, Location): - await bot.send_location( - chat_id, - attachment.latitude, - attachment.longitude, - **grab_extra_fields( - attachment, - [ - "horizontal_accuracy", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - ], - ), - ) - elif isinstance(attachment, Contact): - await bot.send_contact( - chat_id, - attachment.phone_number, - attachment.first_name, - attachment.last_name, - **grab_extra_fields( - attachment, - [ - "vcard", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - ], - ), - ) - elif isinstance(attachment, Poll): - await bot.send_poll( - chat_id, - attachment.question, - [option.text for option in attachment.options], - **grab_extra_fields( - attachment, - [ - "is_anonymous", - "type", - "allows_multiple_answers", - "correct_option_id", - "explanation", - "explanation_parse_mode", - "open_period", - "is_closed", - "disable_notification", - "protect_content", - "reply_markup", - "question_parse_mode", - "message_effect_id", - "reply_to_message_id", - ], - ), - ) - elif isinstance(attachment, Audio): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_audio( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "performer", - "title", - "disable_notification", - "protect_content", - "reply_markup", - "thumbnail", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, Video): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_video( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "supports_streaming", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "thumbnail", - "message_effect_id", - "show_caption_above_media", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, Animation): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_animation( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "thumbnail", - "message_effect_id", - "show_caption_above_media", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, Image): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_photo( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "has_spoiler", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, Sticker): - sticker = await attachment.get_bytes(self) if attachment.id is None else attachment.id - if sticker is not None: - await bot.send_sticker( - chat_id, - sticker, - **grab_extra_fields( - attachment, - [ - "emoji", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - ], - ), - ) - elif isinstance(attachment, Document): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_document( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "thumbnail", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, VoiceMessage): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_voice( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "caption", - "parse_mode", - "disable_notification", - "protect_content", - "reply_markup", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, VideoMessage): - attachment_bytes = await attachment.get_bytes(self) - if attachment_bytes is not None: - await bot.send_video_note( - chat_id, - attachment_bytes, - **grab_extra_fields( - attachment, - [ - "disable_notification", - "protect_content", - "reply_markup", - "thumbnail", - "message_effect_id", - "reply_to_message_id", - "filename", - ], - ), - ) - elif isinstance(attachment, MediaGroup): - files = list() - for media in attachment.group: - if isinstance(media, Image): - media_bytes = await media.get_bytes(self) - files += [ - InputMediaPhoto( - media_bytes, - **grab_extra_fields( - media, - [ - "filename", - "caption", - "parse_mode", - "has_spoiler", - "show_caption_above_media", - ], - ), - ), - ] - elif isinstance(media, Video): - media_bytes = await media.get_bytes(self) - files += [ - InputMediaVideo( - media_bytes, - **grab_extra_fields( - media, - [ - "filename", - "caption", - "parse_mode", - "supports_streaming", - "has_spoiler", - "thumbnail", - "show_caption_above_media", - ], - ), - ), - ] - elif isinstance(media, Animation): - media_bytes = await media.get_bytes(self) - files += [ - InputMediaAnimation( - media_bytes, - **grab_extra_fields( - media, - [ - "filename", - "caption", - "parse_mode", - "has_spoiler", - "thumbnail", - "show_caption_above_media", - ], - ), - ), - ] - elif isinstance(media, Audio): - media_bytes = await media.get_bytes(self) - files += [ - InputMediaAudio( - media_bytes, - **grab_extra_fields( - media, - ["filename", "caption", "parse_mode", "performer", "title", "thumbnail"], - ), - ), - ] - elif isinstance(media, Document): - media_bytes = await media.get_bytes(self) - files += [ - InputMediaDocument( - media_bytes, - **grab_extra_fields(media, ["filename", "caption", "parse_mode", "thumbnail"]), - ), - ] - else: - raise ValueError(f"Attachment {type(media).__name__} can not be sent in a media group!") - await bot.send_media_group( - chat_id, - files, - **grab_extra_fields( - attachment, - [ - "caption", - "disable_notification", - "protect_content", - "message_effect_id", - "reply_to_message_id", - "parse_mode", - ], - ), - ) - else: - raise ValueError(f"Attachment {type(attachment).__name__} is not supported!")
- -
[docs] async def _on_event(self, update: Update, _: Any, create_message: Callable[[Update], Message]) -> None: - """ - Process Telegram update, run pipeline and send response to Telegram. - - :param update: Telegram update that will be processed. - :param create_message: function that converts Telegram update to Chatsky message. - """ - - data_available = update.message is not None or update.callback_query is not None - if update.effective_chat is not None and data_available: - message = create_message(update) - message.original_message = update - resp = await self._pipeline_runner(message, update.effective_chat.id) - if resp.last_response is not None: - await self.cast_message_to_telegram_and_send( - self.application.bot, update.effective_chat.id, resp.last_response - )
- -
[docs] async def on_message(self, update: Update, _: Any) -> None: - """ - Process normal Telegram update, extracting Chatsky message from it - using :py:meth:`~._AbstractTelegramInterface.extract_message_from_telegram`. - - :param update: Telegram update that will be processed. - """ - - await self._on_event(update, _, lambda s: self.extract_message_from_telegram(s.message))
- -
[docs] async def on_callback(self, update: Update, _: Any) -> None: - """ - Process Telegram callback update, creating empty Chatsky message - with only one callback query attachment from `callback_query.data` field. - - :param update: Telegram update that will be processed. - """ - - await self._on_event( - update, _, lambda s: Message(attachments=[CallbackQuery(query_string=s.callback_query.data)]) - )
- -
[docs] async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - self._pipeline_runner = pipeline_runner
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/messengers/telegram/interface.html b/_modules/chatsky/messengers/telegram/interface.html deleted file mode 100644 index 0d857dfa6..000000000 --- a/_modules/chatsky/messengers/telegram/interface.html +++ /dev/null @@ -1,654 +0,0 @@ - - - - - - - - - - chatsky.messengers.telegram.interface — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.messengers.telegram.interface

-"""
-Telegram Interfaces
--------------------
-This module provides concrete implementations of the
-:py:class:`~._AbstractTelegramInterface`.
-"""
-
-from pathlib import Path
-from typing import Any, Optional
-
-from chatsky.core.service.types import PipelineRunnerFunction
-
-from .abstract import _AbstractTelegramInterface
-
-try:
-    from telegram import Update
-except ImportError:
-    Update = Any
-
-
-
[docs]class LongpollingInterface(_AbstractTelegramInterface): - """ - Telegram messenger interface, that requests Telegram API in a loop. - - :param token: The Telegram bot token. - :param attachments_directory: The directory for storing attachments. - :param interval: A time interval between polls (in seconds). - :param timeout: Timeout in seconds for long polling. - """ - - def __init__( - self, token: str, attachments_directory: Optional[Path] = None, interval: int = 2, timeout: int = 20 - ) -> None: - super().__init__(token, attachments_directory) - self.interval = interval - self.timeout = timeout - -
[docs] async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_polling( - poll_interval=self.interval, timeout=self.timeout, allowed_updates=Update.ALL_TYPES - )
- - -
[docs]class WebhookInterface(_AbstractTelegramInterface): - """ - Telegram messenger interface, that brings a special webserver up - and registers up for listening for Telegram updates. - - :param token: The Telegram bot token. - :param attachments_directory: The directory for storing attachments. - :param host: Local host name (or IP address). - :param port: Local port for running Telegram webhook. - """ - - def __init__( - self, token: str, attachments_directory: Optional[Path] = None, host: str = "localhost", port: int = 844 - ): - super().__init__(token, attachments_directory) - self.listen = host - self.port = port - -
[docs] async def connect(self, pipeline_runner: PipelineRunnerFunction, *args, **kwargs): - await super().connect(pipeline_runner, *args, **kwargs) - self.application.run_webhook(listen=self.listen, port=self.port, allowed_updates=Update.ALL_TYPES)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/processing/slots.html b/_modules/chatsky/processing/slots.html deleted file mode 100644 index 30049d306..000000000 --- a/_modules/chatsky/processing/slots.html +++ /dev/null @@ -1,676 +0,0 @@ - - - - - - - - - - chatsky.processing.slots — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.processing.slots

-"""
-Slot Processing
----------------
-This module provides wrappers for :py:class:`~chatsky.slots.slots.SlotManager`'s API as :py:class:`.BaseProcessing`
-subclasses.
-"""
-
-import asyncio
-import logging
-from typing import List
-
-from chatsky.slots.slots import SlotName
-from chatsky.core import Context, BaseProcessing
-from chatsky.responses.slots import FilledTemplate
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class Extract(BaseProcessing): - """ - Extract slots listed slots. - This will override all slots even if they are already extracted. - """ - - slots: List[SlotName] - """A list of slot names to extract.""" - success_only: bool = True - """If set, only successfully extracted values will be stored in the slot storage.""" - - def __init__(self, *slots: SlotName, success_only: bool = True): - super().__init__(slots=slots, success_only=success_only) - -
[docs] async def call(self, ctx: Context): - manager = ctx.framework_data.slot_manager - results = await asyncio.gather( - *(manager.extract_slot(slot, ctx, self.success_only) for slot in self.slots), return_exceptions=True - ) - - for result in results: - if isinstance(result, Exception): - logger.exception("An exception occurred during slot extraction.", exc_info=result)
- - -
[docs]class Unset(BaseProcessing): - """ - Mark specified slots as not extracted and clear extracted values. - """ - - slots: List[SlotName] - """A list of slot names to extract.""" - - def __init__(self, *slots: SlotName): - super().__init__(slots=slots) - -
[docs] async def call(self, ctx: Context): - manager = ctx.framework_data.slot_manager - for slot in self.slots: - try: - manager.unset_slot(slot) - except Exception as exc: - logger.exception("An exception occurred during slot resetting.", exc_info=exc)
- - -
[docs]class UnsetAll(BaseProcessing): - """ - Mark all slots as not extracted and clear all extracted values. - """ - -
[docs] async def call(self, ctx: Context): - manager = ctx.framework_data.slot_manager - manager.unset_all_slots()
- - -
[docs]class FillTemplate(BaseProcessing): - """ - Fill the response template in the current node. - - Response message of the current node should be a format-string: e.g. ``"Your username is {profile.username}"``. - """ - -
[docs] async def call(self, ctx: Context): - response = ctx.current_node.response - - if response is None: - return - - ctx.current_node.response = FilledTemplate(template=response)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/processing/standard.html b/_modules/chatsky/processing/standard.html deleted file mode 100644 index 06cb0e0b4..000000000 --- a/_modules/chatsky/processing/standard.html +++ /dev/null @@ -1,630 +0,0 @@ - - - - - - - - - - chatsky.processing.standard — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.processing.standard

-"""
-Standard Processing
--------------------
-This module provides basic processing functions.
-
-- :py:class:`ModifyResponse` modifies response of the :py:attr:`.Context.current_node`.
-"""
-
-import abc
-
-from chatsky.core import BaseProcessing, BaseResponse, Context, MessageInitTypes
-
-
-
[docs]class ModifyResponse(BaseProcessing, abc.ABC): - """ - Modify the response function of the :py:attr:`.Context.current_node` to call - :py:meth:`modified_response` instead. - """ - -
[docs] @abc.abstractmethod - async def modified_response(self, original_response: BaseResponse, ctx: Context) -> MessageInitTypes: - """ - A function that replaces response of the current node. - - :param original_response: Response of the current node when :py:class:`.ModifyResponse` is called. - :param ctx: Current context. - """ - raise NotImplementedError
- -
[docs] async def call(self, ctx: Context) -> None: - current_response = ctx.current_node.response - if current_response is None: - return - - processing_object = self - - class ModifiedResponse(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - return await processing_object.modified_response(current_response, ctx) - - ctx.current_node.response = ModifiedResponse()
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/responses/slots.html b/_modules/chatsky/responses/slots.html deleted file mode 100644 index 1184c6704..000000000 --- a/_modules/chatsky/responses/slots.html +++ /dev/null @@ -1,650 +0,0 @@ - - - - - - - - - - chatsky.responses.slots — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.responses.slots

-"""
-Slot Responses
---------------
-Slot-related responses.
-"""
-
-from typing import Union, Literal
-import logging
-
-from chatsky.core import Context, Message, BaseResponse
-from chatsky.core.script_function import AnyResponse
-from chatsky.core.message import MessageInitTypes
-
-
-logger = logging.getLogger(__name__)
-
-
-
[docs]class FilledTemplate(BaseResponse): - """ - Fill template with slot values. - The `text` attribute of the template message should be a format-string: - e.g. "Your username is {profile.username}". - - For the example above, if ``profile.username`` slot has value "admin", - it would return a copy of the message with the following text: - "Your username is admin". - """ - - template: AnyResponse - """A response to use as a template.""" - on_exception: Literal["keep_template", "return_none"] = "return_none" - """ - What to do if template filling fails. - - - "keep_template": :py:attr:`template` is returned, unfilled. - - "return_none": an empty message is returned. - """ - - def __init__( - self, - template: Union[MessageInitTypes, BaseResponse], - on_exception: Literal["keep_template", "return_none"] = "return_none", - ): - super().__init__(template=template, on_exception=on_exception) - -
[docs] async def call(self, ctx: Context) -> MessageInitTypes: - result = await self.template(ctx) - - if result.text is not None: - filled = ctx.framework_data.slot_manager.fill_template(result.text) - if isinstance(filled, str): - result.text = filled - return result - else: - if self.on_exception == "return_none": - return Message() - else: - return result - else: - logger.warning(f"`template` of `FilledTemplate` returned `Message` without `text`: {result}.") - return result
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/responses/standard.html b/_modules/chatsky/responses/standard.html deleted file mode 100644 index ffaae3df1..000000000 --- a/_modules/chatsky/responses/standard.html +++ /dev/null @@ -1,615 +0,0 @@ - - - - - - - - - - chatsky.responses.standard — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.responses.standard

-"""
-Standard Responses
-------------------
-This module provides basic responses.
-"""
-
-import random
-from typing import List
-
-from chatsky.core import BaseResponse, Message, Context
-from chatsky.core.message import MessageInitTypes
-
-
-
[docs]class RandomChoice(BaseResponse): - """ - Return a random message from :py:attr:`responses`. - """ - - responses: List[Message] - """A list of messages to choose from.""" - - def __init__(self, *responses: MessageInitTypes): - super().__init__(responses=responses) - -
[docs] async def call(self, ctx: Context) -> MessageInitTypes: - return random.choice(self.responses)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/slots/slots.html b/_modules/chatsky/slots/slots.html deleted file mode 100644 index 1f6a4140f..000000000 --- a/_modules/chatsky/slots/slots.html +++ /dev/null @@ -1,1038 +0,0 @@ - - - - - - - - - - chatsky.slots.slots — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.slots.slots

-"""
-Slots
------
-This module defines base classes for slots and some concrete implementations of them.
-"""
-
-from __future__ import annotations
-
-import asyncio
-import re
-from abc import ABC, abstractmethod
-from typing import Callable, Any, Awaitable, TYPE_CHECKING, Union, Optional, Dict
-from typing_extensions import TypeAlias, Annotated
-import logging
-from functools import reduce
-from string import Formatter
-
-from pydantic import BaseModel, model_validator, Field, field_serializer, field_validator
-
-from chatsky.utils.devel.async_helpers import wrap_sync_function_in_async
-from chatsky.utils.devel.json_serialization import pickle_serializer, pickle_validator
-
-if TYPE_CHECKING:
-    from chatsky.core import Context, Message
-
-
-logger = logging.getLogger(__name__)
-
-
-SlotName: TypeAlias = str
-"""
-A string to identify slots.
-
-Top-level slots are identified by their key in a :py:class:`~.GroupSlot`.
-
-E.g.
-
-.. code:: python
-
-    GroupSlot(
-        user=RegexpSlot(),
-        password=FunctionSlot,
-    )
-
-Has two slots with names "user" and "password".
-
-For nested group slots use dots to separate names:
-
-.. code:: python
-
-    GroupSlot(
-        user=GroupSlot(
-            name=FunctionSlot,
-            password=FunctionSlot,
-        )
-    )
-
-Has two slots with names "user.name" and "user.password".
-"""
-
-
-
[docs]def recursive_getattr(obj, slot_name: SlotName): - def two_arg_getattr(__o, name): - # pydantic handles exception when accessing a non-existing extra-field on its own - # return None by default to avoid that - return getattr(__o, name, None) - - return reduce(two_arg_getattr, [obj, *slot_name.split(".")])
- - -
[docs]def recursive_setattr(obj, slot_name: SlotName, value): - parent_slot, _, slot = slot_name.rpartition(".") - - if parent_slot: - setattr(recursive_getattr(obj, parent_slot), slot, value) - else: - setattr(obj, slot, value)
- - -
[docs]class SlotNotExtracted(Exception): - """This exception can be returned or raised by slot extractor if slot extraction is unsuccessful.""" - - pass
- - -
[docs]class ExtractedSlot(BaseModel, ABC): - """ - Represents value of an extracted slot. - - Instances of this class are managed by framework and - are stored in :py:attr:`~chatsky.core.context.FrameworkData.slot_manager`. - They can be accessed via the ``ctx.framework_data.slot_manager.get_extracted_slot`` method. - """ - - @property - @abstractmethod - def __slot_extracted__(self) -> bool: - """Whether the slot is extracted.""" - raise NotImplementedError - - def __unset__(self): - """Mark slot as not extracted and clear extracted data (except for default value).""" - raise NotImplementedError - - @abstractmethod - def __str__(self): - """String representation is used to fill templates.""" - raise NotImplementedError
- - -
[docs]class ExtractedValueSlot(ExtractedSlot): - """Value extracted from :py:class:`~.ValueSlot`.""" - - is_slot_extracted: bool - extracted_value: Any - default_value: Any = None - -
[docs] @field_serializer("extracted_value", "default_value", when_used="json") - def pickle_serialize_values(self, value): - """ - Cast values to string via pickle. - Allows storing arbitrary data in these fields when using context storages. - """ - if value is not None: - return pickle_serializer(value) - return value
- -
[docs] @field_validator("extracted_value", "default_value", mode="before") - @classmethod - def pickle_validate_values(cls, value): - """ - Restore values after being processed with - :py:meth:`pickle_serialize_values`. - """ - if value is not None: - return pickle_validator(value) - return value
- - @property - def __slot_extracted__(self) -> bool: - return self.is_slot_extracted - - def __unset__(self): - self.is_slot_extracted = False - self.extracted_value = SlotNotExtracted("Slot manually unset.") - - @property - def value(self): - """Extracted value or the default value if the slot is not extracted.""" - return self.extracted_value if self.is_slot_extracted else self.default_value - - def __str__(self): - return str(self.value)
- - -
[docs]class ExtractedGroupSlot(ExtractedSlot, extra="allow"): - __pydantic_extra__: Dict[ - str, Annotated[Union["ExtractedGroupSlot", "ExtractedValueSlot"], Field(union_mode="left_to_right")] - ] - - @property - def __slot_extracted__(self) -> bool: - return all([slot.__slot_extracted__ for slot in self.__pydantic_extra__.values()]) - - def __unset__(self): - for child in self.__pydantic_extra__.values(): - child.__unset__() - - def __str__(self): - return str({key: str(value) for key, value in self.__pydantic_extra__.items()}) - -
[docs] def update(self, old: "ExtractedGroupSlot"): - """ - Rebase this extracted groups slot on top of another one. - This is required to merge slot storage in-context - with a potentially different slot configuration passed to pipeline. - - :param old: An instance of :py:class:`~.ExtractedGroupSlot` stored in-context. - Extracted values will be transferred to this object. - """ - for slot in old.__pydantic_extra__: - if slot in self.__pydantic_extra__: - new_slot = self.__pydantic_extra__[slot] - old_slot = old.__pydantic_extra__[slot] - if isinstance(new_slot, ExtractedGroupSlot) and isinstance(old_slot, ExtractedGroupSlot): - new_slot.update(old_slot) - if isinstance(new_slot, ExtractedValueSlot) and isinstance(old_slot, ExtractedValueSlot): - self.__pydantic_extra__[slot] = old_slot
- - -
[docs]class BaseSlot(BaseModel, frozen=True): - """ - BaseSlot is a base class for all slots. - """ - -
[docs] @abstractmethod - async def get_value(self, ctx: Context) -> ExtractedSlot: - """ - Extract slot value from :py:class:`~.Context` and return an instance of :py:class:`~.ExtractedSlot`. - """ - raise NotImplementedError
- -
[docs] @abstractmethod - def init_value(self) -> ExtractedSlot: - """ - Provide an initial value to fill slot storage with. - """ - raise NotImplementedError
- - -
[docs]class ValueSlot(BaseSlot, frozen=True): - """ - Value slot is a base class for all slots that are designed to extract concrete values. - Subclass it, if you want to declare your own slot type. - """ - - default_value: Any = None - -
[docs] @abstractmethod - async def extract_value(self, ctx: Context) -> Union[Any, SlotNotExtracted]: - """ - Return value extracted from context. - - Return :py:exc:`~.SlotNotExtracted` to mark extraction as unsuccessful. - - Raising exceptions is also allowed and will result in an unsuccessful extraction as well. - """ - raise NotImplementedError
- -
[docs] async def get_value(self, ctx: Context) -> ExtractedValueSlot: - """Wrapper for :py:meth:`~.ValueSlot.extract_value` to handle exceptions.""" - extracted_value = SlotNotExtracted("Caught an exit exception.") - is_slot_extracted = False - - try: - extracted_value = await wrap_sync_function_in_async(self.extract_value, ctx) - is_slot_extracted = not isinstance(extracted_value, SlotNotExtracted) - except Exception as error: - logger.exception(f"Exception occurred during {self.__class__.__name__!r} extraction.", exc_info=error) - extracted_value = error - finally: - if not is_slot_extracted: - logger.debug(f"Slot {self.__class__.__name__!r} was not extracted: {extracted_value}") - return ExtractedValueSlot.model_construct( - is_slot_extracted=is_slot_extracted, - extracted_value=extracted_value, - default_value=self.default_value, - )
- -
[docs] def init_value(self) -> ExtractedValueSlot: - return ExtractedValueSlot.model_construct( - is_slot_extracted=False, - extracted_value=SlotNotExtracted("Initial slot extraction."), - default_value=self.default_value, - )
- - -
[docs]class GroupSlot(BaseSlot, extra="allow", frozen=True): - """ - Base class for :py:class:`~.RootSlot` and :py:class:`~.GroupSlot`. - """ - - __pydantic_extra__: Dict[str, Annotated[Union["GroupSlot", "ValueSlot"], Field(union_mode="left_to_right")]] - - def __init__(self, **kwargs): # supress unexpected argument warnings - super().__init__(**kwargs) - - @model_validator(mode="after") - def __check_extra_field_names__(self): - """ - Extra field names cannot be dunder names or contain dots. - """ - for field in self.__pydantic_extra__.keys(): - if "." in field: - raise ValueError(f"Extra field name cannot contain dots: {field!r}") - if field.startswith("__") and field.endswith("__"): - raise ValueError(f"Extra field names cannot be dunder: {field!r}") - return self - -
[docs] async def get_value(self, ctx: Context) -> ExtractedGroupSlot: - child_values = await asyncio.gather(*(child.get_value(ctx) for child in self.__pydantic_extra__.values())) - return ExtractedGroupSlot( - **{child_name: child_value for child_value, child_name in zip(child_values, self.__pydantic_extra__.keys())} - )
- -
[docs] def init_value(self) -> ExtractedGroupSlot: - return ExtractedGroupSlot( - **{child_name: child.init_value() for child_name, child in self.__pydantic_extra__.items()} - )
- - -
[docs]class RegexpSlot(ValueSlot, frozen=True): - """ - RegexpSlot is a slot type that extracts its value using a regular expression. - You can pass a compiled or a non-compiled pattern to the `regexp` argument. - If you want to extract a particular group, but not the full match, - change the `match_group_idx` parameter. - """ - - regexp: str - match_group_idx: int = 0 - "Index of the group to match." - -
[docs] async def extract_value(self, ctx: Context) -> Union[str, SlotNotExtracted]: - request_text = ctx.last_request.text - search = re.search(self.regexp, request_text) - return ( - search.group(self.match_group_idx) - if search - else SlotNotExtracted(f"Failed to match pattern {self.regexp!r} in {request_text!r}.") - )
- - -
[docs]class FunctionSlot(ValueSlot, frozen=True): - """ - A simpler version of :py:class:`~.ValueSlot`. - - Uses a user-defined `func` to extract slot value from the :py:attr:`~.Context.last_request` Message. - """ - - func: Callable[[Message], Union[Awaitable[Union[Any, SlotNotExtracted]], Any, SlotNotExtracted]] - -
[docs] async def extract_value(self, ctx: Context) -> Union[Any, SlotNotExtracted]: - return await wrap_sync_function_in_async(self.func, ctx.last_request)
- - -
[docs]class SlotManager(BaseModel): - """ - Provides API for managing slots. - - An instance of this class can be accessed via ``ctx.framework_data.slot_manager``. - """ - - slot_storage: ExtractedGroupSlot = Field(default_factory=ExtractedGroupSlot) - """Slot storage. Stored inside ctx.framework_data.""" - root_slot: GroupSlot = Field(default_factory=GroupSlot, exclude=True) - """Slot configuration passed during pipeline initialization.""" - -
[docs] def set_root_slot(self, root_slot: GroupSlot): - """ - Set root_slot configuration from pipeline. - Update extracted slots with the new configuration: - - New slots are added with their :py:meth:`~.BaseSlot.init_value`. - Old extracted slot values are preserved only if their configuration did not change. - That is if they are still present in the config and if their fundamental type did not change - (i.e. `GroupSlot` did not turn into a `ValueSlot` or vice versa). - - This method is called by pipeline and is not supposed to be used otherwise. - """ - self.root_slot = root_slot - new_slot_storage = root_slot.init_value() - new_slot_storage.update(self.slot_storage) - self.slot_storage = new_slot_storage
- -
[docs] def get_slot(self, slot_name: SlotName) -> BaseSlot: - """ - Get slot configuration from the slot name. - - :raises KeyError: If the slot with the specified name does not exist. - """ - slot = recursive_getattr(self.root_slot, slot_name) - if isinstance(slot, BaseSlot): - return slot - raise KeyError(f"Could not find slot {slot_name!r}.")
- -
[docs] async def extract_slot(self, slot_name: SlotName, ctx: Context, success_only: bool) -> None: - """ - Extract slot `slot_name` and store extracted value in `slot_storage`. - - :raises KeyError: If the slot with the specified name does not exist. - - :param slot_name: Name of the slot to extract. - :param ctx: Context. - :param success_only: Whether to store the value only if it is successfully extracted. - """ - slot = self.get_slot(slot_name) - value = await slot.get_value(ctx) - - if value.__slot_extracted__ or success_only is False: - recursive_setattr(self.slot_storage, slot_name, value)
- -
[docs] async def extract_all(self, ctx: Context): - """ - Extract all slots from slot configuration `root_slot` and set `slot_storage` to the extracted value. - """ - self.slot_storage = await self.root_slot.get_value(ctx)
- -
[docs] def get_extracted_slot(self, slot_name: SlotName) -> Union[ExtractedValueSlot, ExtractedGroupSlot]: - """ - Retrieve extracted value from `slot_storage`. - - :raises KeyError: If the slot with the specified name does not exist. - """ - slot = recursive_getattr(self.slot_storage, slot_name) - if isinstance(slot, ExtractedSlot): - return slot - raise KeyError(f"Could not find slot {slot_name!r}.")
- -
[docs] def is_slot_extracted(self, slot_name: str) -> bool: - """ - Return if the specified slot is extracted. - - :raises KeyError: If the slot with the specified name does not exist. - """ - return self.get_extracted_slot(slot_name).__slot_extracted__
- -
[docs] def all_slots_extracted(self) -> bool: - """ - Return if all slots are extracted. - """ - return self.slot_storage.__slot_extracted__
- -
[docs] def unset_slot(self, slot_name: SlotName) -> None: - """ - Mark specified slot as not extracted and clear extracted value. - - :raises KeyError: If the slot with the specified name does not exist. - """ - self.get_extracted_slot(slot_name).__unset__()
- -
[docs] def unset_all_slots(self) -> None: - """ - Mark all slots as not extracted and clear all extracted values. - """ - self.slot_storage.__unset__()
- -
[docs] class KwargOnlyFormatter(Formatter): -
[docs] def get_value(self, key, args, kwargs): - return super().get_value(str(key), args, kwargs)
- -
[docs] def fill_template(self, template: str) -> Optional[str]: - """ - Fill `template` string with extracted slot values and return a formatted string - or None if an exception has occurred while trying to fill template. - - `template` should be a format-string: - - E.g. "Your username is {profile.username}". - - For the example above, if ``profile.username`` slot has value "admin", - it would return the following text: - "Your username is admin". - """ - try: - return self.KwargOnlyFormatter().format(template, **dict(self.slot_storage.__pydantic_extra__.items())) - except Exception as exc: - logger.exception("An exception occurred during template filling.", exc_info=exc) - return None
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/stats/cli.html b/_modules/chatsky/stats/cli.html deleted file mode 100644 index 7ed3766ae..000000000 --- a/_modules/chatsky/stats/cli.html +++ /dev/null @@ -1,851 +0,0 @@ - - - - - - - - - - chatsky.stats.cli — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.stats.cli

-"""
-Command Line Interface
-----------------------
-This modules defines commands that can be called via the command line interface.
-
-"""
-
-from uuid import uuid4
-import tempfile
-import shutil
-import sys
-import argparse
-import os
-import logging
-from urllib import parse
-from pathlib import Path
-from typing import Optional
-
-try:
-    from omegaconf import OmegaConf
-    from .utils import get_superset_session, drop_superset_assets
-except ImportError:
-    raise ImportError("Some packages are not found. Run `pip install chatsky[stats]`")
-
-logger = logging.getLogger(__name__)
-logger.setLevel(logging.INFO)
-
-CHATSKY_DIR = Path(__file__).absolute().parent.parent
-"""
-Root directory of the local `chatsky` installation.
-
-:meta hide-value:
-"""
-DASHBOARD_DIR = str(CHATSKY_DIR / "config" / "superset_dashboard")
-"""
-Local path to superset dashboard files to import.
-
-:meta hide-value:
-"""
-DASHBOARD_SLUG = "chatsky-stats"
-"""
-This variable stores a slug used for building the http address of the Chatsky dashboard.
-"""
-DEFAULT_SUPERSET_URL = parse.urlunsplit(("http", "localhost:8088", "/", "", ""))
-"""
-Default location of the Superset dashboard.
-"""
-
-TYPE_MAPPING_CH = {
-    "FLOAT": "Nullable(Float64)",
-    "STRING": "Nullable(String)",
-    "LONGINTEGER": "Nullable(Int64)",
-    "INTEGER": "Nullable(Int64)",
-    "DATETIME": "Nullable(DateTime)",
-}
-"""
-Mapping of standard sql column types to Clickhouse native types.
-
-:meta hide-value:
-"""
-
-CHATSKY_NODE_STATS_STATEMENT = """
-WITH main AS (
-    SELECT DISTINCT {table}.LogAttributes['context_id'] as context_id,
-    toUInt64OrNull({table}.LogAttributes['request_id']) as request_id,
-    toDateTime(otel_traces.Timestamp) as start_time,
-    otel_traces.SpanName as data_key,
-    {table}.Body as data,
-    {lblfield} as label,
-    {flowfield} as flow_label,
-    {nodefield} as node_label,
-    {table}.TraceId as trace_id,
-    otel_traces.TraceId\nFROM {table}, otel_traces
-    WHERE {table}.TraceId = otel_traces.TraceId and data_key = 'get_current_label'
-    ORDER BY context_id, request_id
-) SELECT context_id,
-    request_id,
-    start_time,
-    data_key,
-    data,
-    label,
-    {label_lag} as prev_label,
-    {flow_lag} as prev_flow,
-    flow_label,
-    node_label
-FROM main
-"""
-CHATSKY_STATS_STATEMENT = """
-WITH main AS (
-    SELECT DISTINCT {table}.LogAttributes['context_id'] as context_id,
-    toUInt64OrNull({table}.LogAttributes['request_id']) as request_id,
-    toDateTime(otel_traces.Timestamp) as start_time,
-    otel_traces.SpanName as data_key,
-    {table}.Body as data,
-    {lblfield} as label,
-    {flowfield} as flow_label,
-    {nodefield} as node_label,
-    {table}.TraceId as trace_id,
-    otel_traces.TraceId\nFROM {table}, otel_traces
-    WHERE {table}.TraceId = otel_traces.TraceId
-    ORDER BY data_key, context_id, request_id
-) SELECT context_id,
-    request_id,
-    start_time,
-    data_key,
-    data,
-    label,
-    {label_lag} as prev_label,
-    {flow_lag} as prev_flow,
-    flow_label,
-    node_label
-FROM main
-"""
-CHATSKY_FINAL_NODES_STATEMENT = """
-WITH main AS (
-    SELECT LogAttributes['context_id'] AS context_id,
-    max(toUInt64OrNull(LogAttributes['request_id'])) AS max_history
-    FROM {table}\nGROUP BY context_id
-)
-SELECT DISTINCT LogAttributes['context_id'] AS context_id,
-toUInt64OrNull({table}.LogAttributes['request_id']) AS request_id,
-toDateTime(otel_traces.Timestamp) AS start_time,
-{lblfield} AS label,
-{flowfield} AS flow_label,
-{nodefield} AS node_label
-FROM {table}
-INNER JOIN main
-ON context_id  = main.context_id
-AND request_id = main.max_history
-INNER JOIN otel_traces
-ON {table}.TraceId = otel_traces.TraceId
-WHERE otel_traces.SpanName = 'get_current_label'
-"""
-
-SQL_STATEMENT_MAPPING = {
-    "chatsky_stats.yaml": CHATSKY_STATS_STATEMENT,
-    "chatsky_node_stats.yaml": CHATSKY_NODE_STATS_STATEMENT,
-    "chatsky_final_nodes.yaml": CHATSKY_FINAL_NODES_STATEMENT,
-}
-"""
-Select statements for dashboard configuration with names and types represented as placeholders.
-The placeholder system makes queries database agnostic, required values are set during the import phase.
-
-:meta hide-value:
-"""
-
-
-
[docs]def import_dashboard(parsed_args: Optional[argparse.Namespace] = None, zip_file: Optional[str] = None): - """ - Import an Apache Superset dashboard to a local instance with specified arguments. - Before using the command, make sure you have your Superset instance - up and running: `ghcr.io/deeppavlov/superset_df_dashboard:latest`. - The import will override existing dashboard configurations if present. - - :param parsed_args: Command line arguments produced by `argparse`. - :param zip_file: Zip archived dashboard config. - """ - host = parsed_args.host if hasattr(parsed_args, "host") else "localhost" - port = parsed_args.port if hasattr(parsed_args, "port") else "8088" - superset_url = parse.urlunsplit(("http", f"{host}:{port}", "/", "", "")) - zip_filename = os.path.basename(zip_file) - db_password = getattr(parsed_args, "db.password") - - session, headers = get_superset_session(parsed_args, superset_url) - drop_superset_assets(session, headers, superset_url) - import_dashboard_url = parse.urljoin(superset_url, "/api/v1/dashboard/import/") - # upload files - with open(zip_file, "rb") as f: - response = session.request( - "POST", - import_dashboard_url, - headers=headers, - data={ - "passwords": '{"databases/chatsky_database.yaml":"' + db_password + '"}', - "overwrite": "true", - }, - files=[("formData", (zip_filename, f, "application/zip"))], - ) - response.raise_for_status() - logger.info(f"Upload finished with status {response.status_code}.")
- - -
[docs]def make_zip_config(parsed_args: argparse.Namespace) -> Path: - """ - Make a zip-archived Apache Superset dashboard config, using specified arguments. - - :param parsed_args: Command line arguments produced by `argparse`. - """ - if hasattr(parsed_args, "outfile") and parsed_args.outfile: - outfile_name = parsed_args.outfile - else: - outfile_name = f"config_{str(uuid4())}.zip" - - file_conf = OmegaConf.load(parsed_args.file) - sys.argv = [__file__] + [f"{key}={value}" for key, value in parsed_args.__dict__.items() if value] - cmd_conf = OmegaConf.from_cli() - cli_conf = OmegaConf.merge(file_conf, cmd_conf) - - if OmegaConf.select(cli_conf, "db.driver") == "clickhousedb+connect": - params = dict( - table="${db.table}", - label_lag="lagInFrame(label) OVER " - "(PARTITION BY context_id ORDER BY request_id ASC " - "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)", - flow_lag="lagInFrame(flow_label) OVER " - "(PARTITION BY context_id ORDER BY request_id ASC " - "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)", - texttype="String", - lblfield="JSON_VALUE(${db.table}.Body, '$.label')", - flowfield="JSON_VALUE(${db.table}.Body, '$.flow')", - nodefield="JSON_VALUE(${db.table}.Body, '$.node')", - ) - else: - raise ValueError("The only supported database driver is 'clickhousedb+connect'.") - - conf = SQL_STATEMENT_MAPPING.copy() - for key in conf.keys(): - conf[key] = {} - conf[key]["sql"] = SQL_STATEMENT_MAPPING[key].format(**params) - - resolve_conf = OmegaConf.create( - { - "database": { - "sqlalchemy_uri": "${db.driver}://${db.user}:XXXXXXXXXX@${db.host}:${db.port}/${db.name}", - }, - **conf, - } - ) - - user_config = OmegaConf.merge(cli_conf, resolve_conf) - OmegaConf.resolve(user_config) - - with tempfile.TemporaryDirectory() as temp_config_dir: - nested_temp_dir = os.path.join(temp_config_dir, "superset_dashboard") - logger.info(f"Copying config files to temporary directory: {nested_temp_dir}.") - - shutil.copytree(DASHBOARD_DIR, nested_temp_dir) - database_dir = Path(os.path.join(nested_temp_dir, "databases")) - dataset_dir = Path(os.path.join(nested_temp_dir, "datasets/chatsky_database")) - - logger.info("Overriding the initial configuration.") - # overwrite sqlalchemy uri - for filepath in database_dir.iterdir(): - file_config = OmegaConf.load(filepath) - new_file_config = OmegaConf.merge(file_config, OmegaConf.select(user_config, "database")) - OmegaConf.save(new_file_config, filepath) - - # overwrite sql expressions and column types - for filepath in dataset_dir.iterdir(): - file_config = OmegaConf.load(filepath) - new_file_config = OmegaConf.merge(file_config, getattr(user_config, filepath.name)) - if OmegaConf.select(cli_conf, "db.driver") == "clickhousedb+connect": - for col in OmegaConf.select(new_file_config, "columns"): - col.type = TYPE_MAPPING_CH.get(col.type, col.type) - OmegaConf.save(new_file_config, filepath) - - if ".zip" not in outfile_name: - raise ValueError(f"Outfile name missing .zip extension: {outfile_name}.") - logger.info(f"Saving the archive to {outfile_name}.") - shutil.make_archive(outfile_name[: outfile_name.rindex(".zip")], format="zip", root_dir=temp_config_dir) - - return Path(outfile_name)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/stats/default_extractors.html b/_modules/chatsky/stats/default_extractors.html deleted file mode 100644 index f0c24454c..000000000 --- a/_modules/chatsky/stats/default_extractors.html +++ /dev/null @@ -1,677 +0,0 @@ - - - - - - - - - - chatsky.stats.default_extractors — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.stats.default_extractors

-"""
-Default Extractors
-------------------
-This module includes a pool of default extractors
-that you can use out of the box.
-
-The default configuration for Superset dashboard leverages the data collected
-by the extractors below. In order to use the default charts,
-make sure that you include those functions in your pipeline.
-Detailed examples can be found in the `tutorials` section.
-
-"""
-
-from datetime import datetime
-
-from chatsky.core import Context
-from chatsky.core.service.extra import ExtraHandlerRuntimeInfo
-from .utils import get_extra_handler_name
-
-
-
[docs]async def get_current_label(ctx: Context, info: ExtraHandlerRuntimeInfo): - """ - Extract the current label on each turn. - This function is required for running the dashboard with the default configuration. - - .. note:: - - Preferrably, it needs to be invoked as `after_handler` of the `Actor` service. - - """ - last_label = ctx.last_label - return { - "flow": last_label.flow_name, - "node": last_label.node_name, - "label": f"{last_label.flow_name}: {last_label.node_name}", - }
- - -
[docs]async def get_timing_before(ctx: Context, info: ExtraHandlerRuntimeInfo): - """ - Store the pipeline component's start time inside the context. - This function is required for running the dashboard with the default configuration. - """ - start_time = datetime.now() - ctx.framework_data.stats[get_extra_handler_name(info, "time")] = start_time
- - -
[docs]async def get_timing_after(ctx: Context, info: ExtraHandlerRuntimeInfo): # noqa: F811 - """ - Extract the pipeline component's execution time. - Requires :py:func:`~.get_timing_before` to be called previously in order to calculate the time. - This function is required for running the dashboard with the default configuration. - """ - start_time = ctx.framework_data.stats.pop(get_extra_handler_name(info, "time"), None) - if start_time is None: - return None - data = {"execution_time": str(datetime.now() - start_time)} - return data
- - -
[docs]async def get_last_response(ctx: Context, info: ExtraHandlerRuntimeInfo): - """ - Extract the text of the last response in the current context. - This handler is best used together with the `Actor` component. - - This function is required to enable charts that aggregate requests and responses. - """ - data = {"last_response": ctx.last_response.text} - return data
- - -
[docs]async def get_last_request(ctx: Context, info: ExtraHandlerRuntimeInfo): - """ - Extract the text of the last request in the current context. - This handler is best used together with the `Actor` component. - - This function is required to enable charts that aggregate requests and responses. - """ - data = {"last_request": ctx.last_request.text} - return data
- - -__all__ = ["get_current_label", "get_timing_before", "get_timing_after", "get_last_request", "get_last_response"] -""" -List of exported functions. - -:meta hide-avlue: -""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/stats/instrumentor.html b/_modules/chatsky/stats/instrumentor.html deleted file mode 100644 index 6dab6e228..000000000 --- a/_modules/chatsky/stats/instrumentor.html +++ /dev/null @@ -1,787 +0,0 @@ - - - - - - - - - - chatsky.stats.instrumentor — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.stats.instrumentor

-"""
-Instrumentor
--------------
-This modules contains the :py:class:`~OtelInstrumentor` class that implements
-Opentelemetry's ``BaseInstrumentor`` interface and allows for automated
-instrumentation of Chatsky applications,
-e.g. for automated logging and log export.
-"""
-
-import asyncio
-import logging
-from typing import Collection, Optional
-
-from wrapt import wrap_function_wrapper, decorator
-from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
-from opentelemetry.instrumentation.utils import unwrap
-from opentelemetry.metrics import get_meter, get_meter_provider, Meter
-from opentelemetry.trace import get_tracer, get_tracer_provider, Tracer
-from opentelemetry._logs import get_logger, get_logger_provider, Logger, SeverityNumber
-from opentelemetry.trace import SpanKind, Span
-from opentelemetry.sdk.trace import TracerProvider
-from opentelemetry.sdk._logs import LoggerProvider, LogRecord
-from opentelemetry.sdk.metrics import MeterProvider
-from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter
-from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
-from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter
-
-from chatsky.core.context import get_last_index
-from chatsky.stats.utils import (
-    resource,
-    get_extra_handler_name,
-    set_logger_destination,
-    set_meter_destination,
-    set_tracer_destination,
-)
-from chatsky.stats import default_extractors
-
-logger = logging.getLogger(__name__)
-
-INSTRUMENTS = ["chatsky"]
-
-
-
[docs]class OtelInstrumentor(BaseInstrumentor): - """ - Utility class for instrumenting Chatsky-related functions - that implements the :py:class:`.BaseInstrumentor` interface. - :py:meth:`._instrument` and :py:meth:`._uninstrument` methods - are available to apply and revert the instrumentation effects, - e.g. enable and disable logging at runtime. - - .. code-block:: - - chatsky_instrumentor = OtelInstrumentor() - chatsky_instrumentor._instrument() - chatsky_instrumentor._uninstrument() - - Opentelemetry provider instances can be optionally passed to the class constructor. - Otherwise, the global logger, tracer and meter providers are leveraged. - - The class implements the :py:meth:`.__call__` method, so that - regular functions can be decorated using the class instance. - - .. code-block:: - - @chatsky_instrumentor - async def function(context, pipeline, runtime_info): - ... - - :param logger_provider: Opentelemetry logger provider. Used to construct a logger instance. - :param tracer_provider: Opentelemetry tracer provider. Used to construct a tracer instance. - :parame meter_provider: Opentelemetry meter provider. Used to construct a meter instance. - """ - - def __init__(self, logger_provider=None, tracer_provider=None, meter_provider=None) -> None: - super().__init__() - self._logger_provider: Optional[LoggerProvider] = None - self._tracer_provider: Optional[TracerProvider] = None - self._meter_provider: Optional[MeterProvider] = None - self._logger: Optional[Logger] = None - self._tracer: Optional[Tracer] = None - self._meter: Optional[Meter] = None - self._configure_providers( - logger_provider=logger_provider, tracer_provider=tracer_provider, meter_provider=meter_provider - ) - - def __enter__(self): - if not self.is_instrumented_by_opentelemetry: - self.instrument() - return self - - def __exit__(self): - if self.is_instrumented_by_opentelemetry: - self.uninstrument() - -
[docs] @classmethod - def from_url(cls, url: str, insecure: bool = True, timeout: Optional[int] = None): - """ - Construct an instrumentor instance using only the url of the OTLP Collector. - Inherently modifies the global provider instances adding an export destination - for the target url. - - .. code-block:: - - instrumentor = OtelInstrumentor.from_url("grpc://localhost:4317") - - :param url: Url of the running Otel Collector server. Due to limited support of HTTP protocol - by the Opentelemetry Python extension, GRPC protocol is preferred. - :param insecure: Whether non-SSL-protected connection is allowed. Defaults to True. - :param timeout: Connection timeout in seconds, optional. - """ - set_logger_destination(OTLPLogExporter(endpoint=url, insecure=insecure, timeout=timeout)) - set_tracer_destination(OTLPSpanExporter(endpoint=url, insecure=insecure, timeout=timeout)) - set_meter_destination(OTLPMetricExporter(endpoint=url, insecure=insecure, timeout=timeout)) - return cls()
- -
[docs] def instrumentation_dependencies(self) -> Collection[str]: - """ - :meta private: - - Required libraries. Implements the Python Opentelemetry instrumentor interface. - - """ - return INSTRUMENTS
- -
[docs] def _instrument(self, logger_provider=None, tracer_provider=None, meter_provider=None): - if any([logger_provider, meter_provider, tracer_provider]): - self._configure_providers( - logger_provider=logger_provider, tracer_provider=tracer_provider, meter_provider=meter_provider - ) - for func_name in default_extractors.__all__: - wrap_function_wrapper(default_extractors, func_name, self.__call__.__wrapped__)
- -
[docs] def _uninstrument(self, **kwargs): - for func_name in default_extractors.__all__: - unwrap(default_extractors, func_name)
- -
[docs] def _configure_providers(self, logger_provider, tracer_provider, meter_provider): - self._logger_provider = logger_provider or get_logger_provider() - self._tracer_provider = tracer_provider or get_tracer_provider() - self._meter_provider = meter_provider or get_meter_provider() - self._logger = get_logger(__name__, None, self._logger_provider) - self._tracer = get_tracer(__name__, None, self._tracer_provider) - self._meter = get_meter(__name__, None, self._meter_provider)
- -
[docs] @decorator - async def __call__(self, wrapped, _, args, kwargs): - """ - Regular functions that match the :py:class:`~chatsky.core.service.types.ExtraHandlerFunction` - signature can be decorated with the class instance to log the returned value. - This method implements the logging procedure. - The returned value is assumed to be `dict` or `NoneType`. - Logging non-atomic values is discouraged, as they cannot be translated using - the `Protobuf` protocol. - Logging is ignored if the application is in 'uninstrumented' state. - - :param wrapped: Function to decorate. - :param args: Positional arguments of the decorated function. - :param kwargs: Keyword arguments of the decorated function. - """ - ctx, info = args - pipeline_component = get_extra_handler_name(info) - attributes = { - "context_id": str(ctx.id), - "request_id": get_last_index(ctx.labels), - "pipeline_component": pipeline_component, - } - - result: Optional[dict] - try: - if asyncio.iscoroutinefunction(wrapped): - result = await wrapped(ctx, info) - else: - result = wrapped(ctx, info) - - if result is None or not self.is_instrumented_by_opentelemetry: - # self.is_instrumented_by_opentelemetry allows to disable - # the decorator programmatically if - # instrumentation is disabled. - return result - - span: Span - with self._tracer.start_as_current_span(wrapped.__name__, kind=SpanKind.INTERNAL) as span: - span_ctx = span.get_span_context() - record = LogRecord( - span_id=span_ctx.span_id, - trace_id=span_ctx.trace_id, - body=result, - trace_flags=span_ctx.trace_flags, - severity_text=None, - severity_number=SeverityNumber(1), - resource=resource, - attributes=attributes, - ) - self._logger.emit(record=record) - return result - - except Exception as exc: - logger.error(f"Stats collector {wrapped.__name__} execution failed!", exc_info=exc)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/stats/utils.html b/_modules/chatsky/stats/utils.html deleted file mode 100644 index add3b00b4..000000000 --- a/_modules/chatsky/stats/utils.html +++ /dev/null @@ -1,793 +0,0 @@ - - - - - - - - - - chatsky.stats.utils — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.stats.utils

-"""
-Utils
------
-This module includes utility functions designed for statistics collection.
-
-The functions below can be used to configure the opentelemetry destination.
-
-.. code:: python
-
-    set_logger_destination(OTLPLogExporter("grpc://localhost:4317", insecure=True))
-    set_tracer_destination(OTLPSpanExporter("grpc://localhost:4317", insecure=True))
-
-"""
-
-import json
-import getpass
-from urllib import parse
-from typing import Optional, Tuple
-from argparse import Namespace, Action
-
-import requests
-from opentelemetry.sdk.resources import Resource
-from opentelemetry._logs import get_logger_provider, set_logger_provider
-from opentelemetry.trace import get_tracer_provider, set_tracer_provider
-from opentelemetry.metrics import get_meter_provider, set_meter_provider
-from opentelemetry.sdk.trace import TracerProvider
-from opentelemetry.sdk._logs import LoggerProvider
-from opentelemetry.sdk.metrics import MeterProvider
-from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
-from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
-from opentelemetry.sdk.trace.export import BatchSpanProcessor
-from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter, SpanExporter
-from opentelemetry.exporter.otlp.proto.grpc._log_exporter import OTLPLogExporter, LogExporter
-from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter, MetricExporter
-
-from chatsky.core.service.extra import ExtraHandlerRuntimeInfo
-
-SERVICE_NAME = "chatsky"
-
-resource = Resource.create({"service.name": SERVICE_NAME})
-"""
-Singletone :py:class:`~Resource` instance shared inside the framework.
-"""
-tracer_provider = TracerProvider(resource=resource)
-"""
-Global tracer provider bound to the Chatsky resource.
-"""
-logger_provider = LoggerProvider(resource=resource)
-"""
-Global logger provider bound to the Chatsky resource.
-"""
-set_logger_provider(logger_provider)
-set_tracer_provider(tracer_provider)
-
-
-
[docs]def set_logger_destination(exporter: Optional[LogExporter] = None): - """ - Configure the global Opentelemetry logger provider to export logs to the given destination. - - :param exporter: Opentelemetry log exporter instance. - """ - if exporter is None: - exporter = OTLPLogExporter(endpoint="grpc://localhost:4317", insecure=True) - get_logger_provider().add_log_record_processor(BatchLogRecordProcessor(exporter))
- - -
[docs]def set_meter_destination(exporter: Optional[MetricExporter] = None): - """ - Configure the global Opentelemetry meter provider to export metrics to the given destination. - - :param exporter: Opentelemetry meter exporter instance. - """ - if exporter is None: - exporter = OTLPMetricExporter(endpoint="grpc://localhost:4317", insecure=True) - cur_meter_provider = get_meter_provider() - new_meter_provider = MeterProvider(resource=resource, metric_readers=[PeriodicExportingMetricReader(exporter)]) - if not isinstance(cur_meter_provider, MeterProvider): - set_meter_provider(new_meter_provider)
- - -
[docs]def set_tracer_destination(exporter: Optional[SpanExporter] = None): - """ - Configure the global Opentelemetry tracer provider to export traces to the given destination. - - :param exporter: Opentelemetry span exporter instance. - """ - if exporter is None: - exporter = OTLPSpanExporter(endpoint="grpc://localhost:4317", insecure=True) - get_tracer_provider().add_span_processor(BatchSpanProcessor(exporter))
- - -
[docs]def get_extra_handler_name(info: ExtraHandlerRuntimeInfo, postfix: str = "") -> str: - """ - This function can be used to obtain a key, under which the wrapper data will be stored - in the context. - - :param info: Handler runtime info obtained from the pipeline. - :param postfix: Field-specific postfix that will be appended to the field name. - """ - - path = info.component.path.replace(".", "-") - return f"{path}" + (f"-{postfix}" if postfix else "")
- - -
[docs]def get_superset_session(args: Namespace, base_url: str = "http://localhost:8088/") -> Tuple[requests.Session, dict]: - """ - Utility function for authorized interaction with Superset HTTP API. - - :param args: Command line arguments including Superset username and Superset password. - :param base_url: Base Superset URL. - - :return: Authorized session - authorization headers tuple. - """ - healthcheck_url = parse.urljoin(base_url, "/healthcheck") - login_url = parse.urljoin(base_url, "/api/v1/security/login") - csrf_url = parse.urljoin(base_url, "/api/v1/security/csrf_token/") - - session = requests.Session() - # do healthcheck - response = session.get(healthcheck_url, timeout=10) - response.raise_for_status() - # get access token - access_request = session.post( - login_url, - headers={"Content-Type": "application/json", "Accept": "*/*"}, - data=json.dumps({"username": args.username, "password": args.password, "refresh": True, "provider": "db"}), - ) - access_token = access_request.json()["access_token"] - # get csrf_token - csrf_request = session.get(csrf_url, headers={"Authorization": f"Bearer {access_token}"}) - csrf_token = csrf_request.json()["result"] - headers = { - "Authorization": f"Bearer {access_token}", - "X-CSRFToken": csrf_token, - } - return session, headers
- - -
[docs]def drop_superset_assets(session: requests.Session, headers: dict, base_url: str): - """ - Drop the existing assets from the Superset dashboard. - - :param session: Authorized Superset session. - :param headers: Superset session headers. - :param base_url: Base Superset URL. - """ - dashboard_url = parse.urljoin(base_url, "/api/v1/dashboard") - charts_url = parse.urljoin(base_url, "/api/v1/chart") - datasets_url = parse.urljoin(base_url, "/api/v1/dataset") - database_url = parse.urljoin(base_url, "/api/v1/database/") - delete_res: requests.Response - - dashboard_res = session.get(dashboard_url, headers=headers) - dashboard_json = dashboard_res.json() - if dashboard_json["count"] > 0: - delete_res = requests.delete(dashboard_url, params={"q": json.dumps(dashboard_json["ids"])}, headers=headers) - delete_res.raise_for_status() - - charts_result = session.get(charts_url, headers=headers) - charts_json = charts_result.json() - if charts_json["count"] > 0: - delete_res = requests.delete(charts_url, params={"q": json.dumps(charts_json["ids"])}, headers=headers) - delete_res.raise_for_status() - - datasets_result = session.get(datasets_url, headers=headers) - datasets_json = datasets_result.json() - if datasets_json["count"] > 0: - delete_res = requests.delete(datasets_url, params={"q": json.dumps(datasets_json["ids"])}, headers=headers) - delete_res.raise_for_status() - - database_res = session.get(database_url, headers=headers) - database_json = database_res.json() - if database_json["count"] > 0: - delete_res = requests.delete(database_url + str(database_json["ids"][-1]), headers=headers) - delete_res.raise_for_status()
- - -
[docs]class PasswordAction(Action): - """ - Child class for Argparse's :py:class:`~Action` that prompts users for passwords interactively, - ensuring password safety, unless the password is specified directly. - - """ - - def __init__( - self, option_strings, dest=None, nargs=0, default=None, required=False, type=None, metavar=None, help=None - ): - super().__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - default=default, - required=required, - metavar=metavar, - type=type, - help=help, - ) - -
[docs] def __call__(self, parser, args, values, option_string=None): - if values: - print(f"{self.dest}: setting passwords explicitly through the command line is discouraged.") - setattr(args, self.dest, values) - else: - setattr(args, self.dest, getpass.getpass(prompt=f"{self.dest}: "))
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/db_benchmark/basic_config.html b/_modules/chatsky/utils/db_benchmark/basic_config.html deleted file mode 100644 index 8585a4c0f..000000000 --- a/_modules/chatsky/utils/db_benchmark/basic_config.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - - - - - chatsky.utils.db_benchmark.basic_config — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.db_benchmark.basic_config

-"""
-Basic Config
-------------
-This module contains basic benchmark configurations.
-
-It defines a simple configurations class (:py:class:`~.BasicBenchmarkConfig`)
-as well as a set of configurations that covers different dialogs a user might have and some edge-cases
-(:py:data:`~.basic_configurations`).
-"""
-
-from typing import Tuple, Optional
-import string
-import random
-
-from humanize import naturalsize
-from pympler import asizeof
-
-from chatsky.core import Message, Context
-from chatsky.utils.db_benchmark.benchmark import BenchmarkConfig
-
-
-
[docs]def get_dict(dimensions: Tuple[int, ...]): - """ - Return misc dictionary build in `dimensions` dimensions. - - :param dimensions: - Dimensions of the dictionary. - Each element of the dimensions tuple is the number of keys on the corresponding level of the dictionary. - The last element of the dimensions tuple is the length of the string values of the dict. - - e.g. dimensions=(1, 2) returns a dictionary with 1 key that points to a string of len 2. - whereas dimensions=(1, 2, 3) returns a dictionary with 1 key that points to a dictionary - with 2 keys each of which points to a string of len 3. - - So, the len of dimensions is the depth of the dictionary, while its values are - the width of the dictionary at each level. - """ - - def _get_dict(dimensions: Tuple[int, ...]): - if len(dimensions) < 2: - # get a random string of length dimensions[0] - return "".join(random.choice(string.printable) for _ in range(dimensions[0])) - return {str(i): _get_dict(dimensions[1:]) for i in range(dimensions[0])} - - if len(dimensions) > 1: - return _get_dict(dimensions) - elif len(dimensions) == 1: - return _get_dict((dimensions[0], 0)) - else: - return _get_dict((0, 0))
- - -
[docs]def get_message(message_dimensions: Tuple[int, ...]): - """ - Return message with a non-empty misc field. - - :param message_dimensions: Dimensions of the misc field of the message. See :py:func:`~.get_dict`. - """ - return Message(misc=get_dict(message_dimensions))
- - -
[docs]def get_context( - dialog_len: int, - message_dimensions: Tuple[int, ...], - misc_dimensions: Tuple[int, ...], -) -> Context: - """ - Return context with a non-empty misc, labels, requests, responses fields. - - :param dialog_len: Number of labels, requests and responses. - :param message_dimensions: - A parameter used to generate messages for requests and responses. See :py:func:`~.get_message`. - :param misc_dimensions: - A parameter used to generate misc field. See :py:func:`~.get_dict`. - """ - return Context( - labels={i: (f"flow_{i}", f"node_{i}") for i in range(dialog_len)}, - requests={i: get_message(message_dimensions) for i in range(dialog_len)}, - responses={i: get_message(message_dimensions) for i in range(dialog_len)}, - misc=get_dict(misc_dimensions), - )
- - -
[docs]class BasicBenchmarkConfig(BenchmarkConfig, frozen=True): - """ - A simple benchmark configuration that generates contexts using two parameters: - - - `message_dimensions` -- to configure the way messages are generated. - - `misc_dimensions` -- to configure size of context's misc field. - - Dialog length is configured using `from_dialog_len`, `to_dialog_len`, `step_dialog_len`. - """ - - context_num: int = 30 - """ - Number of times the contexts will be benchmarked. - Increasing this number decreases standard error of the mean for benchmarked data. - """ - from_dialog_len: int = 300 - """Starting dialog len of a context.""" - to_dialog_len: int = 311 - """ - Final dialog len of a context. - :py:meth:`~.BasicBenchmarkConfig.context_updater` will return contexts - until their dialog len is less then `to_dialog_len`. - """ - step_dialog_len: int = 1 - """ - Increment step for dialog len. - :py:meth:`~.BasicBenchmarkConfig.context_updater` will return contexts - increasing dialog len by `step_dialog_len`. - """ - message_dimensions: Tuple[int, ...] = (10, 10) - """ - Dimensions of misc dictionaries inside messages. - See :py:func:`~.get_message`. - """ - misc_dimensions: Tuple[int, ...] = (10, 10) - """ - Dimensions of misc dictionary. - See :py:func:`~.get_dict`. - """ - -
[docs] def get_context(self) -> Context: - """ - Return context with `from_dialog_len`, `message_dimensions`, `misc_dimensions`. - - Wraps :py:func:`~.get_context`. - """ - return get_context(self.from_dialog_len, self.message_dimensions, self.misc_dimensions)
- -
[docs] def info(self): - """ - Return fields of this instance and sizes of objects defined by this config. - - :return: - A dictionary with two keys. - Key "params" stores fields of this configuration. - Key "sizes" stores string representation of following values: - - - "starting_context_size" -- size of a context with `from_dialog_len`. - - "final_context_size" -- size of a context with `to_dialog_len`. - A context of this size will never actually be benchmarked. - - "misc_size" -- size of a misc field of a context. - - "message_size" -- size of a misc field of a message. - """ - return { - "params": self.model_dump(), - "sizes": { - "starting_context_size": naturalsize(asizeof.asizeof(self.get_context()), gnu=True), - "final_context_size": naturalsize( - asizeof.asizeof(get_context(self.to_dialog_len, self.message_dimensions, self.misc_dimensions)), - gnu=True, - ), - "misc_size": naturalsize(asizeof.asizeof(get_dict(self.misc_dimensions)), gnu=True), - "message_size": naturalsize(asizeof.asizeof(get_message(self.message_dimensions)), gnu=True), - }, - }
- -
[docs] def context_updater(self, context: Context) -> Optional[Context]: - """ - Update context to have `step_dialog_len` more labels, requests and responses, - unless such dialog len would be equal to `to_dialog_len` or exceed than it, - in which case None is returned. - """ - start_len = len(context.labels) - if start_len + self.step_dialog_len < self.to_dialog_len: - for i in range(start_len, start_len + self.step_dialog_len): - context.add_label((f"flow_{i}", f"node_{i}")) - context.add_request(get_message(self.message_dimensions)) - context.add_response(get_message(self.message_dimensions)) - return context - else: - return None
- - -basic_configurations = { - "large-misc": BasicBenchmarkConfig( - from_dialog_len=1, - to_dialog_len=50, - message_dimensions=(3, 5, 6, 5, 3), - misc_dimensions=(2, 4, 3, 8, 100), - ), - "short-messages": BasicBenchmarkConfig( - from_dialog_len=500, - to_dialog_len=550, - message_dimensions=(2, 30), - misc_dimensions=(0, 0), - ), - "default": BasicBenchmarkConfig(), - "large-misc--long-dialog": BasicBenchmarkConfig( - from_dialog_len=500, - to_dialog_len=550, - message_dimensions=(3, 5, 6, 5, 3), - misc_dimensions=(2, 4, 3, 8, 100), - ), - "very-long-dialog-len": BasicBenchmarkConfig( - context_num=10, - from_dialog_len=10000, - to_dialog_len=10050, - ), - "very-long-message-len": BasicBenchmarkConfig( - context_num=10, - from_dialog_len=1, - to_dialog_len=3, - message_dimensions=(10000, 1), - ), - "very-long-misc-len": BasicBenchmarkConfig( - context_num=10, - from_dialog_len=1, - to_dialog_len=3, - misc_dimensions=(10000, 1), - ), -} -""" -Configuration that covers many dialog cases (as well as some edge-cases). - -:meta hide-value: -""" -
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/db_benchmark/benchmark.html b/_modules/chatsky/utils/db_benchmark/benchmark.html deleted file mode 100644 index 1f70c94d5..000000000 --- a/_modules/chatsky/utils/db_benchmark/benchmark.html +++ /dev/null @@ -1,1005 +0,0 @@ - - - - - - - - - - chatsky.utils.db_benchmark.benchmark — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.db_benchmark.benchmark

-"""
-Context storage benchmarking
-----------------------------
-This module contains functions for context storages benchmarking.
-
-The basic function is :py:func:`~.time_context_read_write` but it has a low level interface.
-
-Higher level wrappers of the function provided by this module are:
-
-- :py:func:`~.save_results_to_file` -- saves results for a list of benchmark cases.
-- :py:func:`~.benchmark_all` -- a wrapper of `save_results_to_file`. Creates cases from configs.
-
-Wrappers use :py:class:`~.BenchmarkConfig` interface to configure benchmarks.
-A simple configuration class as well as a configuration set are provided by
-:py:mod:`chatsky.utils.db_benchmark.basic_config`.
-
-To view files generated by :py:func:`~.save_results_to_file` use either
-:py:func:`~chatsky.utils.db_benchmark.report.report` or
-`our streamlit app <../_misc/benchmark_streamlit.py>`_.
-"""
-
-from uuid import uuid4
-from pathlib import Path
-from time import perf_counter
-from typing import Tuple, List, Dict, Union, Optional, Callable, Any
-import json
-import importlib
-from statistics import mean
-import abc
-from traceback import extract_tb, StackSummary
-
-from pydantic import BaseModel, Field
-from tqdm.auto import tqdm
-
-from chatsky.context_storages import DBContextStorage
-from chatsky.core import Context
-
-
-
[docs]def time_context_read_write( - context_storage: DBContextStorage, - context_factory: Callable[[], Context], - context_num: int, - context_updater: Optional[Callable[[Context], Optional[Context]]] = None, -) -> Tuple[List[float], List[Dict[int, float]], List[Dict[int, float]]]: - """ - Benchmark `context_storage` by writing and reading `context`\\s generated by `context_factory` - into it / from it `context_num` times. - If `context_updater` is not `None` it is used to update `context`\\s and benchmark update operation. - - This function clears `context_storage` before and after execution. - - :param context_storage: Context storage to benchmark. - :param context_factory: A function that creates contexts which will be written into context storage. - :param context_num: A number of times the context will be written and read. - :param context_updater: - None or a function. - If not None, function should accept :py:class:`~.Context` and return an updated :py:class:`~.Context`. - The updated context can be either the same object (at the same pointer) or a different object (e.g. copied). - The updated context should have a higher dialog length than the received context - (to emulate context updating during dialog). - The function should return `None` to stop updating contexts. - For an example of such function, see implementation of - :py:meth:`chatsky.utils.db_benchmark.basic_config.BasicBenchmarkConfig.context_updater`. - - To avoid keeping many contexts in memory, - this function will be called repeatedly at least `context_num` times. - :return: - A tuple of 3 elements. - - The first element -- a list of write times. Its length is equal to `context_num`. - - The second element -- a list of dictionaries with read times. - Each dictionary maps from int to float. The key in the mapping is the `dialog_len` of the context and the - values are the read times for the corresponding `dialog_len`. - If `context_updater` is None, all dictionaries will have only one key -- - dialog length of the context returned by `context_factory`. - Otherwise, the dictionaries will also have a key for each updated context. - - The third element -- a list of dictionaries with update times. - Structurally the same as the second element, but none of the elements here have a key for - dialog_len of the context returned by `context_factory`. - So if `context_updater` is None, all dictionaries will be empty. - """ - context_storage.clear() - - write_times: List[float] = [] - read_times: List[Dict[int, float]] = [] - update_times: List[Dict[int, float]] = [] - - for _ in tqdm(range(context_num), desc=f"Benchmarking context storage:{context_storage.full_path}", leave=False): - context = context_factory() - - ctx_id = uuid4() - - # write operation benchmark - write_start = perf_counter() - context_storage[ctx_id] = context - write_times.append(perf_counter() - write_start) - - read_times.append({}) - update_times.append({}) - - # read operation benchmark - read_start = perf_counter() - _ = context_storage[ctx_id] - read_time = perf_counter() - read_start - read_times[-1][len(context.labels)] = read_time - - if context_updater is not None: - updated_context = context_updater(context) - - while updated_context is not None: - update_start = perf_counter() - context_storage[ctx_id] = updated_context - update_time = perf_counter() - update_start - update_times[-1][len(updated_context.labels)] = update_time - - read_start = perf_counter() - _ = context_storage[ctx_id] - read_time = perf_counter() - read_start - read_times[-1][len(updated_context.labels)] = read_time - - updated_context = context_updater(updated_context) - - context_storage.clear() - return write_times, read_times, update_times
- - -
[docs]class DBFactory(BaseModel): - """ - A class for storing information about context storage to benchmark. - Also used to create a context storage from the configuration. - """ - - uri: str - """URI of the context storage.""" - factory_module: str = "chatsky.context_storages" - """A module containing `factory`.""" - factory: str = "context_storage_factory" - """Name of the context storage factory. (function that creates context storages from URIs)""" - -
[docs] def db(self): - """ - Create a context storage using `factory` from `uri`. - """ - module = importlib.import_module(self.factory_module) - return getattr(module, self.factory)(self.uri)
- - -
[docs]class BenchmarkConfig(BaseModel, abc.ABC, frozen=True): - """ - Configuration for a benchmark. - - Defines methods and parameters required to run :py:func:`~.time_context_read_write`. - Also defines a method (`info`) for displaying information about this configuration. - - A simple way to configure benchmarks is provided by - :py:class:`~.chatsky.utils.db_benchmark.basic_config.BasicBenchmarkConfig`. - - Inherit from this class only if `BasicBenchmarkConfig` is not enough for your benchmarking needs. - """ - - context_num: int = 30 - """ - Number of times the contexts will be benchmarked. - Increasing this number decreases standard error of the mean for benchmarked data. - """ - -
[docs] @abc.abstractmethod - def get_context(self) -> Context: - """ - Return context to benchmark read and write operations with. - - This function will be called `context_num` times. - """ - ...
- -
[docs] @abc.abstractmethod - def info(self) -> Dict[str, Any]: - """ - Return a dictionary with information about this configuration. - """ - ...
- -
[docs] @abc.abstractmethod - def context_updater(self, context: Context) -> Optional[Context]: - """ - Update context with new dialog turns or return `None` to stop updates. - - This function is used to benchmark update and read operations. - - This function will be called AT LEAST `context_num` times. - - :return: Updated context or `None` to stop updating context. - """ - ...
- - -
[docs]class BenchmarkCase(BaseModel): - """ - This class represents a benchmark case and includes - information about it, its configuration and configuration of a context storage to benchmark. - """ - - name: str - """Name of a benchmark case.""" - db_factory: DBFactory - """DBFactory that specifies context storage to benchmark.""" - benchmark_config: BenchmarkConfig - """Benchmark configuration.""" - uuid: str = Field(default_factory=lambda: str(uuid4())) - """Unique id of the case. Defaults to a random uuid.""" - description: str = "" - """Description of the case. Defaults to an empty string.""" - -
[docs] @staticmethod - def set_average_results(benchmark): - """ - Modify `benchmark` dictionary to include averaged benchmark results. - - Add field "average_results" to the benchmark that contains the following fields: - - - average_write_time - - average_read_time - - average_update_time - - read_times_grouped_by_context_num -- a list of read times. - Each element is the average of read times with the same context_num. - - read_times_grouped_by_dialog_len -- a dictionary of read times. - Its values are the averages of read times with the same dialog_len, - its keys are dialog_len values. - - update_times_grouped_by_context_num - - update_times_grouped_by_dialog_len - - pretty_write -- average write time with only 3 significant digits. - - pretty_read - - pretty_update - - pretty_read+update -- sum of average read and update times with only 3 significant digits. - - :param benchmark: - A dictionary returned by `BenchmarkCase._run`. - Should include a "success" and "result" fields. - "success" field should be true. - "result" field should be a dictionary with the values returned by - :py:func:`~.time_context_read_write` and keys - "write_times", "read_times" and "update_times". - :return: None - """ - if not benchmark["success"] or isinstance(benchmark["result"], str): - return - - def get_complex_stats(results): - if len(results) == 0 or len(results[0]) == 0: - return [], {}, None - - average_grouped_by_context_num = [mean(times.values()) for times in results] - average_grouped_by_dialog_len = {key: mean([times[key] for times in results]) for key in results[0].keys()} - average = float(mean(average_grouped_by_context_num)) - return average_grouped_by_context_num, average_grouped_by_dialog_len, average - - read_stats = get_complex_stats(benchmark["result"]["read_times"]) - update_stats = get_complex_stats(benchmark["result"]["update_times"]) - - result = { - "average_write_time": mean(benchmark["result"]["write_times"]), - "average_read_time": read_stats[2], - "average_update_time": update_stats[2], - "read_times_grouped_by_context_num": read_stats[0], - "read_times_grouped_by_dialog_len": read_stats[1], - "update_times_grouped_by_context_num": update_stats[0], - "update_times_grouped_by_dialog_len": update_stats[1], - } - result["pretty_write"] = ( - float(f'{result["average_write_time"]:.3}') if result["average_write_time"] is not None else None - ) - result["pretty_read"] = ( - float(f'{result["average_read_time"]:.3}') if result["average_read_time"] is not None else None - ) - result["pretty_update"] = ( - float(f'{result["average_update_time"]:.3}') if result["average_update_time"] is not None else None - ) - result["pretty_read+update"] = ( - float(f'{result["average_read_time"] + result["average_update_time"]:.3}') - if result["average_read_time"] is not None and result["average_update_time"] is not None - else None - ) - - benchmark["average_results"] = result
- -
[docs] def _run(self): - try: - write_times, read_times, update_times = time_context_read_write( - self.db_factory.db(), - self.benchmark_config.get_context, - self.benchmark_config.context_num, - self.benchmark_config.context_updater, - ) - return { - "success": True, - "result": { - "write_times": write_times, - "read_times": read_times, - "update_times": update_times, - }, - } - except Exception as e: - return { - "success": False, - "result": { - "type": e.__class__.__name__, - "msg": getattr(e, "message", str(e)), - "traceback": "\n".join(StackSummary.from_list(extract_tb(e.__traceback__)).format()), - }, - }
- -
[docs] def run(self): - """ - Run benchmark, return results. - - :return: - A dictionary with 3 keys: "success", "result", "average_results". - - Success is a bool value. It is false if an exception was raised during benchmarking. - - Result is either an exception message or a dictionary with 3 keys - ("write_times", "read_times", "update_times"). - Values of those fields are the values returned by :py:func:`~.time_context_read_write`. - - Average results field is as described in :py:meth:`~.BenchmarkCase.set_average_results`. - """ - benchmark = self._run() - BenchmarkCase.set_average_results(benchmark) - return benchmark
- - -
[docs]def save_results_to_file( - benchmark_cases: List[BenchmarkCase], - file: Union[str, Path], - name: str, - description: str, - exist_ok: bool = False, -): - """ - Benchmark all `benchmark_cases` and save results to a file. - - Result are saved in json format with this schema: - `utils/db_benchmark/benchmark_schema.json <../_misc/benchmark_schema.json>`_. - - Files created by this function cen be viewed either by using :py:func:`~chatsky.utils.db_benchmark.report.report` or - streamlit app located in the utils directory: - `utils/db_benchmark/benchmark_streamlit.py <../_misc/benchmark_streamlit.py>`_. - - :param benchmark_cases: A list of benchmark cases that specify benchmarks. - :param file: File to save results to. - :param name: Name of the benchmark set. - :param description: Description of the benchmark set. - :param exist_ok: Whether to continue if the file already exists. - """ - with open(file, "w" if exist_ok else "x", encoding="utf-8") as fd: - uuid = str(uuid4()) - result: Dict[str, Any] = { - "name": name, - "description": description, - "uuid": uuid, - "benchmarks": [], - } - cases = tqdm(benchmark_cases, leave=False) - case: BenchmarkCase - for case in cases: - cases.set_description(f"Benchmarking: {case.name}") - result["benchmarks"].append( - { - **case.model_dump(exclude={"benchmark_config"}), - "benchmark_config": case.benchmark_config.info(), - **case.run(), - } - ) - - json.dump(result, fd)
- - -
[docs]def benchmark_all( - file: Union[str, Path], - name: str, - description: str, - db_uri: str, - benchmark_configs: Dict[str, BenchmarkConfig], - exist_ok: bool = False, -): - """ - A wrapper for :py:func:`~.save_results_to_file`. - - Generates `benchmark_cases` from `db_uri` and `benchmark_configs`: - `db_uri` is used to initialize :py:class:`~.DBFactory` instance - which is then used along with `benchmark_configs` to initialize :py:class:`~.BenchmarkCase` instances. - - :param file: File to save results to. - :param name: Name of the benchmark set. - :param description: Description of the benchmark set. The same description is used for benchmark cases. - :param db_uri: URI of the database to benchmark - :param benchmark_configs: Mapping from case names to configs. - :param exist_ok: Whether to continue if the file already exists. - """ - save_results_to_file( - [ - BenchmarkCase( - name=case_name, - description=description, - db_factory=DBFactory(uri=db_uri), - benchmark_config=benchmark_config, - ) - for case_name, benchmark_config in benchmark_configs.items() - ], - file, - name, - description, - exist_ok=exist_ok, - )
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/db_benchmark/report.html b/_modules/chatsky/utils/db_benchmark/report.html deleted file mode 100644 index 4b46df4ca..000000000 --- a/_modules/chatsky/utils/db_benchmark/report.html +++ /dev/null @@ -1,655 +0,0 @@ - - - - - - - - - - chatsky.utils.db_benchmark.report — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.db_benchmark.report

-"""
-Report
---------
-This method contains a function to print benchmark results to console.
-"""
-
-from pathlib import Path
-from typing import Union, Set, Literal
-import json
-
-
-
[docs]def report( - file: Union[str, Path], - display: Set[Literal["name", "desc", "config", "metrics"]] = set({"name", "metrics"}), -): - """ - Print average results from a result file to stdout. - - Printed stats contain benchmark configs, object sizes, average benchmark values for successful cases and - exception message for unsuccessful cases. - - :param file: - File with benchmark results generated by - :py:func:`~chatsky.utils.db_benchmark.benchmark.save_results_to_file`. - :param display: - A set of objects to display in results. - Values allowed inside the set: - - - "name" -- displays the name of the benchmark case. - - "desc" -- displays the description of the benchmark case. - - "config" -- displays the config info of the benchmark case. - - "metrics" -- displays average write, read, update read+update times. - """ - with open(file, "r", encoding="utf-8") as fd: - file_contents = json.load(fd) - - sep = "-" * 80 - - report_result = "\n".join([sep, file_contents["name"], sep, file_contents["description"], sep, ""]) - - for benchmark in file_contents["benchmarks"]: - reported_values = { - "name": benchmark["name"], - "desc": benchmark["description"], - "config": "\n".join(f"{k}: {v}" for k, v in benchmark["benchmark_config"].items()), - "metrics": "".join( - [ - ( - f"{metric.title() + ': ' + str(benchmark['average_results']['pretty_' + metric]):20}" - if benchmark["success"] - else benchmark["result"] - ) - for metric in ("write", "read", "update", "read+update") - ] - ), - } - - result = [] - for value_name, value in reported_values.items(): - if value_name in display: - result.append(value) - result.append("") - - report_result += f"\n{sep}\n".join(result) - - print(report_result, end="")
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/devel/async_helpers.html b/_modules/chatsky/utils/devel/async_helpers.html deleted file mode 100644 index e63c2c401..000000000 --- a/_modules/chatsky/utils/devel/async_helpers.html +++ /dev/null @@ -1,614 +0,0 @@ - - - - - - - - - - chatsky.utils.devel.async_helpers — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.devel.async_helpers

-"""
-Async Helpers
--------------
-Tools to help with async.
-"""
-
-import asyncio
-from typing import Callable, Any
-
-
-
[docs]async def wrap_sync_function_in_async(func: Callable, *args, **kwargs) -> Any: - """ - Utility function, that wraps both functions and coroutines in coroutines. - Invokes `func` if it is just a callable and awaits, if it is a coroutine. - - :param func: Callable to wrap. - :param \\*args: Function args. - :param \\**kwargs: Function kwargs. - :return: What function returns. - """ - result = func(*args, **kwargs) - if asyncio.iscoroutine(result): - return await result - else: - return result
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/devel/extra_field_helpers.html b/_modules/chatsky/utils/devel/extra_field_helpers.html deleted file mode 100644 index 68b86d2a0..000000000 --- a/_modules/chatsky/utils/devel/extra_field_helpers.html +++ /dev/null @@ -1,611 +0,0 @@ - - - - - - - - - - chatsky.utils.devel.extra_field_helpers — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.devel.extra_field_helpers

-"""
-Extra field helpers
--------------------
-Helpers for managing pydantic extra fields.
-"""
-
-from typing import List
-
-from pydantic import BaseModel
-
-
-
[docs]def grab_extra_fields(attachment: BaseModel, extra_fields: List[str]): - """ - Convenience method for passing attachment extras as named arguments to API functions. - This might be useful for making sure no typos appear in code. - Accepts a list of extra names and makes a dictionary of extras mathing these names. - - :param attachment: attachment whose extras will be used. - :param extra_fields: list of extras that will be used. - """ - - return {extra_field: attachment.__pydantic_extra__.get(extra_field, None) for extra_field in extra_fields}
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/devel/json_serialization.html b/_modules/chatsky/utils/devel/json_serialization.html deleted file mode 100644 index 1e0838081..000000000 --- a/_modules/chatsky/utils/devel/json_serialization.html +++ /dev/null @@ -1,743 +0,0 @@ - - - - - - - - - - chatsky.utils.devel.json_serialization — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.devel.json_serialization

-"""
-Serialization
--------------
-Tools that provide JSON serialization via Pickle for unserializable objects.
-
-- :py:data:`~.PickleEncodedValue`:
-    A field annotated with this will be pickled/unpickled during JSON-serialization/validation.
-- :py:data:`~.JSONSerializableDict`:
-    A dictionary field annotated with this will make all its items smart-serializable:
-    If an item is serializable -- nothing would change.
-    Otherwise -- it will be serialized via pickle.
-- :py:class:`~.JSONSerializableExtras`:
-    A pydantic base class that makes its extra fields a `JSONSerializableDict`.
-"""
-
-from base64 import decodebytes, encodebytes
-from copy import deepcopy
-from pickle import dumps, loads
-from typing import Any, Dict, List, Union
-from typing_extensions import TypeAlias
-from pydantic import (
-    JsonValue,
-    RootModel,
-    BaseModel,
-    model_validator,
-    model_serializer,
-)
-from pydantic_core import PydanticSerializationError
-
-_JSON_EXTRA_FIELDS_KEYS = "__pickled_extra_fields__"
-"""
-This key is used in :py:data:`~.JSONSerializableDict` to remember pickled items.
-"""
-
-Serializable: TypeAlias = Dict[str, Union[JsonValue, List[Any], Dict[str, Any], Any]]
-"""Type annotation for objects supported by :py:func:`~.json_pickle_serializer`."""
-
-
-
[docs]class _WrapperModel(RootModel): - """ - Wrapper model for testing whether an object is serializable to JSON. - """ - - root: Any
- - -
[docs]def pickle_serializer(value: Any) -> str: - """ - Serializer function that serializes any pickle-serializable value into JSON-serializable. - Serializes value with pickle and encodes bytes as base64 string. - - :param value: Pickle-serializable object. - :return: String-encoded object. - """ - - return encodebytes(dumps(value)).decode()
- - -
[docs]def pickle_validator(value: str) -> Any: - """ - Validator function that validates base64 string encoded bytes as a pickle-serializable value. - Decodes base64 string and validates value with pickle. - - :param value: String-encoded string. - :return: Pickle-serializable object. - """ - - return loads(decodebytes(value.encode()))
- - -
[docs]def json_pickle_serializer(model: Serializable) -> Serializable: - """ - Serializer function that serializes a dictionary or Pydantic object to JSON. - For every object field, it checks whether the field is JSON serializable, - and if it's not, serializes it using pickle. - It also keeps track of pickle-serializable field names in a special list. - - :param model: Pydantic model object or a dictionary. - :original_serializer: Original serializer function for model. - :return: model with all the fields serialized to JSON. - """ - - extra_fields = list() - model_copy = deepcopy(model) - - for field_name, field_value in model_copy.items(): - try: - if isinstance(field_value, bytes): - raise PydanticSerializationError("") - else: - model_copy[field_name] = _WrapperModel(root=field_value).model_dump(mode="json") - except PydanticSerializationError: - model_copy[field_name] = pickle_serializer(field_value) - extra_fields += [field_name] - - if len(extra_fields) > 0: - model_copy[_JSON_EXTRA_FIELDS_KEYS] = extra_fields - return model_copy
- - -
[docs]def json_pickle_validator(model: Serializable) -> Serializable: - """ - Validator function that validates a JSON dictionary to a python dictionary. - For every object field, it checks if the field is pickle-serialized, - and if it is, validates it using pickle. - - :param model: Pydantic model object or a dictionary. - :return: model with all the fields serialized to JSON. - """ - - model_copy = deepcopy(model) - - if _JSON_EXTRA_FIELDS_KEYS in model.keys(): - for extra_key in model[_JSON_EXTRA_FIELDS_KEYS]: - extra_value = model[extra_key] - model_copy[extra_key] = pickle_validator(extra_value) - del model_copy[_JSON_EXTRA_FIELDS_KEYS] - - return model_copy
- - -
[docs]class JSONSerializableExtras(BaseModel, extra="allow"): - """ - This model makes extra fields pickle-serializable. - Do not use :py:data:`~._JSON_EXTRA_FIELDS_KEYS` as an extra field name. - """ - - def __init__(self, **kwargs): # supress unknown arg warnings - super().__init__(**kwargs) - -
[docs] @model_validator(mode="after") - def extra_validator(self): - """ - Validate model along with the `extras` field: i.e. all the fields not listed in the model. - - :return: Validated model. - """ - self.__pydantic_extra__ = json_pickle_validator(self.__pydantic_extra__) - return self
- -
[docs] @model_serializer(mode="wrap", when_used="json") - def extra_serializer(self, original_serializer) -> Dict[str, Any]: - """ - Serialize model along with the `extras` field: i.e. all the fields not listed in the model. - - :param original_serializer: Function originally used for serialization by Pydantic. - :return: Serialized model. - """ - model_copy = self.model_copy(deep=True) - for extra_name in self.model_extra.keys(): - delattr(model_copy, extra_name) - model_dict = original_serializer(model_copy) - model_dict.update(json_pickle_serializer(self.model_extra)) - return model_dict
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/testing/cleanup_db.html b/_modules/chatsky/utils/testing/cleanup_db.html deleted file mode 100644 index 895774243..000000000 --- a/_modules/chatsky/utils/testing/cleanup_db.html +++ /dev/null @@ -1,702 +0,0 @@ - - - - - - - - - - chatsky.utils.testing.cleanup_db — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.testing.cleanup_db

-"""
-Cleanup DB
-----------
-This module defines functions that allow to delete data in various types of databases,
-including JSON, MongoDB, Pickle, Redis, Shelve, SQL, and YDB databases.
-"""
-
-import os
-
-from chatsky.context_storages import (
-    JSONContextStorage,
-    MongoContextStorage,
-    PickleContextStorage,
-    RedisContextStorage,
-    ShelveContextStorage,
-    SQLContextStorage,
-    YDBContextStorage,
-    json_available,
-    mongo_available,
-    pickle_available,
-    redis_available,
-    sqlite_available,
-    postgres_available,
-    mysql_available,
-    ydb_available,
-)
-
-
-
[docs]async def delete_json(storage: JSONContextStorage): - """ - Delete all data from a JSON context storage. - - :param storage: A JSONContextStorage object. - """ - if not json_available: - raise Exception("Can't delete JSON database - JSON provider unavailable!") - if os.path.isfile(storage.path): - os.remove(storage.path)
- - -
[docs]async def delete_mongo(storage: MongoContextStorage): - """ - Delete all data from a MongoDB context storage. - - :param storage: A MongoContextStorage object - """ - if not mongo_available: - raise Exception("Can't delete mongo database - mongo provider unavailable!") - await storage.collection.drop()
- - -
[docs]async def delete_pickle(storage: PickleContextStorage): - """ - Delete all data from a Pickle context storage. - - :param storage: A PickleContextStorage object. - """ - if not pickle_available: - raise Exception("Can't delete pickle database - pickle provider unavailable!") - if os.path.isfile(storage.path): - os.remove(storage.path)
- - -
[docs]async def delete_redis(storage: RedisContextStorage): - """ - Delete all data from a Redis context storage. - - :param storage: A RedisContextStorage object. - """ - if not redis_available: - raise Exception("Can't delete redis database - redis provider unavailable!") - await storage.clear_async()
- - -
[docs]async def delete_shelve(storage: ShelveContextStorage): - """ - Delete all data from a Shelve context storage. - - :param storage: A ShelveContextStorage object. - """ - if os.path.isfile(storage.path): - os.remove(storage.path)
- - -
[docs]async def delete_sql(storage: SQLContextStorage): - """ - Delete all data from an SQL context storage. - - :param storage: An SQLContextStorage object. - """ - if storage.dialect == "postgres" and not postgres_available: - raise Exception("Can't delete postgres database - postgres provider unavailable!") - if storage.dialect == "sqlite" and not sqlite_available: - raise Exception("Can't delete sqlite database - sqlite provider unavailable!") - if storage.dialect == "mysql" and not mysql_available: - raise Exception("Can't delete mysql database - mysql provider unavailable!") - async with storage.engine.connect() as conn: - await conn.run_sync(storage.table.drop, storage.engine)
- - -
[docs]async def delete_ydb(storage: YDBContextStorage): - """ - Delete all data from a YDB context storage. - - :param storage: A YDBContextStorage object. - """ - if not ydb_available: - raise Exception("Can't delete ydb database - ydb provider unavailable!") - - async def callee(session): - await session.drop_table("/".join([storage.database, storage.table_name])) - - await storage.pool.retry_operation(callee)
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/chatsky/utils/testing/common.html b/_modules/chatsky/utils/testing/common.html deleted file mode 100644 index 3d7950b9e..000000000 --- a/_modules/chatsky/utils/testing/common.html +++ /dev/null @@ -1,660 +0,0 @@ - - - - - - - - - - chatsky.utils.testing.common — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

Source code for chatsky.utils.testing.common

-"""
-Common
-------
-This module contains several functions which are used to run demonstrations in tutorials.
-"""
-
-from os import getenv
-from typing import Tuple, Iterable
-from uuid import uuid4
-
-from chatsky.core import Message, Pipeline
-from chatsky.core.message import MessageInitTypes
-
-
-
[docs]def is_interactive_mode() -> bool: # pragma: no cover - """ - Checking whether the tutorial code should be run in interactive mode. - - :return: `True` if it's being executed by Jupyter kernel and DISABLE_INTERACTIVE_MODE env variable isn't set, - `False` otherwise. - """ - - shell = None - try: - from IPython import get_ipython - - shell = get_ipython().__class__.__name__ - finally: - return shell != "ZMQInteractiveShell" and getenv("DISABLE_INTERACTIVE_MODE") is None
- - -
[docs]def check_happy_path( - pipeline: Pipeline, - happy_path: Iterable[Tuple[MessageInitTypes, MessageInitTypes]], - *, - response_comparator=Message.__eq__, - printout: bool = False, -): - """ - Running tutorial with provided pipeline for provided requests, comparing responses with correct expected responses. - - :param pipeline: The Pipeline instance, that will be used for checking. - :param happy_path: A tuple of (request, response) tuples, so-called happy path, - its requests are passed to pipeline and the pipeline responses are compared to its responses. - :param response_comparator: - Function that checks reference response (first argument) with the actual response (second argument). - Defaults to ``Message.__eq__``. - :param printout: Whether to print the requests/responses during iteration. - """ - ctx_id = uuid4() # get random ID for current context - for step_id, (request_raw, reference_response_raw) in enumerate(happy_path): - - request = Message.model_validate(request_raw) - reference_response = Message.model_validate(reference_response_raw) - if printout: - print(f"USER: {request}") - - ctx = pipeline(request, ctx_id) - - actual_response = ctx.last_response - if printout: - print(f"BOT : {actual_response}") - - if not response_comparator(reference_response, actual_response): - raise AssertionError( - f"""check_happy_path failed -step id: {step_id} -reference response: {reference_response} -actual response: {actual_response} -""" - )
-
- -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_modules/index.html b/_modules/index.html deleted file mode 100644 index e0f4f771f..000000000 --- a/_modules/index.html +++ /dev/null @@ -1,636 +0,0 @@ - - - - - - - - - - Overview: module code — chatsky 0.9.0 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
-
-
-
-
- - - -
-
- -
- - - - - - - - - - - -
- -
- - -
-
- -
-
- -
- -
- - - - -
- -
- - -
-
- - - - - -
- -

All modules for which code is available

- - -
- - - - - -
- -
-
-
- -
- - - - -
- - -
-
-
- - - - - - - - \ No newline at end of file diff --git a/_sources/about_us.rst.txt b/_sources/about_us.rst.txt deleted file mode 100644 index f6cd70f14..000000000 --- a/_sources/about_us.rst.txt +++ /dev/null @@ -1,8 +0,0 @@ -About us --------- - -Chatsky is developed by the Chatsky development department of `DeepPavlov `_. - -Founder and leader of the project -- Denis Kuznetsov. - -Developers -- Roman Zlobin, Aleksandr Sergeev, Daniil Ignatiev, Aleksandr Sakharov. \ No newline at end of file diff --git a/_sources/apiref/chatsky.conditions.service.rst.txt b/_sources/apiref/chatsky.conditions.service.rst.txt deleted file mode 100644 index 0567cc540..000000000 --- a/_sources/apiref/chatsky.conditions.service.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/conditions/service - -.. automodule:: chatsky.conditions.service - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.conditions.slots.rst.txt b/_sources/apiref/chatsky.conditions.slots.rst.txt deleted file mode 100644 index bf9d0e20b..000000000 --- a/_sources/apiref/chatsky.conditions.slots.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/conditions/slots - -.. automodule:: chatsky.conditions.slots - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.conditions.standard.rst.txt b/_sources/apiref/chatsky.conditions.standard.rst.txt deleted file mode 100644 index 8bd052058..000000000 --- a/_sources/apiref/chatsky.conditions.standard.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/conditions/standard - -.. automodule:: chatsky.conditions.standard - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.database.rst.txt b/_sources/apiref/chatsky.context_storages.database.rst.txt deleted file mode 100644 index 02bc5eed8..000000000 --- a/_sources/apiref/chatsky.context_storages.database.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/database - -.. automodule:: chatsky.context_storages.database - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.json.rst.txt b/_sources/apiref/chatsky.context_storages.json.rst.txt deleted file mode 100644 index 66fc32485..000000000 --- a/_sources/apiref/chatsky.context_storages.json.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/json - -.. automodule:: chatsky.context_storages.json - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.mongo.rst.txt b/_sources/apiref/chatsky.context_storages.mongo.rst.txt deleted file mode 100644 index 9a094cc39..000000000 --- a/_sources/apiref/chatsky.context_storages.mongo.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/mongo - -.. automodule:: chatsky.context_storages.mongo - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.pickle.rst.txt b/_sources/apiref/chatsky.context_storages.pickle.rst.txt deleted file mode 100644 index e96610c59..000000000 --- a/_sources/apiref/chatsky.context_storages.pickle.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/pickle - -.. automodule:: chatsky.context_storages.pickle - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.protocol.rst.txt b/_sources/apiref/chatsky.context_storages.protocol.rst.txt deleted file mode 100644 index 674f54933..000000000 --- a/_sources/apiref/chatsky.context_storages.protocol.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/protocol - -.. automodule:: chatsky.context_storages.protocol - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.redis.rst.txt b/_sources/apiref/chatsky.context_storages.redis.rst.txt deleted file mode 100644 index c11128bcd..000000000 --- a/_sources/apiref/chatsky.context_storages.redis.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/redis - -.. automodule:: chatsky.context_storages.redis - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.shelve.rst.txt b/_sources/apiref/chatsky.context_storages.shelve.rst.txt deleted file mode 100644 index a9dceed07..000000000 --- a/_sources/apiref/chatsky.context_storages.shelve.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/shelve - -.. automodule:: chatsky.context_storages.shelve - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.sql.rst.txt b/_sources/apiref/chatsky.context_storages.sql.rst.txt deleted file mode 100644 index bb298e408..000000000 --- a/_sources/apiref/chatsky.context_storages.sql.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/sql - -.. automodule:: chatsky.context_storages.sql - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.context_storages.ydb.rst.txt b/_sources/apiref/chatsky.context_storages.ydb.rst.txt deleted file mode 100644 index 760d082a3..000000000 --- a/_sources/apiref/chatsky.context_storages.ydb.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/context_storages/ydb - -.. automodule:: chatsky.context_storages.ydb - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.context.rst.txt b/_sources/apiref/chatsky.core.context.rst.txt deleted file mode 100644 index 934ea8f61..000000000 --- a/_sources/apiref/chatsky.core.context.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/context - -.. automodule:: chatsky.core.context - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.message.rst.txt b/_sources/apiref/chatsky.core.message.rst.txt deleted file mode 100644 index 88e1727ff..000000000 --- a/_sources/apiref/chatsky.core.message.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/message - -.. automodule:: chatsky.core.message - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.node_label.rst.txt b/_sources/apiref/chatsky.core.node_label.rst.txt deleted file mode 100644 index 534edb9e1..000000000 --- a/_sources/apiref/chatsky.core.node_label.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/node_label - -.. automodule:: chatsky.core.node_label - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.pipeline.rst.txt b/_sources/apiref/chatsky.core.pipeline.rst.txt deleted file mode 100644 index 0d026b2bc..000000000 --- a/_sources/apiref/chatsky.core.pipeline.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/pipeline - -.. automodule:: chatsky.core.pipeline - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.script.rst.txt b/_sources/apiref/chatsky.core.script.rst.txt deleted file mode 100644 index 4167471eb..000000000 --- a/_sources/apiref/chatsky.core.script.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/script - -.. automodule:: chatsky.core.script - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.script_function.rst.txt b/_sources/apiref/chatsky.core.script_function.rst.txt deleted file mode 100644 index 3c92e20e9..000000000 --- a/_sources/apiref/chatsky.core.script_function.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/script_function - -.. automodule:: chatsky.core.script_function - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.script_parsing.rst.txt b/_sources/apiref/chatsky.core.script_parsing.rst.txt deleted file mode 100644 index 4ade7fba0..000000000 --- a/_sources/apiref/chatsky.core.script_parsing.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/script_parsing - -.. automodule:: chatsky.core.script_parsing - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.actor.rst.txt b/_sources/apiref/chatsky.core.service.actor.rst.txt deleted file mode 100644 index d5b07a091..000000000 --- a/_sources/apiref/chatsky.core.service.actor.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/actor - -.. automodule:: chatsky.core.service.actor - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.component.rst.txt b/_sources/apiref/chatsky.core.service.component.rst.txt deleted file mode 100644 index b514f654a..000000000 --- a/_sources/apiref/chatsky.core.service.component.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/component - -.. automodule:: chatsky.core.service.component - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.extra.rst.txt b/_sources/apiref/chatsky.core.service.extra.rst.txt deleted file mode 100644 index 20ee30b7f..000000000 --- a/_sources/apiref/chatsky.core.service.extra.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/extra - -.. automodule:: chatsky.core.service.extra - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.group.rst.txt b/_sources/apiref/chatsky.core.service.group.rst.txt deleted file mode 100644 index 7d0ceedbb..000000000 --- a/_sources/apiref/chatsky.core.service.group.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/group - -.. automodule:: chatsky.core.service.group - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.service.rst.txt b/_sources/apiref/chatsky.core.service.service.rst.txt deleted file mode 100644 index eaa68b6d8..000000000 --- a/_sources/apiref/chatsky.core.service.service.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/service - -.. automodule:: chatsky.core.service.service - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.service.types.rst.txt b/_sources/apiref/chatsky.core.service.types.rst.txt deleted file mode 100644 index b4d0539cd..000000000 --- a/_sources/apiref/chatsky.core.service.types.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/service/types - -.. automodule:: chatsky.core.service.types - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.transition.rst.txt b/_sources/apiref/chatsky.core.transition.rst.txt deleted file mode 100644 index 58f8136af..000000000 --- a/_sources/apiref/chatsky.core.transition.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/transition - -.. automodule:: chatsky.core.transition - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.core.utils.rst.txt b/_sources/apiref/chatsky.core.utils.rst.txt deleted file mode 100644 index f9a75961a..000000000 --- a/_sources/apiref/chatsky.core.utils.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/core/utils - -.. automodule:: chatsky.core.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.destinations.standard.rst.txt b/_sources/apiref/chatsky.destinations.standard.rst.txt deleted file mode 100644 index 433f3754e..000000000 --- a/_sources/apiref/chatsky.destinations.standard.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/destinations/standard - -.. automodule:: chatsky.destinations.standard - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.messengers.common.interface.rst.txt b/_sources/apiref/chatsky.messengers.common.interface.rst.txt deleted file mode 100644 index 38b681549..000000000 --- a/_sources/apiref/chatsky.messengers.common.interface.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/messengers/common/interface - -.. automodule:: chatsky.messengers.common.interface - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.messengers.common.types.rst.txt b/_sources/apiref/chatsky.messengers.common.types.rst.txt deleted file mode 100644 index 4036e2dae..000000000 --- a/_sources/apiref/chatsky.messengers.common.types.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/messengers/common/types - -.. automodule:: chatsky.messengers.common.types - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.messengers.console.rst.txt b/_sources/apiref/chatsky.messengers.console.rst.txt deleted file mode 100644 index e86dc7eee..000000000 --- a/_sources/apiref/chatsky.messengers.console.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/messengers/console - -.. automodule:: chatsky.messengers.console - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.messengers.telegram.abstract.rst.txt b/_sources/apiref/chatsky.messengers.telegram.abstract.rst.txt deleted file mode 100644 index 01bdc5ab7..000000000 --- a/_sources/apiref/chatsky.messengers.telegram.abstract.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/messengers/telegram/abstract - -.. automodule:: chatsky.messengers.telegram.abstract - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.messengers.telegram.interface.rst.txt b/_sources/apiref/chatsky.messengers.telegram.interface.rst.txt deleted file mode 100644 index ff4524b8b..000000000 --- a/_sources/apiref/chatsky.messengers.telegram.interface.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/messengers/telegram/interface - -.. automodule:: chatsky.messengers.telegram.interface - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.processing.slots.rst.txt b/_sources/apiref/chatsky.processing.slots.rst.txt deleted file mode 100644 index a0388586c..000000000 --- a/_sources/apiref/chatsky.processing.slots.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/processing/slots - -.. automodule:: chatsky.processing.slots - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.processing.standard.rst.txt b/_sources/apiref/chatsky.processing.standard.rst.txt deleted file mode 100644 index ea28855f6..000000000 --- a/_sources/apiref/chatsky.processing.standard.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/processing/standard - -.. automodule:: chatsky.processing.standard - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.responses.slots.rst.txt b/_sources/apiref/chatsky.responses.slots.rst.txt deleted file mode 100644 index 06191efd3..000000000 --- a/_sources/apiref/chatsky.responses.slots.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/responses/slots - -.. automodule:: chatsky.responses.slots - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.responses.standard.rst.txt b/_sources/apiref/chatsky.responses.standard.rst.txt deleted file mode 100644 index 46e152bf3..000000000 --- a/_sources/apiref/chatsky.responses.standard.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/responses/standard - -.. automodule:: chatsky.responses.standard - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.slots.slots.rst.txt b/_sources/apiref/chatsky.slots.slots.rst.txt deleted file mode 100644 index 71030a9d6..000000000 --- a/_sources/apiref/chatsky.slots.slots.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/slots/slots - -.. automodule:: chatsky.slots.slots - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.stats.cli.rst.txt b/_sources/apiref/chatsky.stats.cli.rst.txt deleted file mode 100644 index 718e3fbd7..000000000 --- a/_sources/apiref/chatsky.stats.cli.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/stats/cli - -.. automodule:: chatsky.stats.cli - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.stats.default_extractors.rst.txt b/_sources/apiref/chatsky.stats.default_extractors.rst.txt deleted file mode 100644 index d48287e79..000000000 --- a/_sources/apiref/chatsky.stats.default_extractors.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/stats/default_extractors - -.. automodule:: chatsky.stats.default_extractors - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.stats.instrumentor.rst.txt b/_sources/apiref/chatsky.stats.instrumentor.rst.txt deleted file mode 100644 index eb8ed79c6..000000000 --- a/_sources/apiref/chatsky.stats.instrumentor.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/stats/instrumentor - -.. automodule:: chatsky.stats.instrumentor - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.stats.utils.rst.txt b/_sources/apiref/chatsky.stats.utils.rst.txt deleted file mode 100644 index c3d50672f..000000000 --- a/_sources/apiref/chatsky.stats.utils.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/stats/utils - -.. automodule:: chatsky.stats.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.db_benchmark.basic_config.rst.txt b/_sources/apiref/chatsky.utils.db_benchmark.basic_config.rst.txt deleted file mode 100644 index c712a5042..000000000 --- a/_sources/apiref/chatsky.utils.db_benchmark.basic_config.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/db_benchmark/basic_config - -.. automodule:: chatsky.utils.db_benchmark.basic_config - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.db_benchmark.benchmark.rst.txt b/_sources/apiref/chatsky.utils.db_benchmark.benchmark.rst.txt deleted file mode 100644 index c84df5848..000000000 --- a/_sources/apiref/chatsky.utils.db_benchmark.benchmark.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/db_benchmark/benchmark - -.. automodule:: chatsky.utils.db_benchmark.benchmark - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.db_benchmark.report.rst.txt b/_sources/apiref/chatsky.utils.db_benchmark.report.rst.txt deleted file mode 100644 index 74a508410..000000000 --- a/_sources/apiref/chatsky.utils.db_benchmark.report.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/db_benchmark/report - -.. automodule:: chatsky.utils.db_benchmark.report - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.devel.async_helpers.rst.txt b/_sources/apiref/chatsky.utils.devel.async_helpers.rst.txt deleted file mode 100644 index 696450282..000000000 --- a/_sources/apiref/chatsky.utils.devel.async_helpers.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/devel/async_helpers - -.. automodule:: chatsky.utils.devel.async_helpers - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.devel.extra_field_helpers.rst.txt b/_sources/apiref/chatsky.utils.devel.extra_field_helpers.rst.txt deleted file mode 100644 index 200ac53b3..000000000 --- a/_sources/apiref/chatsky.utils.devel.extra_field_helpers.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/devel/extra_field_helpers - -.. automodule:: chatsky.utils.devel.extra_field_helpers - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.devel.json_serialization.rst.txt b/_sources/apiref/chatsky.utils.devel.json_serialization.rst.txt deleted file mode 100644 index a3729f8f8..000000000 --- a/_sources/apiref/chatsky.utils.devel.json_serialization.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/devel/json_serialization - -.. automodule:: chatsky.utils.devel.json_serialization - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.testing.cleanup_db.rst.txt b/_sources/apiref/chatsky.utils.testing.cleanup_db.rst.txt deleted file mode 100644 index 4375a20b1..000000000 --- a/_sources/apiref/chatsky.utils.testing.cleanup_db.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/testing/cleanup_db - -.. automodule:: chatsky.utils.testing.cleanup_db - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.testing.common.rst.txt b/_sources/apiref/chatsky.utils.testing.common.rst.txt deleted file mode 100644 index 308eee8c5..000000000 --- a/_sources/apiref/chatsky.utils.testing.common.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/testing/common - -.. automodule:: chatsky.utils.testing.common - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/chatsky.utils.testing.toy_script.rst.txt b/_sources/apiref/chatsky.utils.testing.toy_script.rst.txt deleted file mode 100644 index 07fa4707f..000000000 --- a/_sources/apiref/chatsky.utils.testing.toy_script.rst.txt +++ /dev/null @@ -1,6 +0,0 @@ -:source_name: chatsky/utils/testing/toy_script - -.. automodule:: chatsky.utils.testing.toy_script - :members: - :undoc-members: - :show-inheritance: diff --git a/_sources/apiref/index_conditions.rst.txt b/_sources/apiref/index_conditions.rst.txt deleted file mode 100644 index bd7960a39..000000000 --- a/_sources/apiref/index_conditions.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Conditions -========== - -.. autosummary:: - :toctree: - - chatsky.conditions.standard - chatsky.conditions.slots - chatsky.conditions.service diff --git a/_sources/apiref/index_context_storages.rst.txt b/_sources/apiref/index_context_storages.rst.txt deleted file mode 100644 index fb1bebf94..000000000 --- a/_sources/apiref/index_context_storages.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Context Storages -================ - -.. autosummary:: - :toctree: - - chatsky.context_storages.json - chatsky.context_storages.ydb - chatsky.context_storages.sql - chatsky.context_storages.mongo - chatsky.context_storages.shelve - chatsky.context_storages.protocol - chatsky.context_storages.pickle - chatsky.context_storages.database - chatsky.context_storages.redis diff --git a/_sources/apiref/index_core.rst.txt b/_sources/apiref/index_core.rst.txt deleted file mode 100644 index 0209618ca..000000000 --- a/_sources/apiref/index_core.rst.txt +++ /dev/null @@ -1,19 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Core -==== - -.. autosummary:: - :toctree: - - chatsky.core.script_function - chatsky.core.pipeline - chatsky.core.context - chatsky.core.node_label - chatsky.core.transition - chatsky.core.script - chatsky.core.utils - chatsky.core.script_parsing - chatsky.core.message diff --git a/_sources/apiref/index_core.service.rst.txt b/_sources/apiref/index_core.service.rst.txt deleted file mode 100644 index b6f22bfe0..000000000 --- a/_sources/apiref/index_core.service.rst.txt +++ /dev/null @@ -1,16 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Core.Service -============ - -.. autosummary:: - :toctree: - - chatsky.core.service.component - chatsky.core.service.service - chatsky.core.service.group - chatsky.core.service.types - chatsky.core.service.extra - chatsky.core.service.actor diff --git a/_sources/apiref/index_db_benchmark.rst.txt b/_sources/apiref/index_db_benchmark.rst.txt deleted file mode 100644 index 73849e1d7..000000000 --- a/_sources/apiref/index_db_benchmark.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -DB Benchmark -============ - -.. autosummary:: - :toctree: - - chatsky.utils.db_benchmark.report - chatsky.utils.db_benchmark.basic_config - chatsky.utils.db_benchmark.benchmark diff --git a/_sources/apiref/index_destinations.rst.txt b/_sources/apiref/index_destinations.rst.txt deleted file mode 100644 index 50fb6f5e5..000000000 --- a/_sources/apiref/index_destinations.rst.txt +++ /dev/null @@ -1,11 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Destinations -============ - -.. autosummary:: - :toctree: - - chatsky.destinations.standard diff --git a/_sources/apiref/index_development_utils.rst.txt b/_sources/apiref/index_development_utils.rst.txt deleted file mode 100644 index 72a39c721..000000000 --- a/_sources/apiref/index_development_utils.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Development Utils -================= - -.. autosummary:: - :toctree: - - chatsky.utils.devel.json_serialization - chatsky.utils.devel.async_helpers - chatsky.utils.devel.extra_field_helpers diff --git a/_sources/apiref/index_messenger_interfaces.rst.txt b/_sources/apiref/index_messenger_interfaces.rst.txt deleted file mode 100644 index a490ab10d..000000000 --- a/_sources/apiref/index_messenger_interfaces.rst.txt +++ /dev/null @@ -1,15 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Messenger Interfaces -==================== - -.. autosummary:: - :toctree: - - chatsky.messengers.console - chatsky.messengers.common.interface - chatsky.messengers.telegram.abstract - chatsky.messengers.telegram.interface - chatsky.messengers.common.types diff --git a/_sources/apiref/index_processing.rst.txt b/_sources/apiref/index_processing.rst.txt deleted file mode 100644 index 072f93679..000000000 --- a/_sources/apiref/index_processing.rst.txt +++ /dev/null @@ -1,12 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Processing -========== - -.. autosummary:: - :toctree: - - chatsky.processing.slots - chatsky.processing.standard diff --git a/_sources/apiref/index_responses.rst.txt b/_sources/apiref/index_responses.rst.txt deleted file mode 100644 index 213480a67..000000000 --- a/_sources/apiref/index_responses.rst.txt +++ /dev/null @@ -1,12 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Responses -========= - -.. autosummary:: - :toctree: - - chatsky.responses.standard - chatsky.responses.slots diff --git a/_sources/apiref/index_slots.rst.txt b/_sources/apiref/index_slots.rst.txt deleted file mode 100644 index ec0dd09cf..000000000 --- a/_sources/apiref/index_slots.rst.txt +++ /dev/null @@ -1,11 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Slots -===== - -.. autosummary:: - :toctree: - - chatsky.slots.slots diff --git a/_sources/apiref/index_stats.rst.txt b/_sources/apiref/index_stats.rst.txt deleted file mode 100644 index 23a12db23..000000000 --- a/_sources/apiref/index_stats.rst.txt +++ /dev/null @@ -1,14 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Stats -===== - -.. autosummary:: - :toctree: - - chatsky.stats.default_extractors - chatsky.stats.cli - chatsky.stats.utils - chatsky.stats.instrumentor diff --git a/_sources/apiref/index_testing_utils.rst.txt b/_sources/apiref/index_testing_utils.rst.txt deleted file mode 100644 index 3adfffbfe..000000000 --- a/_sources/apiref/index_testing_utils.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -:orphan: - -.. This is an auto-generated RST file representing documentation source directory structure - -Testing Utils -============= - -.. autosummary:: - :toctree: - - chatsky.utils.testing.cleanup_db - chatsky.utils.testing.common - chatsky.utils.testing.toy_script diff --git a/_sources/community.rst.txt b/_sources/community.rst.txt deleted file mode 100644 index f23cb0497..000000000 --- a/_sources/community.rst.txt +++ /dev/null @@ -1,21 +0,0 @@ -Community ---------- - -This section provides links to different platforms where users of Chatsky can ask questions, -share their experiences, report issues, and communicate with the Chatsky development team and other Chatsky users. - -Please take a short survey about Chatsky: -`Google Form `_. -This will allow us to make it better. - -`DeepPavlov Forum `_ is designed to discuss various aspects of DeepPavlov, -which includes Chatsky. - -`Telegram `_ is a group chat where Chatsky users can ask questions and -get help from the community. - -`GitHub Issues `_ is a platform where users -can report issues, suggest features, and track the progress of Chatsky development. - -`Stack Overflow `_ is a platform where Chatsky users can ask -technical questions and get answers from the community. \ No newline at end of file diff --git a/_sources/development.rst.txt b/_sources/development.rst.txt deleted file mode 100644 index 2d1d240c1..000000000 --- a/_sources/development.rst.txt +++ /dev/null @@ -1,23 +0,0 @@ -Development ------------ - -Contribution -~~~~~~~~~~~~~~~ - -`Contribution rules `_ provide -guidelines and rules for contributing to the Chatsky project. It includes information on -how to contribute code to the project, manage your workflow, use tests, and so on. - -Project roadmap -~~~~~~~~~~~~~~~ - -`Project roadmap `_ -outlines the future development plans for Chatsky, including new features and enhancements -that are planned for upcoming releases. - -Release notes -~~~~~~~~~~~~~ - -`Release notes `_ -contain information about the latest releases of Chatsky, including new features, improvements, and bug fixes. - diff --git a/_sources/examples.rst.txt b/_sources/examples.rst.txt deleted file mode 100644 index 6c5854dbc..000000000 --- a/_sources/examples.rst.txt +++ /dev/null @@ -1,4 +0,0 @@ -Examples --------- - -Examples are available in this `repository `_. diff --git a/_sources/get_started.rst.txt b/_sources/get_started.rst.txt deleted file mode 100644 index ad3db756e..000000000 --- a/_sources/get_started.rst.txt +++ /dev/null @@ -1,79 +0,0 @@ -Getting started ---------------- - -Installation -~~~~~~~~~~~~ - -Chatsky can be easily installed on your system using the ``pip`` package manager: - -.. code-block:: console - - pip install chatsky - -This framework is compatible with Python 3.8 and newer versions. - -The above command will set the minimum dependencies to start working with Chatsky. -The installation process allows the user to choose from different packages based on their dependencies, which are: - -.. code-block:: console - - pip install chatsky[json] # dependencies for using JSON - pip install chatsky[pickle] # dependencies for using Pickle - pip install chatsky[redis] # dependencies for using Redis - pip install chatsky[mongodb] # dependencies for using MongoDB - pip install chatsky[mysql] # dependencies for using MySQL - pip install chatsky[postgresql] # dependencies for using PostgreSQL - pip install chatsky[sqlite] # dependencies for using SQLite - pip install chatsky[ydb] # dependencies for using Yandex Database - pip install chatsky[telegram] # dependencies for using Telegram - pip install chatsky[benchmark] # dependencies for benchmarking - -For example, if you are going to use one of the database backends, -you can specify the corresponding requirements yourself. - -Additionally, you also have the option to download the source code directly from the -`GitHub `_ repository using the commands: - -.. code-block:: console - - git clone https://github.com/deeppavlov/chatsky.git - cd chatsky - -Once you are in the directory, you can run the command ``poetry install --all-extras`` to set up all the requirements for the library. - -Quick start with a project template -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you don't want to bother with setting up project files, you can use the `Chatsky Project Template `_ -repository, which offers a ready-to-use simple bot that can be modified to your needs. - -Key concepts -~~~~~~~~~~~~ - -Chatsky is a powerful tool for creating conversational services. -It allows developers to easily write and manage dialog systems by defining a special -dialog graph that describes the behavior of the service. -Chatsky offers a specialized language (DSL) for quickly writing dialog graphs, -making it easy for developers to create chatbots for a wide -range of applications, such as social networks, call centers, websites, personal assistants, etc. - -Chatsky has several important concepts: - -**Script**: First of all, to create a dialog agent it is necessary -to create a dialog :py:class:`~chatsky.core.script.Script`. -A dialog `script` is a dictionary, where keys correspond to different `flows`. -A script can contain multiple scripts, which are flows too, what is needed in order to divide -a dialog into sub-dialogs and process them separately. - -**Flow**: As mentioned above, the dialog is divided into flows. -Each flow represent a sub-dialog corresponding to the discussion of a particular topic. -Each flow is also a dictionary, where the keys are the `nodes`. - -**Node**: A `node` is the smallest unit of a dialog flow, and it contains the bot's response -to a user's input as well as a `condition` that determines -the `transition` to another node, whether it's within the current or another flow. - -**Keywords**: Chatsky uses several special `keywords`. These keywords are the keys in the dictionaries inside the script. -The most important for using the framework are `RESPONSE` and `TRANSITIONS` keywords. -The first one corresponds to the response that the bot will send to the user from the current node. -The second corresponds to the transition conditions from the current node to other nodes. \ No newline at end of file diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt deleted file mode 100644 index efc14b7a8..000000000 --- a/_sources/index.rst.txt +++ /dev/null @@ -1,97 +0,0 @@ -.. Chatsky documentation master file, created by - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Chatsky -======= - -*Date*: |today| *Version*: |version| - -.. image:: https://pepy.tech/badge/chatsky - :alt: Number of downloads - :target: https://pypi.org/project/chatsky/ - -.. image:: https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11-green.svg - :alt: Supported python versions - -Chatsky is an open-source, `Apache 2.0 `_-licensed library -that was developed specifically for creating dialog systems. Chatsky provides a comprehensive set of tools and resources, -targeting a wide range of applications, including chatbots, virtual assistants, and other interactive systems. -It allows developers to easily create and manage complex dialog flows, integrate with natural language processing (NLP) tools, -and handle user input in a flexible and efficient manner. Additionally, the framework is highly customizable, -allowing developers to easily adapt it to their specific needs and requirements. - -Chatsky documentation includes the following sections: - -:doc:`Getting started <./get_started>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Essential information about installing and using the library -aimed at beginners can be found in the ``Getting started`` part -of the documentation. This section also introduces the basic terms -that form the principles of the framework. -For deeper understanding of the API, consult the documentation sections described below. - -:doc:`User guides <./user_guides>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``User guides`` section provides comprehensive human-readable explanations -of how your conversational service should be set up and function. -It specifically highlights the aspects that are not covered by the API reference, -e.g. deployment, optimization techniques, etc. - -:doc:`Tutorials <./tutorials>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Most capabilities of Chatsky can also be explored in the ``Tutorials`` -section. These interactive files dynamically showcase how different -modules and classes of the framework interact. - -:doc:`Examples <./examples>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Links to demonsration projects that leverage the library -integrating it with external services and models and serve as examples -can be found in this section. - -:doc:`API reference <./reference>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The API reference contains documentation for classes and abstractions -used in the library which can be used to determine the exact typing -and behavior of all the functions involved. - -:doc:`Development <./development>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``Development`` section shows the library's current development status and specifies the contribution rules. - -:doc:`Community <./community>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``Community`` section links you to useful resources where you can find supplemental information -about the framework and ask questions. - -:doc:`About us <./about_us>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can get more info about the development team in the ``About us`` section. - -.. toctree:: - :hidden: - :maxdepth: 1 - - get_started - user_guides - tutorials - examples - reference - development - community - about_us - -Indices and tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` diff --git a/_sources/reference.rst.txt b/_sources/reference.rst.txt deleted file mode 100644 index 467c763a2..000000000 --- a/_sources/reference.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -API reference -------------- - -.. toctree:: - :name: reference - :glob: - :maxdepth: 1 - - apiref/index_* diff --git a/_sources/tutorials.rst.txt b/_sources/tutorials.rst.txt deleted file mode 100644 index ad6783ed7..000000000 --- a/_sources/tutorials.rst.txt +++ /dev/null @@ -1,31 +0,0 @@ -Tutorials ---------- -Tutorials page is a collection of instructional materials designed to help developers learn -how to use Chatsky to build conversational agents. The tutorials cover a range of topics, -from getting started with Chatsky to more advanced topics such as integrating external APIs. -Each tutorial includes detailed explanations and code examples. Tutorials cover different aspects -of the framework and are organized into sections. - -The Context Storages section describes how to use context storages in Chatsky. - -The Messengers section covers how to use the Telegram messenger with Chatsky and how to create web interface. - -The Service section teaches how to use Services: objects that execute -before or after the main part. - -The Script section covers the basics of the script concept, including conditions, responses, transitions, -and serialization. It also includes tutorials on pre-response and pre-transition processing. - -The main difference between Tutorials and Examples is that Tutorials typically show how to implement -a specific feature or solve a particular problem, whereas Examples provide a more -comprehensive overview of how to build a complete application. - -| To understand the basics of Chatsky, read the following tutorials: -| 1) Script / Core / 1. Basics -| 2) Script / Core / 2. Conditions - -.. toctree:: - :name: tutorials - :glob: - - tutorials/index_* diff --git a/_sources/tutorials/index_context_storages.rst.txt b/_sources/tutorials/index_context_storages.rst.txt deleted file mode 100644 index 55d72b14b..000000000 --- a/_sources/tutorials/index_context_storages.rst.txt +++ /dev/null @@ -1,16 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Context Storages -================ - -.. nbgallery:: - tutorials.context_storages.1_basics.py - tutorials.context_storages.2_postgresql.py - tutorials.context_storages.3_mongodb.py - tutorials.context_storages.4_redis.py - tutorials.context_storages.5_mysql.py - tutorials.context_storages.6_sqlite.py - tutorials.context_storages.7_yandex_database.py - tutorials.context_storages.8_db_benchmarking.py diff --git a/_sources/tutorials/index_interfaces.rst.txt b/_sources/tutorials/index_interfaces.rst.txt deleted file mode 100644 index 1dd6acaba..000000000 --- a/_sources/tutorials/index_interfaces.rst.txt +++ /dev/null @@ -1,23 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Interfaces -========== - -Telegram --------- - -.. nbgallery:: - tutorials.messengers.telegram.1_basic.py - tutorials.messengers.telegram.2_attachments.py - tutorials.messengers.telegram.3_advanced.py - -Web API -------- - -.. nbgallery:: - tutorials.messengers.web_api_interface.1_fastapi.py - tutorials.messengers.web_api_interface.2_websocket_chat.py - tutorials.messengers.web_api_interface.3_load_testing_with_locust.py - tutorials.messengers.web_api_interface.4_streamlit_chat.py diff --git a/_sources/tutorials/index_script.rst.txt b/_sources/tutorials/index_script.rst.txt deleted file mode 100644 index 484881a77..000000000 --- a/_sources/tutorials/index_script.rst.txt +++ /dev/null @@ -1,27 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Script -====== - -Core ----- - -.. nbgallery:: - tutorials.script.core.1_basics.py - tutorials.script.core.2_conditions.py - tutorials.script.core.3_responses.py - tutorials.script.core.4_transitions.py - tutorials.script.core.5_global_local.py - tutorials.script.core.6_context_serialization.py - tutorials.script.core.7_pre_response_processing.py - tutorials.script.core.8_misc.py - tutorials.script.core.9_pre_transition_processing.py - -Responses ---------- - -.. nbgallery:: - tutorials.script.responses.1_media.py - tutorials.script.responses.2_multi_message.py diff --git a/_sources/tutorials/index_service.rst.txt b/_sources/tutorials/index_service.rst.txt deleted file mode 100644 index dfbe8d0a4..000000000 --- a/_sources/tutorials/index_service.rst.txt +++ /dev/null @@ -1,13 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Service -======= - -.. nbgallery:: - tutorials.service.1_basics.py - tutorials.service.2_advanced.py - tutorials.service.3_groups.py - tutorials.service.4_conditions_and_paths.py - tutorials.service.5_extra_handlers.py diff --git a/_sources/tutorials/index_slots.rst.txt b/_sources/tutorials/index_slots.rst.txt deleted file mode 100644 index d26067a76..000000000 --- a/_sources/tutorials/index_slots.rst.txt +++ /dev/null @@ -1,9 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Slots -===== - -.. nbgallery:: - tutorials.slots.1_basic_example.py diff --git a/_sources/tutorials/index_stats.rst.txt b/_sources/tutorials/index_stats.rst.txt deleted file mode 100644 index 64fa94120..000000000 --- a/_sources/tutorials/index_stats.rst.txt +++ /dev/null @@ -1,10 +0,0 @@ -:orphan: - -.. This is an auto-generated RST index file representing tutorials directory structure - -Stats -===== - -.. nbgallery:: - tutorials.stats.1_extractor_functions.py - tutorials.stats.2_pipeline_integration.py diff --git a/_sources/tutorials/tutorials.context_storages.1_basics.py.txt b/_sources/tutorials/tutorials.context_storages.1_basics.py.txt deleted file mode 100644 index 4dbc2335a..000000000 --- a/_sources/tutorials/tutorials.context_storages.1_basics.py.txt +++ /dev/null @@ -1,39 +0,0 @@ -# %% [markdown] -""" -# 1. Basics - -The following tutorial shows the basic use of the database connection. - -See %mddoclink(api,context_storages.database,context_storage_factory) function -for creating a context storage by path. - -In this example JSON file is used as a storage. -""" - -# %pip install chatsky[json,pickle] - -# %% -import pathlib - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - -pathlib.Path("dbs").mkdir(exist_ok=True) -db = context_storage_factory("json://dbs/file.json") -# db = context_storage_factory("pickle://dbs/file.pkl") -# db = context_storage_factory("shelve://dbs/file") - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - # a function for automatic tutorial running (testing) with HAPPY_PATH - - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.2_postgresql.py.txt b/_sources/tutorials/tutorials.context_storages.2_postgresql.py.txt deleted file mode 100644 index f13fc95b4..000000000 --- a/_sources/tutorials/tutorials.context_storages.2_postgresql.py.txt +++ /dev/null @@ -1,46 +0,0 @@ -# %% [markdown] -""" -# 2. PostgreSQL - -This is a tutorial on using PostgreSQL. - -See %mddoclink(api,context_storages.sql,SQLContextStorage) class -for storing your users' contexts in SQL databases. - -Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/) -and [asyncpg](https://magicstack.github.io/asyncpg/current/) -libraries for asynchronous access to PostgreSQL DB. -""" - -# %pip install chatsky[postgresql] - -# %% -import os - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -db_uri = "postgresql+asyncpg://{}:{}@localhost:5432/{}".format( - os.environ["POSTGRES_USERNAME"], - os.environ["POSTGRES_PASSWORD"], - os.environ["POSTGRES_DB"], -) -db = context_storage_factory(db_uri) - - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.3_mongodb.py.txt b/_sources/tutorials/tutorials.context_storages.3_mongodb.py.txt deleted file mode 100644 index 3bb80c53c..000000000 --- a/_sources/tutorials/tutorials.context_storages.3_mongodb.py.txt +++ /dev/null @@ -1,44 +0,0 @@ -# %% [markdown] -""" -# 3. MongoDB - -This is a tutorial on using MongoDB. - -See %mddoclink(api,context_storages.mongo,MongoContextStorage) class -for storing you users' contexts in Mongo database. - -Chatsky uses [motor](https://motor.readthedocs.io/en/stable/) -library for asynchronous access to MongoDB. -""" - -# %pip install chatsky[mongodb] - -# %% -import os - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -db_uri = "mongodb://{}:{}@localhost:27017/{}".format( - os.environ["MONGO_INITDB_ROOT_USERNAME"], - os.environ["MONGO_INITDB_ROOT_PASSWORD"], - os.environ["MONGO_INITDB_ROOT_USERNAME"], -) -db = context_storage_factory(db_uri) - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.4_redis.py.txt b/_sources/tutorials/tutorials.context_storages.4_redis.py.txt deleted file mode 100644 index 5325d2fb4..000000000 --- a/_sources/tutorials/tutorials.context_storages.4_redis.py.txt +++ /dev/null @@ -1,43 +0,0 @@ -# %% [markdown] -""" -# 4. Redis - -This is a tutorial on using Redis. - -See %mddoclink(api,context_storages.redis,RedisContextStorage) class -for storing you users' contexts in Redis database. - -Chatsky uses [redis.asyncio](https://redis.readthedocs.io/en/latest/) -library for asynchronous access to Redis DB. -""" - -# %pip install chatsky[redis] - -# %% -import os - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -db_uri = "redis://{}:{}@localhost:6379/{}".format( - "", os.environ["REDIS_PASSWORD"], "0" -) -db = context_storage_factory(db_uri) - - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.5_mysql.py.txt b/_sources/tutorials/tutorials.context_storages.5_mysql.py.txt deleted file mode 100644 index 8c61248b8..000000000 --- a/_sources/tutorials/tutorials.context_storages.5_mysql.py.txt +++ /dev/null @@ -1,46 +0,0 @@ -# %% [markdown] -""" -# 5. MySQL - -This is a tutorial on using MySQL. - -See %mddoclink(api,context_storages.sql,SQLContextStorage) class -for storing you users' contexts in SQL databases. - -Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/) -and [asyncmy](https://github.com/long2ice/asyncmy) -libraries for asynchronous access to MySQL DB. -""" - -# %pip install chatsky[mysql] - -# %% -import os - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -db_uri = "mysql+asyncmy://{}:{}@localhost:3307/{}".format( - os.environ["MYSQL_USERNAME"], - os.environ["MYSQL_PASSWORD"], - os.environ["MYSQL_DATABASE"], -) -db = context_storage_factory(db_uri) - - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.6_sqlite.py.txt b/_sources/tutorials/tutorials.context_storages.6_sqlite.py.txt deleted file mode 100644 index 6ec4ee931..000000000 --- a/_sources/tutorials/tutorials.context_storages.6_sqlite.py.txt +++ /dev/null @@ -1,50 +0,0 @@ -# %% [markdown] -""" -# 6. SQLite - -This is a tutorial on using SQLite. - -See %mddoclink(api,context_storages.sql,SQLContextStorage) class -for storing you users' contexts in SQL databases. - -Chatsky uses [sqlalchemy](https://docs.sqlalchemy.org/en/20/) -and [aiosqlite](https://readthedocs.org/projects/aiosqlite/) -libraries for asynchronous access to SQLite DB. - -Note that protocol separator for windows differs from one for linux. -""" - -# %pip install chatsky[sqlite] - -# %% -import pathlib -from platform import system - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -pathlib.Path("dbs").mkdir(exist_ok=True) -db_file = pathlib.Path("dbs/sqlite.db") -db_file.touch(exist_ok=True) - -separator = "///" if system() == "Windows" else "////" -db_uri = f"sqlite+aiosqlite:{separator}{db_file.absolute()}" -db = context_storage_factory(db_uri) - - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.7_yandex_database.py.txt b/_sources/tutorials/tutorials.context_storages.7_yandex_database.py.txt deleted file mode 100644 index 19c3b4a72..000000000 --- a/_sources/tutorials/tutorials.context_storages.7_yandex_database.py.txt +++ /dev/null @@ -1,51 +0,0 @@ -# %% [markdown] -""" -# 7. Yandex DataBase - -This is a tutorial on how to use Yandex DataBase. - -See %mddoclink(api,context_storages.ydb,YDBContextStorage) class -for storing you users' contexts in Yandex database. - -Chatsky uses [ydb.aio](https://ydb.tech/en/docs/) -library for asynchronous access to Yandex DB. -""" - -# %pip install chatsky[ydb] - -# %% -import os - -from chatsky.context_storages import context_storage_factory - -from chatsky import Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - - -# %% -# ##### Connecting to yandex cloud -# https://github.com/zinal/ydb-python-sdk/blob/ex_basic-example_p1/examples/basic_example_v1/README.md -# export YDB_SERVICE_ACCOUNT_KEY_FILE_CREDENTIALS=$HOME/key-ydb-sa-0.json -# export YDB_ENDPOINT=grpcs://ydb.serverless.yandexcloud.net:2135 -# export YDB_DATABASE=/ru-central1/qwertyuiopasdfgh/123456789qwertyui -# ##### or use local-ydb with variables from .env_file -# db_uri="grpc://localhost:2136/local" - -db_uri = "{}{}".format( - os.environ["YDB_ENDPOINT"], - os.environ["YDB_DATABASE"], -) -db = context_storage_factory(db_uri) - -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, context_storage=db) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.context_storages.8_db_benchmarking.py.txt b/_sources/tutorials/tutorials.context_storages.8_db_benchmarking.py.txt deleted file mode 100644 index 16a8df959..000000000 --- a/_sources/tutorials/tutorials.context_storages.8_db_benchmarking.py.txt +++ /dev/null @@ -1,247 +0,0 @@ -# %% [markdown] -""" -# 8. Context storage benchmarking - -This tutorial shows how to benchmark context storages. - -For more info see [API reference](%doclink(api,utils.db_benchmark.benchmark)). -""" - -# %pip install chatsky[benchmark,postgresql,mongodb,redis,mysql,sqlite,ydb] - -# %% -from pathlib import Path -from platform import system -import tempfile - -import chatsky.utils.db_benchmark as benchmark - -# %% [markdown] -""" -## Context storage setup -""" - -# %% -# this cell is only required for shelve and sqlite databases -tutorial_dir = Path(tempfile.mkdtemp()) -db_path = tutorial_dir / "dbs" -db_path.mkdir() -sqlite_file = db_path / "sqlite.db" -sqlite_file.touch(exist_ok=True) -sqlite_separator = "///" if system() == "Windows" else "////" - -# %% -storages = { - "Shelve": f"shelve://{db_path}/shelve", - "PostgreSQL": "postgresql+asyncpg://postgres:pass@localhost:5432/test", - "MongoDB": "mongodb://admin:pass@localhost:27017/admin", - "Redis": "redis://:pass@localhost:6379/0", - "MySQL": "mysql+asyncmy://root:pass@localhost:3307/test", - "SQLite": f"sqlite+aiosqlite:{sqlite_separator}{sqlite_file.absolute()}", - "YDB": "grpc://localhost:2136/local", -} - -# %% [markdown] -""" -## Saving benchmark results to a file - -Benchmark results are saved to files. - -For that there exist two functions: -%mddoclink(api,utils.db_benchmark.benchmark,benchmark_all) -and -%mddoclink(api,utils.db_benchmark.benchmark,save_results_to_file). - -Note: context storages passed into these functions will be cleared. - -Once the benchmark results are saved to a file, -you can view and analyze them using two methods: - -* [Using the Report Function](#Using-the-report-function): This function - can display specified information from a given file. - By default, it prints the name and average metrics for each benchmark case. - -* [Using the Streamlit App](#Using-Streamlit-app): A Streamlit app - is available for viewing and comparing benchmark results. - You can upload benchmark result files using the app's "Benchmark sets" tab, - inspect individual results in the "View" tab, - and compare metrics in the "Compare" tab. - -Benchmark results are saved according to a specific schema, -which can be found in the benchmark schema documentation. -Each database being benchmarked will have its own result file. - -### Configuration - -The first one is a higher-level wrapper of the second one. -The first function accepts -%mddoclink(api,utils.db_benchmark.benchmark,BenchmarkCase) -which configures databases to be benchmarked and parameters of the benchmarks. -The second function accepts only a single URI for the database -and several benchmark configurations. So, the second function is simpler to use, -while the first function allows for more configuration -(e.g. having different databases benchmarked in a single file). - -Both function use -%mddoclink(api,utils.db_benchmark.benchmark,BenchmarkConfig) -to configure benchmark behaviour. -`BenchmarkConfig` is only an interface for benchmark configurations. -Its most basic implementation is -%mddoclink(api,utils.db_benchmark.basic_config,BasicBenchmarkConfig). - -Chatsky provides configuration presets in the -%mddoclink(api,utils.db_benchmark.basic_config) module, -covering various contexts, messages, and edge cases. -You can use these presets by passing them to the benchmark functions or create -your own configuration. - -To learn more about using presets see -[Configuration presets](#Configuration-presets) - -Benchmark configs have several parameters: - -Setting `context_num` to 50 means that we'll run fifty cycles of -writing and reading context. This way we'll be able to get a more accurate -average read/write time as well as check if read/write times are dependent -on the number of contexts in the storage. - -You can also configure the `dialog_len`, `message_dimensions` and -`misc_dimensions` parameters. This allows you to set the contexts -you want your database to be benchmarked with. - -### File structure - -The files are saved according to [the schema]( -../_misc/benchmark_schema.json -). -""" - -# %% -for db_name, db_uri in storages.items(): - benchmark.benchmark_all( - file=tutorial_dir / f"{db_name}.json", - name="Tutorial benchmark", - description="Benchmark for tutorial", - db_uri=db_uri, - benchmark_configs={ - "simple_config": benchmark.BasicBenchmarkConfig( - context_num=50, - from_dialog_len=1, - to_dialog_len=5, - message_dimensions=(3, 10), - misc_dimensions=(3, 10), - ), - }, - ) - -# %% [markdown] -""" -Running the cell above will create a file -with benchmark results for every benchmarked DB: -""" - -# %% -list(tutorial_dir.iterdir()) - -# %% [markdown] -""" -## Viewing benchmark results - -Now that the results are saved to a file you can either view them using the -%mddoclink(api,utils.db_benchmark.report,report) -function or [our streamlit app]( -../_misc/benchmark_streamlit.py -). -""" - -# %% [markdown] -""" -### Using the report function - -The report function will print specified information from a given file. - -By default it prints the name and average metrics for each case. -""" - -# %% -benchmark.report( - file=tutorial_dir / "Shelve.json", display={"name", "config", "metrics"} -) - -# %% [markdown] -""" -### Using Streamlit app - -To run the app, execute: - -``` -# download file -curl "https://deeppavlov.github.io/chatsky/"\ -"_misc/benchmark_streamlit.py" -o benchmark_streamlit.py -# install dependencies -pip install chatsky[benchmark] -# run -streamlit run benchmark_streamlit.py -``` - -You can upload files with benchmark results -using the first tab of the app ("Benchmark sets"): - -.. figure:: ../_static/images/benchmark_sets.png - -The second tab ("View") lets you inspect individual benchmark results. -It also allows you to add a specific benchmark result -to the "Compare" tab via the button in the top-right corner. - -.. figure:: ../_static/images/benchmark_view.png - -In the "Compare" tab you can view main metrics -(write, read, update, read+update) of previously added benchmark results: - -.. figure:: ../_static/images/benchmark_compare.png - -"Mass compare" tab saves you the trouble of manually adding -to compare every benchmark result from a single file. - -.. figure:: ../_static/images/benchmark_mass_compare.png -""" - -# %% [markdown] -""" -## Additional information - -### Configuration presets - -The -%mddoclink(api,utils.db_benchmark.basic_config) -module also includes a dictionary containing configuration presets. -Those cover various contexts and messages as well as some edge cases. -""" - -# %% -print(benchmark.basic_config.basic_configurations.keys()) - -# %% [markdown] -""" -To use configuration presets, simply pass them to benchmark functions: - - benchmark.benchmark_all( - ..., - benchmark_configs=benchmark.basic_configurations - ) -""" - -# %% [markdown] -""" -### Custom configuration - -If the basic configuration is not enough for you, you can create your own. - -To do so, inherit from the -%mddoclink(api,utils.db_benchmark.benchmark,BenchmarkConfig) -class. You need to define three methods: - -- `get_context` -- method to get initial contexts. -- `info` -- method for getting display info representing the configuration. -- `context_updater` -- method for updating contexts. -""" diff --git a/_sources/tutorials/tutorials.messengers.telegram.1_basic.py.txt b/_sources/tutorials/tutorials.messengers.telegram.1_basic.py.txt deleted file mode 100644 index 66dd055f1..000000000 --- a/_sources/tutorials/tutorials.messengers.telegram.1_basic.py.txt +++ /dev/null @@ -1,107 +0,0 @@ -# %% [markdown] -""" -# Telegram: 1. Basic - -The following tutorial shows how to run a regular Chatsky script in Telegram. -It asks users for the '/start' command and then loops in one place. - -Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) -class and [python-telegram-bot](https://docs.python-telegram-bot.org/) -library are used for accessing telegram API in polling mode. - -Telegram API token is required to access telegram API. -""" - -# %pip install chatsky[telegram] - -# %% -import os - -from chatsky import ( - RESPONSE, - TRANSITIONS, - Pipeline, - Transition as Tr, - conditions as cnd, - destinations as dst, -) -from chatsky.messengers.telegram import LongpollingInterface -from chatsky.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -In order to integrate your script with Telegram, you need an instance of the -%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface) class. -Two of its child subclasses that can be instantiated -are %mddoclink(api,messengers.telegram.interface,LongpollingInterface) and -%mddoclink(api,messengers.telegram.interface,WebhookInterface). -The latter requires a webserver, so here we use long polling interface. - -%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface) -encapsulates the bot logic. The only required -argument for a bot to run is a token. Some other parameters -(such as host, port, interval, etc.) can be passed as keyword arguments -(for their specs see [documentations of the child subclasses]( -%doclink(api,messengers.telegram.interface) -). - -Either of the two interfaces connect the bot to Telegram. -They can be passed directly to a Chatsky `Pipeline` instance. - - -
- -Note - -You can also import `LongpollingInterface` -under the alias of `TelegramInterface` from `chatsky.messengers`: - -```python -from chatsky.messengers import TelegramInterface -``` - -
-""" - - -# %% -script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: [ - Tr(dst="greeting_node", cnd=cnd.ExactMatch("/start")) - ], - }, - "greeting_node": { - RESPONSE: "Hi", - TRANSITIONS: [Tr(dst=dst.Current())], - }, - "fallback_node": { - RESPONSE: "Please, repeat the request", - TRANSITIONS: [ - Tr(dst="greeting_node", cnd=cnd.ExactMatch("/start")) - ], - }, - } -} - - -# %% -interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline( - script=script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), - messenger_interface=interface, - # The interface can be passed as a pipeline argument. -) - - -if __name__ == "__main__": - if is_interactive_mode(): - # prevent run during doc building - pipeline.run() diff --git a/_sources/tutorials/tutorials.messengers.telegram.2_attachments.py.txt b/_sources/tutorials/tutorials.messengers.telegram.2_attachments.py.txt deleted file mode 100644 index 68a135c70..000000000 --- a/_sources/tutorials/tutorials.messengers.telegram.2_attachments.py.txt +++ /dev/null @@ -1,278 +0,0 @@ -# %% [markdown] -""" -# Telegram: 2. Attachments - -The following tutorial shows how to send different attachments using -telegram interfaces. - -Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) -class and [python-telegram-bot](https://docs.python-telegram-bot.org/) -library are used for accessing telegram API in polling mode. - -Telegram API token is required to access telegram API. -""" - -# %pip install chatsky[telegram] - -# %% -import os - -from pydantic import HttpUrl - -from chatsky import ( - GLOBAL, - RESPONSE, - TRANSITIONS, - Message, - Pipeline, - Transition as Tr, - conditions as cnd, -) -from chatsky.messengers.telegram import LongpollingInterface -from chatsky.core.message import ( - Animation, - Audio, - Contact, - Document, - Location, - Image, - MediaGroup, - Poll, - PollOption, - Sticker, - Video, - VideoMessage, - VoiceMessage, -) -from chatsky.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -Example attachment data is specified below in form of dictionaries. -List of attachments that telegram messenger interface can send can -be found here: -%mddoclink(api,messengers.telegram.abstract,_AbstractTelegramInterface.supported_request_attachment_types). -""" - -# %% - -EXAMPLE_ATTACHMENT_SOURCE = ( - "https://github.com/deeppavlov/chatsky/wiki/example_attachments" -) - -location_data = {"latitude": 50.65, "longitude": 3.916667} - -contact_data = { - "phone_number": "8-900-555-35-35", - "first_name": "Hope", - "last_name": "Credit", -} - -sticker_data = { - "id": ( - "CAACAgIAAxkBAAErAAFXZibO5ksphCKS" - + "XSe1CYiw5588yqsAAkEAAzyKVxogmx2BPCogYDQE" - ), - "caption": "A sticker I've just found", -} - -audio_data = { - "source": HttpUrl( - f"{EXAMPLE_ATTACHMENT_SOURCE}/separation-william-king.mp3" - ), - "caption": "Separation melody by William King", - "filename": "separation-william-king.mp3", -} - -video_data = { - "source": HttpUrl( - f"{EXAMPLE_ATTACHMENT_SOURCE}/crownfall-lags-nkognit0.mp4" - ), - "caption": "Epic Dota2 gameplay by Nkognit0", - "filename": "crownfall-lags-nkognit0.mp4", -} - -animation_data = { - # For some reason, if we don't define filename explicitly, - # animation is sent as file. - "source": HttpUrl( - f"{EXAMPLE_ATTACHMENT_SOURCE}/hong-kong-simplyart4794.gif" - ), - "caption": "Hong Kong skyscraper views by Simplyart4794", - "filename": "hong-kong-simplyart4794.gif", -} - -image_data = { - "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png"), - "caption": "DeepPavlov logo", - "filename": "deeppavlov.png", -} - -document_data = { - "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), - "caption": "DeepPavlov article", - "filename": "deeppavlov-article.pdf", -} - -ATTACHMENTS = [ - "location", - "contact", - "poll", - "sticker", - "audio", - "video", - "animation", - "image", - "document", - "voice_message", - "video_message", - "media_group", -] - -QUOTED_ATTACHMENTS = [f'"{attachment}"' for attachment in ATTACHMENTS] - - -# %% [markdown] -""" -The bot below sends different attachments on request. - -[Here](%doclink(api,core.message)) you can find -all the attachment options available. -""" - -# %% -script = { - GLOBAL: { - TRANSITIONS: [ - Tr( - dst=("main_flow", f"{attachment}_node"), - cnd=cnd.ExactMatch(attachment), - ) - for attachment in ATTACHMENTS - ] - }, - "main_flow": { - "start_node": { - TRANSITIONS: [Tr(dst="intro_node", cnd=cnd.ExactMatch("/start"))], - }, - "intro_node": { - RESPONSE: f'Type {", ".join(QUOTED_ATTACHMENTS[:-1])}' - f" or {QUOTED_ATTACHMENTS[-1]}" - f" to receive a corresponding attachment!", - }, - "location_node": { - RESPONSE: Message( - text="Here's your location!", - attachments=[Location(**location_data)], - ), - }, - "contact_node": { - RESPONSE: Message( - text="Here's your contact!", - attachments=[Contact(**contact_data)], - ), - }, - "poll_node": { - RESPONSE: Message( - text="Here's your poll!", - attachments=[ - Poll( - question="What is the poll question?", - options=[ - PollOption(text="This one!"), - PollOption(text="Not this one :("), - ], - ), - ], - ), - }, - "sticker_node": { - RESPONSE: Message( - text="Here's your sticker!", - attachments=[Sticker(**sticker_data)], - ), - }, - "audio_node": { - RESPONSE: Message( - text="Here's your audio!", - attachments=[Audio(**audio_data)], - ), - }, - "video_node": { - RESPONSE: Message( - text="Here's your video!", - attachments=[Video(**video_data)], - ), - }, - "animation_node": { - RESPONSE: Message( - text="Here's your animation!", - attachments=[Animation(**animation_data)], - ), - }, - "image_node": { - RESPONSE: Message( - text="Here's your image!", - attachments=[Image(**image_data)], - ), - }, - "document_node": { - RESPONSE: Message( - text="Here's your document!", - attachments=[Document(**document_data)], - ), - }, - "voice_message_node": { - RESPONSE: Message( - text="Here's your voice message!", - attachments=[VoiceMessage(source=audio_data["source"])], - ), - }, - "video_message_node": { - RESPONSE: Message( - text="Here's your video message!", - attachments=[VideoMessage(source=video_data["source"])], - ), - }, - "media_group_node": { - RESPONSE: Message( - text="Here's your media group!", - attachments=[ - MediaGroup( - group=[ - Image(**image_data), - Video(**video_data), - ], - ) - ], - ), - }, - "fallback_node": { - RESPONSE: f"Unknown attachment type, try again! " - f"Supported attachments are: " - f'{", ".join(QUOTED_ATTACHMENTS[:-1])} ' - f"and {QUOTED_ATTACHMENTS[-1]}.", - }, - }, -} - - -# %% -interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline( - script=script, - start_label=("main_flow", "start_node"), - fallback_label=("main_flow", "fallback_node"), - messenger_interface=interface, - # The interface can be passed as a pipeline argument. -) - - -if __name__ == "__main__": - if is_interactive_mode(): - # prevent run during doc building - pipeline.run() diff --git a/_sources/tutorials/tutorials.messengers.telegram.3_advanced.py.txt b/_sources/tutorials/tutorials.messengers.telegram.3_advanced.py.txt deleted file mode 100644 index 38bcd13cc..000000000 --- a/_sources/tutorials/tutorials.messengers.telegram.3_advanced.py.txt +++ /dev/null @@ -1,267 +0,0 @@ -# %% [markdown] -""" -# Telegram: 3. Advanced - -The following tutorial shows several advanced cases of user-to-bot interaction. - -Here, %mddoclink(api,messengers.telegram.interface,LongpollingInterface) -class and [python-telegram-bot](https://docs.python-telegram-bot.org/) -library are used for accessing telegram API in polling mode. - -Telegram API token is required to access telegram API. -""" - -# %pip install chatsky[telegram] - -# %% -import os -from hashlib import sha256 -from urllib.request import urlopen - -from pydantic import HttpUrl -from telegram import InlineKeyboardButton, InlineKeyboardMarkup -from telegram.constants import ParseMode - -from chatsky import ( - RESPONSE, - TRANSITIONS, - LOCAL, - Message, - Pipeline, - BaseResponse, - Context, - Transition as Tr, - conditions as cnd, -) -from chatsky.messengers.telegram import LongpollingInterface -from chatsky.core.message import ( - DataAttachment, - Document, - Image, - Location, - Sticker, - MessageInitTypes, -) -from chatsky.utils.testing.common import is_interactive_mode - - -# %% [markdown] -""" -This bot shows different special telegram messenger interface use cases, -such as: - -1. Interactive keyboard with buttons. -2. Text formatted with Markdown V2. -3. Multiple attachments of different kind handling. -4. Image with a spoiler. -5. Document with a thumbnail. -6. Attachment bytes hash. - -Last option ("Raw attachments!") button might be especially interesting, -because it shows how bot percepts different telegram attachments sent by user -in terms and datastructures of Chatsky. - -
- -Tip - -Check out -[this](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot) -class for information about different arguments -for sending attachments, `send_...` methods. - -For example, documentation for `Image` extra fields can be found in the -[send_photo](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot.send_photo) -method. - -The `Message` class also supports extra keywords as described in -[send_message](https://docs.python-telegram-bot.org/en/v21.3/telegram.bot.html#telegram.Bot.send_message). - -
-""" - -# %% - -EXAMPLE_ATTACHMENT_SOURCE = ( - "https://github.com/deeppavlov/chatsky/wiki/example_attachments" -) - -image_url = HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov.png") - -formatted_text = """ -Visit [this link](https://core.telegram.org/bots/api#formatting-options) -for more information about formatting options in telegram\. -""" # noqa: W605 - -location_data = {"latitude": 59.9386, "longitude": 30.3141} - -sticker_data = { - "id": ( - "CAACAgIAAxkBAAErBZ1mKAbZvEOmhscojaIL5q0u8v" - + "gp1wACRygAAiSjCUtLa7RHZy76ezQE" - ), -} - -image_data = { - "source": image_url, - "caption": "DeepPavlov logo", - "has_spoiler": True, - "filename": "deeppavlov_logo.png", -} -# last 3 fields are extra keywords passed directly to the -# telegram.Bot.send_photo method - -document_data = { - "source": HttpUrl(f"{EXAMPLE_ATTACHMENT_SOURCE}/deeppavlov-article.pdf"), - "caption": "DeepPavlov article", - "filename": "deeppavlov_article.pdf", - "thumbnail": urlopen(str(image_url)).read(), -} - - -# %% -class DataAttachmentHash(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - attachment = [ - a - for a in ctx.last_request.attachments - if isinstance(a, DataAttachment) - ] - if len(attachment) > 0: - attachment_bytes = await attachment[0].get_bytes( - ctx.pipeline.messenger_interface - ) - attachment_hash = sha256(attachment_bytes).hexdigest() - response = ( - "Here's your previous request first attachment sha256 hash:\n" - f"```\n{attachment_hash}\n```" - ) - return Message(text=response, parse_mode=ParseMode.MARKDOWN_V2) - else: - return "Last request did not contain any data attachment!" - - -# %% -script = { - "main_flow": { - LOCAL: { - TRANSITIONS: [ - Tr(dst="main_node", cnd=cnd.ExactMatch("/start")), - Tr(dst="formatted_node", cnd=cnd.HasCallbackQuery("formatted")), - Tr( - dst="attachments_node", - cnd=cnd.HasCallbackQuery("attachments"), - ), - Tr(dst="secret_node", cnd=cnd.HasCallbackQuery("secret")), - Tr(dst="thumbnail_node", cnd=cnd.HasCallbackQuery("thumbnail")), - Tr(dst="hash_init_node", cnd=cnd.HasCallbackQuery("hash")), - Tr(dst="main_node", cnd=cnd.HasCallbackQuery("restart")), - ] - }, - "start_node": {}, - "main_node": { - RESPONSE: Message( - text="Welcome! Choose what you want to receive.", - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - "Cute formatted text!", - callback_data="formatted", - ), - ], - [ - InlineKeyboardButton( - "Multiple attachments!", - callback_data="attachments", - ), - InlineKeyboardButton( - "Secret image!", callback_data="secret" - ), - InlineKeyboardButton( - "Document with thumbnail!", - callback_data="thumbnail", - ), - ], - [ - InlineKeyboardButton( - "First attachment bytes hash!", - callback_data="hash", - ), - ], - [ - InlineKeyboardButton( - "Restart!", callback_data="restart" - ), - ], - ], - ), - # you can add extra fields to the message itself; - # they are passed to telegram.Bot.send_message - ), - }, - "formatted_node": { - RESPONSE: Message( - text=formatted_text, parse_mode=ParseMode.MARKDOWN_V2 - ), - }, - "attachments_node": { - RESPONSE: Message( - text="Here's your message with multiple attachments " - + "(a location and a sticker)!", - attachments=[ - Location(**location_data), - Sticker(**sticker_data), - ], - ), - }, - "secret_node": { - RESPONSE: Message( - text="Here's your secret image!", - attachments=[Image(**image_data)], - ), - }, - "thumbnail_node": { - RESPONSE: Message( - text="Here's your document with thumbnail!", - attachments=[Document(**document_data)], - ), - }, - "hash_init_node": { - RESPONSE: Message( - text="Alright! Now send me a message with data attachment " - + "(audio, video, animation, image, sticker or document)!" - ), - TRANSITIONS: [Tr(dst="hash_request_node")], - }, - "hash_request_node": { - RESPONSE: DataAttachmentHash(), - }, - "fallback_node": { - RESPONSE: Message( - text="Bot has entered unrecoverable state:" - + "\nRun /start command again to restart." - ), - }, - }, -} - - -# %% -interface = LongpollingInterface(token=os.environ["TG_BOT_TOKEN"]) - - -# %% -pipeline = Pipeline( - script=script, - start_label=("main_flow", "start_node"), - fallback_label=("main_flow", "fallback_node"), - messenger_interface=interface, - # The interface can be passed as a pipeline argument. -) - - -if __name__ == "__main__": - if is_interactive_mode(): - # prevent run during doc building - pipeline.run() diff --git a/_sources/tutorials/tutorials.messengers.web_api_interface.1_fastapi.py.txt b/_sources/tutorials/tutorials.messengers.web_api_interface.1_fastapi.py.txt deleted file mode 100644 index 208685123..000000000 --- a/_sources/tutorials/tutorials.messengers.web_api_interface.1_fastapi.py.txt +++ /dev/null @@ -1,109 +0,0 @@ -# %% [markdown] -""" -# Web API: 1. FastAPI - -This tutorial shows how to create an API for Chatsky using FastAPI and -introduces messenger interfaces. - -You can see the result at http://127.0.0.1:8000/docs. - -Here, %mddoclink(api,messengers.common.interface,CallbackMessengerInterface) -is used to process requests. - -%mddoclink(api,core.message,Message) -is used in creating a JSON Schema for the endpoint. -""" -# %pip install chatsky uvicorn fastapi - -# %% -from chatsky.messengers.common.interface import CallbackMessengerInterface -from chatsky import Message, Pipeline -from chatsky.utils.testing import TOY_SCRIPT_KWARGS, is_interactive_mode - -import uvicorn -from fastapi import FastAPI - -# %% [markdown] -""" -Messenger interfaces establish communication between users and the pipeline. -They manage message channel initialization and termination -as well as pipeline execution on every user request. -There are two built-in messenger interface types that can be extended -through inheritance: - -* `PollingMessengerInterface` - Starts polling for user requests - in a loop upon initialization, - it has following methods: - - * `_request()` - Method that is used to retrieve user requests - from a messenger, should return list of tuples: - (user request, unique dialog id). - * `_respond(responses)` - Method that sends responses generated by pipeline - to users through a messenger, - accepts list of dialog `Contexts`. - * `_on_exception(e)` - Method that is called when a critical exception - occurs. I.e. exception from context storage or messenger interface, - not a service exception. - - Such exceptions lead to the termination of the loop. - * `connect(pipeline_runner, loop, timeout)` - - Method that starts the polling loop. - - This method is called inside `pipeline.run` method. - - It accepts 3 arguments: - - * a callback that runs pipeline, - * a function that should return True to continue polling, - * and time to wait between loop executions. - -* `CallbackMessengerInterface` - Creates message channel - and provides a callback for pipeline execution, - it has following methods: - - * `on_request(request, ctx_id)` or `on_request_async(request, ctx_id)` - - Method that should be called each time - user provides new input to pipeline, - returns dialog Context. - * `connect(pipeline_runner)` - Method that sets `pipeline_runner` as - a function to be called inside `on_request`. - - This method is called inside `pipeline.run` method. - -You can find API reference for these classes -[here](%doclink(api,messengers.common.interface)). - -Here the default `CallbackMessengerInterface` is used to setup -communication between the pipeline on the server side and the messenger client. -""" - -# %% -messenger_interface = CallbackMessengerInterface() -# CallbackMessengerInterface instantiating the dedicated messenger interface -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, messenger_interface=messenger_interface -) - - -# %% -app = FastAPI() - - -@app.post("/chat", response_model=Message) -async def respond( - user_id: str, - user_message: Message, -): - context = await messenger_interface.on_request_async(user_message, user_id) - return context.last_response - - -# %% -if __name__ == "__main__": - if is_interactive_mode(): # do not run this during doc building - pipeline.run() # runs the messenger_interface.connect method - uvicorn.run( - app, - host="127.0.0.1", - port=8000, - ) diff --git a/_sources/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.py.txt b/_sources/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.py.txt deleted file mode 100644 index ae5ff5440..000000000 --- a/_sources/tutorials/tutorials.messengers.web_api_interface.2_websocket_chat.py.txt +++ /dev/null @@ -1,116 +0,0 @@ -# %% [markdown] -""" -# Web API: 2. WebSocket Chat - -This tutorial shows how to create a Web chat on FastAPI using websockets. - -You can see the result at http://127.0.0.1:8000/. - -This tutorial is a modified version of the FastAPI tutorial on WebSockets: -https://fastapi.tiangolo.com/advanced/websockets/. - -As mentioned in that tutorial, - -> ... for this example, we'll use a very simple HTML document -> with some JavaScript, all inside a long string. -> This, of course, is not optimal and you wouldn't use it for production. - -Here, %mddoclink(api,messengers.common.interface,CallbackMessengerInterface) -is used to process requests. - -%mddoclink(api,core.message,Message) is used to represent text messages. -""" - -# %pip install chatsky uvicorn fastapi - -# %% -from chatsky.messengers.common.interface import CallbackMessengerInterface -from chatsky import Message, Pipeline -from chatsky.utils.testing import TOY_SCRIPT_KWARGS, is_interactive_mode - -import uvicorn -from fastapi import FastAPI, WebSocket, WebSocketDisconnect -from fastapi.responses import HTMLResponse - - -# %% -messenger_interface = CallbackMessengerInterface() -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, messenger_interface=messenger_interface -) - - -# %% -app = FastAPI() -PORT = 8000 - -html = f""" - - - - Chat - - -

WebSocket Chat

-
- - -
-
    -
- - - -""" - - -@app.get("/") -async def get(): - return HTMLResponse(html) - - -@app.websocket("/ws/{client_id}") -async def websocket_endpoint(websocket: WebSocket, client_id: int): - await websocket.accept() - try: - while True: - data = await websocket.receive_text() - await websocket.send_text(f"User: {data}") - request = Message(data) - context = await messenger_interface.on_request_async( - request, client_id - ) - response = context.last_response.text - if response is not None: - await websocket.send_text(f"Bot: {response}") - else: - await websocket.send_text("Bot did not return text.") - except WebSocketDisconnect: # ignore disconnections - pass - - -# %% -if __name__ == "__main__": - if is_interactive_mode(): # do not run this during doc building - pipeline.run() - uvicorn.run( - app, - host="127.0.0.1", - port=PORT, - ) diff --git a/_sources/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.py.txt b/_sources/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.py.txt deleted file mode 100644 index c5e9a6bfa..000000000 --- a/_sources/tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.py.txt +++ /dev/null @@ -1,156 +0,0 @@ -# %% [markdown] -""" -# Web API: 3. Load testing with Locust - -This tutorial shows how to use an API endpoint -created in the FastAPI tutorial in load testing. -""" - -# %pip install chatsky locust - -# %% [markdown] -""" -## Running Locust - -1. Run this file directly: - ```bash - python {file_name} - ``` -2. Run locust targeting this file: - ```bash - locust -f {file_name} - ``` -3. Run from python: - ```python - import sys - from locust import main - - sys.argv = ["locust", "-f", {file_name}] - main.main() - ``` - -You should see the result at http://127.0.0.1:8089. - -Make sure that your POST endpoint is also running (run the FastAPI tutorial). - -If using the FastAPI tutorial, set "Host" to `http://127.0.0.1:8000`, -when prompted by Locust. -""" - - -# %% -################################################################################ -# this patch is only needed to run this file in IPython kernel -# and can be safely removed -import gevent.monkey - -gevent.monkey.patch_all() -################################################################################ - - -# %% -import uuid -import time -import sys - -from locust import FastHttpUser, task, constant, main - -from chatsky import Message -from chatsky.utils.testing import HAPPY_PATH, is_interactive_mode - - -# %% -class ChatskyUser(FastHttpUser): - wait_time = constant(1) - - def check_happy_path(self, happy_path): - """ - Check a happy path. - For each `(request, response)` pair in `happy_path`: - 1. Send request to the API endpoint and catch its response. - 2. Compare API response with the `response`. - If they do not match, fail the request. - - :param happy_path: - An iterable of tuples of - `(Message, Message | Callable(Message->str|None) | None)`. - - If the second element is `Message`, - check that API response matches it. - - If the second element is `None`, - do not check the API response. - - If the second element is a `Callable`, - call it with the API response as its argument. - If the function returns a string, - that string is considered an error message. - If the function returns `None`, - the API response is considered correct. - """ - user_id = str(uuid.uuid4()) - - for request, response in happy_path: - request = Message.model_validate(request) - with self.client.post( - f"/chat?user_id={user_id}", - headers={ - "accept": "application/json", - "Content-Type": "application/json", - }, - # Name is the displayed name of the request. - name=f"/chat?user_message={request.model_dump_json()}", - data=request.model_dump_json(), - catch_response=True, - ) as candidate_response: - candidate_response.raise_for_status() - text_response = Message.model_validate( - candidate_response.json() - ) - - if response is not None: - if callable(response): - error_message = response(text_response) - if error_message is not None: - candidate_response.failure(error_message) - elif text_response != Message.model_validate(response): - candidate_response.failure( - f"Expected: {response.model_dump_json()}\n" - f"Got: {text_response.model_dump_json()}" - ) - - time.sleep(self.wait_time()) - - @task(3) # <- this task is 3 times more likely than the other - def dialog_1(self): - self.check_happy_path(HAPPY_PATH) - - @task - def dialog_2(self): - def check_first_message(msg: Message) -> str | None: - if msg.text is None: - return f"Message does not contain text: {msg.model_dump_json()}" - if "Hi" not in msg.text: - return ( - f'"Hi" is not in the response message: ' - f"{msg.model_dump_json()}" - ) - return None - - self.check_happy_path( - [ - # a function can be used to check the return message - ("Hi", check_first_message), - # a None is used if return message should not be checked - ("i'm fine, how are you?", None), - # this should fail - ("Hi", check_first_message), - ] - ) - - -# %% -if __name__ == "__main__": - if is_interactive_mode(): - sys.argv = ["locust", "-f", __file__] - main.main() diff --git a/_sources/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.py.txt b/_sources/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.py.txt deleted file mode 100644 index 280160b6c..000000000 --- a/_sources/tutorials/tutorials.messengers.web_api_interface.4_streamlit_chat.py.txt +++ /dev/null @@ -1,193 +0,0 @@ -# %% [markdown] -# # Web API: 4. Streamlit chat interface -# -# This tutorial shows how to use an API endpoint created in the FastAPI tutorial -# in a Streamlit chat. -# -# A demonstration of the chat: -# ![demo](https://user-images.githubusercontent.com/61429541/238721597-ef88261d-e9e6-497d-ba68-0bcc9a765808.png) - -# %pip install chatsky streamlit streamlit-chat - -# %% [markdown] -# ## Running Streamlit: -# -# ```bash -# streamlit run {file_name} -# ``` - - -# %% [markdown] -# ## Module and package import - - -# %% -########################################################### -# This patch is only needed to import Message from chatsky. -# Streamlit Chat interface can be written without using it. -import asyncio - -loop = asyncio.new_event_loop() -asyncio.set_event_loop(loop) -########################################################### - - -# %% -import uuid -import itertools - -import requests -import streamlit as st -from streamlit_chat import message -import streamlit.components.v1 as components -from chatsky import Message - - -# %% [markdown] -# ## API configuration -# -# Here we define methods to contact the API endpoint. - - -# %% -API_URL = "http://localhost:8000/chat" - - -def query(payload, user_id) -> requests.Response: - response = requests.post( - API_URL + f"?user_id={user_id}", - headers={ - "accept": "application/json", - "Content-Type": "application/json", - }, - json=payload, - ) - return response - - -# %% [markdown] -# ## Streamlit configuration -# -# Here we configure Streamlit page and initialize some session variables: -# -# 1. `user_id` -- stores user_id to be used in pipeline. -# 2. `bot_responses` -- a list of bot responses. -# 3. `user_requests` -- a list of user requests. - - -# %% -st.set_page_config(page_title="Streamlit Chatsky Chat", page_icon=":robot:") - -st.header("Streamlit Chatsky Chat") - -if "user_id" not in st.session_state: - st.session_state["user_id"] = str(uuid.uuid4()) - -if "bot_responses" not in st.session_state: - st.session_state["bot_responses"] = [] - -if "user_requests" not in st.session_state: - st.session_state["user_requests"] = [] - - -# %% [markdown] -# ## UI setup -# -# Here we configure elements that will be used -# in Streamlit to interact with the API. -# -# First we define a text input field which -# a user is supposed to type his requests into. -# Then we define a button that sends a query -# to the API, logs requests and responses, -# and clears the text field. - - -# %% -def send_and_receive(): - """ - Send text inside the input field. Receive response from API endpoint. - - Add both the request and response to `user_requests` and `bot_responses`. - - We do not call this function inside the `text_input.on_change` because then - we'd call it whenever the text field loses focus - (e.g. when a browser tab is switched). - """ - user_request = st.session_state["input"] - - if user_request == "": - return - - st.session_state["user_requests"].append(user_request) - - bot_response = query( - Message(user_request).model_dump(), - user_id=st.session_state["user_id"], - ) - bot_response.raise_for_status() - - bot_message = Message.model_validate(bot_response.json()).text - - # # Implementation without using Message: - # bot_response = query( - # {"text": user_request}, - # user_id=st.session_state["user_id"] - # ) - # bot_response.raise_for_status() - # - # bot_message = bot_response.json()["response"]["text"] - - st.session_state["bot_responses"].append(bot_message) - - st.session_state["input"] = "" - - -# %% -st.text_input("You: ", key="input") -st.button("Send", on_click=send_and_receive) - - -# %% [markdown] -# ### Component patch -# -# Here we add a component that presses the -# `Send` button whenever user presses the `Enter` key. - - -# %% -components.html( - """ - -""", - height=0, - width=0, -) - - -# %% [markdown] -# ### Message display -# -# Here we use the `streamlit-chat` package to -# display user requests and bot responses. - - -# %% -for i, bot_response, user_request in zip( - itertools.count(0), - st.session_state.get("bot_responses", []), - st.session_state.get("user_requests", []), -): - message(user_request, key=f"{i}_user", is_user=True) - message(bot_response, key=f"{i}_bot") diff --git a/_sources/tutorials/tutorials.script.core.1_basics.py.txt b/_sources/tutorials/tutorials.script.core.1_basics.py.txt deleted file mode 100644 index 74c440985..000000000 --- a/_sources/tutorials/tutorials.script.core.1_basics.py.txt +++ /dev/null @@ -1,177 +0,0 @@ -# %% [markdown] -""" -# Core: 1. Basics - -This notebook shows a basic example of creating a simple dialog bot (agent). - -Here, basic usage of %mddoclink(api,core.pipeline,Pipeline) is shown. - -Additionally, function %mddoclink(api,utils.testing.common,check_happy_path) -that can be used for Pipeline testing is presented. - -Let's do all the necessary imports from Chatsky: -""" - -# %pip install chatsky - -# %% -from chatsky import ( - TRANSITIONS, - RESPONSE, - Pipeline, - Transition as Tr, - conditions as cnd, - # all the aliases used in tutorials are available for direct import - # e.g. you can do `from chatsky import Tr` instead -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% [markdown] -""" -First of all, to create a dialog agent, we need to create a dialog script. -Below script means a dialog script. - -A script is a dictionary, where the keys are the names of the flows. -A script can contain multiple scripts, which is needed in order to divide -a dialog into sub-dialogs and process them separately. - -For example, the separation can be tied to the topic of the dialog. -In this tutorial there is one flow called `greeting_flow`. - -Flow describes a sub-dialog using linked nodes. -Each node has the keywords `RESPONSE` and `TRANSITIONS`. - -* `RESPONSE` contains the response - that the agent will return from the current node. -* `TRANSITIONS` is a list of %mddoclink(api,core.transition,Transition)s - that describes possible transitions from the current node as well as their - conditions and priorities. -""" - - -# %% -toy_script = { - "greeting_flow": { - "start_node": { # This is the initial node, - # it doesn't contain a `RESPONSE`. - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - # This transition means that the next node would be "node1" - # if user's message is "Hi" - }, - "node1": { - RESPONSE: "Hi, how are you?", - # When the bot enters node1, - # return "Hi, how are you?". - TRANSITIONS: [ - Tr(dst="node2", cnd=cnd.ExactMatch("I'm fine, how are you?")) - ], - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr(dst="node3", cnd=cnd.ExactMatch("Let's talk about music.")) - ], - }, - "node3": { - RESPONSE: "Sorry, I can not talk about music now.", - TRANSITIONS: [Tr(dst="node4", cnd=cnd.ExactMatch("Ok, goodbye."))], - }, - "node4": { - RESPONSE: "Bye", - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - "fallback_node": { - # We get to this node if the conditions - # for switching to other nodes are not performed. - RESPONSE: "Ooops", - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - } -} - - -happy_path = ( - ( - "Hi", - "Hi, how are you?", - ), # start_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "Bye"), # node3 -> node4 - ("Hi", "Hi, how are you?"), # node4 -> node1 - ("stop", "Ooops"), # node1 -> fallback_node - ( - "stop", - "Ooops", - ), # fallback_node -> fallback_node - ( - "Hi", - "Hi, how are you?", - ), # fallback_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "Bye"), # node3 -> node4 -) - - -# %% [markdown] -""" -`Pipeline` is an object that processes user -inputs and produces responses. - -To create the pipeline you need to pass the script (`script`), -initial node (`start_label`) and -the node to which the default transition will take place -if none of the current conditions are met (`fallback_label`). - -If `fallback_label` is not set, it defaults to `start_label`. - -Roughly, the process is as follows: - -1. Pipeline receives a user request. -2. The next node is determined with the help of `TRANSITIONS`. -3. Response of the chosen node is sent to the user. - -For a more detailed description, see [here]( -%doclink(api,core.pipeline,Pipeline._run_pipeline) -). -""" - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path( - pipeline, - happy_path, - printout=True, - ) # This is a function for automatic tutorial - # running (testing tutorial) with `happy_path`. - - if is_interactive_mode(): - pipeline.run() - # this method runs the pipeline with the preconfigured interface - # which is CLI by default: it allows chatting with the bot - # via command line diff --git a/_sources/tutorials/tutorials.script.core.2_conditions.py.txt b/_sources/tutorials/tutorials.script.core.2_conditions.py.txt deleted file mode 100644 index 3e27db673..000000000 --- a/_sources/tutorials/tutorials.script.core.2_conditions.py.txt +++ /dev/null @@ -1,221 +0,0 @@ -# %% [markdown] -""" -# Core: 2. Conditions - -This tutorial shows different options for -setting transition conditions from one node to another. - -Here, [conditions](%doclink(api,conditions.standard)) -for script transitions are shown. -""" - -# %pip install chatsky - -# %% -import re - -from chatsky import ( - Context, - TRANSITIONS, - RESPONSE, - Message, - Pipeline, - BaseCondition, - Transition as Tr, - conditions as cnd, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - -# %% [markdown] -""" -The transition condition is determined by -%mddoclink(api,core.script_function,BaseCondition). - -If this function returns `True`, -then the corresponding transition is considered possible. - -Condition functions have signature - - class MyCondition(BaseCondition): - async def call(self, ctx: Context) -> bool: - -This script covers the following pre-defined conditions: - -- `ExactMatch` returns `True` if the user's request completely - matches the value passed to the function. -- `Regexp` returns `True` if the pattern matches the user's request. - `Regexp` has same signature as `re.compile` function. -- `Any` returns `True` if one element of input sequence of conditions is `True`. -- `All` returns `True` if All elements of input - sequence of conditions are `True`. - -For a full list of available conditions see -[here](%doclink(api,conditions.standard)). - -The `cnd` field of `Transition` may also be a constant bool value. -""" - - -# %% -class HiLowerCase(BaseCondition): - """ - Return True if `hi` in both uppercase and lowercase - letters is contained in the user request. - """ - - async def call(self, ctx: Context) -> bool: - request = ctx.last_request - return "hi" in request.text.lower() - - -# %% [markdown] -""" -Conditions are subclasses of `pydantic.BaseModel`. - -You can define custom fields to make them more customizable: -""" - - -# %% -class ComplexUserAnswer(BaseCondition): - """ - Checks if the misc field of the last message is of a certain value. - - Messages are more complex than just strings. - The misc field can be used to store metadata about the message. - More on that in the next tutorial. - """ - - value: dict - - async def call(self, ctx: Context) -> bool: - request = ctx.last_request - return request.misc == self.value - - -customized_condition = ComplexUserAnswer(value={"some_key": "some_value"}) - - -# %% -toy_script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - # If "Hi" == request of user then we make the transition - }, - "node1": { - RESPONSE: "Hi, how are you?", - TRANSITIONS: [ - Tr( - dst="node2", - cnd=cnd.Regexp(r".*how are you", flags=re.IGNORECASE), - ) - ], - # pattern matching - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr( - dst="node3", - cnd=cnd.All( - cnd.Regexp(r"talk"), cnd.Regexp(r"about.*music") - ), - ) - ], - # Combine sequences of conditions with `cnd.All` - }, - "node3": { - RESPONSE: "Sorry, I can not talk about music now.", - TRANSITIONS: [ - Tr(dst="node4", cnd=cnd.Regexp(re.compile(r"Ok, goodbye."))) - ], - }, - "node4": { - RESPONSE: "bye", - TRANSITIONS: [ - Tr( - dst="node1", - cnd=cnd.Any( - HiLowerCase(), - cnd.ExactMatch("hello"), - ), - ) - ], - # Combine sequences of conditions with `cnd.Any` - }, - "fallback_node": { # We get to this node - # if no suitable transition was found - RESPONSE: "Ooops", - TRANSITIONS: [ - Tr(dst="node1", cnd=customized_condition), - # use a previously instantiated condition here - Tr(dst="start_node", cnd=False), - # This transition will never be made - Tr(dst="fallback_node"), - # `True` is the default value of `cnd` - # this transition will always be valid - ], - }, - } -} - -# testing -happy_path = ( - ( - "Hi", - "Hi, how are you?", - ), # start_node -> node1 - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "bye"), # node3 -> node4 - ("Hi", "Hi, how are you?"), # node4 -> node1 - ("stop", "Ooops"), # node1 -> fallback_node - ( - "one", - "Ooops", - ), # fallback_node -> fallback_node - ( - "help", - "Ooops", - ), # fallback_node -> fallback_node - ( - "nope", - "Ooops", - ), # fallback_node -> fallback_node - ( - Message(misc={"some_key": "some_value"}), - "Hi, how are you?", - ), # fallback_node -> node1 - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "bye"), # node3 -> node4 -) - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.3_responses.py.txt b/_sources/tutorials/tutorials.script.core.3_responses.py.txt deleted file mode 100644 index 8c9dd0d7b..000000000 --- a/_sources/tutorials/tutorials.script.core.3_responses.py.txt +++ /dev/null @@ -1,251 +0,0 @@ -# %% [markdown] -""" -# Core: 3. Responses - -This tutorial shows different options for setting responses. - -Here, [responses](%doclink(api,responses.standard)) -that allow giving custom answers to users are shown. -""" - -# %pip install chatsky - -# %% -import re -import random -from typing import Union - -from chatsky import ( - TRANSITIONS, - RESPONSE, - Context, - Message, - Pipeline, - Transition as Tr, - conditions as cnd, - responses as rsp, - destinations as dst, - BaseResponse, - MessageInitTypes, - AnyResponse, - AbsoluteNodeLabel, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% [markdown] -""" -Response of a node is determined by -%mddoclink(api,core.script_function,BaseResponse). - -Response can be constant in which case it is an instance -of %mddoclink(api,core.message,Message). - -`Message` has an option to be instantiated from a string -which is what we've been using so far. -Under the hood `RESPONSE: "text"` is converted into -`RESPONSE: Message(text="text")`. -This class should be used over simple strings when -some additional information needs to be sent such as images/metadata. - -More information on that can be found in the [media tutorial]( -%doclink(tutorial,script.responses.1_media) -). - -Instances of this class are returned by -%mddoclink(api,core.context,Context.last_request) and -%mddoclink(api,core.context,Context.last_response). -In the previous tutorial we showed how to access fields of messages -to build custom conditions. - -Node `RESPONSE` can also be set to a custom function. -This is demonstrated below: -""" - - -# %% -class CannotTalkAboutTopic(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - request = ctx.last_request - if request.text is None: - topic = None - else: - topic_pattern = re.compile(r"(.*talk about )(.*)\.") - topic = topic_pattern.findall(request.text) - topic = topic and topic[0] and topic[0][-1] - if topic: - return f"Sorry, I can not talk about {topic} now." - else: - return "Sorry, I can not talk about that now." - - -class UpperCase(BaseResponse): - response: AnyResponse # either const response or another BaseResponse - - def __init__(self, response: Union[MessageInitTypes, BaseResponse]): - # defining this allows passing response as a positional argument - # and allows to make a more detailed type annotation: - # AnyResponse cannot be a string but can be initialized from it, - # so MessageInitTypes annotates that we can init from a string - super().__init__(response=response) - - async def call(self, ctx: Context) -> MessageInitTypes: - response = await self.response(ctx) - # const response is converted to BaseResponse, - # so we call it regardless of the response type - - if response.text is not None: - response.text = response.text.upper() - return response - - -class FallbackTrace(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - return Message( - misc={ - "previous_node": await dst.Previous()(ctx), - "last_request": ctx.last_request, - } - ) - - -# %% [markdown] -""" -Chatsky provides one basic response as part of -the %mddoclink(api,responses.standard) module: - -- `RandomChoice` randomly chooses a message out of the ones passed to it. -""" - - -# %% -toy_script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - "node1": { - RESPONSE: rsp.RandomChoice( - "Hi, what is up?", - "Hello, how are you?", - ), - # Random choice from candidate list. - TRANSITIONS: [ - Tr(dst="node2", cnd=cnd.ExactMatch("I'm fine, how are you?")) - ], - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr(dst="node3", cnd=cnd.ExactMatch("Let's talk about music.")) - ], - }, - "node3": { - RESPONSE: CannotTalkAboutTopic(), - TRANSITIONS: [Tr(dst="node4", cnd=cnd.ExactMatch("Ok, goodbye."))], - }, - "node4": { - RESPONSE: UpperCase("bye"), - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - "fallback_node": { - RESPONSE: FallbackTrace(), - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - } -} - -# testing -happy_path = ( - ( - "Hi", - "Hello, how are you?", - ), # start_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "BYE"), # node3 -> node4 - ("Hi", "Hello, how are you?"), # node4 -> node1 - ( - "stop", - Message( - misc={ - "previous_node": AbsoluteNodeLabel( - flow_name="greeting_flow", node_name="node1" - ), - "last_request": Message("stop"), - } - ), - ), - # node1 -> fallback_node - ( - "one", - Message( - misc={ - "previous_node": AbsoluteNodeLabel( - flow_name="greeting_flow", node_name="fallback_node" - ), - "last_request": Message("one"), - } - ), - ), # f_n->f_n - ( - "help", - Message( - misc={ - "previous_node": AbsoluteNodeLabel( - flow_name="greeting_flow", node_name="fallback_node" - ), - "last_request": Message("help"), - } - ), - ), # f_n->f_n - ( - "nope", - Message( - misc={ - "previous_node": AbsoluteNodeLabel( - flow_name="greeting_flow", node_name="fallback_node" - ), - "last_request": Message("nope"), - } - ), - ), # f_n->f_n - ( - "Hi", - "Hi, what is up?", - ), # fallback_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about music now.", - ), # node2 -> node3 - ("Ok, goodbye.", "BYE"), # node3 -> node4 -) - -# %% -random.seed(31415) # predestination of choice - - -pipeline = Pipeline( - script=toy_script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.4_transitions.py.txt b/_sources/tutorials/tutorials.script.core.4_transitions.py.txt deleted file mode 100644 index 482697c31..000000000 --- a/_sources/tutorials/tutorials.script.core.4_transitions.py.txt +++ /dev/null @@ -1,338 +0,0 @@ -# %% [markdown] -""" -# Core: 4. Transitions - -This tutorial shows settings for transitions between flows and nodes. - -Here, [conditions](%doclink(api,conditions.standard)) -for transition between many different script steps are shown. - -Some of the destination steps can be set using -[destinations](%doclink(api,destinations.standard)). -""" - - -# %pip install chatsky - -# %% -import re - -from chatsky import ( - TRANSITIONS, - RESPONSE, - Context, - NodeLabelInitTypes, - Pipeline, - Transition as Tr, - BaseDestination, - conditions as cnd, - destinations as dst, -) -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - -# %% [markdown] -""" -The `TRANSITIONS` keyword is used to determine a list of transitions from -the current node. After receiving user request, Pipeline will choose the -next node relying on that list. -If no transition in the list is suitable, transition will be made -to the fallback node. - -Each transition is represented by the %mddoclink(api,core.transition,Transition) -class. - -It has three main fields: - -- dst: Destination determines the node to which the transition is made. -- cnd: Condition determines if the transition is allowed. -- priority: Allows choosing one of the transitions if several are allowed. - Higher priority transitions will be chosen over the rest. - If priority is not set, - %mddoclink(api,core.pipeline,Pipeline.default_priority) - is used instead. - Default priority is 1 by default (but may be set via Pipeline). - -For more details on how the next node is chosen see -[here](%doclink(api,core.transition,get_next_label)). - -Like conditions, all of these fields can be either constant values or -custom functions (%mddoclink(api,core.script_function,BaseDestination), -%mddoclink(api,core.script_function,BaseCondition), -%mddoclink(api,core.script_function,BasePriority)). -""" - -# %% [markdown] -""" -## Destinations - -Destination node is specified with a %mddoclink(api,core.node_label,NodeLabel) -class. - -It contains two field: - -- "flow_name": Name of the flow the node belongs to. - Optional; if not set, will use the flow of the current node. -- "node_name": Name of the node inside the flow. - -Instances of this class can be initialized from a tuple of two strings -(flow name and node name) or a single string (node name; relative flow name). -This happens automatically for return values of `BaseDestination` -and for the `dst` field of `Transition`. -""" - - -# %% -class GreetingFlowNode2(BaseDestination): - async def call(self, ctx: Context) -> NodeLabelInitTypes: - return "greeting_flow", "node2" - - -# %% [markdown] -""" -Chatsky provides several basic transitions as part of -the %mddoclink(api,destinations.standard) module: - -- `FromHistory` returns a node from label history. - `Current` and `Previous` are subclasses of it that return specific nodes - (current node and previous node respectively). -- `Start` returns the start node. -- `Fallback` returns the fallback node. -- `Forward` returns the next node (in order of definition) - in the current flow relative to the current node. -- `Backward` returns the previous node (in order of definition) - in the current flow relative to the current node. -""" - -# %% -toy_script = { - "global_flow": { - "start_node": { - TRANSITIONS: [ - Tr( - dst=("music_flow", "node1"), - cnd=cnd.Regexp(r"talk about music"), - # this condition is checked first. - # if it fails, pipeline will try the next transition - ), - Tr( - dst=("greeting_flow", "node1"), - cnd=cnd.Regexp(r"hi|hello", flags=re.IGNORECASE), - ), - Tr( - dst="fallback_node", - # a single string references a node in the same flow - ), - # this transition will only be made if previous ones fail - ] - }, - "fallback_node": { - RESPONSE: "Ooops", - TRANSITIONS: [ - Tr( - dst=("music_flow", "node1"), - cnd=cnd.Regexp(r"talk about music"), - ), - Tr( - dst=("greeting_flow", "node1"), - cnd=cnd.Regexp(r"hi|hello", flags=re.IGNORECASE), - ), - Tr( - dst=dst.Previous(), - cnd=cnd.Regexp(r"previous", flags=re.IGNORECASE), - ), - Tr( - dst=dst.Current(), # this goes to the current node - # i.e. fallback node - ), - ], - }, - }, - "greeting_flow": { - "node1": { - RESPONSE: "Hi, how are you?", - TRANSITIONS: [ - Tr( - dst=("global_flow", "fallback_node"), - priority=0.1, - ), # due to low priority (default priority is 1) - # this transition will be made if the next one fails - Tr(dst="node2", cnd=cnd.Regexp(r"how are you")), - ], - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr( - dst=dst.Fallback(), - priority=0.1, - ), - # there is no need to specify such transition: - # For any node if all transitions fail, - # fallback node becomes the next node. - # Here, this transition exists for demonstration purposes. - Tr( - dst=dst.Forward(), # i.e. "node3" of this flow - cnd=cnd.Regexp(r"talk about"), - priority=0.5, - ), # this transition is the third candidate - Tr( - dst=("music_flow", "node1"), - cnd=cnd.Regexp(r"talk about music"), - ), # this transition is the first candidate - Tr( - dst=dst.Previous(), - cnd=cnd.Regexp(r"previous", flags=re.IGNORECASE), - ), # this transition is the second candidate - ], - }, - "node3": { - RESPONSE: "Sorry, I can not talk about that now.", - TRANSITIONS: [Tr(dst=dst.Forward(), cnd=cnd.Regexp(r"bye"))], - }, - "node4": { - RESPONSE: "Bye", - TRANSITIONS: [ - Tr( - dst="node1", - cnd=cnd.Regexp(r"hi|hello", flags=re.IGNORECASE), - ) - ], - }, - }, - "music_flow": { - "node1": { - RESPONSE: "I love `System of a Down` group, " - "would you like to talk about it?", - TRANSITIONS: [ - Tr( - dst=dst.Forward(), - cnd=cnd.Regexp(r"yes|yep|ok", flags=re.IGNORECASE), - ) - ], - }, - "node2": { - RESPONSE: "System of a Down is an Armenian-American " - "heavy metal band formed in 1994.", - TRANSITIONS: [ - Tr( - dst=dst.Forward(), - cnd=cnd.Regexp(r"next", flags=re.IGNORECASE), - ), - Tr( - dst=dst.Current(), - cnd=cnd.Regexp(r"repeat", flags=re.IGNORECASE), - ), - ], - }, - "node3": { - RESPONSE: "The band achieved commercial success " - "with the release of five studio albums.", - TRANSITIONS: [ - Tr( - dst=dst.Forward(), - cnd=cnd.Regexp(r"next", flags=re.IGNORECASE), - ), - Tr( - dst=dst.Backward(), - cnd=cnd.Regexp(r"back", flags=re.IGNORECASE), - ), - Tr( - dst=dst.Current(), - cnd=cnd.Regexp(r"repeat", flags=re.IGNORECASE), - ), - ], - }, - "node4": { - RESPONSE: "That's all I know.", - TRANSITIONS: [ - Tr( - dst=GreetingFlowNode2(), - cnd=cnd.Regexp(r"next", flags=re.IGNORECASE), - ), - Tr( - dst=("greeting_flow", "node4"), - cnd=cnd.Regexp(r"next time", flags=re.IGNORECASE), - priority=2, - ), # "next" is contained in "next_time" so we need higher - # priority here. - # Otherwise, this transition will never be made - ], - }, - }, -} - -# testing -happy_path = ( - ("hi", "Hi, how are you?"), - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), - ( - "talk about music.", - "I love `System of a Down` group, " "would you like to talk about it?", - ), - ( - "yes", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "next", - "The band achieved commercial success " - "with the release of five studio albums.", - ), - ( - "back", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "repeat", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "next", - "The band achieved commercial success " - "with the release of five studio albums.", - ), - ("next", "That's all I know."), - ( - "next", - "Good. What do you want to talk about?", - ), - ("previous", "That's all I know."), - ("next time", "Bye"), - ("stop", "Ooops"), - ("previous", "Bye"), - ("stop", "Ooops"), - ("nope", "Ooops"), - ("hi", "Hi, how are you?"), - ("stop", "Ooops"), - ("previous", "Hi, how are you?"), - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), - ( - "let's talk about something.", - "Sorry, I can not talk about that now.", - ), - ("Ok, goodbye.", "Bye"), -) - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("global_flow", "start_node"), - fallback_label=("global_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.5_global_local.py.txt b/_sources/tutorials/tutorials.script.core.5_global_local.py.txt deleted file mode 100644 index 7010a6f16..000000000 --- a/_sources/tutorials/tutorials.script.core.5_global_local.py.txt +++ /dev/null @@ -1,254 +0,0 @@ -# %% [markdown] -""" -# Core: 5. Global and Local nodes -""" - - -# %pip install chatsky - -# %% -import re - -from chatsky import ( - GLOBAL, - TRANSITIONS, - RESPONSE, - Pipeline, - Transition as Tr, - conditions as cnd, - destinations as dst, -) -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - -# %% [markdown] -""" -Keywords `GLOBAL` and `LOCAL` are used to define global and local nodes -respectively. Global node is defined at the script level (along with flows) -and local node is defined at the flow level (along with nodes inside a flow). - -Every local node inherits properties from the global node. -Every node inherits properties from the local node (of its flow). - -For example, if we are to set list `A` as transitions for the -local node of a flow, then every node of that flow would effectively -have the `A` list extended with its own transitions. - -
- -To sum up transition priorities: - -Transition A is of higher priority compared to Transition B: - -1. If A.priority > B.priority; OR -2. If A is a node transition and B is a local or global transition; - or A is a local transition and B is a global transition; OR -3. If A is defined in the transition list earlier than B. - -
- -For more information on node inheritance, see [here]( -%doclink(api,core.script,Script.get_inherited_node) -). - -
- -Note - -Property %mddoclink(api,core.context,Context.current_node) does not return -the current node as is. Instead it returns a node that is modified -by the global and local nodes. - -
-""" - -# %% -toy_script = { - GLOBAL: { - TRANSITIONS: [ - Tr( - dst=("greeting_flow", "node1"), - cnd=cnd.Regexp(r"\b(hi|hello)\b", flags=re.I), - priority=1.1, - ), - Tr( - dst=("music_flow", "node1"), - cnd=cnd.Regexp(r"talk about music"), - priority=1.1, - ), - Tr( - dst=dst.Forward(), - cnd=cnd.All( - cnd.Regexp(r"next\b"), - cnd.CheckLastLabels( - labels=[("music_flow", i) for i in ["node2", "node3"]] - ), # this checks if the current node is - # music_flow.node2 or music_flow.node3 - ), - ), - Tr( - dst=dst.Current(), - cnd=cnd.All( - cnd.Regexp(r"repeat", flags=re.I), - cnd.Negation( - cnd.CheckLastLabels(flow_labels=["global_flow"]) - ), - ), - priority=0.2, - ), - ], - }, - "global_flow": { - "start_node": {}, - "fallback_node": { - RESPONSE: "Ooops", - TRANSITIONS: [ - Tr( - dst=dst.Previous(), - cnd=cnd.Regexp(r"previous", flags=re.I), - ) - ], - }, - }, - "greeting_flow": { - "node1": { - RESPONSE: "Hi, how are you?", - TRANSITIONS: [Tr(dst="node2", cnd=cnd.Regexp(r"how are you"))], - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr( - dst=dst.Forward(), - cnd=cnd.Regexp(r"talk about"), - priority=0.5, - ), - Tr( - dst=dst.Previous(), - cnd=cnd.Regexp(r"previous", flags=re.I), - ), - ], - }, - "node3": { - RESPONSE: "Sorry, I can not talk about that now.", - TRANSITIONS: [Tr(dst=dst.Forward(), cnd=cnd.Regexp(r"bye"))], - }, - "node4": {RESPONSE: "bye"}, - # This node does not define its own transitions. - # It will use global transitions only. - }, - "music_flow": { - "node1": { - RESPONSE: "I love `System of a Down` group, " - "would you like to talk about it?", - TRANSITIONS: [ - Tr( - dst=dst.Forward(), - cnd=cnd.Regexp(r"yes|yep|ok", flags=re.IGNORECASE), - ) - ], - }, - "node2": { - RESPONSE: "System of a Down is an Armenian-American " - "heavy metal band formed in 1994.", - }, - "node3": { - RESPONSE: "The band achieved commercial success " - "with the release of five studio albums.", - TRANSITIONS: [ - Tr( - dst=dst.Backward(), - cnd=cnd.Regexp(r"back", flags=re.IGNORECASE), - ), - ], - }, - "node4": { - RESPONSE: "That's all I know.", - TRANSITIONS: [ - Tr( - dst=("greeting_flow", "node4"), - cnd=cnd.Regexp(r"next time", flags=re.I), - ), - Tr( - dst=("greeting_flow", "node2"), - cnd=cnd.Regexp(r"next", flags=re.I), - ), - ], - }, - }, -} - -# testing -happy_path = ( - ("hi", "Hi, how are you?"), - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), - ( - "talk about music.", - "I love `System of a Down` group, " "would you like to talk about it?", - ), - ( - "yes", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "next", - "The band achieved commercial success " - "with the release of five studio albums.", - ), - ( - "back", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "repeat", - "System of a Down is " - "an Armenian-American heavy metal band formed in 1994.", - ), - ( - "next", - "The band achieved commercial success " - "with the release of five studio albums.", - ), - ("next", "That's all I know."), - ( - "next", - "Good. What do you want to talk about?", - ), - ("previous", "That's all I know."), - ("next time", "bye"), - ("stop", "Ooops"), - ("previous", "bye"), - ("stop", "Ooops"), - ("nope", "Ooops"), - ("hi", "Hi, how are you?"), - ("stop", "Ooops"), - ("previous", "Hi, how are you?"), - ( - "i'm fine, how are you?", - "Good. What do you want to talk about?", - ), - ( - "let's talk about something.", - "Sorry, I can not talk about that now.", - ), - ("Ok, goodbye.", "bye"), -) - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("global_flow", "start_node"), - fallback_label=("global_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.6_context_serialization.py.txt b/_sources/tutorials/tutorials.script.core.6_context_serialization.py.txt deleted file mode 100644 index b74e1abe3..000000000 --- a/_sources/tutorials/tutorials.script.core.6_context_serialization.py.txt +++ /dev/null @@ -1,85 +0,0 @@ -# %% [markdown] -""" -# Core: 6. Context serialization -""" - -# %pip install chatsky - -# %% -import logging - -from chatsky import ( - TRANSITIONS, - RESPONSE, - Context, - Pipeline, - Transition as Tr, - BaseResponse, - MessageInitTypes, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% -class RequestCounter(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - return f"answer {len(ctx.requests)}" - - -# %% -toy_script = { - "flow_start": { - "node_start": { - RESPONSE: RequestCounter(), - TRANSITIONS: [Tr(dst=("flow_start", "node_start"))], - } - } -} - -# testing -happy_path = ( - ("hi", "answer 1"), - ("how are you?", "answer 2"), - ("ok", "answer 3"), - ("good", "answer 4"), -) - -# %% [markdown] -""" -Draft function that performs serialization. -""" - - -# %% -def process_response(ctx: Context): - ctx_json = ctx.model_dump_json() - if isinstance(ctx_json, str): - logging.info("context serialized to json str") - else: - raise Exception(f"ctx={ctx_json} has to be serialized to json string") - - ctx_dict = ctx.model_dump() - if isinstance(ctx_dict, dict): - logging.info("context serialized to dict") - else: - raise Exception(f"ctx={ctx_dict} has to be serialized to dict") - - if not isinstance(ctx, Context): - raise Exception(f"ctx={ctx} has to have Context type") - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("flow_start", "node_start"), - post_services=[process_response], -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.7_pre_response_processing.py.txt b/_sources/tutorials/tutorials.script.core.7_pre_response_processing.py.txt deleted file mode 100644 index b51fc86e2..000000000 --- a/_sources/tutorials/tutorials.script.core.7_pre_response_processing.py.txt +++ /dev/null @@ -1,190 +0,0 @@ -# %% [markdown] -""" -# Core: 7. Pre-response processing - -Here, %mddoclink(api,core.script,PRE_RESPONSE) -is demonstrated which can be used for -additional context processing before response handlers. -""" - -# %pip install chatsky - -# %% -from chatsky import ( - GLOBAL, - LOCAL, - RESPONSE, - TRANSITIONS, - PRE_RESPONSE, - Context, - Message, - MessageInitTypes, - BaseResponse, - Transition as Tr, - Pipeline, - destinations as dst, - processing as proc, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% [markdown] -""" -Processing functions have the same signature as -conditions, responses or destinations -except they don't return anything: - -.. python: - - class MyProcessing(BaseProcessing): - async def call(self, ctx: Context) -> None: - ... - - -The main way for processing functions to interact with the script -is modifying `ctx.current_node`, which is used by pipeline -to store a copy of the current node in script. -Any of its attributes can be safely edited, and these changes will -only have an effect during the current turn of the current context. -""" - - -# %% [markdown] -""" -In this tutorial we'll subclass -%mddoclink(api,processing.standard,ModifyResponse) -processing function so that it would modify response -of the current node to include a prefix. -""" - - -# %% -class AddPrefix(proc.ModifyResponse): - prefix: str - - def __init__(self, prefix: str): - # basemodel does not allow positional arguments by default - super().__init__(prefix=prefix) - - async def modified_response( - self, original_response: BaseResponse, ctx: Context - ) -> MessageInitTypes: - result = await original_response(ctx) - - if result.text is not None: - result.text = f"{self.prefix}: {result.text}" - return result - - -# %% [markdown] -""" -
- -Tip - -You can use `ModifyResponse` to catch exceptions in response functions: - -.. python: - - class ExceptionHandler(proc.ModifyResponse): - async def modified_response(self, original_response, ctx): - try: - return await original_response(ctx) - except Exception as exc: - return str(exc) - -
-""" - - -# %% -toy_script = { - "root": { - "start": { - TRANSITIONS: [Tr(dst=("flow", "step_0"))], - }, - "fallback": {RESPONSE: "the end"}, - }, - GLOBAL: { - PRE_RESPONSE: { - "proc_name_1": AddPrefix("l1_global"), - "proc_name_2": AddPrefix("l2_global"), - } - }, - "flow": { - LOCAL: { - PRE_RESPONSE: { - "proc_name_2": AddPrefix("l2_local"), - "proc_name_3": AddPrefix("l3_local"), - }, - TRANSITIONS: [Tr(dst=dst.Forward(loop=True))], - }, - "step_0": { - RESPONSE: "first", - }, - "step_1": { - PRE_RESPONSE: {"proc_name_1": AddPrefix("l1_step_1")}, - RESPONSE: "second", - }, - "step_2": { - PRE_RESPONSE: {"proc_name_2": AddPrefix("l2_step_2")}, - RESPONSE: "third", - }, - "step_3": { - PRE_RESPONSE: {"proc_name_3": AddPrefix("l3_step_3")}, - RESPONSE: "fourth", - }, - "step_4": { - PRE_RESPONSE: {"proc_name_4": AddPrefix("l4_step_4")}, - RESPONSE: "fifth", - }, - }, -} - - -# %% [markdown] -""" -The order of execution for processing functions is as follows: - -1. All node-specific functions are executed in the order of definition; -2. All local functions are executed in the order of definition except those with - keys matching to previously executed functions; -3. All global functions are executed in the order of definition - except those with keys matching to previously executed functions. - -That means that if both global and local nodes -define a processing function with key "processing_name", -only the one inside the local node will be executed. - -This demonstrated in the happy path below -(the first prefix in the text is the last one to execute): -""" - - -# %% -# testing -happy_path = ( - (Message(), "l1_global: l3_local: l2_local: first"), - (Message(), "l3_local: l2_local: l1_step_1: second"), - (Message(), "l1_global: l3_local: l2_step_2: third"), - (Message(), "l1_global: l2_local: l3_step_3: fourth"), - (Message(), "l1_global: l3_local: l2_local: l4_step_4: fifth"), - (Message(), "l1_global: l3_local: l2_local: first"), -) - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.8_misc.py.txt b/_sources/tutorials/tutorials.script.core.8_misc.py.txt deleted file mode 100644 index 536671d57..000000000 --- a/_sources/tutorials/tutorials.script.core.8_misc.py.txt +++ /dev/null @@ -1,133 +0,0 @@ -# %% [markdown] -""" -# Core: 8. Misc - -This tutorial shows `MISC` (miscellaneous) keyword usage. - -See %mddoclink(api,core.script,MISC) -for more information. -""" - -# %pip install chatsky - -# %% -from chatsky import ( - GLOBAL, - LOCAL, - RESPONSE, - TRANSITIONS, - MISC, - Context, - Message, - Pipeline, - MessageInitTypes, - BaseResponse, - Transition as Tr, - destinations as dst, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% [markdown] -""" -`MISC` is used to store custom node data. -It can be accessed via `ctx.current_node.misc`. -""" - - -# %% -class CustomResponse(BaseResponse): - async def call(self, ctx: Context) -> MessageInitTypes: - return ( - f"node_name={ctx.last_label.node_name}: " - f"current_node.misc={ctx.current_node.misc}" - ) - - -# %% -toy_script = { - "root": { - "start": { - TRANSITIONS: [Tr(dst=("flow", "step_0"))], - }, - "fallback": {RESPONSE: "the end"}, - }, - GLOBAL: { - MISC: { - "var1": "global_data", - "var2": "global_data", - "var3": "global_data", - } - }, - "flow": { - LOCAL: { - MISC: { - "var2": "global data is overwritten by local", - "var3": "global data is overwritten by local", - }, - TRANSITIONS: [Tr(dst=dst.Forward(loop=True))], - }, - "step_0": { - MISC: {"var3": "this overwrites local values - step_0"}, - RESPONSE: CustomResponse(), - }, - "step_1": { - MISC: {"var3": "this overwrites local values - step_1"}, - RESPONSE: CustomResponse(), - }, - "step_2": { - MISC: {"var3": "this overwrites local values - step_2"}, - RESPONSE: CustomResponse(), - }, - }, -} - - -# testing -happy_path = ( - ( - Message(), - "node_name=step_0: current_node.misc=" - "{'var3': 'this overwrites local values - step_0', " - "'var2': 'global data is overwritten by local', " - "'var1': 'global_data'}", - ), - ( - Message(), - "node_name=step_1: current_node.misc=" - "{'var3': 'this overwrites local values - step_1', " - "'var2': 'global data is overwritten by local', " - "'var1': 'global_data'}", - ), - ( - Message(), - "node_name=step_2: current_node.misc=" - "{'var3': 'this overwrites local values - step_2', " - "'var2': 'global data is overwritten by local', " - "'var1': 'global_data'}", - ), - ( - Message(), - "node_name=step_0: current_node.misc=" - "{'var3': 'this overwrites local values - step_0', " - "'var2': 'global data is overwritten by local', " - "'var1': 'global_data'}", - ), -) - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.core.9_pre_transition_processing.py.txt b/_sources/tutorials/tutorials.script.core.9_pre_transition_processing.py.txt deleted file mode 100644 index 86c69fc41..000000000 --- a/_sources/tutorials/tutorials.script.core.9_pre_transition_processing.py.txt +++ /dev/null @@ -1,139 +0,0 @@ -# %% [markdown] -""" -# Core: 9. Pre-transition processing - -Here, %mddoclink(api,core.script,PRE_TRANSITION) -is demonstrated which can be used for additional context -processing before transitioning to the next step. -""" - -# %pip install chatsky - -# %% -from chatsky import ( - GLOBAL, - RESPONSE, - TRANSITIONS, - PRE_RESPONSE, - PRE_TRANSITION, - Context, - Pipeline, - BaseProcessing, - BaseResponse, - MessageInitTypes, - Transition as Tr, - destinations as dst, - processing as proc, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - - -# %% [markdown] -""" -Processing functions can be used at two stages: - -1. Pre-transition. Triggers after response is received but before - the next node is considered. -2. Pre-response. Triggers after transition is chosen and current node is - changed but before response of that node is calculated. - -In this tutorial we'll save the response function of the current node -during pre-transition and extract it during pre-response -(at which point current node is already changed). -""" - - -# %% -class SavePreviousNodeResponse(BaseProcessing): - async def call(self, ctx: Context) -> None: - if ctx.current_node.response is not None: - ctx.misc["previous_node_response"] = ctx.current_node.response - # This function is called as Pre-transition - # so current node is going to be the previous one - # when we reach the Pre-response step - - -class PrependPreviousNodeResponse(proc.ModifyResponse): - async def modified_response( - self, original_response: BaseResponse, ctx: Context - ) -> MessageInitTypes: - result = await original_response(ctx) - - previous_node_response = ctx.misc.get("previous_node_response") - if previous_node_response is None: - return result - else: - previous_result = await previous_node_response(ctx) - return f"previous={previous_result.text}: current={result.text}" - - -# %% [markdown] -""" -
- -Note - -Previous node can be accessed another way. - -Instead of storing the node response in misc, -one can obtain previous label -with `dst.Previous()(ctx)` and then get the node from the -%mddoclink(api,core.script,Script) object: - -```python -ctx.pipeline.script.get_inherited_node(dst.Previous()(ctx)) -``` - -
-""" - - -# %% -# a dialog script -toy_script = { - "root": { - "start": { - TRANSITIONS: [Tr(dst=("flow", "step_0"))], - }, - "fallback": {RESPONSE: "the end"}, - }, - GLOBAL: { - PRE_RESPONSE: {"proc_name_1": PrependPreviousNodeResponse()}, - PRE_TRANSITION: {"proc_name_1": SavePreviousNodeResponse()}, - TRANSITIONS: [Tr(dst=dst.Forward(loop=True))], - }, - "flow": { - "step_0": {RESPONSE: "first"}, - "step_1": {RESPONSE: "second"}, - "step_2": {RESPONSE: "third"}, - "step_3": {RESPONSE: "fourth"}, - "step_4": {RESPONSE: "fifth"}, - }, -} - - -# testing -happy_path = ( - ("1", "first"), - ("2", "previous=first: current=second"), - ("3", "previous=second: current=third"), - ("4", "previous=third: current=fourth"), - ("5", "previous=fourth: current=fifth"), -) - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.responses.1_media.py.txt b/_sources/tutorials/tutorials.script.responses.1_media.py.txt deleted file mode 100644 index 7ecf86fa4..000000000 --- a/_sources/tutorials/tutorials.script.responses.1_media.py.txt +++ /dev/null @@ -1,112 +0,0 @@ -# %% [markdown] -""" -# Responses: 1. Media - -Here, %mddoclink(api,core.message,Attachment) class is shown. -Attachments can be used for attaching different media elements -(such as %mddoclink(api,core.message,Image), -%mddoclink(api,core.message,Document) -or %mddoclink(api,core.message,Audio)). - -They can be attached to any message but will only work if the chosen -[messenger interface](%doclink(api,index_messenger_interfaces)) supports them. -""" - -# %pip install chatsky - -# %% -from chatsky import ( - RESPONSE, - TRANSITIONS, - Message, - Pipeline, - Transition as Tr, - conditions as cnd, - destinations as dst, -) -from chatsky.core.message import Image - -from chatsky.utils.testing import ( - check_happy_path, - is_interactive_mode, -) - - -# %% -img_url = "https://www.python.org/static/img/python-logo.png" -toy_script = { - "root": { - "start": { - TRANSITIONS: [Tr(dst=("pics", "ask_picture"))], - }, - "fallback": { - RESPONSE: "Final node reached, send any message to restart.", - TRANSITIONS: [Tr(dst=("pics", "ask_picture"))], - }, - }, - "pics": { - "ask_picture": { - RESPONSE: "Please, send me a picture url", - TRANSITIONS: [ - Tr( - dst=("pics", "send_one"), - cnd=cnd.Regexp(r"^http.+\.png$"), - ), - Tr( - dst=("pics", "send_many"), - cnd=cnd.Regexp(f"{img_url} repeat 10 times"), - ), - Tr( - dst=dst.Current(), - ), - ], - }, - "send_one": { - RESPONSE: Message( # need to use the Message class to send images - text="here's my picture!", - attachments=[Image(source=img_url)], - ), - }, - "send_many": { - RESPONSE: Message( - text="Look at my pictures", - attachments=[Image(source=img_url)] * 10, - ), - }, - }, -} - -happy_path = ( - ("Hi", "Please, send me a picture url"), - ("no", "Please, send me a picture url"), - ( - img_url, - Message( - text="here's my picture!", - attachments=[Image(source=img_url)], - ), - ), - ("ok", "Final node reached, send any message to restart."), - ("ok", "Please, send me a picture url"), - ( - f"{img_url} repeat 10 times", - Message( - text="Look at my pictures", - attachments=[Image(source=img_url)] * 10, - ), - ), - ("ok", "Final node reached, send any message to restart."), -) - - -# %% -pipeline = Pipeline( - script=toy_script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.script.responses.2_multi_message.py.txt b/_sources/tutorials/tutorials.script.responses.2_multi_message.py.txt deleted file mode 100644 index f6019f3a2..000000000 --- a/_sources/tutorials/tutorials.script.responses.2_multi_message.py.txt +++ /dev/null @@ -1,156 +0,0 @@ -# %% [markdown] -""" -# Responses: 2. Multi Message - -This tutorial shows how to store several messages inside a single one. -This might be useful if you want Chatsky Pipeline to send `response` candidates -to the messenger interface instead of a final response. -""" - -# %pip install chatsky - -# %% - -from chatsky import ( - TRANSITIONS, - RESPONSE, - Message, - Pipeline, - Transition as Tr, - conditions as cnd, -) - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - -# %% -toy_script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - "node1": { - RESPONSE: Message( - misc={ - "messages": [ - Message( - text="Hi, what is up?", misc={"confidences": 0.85} - ), - Message( - text="Hello, how are you?", - misc={"confidences": 0.9}, - ), - ] - } - ), - TRANSITIONS: [ - Tr(dst="node2", cnd=cnd.ExactMatch("I'm fine, how are you?")) - ], - }, - "node2": { - RESPONSE: "Good. What do you want to talk about?", - TRANSITIONS: [ - Tr(dst="node3", cnd=cnd.ExactMatch("Let's talk about music.")) - ], - }, - "node3": { - RESPONSE: "Sorry, I can not talk about that now.", - TRANSITIONS: [Tr(dst="node4", cnd=cnd.ExactMatch("Ok, goodbye."))], - }, - "node4": { - RESPONSE: "bye", - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - "fallback_node": { - RESPONSE: "Ooops", - TRANSITIONS: [Tr(dst="node1", cnd=cnd.ExactMatch("Hi"))], - }, - } -} - -# testing -happy_path = ( - ( - "Hi", - Message( - misc={ - "messages": [ - Message("Hi, what is up?", misc={"confidences": 0.85}), - Message("Hello, how are you?", misc={"confidences": 0.9}), - ] - } - ), - ), # start_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about that now.", - ), # node2 -> node3 - ("Ok, goodbye.", "bye"), # node3 -> node4 - ( - "Hi", - Message( - misc={ - "messages": [ - Message("Hi, what is up?", misc={"confidences": 0.85}), - Message("Hello, how are you?", misc={"confidences": 0.9}), - ] - } - ), - ), # node4 -> node1 - ( - "stop", - "Ooops", - ), - # node1 -> fallback_node - ( - "one", - "Ooops", - ), # f_n->f_n - ( - "help", - "Ooops", - ), # f_n->f_n - ( - "nope", - "Ooops", - ), # f_n->f_n - ( - "Hi", - Message( - misc={ - "messages": [ - Message("Hi, what is up?", misc={"confidences": 0.85}), - Message("Hello, how are you?", misc={"confidences": 0.9}), - ] - } - ), - ), # fallback_node -> node1 - ( - "I'm fine, how are you?", - "Good. What do you want to talk about?", - ), # node1 -> node2 - ( - "Let's talk about music.", - "Sorry, I can not talk about that now.", - ), # node2 -> node3 - ("Ok, goodbye.", "bye"), # node3 -> node4 -) - -# %% - -pipeline = Pipeline( - script=toy_script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), -) - -if __name__ == "__main__": - check_happy_path(pipeline, happy_path, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.service.1_basics.py.txt b/_sources/tutorials/tutorials.service.1_basics.py.txt deleted file mode 100644 index 68962f251..000000000 --- a/_sources/tutorials/tutorials.service.1_basics.py.txt +++ /dev/null @@ -1,85 +0,0 @@ -# %% [markdown] -""" -# 1. Basic services - -This tutorial shows basics of using services. - -Here, %mddoclink(api,core.context,Context.misc) -dictionary of context is used for storing additional data. -""" - -# %pip install chatsky - -# %% -import logging -import sys -from importlib import reload - -from chatsky import Context, Pipeline - -from chatsky.utils.testing import ( - check_happy_path, - is_interactive_mode, - HAPPY_PATH, - TOY_SCRIPT_KWARGS, -) - -reload(logging) -logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="") -# fix jupyter logs display - -logger = logging.getLogger(__name__) - - -# %% [markdown] -""" -When Pipeline is created, additional pre- -and post-services can be defined. These run before and after -`Actor` respectively. - -
- -Reminder - -`Actor` is a Pipeline component that processes user request, determines -the next node and generates a response from that node. - -
- -Services can be used to access external APIs, annotate user input, etc. - -Service callables only take one parameter: `ctx` (Context) and have no return. - -Here a pre-service ("ping") and -a post-service ("pong") are added to the pipeline. -""" - - -# %% -def ping_processor(ctx: Context): - logger.info("ping - ...") - ctx.misc["ping"] = True - - -# services can be both sync and async -async def pong_processor(ctx: Context): - ping_pong = ctx.misc.get("ping", False) - logger.info("... - pong") - logger.info( - f"Ping-pong exchange: " f"{'completed' if ping_pong else 'failed'}." - ) - - -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, # contains script, start and fallback labels - pre_services=[ping_processor], - post_services=[pong_processor], - # To add a service simply add it to the corresponding service list -) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH[:1], printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.service.2_advanced.py.txt b/_sources/tutorials/tutorials.service.2_advanced.py.txt deleted file mode 100644 index 17dfcca24..000000000 --- a/_sources/tutorials/tutorials.service.2_advanced.py.txt +++ /dev/null @@ -1,143 +0,0 @@ -# %% [markdown] -""" -# 2. Advanced services - -This tutorial demonstrates various configuration options for services. - -For more information, see -[API ref](%doclink(api,core.service.service,Service)). -""" - -# %pip install chatsky - -# %% -import logging -import sys -from importlib import reload - -from chatsky import Context, Pipeline, BaseProcessing -from chatsky.core.service import Service -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) - -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - -reload(logging) -logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="") -logger = logging.getLogger(__name__) - -# %% [markdown] -""" -## Intro - -In the previous tutorial we used a function as a Service. -Under the hood a function is converted to a `Service` object -with the function as its `handler` argument. - -The `Service` model has other arguments that modify its execution. - -## Service Arguments - -* `handler` - Function or `BaseProcessing`. -* `before_handler` - a list of functions that run before the service. - You can read more about the handlers in this [tutorial]( - %doclink(tutorial,service.5_extra_handlers) - ). -* `after_handler` - a list of functions that run after the service. - You can read more about the handlers in this [tutorial]( - %doclink(tutorial,service.5_extra_handlers) - ). -* `timeout` - service timeout. -* `concurrent` - whether this service can run concurrently, - see [tutorial 3]( - %doclink(tutorial,service.3_groups)). -* `start_condition` - service start condition, see [tutorial 4]( - %doclink(tutorial,service.4_conditions_and_paths)). -* `name` - name of the service, - see [tutorial 4]( - %doclink(tutorial,service.4_conditions_and_paths)). - -## Service subclassing - -Services can also be defined as subclasses of `Service`, -allowing access to all the fields described above via `self`. - -To do this, derive your class from `Service`, -then implement an async `call` method which will -now replace the `handler` (see the `PreProcess` example below). - -
- -Tip - -When defining a service as a subclass of `Service`, you can also change -default parameters such as `timeout` or `start_condition`. - -
- -## Code explanation - -In this example, pipeline contains three services, -defined in three different ways. - -The first is defined as a Service with a function handler. - -The second derives from the `Service` class. - -The third is defined as a Service with a processing handler. -""" - - -# %% -async def function_handler(ctx: Context): - logger.info( - "function_handler running:\n" - "timeout of this service cannot be determined" - ) - - -class ServiceSubclass(Service): - async def call(self, ctx: Context): - logger.info( - f"{self.name or self.computed_name} running:\n" - f"timeout: {self.timeout}" - ) - - timeout: float = 1.0 - # this overrides the default `None` timeout, - # but can still be overridden in class instances - - -class ProcessingService(BaseProcessing): - async def call(self, ctx: Context) -> None: - try: - logger.info(self.timeout) - except AttributeError: - # this is BaseProcessing not Service so there's no `timeout` field - logger.info( - "ProcessingService running:\n" - "timeout of this service cannot be determined" - ) - - -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, - pre_services=[ - Service( - handler=function_handler, - timeout=0.5, - ), - ServiceSubclass(name="ServiceSubclassWithCustomName", timeout=100), - ServiceSubclass(), - Service(handler=ProcessingService(), timeout=4), - ], -) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH[:1], printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.service.3_groups.py.txt b/_sources/tutorials/tutorials.service.3_groups.py.txt deleted file mode 100644 index 84866eca9..000000000 --- a/_sources/tutorials/tutorials.service.3_groups.py.txt +++ /dev/null @@ -1,156 +0,0 @@ -# %% [markdown] -""" -# 3. Service Groups - -The following tutorial shows how to group multiple services. - -For more information, see -[API ref](%doclink(api,core.service.group,ServiceGroup)). -""" - -# %pip install chatsky - -# %% -import asyncio -import logging -import sys -from importlib import reload - -from chatsky.core import Context, Pipeline -from chatsky.core.service import ServiceGroup - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS - -reload(logging) -logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="") -logger = logging.getLogger(__name__) - -# %% [markdown] -""" -## Intro - -Service groups are used to combine several services -(or service groups) into one. - -Both services and service groups inherit interface from `PipelineComponent` -class which defines all the fields described in the [previous tutorial]( -%doclink(tutorial,service.2_advanced)) -except `handler`. - -Instead of `handler` service group defines `components`: -a list of services or service groups. - -Pipeline pre-services and post-services are actually service groups -and you can pass a ServiceGroup instead of a list when initializing Pipeline. - -## Component execution - -Components inside a service group are executed sequentially, except for -components with the `concurrent` attribute set to `True`: -Continuous sequences of concurrent components are executed concurrently -(via `asyncio.gather`). - -For example, if components are `[1, 1, 0, 0, 1, 1, 1, 0]` where -"1" indicates a concurrent component, the components are executed as follows: - -1. Components 1 and 2 (concurrently); -2. Component 3; -3. Component 4; -4. Components 5, 6 and 7 (concurrently); -5. Component 8. - -
- -Note - -Components processing different contexts are always executed independently -of each other. - -
- -### Fully concurrent flag - -Service groups have a `fully_concurrent` flag which makes it treat -every component inside it as concurrent, -running all components simultaneously. - -This is convenient if you have a bunch of functions, -that you want to run simultaneously, -but don't want to make a service for each of them. - -## Code explanation - -In this example, we define `pre_services` as a `ServiceGroup` instead of a list. -This allows us to set the `fully_concurrent` flag to `True`. -The service group consists of 10 services that sleep 0.01 seconds each. -But since they are executed concurrently, the entire service group -takes much less time than 0.1 seconds. - -To further demonstrate ServiceGroup's execution logic, -`post_services` is a ServiceGroup with concurrent components 'A' and 'B', -which execute simultaneously, and also one regular component 'C' at the end. - -If 'A' and 'B' weren't concurrent, all steps for component 'A' would complete -before component 'B' begins its execution, but instead they start -at the same time. Only after both of them have finished, -does component 'C' start working. -""" - - -# %% -async def time_consuming_service(_): - await asyncio.sleep(0.01) - - -def interact(stage: str, service: str): - async def slow_service(_: Context): - logger.info(f"{stage} with service {service}") - await asyncio.sleep(0.1) - - return slow_service - - -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, - pre_services=ServiceGroup( - components=[time_consuming_service for _ in range(0, 10)], - fully_concurrent=True, - ), - post_services=[ - ServiceGroup( - name="InteractWithServiceA", - components=[ - interact("Starting interaction", "A"), - interact("Finishing interaction", "A"), - ], - concurrent=True, - ), - ServiceGroup( - name="InteractWithServiceB", - components=[ - interact("Starting interaction", "B"), - interact("Finishing interaction", "B"), - ], - concurrent=True, - ), - ServiceGroup( - name="InteractWithServiceC", - components=[ - interact("Starting interaction", "C"), - interact("Finishing interaction", "C"), - ], - concurrent=False, - ), - ], -) - - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH[:1], printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.service.4_conditions_and_paths.py.txt b/_sources/tutorials/tutorials.service.4_conditions_and_paths.py.txt deleted file mode 100644 index e2a00060c..000000000 --- a/_sources/tutorials/tutorials.service.4_conditions_and_paths.py.txt +++ /dev/null @@ -1,209 +0,0 @@ -# %% [markdown] -""" -# 4. Conditions and paths - -This tutorial explains how a unique path is generated for each component -and how to add a condition for component execution. - -[API ref for service conditions](%doclink(api,conditions.service)) -""" - -# %pip install chatsky - -# %% -import logging -import sys -from importlib import reload - -from chatsky.conditions import Not, All, ServiceFinished -from chatsky.core.service import Service, ServiceGroup -from chatsky import Pipeline, Context, AnyCondition - -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS - -reload(logging) -logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="") -logger = logging.getLogger(__name__) - -# %% [markdown] -""" -## Component paths - -Each component has a unique path that can be used as an ID for that component. - -A path of a component is a "." concatenation of names of service groups this -component is in and the name of this component. - -For example, if component "c" is in group "b" which itself is in group "a" -then path of "c" is "a.b.c". - -
- -Naming services - -When choosing a name for a component, keep in mind that -it should be unique among other components in the same service group. - -
- -### Computed names - -If a component does not have a name, one is provided to it: - -1. Each component has `computed_name` property which returns a default name. - All service groups have "service_group" as their computed name. - For services the computed name is the name of the handler **or** of the - service class if handler is not present. - - E.g. A service with handler `my_func` has computed name "my_func"; - and if service `MyService` is a subclass of `Service` and does not - define `handler` its computed name is "MyService". -2. If there are two or more components in a service group with the same - computed name, they are assigned names with an incrementing postfix. - For example, if there are two service groups with no name in pre-services - they will be named "service_group#0" and "service_group#1". - -### Names of basic components - -* Main component (pipeline) has an empty name; -* Name of the pre services group is "pre"; -* Name of the post services group is "post"; -* Name of the actor service is "actor". - -For example, if you have `pre_services=[my_func]`, the full path of the -`my_func` component is ".pre.my_func". - -## Component start condition - -Any component (service or service group) can have a `start_condition`. - -Start condition is a `BaseCondition` that determines whether the component -should be executed. - -For more information about conditions, see the [condition tutorial]( -%doclink(tutorial,script.core.2_conditions)). - -### Component status - -At any time each component has a certain status: - -* `NOT_RUN` - Component hasn't bee executed yet or - start condition returned False. -* `RUNNING` - Component is currently being executed. -* `FINISHED` - Component finished successfully. -* `FAILED` - Component execution failed. - -For more information, see -%mddoclink(api,core.context,FrameworkData.service_states). - -### ServiceFinished condition - -`ServiceFinished` is a condition that returns `True` if another service -has the `FINISHED` status. - -`ServiceFinished` accepts the following constructor parameters: - -* `path` (required) - a path of another component. -* `wait` - whether it should wait for the component to complete. - This means that the component status cannot be - `NOT_RUN` or `RUNNING` at the time of the check. - Defaults to `False`. - -
- -Warning! - -It is possible for the pipeline to get stuck in infinite waiting -with the `ServiceFinished` condition. - -Either disable `wait` or set a timeout for the service. - -
- -For more information about `ServiceFinished`, see [API ref]( -%doclink(api,conditions.service,ServiceFinished)). - -## Code explanation - -In this example, two conditionally executed services are illustrated. - -The service named `running_service` is executed -only if both `SimpleServices` in pre service group -have finished successfully. - -`never_running_service` is executed only if `running_service` is not finished, -which should never happen. - -Lastly, `context_printing_service` prints pipeline runtime information, -that contains execution state of all previously run services. -""" - - -# %% -class SimpleService(Service): - async def call(self, _: Context): - logger.info(f"Service '{self.name}' is running...") - - -class NeverRunningService(Service): - async def call(self, _: Context): - raise Exception(f"Oh no! The '{self.name}' service is running!") - - start_condition: AnyCondition = Not( - ServiceFinished(".post.named_group.running_service", wait=True) - ) - - -class RuntimeInfoPrintingService(Service): - async def call(self, _: Context): - logger.info( - f"Service '{self.name}' runtime execution info:" - f"{self.model_dump_json(indent=4)}" - ) - - -# %% -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, - pre_services=[ - SimpleService(), - # This service will be named "SimpleService#0" - SimpleService(), - # This service will be named "SimpleService#1" - ], - # this group is named "pre" - post_services=[ - ServiceGroup( - name="named_group", - components=[ - SimpleService( - start_condition=All( - ServiceFinished(".pre.SimpleService#0"), - ServiceFinished(".pre.SimpleService#1"), - ), - # Alternative: - # ServiceFinished(".pre") - name="running_service", - ), - # This simple service is named "running_service" - NeverRunningService(), - # this service will be named "NeverRunningService" - ], - fully_concurrent=True, - # Makes components in the group run asynchronously, - # unless one is waiting for another to complete, - # which is what happens with NeverRunningService. - ), - RuntimeInfoPrintingService(), - ], -) - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH[:1], printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.service.5_extra_handlers.py.txt b/_sources/tutorials/tutorials.service.5_extra_handlers.py.txt deleted file mode 100644 index 46227d351..000000000 --- a/_sources/tutorials/tutorials.service.5_extra_handlers.py.txt +++ /dev/null @@ -1,143 +0,0 @@ -# %% [markdown] -""" -# 5. Extra handlers - -This tutorial shows usage of extra handlers: -functions that run before/after components. - -For API ref, see: - -* %mddoclink(api,core.service.extra,BeforeHandler) -* %mddoclink(api,core.service.extra,AfterHandler). -""" - -# %pip install chatsky - -# %% -import asyncio -import json -import random -import logging -import sys -from importlib import reload -from datetime import datetime - -from chatsky.core.service import ( - ServiceGroup, - ExtraHandlerRuntimeInfo, -) -from chatsky import Context, Pipeline -from chatsky.utils.testing.common import ( - check_happy_path, - is_interactive_mode, -) -from chatsky.utils.testing.toy_script import HAPPY_PATH, TOY_SCRIPT_KWARGS - -reload(logging) -logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="") -logger = logging.getLogger(__name__) - -random.seed(0) - -# %% [markdown] -""" -## Intro - -Extra handlers are additional function -lists (before-functions and/or after-functions) -that can be added to any pipeline components (service and service groups). - -Extra handlers main purpose should be statistics collection. - -## Usage - -Extra handlers can be attached to pipeline component using -`before_handler` and `after_handler` constructor parameter. - -Extra handler callable signature can be one of the following: -`[ctx]` or `[ctx, info]`, where: - -* `ctx` - `Context` of the current dialog. -* `info` - Dictionary, containing information about current extra handler - and the pipeline component that called this. - - For example, `info.stage` will tell you if this Extra Handler is a - BeforeHandler or AfterHandler; `info.component.name` will give - you the component's name; `info.component.get_state(ctx)` will - return the component's execution state (which is `NOT_RUN` for - before handlers and `FINISHED` for after handlers). - -### Extra Handler configuration - -Instead of passing a list of functions as extra handler you can pass an instance -of either %mddoclink(api,core.service.extra,BeforeHandler) or -%mddoclink(api,core.service.extra,AfterHandler). - -This allows changing the `timeout` and `concurrent` options to change -the way extra handlers are executed. - -### Mass extra handler addition - -You can use %mddoclink(api,core.service.group,ServiceGroup.add_extra_handler) -to add a function as an extra handler to a service group and if you pass -a `condition` function it will also add the extra handler to all its -subcomponents that satisfy the condition function. - -## Code explanation - -Here 5 `heavy_service`s are run in a single concurrent service group. -Each of them sleeps for random a amount of seconds (between 0 and 0.05). - -To each of them (as well as the group) -time measurement extra handler is attached, -that writes execution time to `ctx.misc`. - -In the end `ctx.misc` is logged to info channel. -""" - - -# %% -def collect_timestamp_before(ctx: Context, info: ExtraHandlerRuntimeInfo): - ctx.misc.update({f"{info.component.path}": datetime.now()}) - - -def collect_timestamp_after(ctx: Context, info: ExtraHandlerRuntimeInfo): - ctx.misc.update( - { - f"{info.component.path}": datetime.now() - - ctx.misc[f"{info.component.path}"] - } - ) - - -async def heavy_service(_): - await asyncio.sleep(random.randint(0, 5) / 100) - - -def logging_service(ctx: Context): - logger.info(f"Context misc: {json.dumps(ctx.misc, indent=4, default=str)}") - - -# %% -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, - pre_services=ServiceGroup( - before_handler=[collect_timestamp_before], - after_handler=[collect_timestamp_after], - components=[ - { - "handler": heavy_service, - "before_handler": [collect_timestamp_before], - "after_handler": [collect_timestamp_after], - } - ] - * 5, - ), - post_services=[logging_service], -) - -# %% -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH[:1], printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.slots.1_basic_example.py.txt b/_sources/tutorials/tutorials.slots.1_basic_example.py.txt deleted file mode 100644 index dcddfbade..000000000 --- a/_sources/tutorials/tutorials.slots.1_basic_example.py.txt +++ /dev/null @@ -1,211 +0,0 @@ -# %% [markdown] -""" -# 1. Basic Example - -The following tutorial shows basic usage of slots extraction -module packaged with `chatsky`. -""" - -# %pip install chatsky - -# %% -from chatsky import ( - RESPONSE, - TRANSITIONS, - PRE_TRANSITION, - PRE_RESPONSE, - GLOBAL, - LOCAL, - Pipeline, - Transition as Tr, - conditions as cnd, - processing as proc, - responses as rsp, -) - -from chatsky.slots import RegexpSlot - -from chatsky.utils.testing import ( - check_happy_path, - is_interactive_mode, -) - -# %% [markdown] -""" -The slots fall into the following category groups: - -- Value slots can be used to extract slot values from user utterances. -- Group slots can be used to split value slots into groups - with an arbitrary level of nesting. - -You can build the slot tree by passing the child slot instances as extra fields -of the parent slot. In the following cell, we define two slot groups: - - Group 1: person.username, person.email - Group 2: friend.first_name, friend.last_name - -Currently there are two types of value slots: - -- %mddoclink(api,slots.slots,RegexpSlot): - Extracts slot values via regexp. -- %mddoclink(api,slots.slots,FunctionSlot): - Extracts slot values with the help of a user-defined function. -""" - -# %% -SLOTS = { - "person": { - "username": RegexpSlot( - regexp=r"username is ([a-zA-Z]+)", - match_group_idx=1, - ), - "email": RegexpSlot( - regexp=r"email is ([a-z@\.A-Z]+)", - match_group_idx=1, - ), - }, - "friend": { - "first_name": RegexpSlot(regexp=r"^[A-Z][a-z]+?(?= )"), - "last_name": RegexpSlot(regexp=r"(?<= )[A-Z][a-z]+"), - }, -} - -# %% [markdown] -""" -The slots module provides several functions for managing slots in-script: - -- %mddoclink(api,conditions.slots,SlotsExtracted): - Condition for checking if specified slots are extracted. -- %mddoclink(api,processing.slots,Extract): - A processing function that extracts specified slots. -- %mddoclink(api,processing.slots,Unset): - A processing function that marks specified slots as not extracted, - effectively resetting their state. -- %mddoclink(api,processing.slots,UnsetAll): - A processing function that marks all slots as not extracted. -- %mddoclink(api,processing.slots,FillTemplate): - A processing function that fills the `response` - Message text with extracted slot values. -- %mddoclink(api,responses.slots,FilledTemplate): - A response function that takes a Message with a - format-string text and returns Message - with its text string filled with extracted slot values. - -The usage of all the above functions is shown in the following script: -""" - -# %% -script = { - GLOBAL: { - TRANSITIONS: [ - Tr(dst=("username_flow", "ask"), cnd=cnd.Regexp(r"^[sS]tart")) - ] - }, - "username_flow": { - LOCAL: { - PRE_TRANSITION: {"get_slot": proc.Extract("person.username")}, - TRANSITIONS: [ - Tr( - dst=("email_flow", "ask"), - cnd=cnd.SlotsExtracted("person.username"), - priority=1.2, - ), - Tr(dst=("username_flow", "repeat_question"), priority=0.8), - ], - }, - "ask": { - RESPONSE: "Write your username (my username is ...):", - }, - "repeat_question": { - RESPONSE: "Please, type your username again (my username is ...):", - }, - }, - "email_flow": { - LOCAL: { - PRE_TRANSITION: {"get_slot": proc.Extract("person.email")}, - TRANSITIONS: [ - Tr( - dst=("friend_flow", "ask"), - cnd=cnd.SlotsExtracted("person.username", "person.email"), - priority=1.2, - ), - Tr(dst=("email_flow", "repeat_question"), priority=0.8), - ], - }, - "ask": { - RESPONSE: "Write your email (my email is ...):", - }, - "repeat_question": { - RESPONSE: "Please, write your email again (my email is ...):", - }, - }, - "friend_flow": { - LOCAL: { - PRE_TRANSITION: {"get_slots": proc.Extract("friend")}, - TRANSITIONS: [ - Tr( - dst=("root", "utter"), - cnd=cnd.SlotsExtracted( - "friend.first_name", "friend.last_name", mode="any" - ), - priority=1.2, - ), - Tr(dst=("friend_flow", "repeat_question"), priority=0.8), - ], - }, - "ask": {RESPONSE: "Please, name me one of your friends: (John Doe)"}, - "repeat_question": { - RESPONSE: "Please, name me one of your friends again: (John Doe)" - }, - }, - "root": { - "start": { - TRANSITIONS: [Tr(dst=("username_flow", "ask"))], - }, - "fallback": { - RESPONSE: "Finishing query", - TRANSITIONS: [Tr(dst=("username_flow", "ask"))], - }, - "utter": { - RESPONSE: rsp.FilledTemplate( - "Your friend is {friend.first_name} {friend.last_name}" - ), - TRANSITIONS: [Tr(dst=("root", "utter_alternative"))], - }, - "utter_alternative": { - RESPONSE: "Your username is {person.username}. " - "Your email is {person.email}.", - PRE_RESPONSE: {"fill": proc.FillTemplate()}, - }, - }, -} - -# %% -HAPPY_PATH = [ - ("hi", "Write your username (my username is ...):"), - ("my username is groot", "Write your email (my email is ...):"), - ( - "my email is groot@gmail.com", - "Please, name me one of your friends: (John Doe)", - ), - ("Bob Page", "Your friend is Bob Page"), - ("ok", "Your username is groot. Your email is groot@gmail.com."), - ("ok", "Finishing query"), -] - -# %% -pipeline = Pipeline( - script=script, - start_label=("root", "start"), - fallback_label=("root", "fallback"), - slots=SLOTS, -) - -if __name__ == "__main__": - check_happy_path( - pipeline, HAPPY_PATH, printout=True - ) # This is a function for automatic tutorial running - # (testing) with HAPPY_PATH - - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.stats.1_extractor_functions.py.txt b/_sources/tutorials/tutorials.stats.1_extractor_functions.py.txt deleted file mode 100644 index 8ce68358a..000000000 --- a/_sources/tutorials/tutorials.stats.1_extractor_functions.py.txt +++ /dev/null @@ -1,125 +0,0 @@ -# %% [markdown] -""" -# 1. Extractor Functions - -The following example covers the basics of using the `stats` module. - -Statistics are collected from pipeline services by extractor functions -that report the state of one or more pipeline components. The `stats` module -provides several default extractors, but users are free to define their own -extractor functions. You can find API reference for default extractors -[here](%doclink(api,stats.default_extractors)). - -It is a preferred practice to define extractors as asynchronous functions. -Extractors need to have the following uniform signature: -the expected arguments are always `Context`, and `ExtraHandlerRuntimeInfo`, -while the expected return value is an arbitrary `dict` or a `None`. -The returned value gets persisted to Clickhouse as JSON -which is why it can contain arbitrarily nested dictionaries, -but it cannot contain any complex objects that cannot be trivially serialized. - -The output of these functions will be captured by an OpenTelemetry -instrumentor and directed to the Opentelemetry collector server which -in its turn batches and persists data to Clickhouse or other OLAP storages. - -
- -Both the Opentelemetry collector and the Clickhouse instance must be running -during statistics collection. -If you cloned the Chatsky repo, launch them using `docker compose`: -```bash -docker compose --profile stats up -``` - -
- -For more information on OpenTelemetry instrumentation, -refer to the body of this tutorial as well as [OpenTelemetry documentation]( -https://opentelemetry.io/docs/instrumentation/python/manual/ -). - -""" - -# %pip install chatsky[stats] - -# %% -import asyncio - -from chatsky.core.service import ( - ExtraHandlerRuntimeInfo, - ExtraHandlerType, - to_service, -) -from chatsky import Context, Pipeline -from chatsky.stats import OtelInstrumentor, default_extractors -from chatsky.utils.testing import is_interactive_mode, check_happy_path -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - -# %% [markdown] -""" -The cells below configure log export with the help of OTLP instrumentation. - -* The initial step is to configure the export destination. -`from_url` method of the `OtelInstrumentor` class simplifies this task -allowing you to only pass the url of the OTLP Collector server. - -* Alternatively, you can use the utility functions -provided by the `stats` module: -`set_logger_destination`, `set_tracer_destination`, or `set_meter_destination`. -These accept an appropriate Opentelemetry exporter instance -and bind it to provider classes. - -* Nextly, the `OtelInstrumentor` class should be constructed to log the output -of extractor functions. Custom extractors need to be decorated -with the `OtelInstrumentor` instance. Default extractors are instrumented -by calling the `instrument` method. - -* The entirety of the process is illustrated in the example below. - -""" - - -# %% -chatsky_instrumentor = OtelInstrumentor.from_url("grpc://localhost:4317") -chatsky_instrumentor.instrument() - -# %% [markdown] -""" -The following cell shows a custom extractor function. The data obtained from -the context and the runtime information gets shaped as a dict and returned -from the function body. The `chatsky_instrumentor` decorator then ensures -that the output is logged by OpenTelemetry. - -""" - - -# %% -# decorated by an OTLP Instrumentor instance -@chatsky_instrumentor -async def get_service_state(ctx: Context, info: ExtraHandlerRuntimeInfo): - # extract the execution state of a target service - data = { - "execution_state": info.component.get_state(ctx), - } - # return the state as an arbitrary dict for further logging - return data - - -# %% -# configure `get_service_state` to run after the `heavy_service` -@to_service(after_handler=[get_service_state]) -async def heavy_service(ctx: Context): - _ = ctx # get something from ctx if needed - await asyncio.sleep(0.02) - - -# %% -pipeline = Pipeline(**TOY_SCRIPT_KWARGS, pre_services=[heavy_service]) - -pipeline.actor.add_extra_handler( - ExtraHandlerType.BEFORE, default_extractors.get_current_label -) -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/tutorials/tutorials.stats.2_pipeline_integration.py.txt b/_sources/tutorials/tutorials.stats.2_pipeline_integration.py.txt deleted file mode 100644 index 85c54ebf5..000000000 --- a/_sources/tutorials/tutorials.stats.2_pipeline_integration.py.txt +++ /dev/null @@ -1,135 +0,0 @@ -# %% [markdown] -""" -# 2. Pipeline Integration - -In the Chatsky ecosystem, extractor functions act as regular extra handlers ( -[see the extra handlers tutorial]( -%doclink(tutorial,service.5_extra_handlers) -) -). -Hence, you can decorate any part of your pipeline, including services, -service groups and the pipeline as a whole, to obtain the statistics -specific for that component. Some examples of this functionality -are showcased in this tutorial. - -
- -Both the Opentelemetry collector and the Clickhouse instance must be running -during statistics collection. -If you cloned the Chatsky repo, launch them using `docker compose`: -```bash -docker compose --profile stats up -``` - -
-""" - -# %pip install chatsky[stats] - -# %% -import asyncio - -from chatsky.core.service import ( - ExtraHandlerRuntimeInfo, - ServiceGroup, - ExtraHandlerType, -) -from chatsky import Context, Pipeline -from chatsky.stats import OTLPLogExporter, OTLPSpanExporter -from chatsky.stats import ( - OtelInstrumentor, - set_logger_destination, - set_tracer_destination, -) -from chatsky.stats import default_extractors -from chatsky.utils.testing import is_interactive_mode, check_happy_path -from chatsky.utils.testing.toy_script import TOY_SCRIPT_KWARGS, HAPPY_PATH - -# %% -set_logger_destination(OTLPLogExporter("grpc://localhost:4317", insecure=True)) -set_tracer_destination(OTLPSpanExporter("grpc://localhost:4317", insecure=True)) -chatsky_instrumentor = OtelInstrumentor() -chatsky_instrumentor.instrument() - - -# example extractor function -@chatsky_instrumentor -async def get_service_state(ctx: Context, info: ExtraHandlerRuntimeInfo): - # extract execution state of service from info - data = { - "execution_state": info.component.get_state(ctx), - } - # return a record to save into connected database - return data - - -# %% -# example service -async def heavy_service(ctx: Context): - _ = ctx # get something from ctx if needed - await asyncio.sleep(0.02) - - -# %% [markdown] -""" - -The many ways in which you can use extractor functions are shown in -the following pipeline definition. The functions are used to obtain -statistics from respective components: - -* A service group of two `heavy_service` instances. -* An `Actor` service. -* The pipeline as a whole. - -As is the case with the regular extra handler functions, -you can wire the extractors to run either before or after the target service. -As a result, you can compare the pre-service and post-service states -of the context to measure the performance of various components, etc. - -Some extractors, like `get_current_label`, have restrictions in terms of their -run stage: for instance, `get_current_label` needs to only be used as an -`after_handler` to function correctly. - -""" -# %% -pipeline = Pipeline( - **TOY_SCRIPT_KWARGS, - pre_services=ServiceGroup( - before_handler=[default_extractors.get_timing_before], - after_handler=[ - get_service_state, - default_extractors.get_timing_after, - ], - components=[ - heavy_service, - heavy_service, - ], - ), -) -# These are Extra Handlers for Actor. -pipeline.actor.add_extra_handler( - ExtraHandlerType.BEFORE, default_extractors.get_timing_before -) -pipeline.actor.add_extra_handler(ExtraHandlerType.AFTER, get_service_state) -pipeline.actor.add_extra_handler( - ExtraHandlerType.AFTER, default_extractors.get_current_label -) -pipeline.actor.add_extra_handler( - ExtraHandlerType.AFTER, default_extractors.get_timing_after -) - -# These are global Extra Handlers for Pipeline service -pipeline.services_pipeline.add_extra_handler( - ExtraHandlerType.BEFORE, default_extractors.get_timing_before -) -pipeline.services_pipeline.add_extra_handler( - ExtraHandlerType.AFTER, default_extractors.get_timing_after -) -pipeline.services_pipeline.add_extra_handler( - ExtraHandlerType.AFTER, get_service_state -) - -if __name__ == "__main__": - check_happy_path(pipeline, HAPPY_PATH, printout=True) - if is_interactive_mode(): - pipeline.run() diff --git a/_sources/user_guides.rst.txt b/_sources/user_guides.rst.txt deleted file mode 100644 index b8dbc376d..000000000 --- a/_sources/user_guides.rst.txt +++ /dev/null @@ -1,55 +0,0 @@ -User guides ------------ - -:doc:`Basic concepts <./user_guides/basic_conceptions>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the ``basic concepts`` guide the basics of Chatsky are described, -those include but are not limited to: dialog graph creation, specifying start and fallback nodes, -setting transitions and conditions, using ``Context`` object in order to receive information -about current script execution. - -:doc:`Slot extraction <./user_guides/slot_extraction>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``slot extraction`` guide demonstrates the slot extraction functionality -currently integrated in the library. Chatsky only provides basic building blocks for this task, -which can be trivially extended to support any NLU engine or slot extraction model -of your liking. - -:doc:`Context guide <./user_guides/context_guide>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``context guide`` walks you through the details of working with the -``Context`` object, the backbone of the Chatsky API, including most of the relevant fields and methods. - -:doc:`Superset guide <./user_guides/superset_guide>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``superset guide`` highlights the usage of Superset visualization tool -for exploring the telemetry data collected from your conversational services. -We show how to plug in the telemetry collection and configure the pre-built -Superset dashboard shipped with Chatsky. - -:doc:`Optimization guide <./user_guides/optimization_guide>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``optimization guide`` demonstrates various tools provided by the library -that you can use to profile your conversational service, -and to locate and remove performance bottlenecks. - -:doc:`YAML import guide <./user_guides/pipeline_import>` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``yaml import guide`` shows another option for initializing ``Pipeline`` -objects -- from yaml or json files. - -.. toctree:: - :hidden: - - user_guides/basic_conceptions - user_guides/slot_extraction - user_guides/context_guide - user_guides/superset_guide - user_guides/optimization_guide - user_guides/pipeline_import diff --git a/_sources/user_guides/basic_conceptions.rst.txt b/_sources/user_guides/basic_conceptions.rst.txt deleted file mode 100644 index 1259b6fb0..000000000 --- a/_sources/user_guides/basic_conceptions.rst.txt +++ /dev/null @@ -1,402 +0,0 @@ -Basic Concepts --------------- - -Introduction -~~~~~~~~~~~~ - -Chatsky is a modern tool for designing conversational services. - -Chatsky introduces a specialized Domain-Specific Language (DSL) based on standard Python functions and data structures -which makes it very easy for developers with any level of expertise to design a script for user - bot interaction. -The script comes in a form of a *dialog graph* where -each node equals a specific state of the dialog, i.e. a specific conversation turn. -The graph includes the majority of the conversation logic, and covers one or several user scenarios, all in a single Python dict. - -In this tutorial, we describe the basics of Chatsky API, -and walk you through the process of creating and maintaining a conversational service with the help of Chatsky. - - -Creating Conversational Services with Chatsky -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Installation -============ - -To get started with Chatsky, you need to install its core dependencies, which can be done using the following command: - -.. code-block:: shell - - pip3 install chatsky - -Defining Dialogue Goals and User Scenarios -========================================== - -To create a conversational service using Chatsky, you start by defining the overall dialogue goal -and breaking down the dialogue into smaller scenarios based on the user intents or actions that you want to cover. -Chatsky's Domain-Specific Language makes it easy to break down the dialog script into `flows`, i.e. named groups of nodes -unified by a specific purpose. - -For instance, if one of the dialog options that we provide to the user is to play a game, -the bot can have a 'game' flow that contains dialog states related to this subject, while other flows -cover other topics, e.g. 'time' flow can include questions and answers related to telling the time, -'weather' to telling the weather, etc. - -Creating Dialogue Flows for User Scenarios -========================================== - -Once you have Chatsky installed, you can define dialog flows targeting various user scenarios -and combine them in a global script object. A flow consists of one or more nodes -that represent conversation turns. - -.. note:: - - In other words, the script object has 3 levels of nestedness: - **script - flow - node** - -Let's assume that the only user scenario of the service is the chat bot playing ping pong with the user. -The practical implementation of this is that the bot is supposed to reply 'pong' to messages that say 'ping' -and handle any other messages as exceptions. The pseudo-code for the said flow would be as follows: - -.. code-block:: text - - 1. User starts a conversation - 2. Respond with "Hi!" - - If user writes "Hello!": - 3. Respond with "Let's play ping-pong!" - - If user afterwards writes "Ping" or "ping" or "Ping!" or "ping!": - 4. Respond with "Pong!" - Repeat this behaviour - - If user writes something else: - 5. Respond with "That was against the rules" - Go to responding with "2" after user replies - -This leaves us with a single dialog flow in the dialog graph that we lay down below, with the annotations for -each part of the graph available under the code snippet. - -Example flow & script -===================== - -.. code-block:: python - :linenos: - - from chatsky import Pipeline, TRANSITIONS, RESPONSE, Transition as Tr - import chatsky.conditions as cnd - import chatsky.destinations as dst - - ping_pong_script = { - "greeting_flow": { - "start_node": { - TRANSITIONS: [Tr(dst="greeting_node", cnd=cnd.ExactMatch("/start"))] - # start node handles the initial handshake (command /start) - }, - "greeting_node": { - RESPONSE: "Hi!", - TRANSITIONS: [ - Tr( - dst=("ping_pong_flow", "game_start_node"), - cnd=cnd.ExactMatch("Hello!") - ) - ] - }, - "fallback_node": { - RESPONSE: "That was against the rules", - TRANSITIONS: [Tr(dst="greeting_node")], - # this transition is unconditional - }, - }, - "ping_pong_flow": { - "game_start_node": { - RESPONSE: "Let's play ping-pong!", - TRANSITIONS: [Tr(dst="response_node", cnd=cnd.ExactMatch("Ping!"))], - }, - "response_node": { - RESPONSE: "Pong!", - TRANSITIONS: [Tr(dst=dst.Current(), cnd=cnd.ExactMatch("Ping!"))], - }, - }, - } - - pipeline = Pipeline( - ping_pong_script, - start_label=("greeting_flow", "start_node"), - fallback_label=("greeting_flow", "fallback_node"), - ) - - if __name__ == "__main__": - pipeline.run() - -An example chat with this bot: - -.. code-block:: - - request: /start - response: text='Hi!' - request: Hello! - response: text='Let's play ping-pong!' - request: Ping! - response: text='Pong!' - request: Bye - response: text='That was against the rules' - -The order of request processing is, essentially: - -1. Obtain user request -2. Travel to the next node (chosen based on transitions of the current node) -3. Send the response of the new node - -Below is a breakdown of key features used in the example: - -* ``ping_pong_script``: The dialog **script** mentioned above is a dictionary that has one or more - dialog flows as its values. - -* ``ping_pong_flow`` is the game emulation flow; it contains linked - conversation nodes and possibly some extra data, transitions, etc. - -* A node object is an atomic part of the script. - The required fields of a node object are ``RESPONSE`` and ``TRANSITIONS``. - -* The ``RESPONSE`` field specifies the response that the dialog agent gives to the user in the current turn. - -* The ``TRANSITIONS`` field specifies the edges of the dialog graph that link the dialog states. - This is a list of ``Transition`` instances. They specify the destination node of the potential transition - and a condition for the transition to be valid. - In the example script, we use build-in functions: ``ExactMatch`` requires the user request to - fully match the provided text, while ``Current`` makes a transition to the current node. - However, passing custom callbacks that implement arbitrary logic is also an option. - -* ``start_node`` is the initial node, which contains an empty response and only transfers user to another node - according to the first message user sends. - It transfers user to ``greeting_node`` if user writes text message exactly equal to "Hello!". - -* ``greeting_node`` is the node that will greet user and propose him a ping-pong game. - It transfers user to ``response_node`` if user writes text message exactly equal to "Ping!". - -* ``response_node`` is the node that will play ping-pong game with the user. - It transfers user to ``response_node`` if user writes text message exactly equal to "Ping!". - -* ``fallback_node`` is an "exception handling node"; user will be transferred here if - none of the transition conditions (see ``TRANSITIONS``) is satisfied. - It transfers user to ``greeting_node`` no matter what user writes. - -* ``pipeline`` is a special object that traverses the script graph based on the values of user input. - It is also capable of executing custom actions that you want to run on every turn of the conversation. - The pipeline can be initialized with a script, and with labels of two nodes: - the entrypoint of the graph, aka the 'start node', and the 'fallback node' - (if not provided it defaults to 'start node'). - -.. note:: - - See `tutorial on basic dialog structure <../tutorials/tutorials.script.core.1_basics.html>`_. - -Processing Definition -===================== - -.. note:: - - The topic of this section is explained in greater detail in the following tutorials: - - * `Pre-response processing <../tutorials/tutorials.script.core.7_pre_response_processing.html>`_ - * `Pre-transition processing <../tutorials/tutorials.script.core.9_pre_transition_processing.html>`_ - * `Pipeline processors <../tutorials/tutorials.pipeline.2_pre_and_post_processors.html>`_ - -Processing user requests and extracting additional parameters is a crucial part of building a conversational bot. -Chatsky allows you to define how user requests will be processed to extract additional parameters. -This is done by passing callbacks to a special ``PROCESSING`` fields in a Node dict. - -* ``PRE_RESPONSE`` will happen **after** a transition has been made but **before** response generation. See `tutorial on pre-response processing`_. -* ``PRE_TRANSITION`` will happen **after** obtaining user request but **before** transition to the next node. See `tutorial on pre-transition processing`_. - -Depending on the requirements of your bot and the dialog goal, you may need to interact with external databases or APIs to retrieve data. -For instance, if a user wants to know a schedule, you may need to access a database and extract parameters such as date and location. - -.. code-block:: python - - import requests - from chatsky import BaseProcessing, PRE_TRANSITION - ... - class UseAPI(BaseProcessing): - async def call(self, ctx): - # save to the context field for custom info - ctx.misc["api_call_results"] = requests.get("http://schedule.api/day1").json() - ... - node = { - RESPONSE: ... - TRANSITIONS: ... - PRE_TRANSITION: {"use_api": UseAPI()} - } - -.. note:: - - This function uses ``Context`` to store the result of a request for other functions to use. - Context is a data structure that keeps all the information about a specific conversation. - - To learn more about ``Context`` see the `relevant guide <../user_guides/context_guide.html>`__. - -If you retrieve data from the database or API, it's important to validate it to ensure it meets expectations. - -Generating a bot Response -========================= - -Response is defined in the ``RESPONSE`` section of each node and should be either a ``Message`` object, -that can contain text, images, audios, attachments, etc., or a callback that returns a ``Message``. -The latter allows you to customize the response based on the specific scenario and user input. - -.. note:: - - ``Message`` object can be instantiated from a string (filling its ``text`` field). - We've used this feature for ``RESPONSE`` and will use it now. - -.. code-block:: python - - class MyResponse(BaseResponse): - async def call(self, ctx): - if ctx.misc["user"] == 'vegan': - return "Here is a list of vegan cafes." - return "Here is a list of cafes." - - -For more information on responses, see the `tutorial on response functions`_. - -Handling Fallbacks -================== - -In Chatsky, you should provide handling for situations where the user makes requests -that do not trigger any of the transitions specified in the script graph. -To cover that use case, Chatsky requires you to define a fallback node that the agent will move to -when no adequate transition has been found. - -Like other nodes, the fallback node can either use a message or a callback to produce a response -which gives you a lot of freedom in creating situationally appropriate error messages. -Create friendly error messages and, if possible, suggest alternative options. -This ensures a smoother user experience even when the bot encounters unexpected inputs. - -.. code-block:: python - - class MyResponse(BaseResponse): - """ - Generate a special fallback response depending on the situation. - """ - async def call(self, ctx): - if ctx.last_label == ctx.pipeline.start_label and ctx.last_request.text != "/start": - # start_label can be obtained from the pipeline instance stored inside context - return "You should've started the dialog with '/start'" - else: - return ( - f"That was against the rules!\n" - f"You should've written 'Ping', not '{ctx.last_request.text}'!" - ) - -Testing and Debugging -~~~~~~~~~~~~~~~~~~~~~ - -Periodically testing the conversational service is crucial to ensure it works correctly. -You should also be prepared to debug the code and dialogue logic if problems are discovered during testing. -Thorough testing helps identify and resolve any potential problems in the conversation flow. - -The basic testing procedure offered by Chatsky is end-to-end testing of the pipeline and the script -which ensures that the pipeline yields correct responses for any given input. -It requires a sequence of user request - bot response pairs that form the happy path of your -conversational service. - -.. code-block:: python - - happy_path = ( - ("/start", "Hi!"), - ("Hello!", "Let's play ping-pong!"), - ("Ping!", "Pong!") - ) - -A special function is then used to ascertain complete identity of the messages taken from -the happy path and the pipeline. The function will play out a dialog with the pipeline acting as a user while checking returned messages. - -.. code-block:: python - - from chatsky.utils.testing.common import check_happy_path - - check_happy_path(pipeline, happy_path) - -Monitoring and Analytics -~~~~~~~~~~~~~~~~~~~~~~~~ - -Setting up bot performance monitoring and usage analytics is essential to monitor its operation and identify potential issues. -Monitoring helps you understand how users are interacting with the bot and whether any improvements are needed. -Analytics data can provide valuable insights for refining the bot's behavior and responses. - -Chatsky provides a `statistics` module as an out-of-the-box solution for collecting arbitrary statistical metrics -from your service. Setting up the data collection is as easy as instantiating the relevant class in the same -context with the pipeline. -What's more, the data you obtain can be visualized right away using Apache Superset as a charting engine. - -.. note:: - - More information is available in the respective `guide <../user_guides/superset_guide.html>`__. - -Iterative Improvement -~~~~~~~~~~~~~~~~~~~~~ - -To continually enhance your chat-bot's performance, monitor user feedback and analyze data on bot usage. -For instance, the statistics or the charts may reveal that some flow is visited by users more frequently or -less frequently than planned. This would mean that adjustments to the transition structure -of the graph need to be made. - -Gradually improve the transition logic and response content based on the data received. -This iterative approach ensures that the bot becomes more effective over time. - -Data Protection -~~~~~~~~~~~~~~~ - -Data protection is a critical consideration in bot development, especially when handling sensitive information. - -.. note:: - - The Chatsky framework helps ensure the safety of your application by storing the history and other user data present - in the ``Context`` object under unique ids and abstracting the storage logic away from the user interface. - As a result, it offers the basic level of data protection making it impossible to gain unlawful access to personal information. - -Documentation -~~~~~~~~~~~~~ - -Creating documentation is essential for teamwork and future bot maintenance. -Document how different parts of the script work and how the bot covers the expected interaction scenarios. -It is especially important to document the purpose and functionality of callback functions and pipeline services -that you may have in your project, using Python docstrings. - -.. code-block:: python - - class FavCuisineResponse(BaseResponse): - """ - This function returns a user-targeted response depending on the value - of the 'cuisine preference' slot. - """ - ... - -This documentation serves as a reference for developers involved in the project. - -Scaling -~~~~~~~ - -If your bot becomes popular and requires scaling, consider scalability during development. -Scalability ensures that the bot can handle a growing user base without performance issues. -While having only one application instance will suffice in most cases, there are many ways -how you can adapt the application to a high load environment. - -* With the database connection support that Chatsky offers out of the box, Chatsky projects can be easily scaled through sharing the same database between multiple application instances. However, using an external database is required due to the fact that this is the only kind of storage that can be efficiently shared between processes. -* Likewise, using multiple database instances to ensure the availability of data is also an option. -* The structure of the `Context` object makes it easy to vertically partition the data storing different subsets of data across multiple database instances. - -Further reading -~~~~~~~~~~~~~~~ - -* `Tutorial on basic dialog structure <../tutorials/tutorials.script.core.1_basics.html>`_ -* `Tutorial on transitions <../tutorials/tutorials.script.core.4_transitions.html>`_ -* `Tutorial on conditions <../tutorials/tutorials.script.core.2_conditions.html>`_ -* `Tutorial on response functions <../tutorials/tutorials.script.core.3_responses.html>`_ -* `Tutorial on pre-response processing <../tutorials/tutorials.script.core.7_pre_response_processing.html>`_ -* `Tutorial on pre-transition processing <../tutorials/tutorials.script.core.9_pre_transition_processing.html>`_ -* `Guide on Context <../user_guides/context_guide.html>`_ -* `Tutorial on global and local nodes <../tutorials/tutorials.script.core.5_global_local.html>`_ -* `Tutorial on context serialization <../tutorials/tutorials.script.core.6_context_serialization.html>`_ -* `Tutorial on script MISC <../tutorials/tutorials.script.core.8_misc.html>`_ diff --git a/_sources/user_guides/context_guide.rst.txt b/_sources/user_guides/context_guide.rst.txt deleted file mode 100644 index 5c57edbd3..000000000 --- a/_sources/user_guides/context_guide.rst.txt +++ /dev/null @@ -1,233 +0,0 @@ -Context guide --------------- - -Introduction -~~~~~~~~~~~~ - -The ``Context`` class is a backbone component of the Chatsky API. -Like the name suggests, this data structure is used to store information -about the current state, or context, of a particular conversation. -Each individual user has their own ``Context`` instance and can be identified by it. - -``Context`` is used to keep track of the user's requests, bot's replies, -user-related and request-related annotations, and any other information -that is relevant to the conversation with the user. - -.. note:: - - Since most callback functions used in Chatsky script and Chatsky pipeline (see the `basic guide <./basic_conceptions.rst>`__) - need to either read or update the current dialog state, - the framework-level convention is that all functions of this kind - use ``Context`` as their first parameter. This dependency is being - injected by the pipeline during its run. - Thus, understanding the ``Context`` class is essential for developing custom conversation logic - which is mostly made up by the said functions. - -As a callback parameter, ``Context`` provides a convenient interface for working with data, -allowing developers to easily add, retrieve, -and manipulate data as the conversation progresses. - -Let's consider some of the built-in callback instances to see how the context can be leveraged: - -.. code-block:: python - :linenos: - - class Regexp(BaseCondition): - pattern: str - - @cached_property - def re_object(self) -> Pattern: - return re.compile(self.pattern) - - async def call(self, ctx: Context) -> bool: - request = ctx.last_request - if request.text is None: - return False - return bool(self.re_object.search(request.text)) - -The code above is a condition function (see the `conditions tutorial <../tutorials/tutorials.script.core.2_conditions.py>`__) -that belongs to the ``TRANSITIONS`` section of the script and returns `True` or `False` -depending on whether the current user request matches the given pattern. - -As can be seen from the code block, the current -request (``last_request``) can be retrieved as one of the attributes of the ``Context`` object. -Likewise, the ``last_response`` (bot's current reply) or the ``last_label`` -(the name of the current node) attributes can be used in the same manner. - -Another common use case is leveraging the ``misc`` field (see below for a detailed description): -pipeline functions or ``PROCESSING`` callbacks can write arbitrary values to the misc field, -making those available for other context-dependent functions. - -.. code-block:: python - :linenos: - - import urllib.request - import urllib.error - - class PingExample(BaseProcessing): - async def call(self, ctx): - try: - with urllib.request.urlopen("https://example.com/") as webpage: - web_content = webpage.read().decode( - webpage.headers.get_content_charset() - ) - result = "Example Domain" in web_content - except urllib.error.URLError: - result = False - ctx.misc["can_ping_example_com"] = result - -.. - todo: link to the user defined functions tutorial - - .. note:: - For more information about user-defined functions see the `user functions guide <./user_functions.rst>`__. - -API -~~~ - -This sections describes the API of the ``Context`` class. - -For more information, such as method signatures, see -`API reference <../apiref/chatsky.core.context.html#chatsky.core.context.Context>`__. - -Attributes -========== - -* **id**: This attribute represents the unique context identifier. By default, it is randomly generated using uuid4. - In most cases, this attribute will be used to identify a user. - -* **labels**: The labels attribute stores the history of all passed labels within the conversation. - It maps turn IDs to labels. The collection is ordered, so getting the last item of the mapping - always shows the last visited node. - - Note that `labels` only stores the nodes that were transitioned to - so `start_label` will not be in this attribute. - -* **requests**: The requests attribute maintains the history of all received requests by the agent. - It also maps turn IDs to requests. Like labels, it stores the requests in-order. - -* **responses**: This attribute keeps a record of all agent responses, mapping turn IDs to responses. - Stores the responses in-order. - -* **misc**: The misc attribute is a dictionary for storing custom data. This field is not used by any of the - built-in Chatsky classes or functions, so the values that you write there are guaranteed to persist - throughout the lifetime of the ``Context`` object. - -* **framework_data**: This attribute is used for storing custom data required for pipeline execution. - It is meant to be used by the framework only. Accessing it may result in pipeline breakage. - But there are some methods that provide access to specific fields of framework data. - These methods are described in the next section. - -Methods -======= - -The methods of the ``Context`` class can be divided into two categories: - -* Public methods that get called manually in custom callbacks and in functions that depend on the context. -* Methods that are not designed for manual calls and get called automatically during pipeline runs, - i.e. quasi-private methods. You may still need them when developing extensions or heavily modifying Chatsky. - -Public methods -^^^^^^^^^^^^^^ - -* **last_request**: Return the last request of the context. - -* **last_response**: Return the last response of the context, or `None` if the ``responses`` field is empty. - - Responses are added at the end of each turn, so an empty ``response`` field is something you should definitely consider. - -* **last_label**: Return the last node label of the context (i.e. name of the current node). - -* **clear**: Clear all items from context fields, optionally keeping the data from `hold_last_n_indices` turns. - You can specify which fields to clear using the `field_names` parameter. This method is designed for cases - when contexts are shared over high latency networks. - -* **current_node**: Return the current node of the context. - Use this property to access properties of the current node. - You can safely modify properties of this. The changes will be reflected in - bot behaviour during this turn, bot are not permanent (the node stored inside the script is not changed). - - .. note:: - - See the `preprocessing tutorial <../tutorials/tutorials.script.core.7_pre_response_processing.py>`__. - -* **pipeline**: Return ``Pipeline`` object that is used to process this context. - This can be used to get ``Script``, ``start_label`` or ``fallback_label``. - -Private methods -^^^^^^^^^^^^^^^ - -These methods should not be used outside of the internal workings. - -* **set_last_response** -* **set_last_request** -* **add_request** -* **add_response** -* **add_label** - -Context storages -~~~~~~~~~~~~~~~~ - -Since context instances contain all the information, relevant for a particular user, there needs to be a way -to persistently store that information and to make it accessible in different user sessions. -This functionality is implemented by the ``context storages`` module that provides -the uniform ``DBContextStorage`` interface as well as child classes thereof that integrate -various database types (see the -`api reference <../apiref/chatsky.context_storages.database.html#chatsky.context_storages.database.DBContextStorage>`_). - -The supported storage options are as follows: - -* `JSON `_ -* `pickle `_ -* `shelve `_ -* `SQLite `_ -* `PostgreSQL `_ -* `MySQL `_ -* `MongoDB `_ -* `Redis `_ -* `Yandex DataBase `_ - -``DBContextStorage`` instances can be uniformly constructed using the ``context_storage_factory`` function. -The function's only parameter is a connection string that specifies both the database type -and the connection parameters, for example, *mongodb://admin:pass@localhost:27016/admin*. -(`see the reference <../apiref/chatsky.context_storages.database.html#chatsky.context_storages.database.context_storage_factory>`_) - -.. note:: - To learn how to use ``context_storage_factory`` in your pipeline, see our `Context Storage Tutorials <../tutorials/index_context_storages.html>`__. - -The GitHub-based distribution of Chatsky includes Docker images for each of the supported database types. -Therefore, the easiest way to deploy your service together with a database is to clone the GitHub -distribution and to take advantage of the packaged -`docker compose file `_. - -.. code-block:: shell - :linenos: - - git clone https://github.com/deeppavlov/chatsky.git - cd chatsky - # assuming we need to deploy mongodb - docker compose up mongo - -The images can be configured using the docker compose file or the -`environment file `_, -also available in the distribution. Consult these files for more options. - -.. warning:: - - The data transmission protocols require the data to be JSON-serializable. Chatsky tackles this problem - through utilization of ``pydantic`` as described in the next section. - -Serialization -~~~~~~~~~~~~~ - -The fact that the ``Context`` class is a Pydantic model makes it easily convertible to other data formats, -such as JSON. For instance, as a developer, you don't need to implement instructions on how datetime fields -need to be marshalled, since this functionality is provided by Pydantic out of the box. -As a result, working with web interfaces and databases that require the transmitted data to be serialized -becomes as easy as calling the `model_dump_json` method: - -.. code-block:: python - - serialized_context = context.model_dump_json() - -Knowing that, you can easily extend Chatsky to work with storages like Memcache or web APIs of your liking. \ No newline at end of file diff --git a/_sources/user_guides/optimization_guide.rst.txt b/_sources/user_guides/optimization_guide.rst.txt deleted file mode 100644 index 5d4b3f625..000000000 --- a/_sources/user_guides/optimization_guide.rst.txt +++ /dev/null @@ -1,104 +0,0 @@ -Optimization Guide ------------------- - -Introduction -~~~~~~~~~~~~ - -When optimizing a dialog service to provide the best possible user experience, -it's essential to identify and address performance issues. -Similar to any complex system, a dialog service can have performance bottlenecks at various levels. -These bottlenecks can occur during I/O operations like receiving and sending messages, -as well as when synchronizing service states with a database. -As the number of callbacks in the script and pipeline increases, -the performance of Chatsky classes can degrade leading to longer response time. - -As a result, it becomes necessary to locate the part of the pipeline that is causing issues, so that -further optimization steps can be taken. Chatsky provides several tools that address the need for -profiling individual system components. This guide will walk you through the process -of using these tools in practice and optimizing the profiled application. - -Profiling with Locust testing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -`Locust `__ is a tool for load testing web applications that -simultaneously spawns several user agents that execute a pre-determined behavior -against the target application. Assuming that your pipeline is integrated into a web -server application, like Flask or FastAPI, that is not strongly impacted by the load, -the load testing reveals how well your pipeline would scale to a highly loaded environment. -Using this approach, you can also measure the scalability of each component in your pipeline, -if you take advantage of the Opentelemetry package bundled with the library (`stats` extra required) -as described below. - -Since Locust testing can only target web apps, -this approach only applies if you integrate your dialog pipeline into a web application. -The `FastAPI integration tutorial <../tutorials/tutorials.messengers.web_api_interface.1_fastapi.py>`_ -shows the most straightforward way to do this. -At this stage, you will also need to instrument the pipeline components that you want to additionally profile -using `extractor functions`. Put simply, you are decorating the components of the pipeline -with functions that can report their performance, e.g. their execution time or the CPU load. - -.. note:: - - You can get more info on how instrumentation is done and statistics are collected - in the `stats tutorial <../tutorials/tutorials.stats.1_extractor_functions.py>`__. - -When you are done setting up the instrumentation, you can launch the web server to accept connections from locust. - -The final step is to run a Locust file which will result in artificial load traffic being generated and sent to your server. -A Locust file is a script that implements the behavior of artificial users, -i.e. the requests to the server that will be made during testing. - -.. note:: - - An example Locust script along with instructions on how to run it can be found in the - `load testing tutorial <../tutorials/tutorials.messengers.web_api_interface.3_load_testing_with_locust.py>`_. - The simplest way, however, is to pass a locust file to the Python interpreter. - -Once Locust is running, you can access its GUI, where you can set the number of users to emulate. -After configuring this parameter, the active phase of testing will begin, -and the results will become accessible on an interactive dashboard. -These reported values include timing data, such as the average response time of your service, -allowing you to assess the performance's reasonableness and impact on user experience. - -The data provided by extractor functions will be available in the Clickhouse database; -you can view it using the Superset dashboard (see `instructions <./superset_guide.html>`__) -or analyze it with your own queries using the Clickhouse client. - -Profiling context storages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Benchmarking the performance of context storage is crucial to understanding -how different storage methods impact your dialog service's efficiency. -This process involves running tests to measure the speed and reliability of various context storage solutions. -Given the exact configuration of your system, one or the other database type may be performing more efficiently, -so you may prefer to change your database depending on the testing results. - -.. note:: - The exact instructions of how the testing can be carried out are available in the - `DB benchmarking tutorial <../tutorials/tutorials.context_storages.8_db_benchmarking.py>`__. - -Optimization techniques -~~~~~~~~~~~~~~~~~~~~~~~ - -Aside from choosing an appropriate database type, there exists a number of other recommendations -that may help you improve the efficiency of your service. - -* Firstly, follow the DRY principle not only with regard to your code, but also with regard to - computational operations. In other words, you have to make sure that your callback functions work only once - during a dialog turn and only when needed. E.g. you can take note of the `conditions` api available as a part - of the `Pipeline` module: while normally a pipeline service runs every turn, you can restrict it - to only run on turns when a particular condition is satisfied, greatly reducing - the number of performed actions (see the - `Groups and Conditions tutorial <../tutorials/tutorials.pipeline.4_groups_and_conditions_full.py>`__). - -* Using caching for resource-consuming callbacks and actions may also prove to be a helpful strategy. - In this manner, you can improve the computational efficiency of your pipeline, - while making very few changes to the code itself. - -* Finally, be mindful about the use of computationally expensive algorithms, like NLU classifiers - or LLM-based generative networks, since those require a great deal of time and resources - to produce an answer. In case you need to use one, take full advantage of caching along with - other means to relieve the computational load imposed by neural networks such as message queueing. - -.. - todo: add a link to a user guide about using message queueing. diff --git a/_sources/user_guides/pipeline_import.rst.txt b/_sources/user_guides/pipeline_import.rst.txt deleted file mode 100644 index 99511d0e9..000000000 --- a/_sources/user_guides/pipeline_import.rst.txt +++ /dev/null @@ -1,179 +0,0 @@ -Pipeline YAML import guide --------------------------- - -Introduction -~~~~~~~~~~~~ - -Instead of passing all the arguments to pipeline from a python environment, -you can initialize pipeline by getting the arguments from a file. - -The details of this process are described in this guide. - -Basics -~~~~~~ - -To initialize ``Pipeline`` from a file, call its `from_file <../apiref/chatsky.core.pipeline.html#chatsky.core.pipeline.Pipeline.from_file>`_ -method. It accepts a path to a file, a path to a custom code directory and overrides. - -File -==== - -The file should be a json or yaml file that contains a dictionary. -They keys in the dictionary are the names of pipeline init parameters and the values are the values of the parameters. - -Below is a minimalistic example of such a file: - -.. code-block:: yaml - - script: - flow: - node: - RESPONSE: Hi - TRANSITIONS: - - dst: node - cnd: true - priority: 2 - start_label: - - flow - - node - -.. note:: - - If you're using yaml files, you need to install pyyaml: - - .. code-block:: sh - - pip install chatsky[yaml] - - -Custom dir -========== - -Custom directory allows using any objects inside the yaml file. - -More on that in the :ref:`object-import` section. - -Overrides -========= - -Any pipeline init parameters can be passed to ``from_file``. -They will override parameters defined in the file (or add them if they are not defined in the file). - -.. _object-import: - -Object Import -~~~~~~~~~~~~~ - -JSON values are often not enough to build any serious script. - -For this reason, the init parameters in the pipeline file are preprocessed in two ways: - -String reference replacement -============================ - -Any string that begins with either ``chatsky.``, ``custom.`` or ``external:`` is replaced with a corresponding object. - -The ``chatsky.`` prefix indicates that an object should be found inside the ``chatsky`` library. -For example, string ``chatsky.cnd.ExactMatch`` will be replaced with the ``chatsky.cnd.ExactMatch`` object (which is a class). - -The ``custom.`` prefix allows importing object from the custom directory passed to ``Pipeline.from_file``. -For example, string ``custom.my_response`` will be replaced with the ``my_response`` object defined in ``custom/__init__.py`` -(or will throw an exception if there's no such object). - -The ``external:`` prefix can be used to import any objects (primarily, from external libraries). -For example, string ``external:os.getenv`` will be replaced with the function ``os.getenv``. - -.. note:: - - It is highly recommended to read about the import process for these strings - `here <../apiref/chatsky.core.script_parsing.html#chatsky.core.script_parsing.JSONImporter.resolve_string_reference>`_. - -.. note:: - - If you want to use different prefixes, you can edit the corresponding class variables of the - `JSONImporter <../apiref/chatsky.core.script_parsing.html#chatsky.core.script_parsing.JSONImporter>`_ class: - - .. code-block:: python - - from chatsky.core.script_parsing import JSONImporter - from chatsky import Pipeline - - JSONImporter.CHATSKY_NAMESPACE_PREFIX = "_chatsky:" - - pipeline = Pipeline.from_file(...) - - After changing the prefix variable, ``from_file`` will no longer replace strings that start with ``chatsky.``. - (and will replace strings that start with ``_chatsky:``) - -Single-key dict replacement -=========================== - -Any dictionary containing a **single** key that **begins with any of the prefixes** described in the previous section -will be replaced with a call result of the object referenced by the key. - -Call is made with the arguments passed as a value of the dictionary: - -- If the value is a dictionary; it is passed as kwargs; -- If the value is a list; it is passed as args; -- If the value is ``None``; no arguments are passed; -- Otherwise, the value is passed as the only arg. - -.. list-table:: Examples - :widths: auto - :header-rows: 1 - - * - YAML string - - Resulting object - - Note - * - .. code-block:: yaml - - external:os.getenv: TOKEN - - .. code-block:: python - - os.getenv("TOKEN") - - This falls into the 4th condition (value is not a dict, list or None) so it is passed as the only argument. - * - .. code-block:: yaml - - chatsky.dst.Previous: - - .. code-block:: python - - chatsky.dst.Previous() - - The value is ``None``, so there are no arguments. - * - .. code-block:: yaml - - chatsky.dst.Previous - - .. code-block:: python - - chatsky.dst.Previous - - This is not a dictionary, the resulting object is a class! - * - .. code-block:: yaml - - chatsky.cnd.Regexp: - pattern: "yes" - flags: external:re.I - - .. code-block:: python - - chatsky.cnd.Regexp( - pattern="yes", - flags=re.I - ) - - The value is a dictionary; it is passed as kwargs. - This also showcases that replacement is recursive ``external:re.I`` is replaced as well. - * - .. code-block:: yaml - - chatsky.proc.Extract: - - person.name - - person.age - - .. code-block:: python - - chatsky.proc.Extract( - "person.name", - "person.age" - ) - - The value is a list; it is passed as args. - -Further reading -~~~~~~~~~~~~~~~ - -* `API ref <../apiref/chatsky.core.script_parsing.html>`_ -* `Comprehensive example `_ diff --git a/_sources/user_guides/slot_extraction.rst.txt b/_sources/user_guides/slot_extraction.rst.txt deleted file mode 100644 index 61dcff117..000000000 --- a/_sources/user_guides/slot_extraction.rst.txt +++ /dev/null @@ -1,175 +0,0 @@ -Slot Extraction ---------------- - -Introduction -~~~~~~~~~~~~ - -Extracting and filling slots is an essential part of any conversational service -that comprises the inherent business logic. Like most frameworks, Chatsky -provides components that address this task as a part of its ``slots`` module. -These can be easily customized to leverage neural networks specifically designed -for slot extraction or any other logic you might want to integrate. - -API overview -~~~~~~~~~~~~ - -Defining slots -============== - -The basic building block of the API is the -`BaseSlot <../apiref/chatsky.slots.slots.html#chatsky.slots.slots.BaseSlot>`_ class -and its descendants that vary depending on the value extraction logic. -Each slot has a name by which it can be accessed and a method for extracting values. -Below, we demonstrate the most basic class that extracts values -from user utterances using a regular expression: -`RegexpSlot <../apiref/chatsky.slots.slots.html#chatsky.slots.types.RegexpSlot>`_. - -.. code-block:: python - - from chatsky.slots import RegexpSlot - ... - email_slot = RegexpSlot(regexp=r"[a-z@\.A-Z]+") - -The slots can implement arbitrary logic including requests to external services. -For instance, Deeppavlov library includes a number of models that may be of use for slot -extraction task. In particular, we will demonstrate the use of the following -`NER model `_ -that was trained and validated on the conll_2003 dataset. - -.. code-block:: shell - - docker pull deeppavlov/deeppavlov:latest - docker run -d --name=ner \ - -e CONFIG=ner_conll2003_bert \ - -p 5000:5000 \ - -v ~/.deeppavlov:/root/deeppavlov \ - -v ~/.cache:/root/cache \ - deeppavlov/deeppavlov:latest - -Now that you have a Deeppavlov docker image running on port 5000, you can take the following steps to take -full advantage of its predictions. - -.. code-block:: python - - import requests - from chatsky.slots import FunctionSlot - from chatsky import Message - - # we assume that there is a 'NER' service running on port 5000 - async def extract_first_name(utterance: Message) -> str: - """Return the first entity of type B-PER (first name) found in the utterance.""" - ner_request = requests.post( - "http://localhost:5000/model", - json={"x": [utterance.text]} - ) - ner_tuple = ner_request.json() - if "B-PER" not in ner_tuple[1][0]: - return "" - return ner_tuple[0][0][ner_tuple[1][0].index("B-PER")] - - name_slot = FunctionSlot(func=extract_first_name) - -Individual slots can be grouped allowing the developer to access them together -as a namespace. This can be achieved using the -`GroupSlot <../apiref/chatsky.slots.slots.html#chatsky.slots.slots.GroupSlot>`_ -component that is initialized with other slot instances as its children. -The group slots also allows for arbitrary nesting, i.e. it is possible to include -group slots in other group slots. - -.. code-block:: python - - from chatsky.slots import GroupSlot - - profile_slot = GroupSlot(name=name_slot, email=email_slot) - -After defining all your slots, pass ``GroupSlot`` as pipeline's `slots` argument. -That slot is a root slot: it contains all other group and value slots. - -.. code-block:: python - - from chatsky import Pipeline - - pipeline = Pipeline(..., slots=profile_slot) - -Slot names -========== - -Any slot can be accessed by a slot name: -A dot-separated string that acts as a path from the root slot to the needed slot. - -In the example above ``name_slot`` would have the name "name" -because that is the key used to store it in the ``profile_slot``. - -If you have a nested structure (of ``GroupSlots``) separate the names with dots: - -.. code-block:: python - - from chatsky.slots import GroupSlot - - root_slot = GroupSlot(profile=GroupSlot(name=name_slot, email=email_slot)) - -In this example ``name_slot`` would be accessible by the "profile.name" name. - -Using slots -=========== - -Slots can be extracted at the ``PRE_TRANSITION`` stage -using the `Extract <../apiref/chatsky.processing.slots.html#chatsky.processing.slots.Extract>`_ -function from the `processing` submodule. -You can pass any number of names of the slots that you want to extract to this function. - -.. code-block:: python - - from chatsky import proc - - PRE_TRANSITION: {"extract_first_name": proc.Extract("name", "email")} - -The `conditions` submodule provides a function for checking if specific slots have been extracted. - -.. code-block:: python - - from chatsky import cnd - - TRANSITIONS: [Tr(dst="all_information", cnd=cnd.SlotsExtracted("name", "email", mode="all"))] - TRANSITIONS: [Tr(dst="partial_information", cnd=cnd.SlotsExtracted("name", "email", mode="any"))] - -.. note:: - - You can combine ``slots_extracted`` with the - `Negation <../apiref/chatsky.conditions.standard.html#chatsky.conditions.standard.Negation>`_ - condition to make a transition to an extractor node if a slot has not been extracted yet. - -Both `processing` and `response` submodules provide functions for filling templates with -extracted slot values. -Choose whichever one you like, there's not much difference between them at the moment. - -.. code-block:: python - - from chatsky import proc, rsp - - PRE_RESPONSE: {"fill_response_slots": proc.FillTemplate()} - RESPONSE: "Your first name: {name}" - - - RESPONSE: rsp.FilledTemplate("Your first name: {name}") - -Some real examples of scripts utilizing slot extraction can be found in the -`tutorials section <../tutorials/tutorials.slots.1_basic_example.html>`_. - -Further reading -=============== - -All of the functions described in the previous sections call methods of the -`SlotManager <../apiref/chatsky.slots.slots.html#chatsky.slots.slots.SlotManager>`_ -class under the hood. - -An instance of this class can be accessed in runtime via ``ctx.framework_data.slot_manager``. - -This class allows for more detailed access to the slots API. -For example, you can access exceptions that occurred during slot extraction: - -.. code-block:: python - - slot_manager = ctx.framework_data.slot_manager - extracted_value = slot_manager.get_extracted_slot("name") - exception = extracted_value.extracted_value if not extracted_value.is_slot_extracted else None diff --git a/_sources/user_guides/superset_guide.rst.txt b/_sources/user_guides/superset_guide.rst.txt deleted file mode 100644 index be9b812d5..000000000 --- a/_sources/user_guides/superset_guide.rst.txt +++ /dev/null @@ -1,191 +0,0 @@ -Superset guide ---------------------- - -Description -~~~~~~~~~~~ - -| The Chatsky Stats module can be used to obtain and visualize usage statistics for your service. -| The module relies on several open source solutions that allow for data persistence and visualization - -* `Clickhouse `_ serves as an OLAP storage for data. -* Batching and preprocessing data is based on `OpenTelemetry protocol `_ and the `OpenTelemetry collector `_. -* Interactive visualization is powered by `Apache Superset `_. - -All the mentioned services are shipped as Docker containers, including a pre-built Superset image that ensures API compatibility. - -Collection procedure -~~~~~~~~~~~~~~~~~~~~ - -**Installation** - -.. code-block:: shell - :linenos: - - # clone the original repository to access the docker compose file - git clone https://github.com/deeppavlov/chatsky.git - # install with the stats extra - cd chatsky - pip install .[stats] - -**Launching services** - -.. code-block:: shell - :linenos: - - # clone the original repository to access the docker compose file - git clone https://github.com/deeppavlov/chatsky.git - # launch the required services - cd chatsky - docker compose --profile stats up - -**Collecting data** - -Collecting data is done by means of instrumenting your conversational service before you run it. -Chatsky tutorials (`1 <../tutorials/tutorials.stats.1_extractor_functions.py>`_, `2 <../tutorials/tutorials.stats.2_pipeline_integration.py>`_) -showcase all the steps needed to achieve that. We will run -a special script in order to obtain richly-annotated sample data points to visualize. - -.. code-block:: shell - - python utils/stats/sample_data_provider.py - -Displaying the data -~~~~~~~~~~~~~~~~~~~ - -In order to display the Superset dashboard, you should update the default configuration with the credentials of your database. -The configuration can be optionally saved as a zip archive for inspection / debug. - -You can set most of the configuration options using a YAML file. -The default example file can be found in the `tutorials/stats` directory: - -.. code-block:: yaml - :linenos: - - # tutorials/stats/example_config.yaml - db: - driver: clickhousedb+connect - name: test - user: username - host: clickhouse - port: 8123 - table: otel_logs - -The file can then be used to parametrize the configuration script. - -.. code-block:: shell - - chatsky.stats tutorials/stats/example_config.yaml -P superset -dP pass -U superset --outfile=config_artifact.zip - -.. warning:: - - Here we passed passwords via CLI, which is not recommended. For enhanced security, call the command above omitting the passwords (`chatsky.stats -P -dP -U superset ...`) and you will be prompted to enter them interactively. - -Running the command will automatically import the dashboard as well as the data sources -into the running superset server. If you are using a version of Superset different from the one -shipped with Chatsky, make sure that your access rights are sufficient to edit the workspace. - -Using Superset -~~~~~~~~~~~~~~ - -| In order to view the imported dashboard, log into `Superset `_ using your username and password (which are both `superset` by default and can be configured via `.env_file`). -| The dashboard will then be available in the **Dashboards** section of the Superset UI under the name of **Chatsky stats**. -| The dashboard is split into four sections based on the types of charts and on the chart topic. - -* The **Overview** section summarizes the information about user interaction with your script. And displays a weighted graph of transitions from one node to another. The data is also shown in the form of a table for better introspection capabilities. - -.. figure:: ../_static/images/overview.png - - Overview plots. - -* The data displayed in the **Node stats** section reports, how frequent each of the nodes in your script was visited by users. The information is aggregated in several forms for better interpretability. - -.. figure:: ../_static/images/general_stats.png - - Node stats plots. - -* General service load data aggregated over time can be found in the **Service stats** section. - -.. figure:: ../_static/images/service_stats.png - - Service stats plots. - -* The **Annotations** section contains example charts that show how annotations from supplemental pipeline services can be viewed and analyzed. - -.. figure:: ../_static/images/annotations.png - - Plots for pipeline-produced dialog annotations. - -On some occasions, Superset can show warnings about the database connection being faulty. -In that case, you can navigate to the `Database Connections` section through the `Settings` menu and edit the `chatsky_database` instance updating the credentials. - -.. figure:: ../_static/images/databases.png - - Locate the database settings in the right corner of the screen. - -Customizing the dashboard -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The most notable advantage of using Superset as a visualization tool is that it provides -an easy and intuitive way to create your own charts and to customize the dashboard. - -**Datasets** - -If you aim to create your own chart, Superset will prompt you to select a dataset to draw data from. -The current configuration provides three datasets `chatsky-node-stats`, `chatsky-stats`, and `chatsky-final-nodes`. -However, in most cases, you would use `chatsky-stats` or `chatsky-node-stats`. The former contains all data points, -while the latter only includes the logs produced by `get_current_label` extractor -(`see the API reference <../apiref/chatsky.stats.default_extractors.html#chatsky.stats.default_extractors.get_current_label>`_). -`chatsky-final-nodes` contains the same information as the said datasources, -but only aggregates the labels of nodes visited at the end of dialog graph traversal, -i.e. nodes that terminate the dialog. - -`chatsky-nodes-stats` uses the following variables to store the data: - -* The `context_id` field can be used to distinguish dialog contexts from each other and serves as a user identifier. -* `request_id` is the number of the dialog turn at which the data record was emitted. The data points can be aggregated over this field, showing the distribution of a variable over the dialog history. -* The `data_key` field contains the name of the extractor function that emitted the given record. Since in most cases you will only need the output of one extractor, you can filter out all the other records using filters. -* Finally, the `data` field is a set of JSON-encoded key-value pairs. The keys and values differ depending on the extractor function that emitted the data (you can essentially save arbitrary data under arbitrary keys), which makes filtering the data rows by their `data_key` all the more important. The JSON format implies that individual values need to be extracted using the Superset SQL functions (see below). - - -.. code-block:: - - JSON_VALUE(data, '$.key') - JSON_VALUE(data, '$.outer_key.nested_key') - -**Chart creation** - -.. note:: - - Chart creation is described in detail in the official Superset documentation. - We suggest that you consult it in addition to this section: - `link `_. - -Creating your own chart is as easy as navigating to the `Charts` section of the Superset app -and pressing the `Create` button. - -Initially, you will be prompted for the dataset that you want to use as well as for the chart type. -The Superset GUI provides comprehensive previews of each chart type making it very easy -to find the exact kind that you need. - -At the next step, you will be redirected to the chart creation interface. -Depending on the kind of chat that you have chosen previously, menus will be available -to choose a column for the x-axis and, optionally, a column for the y-axis. As mentioned above, -a separate menu for data filters will also be available. If you need to use the data -from the `data` column, you will need to find the `custom_sql` option when adding the column -and put in the extraction expression, as shown in the examples above. - -**Exporting the chart configuration** - -The configuration of a Superset dashboard can be easily exported and then reused -in other Superset instances. This can be done using the GUI: navigate to the -`Dashboards` section of the Superset application, locate your dashboard (named `Chatsky statistics` per default). -Then press the `export` button on the right and save the zip file to any convenient location. - -**Importing existing configuration files** - -If you need to restore your dashboard or update the configuration, you can import a configuration archive -that has been saved in the manner described below. - -Log in to Superset, open the `Dashboards` tab and press the import button on the right of the screen. -You will be prompted for the database password. If the database credentials match, -the updated dashboard will appear in the dashboard list. \ No newline at end of file diff --git a/_static/Chatsky-full-dark.svg b/_static/Chatsky-full-dark.svg deleted file mode 100644 index 0a63ad937..000000000 --- a/_static/Chatsky-full-dark.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/_static/auto-render.min.js b/_static/auto-render.min.js deleted file mode 100644 index 46d62af28..000000000 --- a/_static/auto-render.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};return function(){r.d(o,{default:function(){return d}});var e=r(771),t=r.n(e);const n=function(e,t,n){let r=n,o=0;const i=e.length;for(;re.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;r=e.search(a),-1!==r;){r>0&&(o.push({type:"text",data:e.slice(0,r)}),e=e.slice(r));const a=t.findIndex((t=>e.startsWith(t.left)));if(r=n(t[a].right,e,t[a].left.length),-1===r)break;const l=e.slice(0,r+t[a].right.length),s=i.test(l)?l:e.slice(t[a].left.length,r);o.push({type:"math",data:s,rawData:l,display:t[a].display}),e=e.slice(r+t[a].right.length)}return""!==e&&o.push({type:"text",data:e}),o};const l=function(e,n){const r=a(e,n.delimiters);if(1===r.length&&"text"===r[0].type)return null;const o=document.createDocumentFragment();for(let e=0;e-1===e.indexOf(" "+t+" ")))&&s(r,t)}}};var d=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},s(e,n)}}(),o=o.default}()})); \ No newline at end of file diff --git a/_static/basic.css b/_static/basic.css deleted file mode 100644 index b97662dd8..000000000 --- a/_static/basic.css +++ /dev/null @@ -1,921 +0,0 @@ -/* - * basic.css - * ~~~~~~~~~ - * - * Sphinx stylesheet -- basic theme. - * - * :copyright: Copyright 2007-2023 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: 270px; - 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; -} - -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/_static/binder_badge_logo.svg b/_static/binder_badge_logo.svg deleted file mode 100644 index 327f6b639..000000000 --- a/_static/binder_badge_logo.svg +++ /dev/null @@ -1 +0,0 @@ - launchlaunchbinderbinder \ No newline at end of file diff --git a/_static/broken_example.png b/_static/broken_example.png deleted file mode 100644 index 4fea24e7d..000000000 Binary files a/_static/broken_example.png and /dev/null differ diff --git a/_static/check-solid.svg b/_static/check-solid.svg deleted file mode 100644 index 92fad4b5c..000000000 --- a/_static/check-solid.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_static/clipboard.min.js b/_static/clipboard.min.js deleted file mode 100644 index 54b3c4638..000000000 --- a/_static/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.8 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 - - - - diff --git a/_static/copybutton.css b/_static/copybutton.css deleted file mode 100644 index f1916ec7d..000000000 --- a/_static/copybutton.css +++ /dev/null @@ -1,94 +0,0 @@ -/* Copy buttons */ -button.copybtn { - position: absolute; - display: flex; - top: .3em; - right: .3em; - width: 1.7em; - height: 1.7em; - opacity: 0; - transition: opacity 0.3s, border .3s, background-color .3s; - user-select: none; - padding: 0; - border: none; - outline: none; - border-radius: 0.4em; - /* The colors that GitHub uses */ - border: #1b1f2426 1px solid; - background-color: #f6f8fa; - color: #57606a; -} - -button.copybtn.success { - border-color: #22863a; - color: #22863a; -} - -button.copybtn svg { - stroke: currentColor; - width: 1.5em; - height: 1.5em; - padding: 0.1em; -} - -div.highlight { - position: relative; -} - -/* Show the copybutton */ -.highlight:hover button.copybtn, button.copybtn.success { - opacity: 1; -} - -.highlight button.copybtn:hover { - background-color: rgb(235, 235, 235); -} - -.highlight button.copybtn:active { - background-color: rgb(187, 187, 187); -} - -/** - * A minimal CSS-only tooltip copied from: - * https://codepen.io/mildrenben/pen/rVBrpK - * - * To use, write HTML like the following: - * - *

Short

- */ - .o-tooltip--left { - position: relative; - } - - .o-tooltip--left:after { - opacity: 0; - visibility: hidden; - position: absolute; - content: attr(data-tooltip); - padding: .2em; - font-size: .8em; - left: -.2em; - background: grey; - color: white; - white-space: nowrap; - z-index: 2; - border-radius: 2px; - transform: translateX(-102%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); -} - -.o-tooltip--left:hover:after { - display: block; - opacity: 1; - visibility: visible; - transform: translateX(-100%) translateY(0); - transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); - transition-delay: .5s; -} - -/* By default the copy button shouldn't show up when printing a page */ -@media print { - button.copybtn { - display: none; - } -} diff --git a/_static/copybutton.js b/_static/copybutton.js deleted file mode 100644 index 2ea7ff3e2..000000000 --- a/_static/copybutton.js +++ /dev/null @@ -1,248 +0,0 @@ -// Localization support -const messages = { - 'en': { - 'copy': 'Copy', - 'copy_to_clipboard': 'Copy to clipboard', - 'copy_success': 'Copied!', - 'copy_failure': 'Failed to copy', - }, - 'es' : { - 'copy': 'Copiar', - 'copy_to_clipboard': 'Copiar al portapapeles', - 'copy_success': '¡Copiado!', - 'copy_failure': 'Error al copiar', - }, - 'de' : { - 'copy': 'Kopieren', - 'copy_to_clipboard': 'In die Zwischenablage kopieren', - 'copy_success': 'Kopiert!', - 'copy_failure': 'Fehler beim Kopieren', - }, - 'fr' : { - 'copy': 'Copier', - 'copy_to_clipboard': 'Copier dans le presse-papier', - 'copy_success': 'Copié !', - 'copy_failure': 'Échec de la copie', - }, - 'ru': { - 'copy': 'Скопировать', - 'copy_to_clipboard': 'Скопировать в буфер', - 'copy_success': 'Скопировано!', - 'copy_failure': 'Не удалось скопировать', - }, - 'zh-CN': { - 'copy': '复制', - 'copy_to_clipboard': '复制到剪贴板', - 'copy_success': '复制成功!', - 'copy_failure': '复制失败', - }, - 'it' : { - 'copy': 'Copiare', - 'copy_to_clipboard': 'Copiato negli appunti', - 'copy_success': 'Copiato!', - 'copy_failure': 'Errore durante la copia', - } -} - -let locale = 'en' -if( document.documentElement.lang !== undefined - && messages[document.documentElement.lang] !== undefined ) { - locale = document.documentElement.lang -} - -let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; -if (doc_url_root == '#') { - doc_url_root = ''; -} - -/** - * SVG files for our copy buttons - */ -let iconCheck = ` - ${messages[locale]['copy_success']} - - -` - -// If the user specified their own SVG use that, otherwise use the default -let iconCopy = ``; -if (!iconCopy) { - iconCopy = ` - ${messages[locale]['copy_to_clipboard']} - - - -` -} - -/** - * Set up copy/paste for code blocks - */ - -const runWhenDOMLoaded = cb => { - if (document.readyState != 'loading') { - cb() - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', cb) - } else { - document.attachEvent('onreadystatechange', function() { - if (document.readyState == 'complete') cb() - }) - } -} - -const codeCellId = index => `codecell${index}` - -// Clears selected text since ClipboardJS will select the text when copying -const clearSelection = () => { - if (window.getSelection) { - window.getSelection().removeAllRanges() - } else if (document.selection) { - document.selection.empty() - } -} - -// Changes tooltip text for a moment, then changes it back -// We want the timeout of our `success` class to be a bit shorter than the -// tooltip and icon change, so that we can hide the icon before changing back. -var timeoutIcon = 2000; -var timeoutSuccessClass = 1500; - -const temporarilyChangeTooltip = (el, oldText, newText) => { - el.setAttribute('data-tooltip', newText) - el.classList.add('success') - // Remove success a little bit sooner than we change the tooltip - // So that we can use CSS to hide the copybutton first - setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) - setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) -} - -// Changes the copy button icon for two seconds, then changes it back -const temporarilyChangeIcon = (el) => { - el.innerHTML = iconCheck; - setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) -} - -const addCopyButtonToCodeCells = () => { - // If ClipboardJS hasn't loaded, wait a bit and try again. This - // happens because we load ClipboardJS asynchronously. - if (window.ClipboardJS === undefined) { - setTimeout(addCopyButtonToCodeCells, 250) - return - } - - // Add copybuttons to all of our code cells - const COPYBUTTON_SELECTOR = 'div.highlight pre'; - const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) - codeCells.forEach((codeCell, index) => { - const id = codeCellId(index) - codeCell.setAttribute('id', id) - - const clipboardButton = id => - `` - codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) - }) - -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} - - -var copyTargetText = (trigger) => { - var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); - - // get filtered text - let exclude = '.linenos'; - - let text = filterText(target, exclude); - return formatCopyText(text, '', false, true, true, true, '', '') -} - - // Initialize with a callback so we can modify the text before copy - const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) - - // Update UI with error/success messages - clipboard.on('success', event => { - clearSelection() - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) - temporarilyChangeIcon(event.trigger) - }) - - clipboard.on('error', event => { - temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) - }) -} - -runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/_static/copybutton_funcs.js b/_static/copybutton_funcs.js deleted file mode 100644 index dbe1aaad7..000000000 --- a/_static/copybutton_funcs.js +++ /dev/null @@ -1,73 +0,0 @@ -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string -} - -/** - * Removes excluded text from a Node. - * - * @param {Node} target Node to filter. - * @param {string} exclude CSS selector of nodes to exclude. - * @returns {DOMString} Text from `target` with text removed. - */ -export function filterText(target, exclude) { - const clone = target.cloneNode(true); // clone as to not modify the live DOM - if (exclude) { - // remove excluded nodes - clone.querySelectorAll(exclude).forEach(node => node.remove()); - } - return clone.innerText; -} - -// Callback when a copy button is clicked. Will be passed the node that was clicked -// should then grab the text and replace pieces of text that shouldn't be used in output -export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { - var regexp; - var match; - - // Do we check for line continuation characters and "HERE-documents"? - var useLineCont = !!lineContinuationChar - var useHereDoc = !!hereDocDelim - - // create regexp to capture prompt and remaining line - if (isRegexp) { - regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') - } else { - regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') - } - - const outputLines = []; - var promptFound = false; - var gotLineCont = false; - var gotHereDoc = false; - const lineGotPrompt = []; - for (const line of textContent.split('\n')) { - match = line.match(regexp) - if (match || gotLineCont || gotHereDoc) { - promptFound = regexp.test(line) - lineGotPrompt.push(promptFound) - if (removePrompts && promptFound) { - outputLines.push(match[2]) - } else { - outputLines.push(line) - } - gotLineCont = line.endsWith(lineContinuationChar) & useLineCont - if (line.includes(hereDocDelim) & useHereDoc) - gotHereDoc = !gotHereDoc - } else if (!onlyCopyPromptLines) { - outputLines.push(line) - } else if (copyEmptyLines && line.trim() === '') { - outputLines.push(line) - } - } - - // If no lines with the prompt were found then just use original lines - if (lineGotPrompt.some(v => v === true)) { - textContent = outputLines.join('\n'); - } - - // Remove a trailing newline to avoid auto-running when pasting - if (textContent.endsWith("\n")) { - textContent = textContent.slice(0, -1) - } - return textContent -} diff --git a/_static/css/custom.css b/_static/css/custom.css deleted file mode 100644 index 4957fe1a4..000000000 --- a/_static/css/custom.css +++ /dev/null @@ -1,45 +0,0 @@ -.call-to-action-any { - width: 2rem; - height: 2rem; - margin-inline-end: 10px; - background-size: 100% 100%; -} - -.pytorch-colab { - background-image: url("../images/logo-colab.svg"); -} - -.pytorch-download { - background-image: url("../images/logo-download.svg"); -} - -.pytorch-github { - background-image: url("../images/logo-github.svg"); -} - - - -.call-to-action-desktop-view { - display: none; -} - -.call-to-action-mobile-view { - display: block; -} - -#google-colab-link, #download-notebook-link, #github-view-link { - padding-bottom: 0.625rem; - display: flex; -} - - - -@media screen and (min-width: 768px) { - .call-to-action-desktop-view { - display: block; - } - - .call-to-action-mobile-view { - display: none; - } -} diff --git a/_static/doctools.js b/_static/doctools.js deleted file mode 100644 index d06a71d75..000000000 --- a/_static/doctools.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * doctools.js - * ~~~~~~~~~~~ - * - * Base JavaScript utilities for all Sphinx HTML documentation. - * - * :copyright: Copyright 2007-2023 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/_static/documentation_options.js b/_static/documentation_options.js deleted file mode 100644 index 4184d466e..000000000 --- a/_static/documentation_options.js +++ /dev/null @@ -1,14 +0,0 @@ -var DOCUMENTATION_OPTIONS = { - URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.9.0', - LANGUAGE: 'en', - COLLAPSE_INDEX: false, - BUILDER: 'html', - FILE_SUFFIX: '.html', - LINK_SUFFIX: '.html', - HAS_SOURCE: true, - SOURCELINK_SUFFIX: '.txt', - NAVIGATION_WITH_KEYS: false, - SHOW_SEARCH_SUMMARY: true, - ENABLE_SEARCH_SHORTCUTS: true, -}; \ No newline at end of file diff --git a/_static/drawio/core/user_actor.png b/_static/drawio/core/user_actor.png deleted file mode 100644 index 382c79ae7..000000000 Binary files a/_static/drawio/core/user_actor.png and /dev/null differ diff --git a/_static/file.png b/_static/file.png deleted file mode 100644 index a858a410e..000000000 Binary files a/_static/file.png and /dev/null differ diff --git a/_static/images/Chatsky-full-dark.svg b/_static/images/Chatsky-full-dark.svg deleted file mode 100644 index 0a63ad937..000000000 --- a/_static/images/Chatsky-full-dark.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/_static/images/Chatsky-full-light.svg b/_static/images/Chatsky-full-light.svg deleted file mode 100644 index 44e440fd8..000000000 --- a/_static/images/Chatsky-full-light.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/_static/images/Chatsky-min-dark.svg b/_static/images/Chatsky-min-dark.svg deleted file mode 100644 index 0d91ec949..000000000 --- a/_static/images/Chatsky-min-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_static/images/Chatsky-min-light.svg b/_static/images/Chatsky-min-light.svg deleted file mode 100644 index 044cd1b95..000000000 --- a/_static/images/Chatsky-min-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/_static/images/additional_stats.png b/_static/images/additional_stats.png deleted file mode 100644 index 2b9d2c3f5..000000000 Binary files a/_static/images/additional_stats.png and /dev/null differ diff --git a/_static/images/annotations.png b/_static/images/annotations.png deleted file mode 100644 index b1e524890..000000000 Binary files a/_static/images/annotations.png and /dev/null differ diff --git a/_static/images/benchmark_compare.png b/_static/images/benchmark_compare.png deleted file mode 100644 index 8765ed48c..000000000 Binary files a/_static/images/benchmark_compare.png and /dev/null differ diff --git a/_static/images/benchmark_mass_compare.png b/_static/images/benchmark_mass_compare.png deleted file mode 100644 index 71911c554..000000000 Binary files a/_static/images/benchmark_mass_compare.png and /dev/null differ diff --git a/_static/images/benchmark_sets.png b/_static/images/benchmark_sets.png deleted file mode 100644 index 4eadd2873..000000000 Binary files a/_static/images/benchmark_sets.png and /dev/null differ diff --git a/_static/images/benchmark_view.png b/_static/images/benchmark_view.png deleted file mode 100644 index c8a5afc81..000000000 Binary files a/_static/images/benchmark_view.png and /dev/null differ diff --git a/_static/images/databases.png b/_static/images/databases.png deleted file mode 100644 index 167c4ce61..000000000 Binary files a/_static/images/databases.png and /dev/null differ diff --git a/_static/images/general_stats.png b/_static/images/general_stats.png deleted file mode 100644 index 660f2ed89..000000000 Binary files a/_static/images/general_stats.png and /dev/null differ diff --git a/_static/images/logo-colab.svg b/_static/images/logo-colab.svg deleted file mode 100644 index 2ab15e2f3..000000000 --- a/_static/images/logo-colab.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - diff --git a/_static/images/logo-deeppavlov.svg b/_static/images/logo-deeppavlov.svg deleted file mode 100644 index 5aa494ffa..000000000 --- a/_static/images/logo-deeppavlov.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/_static/images/logo-download.svg b/_static/images/logo-download.svg deleted file mode 100644 index cc37d638e..000000000 --- a/_static/images/logo-download.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/_static/images/logo-github.svg b/_static/images/logo-github.svg deleted file mode 100644 index 2c2570da1..000000000 --- a/_static/images/logo-github.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/_static/images/overview.png b/_static/images/overview.png deleted file mode 100644 index 5bd0fb6a1..000000000 Binary files a/_static/images/overview.png and /dev/null differ diff --git a/_static/images/service_stats.png b/_static/images/service_stats.png deleted file mode 100644 index 7ce724286..000000000 Binary files a/_static/images/service_stats.png and /dev/null differ diff --git a/_static/jupyterlite_badge_logo.svg b/_static/jupyterlite_badge_logo.svg deleted file mode 100644 index 5de36d7fd..000000000 --- a/_static/jupyterlite_badge_logo.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -launchlaunchlitelite \ No newline at end of file diff --git a/_static/katex-math.css b/_static/katex-math.css deleted file mode 100644 index bdd1634d8..000000000 --- a/_static/katex-math.css +++ /dev/null @@ -1,50 +0,0 @@ -/* Responsives: make equations scrollable on small screens. - * See: https://github.com/Khan/KaTeX/issues/327 */ -.katex-display > .katex { - max-width: 100%; -} -.katex-display > .katex > .katex-html { - max-width: 100%; - overflow-x: auto; - overflow-y: hidden; - padding-left: 2px; - padding-right: 2px; - padding-bottom: 1px; - padding-top: 3px; -} -/* Increase margin around equations */ -.katex-display { - margin: 1.2em 0; -} -/* Equation number floats to the right and shows permalink for mouse hover - on the right side of equation number. */ -div.math { - position: relative; - padding-right: 2.5em; -} -.eqno { - height: 100%; - position: absolute; - right: 0; - padding-left: 5px; - padding-bottom: 5px; - padding-right: 1px; -} -.eqno:before { - /* Force vertical alignment of number */ - display: inline-block; - height: 100%; - vertical-align: middle; - content: ""; -} -.eqno .headerlink { - display: none; - visibility: hidden; - font-size: 14px; - padding-left: .3em; -} -.eqno:hover .headerlink { - display: inline-block; - visibility: visible; - margin-right: -1.05em; -} diff --git a/_static/katex.min.js b/_static/katex.min.js deleted file mode 100644 index e701cfde7..000000000 --- a/_static/katex.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.katex=t():e.katex=t()}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={d:function(t,r){for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return Yn}});class r{constructor(e,t){this.name=void 0,this.position=void 0,this.length=void 0,this.rawMessage=void 0;let n,o,s="KaTeX parse error: "+e;const i=t&&t.loc;if(i&&i.start<=i.end){const e=i.lexer.input;n=i.start,o=i.end,n===e.length?s+=" at end of input: ":s+=" at position "+(n+1)+": ";const t=e.slice(n,o).replace(/[^]/g,"$&\u0332");let r,a;r=n>15?"\u2026"+e.slice(n-15,n):e.slice(0,n),a=o+15":">","<":"<",'"':""","'":"'"},i=/[&><"']/g;const a=function(e){return"ordgroup"===e.type||"color"===e.type?1===e.body.length?a(e.body[0]):e:"font"===e.type?a(e.body):e};var l={contains:function(e,t){return-1!==e.indexOf(t)},deflt:function(e,t){return void 0===e?t:e},escape:function(e){return String(e).replace(i,(e=>s[e]))},hyphenate:function(e){return e.replace(o,"-$1").toLowerCase()},getBaseElem:a,isCharacterBox:function(e){const t=a(e);return"mathord"===t.type||"textord"===t.type||"atom"===t.type},protocolFromUrl:function(e){const t=/^[\x00-\x20]*([^\\/#?]*?)(:|�*58|�*3a|&colon)/i.exec(e);return t?":"!==t[2]?null:/^[a-zA-Z][a-zA-Z0-9+\-.]*$/.test(t[1])?t[1].toLowerCase():null:"_relative"}};const h={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format "},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color ",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:e=>"#"+e},macros:{type:"object",cli:"-m, --macro ",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(e,t)=>(t.push(e),t)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:e=>Math.max(0,e),cli:"--min-rule-thickness ",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily large",processor:e=>Math.max(0,e),cli:"-s, --max-size ",cliProcessor:parseInt},maxExpand:{type:"number",default:1e3,description:"Limit the number of macro expansions to the specified number, to prevent e.g. infinite macro loops. If set to Infinity, the macro expander will try to fully expand as in LaTeX.",processor:e=>Math.max(0,e),cli:"-e, --max-expand ",cliProcessor:e=>"Infinity"===e?1/0:parseInt(e)},globalGroup:{type:"boolean",cli:!1}};function c(e){if(e.default)return e.default;const t=e.type,r=Array.isArray(t)?t[0]:t;if("string"!=typeof r)return r.enum[0];switch(r){case"boolean":return!1;case"string":return"";case"number":return 0;case"object":return{}}}class m{constructor(e){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,this.globalGroup=void 0,e=e||{};for(const t in h)if(h.hasOwnProperty(t)){const r=h[t];this[t]=void 0!==e[t]?r.processor?r.processor(e[t]):e[t]:c(r)}}reportNonstrict(e,t,r){let o=this.strict;if("function"==typeof o&&(o=o(e,t,r)),o&&"ignore"!==o){if(!0===o||"error"===o)throw new n("LaTeX-incompatible input and strict mode is set to 'error': "+t+" ["+e+"]",r);"warn"===o?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+o+"': "+t+" ["+e+"]")}}useStrictBehavior(e,t,r){let n=this.strict;if("function"==typeof n)try{n=n(e,t,r)}catch(e){n="error"}return!(!n||"ignore"===n)&&(!0===n||"error"===n||("warn"===n?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+t+" ["+e+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+n+"': "+t+" ["+e+"]"),!1)))}isTrusted(e){if(e.url&&!e.protocol){const t=l.protocolFromUrl(e.url);if(null==t)return!1;e.protocol=t}const t="function"==typeof this.trust?this.trust(e):this.trust;return Boolean(t)}}class p{constructor(e,t,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=e,this.size=t,this.cramped=r}sup(){return u[d[this.id]]}sub(){return u[g[this.id]]}fracNum(){return u[f[this.id]]}fracDen(){return u[b[this.id]]}cramp(){return u[y[this.id]]}text(){return u[x[this.id]]}isTight(){return this.size>=2}}const u=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],d=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],f=[2,3,4,5,6,7,6,7],b=[3,3,5,5,7,7,7,7],y=[1,1,3,3,5,5,7,7],x=[0,1,2,3,2,3,2,3];var w={DISPLAY:u[0],TEXT:u[2],SCRIPT:u[4],SCRIPTSCRIPT:u[6]};const v=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"armenian",blocks:[[1328,1423]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];const k=[];function S(e){for(let t=0;t=k[t]&&e<=k[t+1])return!0;return!1}v.forEach((e=>e.blocks.forEach((e=>k.push(...e)))));const M=80,z={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"};class A{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createDocumentFragment();for(let t=0;te.toText())).join("")}}var T={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{32:[0,0,0,0,.25],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473],160:[0,0,0,0,.25]},"Fraktur-Regular":{32:[0,0,0,0,.25],33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],160:[0,0,0,0,.25],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],160:[0,0,0,0,.25],163:[0,.69444,0,0,.86853],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8773:[.027,.638,0,0,.894],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{32:[0,0,0,0,.25],33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],160:[0,0,0,0,.25],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],160:[0,0,0,0,.25],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],163:[0,.69444,0,0,.76909],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.123,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,.778],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.673,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.903,0,0,.278],8943:[-.19,.313,0,0,1.172],8945:[-.1,.823,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.745,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.745,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{32:[0,0,0,0,.25],48:[0,.44444,0,0,.575],49:[0,.44444,0,0,.575],50:[0,.44444,0,0,.575],51:[.19444,.44444,0,0,.575],52:[.19444,.44444,0,0,.575],53:[.19444,.44444,0,0,.575],54:[0,.64444,0,0,.575],55:[.19444,.44444,0,0,.575],56:[0,.64444,0,0,.575],57:[.19444,.44444,0,0,.575],65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],160:[0,0,0,0,.25],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333],57649:[0,.44444,0,0,.39352],57911:[.19444,.44444,0,0,.43889]},"Math-Italic":{32:[0,0,0,0,.25],48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],160:[0,0,0,0,.25],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059],57649:[0,.43056,0,.02778,.32246],57911:[.19444,.43056,0,.08334,.38403]},"SansSerif-Bold":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],160:[0,0,0,0,.25],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{32:[0,0,0,0,.25],33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],160:[0,0,0,0,.25],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],160:[0,0,0,0,.25],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{32:[0,0,0,0,.25],65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212],160:[0,0,0,0,.25]},"Size1-Regular":{32:[0,0,0,0,.25],40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],160:[0,0,0,0,.25],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{32:[0,0,0,0,.25],40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],160:[0,0,0,0,.25],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{32:[0,0,0,0,.25],40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],160:[0,0,0,0,.25],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{32:[0,0,0,0,.25],40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],160:[0,0,0,0,.25],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}};const B={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},C={"\xc5":"A","\xd0":"D","\xde":"o","\xe5":"a","\xf0":"d","\xfe":"o","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};function N(e,t,r){if(!T[t])throw new Error("Font metrics not found for font: "+t+".");let n=e.charCodeAt(0),o=T[t][n];if(!o&&e[0]in C&&(n=C[e[0]].charCodeAt(0),o=T[t][n]),o||"text"!==r||S(n)&&(o=T[t][77]),o)return{depth:o[0],height:o[1],italic:o[2],skew:o[3],width:o[4]}}const q={};const I=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],R=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],H=function(e,t){return t.size<2?e:I[e-1][t.size-1]};class O{constructor(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||O.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=R[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}extend(e){const t={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(const r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return new O(t)}havingStyle(e){return this.style===e?this:this.extend({style:e,size:H(this.textSize,e)})}havingCrampedStyle(){return this.havingStyle(this.style.cramp())}havingSize(e){return this.size===e&&this.textSize===e?this:this.extend({style:this.style.text(),size:e,textSize:e,sizeMultiplier:R[e-1]})}havingBaseStyle(e){e=e||this.style.text();const t=H(O.BASESIZE,e);return this.size===t&&this.textSize===O.BASESIZE&&this.style===e?this:this.extend({style:e,size:t})}havingBaseSizing(){let e;switch(this.style.id){case 4:case 5:e=3;break;case 6:case 7:e=1;break;default:e=6}return this.extend({style:this.style.text(),size:e})}withColor(e){return this.extend({color:e})}withPhantom(){return this.extend({phantom:!0})}withFont(e){return this.extend({font:e})}withTextFontFamily(e){return this.extend({fontFamily:e,font:""})}withTextFontWeight(e){return this.extend({fontWeight:e,font:""})}withTextFontShape(e){return this.extend({fontShape:e,font:""})}sizingClasses(e){return e.size!==this.size?["sizing","reset-size"+e.size,"size"+this.size]:[]}baseSizingClasses(){return this.size!==O.BASESIZE?["sizing","reset-size"+this.size,"size"+O.BASESIZE]:[]}fontMetrics(){return this._fontMetrics||(this._fontMetrics=function(e){let t;if(t=e>=5?0:e>=3?1:2,!q[t]){const e=q[t]={cssEmPerMu:B.quad[t]/18};for(const r in B)B.hasOwnProperty(r)&&(e[r]=B[r][t])}return q[t]}(this.size)),this._fontMetrics}getColor(){return this.phantom?"transparent":this.color}}O.BASESIZE=6;var E=O;const L={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},D={ex:!0,em:!0,mu:!0},V=function(e){return"string"!=typeof e&&(e=e.unit),e in L||e in D||"ex"===e},P=function(e,t){let r;if(e.unit in L)r=L[e.unit]/t.fontMetrics().ptPerEm/t.sizeMultiplier;else if("mu"===e.unit)r=t.fontMetrics().cssEmPerMu;else{let o;if(o=t.style.isTight()?t.havingStyle(t.style.text()):t,"ex"===e.unit)r=o.fontMetrics().xHeight;else{if("em"!==e.unit)throw new n("Invalid unit: '"+e.unit+"'");r=o.fontMetrics().quad}o!==t&&(r*=o.sizeMultiplier/t.sizeMultiplier)}return Math.min(e.number*r,t.maxSize)},F=function(e){return+e.toFixed(4)+"em"},G=function(e){return e.filter((e=>e)).join(" ")},U=function(e,t,r){if(this.classes=e||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},t){t.style.isTight()&&this.classes.push("mtight");const e=t.getColor();e&&(this.style.color=e)}},Y=function(e){const t=document.createElement(e);t.className=G(this.classes);for(const e in this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);for(const e in this.attributes)this.attributes.hasOwnProperty(e)&&t.setAttribute(e,this.attributes[e]);for(let e=0;e",t};class W{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,U.call(this,e,r,n),this.children=t||[]}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return l.contains(this.classes,e)}toNode(){return Y.call(this,"span")}toMarkup(){return X.call(this,"span")}}class _{constructor(e,t,r,n){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,U.call(this,t,n),this.children=r||[],this.setAttribute("href",e)}setAttribute(e,t){this.attributes[e]=t}hasClass(e){return l.contains(this.classes,e)}toNode(){return Y.call(this,"a")}toMarkup(){return X.call(this,"a")}}class j{constructor(e,t,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=t,this.src=e,this.classes=["mord"],this.style=r}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createElement("img");e.src=this.src,e.alt=this.alt,e.className="mord";for(const t in this.style)this.style.hasOwnProperty(t)&&(e.style[t]=this.style[t]);return e}toMarkup(){let e=''+l.escape(this.alt)+'=n[0]&&e<=n[1])return r.name}}return null}(this.text.charCodeAt(0));l&&this.classes.push(l+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=$[this.text])}hasClass(e){return l.contains(this.classes,e)}toNode(){const e=document.createTextNode(this.text);let t=null;this.italic>0&&(t=document.createElement("span"),t.style.marginRight=F(this.italic)),this.classes.length>0&&(t=t||document.createElement("span"),t.className=G(this.classes));for(const e in this.style)this.style.hasOwnProperty(e)&&(t=t||document.createElement("span"),t.style[e]=this.style[e]);return t?(t.appendChild(e),t):e}toMarkup(){let e=!1,t="0&&(r+="margin-right:"+this.italic+"em;");for(const e in this.style)this.style.hasOwnProperty(e)&&(r+=l.hyphenate(e)+":"+this.style[e]+";");r&&(e=!0,t+=' style="'+l.escape(r)+'"');const n=l.escape(this.text);return e?(t+=">",t+=n,t+="",t):n}}class K{constructor(e,t){this.children=void 0,this.attributes=void 0,this.children=e||[],this.attributes=t||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","svg");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);for(let t=0;t':''}}class Q{constructor(e){this.attributes=void 0,this.attributes=e||{}}toNode(){const e=document.createElementNS("http://www.w3.org/2000/svg","line");for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);return e}toMarkup(){let e="","\\gt",!0),se(ie,le,ye,"\u2208","\\in",!0),se(ie,le,ye,"\ue020","\\@not"),se(ie,le,ye,"\u2282","\\subset",!0),se(ie,le,ye,"\u2283","\\supset",!0),se(ie,le,ye,"\u2286","\\subseteq",!0),se(ie,le,ye,"\u2287","\\supseteq",!0),se(ie,he,ye,"\u2288","\\nsubseteq",!0),se(ie,he,ye,"\u2289","\\nsupseteq",!0),se(ie,le,ye,"\u22a8","\\models"),se(ie,le,ye,"\u2190","\\leftarrow",!0),se(ie,le,ye,"\u2264","\\le"),se(ie,le,ye,"\u2264","\\leq",!0),se(ie,le,ye,"<","\\lt",!0),se(ie,le,ye,"\u2192","\\rightarrow",!0),se(ie,le,ye,"\u2192","\\to"),se(ie,he,ye,"\u2271","\\ngeq",!0),se(ie,he,ye,"\u2270","\\nleq",!0),se(ie,le,xe,"\xa0","\\ "),se(ie,le,xe,"\xa0","\\space"),se(ie,le,xe,"\xa0","\\nobreakspace"),se(ae,le,xe,"\xa0","\\ "),se(ae,le,xe,"\xa0"," "),se(ae,le,xe,"\xa0","\\space"),se(ae,le,xe,"\xa0","\\nobreakspace"),se(ie,le,xe,null,"\\nobreak"),se(ie,le,xe,null,"\\allowbreak"),se(ie,le,be,",",","),se(ie,le,be,";",";"),se(ie,he,me,"\u22bc","\\barwedge",!0),se(ie,he,me,"\u22bb","\\veebar",!0),se(ie,le,me,"\u2299","\\odot",!0),se(ie,le,me,"\u2295","\\oplus",!0),se(ie,le,me,"\u2297","\\otimes",!0),se(ie,le,we,"\u2202","\\partial",!0),se(ie,le,me,"\u2298","\\oslash",!0),se(ie,he,me,"\u229a","\\circledcirc",!0),se(ie,he,me,"\u22a1","\\boxdot",!0),se(ie,le,me,"\u25b3","\\bigtriangleup"),se(ie,le,me,"\u25bd","\\bigtriangledown"),se(ie,le,me,"\u2020","\\dagger"),se(ie,le,me,"\u22c4","\\diamond"),se(ie,le,me,"\u22c6","\\star"),se(ie,le,me,"\u25c3","\\triangleleft"),se(ie,le,me,"\u25b9","\\triangleright"),se(ie,le,fe,"{","\\{"),se(ae,le,we,"{","\\{"),se(ae,le,we,"{","\\textbraceleft"),se(ie,le,pe,"}","\\}"),se(ae,le,we,"}","\\}"),se(ae,le,we,"}","\\textbraceright"),se(ie,le,fe,"{","\\lbrace"),se(ie,le,pe,"}","\\rbrace"),se(ie,le,fe,"[","\\lbrack",!0),se(ae,le,we,"[","\\lbrack",!0),se(ie,le,pe,"]","\\rbrack",!0),se(ae,le,we,"]","\\rbrack",!0),se(ie,le,fe,"(","\\lparen",!0),se(ie,le,pe,")","\\rparen",!0),se(ae,le,we,"<","\\textless",!0),se(ae,le,we,">","\\textgreater",!0),se(ie,le,fe,"\u230a","\\lfloor",!0),se(ie,le,pe,"\u230b","\\rfloor",!0),se(ie,le,fe,"\u2308","\\lceil",!0),se(ie,le,pe,"\u2309","\\rceil",!0),se(ie,le,we,"\\","\\backslash"),se(ie,le,we,"\u2223","|"),se(ie,le,we,"\u2223","\\vert"),se(ae,le,we,"|","\\textbar",!0),se(ie,le,we,"\u2225","\\|"),se(ie,le,we,"\u2225","\\Vert"),se(ae,le,we,"\u2225","\\textbardbl"),se(ae,le,we,"~","\\textasciitilde"),se(ae,le,we,"\\","\\textbackslash"),se(ae,le,we,"^","\\textasciicircum"),se(ie,le,ye,"\u2191","\\uparrow",!0),se(ie,le,ye,"\u21d1","\\Uparrow",!0),se(ie,le,ye,"\u2193","\\downarrow",!0),se(ie,le,ye,"\u21d3","\\Downarrow",!0),se(ie,le,ye,"\u2195","\\updownarrow",!0),se(ie,le,ye,"\u21d5","\\Updownarrow",!0),se(ie,le,ge,"\u2210","\\coprod"),se(ie,le,ge,"\u22c1","\\bigvee"),se(ie,le,ge,"\u22c0","\\bigwedge"),se(ie,le,ge,"\u2a04","\\biguplus"),se(ie,le,ge,"\u22c2","\\bigcap"),se(ie,le,ge,"\u22c3","\\bigcup"),se(ie,le,ge,"\u222b","\\int"),se(ie,le,ge,"\u222b","\\intop"),se(ie,le,ge,"\u222c","\\iint"),se(ie,le,ge,"\u222d","\\iiint"),se(ie,le,ge,"\u220f","\\prod"),se(ie,le,ge,"\u2211","\\sum"),se(ie,le,ge,"\u2a02","\\bigotimes"),se(ie,le,ge,"\u2a01","\\bigoplus"),se(ie,le,ge,"\u2a00","\\bigodot"),se(ie,le,ge,"\u222e","\\oint"),se(ie,le,ge,"\u222f","\\oiint"),se(ie,le,ge,"\u2230","\\oiiint"),se(ie,le,ge,"\u2a06","\\bigsqcup"),se(ie,le,ge,"\u222b","\\smallint"),se(ae,le,ue,"\u2026","\\textellipsis"),se(ie,le,ue,"\u2026","\\mathellipsis"),se(ae,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u2026","\\ldots",!0),se(ie,le,ue,"\u22ef","\\@cdots",!0),se(ie,le,ue,"\u22f1","\\ddots",!0),se(ie,le,we,"\u22ee","\\varvdots"),se(ie,le,ce,"\u02ca","\\acute"),se(ie,le,ce,"\u02cb","\\grave"),se(ie,le,ce,"\xa8","\\ddot"),se(ie,le,ce,"~","\\tilde"),se(ie,le,ce,"\u02c9","\\bar"),se(ie,le,ce,"\u02d8","\\breve"),se(ie,le,ce,"\u02c7","\\check"),se(ie,le,ce,"^","\\hat"),se(ie,le,ce,"\u20d7","\\vec"),se(ie,le,ce,"\u02d9","\\dot"),se(ie,le,ce,"\u02da","\\mathring"),se(ie,le,de,"\ue131","\\@imath"),se(ie,le,de,"\ue237","\\@jmath"),se(ie,le,we,"\u0131","\u0131"),se(ie,le,we,"\u0237","\u0237"),se(ae,le,we,"\u0131","\\i",!0),se(ae,le,we,"\u0237","\\j",!0),se(ae,le,we,"\xdf","\\ss",!0),se(ae,le,we,"\xe6","\\ae",!0),se(ae,le,we,"\u0153","\\oe",!0),se(ae,le,we,"\xf8","\\o",!0),se(ae,le,we,"\xc6","\\AE",!0),se(ae,le,we,"\u0152","\\OE",!0),se(ae,le,we,"\xd8","\\O",!0),se(ae,le,ce,"\u02ca","\\'"),se(ae,le,ce,"\u02cb","\\`"),se(ae,le,ce,"\u02c6","\\^"),se(ae,le,ce,"\u02dc","\\~"),se(ae,le,ce,"\u02c9","\\="),se(ae,le,ce,"\u02d8","\\u"),se(ae,le,ce,"\u02d9","\\."),se(ae,le,ce,"\xb8","\\c"),se(ae,le,ce,"\u02da","\\r"),se(ae,le,ce,"\u02c7","\\v"),se(ae,le,ce,"\xa8",'\\"'),se(ae,le,ce,"\u02dd","\\H"),se(ae,le,ce,"\u25ef","\\textcircled");const ve={"--":!0,"---":!0,"``":!0,"''":!0};se(ae,le,we,"\u2013","--",!0),se(ae,le,we,"\u2013","\\textendash"),se(ae,le,we,"\u2014","---",!0),se(ae,le,we,"\u2014","\\textemdash"),se(ae,le,we,"\u2018","`",!0),se(ae,le,we,"\u2018","\\textquoteleft"),se(ae,le,we,"\u2019","'",!0),se(ae,le,we,"\u2019","\\textquoteright"),se(ae,le,we,"\u201c","``",!0),se(ae,le,we,"\u201c","\\textquotedblleft"),se(ae,le,we,"\u201d","''",!0),se(ae,le,we,"\u201d","\\textquotedblright"),se(ie,le,we,"\xb0","\\degree",!0),se(ae,le,we,"\xb0","\\degree"),se(ae,le,we,"\xb0","\\textdegree",!0),se(ie,le,we,"\xa3","\\pounds"),se(ie,le,we,"\xa3","\\mathsterling",!0),se(ae,le,we,"\xa3","\\pounds"),se(ae,le,we,"\xa3","\\textsterling",!0),se(ie,he,we,"\u2720","\\maltese"),se(ae,he,we,"\u2720","\\maltese");const ke='0123456789/@."';for(let e=0;e{if(G(e.classes)!==G(t.classes)||e.skew!==t.skew||e.maxFontSize!==t.maxFontSize)return!1;if(1===e.classes.length){const t=e.classes[0];if("mbin"===t||"mord"===t)return!1}for(const r in e.style)if(e.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;for(const r in t.style)if(t.style.hasOwnProperty(r)&&e.style[r]!==t.style[r])return!1;return!0},Ie=function(e){let t=0,r=0,n=0;for(let o=0;ot&&(t=s.height),s.depth>r&&(r=s.depth),s.maxFontSize>n&&(n=s.maxFontSize)}e.height=t,e.depth=r,e.maxFontSize=n},Re=function(e,t,r,n){const o=new W(e,t,r,n);return Ie(o),o},He=(e,t,r,n)=>new W(e,t,r,n),Oe=function(e){const t=new A(e);return Ie(t),t},Ee=function(e,t,r){let n,o="";switch(e){case"amsrm":o="AMS";break;case"textrm":o="Main";break;case"textsf":o="SansSerif";break;case"texttt":o="Typewriter";break;default:o=e}return n="textbf"===t&&"textit"===r?"BoldItalic":"textbf"===t?"Bold":"textit"===t?"Italic":"Regular",o+"-"+n},Le={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathnormal:{variant:"italic",fontName:"Math-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},De={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]};var Ve={fontMap:Le,makeSymbol:Ne,mathsym:function(e,t,r,n){return void 0===n&&(n=[]),"boldsymbol"===r.font&&Ce(e,"Main-Bold",t).metrics?Ne(e,"Main-Bold",t,r,n.concat(["mathbf"])):"\\"===e||"main"===oe[t][e].font?Ne(e,"Main-Regular",t,r,n):Ne(e,"AMS-Regular",t,r,n.concat(["amsrm"]))},makeSpan:Re,makeSvgSpan:He,makeLineSpan:function(e,t,r){const n=Re([e],[],t);return n.height=Math.max(r||t.fontMetrics().defaultRuleThickness,t.minRuleThickness),n.style.borderBottomWidth=F(n.height),n.maxFontSize=1,n},makeAnchor:function(e,t,r,n){const o=new _(e,t,r,n);return Ie(o),o},makeFragment:Oe,wrapFragment:function(e,t){return e instanceof A?Re([],[e],t):e},makeVList:function(e,t){const{children:r,depth:n}=function(e){if("individualShift"===e.positionType){const t=e.children,r=[t[0]],n=-t[0].shift-t[0].elem.depth;let o=n;for(let e=1;e0)return Ne(s,h,o,t,i.concat(c));if(l){let e,n;if("boldsymbol"===l){const t=function(e,t,r,n,o){return"textord"!==o&&Ce(e,"Math-BoldItalic",t).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(s,o,0,0,r);e=t.fontName,n=[t.fontClass]}else a?(e=Le[l].fontName,n=[l]):(e=Ee(l,t.fontWeight,t.fontShape),n=[l,t.fontWeight,t.fontShape]);if(Ce(s,e,o).metrics)return Ne(s,e,o,t,i.concat(n));if(ve.hasOwnProperty(s)&&"Typewriter"===e.slice(0,10)){const r=[];for(let a=0;a{const r=Re(["mspace"],[],t),n=P(e,t);return r.style.marginRight=F(n),r},staticSvg:function(e,t){const[r,n,o]=De[e],s=new J(r),i=new K([s],{width:F(n),height:F(o),style:"width:"+F(n),viewBox:"0 0 "+1e3*n+" "+1e3*o,preserveAspectRatio:"xMinYMin"}),a=He(["overlay"],[i],t);return a.height=o,a.style.height=F(o),a.style.width=F(n),a},svgData:De,tryCombineChars:e=>{for(let t=0;t{const r=t.classes[0],n=e.classes[0];"mbin"===r&&l.contains(et,n)?t.classes[0]="mord":"mbin"===n&&l.contains(Qe,r)&&(e.classes[0]="mord")}),{node:i},a,h),ot(o,((e,t)=>{const r=at(t),n=at(e),o=r&&n?e.hasClass("mtight")?Ye[r][n]:Ue[r][n]:null;if(o)return Ve.makeGlue(o,s)}),{node:i},a,h),o},ot=function(e,t,r,n,o){n&&e.push(n);let s=0;for(;sr=>{e.splice(t+1,0,r),s++})(s)}n&&e.pop()},st=function(e){return e instanceof A||e instanceof _||e instanceof W&&e.hasClass("enclosing")?e:null},it=function(e,t){const r=st(e);if(r){const e=r.children;if(e.length){if("right"===t)return it(e[e.length-1],"right");if("left"===t)return it(e[0],"left")}}return e},at=function(e,t){return e?(t&&(e=it(e,t)),rt[e.classes[0]]||null):null},lt=function(e,t){const r=["nulldelimiter"].concat(e.baseSizingClasses());return Je(t.concat(r))},ht=function(e,t,r){if(!e)return Je();if(We[e.type]){let n=We[e.type](e,t);if(r&&t.size!==r.size){n=Je(t.sizingClasses(r),[n],t);const e=t.sizeMultiplier/r.sizeMultiplier;n.height*=e,n.depth*=e}return n}throw new n("Got group of unknown type: '"+e.type+"'")};function ct(e,t){const r=Je(["base"],e,t),n=Je(["strut"]);return n.style.height=F(r.height+r.depth),r.depth&&(n.style.verticalAlign=F(-r.depth)),r.children.unshift(n),r}function mt(e,t){let r=null;1===e.length&&"tag"===e[0].type&&(r=e[0].tag,e=e[0].body);const n=nt(e,t,"root");let o;2===n.length&&n[1].hasClass("tag")&&(o=n.pop());const s=[];let i,a=[];for(let e=0;e0&&(s.push(ct(a,t)),a=[]),s.push(n[e]));a.length>0&&s.push(ct(a,t)),r?(i=ct(nt(r,t,!0)),i.classes=["tag"],s.push(i)):o&&s.push(o);const l=Je(["katex-html"],s);if(l.setAttribute("aria-hidden","true"),i){const e=i.children[0];e.style.height=F(l.height+l.depth),l.depth&&(e.style.verticalAlign=F(-l.depth))}return l}function pt(e){return new A(e)}class ut{constructor(e,t,r){this.type=void 0,this.attributes=void 0,this.children=void 0,this.classes=void 0,this.type=e,this.attributes={},this.children=t||[],this.classes=r||[]}setAttribute(e,t){this.attributes[e]=t}getAttribute(e){return this.attributes[e]}toNode(){const e=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(const t in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,t)&&e.setAttribute(t,this.attributes[t]);this.classes.length>0&&(e.className=G(this.classes));for(let t=0;t0&&(e+=' class ="'+l.escape(G(this.classes))+'"'),e+=">";for(let t=0;t",e}toText(){return this.children.map((e=>e.toText())).join("")}}class dt{constructor(e){this.text=void 0,this.text=e}toNode(){return document.createTextNode(this.text)}toMarkup(){return l.escape(this.toText())}toText(){return this.text}}var gt={MathNode:ut,TextNode:dt,SpaceNode:class{constructor(e){this.width=void 0,this.character=void 0,this.width=e,this.character=e>=.05555&&e<=.05556?"\u200a":e>=.1666&&e<=.1667?"\u2009":e>=.2222&&e<=.2223?"\u2005":e>=.2777&&e<=.2778?"\u2005\u200a":e>=-.05556&&e<=-.05555?"\u200a\u2063":e>=-.1667&&e<=-.1666?"\u2009\u2063":e>=-.2223&&e<=-.2222?"\u205f\u2063":e>=-.2778&&e<=-.2777?"\u2005\u2063":null}toNode(){if(this.character)return document.createTextNode(this.character);{const e=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return e.setAttribute("width",F(this.width)),e}}toMarkup(){return this.character?""+this.character+"":''}toText(){return this.character?this.character:" "}},newDocumentFragment:pt};const ft=function(e,t,r){return!oe[t][e]||!oe[t][e].replace||55349===e.charCodeAt(0)||ve.hasOwnProperty(e)&&r&&(r.fontFamily&&"tt"===r.fontFamily.slice(4,6)||r.font&&"tt"===r.font.slice(4,6))||(e=oe[t][e].replace),new gt.TextNode(e)},bt=function(e){return 1===e.length?e[0]:new gt.MathNode("mrow",e)},yt=function(e,t){if("texttt"===t.fontFamily)return"monospace";if("textsf"===t.fontFamily)return"textit"===t.fontShape&&"textbf"===t.fontWeight?"sans-serif-bold-italic":"textit"===t.fontShape?"sans-serif-italic":"textbf"===t.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===t.fontShape&&"textbf"===t.fontWeight)return"bold-italic";if("textit"===t.fontShape)return"italic";if("textbf"===t.fontWeight)return"bold";const r=t.font;if(!r||"mathnormal"===r)return null;const n=e.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"textord"===e.type?"bold":"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";let o=e.text;if(l.contains(["\\imath","\\jmath"],o))return null;oe[n][o]&&oe[n][o].replace&&(o=oe[n][o].replace);return N(o,Ve.fontMap[r].fontName,n)?Ve.fontMap[r].variant:null},xt=function(e,t,r){if(1===e.length){const n=vt(e[0],t);return r&&n instanceof ut&&"mo"===n.type&&(n.setAttribute("lspace","0em"),n.setAttribute("rspace","0em")),[n]}const n=[];let o;for(let r=0;r0&&(e.text=e.text.slice(0,1)+"\u0338"+e.text.slice(1),n.pop())}}}n.push(s),o=s}return n},wt=function(e,t,r){return bt(xt(e,t,r))},vt=function(e,t){if(!e)return new gt.MathNode("mrow");if(_e[e.type]){return _e[e.type](e,t)}throw new n("Got group of unknown type: '"+e.type+"'")};function kt(e,t,r,n,o){const s=xt(e,r);let i;i=1===s.length&&s[0]instanceof ut&&l.contains(["mrow","mtable"],s[0].type)?s[0]:new gt.MathNode("mrow",s);const a=new gt.MathNode("annotation",[new gt.TextNode(t)]);a.setAttribute("encoding","application/x-tex");const h=new gt.MathNode("semantics",[i,a]),c=new gt.MathNode("math",[h]);c.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML"),n&&c.setAttribute("display","block");const m=o?"katex":"katex-mathml";return Ve.makeSpan([m],[c])}const St=function(e){return new E({style:e.displayMode?w.DISPLAY:w.TEXT,maxSize:e.maxSize,minRuleThickness:e.minRuleThickness})},Mt=function(e,t){if(t.displayMode){const r=["katex-display"];t.leqno&&r.push("leqno"),t.fleqn&&r.push("fleqn"),e=Ve.makeSpan(r,[e])}return e},zt=function(e,t,r){const n=St(r);let o;if("mathml"===r.output)return kt(e,t,n,r.displayMode,!0);if("html"===r.output){const t=mt(e,n);o=Ve.makeSpan(["katex"],[t])}else{const s=kt(e,t,n,r.displayMode,!1),i=mt(e,n);o=Ve.makeSpan(["katex"],[s,i])}return Mt(o,r)};const At={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb","\\cdrightarrow":"\u2192","\\cdleftarrow":"\u2190","\\cdlongequal":"="},Tt={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],"\\cdrightarrow":[["rightarrow"],3,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],"\\cdleftarrow":[["leftarrow"],3,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],"\\cdlongequal":[["longequal"],3,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]};var Bt=function(e,t,r,n,o){let s;const i=e.height+e.depth+r+n;if(/fbox|color|angl/.test(t)){if(s=Ve.makeSpan(["stretchy",t],[],o),"fbox"===t){const e=o.color&&o.getColor();e&&(s.style.borderColor=e)}}else{const e=[];/^[bx]cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(t)&&e.push(new Q({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));const r=new K(e,{width:"100%",height:F(i)});s=Ve.makeSvgSpan([],[r],o)}return s.height=i,s.style.height=F(i),s},Ct=function(e){const t=new gt.MathNode("mo",[new gt.TextNode(At[e.replace(/^\\/,"")])]);return t.setAttribute("stretchy","true"),t},Nt=function(e,t){const{span:r,minWidth:n,height:o}=function(){let r=4e5;const n=e.label.slice(1);if(l.contains(["widehat","widecheck","widetilde","utilde"],n)){const s="ordgroup"===(o=e.base).type?o.body.length:1;let i,a,l;if(s>5)"widehat"===n||"widecheck"===n?(i=420,r=2364,l=.42,a=n+"4"):(i=312,r=2340,l=.34,a="tilde4");else{const e=[1,1,2,2,3,3][s];"widehat"===n||"widecheck"===n?(r=[0,1062,2364,2364,2364][e],i=[0,239,300,360,420][e],l=[0,.24,.3,.3,.36,.42][e],a=n+e):(r=[0,600,1033,2339,2340][e],i=[0,260,286,306,312][e],l=[0,.26,.286,.3,.306,.34][e],a="tilde"+e)}const h=new J(a),c=new K([h],{width:"100%",height:F(l),viewBox:"0 0 "+r+" "+i,preserveAspectRatio:"none"});return{span:Ve.makeSvgSpan([],[c],t),minWidth:0,height:l}}{const e=[],o=Tt[n],[s,i,a]=o,l=a/1e3,h=s.length;let c,m;if(1===h){c=["hide-tail"],m=[o[3]]}else if(2===h)c=["halfarrow-left","halfarrow-right"],m=["xMinYMin","xMaxYMin"];else{if(3!==h)throw new Error("Correct katexImagesData or update code here to support\n "+h+" children.");c=["brace-left","brace-center","brace-right"],m=["xMinYMin","xMidYMin","xMaxYMin"]}for(let n=0;n0&&(r.style.minWidth=F(n)),r};function qt(e,t){if(!e||e.type!==t)throw new Error("Expected node of type "+t+", but got "+(e?"node of type "+e.type:String(e)));return e}function It(e){const t=Rt(e);if(!t)throw new Error("Expected node of symbol group type, but got "+(e?"node of type "+e.type:String(e)));return t}function Rt(e){return e&&("atom"===e.type||re.hasOwnProperty(e.type))?e:null}const Ht=(e,t)=>{let r,n,o;e&&"supsub"===e.type?(n=qt(e.base,"accent"),r=n.base,e.base=r,o=function(e){if(e instanceof W)return e;throw new Error("Expected span but got "+String(e)+".")}(ht(e,t)),e.base=n):(n=qt(e,"accent"),r=n.base);const s=ht(r,t.havingCrampedStyle());let i=0;if(n.isShifty&&l.isCharacterBox(r)){const e=l.getBaseElem(r);i=ee(ht(e,t.havingCrampedStyle())).skew}const a="\\c"===n.label;let h,c=a?s.height+s.depth:Math.min(s.height,t.fontMetrics().xHeight);if(n.isStretchy)h=Nt(n,t),h=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"elem",elem:h,wrapperClasses:["svg-align"],wrapperStyle:i>0?{width:"calc(100% - "+F(2*i)+")",marginLeft:F(2*i)}:void 0}]},t);else{let e,r;"\\vec"===n.label?(e=Ve.staticSvg("vec",t),r=Ve.svgData.vec[1]):(e=Ve.makeOrd({mode:n.mode,text:n.label},t,"textord"),e=ee(e),e.italic=0,r=e.width,a&&(c+=e.depth)),h=Ve.makeSpan(["accent-body"],[e]);const o="\\textcircled"===n.label;o&&(h.classes.push("accent-full"),c=s.height);let l=i;o||(l-=r/2),h.style.left=F(l),"\\textcircled"===n.label&&(h.style.top=".2em"),h=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:-c},{type:"elem",elem:h}]},t)}const m=Ve.makeSpan(["mord","accent"],[h],t);return o?(o.children[0]=m,o.height=Math.max(m.height,o.height),o.classes[0]="mord",o):m},Ot=(e,t)=>{const r=e.isStretchy?Ct(e.label):new gt.MathNode("mo",[ft(e.label,e.mode)]),n=new gt.MathNode("mover",[vt(e.base,t),r]);return n.setAttribute("accent","true"),n},Et=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map((e=>"\\"+e)).join("|"));je({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(e,t)=>{const r=Ze(t[0]),n=!Et.test(e.funcName),o=!n||"\\widehat"===e.funcName||"\\widetilde"===e.funcName||"\\widecheck"===e.funcName;return{type:"accent",mode:e.parser.mode,label:e.funcName,isStretchy:n,isShifty:o,base:r}},htmlBuilder:Ht,mathmlBuilder:Ot}),je({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(e,t)=>{const r=t[0];let n=e.parser.mode;return"math"===n&&(e.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+e.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:e.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Ht,mathmlBuilder:Ot}),je({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"accentUnder",mode:r.mode,label:n,base:o}},htmlBuilder:(e,t)=>{const r=ht(e.base,t),n=Nt(e,t),o="\\utilde"===e.label?.12:0,s=Ve.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:o},{type:"elem",elem:r}]},t);return Ve.makeSpan(["mord","accentunder"],[s],t)},mathmlBuilder:(e,t)=>{const r=Ct(e.label),n=new gt.MathNode("munder",[vt(e.base,t),r]);return n.setAttribute("accentunder","true"),n}});const Lt=e=>{const t=new gt.MathNode("mpadded",e?[e]:[]);return t.setAttribute("width","+0.6em"),t.setAttribute("lspace","0.3em"),t};je({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium","\\\\cdrightarrow","\\\\cdleftarrow","\\\\cdlongequal"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n,funcName:o}=e;return{type:"xArrow",mode:n.mode,label:o,body:t[0],below:r[0]}},htmlBuilder(e,t){const r=t.style;let n=t.havingStyle(r.sup());const o=Ve.wrapFragment(ht(e.body,n,t),t),s="\\x"===e.label.slice(0,2)?"x":"cd";let i;o.classes.push(s+"-arrow-pad"),e.below&&(n=t.havingStyle(r.sub()),i=Ve.wrapFragment(ht(e.below,n,t),t),i.classes.push(s+"-arrow-pad"));const a=Nt(e,t),l=-t.fontMetrics().axisHeight+.5*a.height;let h,c=-t.fontMetrics().axisHeight-.5*a.height-.111;if((o.depth>.25||"\\xleftequilibrium"===e.label)&&(c-=o.depth),i){const e=-t.fontMetrics().axisHeight+i.height+.5*a.height+.111;h=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l},{type:"elem",elem:i,shift:e}]},t)}else h=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:c},{type:"elem",elem:a,shift:l}]},t);return h.children[0].children[0].children[1].classes.push("svg-align"),Ve.makeSpan(["mrel","x-arrow"],[h],t)},mathmlBuilder(e,t){const r=Ct(e.label);let n;if(r.setAttribute("minsize","x"===e.label.charAt(0)?"1.75em":"3.0em"),e.body){const o=Lt(vt(e.body,t));if(e.below){const s=Lt(vt(e.below,t));n=new gt.MathNode("munderover",[r,s,o])}else n=new gt.MathNode("mover",[r,o])}else if(e.below){const o=Lt(vt(e.below,t));n=new gt.MathNode("munder",[r,o])}else n=Lt(),n=new gt.MathNode("mover",[r,n]);return n}});const Dt=Ve.makeSpan;function Vt(e,t){const r=nt(e.body,t,!0);return Dt([e.mclass],r,t)}function Pt(e,t){let r;const n=xt(e.body,t);return"minner"===e.mclass?r=new gt.MathNode("mpadded",n):"mord"===e.mclass?e.isCharacterBox?(r=n[0],r.type="mi"):r=new gt.MathNode("mi",n):(e.isCharacterBox?(r=n[0],r.type="mo"):r=new gt.MathNode("mo",n),"mbin"===e.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"===e.mclass||"mclose"===e.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0em"):"minner"===e.mclass&&(r.attributes.lspace="0.0556em",r.attributes.width="+0.1111em")),r}je({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1,primitive:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"mclass",mode:r.mode,mclass:"m"+n.slice(5),body:Ke(o),isCharacterBox:l.isCharacterBox(o)}},htmlBuilder:Vt,mathmlBuilder:Pt});const Ft=e=>{const t="ordgroup"===e.type&&e.body.length?e.body[0]:e;return"atom"!==t.type||"bin"!==t.family&&"rel"!==t.family?"mord":"m"+t.family};je({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler(e,t){let{parser:r}=e;return{type:"mclass",mode:r.mode,mclass:Ft(t[0]),body:Ke(t[1]),isCharacterBox:l.isCharacterBox(t[1])}}}),je({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler(e,t){let{parser:r,funcName:n}=e;const o=t[1],s=t[0];let i;i="\\stackrel"!==n?Ft(o):"mrel";const a={type:"op",mode:o.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:Ke(o)},h={type:"supsub",mode:s.mode,base:a,sup:"\\underset"===n?null:s,sub:"\\underset"===n?s:null};return{type:"mclass",mode:r.mode,mclass:i,body:[h],isCharacterBox:l.isCharacterBox(h)}},htmlBuilder:Vt,mathmlBuilder:Pt}),je({type:"pmb",names:["\\pmb"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"pmb",mode:r.mode,mclass:Ft(t[0]),body:Ke(t[0])}},htmlBuilder(e,t){const r=nt(e.body,t,!0),n=Ve.makeSpan([e.mclass],r,t);return n.style.textShadow="0.02em 0.01em 0.04px",n},mathmlBuilder(e,t){const r=xt(e.body,t),n=new gt.MathNode("mstyle",r);return n.setAttribute("style","text-shadow: 0.02em 0.01em 0.04px"),n}});const Gt={">":"\\\\cdrightarrow","<":"\\\\cdleftarrow","=":"\\\\cdlongequal",A:"\\uparrow",V:"\\downarrow","|":"\\Vert",".":"no arrow"},Ut=e=>"textord"===e.type&&"@"===e.text;function Yt(e,t,r){const n=Gt[e];switch(n){case"\\\\cdrightarrow":case"\\\\cdleftarrow":return r.callFunction(n,[t[0]],[t[1]]);case"\\uparrow":case"\\downarrow":{const e={type:"atom",text:n,mode:"math",family:"rel"},o={type:"ordgroup",mode:"math",body:[r.callFunction("\\\\cdleft",[t[0]],[]),r.callFunction("\\Big",[e],[]),r.callFunction("\\\\cdright",[t[1]],[])]};return r.callFunction("\\\\cdparent",[o],[])}case"\\\\cdlongequal":return r.callFunction("\\\\cdlongequal",[],[]);case"\\Vert":{const e={type:"textord",text:"\\Vert",mode:"math"};return r.callFunction("\\Big",[e],[])}default:return{type:"textord",text:" ",mode:"math"}}}je({type:"cdlabel",names:["\\\\cdleft","\\\\cdright"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"cdlabel",mode:r.mode,side:n.slice(4),label:t[0]}},htmlBuilder(e,t){const r=t.havingStyle(t.style.sup()),n=Ve.wrapFragment(ht(e.label,r,t),t);return n.classes.push("cd-label-"+e.side),n.style.bottom=F(.8-n.depth),n.height=0,n.depth=0,n},mathmlBuilder(e,t){let r=new gt.MathNode("mrow",[vt(e.label,t)]);return r=new gt.MathNode("mpadded",[r]),r.setAttribute("width","0"),"left"===e.side&&r.setAttribute("lspace","-1width"),r.setAttribute("voffset","0.7em"),r=new gt.MathNode("mstyle",[r]),r.setAttribute("displaystyle","false"),r.setAttribute("scriptlevel","1"),r}}),je({type:"cdlabelparent",names:["\\\\cdparent"],props:{numArgs:1},handler(e,t){let{parser:r}=e;return{type:"cdlabelparent",mode:r.mode,fragment:t[0]}},htmlBuilder(e,t){const r=Ve.wrapFragment(ht(e.fragment,t),t);return r.classes.push("cd-vert-arrow"),r},mathmlBuilder(e,t){return new gt.MathNode("mrow",[vt(e.fragment,t)])}}),je({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;const o=qt(t[0],"ordgroup").body;let s="";for(let e=0;e=1114111)throw new n("\\@char with invalid code point "+s);return a<=65535?i=String.fromCharCode(a):(a-=65536,i=String.fromCharCode(55296+(a>>10),56320+(1023&a))),{type:"textord",mode:r.mode,text:i}}});const Xt=(e,t)=>{const r=nt(e.body,t.withColor(e.color),!1);return Ve.makeFragment(r)},Wt=(e,t)=>{const r=xt(e.body,t.withColor(e.color)),n=new gt.MathNode("mstyle",r);return n.setAttribute("mathcolor",e.color),n};je({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,argTypes:["color","original"]},handler(e,t){let{parser:r}=e;const n=qt(t[0],"color-token").color,o=t[1];return{type:"color",mode:r.mode,color:n,body:Ke(o)}},htmlBuilder:Xt,mathmlBuilder:Wt}),je({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,argTypes:["color"]},handler(e,t){let{parser:r,breakOnTokenText:n}=e;const o=qt(t[0],"color-token").color;r.gullet.macros.set("\\current@color",o);const s=r.parseExpression(!0,n);return{type:"color",mode:r.mode,color:o,body:s}},htmlBuilder:Xt,mathmlBuilder:Wt}),je({type:"cr",names:["\\\\"],props:{numArgs:0,numOptionalArgs:0,allowedInText:!0},handler(e,t,r){let{parser:n}=e;const o="["===n.gullet.future().text?n.parseSizeGroup(!0):null,s=!n.settings.displayMode||!n.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode");return{type:"cr",mode:n.mode,newLine:s,size:o&&qt(o,"size").value}},htmlBuilder(e,t){const r=Ve.makeSpan(["mspace"],[],t);return e.newLine&&(r.classes.push("newline"),e.size&&(r.style.marginTop=F(P(e.size,t)))),r},mathmlBuilder(e,t){const r=new gt.MathNode("mspace");return e.newLine&&(r.setAttribute("linebreak","newline"),e.size&&r.setAttribute("height",F(P(e.size,t)))),r}});const _t={"\\global":"\\global","\\long":"\\\\globallong","\\\\globallong":"\\\\globallong","\\def":"\\gdef","\\gdef":"\\gdef","\\edef":"\\xdef","\\xdef":"\\xdef","\\let":"\\\\globallet","\\futurelet":"\\\\globalfuture"},jt=e=>{const t=e.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(t))throw new n("Expected a control sequence",e);return t},$t=(e,t,r,n)=>{let o=e.gullet.macros.get(r.text);null==o&&(r.noexpand=!0,o={tokens:[r],numArgs:0,unexpandable:!e.gullet.isExpandable(r.text)}),e.gullet.macros.set(t,o,n)};je({type:"internal",names:["\\global","\\long","\\\\globallong"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t,funcName:r}=e;t.consumeSpaces();const o=t.fetch();if(_t[o.text])return"\\global"!==r&&"\\\\globallong"!==r||(o.text=_t[o.text]),qt(t.parseFunction(),"internal");throw new n("Invalid token after macro prefix",o)}}),je({type:"internal",names:["\\def","\\gdef","\\edef","\\xdef"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e,o=t.gullet.popToken();const s=o.text;if(/^(?:[\\{}$&#^_]|EOF)$/.test(s))throw new n("Expected a control sequence",o);let i,a=0;const l=[[]];for(;"{"!==t.gullet.future().text;)if(o=t.gullet.popToken(),"#"===o.text){if("{"===t.gullet.future().text){i=t.gullet.future(),l[a].push("{");break}if(o=t.gullet.popToken(),!/^[1-9]$/.test(o.text))throw new n('Invalid argument number "'+o.text+'"');if(parseInt(o.text)!==a+1)throw new n('Argument number "'+o.text+'" out of order');a++,l.push([])}else{if("EOF"===o.text)throw new n("Expected a macro definition");l[a].push(o.text)}let{tokens:h}=t.gullet.consumeArg();return i&&h.unshift(i),"\\edef"!==r&&"\\xdef"!==r||(h=t.gullet.expandTokens(h),h.reverse()),t.gullet.macros.set(s,{tokens:h,numArgs:a,delimiters:l},r===_t[r]),{type:"internal",mode:t.mode}}}),je({type:"internal",names:["\\let","\\\\globallet"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=jt(t.gullet.popToken());t.gullet.consumeSpaces();const o=(e=>{let t=e.gullet.popToken();return"="===t.text&&(t=e.gullet.popToken()," "===t.text&&(t=e.gullet.popToken())),t})(t);return $t(t,n,o,"\\\\globallet"===r),{type:"internal",mode:t.mode}}}),je({type:"internal",names:["\\futurelet","\\\\globalfuture"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e){let{parser:t,funcName:r}=e;const n=jt(t.gullet.popToken()),o=t.gullet.popToken(),s=t.gullet.popToken();return $t(t,n,s,"\\\\globalfuture"===r),t.gullet.pushToken(s),t.gullet.pushToken(o),{type:"internal",mode:t.mode}}});const Zt=function(e,t,r){const n=N(oe.math[e]&&oe.math[e].replace||e,t,r);if(!n)throw new Error("Unsupported symbol "+e+" and font size "+t+".");return n},Kt=function(e,t,r,n){const o=r.havingBaseStyle(t),s=Ve.makeSpan(n.concat(o.sizingClasses(r)),[e],r),i=o.sizeMultiplier/r.sizeMultiplier;return s.height*=i,s.depth*=i,s.maxFontSize=o.sizeMultiplier,s},Jt=function(e,t,r){const n=t.havingBaseStyle(r),o=(1-t.sizeMultiplier/n.sizeMultiplier)*t.fontMetrics().axisHeight;e.classes.push("delimcenter"),e.style.top=F(o),e.height-=o,e.depth+=o},Qt=function(e,t,r,n,o,s){const i=function(e,t,r,n){return Ve.makeSymbol(e,"Size"+t+"-Regular",r,n)}(e,t,o,n),a=Kt(Ve.makeSpan(["delimsizing","size"+t],[i],n),w.TEXT,n,s);return r&&Jt(a,n,w.TEXT),a},er=function(e,t,r){let n;n="Size1-Regular"===t?"delim-size1":"delim-size4";return{type:"elem",elem:Ve.makeSpan(["delimsizinginner",n],[Ve.makeSpan([],[Ve.makeSymbol(e,t,r)])])}},tr=function(e,t,r){const n=T["Size4-Regular"][e.charCodeAt(0)]?T["Size4-Regular"][e.charCodeAt(0)][4]:T["Size1-Regular"][e.charCodeAt(0)][4],o=new J("inner",function(e,t){switch(e){case"\u239c":return"M291 0 H417 V"+t+" H291z M291 0 H417 V"+t+" H291z";case"\u2223":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145z";case"\u2225":return"M145 0 H188 V"+t+" H145z M145 0 H188 V"+t+" H145zM367 0 H410 V"+t+" H367z M367 0 H410 V"+t+" H367z";case"\u239f":return"M457 0 H583 V"+t+" H457z M457 0 H583 V"+t+" H457z";case"\u23a2":return"M319 0 H403 V"+t+" H319z M319 0 H403 V"+t+" H319z";case"\u23a5":return"M263 0 H347 V"+t+" H263z M263 0 H347 V"+t+" H263z";case"\u23aa":return"M384 0 H504 V"+t+" H384z M384 0 H504 V"+t+" H384z";case"\u23d0":return"M312 0 H355 V"+t+" H312z M312 0 H355 V"+t+" H312z";case"\u2016":return"M257 0 H300 V"+t+" H257z M257 0 H300 V"+t+" H257zM478 0 H521 V"+t+" H478z M478 0 H521 V"+t+" H478z";default:return""}}(e,Math.round(1e3*t))),s=new K([o],{width:F(n),height:F(t),style:"width:"+F(n),viewBox:"0 0 "+1e3*n+" "+Math.round(1e3*t),preserveAspectRatio:"xMinYMin"}),i=Ve.makeSvgSpan([],[s],r);return i.height=t,i.style.height=F(t),i.style.width=F(n),{type:"elem",elem:i}},rr={type:"kern",size:-.008},nr=["|","\\lvert","\\rvert","\\vert"],or=["\\|","\\lVert","\\rVert","\\Vert"],sr=function(e,t,r,n,o,s){let i,a,h,c,m="",p=0;i=h=c=e,a=null;let u="Size1-Regular";"\\uparrow"===e?h=c="\u23d0":"\\Uparrow"===e?h=c="\u2016":"\\downarrow"===e?i=h="\u23d0":"\\Downarrow"===e?i=h="\u2016":"\\updownarrow"===e?(i="\\uparrow",h="\u23d0",c="\\downarrow"):"\\Updownarrow"===e?(i="\\Uparrow",h="\u2016",c="\\Downarrow"):l.contains(nr,e)?(h="\u2223",m="vert",p=333):l.contains(or,e)?(h="\u2225",m="doublevert",p=556):"["===e||"\\lbrack"===e?(i="\u23a1",h="\u23a2",c="\u23a3",u="Size4-Regular",m="lbrack",p=667):"]"===e||"\\rbrack"===e?(i="\u23a4",h="\u23a5",c="\u23a6",u="Size4-Regular",m="rbrack",p=667):"\\lfloor"===e||"\u230a"===e?(h=i="\u23a2",c="\u23a3",u="Size4-Regular",m="lfloor",p=667):"\\lceil"===e||"\u2308"===e?(i="\u23a1",h=c="\u23a2",u="Size4-Regular",m="lceil",p=667):"\\rfloor"===e||"\u230b"===e?(h=i="\u23a5",c="\u23a6",u="Size4-Regular",m="rfloor",p=667):"\\rceil"===e||"\u2309"===e?(i="\u23a4",h=c="\u23a5",u="Size4-Regular",m="rceil",p=667):"("===e||"\\lparen"===e?(i="\u239b",h="\u239c",c="\u239d",u="Size4-Regular",m="lparen",p=875):")"===e||"\\rparen"===e?(i="\u239e",h="\u239f",c="\u23a0",u="Size4-Regular",m="rparen",p=875):"\\{"===e||"\\lbrace"===e?(i="\u23a7",a="\u23a8",c="\u23a9",h="\u23aa",u="Size4-Regular"):"\\}"===e||"\\rbrace"===e?(i="\u23ab",a="\u23ac",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\lgroup"===e||"\u27ee"===e?(i="\u23a7",c="\u23a9",h="\u23aa",u="Size4-Regular"):"\\rgroup"===e||"\u27ef"===e?(i="\u23ab",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\lmoustache"===e||"\u23b0"===e?(i="\u23a7",c="\u23ad",h="\u23aa",u="Size4-Regular"):"\\rmoustache"!==e&&"\u23b1"!==e||(i="\u23ab",c="\u23a9",h="\u23aa",u="Size4-Regular");const d=Zt(i,u,o),g=d.height+d.depth,f=Zt(h,u,o),b=f.height+f.depth,y=Zt(c,u,o),x=y.height+y.depth;let v=0,k=1;if(null!==a){const e=Zt(a,u,o);v=e.height+e.depth,k=2}const S=g+x+v,M=S+Math.max(0,Math.ceil((t-S)/(k*b)))*k*b;let z=n.fontMetrics().axisHeight;r&&(z*=n.sizeMultiplier);const A=M/2-z,T=[];if(m.length>0){const e=M-g-x,t=Math.round(1e3*M),r=function(e,t){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v1759 h347 v-84\nH403z M403 1759 V0 H319 V1759 v"+t+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v1759 H0 v84 H347z\nM347 1759 V0 H263 V1759 v"+t+" v1759 h84z";case"vert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z";case"doublevert":return"M145 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M188 15 H145 v585 v"+t+" v585 h43z\nM367 15 v585 v"+t+" v585 c2.667,10,9.667,15,21,15\nc10,0,16.667,-5,20,-15 v-585 v"+-t+" v-585 c-2.667,-10,-9.667,-15,-21,-15\nc-10,0,-16.667,5,-20,15z M410 15 H367 v585 v"+t+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+t+" v1715 h263 v84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+t+" v1799 H0 v-84 H319z\nMM319 602 V0 H403 V602 v"+t+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+t+" v602 h84z\nM403 1759 V0 H319 V1759 v"+t+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+t+" v602 h84z\nM347 1759 V0 h-84 V1759 v"+t+" v602 h84z";case"lparen":return"M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1\nc-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,\n-36,557 l0,"+(t+84)+"c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,\n949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9\nc0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,\n-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189\nl0,-"+(t+92)+"c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,\n-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z";case"rparen":return"M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,\n63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5\nc11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,"+(t+9)+"\nc-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664\nc-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11\nc0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17\nc242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558\nl0,-"+(t+144)+"c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,\n-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z";default:throw new Error("Unknown stretchy delimiter.")}}(m,Math.round(1e3*e)),o=new J(m,r),s=(p/1e3).toFixed(3)+"em",i=(t/1e3).toFixed(3)+"em",a=new K([o],{width:s,height:i,viewBox:"0 0 "+p+" "+t}),l=Ve.makeSvgSpan([],[a],n);l.height=t/1e3,l.style.width=s,l.style.height=i,T.push({type:"elem",elem:l})}else{if(T.push(er(c,u,o)),T.push(rr),null===a){const e=M-g-x+.016;T.push(tr(h,e,n))}else{const e=(M-g-x-v)/2+.016;T.push(tr(h,e,n)),T.push(rr),T.push(er(a,u,o)),T.push(rr),T.push(tr(h,e,n))}T.push(rr),T.push(er(i,u,o))}const B=n.havingBaseStyle(w.TEXT),C=Ve.makeVList({positionType:"bottom",positionData:A,children:T},B);return Kt(Ve.makeSpan(["delimsizing","mult"],[C],B),w.TEXT,n,s)},ir=.08,ar=function(e,t,r,n,o){const s=function(e,t,r){t*=1e3;let n="";switch(e){case"sqrtMain":n=function(e,t){return"M95,"+(622+e+t)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+e/2.075+" -"+e+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+e)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize1":n=function(e,t){return"M263,"+(601+e+t)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+e/2.084+" -"+e+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+e)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize2":n=function(e,t){return"M983 "+(10+e+t)+"\nl"+e/3.13+" -"+e+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+e)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+e)+" "+t+"h400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize3":n=function(e,t){return"M424,"+(2398+e+t)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+e/4.223+" -"+e+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+e)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+e)+" "+t+"\nh400000v"+(40+e)+"h-400000z"}(t,M);break;case"sqrtSize4":n=function(e,t){return"M473,"+(2713+e+t)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+e/5.298+" -"+e+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+e)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+e)+" "+t+"h400000v"+(40+e)+"H1017.7z"}(t,M);break;case"sqrtTall":n=function(e,t,r){return"M702 "+(e+t)+"H400000"+(40+e)+"\nH742v"+(r-54-t-e)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+t+"H400000v"+(40+e)+"H742z"}(t,M,r)}return n}(e,n,r),i=new J(e,s),a=new K([i],{width:"400em",height:F(t),viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Ve.makeSvgSpan(["hide-tail"],[a],o)},lr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"],hr=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"],cr=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],mr=[0,1.2,1.8,2.4,3],pr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],ur=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],dr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],gr=function(e){if("small"===e.type)return"Main-Regular";if("large"===e.type)return"Size"+e.size+"-Regular";if("stack"===e.type)return"Size4-Regular";throw new Error("Add support for delim type '"+e.type+"' here.")},fr=function(e,t,r,n){for(let o=Math.min(2,3-n.style.size);ot)return r[o]}return r[r.length-1]},br=function(e,t,r,n,o,s){let i;"<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),i=l.contains(cr,e)?pr:l.contains(lr,e)?dr:ur;const a=fr(e,t,i,n);return"small"===a.type?function(e,t,r,n,o,s){const i=Ve.makeSymbol(e,"Main-Regular",o,n),a=Kt(i,t,n,s);return r&&Jt(a,n,t),a}(e,a.style,r,n,o,s):"large"===a.type?Qt(e,a.size,r,n,o,s):sr(e,t,r,n,o,s)};var yr={sqrtImage:function(e,t){const r=t.havingBaseSizing(),n=fr("\\surd",e*r.sizeMultiplier,dr,r);let o=r.sizeMultiplier;const s=Math.max(0,t.minRuleThickness-t.fontMetrics().sqrtRuleThickness);let i,a,l=0,h=0,c=0;return"small"===n.type?(c=1e3+1e3*s+80,e<1?o=1:e<1.4&&(o=.7),l=(1+s+ir)/o,h=(1+s)/o,i=ar("sqrtMain",l,c,s,t),i.style.minWidth="0.853em",a=.833/o):"large"===n.type?(c=1080*mr[n.size],h=(mr[n.size]+s)/o,l=(mr[n.size]+s+ir)/o,i=ar("sqrtSize"+n.size,l,c,s,t),i.style.minWidth="1.02em",a=1/o):(l=e+s+ir,h=e+s,c=Math.floor(1e3*e+s)+80,i=ar("sqrtTall",l,c,s,t),i.style.minWidth="0.742em",a=1.056),i.height=h,i.style.height=F(l),{span:i,advanceWidth:a,ruleWidth:(t.fontMetrics().sqrtRuleThickness+s)*o}},sizedDelim:function(e,t,r,o,s){if("<"===e||"\\lt"===e||"\u27e8"===e?e="\\langle":">"!==e&&"\\gt"!==e&&"\u27e9"!==e||(e="\\rangle"),l.contains(lr,e)||l.contains(cr,e))return Qt(e,t,!1,r,o,s);if(l.contains(hr,e))return sr(e,mr[t],!1,r,o,s);throw new n("Illegal delimiter: '"+e+"'")},sizeToMaxHeight:mr,customSizedDelim:br,leftRightDelim:function(e,t,r,n,o,s){const i=n.fontMetrics().axisHeight*n.sizeMultiplier,a=5/n.fontMetrics().ptPerEm,l=Math.max(t-i,r+i),h=Math.max(l/500*901,2*l-a);return br(e,h,!0,n,o,s)}};const xr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},wr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function vr(e,t){const r=Rt(e);if(r&&l.contains(wr,r.text))return r;throw new n(r?"Invalid delimiter '"+r.text+"' after '"+t.funcName+"'":"Invalid delimiter type '"+e.type+"'",e)}function kr(e){if(!e.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}je({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1,argTypes:["primitive"]},handler:(e,t)=>{const r=vr(t[0],e);return{type:"delimsizing",mode:e.parser.mode,size:xr[e.funcName].size,mclass:xr[e.funcName].mclass,delim:r.text}},htmlBuilder:(e,t)=>"."===e.delim?Ve.makeSpan([e.mclass]):yr.sizedDelim(e.delim,e.size,t,e.mode,[e.mclass]),mathmlBuilder:e=>{const t=[];"."!==e.delim&&t.push(ft(e.delim,e.mode));const r=new gt.MathNode("mo",t);"mopen"===e.mclass||"mclose"===e.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r.setAttribute("stretchy","true");const n=F(yr.sizeToMaxHeight[e.size]);return r.setAttribute("minsize",n),r.setAttribute("maxsize",n),r}}),je({type:"leftright-right",names:["\\right"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=e.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new n("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:e.parser.mode,delim:vr(t[0],e).text,color:r}}}),je({type:"leftright",names:["\\left"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=vr(t[0],e),n=e.parser;++n.leftrightDepth;const o=n.parseExpression(!1);--n.leftrightDepth,n.expect("\\right",!1);const s=qt(n.parseFunction(),"leftright-right");return{type:"leftright",mode:n.mode,body:o,left:r.text,right:s.delim,rightColor:s.color}},htmlBuilder:(e,t)=>{kr(e);const r=nt(e.body,t,!0,["mopen","mclose"]);let n,o,s=0,i=0,a=!1;for(let e=0;e{kr(e);const r=xt(e.body,t);if("."!==e.left){const t=new gt.MathNode("mo",[ft(e.left,e.mode)]);t.setAttribute("fence","true"),r.unshift(t)}if("."!==e.right){const t=new gt.MathNode("mo",[ft(e.right,e.mode)]);t.setAttribute("fence","true"),e.rightColor&&t.setAttribute("mathcolor",e.rightColor),r.push(t)}return bt(r)}}),je({type:"middle",names:["\\middle"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{const r=vr(t[0],e);if(!e.parser.leftrightDepth)throw new n("\\middle without preceding \\left",r);return{type:"middle",mode:e.parser.mode,delim:r.text}},htmlBuilder:(e,t)=>{let r;if("."===e.delim)r=lt(t,[]);else{r=yr.sizedDelim(e.delim,1,t,e.mode,[]);const n={delim:e.delim,options:t};r.isMiddle=n}return r},mathmlBuilder:(e,t)=>{const r="\\vert"===e.delim||"|"===e.delim?ft("|","text"):ft(e.delim,e.mode),n=new gt.MathNode("mo",[r]);return n.setAttribute("fence","true"),n.setAttribute("lspace","0.05em"),n.setAttribute("rspace","0.05em"),n}});const Sr=(e,t)=>{const r=Ve.wrapFragment(ht(e.body,t),t),n=e.label.slice(1);let o,s=t.sizeMultiplier,i=0;const a=l.isCharacterBox(e.body);if("sout"===n)o=Ve.makeSpan(["stretchy","sout"]),o.height=t.fontMetrics().defaultRuleThickness/s,i=-.5*t.fontMetrics().xHeight;else if("phase"===n){const e=P({number:.6,unit:"pt"},t),n=P({number:.35,unit:"ex"},t);s/=t.havingBaseSizing().sizeMultiplier;const a=r.height+r.depth+e+n;r.style.paddingLeft=F(a/2+e);const l=Math.floor(1e3*a*s),c="M400000 "+(h=l)+" H0 L"+h/2+" 0 l65 45 L145 "+(h-80)+" H400000z",m=new K([new J("phase",c)],{width:"400em",height:F(l/1e3),viewBox:"0 0 400000 "+l,preserveAspectRatio:"xMinYMin slice"});o=Ve.makeSvgSpan(["hide-tail"],[m],t),o.style.height=F(a),i=r.depth+e+n}else{/cancel/.test(n)?a||r.classes.push("cancel-pad"):"angl"===n?r.classes.push("anglpad"):r.classes.push("boxpad");let s=0,l=0,h=0;/box/.test(n)?(h=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness),s=t.fontMetrics().fboxsep+("colorbox"===n?0:h),l=s):"angl"===n?(h=Math.max(t.fontMetrics().defaultRuleThickness,t.minRuleThickness),s=4*h,l=Math.max(0,.25-r.depth)):(s=a?.2:0,l=s),o=Bt(r,n,s,l,t),/fbox|boxed|fcolorbox/.test(n)?(o.style.borderStyle="solid",o.style.borderWidth=F(h)):"angl"===n&&.049!==h&&(o.style.borderTopWidth=F(h),o.style.borderRightWidth=F(h)),i=r.depth+l,e.backgroundColor&&(o.style.backgroundColor=e.backgroundColor,e.borderColor&&(o.style.borderColor=e.borderColor))}var h;let c;if(e.backgroundColor)c=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:o,shift:i},{type:"elem",elem:r,shift:0}]},t);else{const e=/cancel|phase/.test(n)?["svg-align"]:[];c=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:0},{type:"elem",elem:o,shift:i,wrapperClasses:e}]},t)}return/cancel/.test(n)&&(c.height=r.height,c.depth=r.depth),/cancel/.test(n)&&!a?Ve.makeSpan(["mord","cancel-lap"],[c],t):Ve.makeSpan(["mord"],[c],t)},Mr=(e,t)=>{let r=0;const n=new gt.MathNode(e.label.indexOf("colorbox")>-1?"mpadded":"menclose",[vt(e.body,t)]);switch(e.label){case"\\cancel":n.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":n.setAttribute("notation","downdiagonalstrike");break;case"\\phase":n.setAttribute("notation","phasorangle");break;case"\\sout":n.setAttribute("notation","horizontalstrike");break;case"\\fbox":n.setAttribute("notation","box");break;case"\\angl":n.setAttribute("notation","actuarial");break;case"\\fcolorbox":case"\\colorbox":if(r=t.fontMetrics().fboxsep*t.fontMetrics().ptPerEm,n.setAttribute("width","+"+2*r+"pt"),n.setAttribute("height","+"+2*r+"pt"),n.setAttribute("lspace",r+"pt"),n.setAttribute("voffset",r+"pt"),"\\fcolorbox"===e.label){const r=Math.max(t.fontMetrics().fboxrule,t.minRuleThickness);n.setAttribute("style","border: "+r+"em solid "+String(e.borderColor))}break;case"\\xcancel":n.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return e.backgroundColor&&n.setAttribute("mathbackground",e.backgroundColor),n};je({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,argTypes:["color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=qt(t[0],"color-token").color,i=t[1];return{type:"enclose",mode:n.mode,label:o,backgroundColor:s,body:i}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,argTypes:["color","color","text"]},handler(e,t,r){let{parser:n,funcName:o}=e;const s=qt(t[0],"color-token").color,i=qt(t[1],"color-token").color,a=t[2];return{type:"enclose",mode:n.mode,label:o,backgroundColor:i,borderColor:s,body:a}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\fbox",body:t[0]}}}),je({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout","\\phase"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"enclose",mode:r.mode,label:n,body:o}},htmlBuilder:Sr,mathmlBuilder:Mr}),je({type:"enclose",names:["\\angl"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"enclose",mode:r.mode,label:"\\angl",body:t[0]}}});const zr={};function Ar(e){let{type:t,names:r,props:n,handler:o,htmlBuilder:s,mathmlBuilder:i}=e;const a={type:t,numArgs:n.numArgs||0,allowedInText:!1,numOptionalArgs:0,handler:o};for(let e=0;e{if(!e.parser.settings.displayMode)throw new n("{"+e.envName+"} can be used only in display mode.")};function Rr(e){if(-1===e.indexOf("ed"))return-1===e.indexOf("*")}function Hr(e,t,r){let{hskipBeforeAndAfter:o,addJot:s,cols:i,arraystretch:a,colSeparationType:l,autoTag:h,singleRow:c,emptySingleRow:m,maxNumCols:p,leqno:u}=t;if(e.gullet.beginGroup(),c||e.gullet.macros.set("\\cr","\\\\\\relax"),!a){const t=e.gullet.expandMacroAsText("\\arraystretch");if(null==t)a=1;else if(a=parseFloat(t),!a||a<0)throw new n("Invalid \\arraystretch: "+t)}e.gullet.beginGroup();let d=[];const g=[d],f=[],b=[],y=null!=h?[]:void 0;function x(){h&&e.gullet.macros.set("\\@eqnsw","1",!0)}function w(){y&&(e.gullet.macros.get("\\df@tag")?(y.push(e.subparse([new Nr("\\df@tag")])),e.gullet.macros.set("\\df@tag",void 0,!0)):y.push(Boolean(h)&&"1"===e.gullet.macros.get("\\@eqnsw")))}for(x(),b.push(qr(e));;){let t=e.parseExpression(!1,c?"\\end":"\\\\");e.gullet.endGroup(),e.gullet.beginGroup(),t={type:"ordgroup",mode:e.mode,body:t},r&&(t={type:"styling",mode:e.mode,style:r,body:[t]}),d.push(t);const o=e.fetch().text;if("&"===o){if(p&&d.length===p){if(c||l)throw new n("Too many tab characters: &",e.nextToken);e.settings.reportNonstrict("textEnv","Too few columns specified in the {array} column argument.")}e.consume()}else{if("\\end"===o){w(),1===d.length&&"styling"===t.type&&0===t.body[0].body.length&&(g.length>1||!m)&&g.pop(),b.length0&&(x+=.25),c.push({pos:x,isDashed:e[t]})}for(v(i[0]),r=0;r0&&(p+=y,le)))for(r=0;r=a)continue;(o>0||e.hskipBeforeAndAfter)&&(i=l.deflt(c.pregap,u),0!==i&&(z=Ve.makeSpan(["arraycolsep"],[]),z.style.width=F(i),M.push(z)));let d=[];for(r=0;r0){const e=Ve.makeLineSpan("hline",t,m),r=Ve.makeLineSpan("hdashline",t,m),n=[{type:"elem",elem:h,shift:0}];for(;c.length>0;){const t=c.pop(),o=t.pos-k;t.isDashed?n.push({type:"elem",elem:r,shift:o}):n.push({type:"elem",elem:e,shift:o})}h=Ve.makeVList({positionType:"individualShift",children:n},t)}if(0===T.length)return Ve.makeSpan(["mord"],[h],t);{let e=Ve.makeVList({positionType:"individualShift",children:T},t);return e=Ve.makeSpan(["tag"],[e],t),Ve.makeFragment([h,e])}},Lr={c:"center ",l:"left ",r:"right "},Dr=function(e,t){const r=[],n=new gt.MathNode("mtd",[],["mtr-glue"]),o=new gt.MathNode("mtd",[],["mml-eqn-num"]);for(let s=0;s0){const t=e.cols;let r="",n=!1,o=0,i=t.length;"separator"===t[0].type&&(a+="top ",o=1),"separator"===t[t.length-1].type&&(a+="bottom ",i-=1);for(let e=o;e0?"left ":"",a+=c[c.length-1].length>0?"right ":"";for(let e=1;e-1?"alignat":"align",s="split"===e.envName,i=Hr(e.parser,{cols:r,addJot:!0,autoTag:s?void 0:Rr(e.envName),emptySingleRow:!0,colSeparationType:o,maxNumCols:s?2:void 0,leqno:e.parser.settings.leqno},"display");let a,l=0;const h={type:"ordgroup",mode:e.mode,body:[]};if(t[0]&&"ordgroup"===t[0].type){let e="";for(let r=0;r0&&c&&(n=1),r[e]={type:"align",align:t,pregap:n,postgap:0}}return i.colSeparationType=c?"align":"alignat",i};Ar({type:"array",names:["array","darray"],props:{numArgs:1},handler(e,t){const r=(Rt(t[0])?[t[0]]:qt(t[0],"ordgroup").body).map((function(e){const t=It(e).text;if(-1!=="lcr".indexOf(t))return{type:"align",align:t};if("|"===t)return{type:"separator",separator:"|"};if(":"===t)return{type:"separator",separator:":"};throw new n("Unknown column alignment: "+t,e)})),o={cols:r,hskipBeforeAndAfter:!0,maxNumCols:r.length};return Hr(e.parser,o,Or(e.envName))},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix","matrix*","pmatrix*","bmatrix*","Bmatrix*","vmatrix*","Vmatrix*"],props:{numArgs:0},handler(e){const t={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[e.envName.replace("*","")];let r="c";const o={hskipBeforeAndAfter:!1,cols:[{type:"align",align:r}]};if("*"===e.envName.charAt(e.envName.length-1)){const t=e.parser;if(t.consumeSpaces(),"["===t.fetch().text){if(t.consume(),t.consumeSpaces(),r=t.fetch().text,-1==="lcr".indexOf(r))throw new n("Expected l or c or r",t.nextToken);t.consume(),t.consumeSpaces(),t.expect("]"),t.consume(),o.cols=[{type:"align",align:r}]}}const s=Hr(e.parser,o,Or(e.envName)),i=Math.max(0,...s.body.map((e=>e.length)));return s.cols=new Array(i).fill({type:"align",align:r}),t?{type:"leftright",mode:e.mode,body:[s],left:t[0],right:t[1],rightColor:void 0}:s},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["smallmatrix"],props:{numArgs:0},handler(e){const t=Hr(e.parser,{arraystretch:.5},"script");return t.colSeparationType="small",t},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["subarray"],props:{numArgs:1},handler(e,t){const r=(Rt(t[0])?[t[0]]:qt(t[0],"ordgroup").body).map((function(e){const t=It(e).text;if(-1!=="lc".indexOf(t))return{type:"align",align:t};throw new n("Unknown column alignment: "+t,e)}));if(r.length>1)throw new n("{subarray} can contain only one column");let o={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if(o=Hr(e.parser,o,"script"),o.body.length>0&&o.body[0].length>1)throw new n("{subarray} can contain only one column");return o},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["cases","dcases","rcases","drcases"],props:{numArgs:0},handler(e){const t=Hr(e.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},Or(e.envName));return{type:"leftright",mode:e.mode,body:[t],left:e.envName.indexOf("r")>-1?".":"\\{",right:e.envName.indexOf("r")>-1?"\\}":".",rightColor:void 0}},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["align","align*","aligned","split"],props:{numArgs:0},handler:Vr,htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["gathered","gather","gather*"],props:{numArgs:0},handler(e){l.contains(["gather","gather*"],e.envName)&&Ir(e);const t={cols:[{type:"align",align:"c"}],addJot:!0,colSeparationType:"gather",autoTag:Rr(e.envName),emptySingleRow:!0,leqno:e.parser.settings.leqno};return Hr(e.parser,t,"display")},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["alignat","alignat*","alignedat"],props:{numArgs:1},handler:Vr,htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["equation","equation*"],props:{numArgs:0},handler(e){Ir(e);const t={autoTag:Rr(e.envName),emptySingleRow:!0,singleRow:!0,maxNumCols:1,leqno:e.parser.settings.leqno};return Hr(e.parser,t,"display")},htmlBuilder:Er,mathmlBuilder:Dr}),Ar({type:"array",names:["CD"],props:{numArgs:0},handler(e){return Ir(e),function(e){const t=[];for(e.gullet.beginGroup(),e.gullet.macros.set("\\cr","\\\\\\relax"),e.gullet.beginGroup();;){t.push(e.parseExpression(!1,"\\\\")),e.gullet.endGroup(),e.gullet.beginGroup();const r=e.fetch().text;if("&"!==r&&"\\\\"!==r){if("\\end"===r){0===t[t.length-1].length&&t.pop();break}throw new n("Expected \\\\ or \\cr or \\end",e.nextToken)}e.consume()}let r=[];const o=[r];for(let a=0;a-1);else{if(!("<>AV".indexOf(o)>-1))throw new n('Expected one of "<>AV=|." after @',l[t]);for(let e=0;e<2;e++){let r=!0;for(let h=t+1;h{const r=e.font,n=t.withFont(r);return ht(e.body,n)},Gr=(e,t)=>{const r=e.font,n=t.withFont(r);return vt(e.body,n)},Ur={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};je({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=Ze(t[0]);let s=n;return s in Ur&&(s=Ur[s]),{type:"font",mode:r.mode,font:s.slice(1),body:o}},htmlBuilder:Fr,mathmlBuilder:Gr}),je({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1},handler:(e,t)=>{let{parser:r}=e;const n=t[0],o=l.isCharacterBox(n);return{type:"mclass",mode:r.mode,mclass:Ft(n),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:n}],isCharacterBox:o}}}),je({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it","\\cal"],props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n,breakOnTokenText:o}=e;const{mode:s}=r,i=r.parseExpression(!0,o);return{type:"font",mode:s,font:"math"+n.slice(1),body:{type:"ordgroup",mode:r.mode,body:i}}},htmlBuilder:Fr,mathmlBuilder:Gr});const Yr=(e,t)=>{let r=t;return"display"===e?r=r.id>=w.SCRIPT.id?r.text():w.DISPLAY:"text"===e&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===e?r=w.SCRIPT:"scriptscript"===e&&(r=w.SCRIPTSCRIPT),r},Xr=(e,t)=>{const r=Yr(e.size,t.style),n=r.fracNum(),o=r.fracDen();let s;s=t.havingStyle(n);const i=ht(e.numer,s,t);if(e.continued){const e=8.5/t.fontMetrics().ptPerEm,r=3.5/t.fontMetrics().ptPerEm;i.height=i.height0?3*c:7*c,u=t.fontMetrics().denom1):(h>0?(m=t.fontMetrics().num2,p=c):(m=t.fontMetrics().num3,p=3*c),u=t.fontMetrics().denom2),l){const e=t.fontMetrics().axisHeight;m-i.depth-(e+.5*h){let r=new gt.MathNode("mfrac",[vt(e.numer,t),vt(e.denom,t)]);if(e.hasBarLine){if(e.barSize){const n=P(e.barSize,t);r.setAttribute("linethickness",F(n))}}else r.setAttribute("linethickness","0px");const n=Yr(e.size,t.style);if(n.size!==t.style.size){r=new gt.MathNode("mstyle",[r]);const e=n.size===w.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",e),r.setAttribute("scriptlevel","0")}if(null!=e.leftDelim||null!=e.rightDelim){const t=[];if(null!=e.leftDelim){const r=new gt.MathNode("mo",[new gt.TextNode(e.leftDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}if(t.push(r),null!=e.rightDelim){const r=new gt.MathNode("mo",[new gt.TextNode(e.rightDelim.replace("\\",""))]);r.setAttribute("fence","true"),t.push(r)}return bt(t)}return r};je({type:"genfrac",names:["\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,allowedInArgument:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];let i,a=null,l=null,h="auto";switch(n){case"\\dfrac":case"\\frac":case"\\tfrac":i=!0;break;case"\\\\atopfrac":i=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":i=!1,a="(",l=")";break;case"\\\\bracefrac":i=!1,a="\\{",l="\\}";break;case"\\\\brackfrac":i=!1,a="[",l="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\dfrac":case"\\dbinom":h="display";break;case"\\tfrac":case"\\tbinom":h="text"}return{type:"genfrac",mode:r.mode,continued:!1,numer:o,denom:s,hasBarLine:i,leftDelim:a,rightDelim:l,size:h,barSize:null}},htmlBuilder:Xr,mathmlBuilder:Wr}),je({type:"genfrac",names:["\\cfrac"],props:{numArgs:2},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=t[1];return{type:"genfrac",mode:r.mode,continued:!0,numer:o,denom:s,hasBarLine:!0,leftDelim:null,rightDelim:null,size:"display",barSize:null}}}),je({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler(e){let t,{parser:r,funcName:n,token:o}=e;switch(n){case"\\over":t="\\frac";break;case"\\choose":t="\\binom";break;case"\\atop":t="\\\\atopfrac";break;case"\\brace":t="\\\\bracefrac";break;case"\\brack":t="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:t,token:o}}});const _r=["display","text","script","scriptscript"],jr=function(e){let t=null;return e.length>0&&(t=e,t="."===t?null:t),t};je({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,allowedInArgument:!0,argTypes:["math","math","size","text","math","math"]},handler(e,t){let{parser:r}=e;const n=t[4],o=t[5],s=Ze(t[0]),i="atom"===s.type&&"open"===s.family?jr(s.text):null,a=Ze(t[1]),l="atom"===a.type&&"close"===a.family?jr(a.text):null,h=qt(t[2],"size");let c,m=null;h.isBlank?c=!0:(m=h.value,c=m.number>0);let p="auto",u=t[3];if("ordgroup"===u.type){if(u.body.length>0){const e=qt(u.body[0],"textord");p=_r[Number(e.text)]}}else u=qt(u,"textord"),p=_r[Number(u.text)];return{type:"genfrac",mode:r.mode,numer:n,denom:o,continued:!1,hasBarLine:c,barSize:m,leftDelim:i,rightDelim:l,size:p}},htmlBuilder:Xr,mathmlBuilder:Wr}),je({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler(e,t){let{parser:r,funcName:n,token:o}=e;return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:qt(t[0],"size").value,token:o}}}),je({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0],s=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e}(qt(t[1],"infix").size),i=t[2],a=s.number>0;return{type:"genfrac",mode:r.mode,numer:o,denom:i,continued:!1,hasBarLine:a,barSize:s,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Xr,mathmlBuilder:Wr});const $r=(e,t)=>{const r=t.style;let n,o;"supsub"===e.type?(n=e.sup?ht(e.sup,t.havingStyle(r.sup()),t):ht(e.sub,t.havingStyle(r.sub()),t),o=qt(e.base,"horizBrace")):o=qt(e,"horizBrace");const s=ht(o.base,t.havingBaseStyle(w.DISPLAY)),i=Nt(o,t);let a;if(o.isOver?(a=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:i}]},t),a.children[0].children[0].children[1].classes.push("svg-align")):(a=Ve.makeVList({positionType:"bottom",positionData:s.depth+.1+i.height,children:[{type:"elem",elem:i},{type:"kern",size:.1},{type:"elem",elem:s}]},t),a.children[0].children[0].children[0].classes.push("svg-align")),n){const e=Ve.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t);a=o.isOver?Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:e},{type:"kern",size:.2},{type:"elem",elem:n}]},t):Ve.makeVList({positionType:"bottom",positionData:e.depth+.2+n.height+n.depth,children:[{type:"elem",elem:n},{type:"kern",size:.2},{type:"elem",elem:e}]},t)}return Ve.makeSpan(["mord",o.isOver?"mover":"munder"],[a],t)};je({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler(e,t){let{parser:r,funcName:n}=e;return{type:"horizBrace",mode:r.mode,label:n,isOver:/^\\over/.test(n),base:t[0]}},htmlBuilder:$r,mathmlBuilder:(e,t)=>{const r=Ct(e.label);return new gt.MathNode(e.isOver?"mover":"munder",[vt(e.base,t),r])}}),je({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[1],o=qt(t[0],"url").url;return r.settings.isTrusted({command:"\\href",url:o})?{type:"href",mode:r.mode,href:o,body:Ke(n)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:(e,t)=>{const r=nt(e.body,t,!1);return Ve.makeAnchor(e.href,[],r,t)},mathmlBuilder:(e,t)=>{let r=wt(e.body,t);return r instanceof ut||(r=new ut("mrow",[r])),r.setAttribute("href",e.href),r}}),je({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=qt(t[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:n}))return r.formatUnsupportedCmd("\\url");const o=[];for(let e=0;e{let{parser:r,funcName:o,token:s}=e;const i=qt(t[0],"raw").string,a=t[1];let l;r.settings.strict&&r.settings.reportNonstrict("htmlExtension","HTML extension is disabled on strict mode");const h={};switch(o){case"\\htmlClass":h.class=i,l={command:"\\htmlClass",class:i};break;case"\\htmlId":h.id=i,l={command:"\\htmlId",id:i};break;case"\\htmlStyle":h.style=i,l={command:"\\htmlStyle",style:i};break;case"\\htmlData":{const e=i.split(",");for(let t=0;t{const r=nt(e.body,t,!1),n=["enclosing"];e.attributes.class&&n.push(...e.attributes.class.trim().split(/\s+/));const o=Ve.makeSpan(n,r,t);for(const t in e.attributes)"class"!==t&&e.attributes.hasOwnProperty(t)&&o.setAttribute(t,e.attributes[t]);return o},mathmlBuilder:(e,t)=>wt(e.body,t)}),je({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"htmlmathml",mode:r.mode,html:Ke(t[0]),mathml:Ke(t[1])}},htmlBuilder:(e,t)=>{const r=nt(e.html,t,!1);return Ve.makeFragment(r)},mathmlBuilder:(e,t)=>wt(e.mathml,t)});const Zr=function(e){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(e))return{number:+e,unit:"bp"};{const t=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e);if(!t)throw new n("Invalid size: '"+e+"' in \\includegraphics");const r={number:+(t[1]+t[2]),unit:t[3]};if(!V(r))throw new n("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r}};je({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:(e,t,r)=>{let{parser:o}=e,s={number:0,unit:"em"},i={number:.9,unit:"em"},a={number:0,unit:"em"},l="";if(r[0]){const e=qt(r[0],"raw").string.split(",");for(let t=0;t{const r=P(e.height,t);let n=0;e.totalheight.number>0&&(n=P(e.totalheight,t)-r);let o=0;e.width.number>0&&(o=P(e.width,t));const s={height:F(r+n)};o>0&&(s.width=F(o)),n>0&&(s.verticalAlign=F(-n));const i=new j(e.src,e.alt,s);return i.height=r,i.depth=n,i},mathmlBuilder:(e,t)=>{const r=new gt.MathNode("mglyph",[]);r.setAttribute("alt",e.alt);const n=P(e.height,t);let o=0;if(e.totalheight.number>0&&(o=P(e.totalheight,t)-n,r.setAttribute("valign",F(-o))),r.setAttribute("height",F(n+o)),e.width.number>0){const n=P(e.width,t);r.setAttribute("width",F(n))}return r.setAttribute("src",e.src),r}}),je({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],primitive:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=qt(t[0],"size");if(r.settings.strict){const e="m"===n[1],t="mu"===o.value.unit;e?(t||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" supports only mu units, not "+o.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" works only in math mode")):t&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+n+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:o.value}},htmlBuilder(e,t){return Ve.makeGlue(e.dimension,t)},mathmlBuilder(e,t){const r=P(e.dimension,t);return new gt.SpaceNode(r)}}),je({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r,funcName:n}=e;const o=t[0];return{type:"lap",mode:r.mode,alignment:n.slice(5),body:o}},htmlBuilder:(e,t)=>{let r;"clap"===e.alignment?(r=Ve.makeSpan([],[ht(e.body,t)]),r=Ve.makeSpan(["inner"],[r],t)):r=Ve.makeSpan(["inner"],[ht(e.body,t)]);const n=Ve.makeSpan(["fix"],[]);let o=Ve.makeSpan([e.alignment],[r,n],t);const s=Ve.makeSpan(["strut"]);return s.style.height=F(o.height+o.depth),o.depth&&(s.style.verticalAlign=F(-o.depth)),o.children.unshift(s),o=Ve.makeSpan(["thinbox"],[o],t),Ve.makeSpan(["mord","vbox"],[o],t)},mathmlBuilder:(e,t)=>{const r=new gt.MathNode("mpadded",[vt(e.body,t)]);if("rlap"!==e.alignment){const t="llap"===e.alignment?"-1":"-0.5";r.setAttribute("lspace",t+"width")}return r.setAttribute("width","0px"),r}}),je({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){let{funcName:r,parser:n}=e;const o=n.mode;n.switchMode("math");const s="\\("===r?"\\)":"$",i=n.parseExpression(!1,s);return n.expect(s),n.switchMode(o),{type:"styling",mode:n.mode,style:"text",body:i}}}),je({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler(e,t){throw new n("Mismatched "+e.funcName)}});const Kr=(e,t)=>{switch(t.style.size){case w.DISPLAY.size:return e.display;case w.TEXT.size:return e.text;case w.SCRIPT.size:return e.script;case w.SCRIPTSCRIPT.size:return e.scriptscript;default:return e.text}};je({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4,primitive:!0},handler:(e,t)=>{let{parser:r}=e;return{type:"mathchoice",mode:r.mode,display:Ke(t[0]),text:Ke(t[1]),script:Ke(t[2]),scriptscript:Ke(t[3])}},htmlBuilder:(e,t)=>{const r=Kr(e,t),n=nt(r,t,!1);return Ve.makeFragment(n)},mathmlBuilder:(e,t)=>{const r=Kr(e,t);return wt(r,t)}});const Jr=(e,t,r,n,o,s,i)=>{e=Ve.makeSpan([],[e]);const a=r&&l.isCharacterBox(r);let h,c,m;if(t){const e=ht(t,n.havingStyle(o.sup()),n);c={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing1,n.fontMetrics().bigOpSpacing3-e.depth)}}if(r){const e=ht(r,n.havingStyle(o.sub()),n);h={elem:e,kern:Math.max(n.fontMetrics().bigOpSpacing2,n.fontMetrics().bigOpSpacing4-e.height)}}if(c&&h){const t=n.fontMetrics().bigOpSpacing5+h.elem.height+h.elem.depth+h.kern+e.depth+i;m=Ve.makeVList({positionType:"bottom",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:F(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:F(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}else if(h){const t=e.height-i;m=Ve.makeVList({positionType:"top",positionData:t,children:[{type:"kern",size:n.fontMetrics().bigOpSpacing5},{type:"elem",elem:h.elem,marginLeft:F(-s)},{type:"kern",size:h.kern},{type:"elem",elem:e}]},n)}else{if(!c)return e;{const t=e.depth+i;m=Ve.makeVList({positionType:"bottom",positionData:t,children:[{type:"elem",elem:e},{type:"kern",size:c.kern},{type:"elem",elem:c.elem,marginLeft:F(s)},{type:"kern",size:n.fontMetrics().bigOpSpacing5}]},n)}}const p=[m];if(h&&0!==s&&!a){const e=Ve.makeSpan(["mspace"],[],n);e.style.marginRight=F(s),p.unshift(e)}return Ve.makeSpan(["mop","op-limits"],p,n)},Qr=["\\smallint"],en=(e,t)=>{let r,n,o,s=!1;"supsub"===e.type?(r=e.sup,n=e.sub,o=qt(e.base,"op"),s=!0):o=qt(e,"op");const i=t.style;let a,h=!1;if(i.size===w.DISPLAY.size&&o.symbol&&!l.contains(Qr,o.name)&&(h=!0),o.symbol){const e=h?"Size2-Regular":"Size1-Regular";let r="";if("\\oiint"!==o.name&&"\\oiiint"!==o.name||(r=o.name.slice(1),o.name="oiint"===r?"\\iint":"\\iiint"),a=Ve.makeSymbol(o.name,e,"math",t,["mop","op-symbol",h?"large-op":"small-op"]),r.length>0){const e=a.italic,n=Ve.staticSvg(r+"Size"+(h?"2":"1"),t);a=Ve.makeVList({positionType:"individualShift",children:[{type:"elem",elem:a,shift:0},{type:"elem",elem:n,shift:h?.08:0}]},t),o.name="\\"+r,a.classes.unshift("mop"),a.italic=e}}else if(o.body){const e=nt(o.body,t,!0);1===e.length&&e[0]instanceof Z?(a=e[0],a.classes[0]="mop"):a=Ve.makeSpan(["mop"],e,t)}else{const e=[];for(let r=1;r{let r;if(e.symbol)r=new ut("mo",[ft(e.name,e.mode)]),l.contains(Qr,e.name)&&r.setAttribute("largeop","false");else if(e.body)r=new ut("mo",xt(e.body,t));else{r=new ut("mi",[new dt(e.name.slice(1))]);const t=new ut("mo",[ft("\u2061","text")]);r=e.parentIsSupSub?new ut("mrow",[r,t]):pt([r,t])}return r},rn={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};je({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:(e,t)=>{let{parser:r,funcName:n}=e,o=n;return 1===o.length&&(o=rn[o]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:o}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\mathop"],props:{numArgs:1,primitive:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:Ke(n)}},htmlBuilder:en,mathmlBuilder:tn});const nn={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};je({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e;return{type:"op",mode:t.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:en,mathmlBuilder:tn}),je({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0},handler(e){let{parser:t,funcName:r}=e,n=r;return 1===n.length&&(n=nn[n]),{type:"op",mode:t.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:n}},htmlBuilder:en,mathmlBuilder:tn});const on=(e,t)=>{let r,n,o,s,i=!1;if("supsub"===e.type?(r=e.sup,n=e.sub,o=qt(e.base,"operatorname"),i=!0):o=qt(e,"operatorname"),o.body.length>0){const e=o.body.map((e=>{const t=e.text;return"string"==typeof t?{type:"textord",mode:e.mode,text:t}:e})),r=nt(e,t.withFont("mathrm"),!0);for(let e=0;e{let{parser:r,funcName:n}=e;const o=t[0];return{type:"operatorname",mode:r.mode,body:Ke(o),alwaysHandleSupSub:"\\operatornamewithlimits"===n,limits:!1,parentIsSupSub:!1}},htmlBuilder:on,mathmlBuilder:(e,t)=>{let r=xt(e.body,t.withFont("mathrm")),n=!0;for(let e=0;ee.toText())).join("");r=[new gt.TextNode(e)]}const o=new gt.MathNode("mi",r);o.setAttribute("mathvariant","normal");const s=new gt.MathNode("mo",[ft("\u2061","text")]);return e.parentIsSupSub?new gt.MathNode("mrow",[o,s]):gt.newDocumentFragment([o,s])}}),Br("\\operatorname","\\@ifstar\\operatornamewithlimits\\operatorname@"),$e({type:"ordgroup",htmlBuilder(e,t){return e.semisimple?Ve.makeFragment(nt(e.body,t,!1)):Ve.makeSpan(["mord"],nt(e.body,t,!0),t)},mathmlBuilder(e,t){return wt(e.body,t,!0)}}),je({type:"overline",names:["\\overline"],props:{numArgs:1},handler(e,t){let{parser:r}=e;const n=t[0];return{type:"overline",mode:r.mode,body:n}},htmlBuilder(e,t){const r=ht(e.body,t.havingCrampedStyle()),n=Ve.makeLineSpan("overline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*o},{type:"elem",elem:n},{type:"kern",size:o}]},t);return Ve.makeSpan(["mord","overline"],[s],t)},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[new gt.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new gt.MathNode("mover",[vt(e.body,t),r]);return n.setAttribute("accent","true"),n}}),je({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"phantom",mode:r.mode,body:Ke(n)}},htmlBuilder:(e,t)=>{const r=nt(e.body,t.withPhantom(),!1);return Ve.makeFragment(r)},mathmlBuilder:(e,t)=>{const r=xt(e.body,t);return new gt.MathNode("mphantom",r)}}),je({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"hphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{let r=Ve.makeSpan([],[ht(e.body,t.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(let e=0;e{const r=xt(Ke(e.body),t),n=new gt.MathNode("mphantom",r),o=new gt.MathNode("mpadded",[n]);return o.setAttribute("height","0px"),o.setAttribute("depth","0px"),o}}),je({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:(e,t)=>{let{parser:r}=e;const n=t[0];return{type:"vphantom",mode:r.mode,body:n}},htmlBuilder:(e,t)=>{const r=Ve.makeSpan(["inner"],[ht(e.body,t.withPhantom())]),n=Ve.makeSpan(["fix"],[]);return Ve.makeSpan(["mord","rlap"],[r,n],t)},mathmlBuilder:(e,t)=>{const r=xt(Ke(e.body),t),n=new gt.MathNode("mphantom",r),o=new gt.MathNode("mpadded",[n]);return o.setAttribute("width","0px"),o}}),je({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler(e,t){let{parser:r}=e;const n=qt(t[0],"size").value,o=t[1];return{type:"raisebox",mode:r.mode,dy:n,body:o}},htmlBuilder(e,t){const r=ht(e.body,t),n=P(e.dy,t);return Ve.makeVList({positionType:"shift",positionData:-n,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){const r=new gt.MathNode("mpadded",[vt(e.body,t)]),n=e.dy.number+e.dy.unit;return r.setAttribute("voffset",n),r}}),je({type:"internal",names:["\\relax"],props:{numArgs:0,allowedInText:!0},handler(e){let{parser:t}=e;return{type:"internal",mode:t.mode}}}),je({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler(e,t,r){let{parser:n}=e;const o=r[0],s=qt(t[0],"size"),i=qt(t[1],"size");return{type:"rule",mode:n.mode,shift:o&&qt(o,"size").value,width:s.value,height:i.value}},htmlBuilder(e,t){const r=Ve.makeSpan(["mord","rule"],[],t),n=P(e.width,t),o=P(e.height,t),s=e.shift?P(e.shift,t):0;return r.style.borderRightWidth=F(n),r.style.borderTopWidth=F(o),r.style.bottom=F(s),r.width=n,r.height=o+s,r.depth=-s,r.maxFontSize=1.125*o*t.sizeMultiplier,r},mathmlBuilder(e,t){const r=P(e.width,t),n=P(e.height,t),o=e.shift?P(e.shift,t):0,s=t.color&&t.getColor()||"black",i=new gt.MathNode("mspace");i.setAttribute("mathbackground",s),i.setAttribute("width",F(r)),i.setAttribute("height",F(n));const a=new gt.MathNode("mpadded",[i]);return o>=0?a.setAttribute("height",F(o)):(a.setAttribute("height",F(o)),a.setAttribute("depth",F(-o))),a.setAttribute("voffset",F(o)),a}});const an=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];je({type:"sizing",names:an,props:{numArgs:0,allowedInText:!0},handler:(e,t)=>{let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!1,r);return{type:"sizing",mode:o.mode,size:an.indexOf(n)+1,body:s}},htmlBuilder:(e,t)=>{const r=t.havingSize(e.size);return sn(e.body,r,t)},mathmlBuilder:(e,t)=>{const r=t.havingSize(e.size),n=xt(e.body,r),o=new gt.MathNode("mstyle",n);return o.setAttribute("mathsize",F(r.sizeMultiplier)),o}}),je({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:(e,t,r)=>{let{parser:n}=e,o=!1,s=!1;const i=r[0]&&qt(r[0],"ordgroup");if(i){let e="";for(let t=0;t{const r=Ve.makeSpan([],[ht(e.body,t)]);if(!e.smashHeight&&!e.smashDepth)return r;if(e.smashHeight&&(r.height=0,r.children))for(let e=0;e{const r=new gt.MathNode("mpadded",[vt(e.body,t)]);return e.smashHeight&&r.setAttribute("height","0px"),e.smashDepth&&r.setAttribute("depth","0px"),r}}),je({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler(e,t,r){let{parser:n}=e;const o=r[0],s=t[0];return{type:"sqrt",mode:n.mode,body:s,index:o}},htmlBuilder(e,t){let r=ht(e.body,t.havingCrampedStyle());0===r.height&&(r.height=t.fontMetrics().xHeight),r=Ve.wrapFragment(r,t);const n=t.fontMetrics().defaultRuleThickness;let o=n;t.style.idr.height+r.depth+s&&(s=(s+c-r.height-r.depth)/2);const m=a.height-r.height-s-l;r.style.paddingLeft=F(h);const p=Ve.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+m)},{type:"elem",elem:a},{type:"kern",size:l}]},t);if(e.index){const r=t.havingStyle(w.SCRIPTSCRIPT),n=ht(e.index,r,t),o=.6*(p.height-p.depth),s=Ve.makeVList({positionType:"shift",positionData:-o,children:[{type:"elem",elem:n}]},t),i=Ve.makeSpan(["root"],[s]);return Ve.makeSpan(["mord","sqrt"],[i,p],t)}return Ve.makeSpan(["mord","sqrt"],[p],t)},mathmlBuilder(e,t){const{body:r,index:n}=e;return n?new gt.MathNode("mroot",[vt(r,t),vt(n,t)]):new gt.MathNode("msqrt",[vt(r,t)])}});const ln={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};je({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0,primitive:!0},handler(e,t){let{breakOnTokenText:r,funcName:n,parser:o}=e;const s=o.parseExpression(!0,r),i=n.slice(1,n.length-5);return{type:"styling",mode:o.mode,style:i,body:s}},htmlBuilder(e,t){const r=ln[e.style],n=t.havingStyle(r).withFont("");return sn(e.body,n,t)},mathmlBuilder(e,t){const r=ln[e.style],n=t.havingStyle(r),o=xt(e.body,n),s=new gt.MathNode("mstyle",o),i={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[e.style];return s.setAttribute("scriptlevel",i[0]),s.setAttribute("displaystyle",i[1]),s}});$e({type:"supsub",htmlBuilder(e,t){const r=function(e,t){const r=e.base;if(r)return"op"===r.type?r.limits&&(t.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?en:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(t.style.size===w.DISPLAY.size||r.limits)?on:null:"accent"===r.type?l.isCharacterBox(r.base)?Ht:null:"horizBrace"===r.type&&!e.sub===r.isOver?$r:null;return null}(e,t);if(r)return r(e,t);const{base:n,sup:o,sub:s}=e,i=ht(n,t);let a,h;const c=t.fontMetrics();let m=0,p=0;const u=n&&l.isCharacterBox(n);if(o){const e=t.havingStyle(t.style.sup());a=ht(o,e,t),u||(m=i.height-e.fontMetrics().supDrop*e.sizeMultiplier/t.sizeMultiplier)}if(s){const e=t.havingStyle(t.style.sub());h=ht(s,e,t),u||(p=i.depth+e.fontMetrics().subDrop*e.sizeMultiplier/t.sizeMultiplier)}let d;d=t.style===w.DISPLAY?c.sup1:t.style.cramped?c.sup3:c.sup2;const g=t.sizeMultiplier,f=F(.5/c.ptPerEm/g);let b,y=null;if(h){const t=e.base&&"op"===e.base.type&&e.base.name&&("\\oiint"===e.base.name||"\\oiiint"===e.base.name);(i instanceof Z||t)&&(y=F(-i.italic))}if(a&&h){m=Math.max(m,d,a.depth+.25*c.xHeight),p=Math.max(p,c.sub2);const e=4*c.defaultRuleThickness;if(m-a.depth-(h.height-p)0&&(m+=t,p-=t)}const r=[{type:"elem",elem:h,shift:p,marginRight:f,marginLeft:y},{type:"elem",elem:a,shift:-m,marginRight:f}];b=Ve.makeVList({positionType:"individualShift",children:r},t)}else if(h){p=Math.max(p,c.sub1,h.height-.8*c.xHeight);const e=[{type:"elem",elem:h,marginLeft:y,marginRight:f}];b=Ve.makeVList({positionType:"shift",positionData:p,children:e},t)}else{if(!a)throw new Error("supsub must have either sup or sub.");m=Math.max(m,d,a.depth+.25*c.xHeight),b=Ve.makeVList({positionType:"shift",positionData:-m,children:[{type:"elem",elem:a,marginRight:f}]},t)}const x=at(i,"right")||"mord";return Ve.makeSpan([x],[i,Ve.makeSpan(["msupsub"],[b])],t)},mathmlBuilder(e,t){let r,n,o=!1;e.base&&"horizBrace"===e.base.type&&(n=!!e.sup,n===e.base.isOver&&(o=!0,r=e.base.isOver)),!e.base||"op"!==e.base.type&&"operatorname"!==e.base.type||(e.base.parentIsSupSub=!0);const s=[vt(e.base,t)];let i;if(e.sub&&s.push(vt(e.sub,t)),e.sup&&s.push(vt(e.sup,t)),o)i=r?"mover":"munder";else if(e.sub)if(e.sup){const r=e.base;i=r&&"op"===r.type&&r.limits&&t.style===w.DISPLAY||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(t.style===w.DISPLAY||r.limits)?"munderover":"msubsup"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"munder":"msub"}else{const r=e.base;i=r&&"op"===r.type&&r.limits&&(t.style===w.DISPLAY||r.alwaysHandleSupSub)||r&&"operatorname"===r.type&&r.alwaysHandleSupSub&&(r.limits||t.style===w.DISPLAY)?"mover":"msup"}return new gt.MathNode(i,s)}}),$e({type:"atom",htmlBuilder(e,t){return Ve.mathsym(e.text,e.mode,t,["m"+e.family])},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[ft(e.text,e.mode)]);if("bin"===e.family){const n=yt(e,t);"bold-italic"===n&&r.setAttribute("mathvariant",n)}else"punct"===e.family?r.setAttribute("separator","true"):"open"!==e.family&&"close"!==e.family||r.setAttribute("stretchy","false");return r}});const hn={mi:"italic",mn:"normal",mtext:"normal"};$e({type:"mathord",htmlBuilder(e,t){return Ve.makeOrd(e,t,"mathord")},mathmlBuilder(e,t){const r=new gt.MathNode("mi",[ft(e.text,e.mode,t)]),n=yt(e,t)||"italic";return n!==hn[r.type]&&r.setAttribute("mathvariant",n),r}}),$e({type:"textord",htmlBuilder(e,t){return Ve.makeOrd(e,t,"textord")},mathmlBuilder(e,t){const r=ft(e.text,e.mode,t),n=yt(e,t)||"normal";let o;return o="text"===e.mode?new gt.MathNode("mtext",[r]):/[0-9]/.test(e.text)?new gt.MathNode("mn",[r]):"\\prime"===e.text?new gt.MathNode("mo",[r]):new gt.MathNode("mi",[r]),n!==hn[o.type]&&o.setAttribute("mathvariant",n),o}});const cn={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},mn={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};$e({type:"spacing",htmlBuilder(e,t){if(mn.hasOwnProperty(e.text)){const r=mn[e.text].className||"";if("text"===e.mode){const n=Ve.makeOrd(e,t,"textord");return n.classes.push(r),n}return Ve.makeSpan(["mspace",r],[Ve.mathsym(e.text,e.mode,t)],t)}if(cn.hasOwnProperty(e.text))return Ve.makeSpan(["mspace",cn[e.text]],[],t);throw new n('Unknown type of space "'+e.text+'"')},mathmlBuilder(e,t){let r;if(!mn.hasOwnProperty(e.text)){if(cn.hasOwnProperty(e.text))return new gt.MathNode("mspace");throw new n('Unknown type of space "'+e.text+'"')}return r=new gt.MathNode("mtext",[new gt.TextNode("\xa0")]),r}});const pn=()=>{const e=new gt.MathNode("mtd",[]);return e.setAttribute("width","50%"),e};$e({type:"tag",mathmlBuilder(e,t){const r=new gt.MathNode("mtable",[new gt.MathNode("mtr",[pn(),new gt.MathNode("mtd",[wt(e.body,t)]),pn(),new gt.MathNode("mtd",[wt(e.tag,t)])])]);return r.setAttribute("width","100%"),r}});const un={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},dn={"\\textbf":"textbf","\\textmd":"textmd"},gn={"\\textit":"textit","\\textup":"textup"},fn=(e,t)=>{const r=e.font;return r?un[r]?t.withTextFontFamily(un[r]):dn[r]?t.withTextFontWeight(dn[r]):t.withTextFontShape(gn[r]):t};je({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],allowedInArgument:!0,allowedInText:!0},handler(e,t){let{parser:r,funcName:n}=e;const o=t[0];return{type:"text",mode:r.mode,body:Ke(o),font:n}},htmlBuilder(e,t){const r=fn(e,t),n=nt(e.body,r,!0);return Ve.makeSpan(["mord","text"],n,r)},mathmlBuilder(e,t){const r=fn(e,t);return wt(e.body,r)}}),je({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler(e,t){let{parser:r}=e;return{type:"underline",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ht(e.body,t),n=Ve.makeLineSpan("underline-line",t),o=t.fontMetrics().defaultRuleThickness,s=Ve.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:o},{type:"elem",elem:n},{type:"kern",size:3*o},{type:"elem",elem:r}]},t);return Ve.makeSpan(["mord","underline"],[s],t)},mathmlBuilder(e,t){const r=new gt.MathNode("mo",[new gt.TextNode("\u203e")]);r.setAttribute("stretchy","true");const n=new gt.MathNode("munder",[vt(e.body,t),r]);return n.setAttribute("accentunder","true"),n}}),je({type:"vcenter",names:["\\vcenter"],props:{numArgs:1,argTypes:["original"],allowedInText:!1},handler(e,t){let{parser:r}=e;return{type:"vcenter",mode:r.mode,body:t[0]}},htmlBuilder(e,t){const r=ht(e.body,t),n=t.fontMetrics().axisHeight,o=.5*(r.height-n-(r.depth+n));return Ve.makeVList({positionType:"shift",positionData:o,children:[{type:"elem",elem:r}]},t)},mathmlBuilder(e,t){return new gt.MathNode("mpadded",[vt(e.body,t)],["vcenter"])}}),je({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler(e,t,r){throw new n("\\verb ended by end of line instead of matching delimiter")},htmlBuilder(e,t){const r=bn(e),n=[],o=t.havingStyle(t.style.text());for(let t=0;te.body.replace(/ /g,e.star?"\u2423":"\xa0");var yn=Xe;const xn="[ \r\n\t]",wn="(\\\\[a-zA-Z@]+)"+xn+"*",vn="[\u0300-\u036f]",kn=new RegExp(vn+"+$"),Sn="("+xn+"+)|\\\\(\n|[ \r\t]+\n?)[ \r\t]*|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff]"+vn+"*|[\ud800-\udbff][\udc00-\udfff]"+vn+"*|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5|"+wn+"|\\\\[^\ud800-\udfff])";class Mn{constructor(e,t){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=t,this.tokenRegex=new RegExp(Sn,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,t){this.catcodes[e]=t}lex(){const e=this.input,t=this.tokenRegex.lastIndex;if(t===e.length)return new Nr("EOF",new Cr(this,t,t));const r=this.tokenRegex.exec(e);if(null===r||r.index!==t)throw new n("Unexpected character: '"+e[t]+"'",new Nr(e[t],new Cr(this,t,t+1)));const o=r[6]||r[3]||(r[2]?"\\ ":" ");if(14===this.catcodes[o]){const t=e.indexOf("\n",this.tokenRegex.lastIndex);return-1===t?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=t+1,this.lex()}return new Nr(o,new Cr(this,t,this.tokenRegex.lastIndex))}}class zn{constructor(e,t){void 0===e&&(e={}),void 0===t&&(t={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=t,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(0===this.undefStack.length)throw new n("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");const e=this.undefStack.pop();for(const t in e)e.hasOwnProperty(t)&&(null==e[t]?delete this.current[t]:this.current[t]=e[t])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,t,r){if(void 0===r&&(r=!1),r){for(let t=0;t0&&(this.undefStack[this.undefStack.length-1][e]=t)}else{const t=this.undefStack[this.undefStack.length-1];t&&!t.hasOwnProperty(e)&&(t[e]=this.current[e])}null==t?delete this.current[e]:this.current[e]=t}}var An=Tr;Br("\\noexpand",(function(e){const t=e.popToken();return e.isExpandable(t.text)&&(t.noexpand=!0,t.treatAsRelax=!0),{tokens:[t],numArgs:0}})),Br("\\expandafter",(function(e){const t=e.popToken();return e.expandOnce(!0),{tokens:[t],numArgs:0}})),Br("\\@firstoftwo",(function(e){return{tokens:e.consumeArgs(2)[0],numArgs:0}})),Br("\\@secondoftwo",(function(e){return{tokens:e.consumeArgs(2)[1],numArgs:0}})),Br("\\@ifnextchar",(function(e){const t=e.consumeArgs(3);e.consumeSpaces();const r=e.future();return 1===t[0].length&&t[0][0].text===r.text?{tokens:t[1],numArgs:0}:{tokens:t[2],numArgs:0}})),Br("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),Br("\\TextOrMath",(function(e){const t=e.consumeArgs(2);return"text"===e.mode?{tokens:t[0],numArgs:0}:{tokens:t[1],numArgs:0}}));const Tn={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};Br("\\char",(function(e){let t,r=e.popToken(),o="";if("'"===r.text)t=8,r=e.popToken();else if('"'===r.text)t=16,r=e.popToken();else if("`"===r.text)if(r=e.popToken(),"\\"===r.text[0])o=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new n("\\char` missing argument");o=r.text.charCodeAt(0)}else t=10;if(t){if(o=Tn[r.text],null==o||o>=t)throw new n("Invalid base-"+t+" digit "+r.text);let s;for(;null!=(s=Tn[e.future().text])&&s{let o=e.consumeArg().tokens;if(1!==o.length)throw new n("\\newcommand's first argument must be a macro name");const s=o[0].text,i=e.isDefined(s);if(i&&!t)throw new n("\\newcommand{"+s+"} attempting to redefine "+s+"; use \\renewcommand");if(!i&&!r)throw new n("\\renewcommand{"+s+"} when command "+s+" does not yet exist; use \\newcommand");let a=0;if(o=e.consumeArg().tokens,1===o.length&&"["===o[0].text){let t="",r=e.expandNextToken();for(;"]"!==r.text&&"EOF"!==r.text;)t+=r.text,r=e.expandNextToken();if(!t.match(/^\s*[0-9]+\s*$/))throw new n("Invalid number of arguments: "+t);a=parseInt(t),o=e.consumeArg().tokens}return e.macros.set(s,{tokens:o,numArgs:a}),""};Br("\\newcommand",(e=>Bn(e,!1,!0))),Br("\\renewcommand",(e=>Bn(e,!0,!1))),Br("\\providecommand",(e=>Bn(e,!0,!0))),Br("\\message",(e=>{const t=e.consumeArgs(1)[0];return console.log(t.reverse().map((e=>e.text)).join("")),""})),Br("\\errmessage",(e=>{const t=e.consumeArgs(1)[0];return console.error(t.reverse().map((e=>e.text)).join("")),""})),Br("\\show",(e=>{const t=e.popToken(),r=t.text;return console.log(t,e.macros.get(r),yn[r],oe.math[r],oe.text[r]),""})),Br("\\bgroup","{"),Br("\\egroup","}"),Br("~","\\nobreakspace"),Br("\\lq","`"),Br("\\rq","'"),Br("\\aa","\\r a"),Br("\\AA","\\r A"),Br("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),Br("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),Br("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),Br("\u212c","\\mathscr{B}"),Br("\u2130","\\mathscr{E}"),Br("\u2131","\\mathscr{F}"),Br("\u210b","\\mathscr{H}"),Br("\u2110","\\mathscr{I}"),Br("\u2112","\\mathscr{L}"),Br("\u2133","\\mathscr{M}"),Br("\u211b","\\mathscr{R}"),Br("\u212d","\\mathfrak{C}"),Br("\u210c","\\mathfrak{H}"),Br("\u2128","\\mathfrak{Z}"),Br("\\Bbbk","\\Bbb{k}"),Br("\xb7","\\cdotp"),Br("\\llap","\\mathllap{\\textrm{#1}}"),Br("\\rlap","\\mathrlap{\\textrm{#1}}"),Br("\\clap","\\mathclap{\\textrm{#1}}"),Br("\\mathstrut","\\vphantom{(}"),Br("\\underbar","\\underline{\\text{#1}}"),Br("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),Br("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),Br("\\ne","\\neq"),Br("\u2260","\\neq"),Br("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),Br("\u2209","\\notin"),Br("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),Br("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),Br("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),Br("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),Br("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),Br("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),Br("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),Br("\u27c2","\\perp"),Br("\u203c","\\mathclose{!\\mkern-0.8mu!}"),Br("\u220c","\\notni"),Br("\u231c","\\ulcorner"),Br("\u231d","\\urcorner"),Br("\u231e","\\llcorner"),Br("\u231f","\\lrcorner"),Br("\xa9","\\copyright"),Br("\xae","\\textregistered"),Br("\ufe0f","\\textregistered"),Br("\\ulcorner",'\\html@mathml{\\@ulcorner}{\\mathop{\\char"231c}}'),Br("\\urcorner",'\\html@mathml{\\@urcorner}{\\mathop{\\char"231d}}'),Br("\\llcorner",'\\html@mathml{\\@llcorner}{\\mathop{\\char"231e}}'),Br("\\lrcorner",'\\html@mathml{\\@lrcorner}{\\mathop{\\char"231f}}'),Br("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}"),Br("\u22ee","\\vdots"),Br("\\varGamma","\\mathit{\\Gamma}"),Br("\\varDelta","\\mathit{\\Delta}"),Br("\\varTheta","\\mathit{\\Theta}"),Br("\\varLambda","\\mathit{\\Lambda}"),Br("\\varXi","\\mathit{\\Xi}"),Br("\\varPi","\\mathit{\\Pi}"),Br("\\varSigma","\\mathit{\\Sigma}"),Br("\\varUpsilon","\\mathit{\\Upsilon}"),Br("\\varPhi","\\mathit{\\Phi}"),Br("\\varPsi","\\mathit{\\Psi}"),Br("\\varOmega","\\mathit{\\Omega}"),Br("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),Br("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),Br("\\boxed","\\fbox{$\\displaystyle{#1}$}"),Br("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),Br("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),Br("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");const Cn={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};Br("\\dots",(function(e){let t="\\dotso";const r=e.expandAfterFuture().text;return r in Cn?t=Cn[r]:("\\not"===r.slice(0,4)||r in oe.math&&l.contains(["bin","rel"],oe.math[r].group))&&(t="\\dotsb"),t}));const Nn={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};Br("\\dotso",(function(e){return e.future().text in Nn?"\\ldots\\,":"\\ldots"})),Br("\\dotsc",(function(e){const t=e.future().text;return t in Nn&&","!==t?"\\ldots\\,":"\\ldots"})),Br("\\cdots",(function(e){return e.future().text in Nn?"\\@cdots\\,":"\\@cdots"})),Br("\\dotsb","\\cdots"),Br("\\dotsm","\\cdots"),Br("\\dotsi","\\!\\cdots"),Br("\\dotsx","\\ldots\\,"),Br("\\DOTSI","\\relax"),Br("\\DOTSB","\\relax"),Br("\\DOTSX","\\relax"),Br("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),Br("\\,","\\tmspace+{3mu}{.1667em}"),Br("\\thinspace","\\,"),Br("\\>","\\mskip{4mu}"),Br("\\:","\\tmspace+{4mu}{.2222em}"),Br("\\medspace","\\:"),Br("\\;","\\tmspace+{5mu}{.2777em}"),Br("\\thickspace","\\;"),Br("\\!","\\tmspace-{3mu}{.1667em}"),Br("\\negthinspace","\\!"),Br("\\negmedspace","\\tmspace-{4mu}{.2222em}"),Br("\\negthickspace","\\tmspace-{5mu}{.277em}"),Br("\\enspace","\\kern.5em "),Br("\\enskip","\\hskip.5em\\relax"),Br("\\quad","\\hskip1em\\relax"),Br("\\qquad","\\hskip2em\\relax"),Br("\\tag","\\@ifstar\\tag@literal\\tag@paren"),Br("\\tag@paren","\\tag@literal{({#1})}"),Br("\\tag@literal",(e=>{if(e.macros.get("\\df@tag"))throw new n("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"})),Br("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),Br("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),Br("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),Br("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),Br("\\newline","\\\\\\relax"),Br("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");const qn=F(T["Main-Regular"]["T".charCodeAt(0)][1]-.7*T["Main-Regular"]["A".charCodeAt(0)][1]);Br("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+qn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),Br("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+qn+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),Br("\\hspace","\\@ifstar\\@hspacer\\@hspace"),Br("\\@hspace","\\hskip #1\\relax"),Br("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),Br("\\ordinarycolon",":"),Br("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),Br("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),Br("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),Br("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),Br("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),Br("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),Br("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),Br("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),Br("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),Br("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),Br("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),Br("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),Br("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),Br("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),Br("\u2237","\\dblcolon"),Br("\u2239","\\eqcolon"),Br("\u2254","\\coloneqq"),Br("\u2255","\\eqqcolon"),Br("\u2a74","\\Coloneqq"),Br("\\ratio","\\vcentcolon"),Br("\\coloncolon","\\dblcolon"),Br("\\colonequals","\\coloneqq"),Br("\\coloncolonequals","\\Coloneqq"),Br("\\equalscolon","\\eqqcolon"),Br("\\equalscoloncolon","\\Eqqcolon"),Br("\\colonminus","\\coloneq"),Br("\\coloncolonminus","\\Coloneq"),Br("\\minuscolon","\\eqcolon"),Br("\\minuscoloncolon","\\Eqcolon"),Br("\\coloncolonapprox","\\Colonapprox"),Br("\\coloncolonsim","\\Colonsim"),Br("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Br("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Br("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),Br("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),Br("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),Br("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),Br("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),Br("\\injlim","\\DOTSB\\operatorname*{inj\\,lim}"),Br("\\projlim","\\DOTSB\\operatorname*{proj\\,lim}"),Br("\\varlimsup","\\DOTSB\\operatorname*{\\overline{lim}}"),Br("\\varliminf","\\DOTSB\\operatorname*{\\underline{lim}}"),Br("\\varinjlim","\\DOTSB\\operatorname*{\\underrightarrow{lim}}"),Br("\\varprojlim","\\DOTSB\\operatorname*{\\underleftarrow{lim}}"),Br("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),Br("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),Br("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),Br("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),Br("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),Br("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),Br("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),Br("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),Br("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),Br("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),Br("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),Br("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),Br("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),Br("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),Br("\\imath","\\html@mathml{\\@imath}{\u0131}"),Br("\\jmath","\\html@mathml{\\@jmath}{\u0237}"),Br("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),Br("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),Br("\u27e6","\\llbracket"),Br("\u27e7","\\rrbracket"),Br("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),Br("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),Br("\u2983","\\lBrace"),Br("\u2984","\\rBrace"),Br("\\minuso","\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u29b5}}"),Br("\u29b5","\\minuso"),Br("\\darr","\\downarrow"),Br("\\dArr","\\Downarrow"),Br("\\Darr","\\Downarrow"),Br("\\lang","\\langle"),Br("\\rang","\\rangle"),Br("\\uarr","\\uparrow"),Br("\\uArr","\\Uparrow"),Br("\\Uarr","\\Uparrow"),Br("\\N","\\mathbb{N}"),Br("\\R","\\mathbb{R}"),Br("\\Z","\\mathbb{Z}"),Br("\\alef","\\aleph"),Br("\\alefsym","\\aleph"),Br("\\Alpha","\\mathrm{A}"),Br("\\Beta","\\mathrm{B}"),Br("\\bull","\\bullet"),Br("\\Chi","\\mathrm{X}"),Br("\\clubs","\\clubsuit"),Br("\\cnums","\\mathbb{C}"),Br("\\Complex","\\mathbb{C}"),Br("\\Dagger","\\ddagger"),Br("\\diamonds","\\diamondsuit"),Br("\\empty","\\emptyset"),Br("\\Epsilon","\\mathrm{E}"),Br("\\Eta","\\mathrm{H}"),Br("\\exist","\\exists"),Br("\\harr","\\leftrightarrow"),Br("\\hArr","\\Leftrightarrow"),Br("\\Harr","\\Leftrightarrow"),Br("\\hearts","\\heartsuit"),Br("\\image","\\Im"),Br("\\infin","\\infty"),Br("\\Iota","\\mathrm{I}"),Br("\\isin","\\in"),Br("\\Kappa","\\mathrm{K}"),Br("\\larr","\\leftarrow"),Br("\\lArr","\\Leftarrow"),Br("\\Larr","\\Leftarrow"),Br("\\lrarr","\\leftrightarrow"),Br("\\lrArr","\\Leftrightarrow"),Br("\\Lrarr","\\Leftrightarrow"),Br("\\Mu","\\mathrm{M}"),Br("\\natnums","\\mathbb{N}"),Br("\\Nu","\\mathrm{N}"),Br("\\Omicron","\\mathrm{O}"),Br("\\plusmn","\\pm"),Br("\\rarr","\\rightarrow"),Br("\\rArr","\\Rightarrow"),Br("\\Rarr","\\Rightarrow"),Br("\\real","\\Re"),Br("\\reals","\\mathbb{R}"),Br("\\Reals","\\mathbb{R}"),Br("\\Rho","\\mathrm{P}"),Br("\\sdot","\\cdot"),Br("\\sect","\\S"),Br("\\spades","\\spadesuit"),Br("\\sub","\\subset"),Br("\\sube","\\subseteq"),Br("\\supe","\\supseteq"),Br("\\Tau","\\mathrm{T}"),Br("\\thetasym","\\vartheta"),Br("\\weierp","\\wp"),Br("\\Zeta","\\mathrm{Z}"),Br("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),Br("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),Br("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),Br("\\bra","\\mathinner{\\langle{#1}|}"),Br("\\ket","\\mathinner{|{#1}\\rangle}"),Br("\\braket","\\mathinner{\\langle{#1}\\rangle}"),Br("\\Bra","\\left\\langle#1\\right|"),Br("\\Ket","\\left|#1\\right\\rangle");const In=e=>t=>{const r=t.consumeArg().tokens,n=t.consumeArg().tokens,o=t.consumeArg().tokens,s=t.consumeArg().tokens,i=t.macros.get("|"),a=t.macros.get("\\|");t.macros.beginGroup();const l=t=>r=>{e&&(r.macros.set("|",i),o.length&&r.macros.set("\\|",a));let s=t;if(!t&&o.length){"|"===r.future().text&&(r.popToken(),s=!0)}return{tokens:s?o:n,numArgs:0}};t.macros.set("|",l(!1)),o.length&&t.macros.set("\\|",l(!0));const h=t.consumeArg().tokens,c=t.expandTokens([...s,...h,...r]);return t.macros.endGroup(),{tokens:c.reverse(),numArgs:0}};Br("\\bra@ket",In(!1)),Br("\\bra@set",In(!0)),Br("\\Braket","\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),Br("\\Set","\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),Br("\\set","\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),Br("\\angln","{\\angl n}"),Br("\\blue","\\textcolor{##6495ed}{#1}"),Br("\\orange","\\textcolor{##ffa500}{#1}"),Br("\\pink","\\textcolor{##ff00af}{#1}"),Br("\\red","\\textcolor{##df0030}{#1}"),Br("\\green","\\textcolor{##28ae7b}{#1}"),Br("\\gray","\\textcolor{gray}{#1}"),Br("\\purple","\\textcolor{##9d38bd}{#1}"),Br("\\blueA","\\textcolor{##ccfaff}{#1}"),Br("\\blueB","\\textcolor{##80f6ff}{#1}"),Br("\\blueC","\\textcolor{##63d9ea}{#1}"),Br("\\blueD","\\textcolor{##11accd}{#1}"),Br("\\blueE","\\textcolor{##0c7f99}{#1}"),Br("\\tealA","\\textcolor{##94fff5}{#1}"),Br("\\tealB","\\textcolor{##26edd5}{#1}"),Br("\\tealC","\\textcolor{##01d1c1}{#1}"),Br("\\tealD","\\textcolor{##01a995}{#1}"),Br("\\tealE","\\textcolor{##208170}{#1}"),Br("\\greenA","\\textcolor{##b6ffb0}{#1}"),Br("\\greenB","\\textcolor{##8af281}{#1}"),Br("\\greenC","\\textcolor{##74cf70}{#1}"),Br("\\greenD","\\textcolor{##1fab54}{#1}"),Br("\\greenE","\\textcolor{##0d923f}{#1}"),Br("\\goldA","\\textcolor{##ffd0a9}{#1}"),Br("\\goldB","\\textcolor{##ffbb71}{#1}"),Br("\\goldC","\\textcolor{##ff9c39}{#1}"),Br("\\goldD","\\textcolor{##e07d10}{#1}"),Br("\\goldE","\\textcolor{##a75a05}{#1}"),Br("\\redA","\\textcolor{##fca9a9}{#1}"),Br("\\redB","\\textcolor{##ff8482}{#1}"),Br("\\redC","\\textcolor{##f9685d}{#1}"),Br("\\redD","\\textcolor{##e84d39}{#1}"),Br("\\redE","\\textcolor{##bc2612}{#1}"),Br("\\maroonA","\\textcolor{##ffbde0}{#1}"),Br("\\maroonB","\\textcolor{##ff92c6}{#1}"),Br("\\maroonC","\\textcolor{##ed5fa6}{#1}"),Br("\\maroonD","\\textcolor{##ca337c}{#1}"),Br("\\maroonE","\\textcolor{##9e034e}{#1}"),Br("\\purpleA","\\textcolor{##ddd7ff}{#1}"),Br("\\purpleB","\\textcolor{##c6b9fc}{#1}"),Br("\\purpleC","\\textcolor{##aa87ff}{#1}"),Br("\\purpleD","\\textcolor{##7854ab}{#1}"),Br("\\purpleE","\\textcolor{##543b78}{#1}"),Br("\\mintA","\\textcolor{##f5f9e8}{#1}"),Br("\\mintB","\\textcolor{##edf2df}{#1}"),Br("\\mintC","\\textcolor{##e0e5cc}{#1}"),Br("\\grayA","\\textcolor{##f6f7f7}{#1}"),Br("\\grayB","\\textcolor{##f0f1f2}{#1}"),Br("\\grayC","\\textcolor{##e3e5e6}{#1}"),Br("\\grayD","\\textcolor{##d6d8da}{#1}"),Br("\\grayE","\\textcolor{##babec2}{#1}"),Br("\\grayF","\\textcolor{##888d93}{#1}"),Br("\\grayG","\\textcolor{##626569}{#1}"),Br("\\grayH","\\textcolor{##3b3e40}{#1}"),Br("\\grayI","\\textcolor{##21242c}{#1}"),Br("\\kaBlue","\\textcolor{##314453}{#1}"),Br("\\kaGreen","\\textcolor{##71B307}{#1}");const Rn={"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0};class Hn{constructor(e,t,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=t,this.expansionCount=0,this.feed(e),this.macros=new zn(An,t.macros),this.mode=r,this.stack=[]}feed(e){this.lexer=new Mn(e,this.settings)}switchMode(e){this.mode=e}beginGroup(){this.macros.beginGroup()}endGroup(){this.macros.endGroup()}endGroups(){this.macros.endGroups()}future(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]}popToken(){return this.future(),this.stack.pop()}pushToken(e){this.stack.push(e)}pushTokens(e){this.stack.push(...e)}scanArgument(e){let t,r,n;if(e){if(this.consumeSpaces(),"["!==this.future().text)return null;t=this.popToken(),({tokens:n,end:r}=this.consumeArg(["]"]))}else({tokens:n,start:t,end:r}=this.consumeArg());return this.pushToken(new Nr("EOF",r.loc)),this.pushTokens(n),t.range(r,"")}consumeSpaces(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}}consumeArg(e){const t=[],r=e&&e.length>0;r||this.consumeSpaces();const o=this.future();let s,i=0,a=0;do{if(s=this.popToken(),t.push(s),"{"===s.text)++i;else if("}"===s.text){if(--i,-1===i)throw new n("Extra }",s)}else if("EOF"===s.text)throw new n("Unexpected end of input in a macro argument, expected '"+(e&&r?e[a]:"}")+"'",s);if(e&&r)if((0===i||1===i&&"{"===e[a])&&s.text===e[a]){if(++a,a===e.length){t.splice(-a,a);break}}else a=0}while(0!==i||r);return"{"===o.text&&"}"===t[t.length-1].text&&(t.pop(),t.shift()),t.reverse(),{tokens:t,start:o,end:s}}consumeArgs(e,t){if(t){if(t.length!==e+1)throw new n("The length of delimiters doesn't match the number of args!");const r=t[0];for(let e=0;ethis.settings.maxExpand)throw new n("Too many expansions: infinite loop or need to increase maxExpand setting")}expandOnce(e){const t=this.popToken(),r=t.text,o=t.noexpand?null:this._getExpansion(r);if(null==o||e&&o.unexpandable){if(e&&null==o&&"\\"===r[0]&&!this.isDefined(r))throw new n("Undefined control sequence: "+r);return this.pushToken(t),!1}this.countExpansion(1);let s=o.tokens;const i=this.consumeArgs(o.numArgs,o.delimiters);if(o.numArgs){s=s.slice();for(let e=s.length-1;e>=0;--e){let t=s[e];if("#"===t.text){if(0===e)throw new n("Incomplete placeholder at end of macro body",t);if(t=s[--e],"#"===t.text)s.splice(e+1,1);else{if(!/^[1-9]$/.test(t.text))throw new n("Not a valid argument number",t);s.splice(e,2,...i[+t.text-1])}}}}return this.pushTokens(s),s.length}expandAfterFuture(){return this.expandOnce(),this.future()}expandNextToken(){for(;;)if(!1===this.expandOnce()){const e=this.stack.pop();return e.treatAsRelax&&(e.text="\\relax"),e}throw new Error}expandMacro(e){return this.macros.has(e)?this.expandTokens([new Nr(e)]):void 0}expandTokens(e){const t=[],r=this.stack.length;for(this.pushTokens(e);this.stack.length>r;)if(!1===this.expandOnce(!0)){const e=this.stack.pop();e.treatAsRelax&&(e.noexpand=!1,e.treatAsRelax=!1),t.push(e)}return this.countExpansion(t.length),t}expandMacroAsText(e){const t=this.expandMacro(e);return t?t.map((e=>e.text)).join(""):t}_getExpansion(e){const t=this.macros.get(e);if(null==t)return t;if(1===e.length){const t=this.lexer.catcodes[e];if(null!=t&&13!==t)return}const r="function"==typeof t?t(this):t;if("string"==typeof r){let e=0;if(-1!==r.indexOf("#")){const t=r.replace(/##/g,"");for(;-1!==t.indexOf("#"+(e+1));)++e}const t=new Mn(r,this.settings),n=[];let o=t.lex();for(;"EOF"!==o.text;)n.push(o),o=t.lex();n.reverse();return{tokens:n,numArgs:e}}return r}isDefined(e){return this.macros.has(e)||yn.hasOwnProperty(e)||oe.math.hasOwnProperty(e)||oe.text.hasOwnProperty(e)||Rn.hasOwnProperty(e)}isExpandable(e){const t=this.macros.get(e);return null!=t?"string"==typeof t||"function"==typeof t||!t.unexpandable:yn.hasOwnProperty(e)&&!yn[e].primitive}}const On=/^[\u208a\u208b\u208c\u208d\u208e\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2090\u2091\u2095\u1d62\u2c7c\u2096\u2097\u2098\u2099\u2092\u209a\u1d63\u209b\u209c\u1d64\u1d65\u2093\u1d66\u1d67\u1d68\u1d69\u1d6a]/,En=Object.freeze({"\u208a":"+","\u208b":"-","\u208c":"=","\u208d":"(","\u208e":")","\u2080":"0","\u2081":"1","\u2082":"2","\u2083":"3","\u2084":"4","\u2085":"5","\u2086":"6","\u2087":"7","\u2088":"8","\u2089":"9","\u2090":"a","\u2091":"e","\u2095":"h","\u1d62":"i","\u2c7c":"j","\u2096":"k","\u2097":"l","\u2098":"m","\u2099":"n","\u2092":"o","\u209a":"p","\u1d63":"r","\u209b":"s","\u209c":"t","\u1d64":"u","\u1d65":"v","\u2093":"x","\u1d66":"\u03b2","\u1d67":"\u03b3","\u1d68":"\u03c1","\u1d69":"\u03d5","\u1d6a":"\u03c7","\u207a":"+","\u207b":"-","\u207c":"=","\u207d":"(","\u207e":")","\u2070":"0","\xb9":"1","\xb2":"2","\xb3":"3","\u2074":"4","\u2075":"5","\u2076":"6","\u2077":"7","\u2078":"8","\u2079":"9","\u1d2c":"A","\u1d2e":"B","\u1d30":"D","\u1d31":"E","\u1d33":"G","\u1d34":"H","\u1d35":"I","\u1d36":"J","\u1d37":"K","\u1d38":"L","\u1d39":"M","\u1d3a":"N","\u1d3c":"O","\u1d3e":"P","\u1d3f":"R","\u1d40":"T","\u1d41":"U","\u2c7d":"V","\u1d42":"W","\u1d43":"a","\u1d47":"b","\u1d9c":"c","\u1d48":"d","\u1d49":"e","\u1da0":"f","\u1d4d":"g","\u02b0":"h","\u2071":"i","\u02b2":"j","\u1d4f":"k","\u02e1":"l","\u1d50":"m","\u207f":"n","\u1d52":"o","\u1d56":"p","\u02b3":"r","\u02e2":"s","\u1d57":"t","\u1d58":"u","\u1d5b":"v","\u02b7":"w","\u02e3":"x","\u02b8":"y","\u1dbb":"z","\u1d5d":"\u03b2","\u1d5e":"\u03b3","\u1d5f":"\u03b4","\u1d60":"\u03d5","\u1d61":"\u03c7","\u1dbf":"\u03b8"}),Ln={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"},"\u0327":{text:"\\c"}},Dn={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u1e09":"c\u0327\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\xe7":"c\u0327","\u010f":"d\u030c","\u1e0b":"d\u0307","\u1e11":"d\u0327","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u1e1d":"e\u0327\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u0229":"e\u0327","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u0123":"g\u0327","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\u1e29":"h\u0327","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u0137":"k\u0327","\u013a":"l\u0301","\u013e":"l\u030c","\u013c":"l\u0327","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\u0146":"n\u0327","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u0157":"r\u0327","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u015f":"s\u0327","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\u0163":"t\u0327","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u1e08":"C\u0327\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\xc7":"C\u0327","\u010e":"D\u030c","\u1e0a":"D\u0307","\u1e10":"D\u0327","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u1e1c":"E\u0327\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u0228":"E\u0327","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u0122":"G\u0327","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\u1e28":"H\u0327","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0136":"K\u0327","\u0139":"L\u0301","\u013d":"L\u030c","\u013b":"L\u0327","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\u0145":"N\u0327","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u0156":"R\u0327","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u015e":"S\u0327","\u0164":"T\u030c","\u1e6a":"T\u0307","\u0162":"T\u0327","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"};class Vn{constructor(e,t){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new Hn(e,t,this.mode),this.settings=t,this.leftrightDepth=0}expect(e,t){if(void 0===t&&(t=!0),this.fetch().text!==e)throw new n("Expected '"+e+"', got '"+this.fetch().text+"'",this.fetch());t&&this.consume()}consume(){this.nextToken=null}fetch(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken}switchMode(e){this.mode=e,this.gullet.switchMode(e)}parse(){this.settings.globalGroup||this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");try{const e=this.parseExpression(!1);return this.expect("EOF"),this.settings.globalGroup||this.gullet.endGroup(),e}finally{this.gullet.endGroups()}}subparse(e){const t=this.nextToken;this.consume(),this.gullet.pushToken(new Nr("}")),this.gullet.pushTokens(e);const r=this.parseExpression(!1);return this.expect("}"),this.nextToken=t,r}parseExpression(e,t){const r=[];for(;;){"math"===this.mode&&this.consumeSpaces();const n=this.fetch();if(-1!==Vn.endOfExpression.indexOf(n.text))break;if(t&&n.text===t)break;if(e&&yn[n.text]&&yn[n.text].infix)break;const o=this.parseAtom(t);if(!o)break;"internal"!==o.type&&r.push(o)}return"text"===this.mode&&this.formLigatures(r),this.handleInfixNodes(r)}handleInfixNodes(e){let t,r=-1;for(let o=0;o=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+t[0]+'" used in math mode',e);const r=oe[this.mode][t].group,n=Cr.range(e);let s;if(te.hasOwnProperty(r)){const e=r;s={type:"atom",mode:this.mode,family:e,loc:n,text:t}}else s={type:r,mode:this.mode,loc:n,text:t};o=s}else{if(!(t.charCodeAt(0)>=128))return null;this.settings.strict&&(S(t.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+t[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+t[0]+'" ('+t.charCodeAt(0)+")",e)),o={type:"textord",mode:"text",loc:Cr.range(e),text:t}}if(this.consume(),r)for(let t=0;t0 - var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 - var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 - var s_v = "^(" + C + ")?" + v; // vowel in stem - - this.stemWord = function (w) { - var stem; - var suffix; - var firstch; - var origword = w; - - if (w.length < 3) - return w; - - var re; - var re2; - var re3; - var re4; - - firstch = w.substr(0,1); - if (firstch == "y") - w = firstch.toUpperCase() + w.substr(1); - - // Step 1a - re = /^(.+?)(ss|i)es$/; - re2 = /^(.+?)([^s])s$/; - - if (re.test(w)) - w = w.replace(re,"$1$2"); - else if (re2.test(w)) - w = w.replace(re2,"$1$2"); - - // Step 1b - re = /^(.+?)eed$/; - re2 = /^(.+?)(ed|ing)$/; - if (re.test(w)) { - var fp = re.exec(w); - re = new RegExp(mgr0); - if (re.test(fp[1])) { - re = /.$/; - w = w.replace(re,""); - } - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1]; - re2 = new RegExp(s_v); - if (re2.test(stem)) { - w = stem; - re2 = /(at|bl|iz)$/; - re3 = new RegExp("([^aeiouylsz])\\1$"); - re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re2.test(w)) - w = w + "e"; - else if (re3.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - else if (re4.test(w)) - w = w + "e"; - } - } - - // Step 1c - re = /^(.+?)y$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(s_v); - if (re.test(stem)) - w = stem + "i"; - } - - // Step 2 - re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step2list[suffix]; - } - - // Step 3 - re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - suffix = fp[2]; - re = new RegExp(mgr0); - if (re.test(stem)) - w = stem + step3list[suffix]; - } - - // Step 4 - re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; - re2 = /^(.+?)(s|t)(ion)$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - if (re.test(stem)) - w = stem; - } - else if (re2.test(w)) { - var fp = re2.exec(w); - stem = fp[1] + fp[2]; - re2 = new RegExp(mgr1); - if (re2.test(stem)) - w = stem; - } - - // Step 5 - re = /^(.+?)e$/; - if (re.test(w)) { - var fp = re.exec(w); - stem = fp[1]; - re = new RegExp(mgr1); - re2 = new RegExp(meq1); - re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); - if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) - w = stem; - } - re = /ll$/; - re2 = new RegExp(mgr1); - if (re.test(w) && re2.test(w)) { - re = /.$/; - w = w.replace(re,""); - } - - // and turn initial Y back to y - if (firstch == "y") - w = firstch.toLowerCase() + w.substr(1); - return w; - } -} - diff --git a/_static/minus.png b/_static/minus.png deleted file mode 100644 index d96755fda..000000000 Binary files a/_static/minus.png and /dev/null differ diff --git a/_static/nbsphinx-broken-thumbnail.svg b/_static/nbsphinx-broken-thumbnail.svg deleted file mode 100644 index 4919ca882..000000000 --- a/_static/nbsphinx-broken-thumbnail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/_static/nbsphinx-code-cells.css b/_static/nbsphinx-code-cells.css deleted file mode 100644 index a3fb27c30..000000000 --- a/_static/nbsphinx-code-cells.css +++ /dev/null @@ -1,259 +0,0 @@ -/* remove conflicting styling from Sphinx themes */ -div.nbinput.container div.prompt *, -div.nboutput.container div.prompt *, -div.nbinput.container div.input_area pre, -div.nboutput.container div.output_area pre, -div.nbinput.container div.input_area .highlight, -div.nboutput.container div.output_area .highlight { - border: none; - padding: 0; - margin: 0; - box-shadow: none; -} - -div.nbinput.container > div[class*=highlight], -div.nboutput.container > div[class*=highlight] { - margin: 0; -} - -div.nbinput.container div.prompt *, -div.nboutput.container div.prompt * { - background: none; -} - -div.nboutput.container div.output_area .highlight, -div.nboutput.container div.output_area pre { - background: unset; -} - -div.nboutput.container div.output_area div.highlight { - color: unset; /* override Pygments text color */ -} - -/* avoid gaps between output lines */ -div.nboutput.container div[class*=highlight] pre { - line-height: normal; -} - -/* input/output containers */ -div.nbinput.container, -div.nboutput.container { - display: -webkit-flex; - display: flex; - align-items: flex-start; - margin: 0; - width: 100%; -} -@media (max-width: 540px) { - div.nbinput.container, - div.nboutput.container { - flex-direction: column; - } -} - -/* input container */ -div.nbinput.container { - padding-top: 5px; -} - -/* last container */ -div.nblast.container { - padding-bottom: 5px; -} - -/* input prompt */ -div.nbinput.container div.prompt pre, -/* for sphinx_immaterial theme: */ -div.nbinput.container div.prompt pre > code { - color: #307FC1; -} - -/* output prompt */ -div.nboutput.container div.prompt pre, -/* for sphinx_immaterial theme: */ -div.nboutput.container div.prompt pre > code { - color: #BF5B3D; -} - -/* all prompts */ -div.nbinput.container div.prompt, -div.nboutput.container div.prompt { - width: 4.5ex; - padding-top: 5px; - position: relative; - user-select: none; -} - -div.nbinput.container div.prompt > div, -div.nboutput.container div.prompt > div { - position: absolute; - right: 0; - margin-right: 0.3ex; -} - -@media (max-width: 540px) { - div.nbinput.container div.prompt, - div.nboutput.container div.prompt { - width: unset; - text-align: left; - padding: 0.4em; - } - div.nboutput.container div.prompt.empty { - padding: 0; - } - - div.nbinput.container div.prompt > div, - div.nboutput.container div.prompt > div { - position: unset; - } -} - -/* disable scrollbars and line breaks on prompts */ -div.nbinput.container div.prompt pre, -div.nboutput.container div.prompt pre { - overflow: hidden; - white-space: pre; -} - -/* input/output area */ -div.nbinput.container div.input_area, -div.nboutput.container div.output_area { - -webkit-flex: 1; - flex: 1; - overflow: auto; -} -@media (max-width: 540px) { - div.nbinput.container div.input_area, - div.nboutput.container div.output_area { - width: 100%; - } -} - -/* input area */ -div.nbinput.container div.input_area { - border: 1px solid #e0e0e0; - border-radius: 2px; - /*background: #f5f5f5;*/ -} - -/* override MathJax center alignment in output cells */ -div.nboutput.container div[class*=MathJax] { - text-align: left !important; -} - -/* override sphinx.ext.imgmath center alignment in output cells */ -div.nboutput.container div.math p { - text-align: left; -} - -/* standard error */ -div.nboutput.container div.output_area.stderr { - background: #fdd; -} - -/* ANSI colors */ -.ansi-black-fg { color: #3E424D; } -.ansi-black-bg { background-color: #3E424D; } -.ansi-black-intense-fg { color: #282C36; } -.ansi-black-intense-bg { background-color: #282C36; } -.ansi-red-fg { color: #E75C58; } -.ansi-red-bg { background-color: #E75C58; } -.ansi-red-intense-fg { color: #B22B31; } -.ansi-red-intense-bg { background-color: #B22B31; } -.ansi-green-fg { color: #00A250; } -.ansi-green-bg { background-color: #00A250; } -.ansi-green-intense-fg { color: #007427; } -.ansi-green-intense-bg { background-color: #007427; } -.ansi-yellow-fg { color: #DDB62B; } -.ansi-yellow-bg { background-color: #DDB62B; } -.ansi-yellow-intense-fg { color: #B27D12; } -.ansi-yellow-intense-bg { background-color: #B27D12; } -.ansi-blue-fg { color: #208FFB; } -.ansi-blue-bg { background-color: #208FFB; } -.ansi-blue-intense-fg { color: #0065CA; } -.ansi-blue-intense-bg { background-color: #0065CA; } -.ansi-magenta-fg { color: #D160C4; } -.ansi-magenta-bg { background-color: #D160C4; } -.ansi-magenta-intense-fg { color: #A03196; } -.ansi-magenta-intense-bg { background-color: #A03196; } -.ansi-cyan-fg { color: #60C6C8; } -.ansi-cyan-bg { background-color: #60C6C8; } -.ansi-cyan-intense-fg { color: #258F8F; } -.ansi-cyan-intense-bg { background-color: #258F8F; } -.ansi-white-fg { color: #C5C1B4; } -.ansi-white-bg { background-color: #C5C1B4; } -.ansi-white-intense-fg { color: #A1A6B2; } -.ansi-white-intense-bg { background-color: #A1A6B2; } - -.ansi-default-inverse-fg { color: #FFFFFF; } -.ansi-default-inverse-bg { background-color: #000000; } - -.ansi-bold { font-weight: bold; } -.ansi-underline { text-decoration: underline; } - - -div.nbinput.container div.input_area div[class*=highlight] > pre, -div.nboutput.container div.output_area div[class*=highlight] > pre, -div.nboutput.container div.output_area div[class*=highlight].math, -div.nboutput.container div.output_area.rendered_html, -div.nboutput.container div.output_area > div.output_javascript, -div.nboutput.container div.output_area:not(.rendered_html) > img{ - padding: 5px; - margin: 0; -} - -/* fix copybtn overflow problem in chromium (needed for 'sphinx_copybutton') */ -div.nbinput.container div.input_area > div[class^='highlight'], -div.nboutput.container div.output_area > div[class^='highlight']{ - overflow-y: hidden; -} - -/* hide copy button on prompts for 'sphinx_copybutton' extension ... */ -.prompt .copybtn, -/* ... and 'sphinx_immaterial' theme */ -.prompt .md-clipboard.md-icon { - display: none; -} - -/* Some additional styling taken form the Jupyter notebook CSS */ -.jp-RenderedHTMLCommon table, -div.rendered_html table { - border: none; - border-collapse: collapse; - border-spacing: 0; - color: black; - font-size: 12px; - table-layout: fixed; -} -.jp-RenderedHTMLCommon thead, -div.rendered_html thead { - border-bottom: 1px solid black; - vertical-align: bottom; -} -.jp-RenderedHTMLCommon tr, -.jp-RenderedHTMLCommon th, -.jp-RenderedHTMLCommon td, -div.rendered_html tr, -div.rendered_html th, -div.rendered_html td { - text-align: right; - vertical-align: middle; - padding: 0.5em 0.5em; - line-height: normal; - white-space: normal; - max-width: none; - border: none; -} -.jp-RenderedHTMLCommon th, -div.rendered_html th { - font-weight: bold; -} -.jp-RenderedHTMLCommon tbody tr:nth-child(odd), -div.rendered_html tbody tr:nth-child(odd) { - background: #f5f5f5; -} -.jp-RenderedHTMLCommon tbody tr:hover, -div.rendered_html tbody tr:hover { - background: rgba(66, 165, 245, 0.2); -} - diff --git a/_static/nbsphinx-gallery.css b/_static/nbsphinx-gallery.css deleted file mode 100644 index 365c27a96..000000000 --- a/_static/nbsphinx-gallery.css +++ /dev/null @@ -1,31 +0,0 @@ -.nbsphinx-gallery { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - gap: 5px; - margin-top: 1em; - margin-bottom: 1em; -} - -.nbsphinx-gallery > a { - padding: 5px; - border: 1px dotted currentColor; - border-radius: 2px; - text-align: center; -} - -.nbsphinx-gallery > a:hover { - border-style: solid; -} - -.nbsphinx-gallery img { - max-width: 100%; - max-height: 100%; -} - -.nbsphinx-gallery > a > div:first-child { - display: flex; - align-items: start; - justify-content: center; - height: 120px; - margin-bottom: 5px; -} diff --git a/_static/nbsphinx-no-thumbnail.svg b/_static/nbsphinx-no-thumbnail.svg deleted file mode 100644 index 9dca7588f..000000000 --- a/_static/nbsphinx-no-thumbnail.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/_static/no_image.png b/_static/no_image.png deleted file mode 100644 index 8c2d48d5d..000000000 Binary files a/_static/no_image.png and /dev/null differ diff --git a/_static/plus.png b/_static/plus.png deleted file mode 100644 index 7107cec93..000000000 Binary files a/_static/plus.png and /dev/null differ diff --git a/_static/pygments.css b/_static/pygments.css deleted file mode 100644 index 997797f27..000000000 --- a/_static/pygments.css +++ /dev/null @@ -1,152 +0,0 @@ -html[data-theme="light"] .highlight pre { line-height: 125%; } -html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -html[data-theme="light"] .highlight .hll { background-color: #7971292e } -html[data-theme="light"] .highlight { background: #fefefe; color: #545454 } -html[data-theme="light"] .highlight .c { color: #797129 } /* Comment */ -html[data-theme="light"] .highlight .err { color: #d91e18 } /* Error */ -html[data-theme="light"] .highlight .k { color: #7928a1 } /* Keyword */ -html[data-theme="light"] .highlight .l { color: #797129 } /* Literal */ -html[data-theme="light"] .highlight .n { color: #545454 } /* Name */ -html[data-theme="light"] .highlight .o { color: #008000 } /* Operator */ -html[data-theme="light"] .highlight .p { color: #545454 } /* Punctuation */ -html[data-theme="light"] .highlight .ch { color: #797129 } /* Comment.Hashbang */ -html[data-theme="light"] .highlight .cm { color: #797129 } /* Comment.Multiline */ -html[data-theme="light"] .highlight .cp { color: #797129 } /* Comment.Preproc */ -html[data-theme="light"] .highlight .cpf { color: #797129 } /* Comment.PreprocFile */ -html[data-theme="light"] .highlight .c1 { color: #797129 } /* Comment.Single */ -html[data-theme="light"] .highlight .cs { color: #797129 } /* Comment.Special */ -html[data-theme="light"] .highlight .gd { color: #007faa } /* Generic.Deleted */ -html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ -html[data-theme="light"] .highlight .gh { color: #007faa } /* Generic.Heading */ -html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ -html[data-theme="light"] .highlight .gu { color: #007faa } /* Generic.Subheading */ -html[data-theme="light"] .highlight .kc { color: #7928a1 } /* Keyword.Constant */ -html[data-theme="light"] .highlight .kd { color: #7928a1 } /* Keyword.Declaration */ -html[data-theme="light"] .highlight .kn { color: #7928a1 } /* Keyword.Namespace */ -html[data-theme="light"] .highlight .kp { color: #7928a1 } /* Keyword.Pseudo */ -html[data-theme="light"] .highlight .kr { color: #7928a1 } /* Keyword.Reserved */ -html[data-theme="light"] .highlight .kt { color: #797129 } /* Keyword.Type */ -html[data-theme="light"] .highlight .ld { color: #797129 } /* Literal.Date */ -html[data-theme="light"] .highlight .m { color: #797129 } /* Literal.Number */ -html[data-theme="light"] .highlight .s { color: #008000 } /* Literal.String */ -html[data-theme="light"] .highlight .na { color: #797129 } /* Name.Attribute */ -html[data-theme="light"] .highlight .nb { color: #797129 } /* Name.Builtin */ -html[data-theme="light"] .highlight .nc { color: #007faa } /* Name.Class */ -html[data-theme="light"] .highlight .no { color: #007faa } /* Name.Constant */ -html[data-theme="light"] .highlight .nd { color: #797129 } /* Name.Decorator */ -html[data-theme="light"] .highlight .ni { color: #008000 } /* Name.Entity */ -html[data-theme="light"] .highlight .ne { color: #7928a1 } /* Name.Exception */ -html[data-theme="light"] .highlight .nf { color: #007faa } /* Name.Function */ -html[data-theme="light"] .highlight .nl { color: #797129 } /* Name.Label */ -html[data-theme="light"] .highlight .nn { color: #545454 } /* Name.Namespace */ -html[data-theme="light"] .highlight .nx { color: #545454 } /* Name.Other */ -html[data-theme="light"] .highlight .py { color: #007faa } /* Name.Property */ -html[data-theme="light"] .highlight .nt { color: #007faa } /* Name.Tag */ -html[data-theme="light"] .highlight .nv { color: #d91e18 } /* Name.Variable */ -html[data-theme="light"] .highlight .ow { color: #7928a1 } /* Operator.Word */ -html[data-theme="light"] .highlight .pm { color: #545454 } /* Punctuation.Marker */ -html[data-theme="light"] .highlight .w { color: #545454 } /* Text.Whitespace */ -html[data-theme="light"] .highlight .mb { color: #797129 } /* Literal.Number.Bin */ -html[data-theme="light"] .highlight .mf { color: #797129 } /* Literal.Number.Float */ -html[data-theme="light"] .highlight .mh { color: #797129 } /* Literal.Number.Hex */ -html[data-theme="light"] .highlight .mi { color: #797129 } /* Literal.Number.Integer */ -html[data-theme="light"] .highlight .mo { color: #797129 } /* Literal.Number.Oct */ -html[data-theme="light"] .highlight .sa { color: #008000 } /* Literal.String.Affix */ -html[data-theme="light"] .highlight .sb { color: #008000 } /* Literal.String.Backtick */ -html[data-theme="light"] .highlight .sc { color: #008000 } /* Literal.String.Char */ -html[data-theme="light"] .highlight .dl { color: #008000 } /* Literal.String.Delimiter */ -html[data-theme="light"] .highlight .sd { color: #008000 } /* Literal.String.Doc */ -html[data-theme="light"] .highlight .s2 { color: #008000 } /* Literal.String.Double */ -html[data-theme="light"] .highlight .se { color: #008000 } /* Literal.String.Escape */ -html[data-theme="light"] .highlight .sh { color: #008000 } /* Literal.String.Heredoc */ -html[data-theme="light"] .highlight .si { color: #008000 } /* Literal.String.Interpol */ -html[data-theme="light"] .highlight .sx { color: #008000 } /* Literal.String.Other */ -html[data-theme="light"] .highlight .sr { color: #d91e18 } /* Literal.String.Regex */ -html[data-theme="light"] .highlight .s1 { color: #008000 } /* Literal.String.Single */ -html[data-theme="light"] .highlight .ss { color: #007faa } /* Literal.String.Symbol */ -html[data-theme="light"] .highlight .bp { color: #797129 } /* Name.Builtin.Pseudo */ -html[data-theme="light"] .highlight .fm { color: #007faa } /* Name.Function.Magic */ -html[data-theme="light"] .highlight .vc { color: #d91e18 } /* Name.Variable.Class */ -html[data-theme="light"] .highlight .vg { color: #d91e18 } /* Name.Variable.Global */ -html[data-theme="light"] .highlight .vi { color: #d91e18 } /* Name.Variable.Instance */ -html[data-theme="light"] .highlight .vm { color: #797129 } /* Name.Variable.Magic */ -html[data-theme="light"] .highlight .il { color: #797129 } /* Literal.Number.Integer.Long */ -html[data-theme="dark"] .highlight pre { line-height: 125%; } -html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } -html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } -html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e } -html[data-theme="dark"] .highlight { background: #2b2b2b; color: #f8f8f2 } -html[data-theme="dark"] .highlight .c { color: #ffd900 } /* Comment */ -html[data-theme="dark"] .highlight .err { color: #ffa07a } /* Error */ -html[data-theme="dark"] .highlight .k { color: #dcc6e0 } /* Keyword */ -html[data-theme="dark"] .highlight .l { color: #ffd900 } /* Literal */ -html[data-theme="dark"] .highlight .n { color: #f8f8f2 } /* Name */ -html[data-theme="dark"] .highlight .o { color: #abe338 } /* Operator */ -html[data-theme="dark"] .highlight .p { color: #f8f8f2 } /* Punctuation */ -html[data-theme="dark"] .highlight .ch { color: #ffd900 } /* Comment.Hashbang */ -html[data-theme="dark"] .highlight .cm { color: #ffd900 } /* Comment.Multiline */ -html[data-theme="dark"] .highlight .cp { color: #ffd900 } /* Comment.Preproc */ -html[data-theme="dark"] .highlight .cpf { color: #ffd900 } /* Comment.PreprocFile */ -html[data-theme="dark"] .highlight .c1 { color: #ffd900 } /* Comment.Single */ -html[data-theme="dark"] .highlight .cs { color: #ffd900 } /* Comment.Special */ -html[data-theme="dark"] .highlight .gd { color: #00e0e0 } /* Generic.Deleted */ -html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */ -html[data-theme="dark"] .highlight .gh { color: #00e0e0 } /* Generic.Heading */ -html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */ -html[data-theme="dark"] .highlight .gu { color: #00e0e0 } /* Generic.Subheading */ -html[data-theme="dark"] .highlight .kc { color: #dcc6e0 } /* Keyword.Constant */ -html[data-theme="dark"] .highlight .kd { color: #dcc6e0 } /* Keyword.Declaration */ -html[data-theme="dark"] .highlight .kn { color: #dcc6e0 } /* Keyword.Namespace */ -html[data-theme="dark"] .highlight .kp { color: #dcc6e0 } /* Keyword.Pseudo */ -html[data-theme="dark"] .highlight .kr { color: #dcc6e0 } /* Keyword.Reserved */ -html[data-theme="dark"] .highlight .kt { color: #ffd900 } /* Keyword.Type */ -html[data-theme="dark"] .highlight .ld { color: #ffd900 } /* Literal.Date */ -html[data-theme="dark"] .highlight .m { color: #ffd900 } /* Literal.Number */ -html[data-theme="dark"] .highlight .s { color: #abe338 } /* Literal.String */ -html[data-theme="dark"] .highlight .na { color: #ffd900 } /* Name.Attribute */ -html[data-theme="dark"] .highlight .nb { color: #ffd900 } /* Name.Builtin */ -html[data-theme="dark"] .highlight .nc { color: #00e0e0 } /* Name.Class */ -html[data-theme="dark"] .highlight .no { color: #00e0e0 } /* Name.Constant */ -html[data-theme="dark"] .highlight .nd { color: #ffd900 } /* Name.Decorator */ -html[data-theme="dark"] .highlight .ni { color: #abe338 } /* Name.Entity */ -html[data-theme="dark"] .highlight .ne { color: #dcc6e0 } /* Name.Exception */ -html[data-theme="dark"] .highlight .nf { color: #00e0e0 } /* Name.Function */ -html[data-theme="dark"] .highlight .nl { color: #ffd900 } /* Name.Label */ -html[data-theme="dark"] .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ -html[data-theme="dark"] .highlight .nx { color: #f8f8f2 } /* Name.Other */ -html[data-theme="dark"] .highlight .py { color: #00e0e0 } /* Name.Property */ -html[data-theme="dark"] .highlight .nt { color: #00e0e0 } /* Name.Tag */ -html[data-theme="dark"] .highlight .nv { color: #ffa07a } /* Name.Variable */ -html[data-theme="dark"] .highlight .ow { color: #dcc6e0 } /* Operator.Word */ -html[data-theme="dark"] .highlight .pm { color: #f8f8f2 } /* Punctuation.Marker */ -html[data-theme="dark"] .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ -html[data-theme="dark"] .highlight .mb { color: #ffd900 } /* Literal.Number.Bin */ -html[data-theme="dark"] .highlight .mf { color: #ffd900 } /* Literal.Number.Float */ -html[data-theme="dark"] .highlight .mh { color: #ffd900 } /* Literal.Number.Hex */ -html[data-theme="dark"] .highlight .mi { color: #ffd900 } /* Literal.Number.Integer */ -html[data-theme="dark"] .highlight .mo { color: #ffd900 } /* Literal.Number.Oct */ -html[data-theme="dark"] .highlight .sa { color: #abe338 } /* Literal.String.Affix */ -html[data-theme="dark"] .highlight .sb { color: #abe338 } /* Literal.String.Backtick */ -html[data-theme="dark"] .highlight .sc { color: #abe338 } /* Literal.String.Char */ -html[data-theme="dark"] .highlight .dl { color: #abe338 } /* Literal.String.Delimiter */ -html[data-theme="dark"] .highlight .sd { color: #abe338 } /* Literal.String.Doc */ -html[data-theme="dark"] .highlight .s2 { color: #abe338 } /* Literal.String.Double */ -html[data-theme="dark"] .highlight .se { color: #abe338 } /* Literal.String.Escape */ -html[data-theme="dark"] .highlight .sh { color: #abe338 } /* Literal.String.Heredoc */ -html[data-theme="dark"] .highlight .si { color: #abe338 } /* Literal.String.Interpol */ -html[data-theme="dark"] .highlight .sx { color: #abe338 } /* Literal.String.Other */ -html[data-theme="dark"] .highlight .sr { color: #ffa07a } /* Literal.String.Regex */ -html[data-theme="dark"] .highlight .s1 { color: #abe338 } /* Literal.String.Single */ -html[data-theme="dark"] .highlight .ss { color: #00e0e0 } /* Literal.String.Symbol */ -html[data-theme="dark"] .highlight .bp { color: #ffd900 } /* Name.Builtin.Pseudo */ -html[data-theme="dark"] .highlight .fm { color: #00e0e0 } /* Name.Function.Magic */ -html[data-theme="dark"] .highlight .vc { color: #ffa07a } /* Name.Variable.Class */ -html[data-theme="dark"] .highlight .vg { color: #ffa07a } /* Name.Variable.Global */ -html[data-theme="dark"] .highlight .vi { color: #ffa07a } /* Name.Variable.Instance */ -html[data-theme="dark"] .highlight .vm { color: #ffd900 } /* Name.Variable.Magic */ -html[data-theme="dark"] .highlight .il { color: #ffd900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/scripts/bootstrap.js b/_static/scripts/bootstrap.js deleted file mode 100644 index bda8a6027..000000000 --- a/_static/scripts/bootstrap.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see bootstrap.js.LICENSE.txt */ -(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>w,afterRead:()=>b,afterWrite:()=>T,applyStyles:()=>D,arrow:()=>G,auto:()=>r,basePlacements:()=>a,beforeMain:()=>v,beforeRead:()=>g,beforeWrite:()=>E,bottom:()=>n,clippingParents:()=>h,computeStyles:()=>et,createPopper:()=>St,createPopperBase:()=>Lt,createPopperLite:()=>Dt,detectOverflow:()=>gt,end:()=>c,eventListeners:()=>nt,flip:()=>_t,hide:()=>yt,left:()=>o,main:()=>y,modifierPhases:()=>C,offset:()=>wt,placements:()=>m,popper:()=>u,popperGenerator:()=>kt,popperOffsets:()=>Et,preventOverflow:()=>At,read:()=>_,reference:()=>f,right:()=>s,start:()=>l,top:()=>i,variationPlacements:()=>p,viewport:()=>d,write:()=>A});var i="top",n="bottom",s="right",o="left",r="auto",a=[i,n,s,o],l="start",c="end",h="clippingParents",d="viewport",u="popper",f="reference",p=a.reduce((function(t,e){return t.concat([e+"-"+l,e+"-"+c])}),[]),m=[].concat(a,[r]).reduce((function(t,e){return t.concat([e,e+"-"+l,e+"-"+c])}),[]),g="beforeRead",_="read",b="afterRead",v="beforeMain",y="main",w="afterMain",E="beforeWrite",A="write",T="afterWrite",C=[g,_,b,v,y,w,E,A,T];function O(t){return t?(t.nodeName||"").toLowerCase():null}function x(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function k(t){return t instanceof x(t).Element||t instanceof Element}function L(t){return t instanceof x(t).HTMLElement||t instanceof HTMLElement}function S(t){return"undefined"!=typeof ShadowRoot&&(t instanceof x(t).ShadowRoot||t instanceof ShadowRoot)}const D={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];L(s)&&O(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});L(n)&&O(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function $(t){return t.split("-")[0]}var I=Math.max,N=Math.min,P=Math.round;function M(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function j(){return!/^((?!chrome|android).)*safari/i.test(M())}function F(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&L(t)&&(s=t.offsetWidth>0&&P(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&P(n.height)/t.offsetHeight||1);var r=(k(t)?x(t):window).visualViewport,a=!j()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function H(t){var e=F(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function B(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&S(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function W(t){return x(t).getComputedStyle(t)}function z(t){return["table","td","th"].indexOf(O(t))>=0}function R(t){return((k(t)?t.ownerDocument:t.document)||window.document).documentElement}function q(t){return"html"===O(t)?t:t.assignedSlot||t.parentNode||(S(t)?t.host:null)||R(t)}function V(t){return L(t)&&"fixed"!==W(t).position?t.offsetParent:null}function Y(t){for(var e=x(t),i=V(t);i&&z(i)&&"static"===W(i).position;)i=V(i);return i&&("html"===O(i)||"body"===O(i)&&"static"===W(i).position)?e:i||function(t){var e=/firefox/i.test(M());if(/Trident/i.test(M())&&L(t)&&"fixed"===W(t).position)return null;var i=q(t);for(S(i)&&(i=i.host);L(i)&&["html","body"].indexOf(O(i))<0;){var n=W(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function K(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Q(t,e,i){return I(t,N(e,i))}function X(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function U(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const G={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,r=t.state,l=t.name,c=t.options,h=r.elements.arrow,d=r.modifiersData.popperOffsets,u=$(r.placement),f=K(u),p=[o,s].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return X("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:U(t,a))}(c.padding,r),g=H(h),_="y"===f?i:o,b="y"===f?n:s,v=r.rects.reference[p]+r.rects.reference[f]-d[f]-r.rects.popper[p],y=d[f]-r.rects.reference[f],w=Y(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=Q(T,O,C),k=f;r.modifiersData[l]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&B(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function J(t){return t.split("-")[1]}var Z={top:"auto",right:"auto",bottom:"auto",left:"auto"};function tt(t){var e,r=t.popper,a=t.popperRect,l=t.placement,h=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=o,C=i,O=window;if(p){var k=Y(r),L="clientHeight",S="clientWidth";k===x(r)&&"static"!==W(k=R(r)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===i||(l===o||l===s)&&h===c)&&(C=n,y-=(g&&k===O&&O.visualViewport?O.visualViewport.height:k[L])-a.height,y*=f?1:-1),l!==o&&(l!==i&&l!==n||h!==c)||(T=s,b-=(g&&k===O&&O.visualViewport?O.visualViewport.width:k[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&Z),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:P(i*s)/s||0,y:P(n*s)/s||0}}({x:b,y},x(r)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const et={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:$(e.placement),variation:J(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,tt(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,tt(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var it={passive:!0};const nt={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=x(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,it)})),a&&l.addEventListener("resize",i.update,it),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,it)})),a&&l.removeEventListener("resize",i.update,it)}},data:{}};var st={left:"right",right:"left",bottom:"top",top:"bottom"};function ot(t){return t.replace(/left|right|bottom|top/g,(function(t){return st[t]}))}var rt={start:"end",end:"start"};function at(t){return t.replace(/start|end/g,(function(t){return rt[t]}))}function lt(t){var e=x(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ct(t){return F(R(t)).left+lt(t).scrollLeft}function ht(t){var e=W(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function dt(t){return["html","body","#document"].indexOf(O(t))>=0?t.ownerDocument.body:L(t)&&ht(t)?t:dt(q(t))}function ut(t,e){var i;void 0===e&&(e=[]);var n=dt(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=x(n),r=s?[o].concat(o.visualViewport||[],ht(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ut(q(r)))}function ft(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function pt(t,e,i){return e===d?ft(function(t,e){var i=x(t),n=R(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=j();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ct(t),y:l}}(t,i)):k(e)?function(t,e){var i=F(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):ft(function(t){var e,i=R(t),n=lt(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=I(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=I(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ct(t),l=-n.scrollTop;return"rtl"===W(s||i).direction&&(a+=I(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(R(t)))}function mt(t){var e,r=t.reference,a=t.element,h=t.placement,d=h?$(h):null,u=h?J(h):null,f=r.x+r.width/2-a.width/2,p=r.y+r.height/2-a.height/2;switch(d){case i:e={x:f,y:r.y-a.height};break;case n:e={x:f,y:r.y+r.height};break;case s:e={x:r.x+r.width,y:p};break;case o:e={x:r.x-a.width,y:p};break;default:e={x:r.x,y:r.y}}var m=d?K(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case l:e[m]=e[m]-(r[g]/2-a[g]/2);break;case c:e[m]=e[m]+(r[g]/2-a[g]/2)}}return e}function gt(t,e){void 0===e&&(e={});var o=e,r=o.placement,l=void 0===r?t.placement:r,c=o.strategy,p=void 0===c?t.strategy:c,m=o.boundary,g=void 0===m?h:m,_=o.rootBoundary,b=void 0===_?d:_,v=o.elementContext,y=void 0===v?u:v,w=o.altBoundary,E=void 0!==w&&w,A=o.padding,T=void 0===A?0:A,C=X("number"!=typeof T?T:U(T,a)),x=y===u?f:u,S=t.rects.popper,D=t.elements[E?x:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ut(q(t)),i=["absolute","fixed"].indexOf(W(t).position)>=0&&L(t)?Y(t):t;return k(i)?e.filter((function(t){return k(t)&&B(t,i)&&"body"!==O(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=pt(t,i,n);return e.top=I(s.top,e.top),e.right=N(s.right,e.right),e.bottom=N(s.bottom,e.bottom),e.left=I(s.left,e.left),e}),pt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(k(D)?D:D.contextElement||R(t.elements.popper),g,b,p),P=F(t.elements.reference),M=mt({reference:P,element:S,strategy:"absolute",placement:l}),j=ft(Object.assign({},S,M)),H=y===u?j:P,z={top:$.top-H.top+C.top,bottom:H.bottom-$.bottom+C.bottom,left:$.left-H.left+C.left,right:H.right-$.right+C.right},V=t.modifiersData.offset;if(y===u&&V){var K=V[l];Object.keys(z).forEach((function(t){var e=[s,n].indexOf(t)>=0?1:-1,o=[i,n].indexOf(t)>=0?"y":"x";z[t]+=K[o]*e}))}return z}const _t={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,c=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=c.mainAxis,u=void 0===d||d,f=c.altAxis,g=void 0===f||f,_=c.fallbackPlacements,b=c.padding,v=c.boundary,y=c.rootBoundary,w=c.altBoundary,E=c.flipVariations,A=void 0===E||E,T=c.allowedAutoPlacements,C=e.options.placement,O=$(C),x=_||(O!==C&&A?function(t){if($(t)===r)return[];var e=ot(t);return[at(t),e,at(e)]}(C):[ot(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat($(i)===r?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,l=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?m:c,d=J(n),u=d?l?p:p.filter((function(t){return J(t)===d})):a,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var g=f.reduce((function(e,i){return e[i]=gt(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[$(i)],e}),{});return Object.keys(g).sort((function(t,e){return g[t]-g[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,I=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=gt(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?s:o:F?n:i;L[B]>S[B]&&(z=ot(z));var R=ot(z),q=[];if(u&&q.push(W[j]<=0),g&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,I=!1;break}D.set(M,q)}if(I)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function bt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function vt(t){return[i,s,n,o].some((function(e){return t[e]>=0}))}const yt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=gt(e,{elementContext:"reference"}),a=gt(e,{altBoundary:!0}),l=bt(r,n),c=bt(a,s,o),h=vt(l),d=vt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},wt={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,n=t.options,r=t.name,a=n.offset,l=void 0===a?[0,0]:a,c=m.reduce((function(t,n){return t[n]=function(t,e,n){var r=$(t),a=[o,i].indexOf(r)>=0?-1:1,l="function"==typeof n?n(Object.assign({},e,{placement:t})):n,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[o,s].indexOf(r)>=0?{x:h,y:c}:{x:c,y:h}}(n,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[r]=c}},Et={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=mt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},At={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,a=t.name,c=r.mainAxis,h=void 0===c||c,d=r.altAxis,u=void 0!==d&&d,f=r.boundary,p=r.rootBoundary,m=r.altBoundary,g=r.padding,_=r.tether,b=void 0===_||_,v=r.tetherOffset,y=void 0===v?0:v,w=gt(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=$(e.placement),A=J(e.placement),T=!A,C=K(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),P=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?i:o,B="y"===C?n:s,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[B],V=b?-L[W]/2:0,X=A===l?k[W]:L[W],U=A===l?-L[W]:-k[W],G=e.elements.arrow,Z=b&&G?H(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[B],nt=Q(0,k[W],Z[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:X-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&Y(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==P?void 0:P[C])?j:0,ct=z+ot-lt,ht=Q(b?N(R,z+st-lt-at):R,z,b?I(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?i:o,ft="x"===C?n:s,pt=x[O],mt="y"===O?"height":"width",_t=pt+w[ut],bt=pt-w[ft],vt=-1!==[i,o].indexOf(E),yt=null!=(dt=null==P?void 0:P[O])?dt:0,wt=vt?_t:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=Q(t,e,i);return n>i?i:n}(wt,pt,Et):Q(b?wt:_t,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Tt(t,e,i){void 0===i&&(i=!1);var n,s,o=L(e),r=L(e)&&function(t){var e=t.getBoundingClientRect(),i=P(e.width)/t.offsetWidth||1,n=P(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=R(e),l=F(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==O(e)||ht(a))&&(c=(n=e)!==x(n)&&L(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:lt(n)),L(e)?((h=F(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ct(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ct(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var Ot={placement:"bottom",modifiers:[],strategy:"absolute"};function xt(){for(var t=arguments.length,e=new Array(t),i=0;i$t.has(t)&&$t.get(t).get(e)||null,remove(t,e){if(!$t.has(t))return;const i=$t.get(t);i.delete(e),0===i.size&&$t.delete(t)}},Nt="transitionend",Pt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),Mt=t=>{t.dispatchEvent(new Event(Nt))},jt=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ft=t=>jt(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Pt(t)):null,Ht=t=>{if(!jt(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Bt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),Wt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?Wt(t.parentNode):null},zt=()=>{},Rt=t=>{t.offsetHeight},qt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Vt=[],Yt=()=>"rtl"===document.documentElement.dir,Kt=t=>{var e;e=()=>{const e=qt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Vt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Vt)t()})),Vt.push(e)):e()},Qt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Xt=(t,e,i=!0)=>{if(!i)return void Qt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Nt,o),Qt(t))};e.addEventListener(Nt,o),setTimeout((()=>{s||Mt(e)}),n)},Ut=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Gt=/[^.]*(?=\..*)\.|.*/,Jt=/\..*/,Zt=/::\d+$/,te={};let ee=1;const ie={mouseenter:"mouseover",mouseleave:"mouseout"},ne=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function se(t,e){return e&&`${e}::${ee++}`||t.uidEvent||ee++}function oe(t){const e=se(t);return t.uidEvent=e,te[e]=te[e]||{},te[e]}function re(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function ae(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=de(t);return ne.has(o)||(o=t),[n,s,o]}function le(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=ae(e,i,n);if(e in ie){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=oe(t),c=l[a]||(l[a]={}),h=re(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=se(r,e.replace(Gt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return fe(s,{delegateTarget:r}),n.oneOff&&ue.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return fe(n,{delegateTarget:t}),i.oneOff&&ue.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function ce(t,e,i,n,s){const o=re(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function he(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&ce(t,e,i,r.callable,r.delegationSelector)}function de(t){return t=t.replace(Jt,""),ie[t]||t}const ue={on(t,e,i,n){le(t,e,i,n,!1)},one(t,e,i,n){le(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=ae(e,i,n),a=r!==e,l=oe(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))he(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(Zt,"");a&&!e.includes(s)||ce(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;ce(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=qt();let s=null,o=!0,r=!0,a=!1;e!==de(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=fe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function fe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function pe(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function me(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const ge={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${me(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${me(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=pe(t.dataset[n])}return e},getDataAttribute:(t,e)=>pe(t.getAttribute(`data-bs-${me(e)}`))};class _e{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=jt(e)?ge.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...jt(e)?ge.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=jt(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class be extends _e{constructor(t,e){super(),(t=Ft(t))&&(this._element=t,this._config=this._getConfig(e),It.set(this._element,this.constructor.DATA_KEY,this))}dispose(){It.remove(this._element,this.constructor.DATA_KEY),ue.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Xt(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return It.get(Ft(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.2"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ve=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?Pt(i.trim()):null}return e},ye={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Bt(t)&&Ht(t)))},getSelectorFromElement(t){const e=ve(t);return e&&ye.findOne(e)?e:null},getElementFromSelector(t){const e=ve(t);return e?ye.findOne(e):null},getMultipleElementsFromSelector(t){const e=ve(t);return e?ye.find(e):[]}},we=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;ue.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Bt(this))return;const s=ye.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ee=".bs.alert",Ae=`close${Ee}`,Te=`closed${Ee}`;class Ce extends be{static get NAME(){return"alert"}close(){if(ue.trigger(this._element,Ae).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),ue.trigger(this._element,Te),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Ce.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}we(Ce,"close"),Kt(Ce);const Oe='[data-bs-toggle="button"]';class xe extends be{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=xe.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}ue.on(document,"click.bs.button.data-api",Oe,(t=>{t.preventDefault();const e=t.target.closest(Oe);xe.getOrCreateInstance(e).toggle()})),Kt(xe);const ke=".bs.swipe",Le=`touchstart${ke}`,Se=`touchmove${ke}`,De=`touchend${ke}`,$e=`pointerdown${ke}`,Ie=`pointerup${ke}`,Ne={endCallback:null,leftCallback:null,rightCallback:null},Pe={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Me extends _e{constructor(t,e){super(),this._element=t,t&&Me.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Ne}static get DefaultType(){return Pe}static get NAME(){return"swipe"}dispose(){ue.off(this._element,ke)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Qt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Qt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(ue.on(this._element,$e,(t=>this._start(t))),ue.on(this._element,Ie,(t=>this._end(t))),this._element.classList.add("pointer-event")):(ue.on(this._element,Le,(t=>this._start(t))),ue.on(this._element,Se,(t=>this._move(t))),ue.on(this._element,De,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const je=".bs.carousel",Fe=".data-api",He="next",Be="prev",We="left",ze="right",Re=`slide${je}`,qe=`slid${je}`,Ve=`keydown${je}`,Ye=`mouseenter${je}`,Ke=`mouseleave${je}`,Qe=`dragstart${je}`,Xe=`load${je}${Fe}`,Ue=`click${je}${Fe}`,Ge="carousel",Je="active",Ze=".active",ti=".carousel-item",ei=Ze+ti,ii={ArrowLeft:ze,ArrowRight:We},ni={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},si={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class oi extends be{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=ye.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===Ge&&this.cycle()}static get Default(){return ni}static get DefaultType(){return si}static get NAME(){return"carousel"}next(){this._slide(He)}nextWhenVisible(){!document.hidden&&Ht(this._element)&&this.next()}prev(){this._slide(Be)}pause(){this._isSliding&&Mt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?ue.one(this._element,qe,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void ue.one(this._element,qe,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?He:Be;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&ue.on(this._element,Ve,(t=>this._keydown(t))),"hover"===this._config.pause&&(ue.on(this._element,Ye,(()=>this.pause())),ue.on(this._element,Ke,(()=>this._maybeEnableCycle()))),this._config.touch&&Me.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of ye.find(".carousel-item img",this._element))ue.on(t,Qe,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(We)),rightCallback:()=>this._slide(this._directionToOrder(ze)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new Me(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=ii[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=ye.findOne(Ze,this._indicatorsElement);e.classList.remove(Je),e.removeAttribute("aria-current");const i=ye.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(Je),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===He,s=e||Ut(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>ue.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Re).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),Rt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(Je),i.classList.remove(Je,c,l),this._isSliding=!1,r(qe)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return ye.findOne(ei,this._element)}_getItems(){return ye.find(ti,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Yt()?t===We?Be:He:t===We?He:Be}_orderToDirection(t){return Yt()?t===Be?We:ze:t===Be?ze:We}static jQueryInterface(t){return this.each((function(){const e=oi.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}ue.on(document,Ue,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=ye.getElementFromSelector(this);if(!e||!e.classList.contains(Ge))return;t.preventDefault();const i=oi.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===ge.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),ue.on(window,Xe,(()=>{const t=ye.find('[data-bs-ride="carousel"]');for(const e of t)oi.getOrCreateInstance(e)})),Kt(oi);const ri=".bs.collapse",ai=`show${ri}`,li=`shown${ri}`,ci=`hide${ri}`,hi=`hidden${ri}`,di=`click${ri}.data-api`,ui="show",fi="collapse",pi="collapsing",mi=`:scope .${fi} .${fi}`,gi='[data-bs-toggle="collapse"]',_i={parent:null,toggle:!0},bi={parent:"(null|element)",toggle:"boolean"};class vi extends be{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=ye.find(gi);for(const t of i){const e=ye.getSelectorFromElement(t),i=ye.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return _i}static get DefaultType(){return bi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>vi.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(ue.trigger(this._element,ai).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(fi),this._element.classList.add(pi),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(pi),this._element.classList.add(fi,ui),this._element.style[e]="",ue.trigger(this._element,li)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(ue.trigger(this._element,ci).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,Rt(this._element),this._element.classList.add(pi),this._element.classList.remove(fi,ui);for(const t of this._triggerArray){const e=ye.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(pi),this._element.classList.add(fi),ue.trigger(this._element,hi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(ui)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ft(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(gi);for(const e of t){const t=ye.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=ye.find(mi,this._config.parent);return ye.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=vi.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}ue.on(document,di,gi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of ye.getMultipleElementsFromSelector(this))vi.getOrCreateInstance(t,{toggle:!1}).toggle()})),Kt(vi);const yi="dropdown",wi=".bs.dropdown",Ei=".data-api",Ai="ArrowUp",Ti="ArrowDown",Ci=`hide${wi}`,Oi=`hidden${wi}`,xi=`show${wi}`,ki=`shown${wi}`,Li=`click${wi}${Ei}`,Si=`keydown${wi}${Ei}`,Di=`keyup${wi}${Ei}`,$i="show",Ii='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Ni=`${Ii}.${$i}`,Pi=".dropdown-menu",Mi=Yt()?"top-end":"top-start",ji=Yt()?"top-start":"top-end",Fi=Yt()?"bottom-end":"bottom-start",Hi=Yt()?"bottom-start":"bottom-end",Bi=Yt()?"left-start":"right-start",Wi=Yt()?"right-start":"left-start",zi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Ri={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class qi extends be{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=ye.next(this._element,Pi)[0]||ye.prev(this._element,Pi)[0]||ye.findOne(Pi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return zi}static get DefaultType(){return Ri}static get NAME(){return yi}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Bt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!ue.trigger(this._element,xi,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))ue.on(t,"mouseover",zt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add($i),this._element.classList.add($i),ue.trigger(this._element,ki,t)}}hide(){if(Bt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!ue.trigger(this._element,Ci,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.off(t,"mouseover",zt);this._popper&&this._popper.destroy(),this._menu.classList.remove($i),this._element.classList.remove($i),this._element.setAttribute("aria-expanded","false"),ge.removeDataAttribute(this._menu,"popper"),ue.trigger(this._element,Oi,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!jt(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${yi.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:jt(this._config.reference)?t=Ft(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=St(t,this._menu,i)}_isShown(){return this._menu.classList.contains($i)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Bi;if(t.classList.contains("dropstart"))return Wi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?ji:Mi:e?Hi:Fi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(ge.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Qt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=ye.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Ht(t)));i.length&&Ut(i,e,t===Ti,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=ye.find(Ni);for(const i of e){const e=qi.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Ai,Ti].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Ii)?this:ye.prev(this,Ii)[0]||ye.next(this,Ii)[0]||ye.findOne(Ii,t.delegateTarget.parentNode),o=qi.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}ue.on(document,Si,Ii,qi.dataApiKeydownHandler),ue.on(document,Si,Pi,qi.dataApiKeydownHandler),ue.on(document,Li,qi.clearMenus),ue.on(document,Di,qi.clearMenus),ue.on(document,Li,Ii,(function(t){t.preventDefault(),qi.getOrCreateInstance(this).toggle()})),Kt(qi);const Vi="backdrop",Yi="show",Ki=`mousedown.bs.${Vi}`,Qi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Xi={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Ui extends _e{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Qi}static get DefaultType(){return Xi}static get NAME(){return Vi}show(t){if(!this._config.isVisible)return void Qt(t);this._append();const e=this._getElement();this._config.isAnimated&&Rt(e),e.classList.add(Yi),this._emulateAnimation((()=>{Qt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Yi),this._emulateAnimation((()=>{this.dispose(),Qt(t)}))):Qt(t)}dispose(){this._isAppended&&(ue.off(this._element,Ki),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ft(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),ue.on(t,Ki,(()=>{Qt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Xt(t,this._getElement(),this._config.isAnimated)}}const Gi=".bs.focustrap",Ji=`focusin${Gi}`,Zi=`keydown.tab${Gi}`,tn="backward",en={autofocus:!0,trapElement:null},nn={autofocus:"boolean",trapElement:"element"};class sn extends _e{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return en}static get DefaultType(){return nn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),ue.off(document,Gi),ue.on(document,Ji,(t=>this._handleFocusin(t))),ue.on(document,Zi,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,ue.off(document,Gi))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=ye.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===tn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?tn:"forward")}}const on=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",rn=".sticky-top",an="padding-right",ln="margin-right";class cn{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,an,(e=>e+t)),this._setElementAttributes(on,an,(e=>e+t)),this._setElementAttributes(rn,ln,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,an),this._resetElementAttributes(on,an),this._resetElementAttributes(rn,ln)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&ge.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=ge.getDataAttribute(t,e);null!==i?(ge.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(jt(t))e(t);else for(const i of ye.find(t,this._element))e(i)}}const hn=".bs.modal",dn=`hide${hn}`,un=`hidePrevented${hn}`,fn=`hidden${hn}`,pn=`show${hn}`,mn=`shown${hn}`,gn=`resize${hn}`,_n=`click.dismiss${hn}`,bn=`mousedown.dismiss${hn}`,vn=`keydown.dismiss${hn}`,yn=`click${hn}.data-api`,wn="modal-open",En="show",An="modal-static",Tn={backdrop:!0,focus:!0,keyboard:!0},Cn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class On extends be{constructor(t,e){super(t,e),this._dialog=ye.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new cn,this._addEventListeners()}static get Default(){return Tn}static get DefaultType(){return Cn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||ue.trigger(this._element,pn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(wn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(ue.trigger(this._element,dn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(En),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){ue.off(window,hn),ue.off(this._dialog,hn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Ui({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=ye.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),Rt(this._element),this._element.classList.add(En),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,ue.trigger(this._element,mn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){ue.on(this._element,vn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),ue.on(window,gn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),ue.on(this._element,bn,(t=>{ue.one(this._element,_n,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(wn),this._resetAdjustments(),this._scrollBar.reset(),ue.trigger(this._element,fn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(ue.trigger(this._element,un).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(An)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(An),this._queueCallback((()=>{this._element.classList.remove(An),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Yt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Yt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=On.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}ue.on(document,yn,'[data-bs-toggle="modal"]',(function(t){const e=ye.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),ue.one(e,pn,(t=>{t.defaultPrevented||ue.one(e,fn,(()=>{Ht(this)&&this.focus()}))}));const i=ye.findOne(".modal.show");i&&On.getInstance(i).hide(),On.getOrCreateInstance(e).toggle(this)})),we(On),Kt(On);const xn=".bs.offcanvas",kn=".data-api",Ln=`load${xn}${kn}`,Sn="show",Dn="showing",$n="hiding",In=".offcanvas.show",Nn=`show${xn}`,Pn=`shown${xn}`,Mn=`hide${xn}`,jn=`hidePrevented${xn}`,Fn=`hidden${xn}`,Hn=`resize${xn}`,Bn=`click${xn}${kn}`,Wn=`keydown.dismiss${xn}`,zn={backdrop:!0,keyboard:!0,scroll:!1},Rn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class qn extends be{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return zn}static get DefaultType(){return Rn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||ue.trigger(this._element,Nn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new cn).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Dn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(Sn),this._element.classList.remove(Dn),ue.trigger(this._element,Pn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(ue.trigger(this._element,Mn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add($n),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(Sn,$n),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new cn).reset(),ue.trigger(this._element,Fn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Ui({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():ue.trigger(this._element,jn)}:null})}_initializeFocusTrap(){return new sn({trapElement:this._element})}_addEventListeners(){ue.on(this._element,Wn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():ue.trigger(this._element,jn))}))}static jQueryInterface(t){return this.each((function(){const e=qn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}ue.on(document,Bn,'[data-bs-toggle="offcanvas"]',(function(t){const e=ye.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Bt(this))return;ue.one(e,Fn,(()=>{Ht(this)&&this.focus()}));const i=ye.findOne(In);i&&i!==e&&qn.getInstance(i).hide(),qn.getOrCreateInstance(e).toggle(this)})),ue.on(window,Ln,(()=>{for(const t of ye.find(In))qn.getOrCreateInstance(t).show()})),ue.on(window,Hn,(()=>{for(const t of ye.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&qn.getOrCreateInstance(t).hide()})),we(qn),Kt(qn);const Vn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Yn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Kn=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Qn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Yn.has(i)||Boolean(Kn.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Xn={allowList:Vn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Un={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},Gn={entry:"(string|element|function|null)",selector:"(string|element)"};class Jn extends _e{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Xn}static get DefaultType(){return Un}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},Gn)}_setContent(t,e,i){const n=ye.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?jt(e)?this._putElementInTemplate(Ft(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Qn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Qt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Zn=new Set(["sanitize","allowList","sanitizeFn"]),ts="fade",es="show",is=".modal",ns="hide.bs.modal",ss="hover",os="focus",rs={AUTO:"auto",TOP:"top",RIGHT:Yt()?"left":"right",BOTTOM:"bottom",LEFT:Yt()?"right":"left"},as={allowList:Vn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},ls={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class cs extends be{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return as}static get DefaultType(){return ls}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),ue.off(this._element.closest(is),ns,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=ue.trigger(this._element,this.constructor.eventName("show")),e=(Wt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),ue.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.on(t,"mouseover",zt);this._queueCallback((()=>{ue.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!ue.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(es),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))ue.off(t,"mouseover",zt);this._activeTrigger.click=!1,this._activeTrigger[os]=!1,this._activeTrigger[ss]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),ue.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ts,es),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ts),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Jn({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ts)}_isShown(){return this.tip&&this.tip.classList.contains(es)}_createPopper(t){const e=Qt(this._config.placement,[this,t,this._element]),i=rs[e.toUpperCase()];return St(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Qt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Qt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)ue.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ss?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ss?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");ue.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?os:ss]=!0,e._enter()})),ue.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?os:ss]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},ue.on(this._element.closest(is),ns,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=ge.getDataAttributes(this._element);for(const t of Object.keys(e))Zn.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ft(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=cs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Kt(cs);const hs={...cs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},ds={...cs.DefaultType,content:"(null|string|element|function)"};class us extends cs{static get Default(){return hs}static get DefaultType(){return ds}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=us.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Kt(us);const fs=".bs.scrollspy",ps=`activate${fs}`,ms=`click${fs}`,gs=`load${fs}.data-api`,_s="active",bs="[href]",vs=".nav-link",ys=`${vs}, .nav-item > ${vs}, .list-group-item`,ws={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},Es={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class As extends be{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return ws}static get DefaultType(){return Es}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ft(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(ue.off(this._config.target,ms),ue.on(this._config.target,ms,bs,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=ye.find(bs,this._config.target);for(const e of t){if(!e.hash||Bt(e))continue;const t=ye.findOne(decodeURI(e.hash),this._element);Ht(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(_s),this._activateParents(t),ue.trigger(this._element,ps,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))ye.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(_s);else for(const e of ye.parents(t,".nav, .list-group"))for(const t of ye.prev(e,ys))t.classList.add(_s)}_clearActiveClass(t){t.classList.remove(_s);const e=ye.find(`${bs}.${_s}`,t);for(const t of e)t.classList.remove(_s)}static jQueryInterface(t){return this.each((function(){const e=As.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}ue.on(window,gs,(()=>{for(const t of ye.find('[data-bs-spy="scroll"]'))As.getOrCreateInstance(t)})),Kt(As);const Ts=".bs.tab",Cs=`hide${Ts}`,Os=`hidden${Ts}`,xs=`show${Ts}`,ks=`shown${Ts}`,Ls=`click${Ts}`,Ss=`keydown${Ts}`,Ds=`load${Ts}`,$s="ArrowLeft",Is="ArrowRight",Ns="ArrowUp",Ps="ArrowDown",Ms="Home",js="End",Fs="active",Hs="fade",Bs="show",Ws=".dropdown-toggle",zs=`:not(${Ws})`,Rs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',qs=`.nav-link${zs}, .list-group-item${zs}, [role="tab"]${zs}, ${Rs}`,Vs=`.${Fs}[data-bs-toggle="tab"], .${Fs}[data-bs-toggle="pill"], .${Fs}[data-bs-toggle="list"]`;class Ys extends be{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),ue.on(this._element,Ss,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?ue.trigger(e,Cs,{relatedTarget:t}):null;ue.trigger(t,xs,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(Fs),this._activate(ye.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),ue.trigger(t,ks,{relatedTarget:e})):t.classList.add(Bs)}),t,t.classList.contains(Hs)))}_deactivate(t,e){t&&(t.classList.remove(Fs),t.blur(),this._deactivate(ye.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),ue.trigger(t,Os,{relatedTarget:e})):t.classList.remove(Bs)}),t,t.classList.contains(Hs)))}_keydown(t){if(![$s,Is,Ns,Ps,Ms,js].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Bt(t)));let i;if([Ms,js].includes(t.key))i=e[t.key===Ms?0:e.length-1];else{const n=[Is,Ps].includes(t.key);i=Ut(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Ys.getOrCreateInstance(i).show())}_getChildren(){return ye.find(qs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=ye.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=ye.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ws,Fs),n(".dropdown-menu",Bs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(Fs)}_getInnerElement(t){return t.matches(qs)?t:ye.findOne(qs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Ys.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}ue.on(document,Ls,Rs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Bt(this)||Ys.getOrCreateInstance(this).show()})),ue.on(window,Ds,(()=>{for(const t of ye.find(Vs))Ys.getOrCreateInstance(t)})),Kt(Ys);const Ks=".bs.toast",Qs=`mouseover${Ks}`,Xs=`mouseout${Ks}`,Us=`focusin${Ks}`,Gs=`focusout${Ks}`,Js=`hide${Ks}`,Zs=`hidden${Ks}`,to=`show${Ks}`,eo=`shown${Ks}`,io="hide",no="show",so="showing",oo={animation:"boolean",autohide:"boolean",delay:"number"},ro={animation:!0,autohide:!0,delay:5e3};class ao extends be{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return ro}static get DefaultType(){return oo}static get NAME(){return"toast"}show(){ue.trigger(this._element,to).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(io),Rt(this._element),this._element.classList.add(no,so),this._queueCallback((()=>{this._element.classList.remove(so),ue.trigger(this._element,eo),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(ue.trigger(this._element,Js).defaultPrevented||(this._element.classList.add(so),this._queueCallback((()=>{this._element.classList.add(io),this._element.classList.remove(so,no),ue.trigger(this._element,Zs)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(no),super.dispose()}isShown(){return this._element.classList.contains(no)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){ue.on(this._element,Qs,(t=>this._onInteraction(t,!0))),ue.on(this._element,Xs,(t=>this._onInteraction(t,!1))),ue.on(this._element,Us,(t=>this._onInteraction(t,!0))),ue.on(this._element,Gs,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=ao.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function lo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}we(ao),Kt(ao),lo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new cs(t,{delay:{show:500,hide:100}})}))})),lo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),lo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))}))})(); -//# sourceMappingURL=bootstrap.js.map \ No newline at end of file diff --git a/_static/scripts/bootstrap.js.LICENSE.txt b/_static/scripts/bootstrap.js.LICENSE.txt deleted file mode 100644 index 10f979d07..000000000 --- a/_static/scripts/bootstrap.js.LICENSE.txt +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v5.3.2 (https://getbootstrap.com/) - * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ diff --git a/_static/scripts/bootstrap.js.map b/_static/scripts/bootstrap.js.map deleted file mode 100644 index e5bc15752..000000000 --- a/_static/scripts/bootstrap.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"scripts/bootstrap.js","mappings":"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","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.2 (https://getbootstrap.com/)\n * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.2';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? parseSelector(hrefAttribute.trim()) : null;\n }\n return selector;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\n\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\n\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both