Repository: ku-nlp/kwja (language: python)

Issue:
Add interactive mode



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 7df3e096..a22144da 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,17 +1,22 @@
 name: Lint
 
-on: [push, pull_request]
+on: [ push, pull_request ]
 
 jobs:
   lint:
     runs-on: ubuntu-latest
+    strategy:
+      max-parallel: 3
+      fail-fast: false
+      matrix:
+        python-version: [ "3.8", "3.9", "3.10" ]
     steps:
       - name: Checkout repository
         uses: actions/checkout@v3
-      - name: Set up Python 3.9
-        uses: actions/setup-python@v2.1.4
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v3
         with:
-          python-version: 3.9
+          python-version: ${{ matrix.python-version }}
       - name: Install pre-commit
         run: |
           python3 -m pip install --user pipx
diff --git a/README.md b/README.md
index e3bce97c..8e18f6f2 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ KWJA performs many language analysis tasks, including:
 
 ## Requirements
 
-- Python: 3.9+
+- Python: 3.8+
 - Dependencies: See [pyproject.toml](./pyproject.toml).
 
 ## Getting Started
diff --git a/configs/word_module.debug.yaml b/configs/word_module.debug.yaml
index d737811c..6d1600ae 100644
--- a/configs/word_module.debug.yaml
+++ b/configs/word_module.debug.yaml
@@ -14,15 +14,15 @@ defaults:
 do_predict_after_train: false
 checkpoint_path: ""
 document_split_stride: 1
-training_tasks: [
-  "word_analysis",
-  "ner",
-  "word_feature_tagging",
-  "base_phrase_feature_tagging",
-  "dependency_parsing",
-  "cohesion_analysis",
-  "discourse_parsing",
-]
+training_tasks:
+  - reading_prediction
+  - word_analysis
+  - ner
+  - word_feature_tagging
+  - base_phrase_feature_tagging
+  - dependency_parsing
+  - cohesion_analysis
+  - discourse_parsing
 
 # for dependency parser
 k: 4
@@ -40,16 +40,15 @@ restrict_cohesion_target: true
 # set monitor and mode for early_stopping and model_checkpoint
 monitor: valid/aggregated_word_metrics
 mode: max
-aggregating_metrics: [
-  "reading_prediction_accuracy",
-  "word_analysis_f1",
-  "ner_f1",
-  "macro_word_feature_f1",
-  "macro_base_phrase_feature_f1",
-  "base_phrase_LAS_f1",
-  "cohesion_analysis_f1",
-  "discourse_parsing_f1",
-]
+aggregating_metrics:
+  - reading_prediction_accuracy
+  - word_analysis_f1
+  - ner_f1
+  - macro_word_feature_f1
+  - macro_base_phrase_feature_f1
+  - base_phrase_LAS_f1
+  - cohesion_analysis_f1
+  - discourse_parsing_f1
 
 # hyper-parameters to be tuned
 warmup_steps: 100
diff --git a/configs/word_module.yaml b/configs/word_module.yaml
index 5f812c70..9f3e1801 100644
--- a/configs/word_module.yaml
+++ b/configs/word_module.yaml
@@ -14,16 +14,15 @@ defaults:
 do_predict_after_train: true
 checkpoint_path: ""
 document_split_stride: 1
-training_tasks: [
-  "reading_prediction",
-  "word_analysis",
-  "ner",
-  "word_feature_tagging",
-  "base_phrase_feature_tagging",
-  "dependency_parsing",
-  "cohesion_analysis",
-  "discourse_parsing",
-]
+training_tasks:
+  - reading_prediction
+  - word_analysis
+  - ner
+  - word_feature_tagging
+  - base_phrase_feature_tagging
+  - dependency_parsing
+  - cohesion_analysis
+  - discourse_parsing
 
 # for dependency parser
 k: 4
@@ -41,16 +40,15 @@ restrict_cohesion_target: true
 # set monitor and mode for early_stopping and model_checkpoint
 monitor: valid/aggregated_word_metrics
 mode: max
-aggregating_metrics: [
-  "reading_prediction_accuracy",
-  "word_analysis_f1",
-  "ner_f1",
-  "macro_word_feature_f1",
-  "macro_base_phrase_feature_f1",
-  "base_phrase_LAS_f1",
-  "cohesion_analysis_f1",
-  "discourse_parsing_f1",
-]
+aggregating_metrics:
+  - reading_prediction_accuracy
+  - word_analysis_f1
+  - ner_f1
+  - macro_word_feature_f1
+  - macro_base_phrase_feature_f1
+  - base_phrase_LAS_f1
+  - cohesion_analysis_f1
+  - discourse_parsing_f1
 
 # hyper-parameters to be tuned
 warmup_steps: 100
diff --git a/poetry.lock b/poetry.lock
index b3e65c7d..1f79bb0a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,7 +8,7 @@ python-versions = ">=3.6"
 
 [[package]]
 name = "aiohttp"
-version = "3.8.1"
+version = "3.8.3"
 description = "Async http client/server framework (asyncio)"
 category = "main"
 optional = false
@@ -83,12 +83,15 @@ tinydb = "*"
 
 [[package]]
 name = "blosc2"
-version = "0.3.2"
+version = "0.4.1"
 description = "Python wrapper for the C-Blosc2 library."
 category = "main"
 optional = false
 python-versions = ">=3.8, <4"
 
+[package.dependencies]
+msgpack = "*"
+
 [[package]]
 name = "cachetools"
 version = "5.2.0"
@@ -99,7 +102,7 @@ python-versions = "~=3.7"
 
 [[package]]
 name = "certifi"
-version = "2022.9.14"
+version = "2022.9.24"
 description = "Python package for providing Mozilla's CA Bundle."
 category = "main"
 optional = false
@@ -148,7 +151,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
 
 [[package]]
 name = "coverage"
-version = "6.4.4"
+version = "6.5.0"
 description = "Code coverage measurement for Python"
 category = "dev"
 optional = false
@@ -161,7 +164,7 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
 toml = ["tomli"]
 
 [[package]]
-name = "cython"
+name = "Cython"
 version = "0.29.32"
 description = "The Cython compiler for writing C extensions for the Python language."
 category = "main"
@@ -183,7 +186,7 @@ Cython = "*"
 name = "docker-pycreds"
 version = "0.4.0"
 description = "Python bindings for the docker credentials store API"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -249,7 +252,7 @@ tqdm = ["tqdm"]
 name = "gitdb"
 version = "4.0.9"
 description = "Git Object Database"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -257,10 +260,10 @@ python-versions = ">=3.6"
 smmap = ">=3.0.1,<6"
 
 [[package]]
-name = "gitpython"
-version = "3.1.27"
+name = "GitPython"
+version = "3.1.28"
 description = "GitPython is a python library used to interact with Git repositories"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -269,7 +272,7 @@ gitdb = ">=4.0.1,<5"
 
 [[package]]
 name = "google-auth"
-version = "2.11.0"
+version = "2.12.0"
 description = "Google Authentication Library"
 category = "main"
 optional = false
@@ -302,9 +305,17 @@ requests-oauthlib = ">=0.7.0"
 [package.extras]
 tool = ["click (>=6.0.0)"]
 
+[[package]]
+name = "gprof2dot"
+version = "2022.7.29"
+description = "Generate a dot graph from the output of several profilers."
+category = "dev"
+optional = false
+python-versions = ">=2.7"
+
 [[package]]
 name = "grpcio"
-version = "1.49.0"
+version = "1.49.1"
 description = "HTTP/2-based RPC framework"
 category = "main"
 optional = false
@@ -314,11 +325,11 @@ python-versions = ">=3.7"
 six = ">=1.5.2"
 
 [package.extras]
-protobuf = ["grpcio-tools (>=1.49.0)"]
+protobuf = ["grpcio-tools (>=1.49.1)"]
 
 [[package]]
 name = "huggingface-hub"
-version = "0.9.1"
+version = "0.10.0"
 description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
 category = "main"
 optional = false
@@ -333,12 +344,13 @@ tqdm = "*"
 typing-extensions = ">=3.7.4.3"
 
 [package.extras]
-all = ["black (==22.3)", "datasets", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "pytest", "pytest-cov", "soundfile"]
-dev = ["black (==22.3)", "datasets", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "pytest", "pytest-cov", "soundfile"]
+all = ["InquirerPy (==0.3.4)", "Jinja2", "black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "jedi", "mypy", "pytest", "pytest-cov", "soundfile"]
+cli = ["InquirerPy (==0.3.4)"]
+dev = ["InquirerPy (==0.3.4)", "Jinja2", "black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "jedi", "mypy", "pytest", "pytest-cov", "soundfile"]
 fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
-quality = ["black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)"]
+quality = ["black (==22.3)", "flake8 (>=3.8.3)", "flake8-bugbear", "isort (>=5.5.4)", "mypy"]
 tensorflow = ["graphviz", "pydot", "tensorflow"]
-testing = ["datasets", "pytest", "pytest-cov", "soundfile"]
+testing = ["InquirerPy (==0.3.4)", "Jinja2", "isort (>=5.5.4)", "jedi", "pytest", "pytest-cov", "soundfile"]
 torch = ["torch"]
 
 [[package]]
@@ -351,6 +363,7 @@ python-versions = "*"
 
 [package.dependencies]
 antlr4-python3-runtime = ">=4.9.0,<4.10.0"
+importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
 omegaconf = ">=2.2,<3.0"
 packaging = "*"
 
@@ -364,7 +377,7 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "importlib-metadata"
-version = "4.12.0"
+version = "5.0.0"
 description = "Read metadata from Python packages"
 category = "main"
 optional = false
@@ -374,9 +387,24 @@ python-versions = ">=3.7"
 zipp = ">=0.5"
 
 [package.extras]
-docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
 perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
+
+[[package]]
+name = "importlib-resources"
+version = "5.10.0"
+description = "Read resources from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
 
 [[package]]
 name = "iniconfig"
@@ -398,7 +426,7 @@ python-versions = "*"
 name = "jaconv"
 version = "0.3"
 description = "Pure-Python Japanese character interconverter for Hiragana, Katakana, Hankaku, Zenkaku and more"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -425,7 +453,7 @@ python-versions = ">=3.7"
 name = "kyoto-reader"
 version = "2.5.2"
 description = "A KWDLC and KyotoCorpus parser"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "!=3.7.0,!=3.7.1,>=3.7"
 
@@ -437,7 +465,7 @@ pyknp = "*"
 testing = ["pytest"]
 
 [[package]]
-name = "markdown"
+name = "Markdown"
 version = "3.4.1"
 description = "Python implementation of Markdown."
 category = "main"
@@ -451,13 +479,21 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
 testing = ["coverage", "pyyaml"]
 
 [[package]]
-name = "markupsafe"
+name = "MarkupSafe"
 version = "2.1.1"
 description = "Safely add untrusted strings to HTML/XML markup."
 category = "main"
 optional = false
 python-versions = ">=3.7"
 
+[[package]]
+name = "msgpack"
+version = "1.0.4"
+description = "MessagePack serializer"
+category = "main"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "multidict"
 version = "6.0.2"
@@ -468,11 +504,11 @@ python-versions = ">=3.7"
 
 [[package]]
 name = "mypy"
-version = "0.971"
+version = "0.982"
 description = "Optional static typing for Python"
 category = "main"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
 
 [package.dependencies]
 mypy-extensions = ">=0.4.3"
@@ -546,7 +582,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
 
 [[package]]
 name = "pandas"
-version = "1.4.4"
+version = "1.5.0"
 description = "Powerful data structures for data analysis, time series, and statistics"
 category = "main"
 optional = false
@@ -554,10 +590,8 @@ python-versions = ">=3.8"
 
 [package.dependencies]
 numpy = [
-    {version = ">=1.18.5", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""},
-    {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""},
-    {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""},
     {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
+    {version = ">=1.20.3", markers = "python_version < \"3.10\""},
 ]
 python-dateutil = ">=2.8.1"
 pytz = ">=2020.1"
@@ -569,7 +603,7 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
 name = "pathtools"
 version = "0.1.2"
 description = "File system general utilities"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -589,7 +623,7 @@ testing = ["pytest", "pytest-benchmark"]
 name = "promise"
 version = "2.3"
 description = "Promises/A+ implementation for Python"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -601,17 +635,17 @@ test = ["coveralls", "futures", "mock", "pytest (>=2.7.3)", "pytest-benchmark",
 
 [[package]]
 name = "protobuf"
-version = "3.20.1"
+version = "3.19.6"
 description = "Protocol Buffers"
 category = "main"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.5"
 
 [[package]]
 name = "psutil"
 version = "5.9.2"
 description = "Cross-platform lib for process and system monitoring in Python."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
@@ -646,7 +680,7 @@ python-versions = "*"
 pyasn1 = ">=0.4.6,<0.5.0"
 
 [[package]]
-name = "pydeprecate"
+name = "pyDeprecate"
 version = "0.3.2"
 description = "Deprecation tooling"
 category = "main"
@@ -654,7 +688,7 @@ optional = false
 python-versions = ">=3.6"
 
 [[package]]
-name = "pygments"
+name = "Pygments"
 version = "2.13.0"
 description = "Pygments is a syntax highlighting package written in Python."
 category = "main"
@@ -668,7 +702,7 @@ plugins = ["importlib-metadata"]
 name = "pyknp"
 version = "0.6.1"
 description = "Python module for JUMAN/KNP"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7,<4.0"
 
@@ -708,7 +742,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.
 
 [[package]]
 name = "pytest-cov"
-version = "3.0.0"
+version = "4.0.0"
 description = "Pytest plugin for measuring coverage."
 category = "dev"
 optional = false
@@ -721,6 +755,22 @@ pytest = ">=4.6"
 [package.extras]
 testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
 
+[[package]]
+name = "pytest-profiling"
+version = "1.7.0"
+description = "Profiling plugin for py.test"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+gprof2dot = "*"
+pytest = "*"
+six = "*"
+
+[package.extras]
+tests = ["pytest-virtualenv"]
+
 [[package]]
 name = "python-dateutil"
 version = "2.8.2"
@@ -734,17 +784,17 @@ six = ">=1.5"
 
 [[package]]
 name = "python-dotenv"
-version = "0.20.0"
+version = "0.21.0"
 description = "Read key-value pairs from a .env file and set them as environment variables"
 category = "main"
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.7"
 
 [package.extras]
 cli = ["click (>=5.0)"]
 
 [[package]]
-name = "python-levenshtein"
+name = "python-Levenshtein"
 version = "0.12.2"
 description = "Python extension for computing string edit distances and similarities."
 category = "main"
@@ -756,7 +806,7 @@ setuptools = "*"
 
 [[package]]
 name = "pytorch-lightning"
-version = "1.6.5"
+version = "1.7.7"
 description = "PyTorch Lightning is the lightweight PyTorch wrapper for ML researchers. Scale your models. Write less boilerplate."
 category = "main"
 optional = false
@@ -766,38 +816,37 @@ python-versions = ">=3.7"
 fsspec = {version = ">=2021.05.0,<2021.06.0 || >2021.06.0", extras = ["http"]}
 numpy = ">=1.17.2"
 packaging = ">=17.0"
-protobuf = "<=3.20.1"
 pyDeprecate = ">=0.3.1"
 PyYAML = ">=5.4"
-tensorboard = ">=2.2.0"
-torch = ">=1.8"
-torchmetrics = ">=0.4.1"
+tensorboard = ">=2.9.1"
+torch = ">=1.9"
+torchmetrics = ">=0.7.0"
 tqdm = ">=4.57.0"
 typing-extensions = ">=4.0.0"
 
 [package.extras]
-all = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "deepspeed", "fairscale (>=0.4.5)", "flake8 (>=3.9.2)", "gcsfs (>=2021.5.0)", "gym[classic_control] (>=0.17.0)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)", "hydra-core (>=1.0.5)", "ipython[all]", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (>=0.920)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.2.2,!=10.15.0.a)", "scikit-learn (>0.22.1)", "test-tube (>=0.7.5)", "torchtext (>=0.9)", "torchvision (>=0.9)", "wandb (>=0.8.21)"]
-deepspeed = ["deepspeed"]
-dev = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "flake8 (>=3.9.2)", "gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (>=0.920)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.2.2,!=10.15.0.a)", "scikit-learn (>0.22.1)", "test-tube (>=0.7.5)", "torchtext (>=0.9)", "wandb (>=0.8.21)"]
-examples = ["gym[classic_control] (>=0.17.0)", "ipython[all]", "torchvision (>=0.9)"]
-extra = ["gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.7.1)", "matplotlib (>3.1)", "omegaconf (>=2.0.5)", "rich (>=10.2.2,!=10.15.0.a)", "torchtext (>=0.9)"]
+all = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "deepspeed (>=0.6.0)", "fairscale (>=0.4.5)", "fastapi", "gcsfs (>=2021.5.0)", "gym[classic_control] (>=0.17.0)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)", "hydra-core (>=1.0.5)", "ipython[all]", "jsonargparse[signatures] (>=4.12.0)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (==0.971)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas (>1.0)", "pre-commit (>=1.0)", "protobuf (<=3.20.1)", "psutil", "pytest (>=7.0)", "pytest-cov", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.14.0,!=10.15.0.a)", "scikit-learn (>0.22.1)", "torchtext (>=0.10)", "torchvision (>=0.10)", "uvicorn", "wandb (>=0.10.22)"]
+deepspeed = ["deepspeed (>=0.6.0)"]
+dev = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "comet-ml (>=3.1.12)", "coverage (>=6.4)", "fastapi", "gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.12.0)", "matplotlib (>3.1)", "mlflow (>=1.0.0)", "mypy (==0.971)", "neptune-client (>=0.10.0)", "omegaconf (>=2.0.5)", "onnxruntime", "pandas (>1.0)", "pre-commit (>=1.0)", "protobuf (<=3.20.1)", "psutil", "pytest (>=7.0)", "pytest-cov", "pytest-forked", "pytest-rerunfailures (>=10.2)", "rich (>=10.14.0,!=10.15.0.a)", "scikit-learn (>0.22.1)", "torchtext (>=0.10)", "uvicorn", "wandb (>=0.10.22)"]
+examples = ["gym[classic_control] (>=0.17.0)", "ipython[all]", "torchvision (>=0.10)"]
+extra = ["gcsfs (>=2021.5.0)", "hydra-core (>=1.0.5)", "jsonargparse[signatures] (>=4.12.0)", "matplotlib (>3.1)", "omegaconf (>=2.0.5)", "protobuf (<=3.20.1)", "rich (>=10.14.0,!=10.15.0.a)", "torchtext (>=0.10)"]
 fairscale = ["fairscale (>=0.4.5)"]
 hivemind = ["hivemind (>=1.0.1)"]
 horovod = ["horovod (>=0.21.2,!=0.24.0)"]
-loggers = ["comet-ml (>=3.1.12)", "mlflow (>=1.0.0)", "neptune-client (>=0.10.0)", "test-tube (>=0.7.5)", "wandb (>=0.8.21)"]
-strategies = ["deepspeed", "fairscale (>=0.4.5)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)"]
-test = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "coverage (>=6.4)", "flake8 (>=3.9.2)", "mypy (>=0.920)", "onnxruntime", "pandas", "pre-commit (>=1.0)", "pytest (>=6.0)", "pytest-forked", "pytest-rerunfailures (>=10.2)", "scikit-learn (>0.22.1)"]
+loggers = ["comet-ml (>=3.1.12)", "mlflow (>=1.0.0)", "neptune-client (>=0.10.0)", "wandb (>=0.10.22)"]
+strategies = ["deepspeed (>=0.6.0)", "fairscale (>=0.4.5)", "hivemind (>=1.0.1)", "horovod (>=0.21.2,!=0.24.0)"]
+test = ["cloudpickle (>=1.3)", "codecov (>=2.1)", "coverage (>=6.4)", "fastapi", "mypy (==0.971)", "onnxruntime", "pandas (>1.0)", "pre-commit (>=1.0)", "psutil", "pytest (>=7.0)", "pytest-cov", "pytest-forked", "pytest-rerunfailures (>=10.2)", "scikit-learn (>0.22.1)", "uvicorn"]
 
 [[package]]
 name = "pytz"
-version = "2022.2.1"
+version = "2022.4"
 description = "World timezone definitions, modern and historical"
 category = "main"
 optional = false
 python-versions = "*"
 
 [[package]]
-name = "pyyaml"
+name = "PyYAML"
 version = "6.0"
 description = "YAML parser and emitter for Python"
 category = "main"
@@ -847,7 +896,7 @@ rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
 
 [[package]]
 name = "rhoknp"
-version = "0.4.0"
+version = "0.4.4"
 description = "Yet another Python binding for Juman++/KNP"
 category = "main"
 optional = false
@@ -859,7 +908,7 @@ typer = ">=0.6.1,<0.7.0"
 
 [[package]]
 name = "rich"
-version = "12.5.1"
+version = "12.6.0"
 description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
 category = "main"
 optional = false
@@ -868,6 +917,7 @@ python-versions = ">=3.6.3,<4.0.0"
 [package.dependencies]
 commonmark = ">=0.9.0,<0.10.0"
 pygments = ">=2.6.0,<3.0.0"
+typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
 
 [package.extras]
 jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"]
@@ -905,14 +955,19 @@ tests = ["black (>=22.3.0)", "flake8 (>=3.8.2)", "matplotlib (>=3.1.2)", "mypy (
 
 [[package]]
 name = "scipy"
-version = "1.9.1"
-description = "SciPy: Scientific Library for Python"
+version = "1.9.2"
+description = "Fundamental algorithms for scientific computing in Python"
 category = "main"
 optional = false
-python-versions = ">=3.8,<3.12"
+python-versions = ">=3.8"
 
 [package.dependencies]
-numpy = ">=1.18.5,<1.25.0"
+numpy = ">=1.18.5,<1.26.0"
+
+[package.extras]
+dev = ["flake8", "mypy", "pycodestyle", "typing_extensions"]
+doc = ["matplotlib (>2)", "numpydoc", "pydata-sphinx-theme (==0.9.0)", "sphinx (!=4.1.0)", "sphinx-panels (>=0.5.2)", "sphinx-tabs"]
+test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
 
 [[package]]
 name = "sentencepiece"
@@ -924,9 +979,9 @@ python-versions = "*"
 
 [[package]]
 name = "sentry-sdk"
-version = "1.9.8"
+version = "1.9.10"
 description = "Python client for Sentry (https://sentry.io)"
-category = "main"
+category = "dev"
 optional = false
 python-versions = "*"
 
@@ -970,7 +1025,7 @@ scikit-learn = ">=0.21.3"
 name = "setproctitle"
 version = "1.3.2"
 description = "A Python module to customize the process title"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.7"
 
@@ -979,14 +1034,14 @@ test = ["pytest"]
 
 [[package]]
 name = "setuptools"
-version = "65.3.0"
+version = "65.4.1"
 description = "Easily download, build, install, upgrade, and uninstall Python packages"
 category = "main"
 optional = false
 python-versions = ">=3.7"
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
 testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
 testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
 
@@ -994,7 +1049,7 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
 name = "shortuuid"
 version = "1.0.9"
 description = "A generator library for concise, unambiguous and URL-safe UUIDs."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.5"
 
@@ -1010,13 +1065,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
 name = "smmap"
 version = "5.0.0"
 description = "A pure Python implementation of a sliding window memory map manager"
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
 [[package]]
 name = "tensorboard"
-version = "2.9.0"
+version = "2.10.1"
 description = "TensorBoard lets you watch Tensors Flow"
 category = "main"
 optional = false
@@ -1029,7 +1084,7 @@ google-auth-oauthlib = ">=0.4.1,<0.5"
 grpcio = ">=1.24.3"
 markdown = ">=2.6.8"
 numpy = ">=1.12.0"
-protobuf = ">=3.9.2"
+protobuf = ">=3.9.2,<3.20"
 requests = ">=2.21.0,<3"
 setuptools = ">=41.0.0"
 tensorboard-data-server = ">=0.6.0,<0.7.0"
@@ -1219,7 +1274,7 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.
 
 [[package]]
 name = "typing-extensions"
-version = "4.3.0"
+version = "4.4.0"
 description = "Backported and Experimental Type Hints for Python 3.7+"
 category = "main"
 optional = false
@@ -1240,9 +1295,9 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
 
 [[package]]
 name = "wandb"
-version = "0.13.3"
+version = "0.13.4"
 description = "A CLI and library for interacting with the Weights and Biases API."
-category = "main"
+category = "dev"
 optional = false
 python-versions = ">=3.6"
 
@@ -1252,7 +1307,7 @@ docker-pycreds = ">=0.4.0"
 GitPython = ">=1.0.0"
 pathtools = "*"
 promise = ">=2.0,<3"
-protobuf = ">=3.12.0,<4.0dev"
+protobuf = ">=3.12.0,<4.0.0 || >=4.1.0,<4.21.0 || >4.21.0,<5"
 psutil = ">=5.0.0"
 PyYAML = "*"
 requests = ">=2.0.0,<3"
@@ -1274,7 +1329,7 @@ models = ["cloudpickle"]
 sweeps = ["sweeps (>=0.2.0)"]
 
 [[package]]
-name = "werkzeug"
+name = "Werkzeug"
 version = "2.2.2"
 description = "The comprehensive WSGI web application library."
 category = "main"
@@ -1312,20 +1367,20 @@ multidict = ">=4.0"
 
 [[package]]
 name = "zipp"
-version = "3.8.1"
+version = "3.9.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 category = "main"
 optional = false
 python-versions = ">=3.7"
 
 [package.extras]
-docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
-testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
+testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
 
 [metadata]
 lock-version = "1.1"
-python-versions = ">=3.9,<3.11"
-content-hash = "3eab804fa3051b6973ceba8403699c46cd7eb4ddb0bdf1e52ce96618091ac208"
+python-versions = ">=3.8,<3.11"
+content-hash = "29c5a092f03169f9c4af588652c2acddf5069dd2b5bc1d4945e0612c3363f193"
 
 [metadata.files]
 absl-py = [
@@ -1333,78 +1388,93 @@ absl-py = [
     {file = "absl_py-1.2.0-py3-none-any.whl", hash = "sha256:5d15f85b8cc859c6245bc9886ba664460ed96a6fee895416caa37d669ee74a9a"},
 ]
 aiohttp = [
-    {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
-    {file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
-    {file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
-    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
-    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
-    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
-    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
-    {file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
-    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
-    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
-    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
-    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
-    {file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
-    {file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
-    {file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
-    {file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
-    {file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
-    {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
-    {file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
-    {file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
-    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
-    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
-    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
-    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
-    {file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
-    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
-    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
-    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
-    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
-    {file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
-    {file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
-    {file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
-    {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
-    {file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
-    {file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
-    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
-    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
-    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
-    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
-    {file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
-    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
-    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
-    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
-    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
-    {file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
-    {file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
-    {file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
-    {file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
+    {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9"},
+    {file = "aiohttp-3.8.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e"},
+    {file = "aiohttp-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491"},
+    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62"},
+    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d"},
+    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f"},
+    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b"},
+    {file = "aiohttp-3.8.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18"},
+    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5"},
+    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d"},
+    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7"},
+    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142"},
+    {file = "aiohttp-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b"},
+    {file = "aiohttp-3.8.3-cp310-cp310-win32.whl", hash = "sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb"},
+    {file = "aiohttp-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715"},
+    {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008"},
+    {file = "aiohttp-3.8.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d"},
+    {file = "aiohttp-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476"},
+    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c"},
+    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061"},
+    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8"},
+    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d"},
+    {file = "aiohttp-3.8.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2"},
+    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276"},
+    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d"},
+    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091"},
+    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73"},
+    {file = "aiohttp-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34"},
+    {file = "aiohttp-3.8.3-cp311-cp311-win32.whl", hash = "sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697"},
+    {file = "aiohttp-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-win32.whl", hash = "sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b"},
+    {file = "aiohttp-3.8.3-cp36-cp36m-win_amd64.whl", hash = "sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-win32.whl", hash = "sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033"},
+    {file = "aiohttp-3.8.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091"},
+    {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb"},
+    {file = "aiohttp-3.8.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4"},
+    {file = "aiohttp-3.8.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784"},
+    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c"},
+    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849"},
+    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b"},
+    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342"},
+    {file = "aiohttp-3.8.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6"},
+    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96"},
+    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017"},
+    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf"},
+    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6"},
+    {file = "aiohttp-3.8.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937"},
+    {file = "aiohttp-3.8.3-cp38-cp38-win32.whl", hash = "sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76"},
+    {file = "aiohttp-3.8.3-cp38-cp38-win_amd64.whl", hash = "sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446"},
+    {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06"},
+    {file = "aiohttp-3.8.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba"},
+    {file = "aiohttp-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346"},
+    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b"},
+    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7"},
+    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37"},
+    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa"},
+    {file = "aiohttp-3.8.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb"},
+    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8"},
+    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad"},
+    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4"},
+    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c"},
+    {file = "aiohttp-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403"},
+    {file = "aiohttp-3.8.3-cp39-cp39-win32.whl", hash = "sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618"},
+    {file = "aiohttp-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7"},
+    {file = "aiohttp-3.8.3.tar.gz", hash = "sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269"},
 ]
 aiosignal = [
     {file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
@@ -1426,30 +1496,30 @@ BetterJSONStorage = [
     {file = "BetterJSONStorage-1.3.0.tar.gz", hash = "sha256:b81e12bb26c762edcbccd8bf12f6f3f8aa56534dceb646809e7038ff66310860"},
 ]
 blosc2 = [
-    {file = "blosc2-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5b892642dc2d1a31e02b2bd5334835b9ddab24d8b310dc7c84b4676489c2bb5"},
-    {file = "blosc2-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33423af150a529f936b0c48781b6787f118db5ec788e4c3b6b2b7b26ea1a0305"},
-    {file = "blosc2-0.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c6b6e4c3dac7907150f5208f762ac36ba161c1705361b7172ee25897899f84f9"},
-    {file = "blosc2-0.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1337f87fadce2810f3ca6a74d705429077406c3f40e7dcb1bcac78f9a748efe"},
-    {file = "blosc2-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:e97d67b905abe6b2b5e6f6091d05c00016d5fe89b608efca21f2fb997700e432"},
-    {file = "blosc2-0.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b743a784f7fd39f047cb9c108fcfb3ca4b4e342f6906bdf34a6fe8214c3cc554"},
-    {file = "blosc2-0.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88092b6cd8325604dbe3bb97190f6cb5d10d28b0ca323bd69347bcfd6a8604dc"},
-    {file = "blosc2-0.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4a721f13388ec7962913de1243552e0df3ad507e3df6a841410f1b24a4c8f600"},
-    {file = "blosc2-0.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bbbdc750d1f77846fe6a93497c38bd469ce3a98ff2d96608f390c9b0f473bd74"},
-    {file = "blosc2-0.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:8bbc0b75ab6b9c5200daeda1a7f2e403ccf47b85b7cb3e70495e0160d1f8d25c"},
-    {file = "blosc2-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5953614cf2acdee5e44195c51396a874b7e564f5960a3b4f71dc010beabd05c5"},
-    {file = "blosc2-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27c2a3f735bc5546ffb06f781060122e8eda227e30dff13536b00bbab9d0030f"},
-    {file = "blosc2-0.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:894f230eee8ab6323a8227885d934d865890ac6470a6846d8c934323e695bd00"},
-    {file = "blosc2-0.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e9d5ec16f828d4bd0887b3058027b70c0d0490109d78d4b829f8c7ed24248ad"},
-    {file = "blosc2-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:3e66b932028d89ddd43bd6f2815cd81e6c4702a5b601b70beea6ad9c80b70661"},
-    {file = "blosc2-0.3.2.tar.gz", hash = "sha256:99679a330506f85dafe71080a96483ddaf091fabb84f51ed673b8fd860aa34bd"},
+    {file = "blosc2-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47c82c59215b172567a222d7f75c2516253d41258ef4b20ef5071d95d79d714d"},
+    {file = "blosc2-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a9d01da0631d3ef9113eff6985c7f24ac92ff95e99c69f0292906f53a1a81a2"},
+    {file = "blosc2-0.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74327e071624dab73f18cabc4561752a98cfa36c56389af9b608b3b3cb00bb"},
+    {file = "blosc2-0.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97acc796a38fcc3edf9b4fcf7098a5a6616dd8c74b64ccacae0850a2df2350f4"},
+    {file = "blosc2-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:6ae7ca12af92e501d62a2e9e80c472754cdec0e0f8e8e5a2b0da1291299b4076"},
+    {file = "blosc2-0.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6b48b8e62726db0fad3bce7f17391dca3a89489aa7819df69c2acfb9841cc0a6"},
+    {file = "blosc2-0.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:265affcc25db852bec700783fb02d62b6b4f0507e6dbebd0ee00741dbf30cc63"},
+    {file = "blosc2-0.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ff78988cf26ac7428f2881689b8d248efc7fa3da333423efd242d08cad7c03d"},
+    {file = "blosc2-0.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bbdead31f04436f536bfd86287049307554ff31984f9387c405b4b49c3776387"},
+    {file = "blosc2-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a88d9c810ea6247c021b9c82142d540d95903c28ad2d5b4762d2dbff6fb76ac"},
+    {file = "blosc2-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0adb7eef0b649752abc94a9744decb31b2f2767768068086fd2bd9ab566b7ca"},
+    {file = "blosc2-0.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8672b15d3f964ade764f0dd88affafd7890f62cdf29d3b154aac498418331382"},
+    {file = "blosc2-0.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f956020f9a5dcdd123093c51be2334767f07efe65b23c23d121b950f7a027de"},
+    {file = "blosc2-0.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a94043f104ba29831a77c7d3aa96c28f990bc9be2ce86c550755cdde2356eb9"},
+    {file = "blosc2-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:f6da6d2bff621b96552c2379ffb89c8479f7170d46e4431bc9ab28f175beefe4"},
+    {file = "blosc2-0.4.1.tar.gz", hash = "sha256:ba190aa5570a9d85384686849ab3fb330e2faf1e66c7bdcac411668c4cba11e3"},
 ]
 cachetools = [
     {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"},
     {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"},
 ]
 certifi = [
-    {file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"},
-    {file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"},
+    {file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
+    {file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
 ]
 charset-normalizer = [
     {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
@@ -1468,58 +1538,58 @@ commonmark = [
     {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
 ]
 coverage = [
-    {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"},
-    {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"},
-    {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"},
-    {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"},
-    {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"},
-    {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"},
-    {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"},
-    {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"},
-    {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"},
-    {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"},
-    {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"},
-    {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"},
-    {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"},
-    {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"},
-    {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"},
-    {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"},
-    {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"},
-    {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"},
-    {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"},
-    {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"},
-    {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"},
-    {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"},
-    {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"},
-    {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"},
-    {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"},
-    {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"},
-    {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"},
-    {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"},
-    {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"},
-    {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"},
-    {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"},
-    {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"},
-    {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"},
-    {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"},
-    {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"},
-    {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"},
-    {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"},
-    {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"},
-    {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"},
-    {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"},
-    {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"},
-    {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"},
-    {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"},
-    {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"},
-    {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"},
-    {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"},
-    {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"},
-    {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"},
-    {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"},
-    {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"},
-]
-cython = [
+    {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
+    {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
+    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
+    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
+    {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
+    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
+    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
+    {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
+    {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
+    {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
+    {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
+    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
+    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
+    {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
+    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
+    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
+    {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
+    {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
+    {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
+    {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
+    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
+    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
+    {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
+    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
+    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
+    {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
+    {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
+    {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
+    {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
+    {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
+    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
+    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
+    {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
+    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
+    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
+    {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
+    {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
+    {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
+    {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
+    {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
+    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
+    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
+    {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
+    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
+    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
+    {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
+    {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
+    {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
+    {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
+    {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
+]
+Cython = [
     {file = "Cython-0.29.32-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:39afb4679b8c6bf7ccb15b24025568f4f9b4d7f9bf3cbd981021f542acecd75b"},
     {file = "Cython-0.29.32-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dbee03b8d42dca924e6aa057b836a064c769ddfd2a4c2919e65da2c8a362d528"},
     {file = "Cython-0.29.32-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ba622326f2862f9c1f99ca8d47ade49871241920a352c917e16861e25b0e5c3"},
@@ -1662,68 +1732,72 @@ gitdb = [
     {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
     {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
 ]
-gitpython = [
-    {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"},
-    {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
+GitPython = [
+    {file = "GitPython-3.1.28-py3-none-any.whl", hash = "sha256:77bfbd299d8709f6af7e0c70840ef26e7aff7cf0c1ed53b42dd7fc3a310fcb02"},
+    {file = "GitPython-3.1.28.tar.gz", hash = "sha256:6bd3451b8271132f099ceeaf581392eaf6c274af74bb06144307870479d0697c"},
 ]
 google-auth = [
-    {file = "google-auth-2.11.0.tar.gz", hash = "sha256:ed65ecf9f681832298e29328e1ef0a3676e3732b2e56f41532d45f70a22de0fb"},
-    {file = "google_auth-2.11.0-py2.py3-none-any.whl", hash = "sha256:be62acaae38d0049c21ca90f27a23847245c9f161ff54ede13af2cb6afecbac9"},
+    {file = "google-auth-2.12.0.tar.gz", hash = "sha256:f12d86502ce0f2c0174e2e70ecc8d36c69593817e67e1d9c5e34489120422e4b"},
+    {file = "google_auth-2.12.0-py2.py3-none-any.whl", hash = "sha256:98f601773978c969e1769f97265e732a81a8e598da3263895023958d456ee625"},
 ]
 google-auth-oauthlib = [
     {file = "google-auth-oauthlib-0.4.6.tar.gz", hash = "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a"},
     {file = "google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73"},
 ]
+gprof2dot = [
+    {file = "gprof2dot-2022.7.29-py2.py3-none-any.whl", hash = "sha256:f165b3851d3c52ee4915eb1bd6cca571e5759823c2cd0f71a79bda93c2dc85d6"},
+    {file = "gprof2dot-2022.7.29.tar.gz", hash = "sha256:45b4d298bd36608fccf9511c3fd88a773f7a1abc04d6cd39445b11ba43133ec5"},
+]
 grpcio = [
-    {file = "grpcio-1.49.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:79298b2b153d00f34ecbf5db8ec1be9bf37172ff9d2b63a2e0c6ef67f85daf1e"},
-    {file = "grpcio-1.49.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ebb539e0cf618593dc08ea9bf6fc72c538b632663f8efc1a344f6db62545dfaa"},
-    {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7ea88f3dfe2606a93193e8dc240659b5eedcee77ef90d655cbc792273a5c3eb2"},
-    {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a821831518758b725ba857d791a43959e8ffdbe5935e89a50222496fe2ed0f9c"},
-    {file = "grpcio-1.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91763e949104894b639ad47243c69988fc0bbdcbead995280f7d3fcba3824a79"},
-    {file = "grpcio-1.49.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ad119d3db3a284e8f0dbf0149b904280e17a13c9eca38a250109cbee9d569a1d"},
-    {file = "grpcio-1.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e26d4b147447cf63e7e20e92c7b938b6279c0cd463b421f0f6ecc2d991161614"},
-    {file = "grpcio-1.49.0-cp310-cp310-win32.whl", hash = "sha256:172cfa96c6d4de9d84bbd00ffea7aebe820f44430d31cdfea6ec6d209007c117"},
-    {file = "grpcio-1.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:07bbbb2675d5bcd5ea0dd5dc39d47d8cd9f81e12d5689d79079f5eb9bad87127"},
-    {file = "grpcio-1.49.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:a3eccffe9cac99883cbfdcaac0af3d346d5499a7fcb7d57fdd2ec21209471c39"},
-    {file = "grpcio-1.49.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:77742b6805c2b46a59b76f924b3018de89c4e3b565df7ef2d79d4d8eecd37a20"},
-    {file = "grpcio-1.49.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:875cb37144865cc4d26e91add8c9672cf2c95f3547f15ae3daba8f27064fdb30"},
-    {file = "grpcio-1.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1532064b23130be7760c940b82961e3661cb785f686204a937e8edca007069fe"},
-    {file = "grpcio-1.49.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3477cec9b89334ddbb09b44cf099e9a81f4090b48fdd63310f91b71a762896d6"},
-    {file = "grpcio-1.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb4a3812e90d8189ff96b1779c7e7e56d13e7525ef4928b7d897c62c0d8936e3"},
-    {file = "grpcio-1.49.0-cp311-cp311-win32.whl", hash = "sha256:a9e154ff1d55540db0ff9ee123687c45e7946a235d22a44c5347b2384b2a4753"},
-    {file = "grpcio-1.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:894fba84ef949c7f2403eaf09c9d2421eeeec447f4d61829353a371d4d1edb57"},
-    {file = "grpcio-1.49.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:fcd45c259cabb739b8b0c3a539ad2796f627fef77e53e55e6c2b5389b59a00ff"},
-    {file = "grpcio-1.49.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:4ae542364be7b2cb13d8372c2a601a84c77a93327c93a31cda64fd9c5c333448"},
-    {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3e7b20e78b8dcee038002fd42ae13d5e6e81efa7d21cc6682a25f4cbd18f0491"},
-    {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82a40b2fea553a85d045b1934633d108643d3b870a08fa19d47137d967e8e716"},
-    {file = "grpcio-1.49.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85150bce2438feb2a814e0c2dec09fb6e86a58b69a06279a1a49426b88b84176"},
-    {file = "grpcio-1.49.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:db056a709ea38b2da97a6811ed6719329f6576a32240e102e897a05b681a1674"},
-    {file = "grpcio-1.49.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:41d8e725b816964691650967b017a621d935ba7393a153fcf8d6dcae5e41e4b3"},
-    {file = "grpcio-1.49.0-cp37-cp37m-win32.whl", hash = "sha256:6ea56034245fe04ca3c411d2bf7032782722973e4cd2964e89d55110d2142bac"},
-    {file = "grpcio-1.49.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bc39e9bd568b210ba9e44819736eb5e95cada054381dec5a3d2ec0a5f7c0178f"},
-    {file = "grpcio-1.49.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:8d6f4ace5b41502b11c2456d2fa3a2d7ee776bbb7d2c3c05e0309d420654c95a"},
-    {file = "grpcio-1.49.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:9736f24b9a0a5b66df739fa2e82132c1c420cc470df003e53596dcec8d90a52a"},
-    {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a0c39a3d81a727d36c855781886a66d868681e5dc8de76a766a7c3c4827f649b"},
-    {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63c3823fc525882989c0a42191d1efeb37c4d4f4c1dc555cc21d721fe11570e3"},
-    {file = "grpcio-1.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0522e5c678411977db57bb6a4c6dd29e1defc0961ebf2c0a29871459cce0fd9"},
-    {file = "grpcio-1.49.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3cc65127b0755fc0cf4f7e095f2d0ed25fe225eb1525b76c0a32de3efaf7c22f"},
-    {file = "grpcio-1.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:717ed04e949022e33e10c4d11b2a9567da2de5a02019535058482ea54d9f795c"},
-    {file = "grpcio-1.49.0-cp38-cp38-win32.whl", hash = "sha256:dcb480c4141dd051120a3de9af555ef09fc3ce1d0cb47e95b22ac1406554af18"},
-    {file = "grpcio-1.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:faa91b060bf3c520d0a4f11bd550d1b4026d649f188b082690270182fb462ec4"},
-    {file = "grpcio-1.49.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:5df1748ac9d679f47ee39226a7389f73e8c7c49910c7493b0f61735825a0b5ec"},
-    {file = "grpcio-1.49.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:4f70df4d99945a7dc4a358cf0d2c027bba2d3125bc8ad1acb069480747fc58de"},
-    {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:6cbcbbab1c8f677d6b3bb55b22bfce87bd10713a8ef439098f86ada96e69eda8"},
-    {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b40ca1a353ad0410f979d3ac7fdb07e2ad65717411c0c3e2df12a4b811898637"},
-    {file = "grpcio-1.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede5041e8a98ce8761d7b6d966da872ac6a5bf2d8de776b402ea509c8ba8a8bc"},
-    {file = "grpcio-1.49.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:49a8a3aa2d61f1940983795cea78d336f86ab2e5c92029adfc7e6a8ee0fe6e3a"},
-    {file = "grpcio-1.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b3bc29e95a578b5c5a1bf2908ef56f45ad8b669178298cf6a8357a6883baff99"},
-    {file = "grpcio-1.49.0-cp39-cp39-win32.whl", hash = "sha256:60d31d1377429f0f83fe10887276e8bdef16fb297dfbccab896a145d7b171bc8"},
-    {file = "grpcio-1.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:4e0374c5b31fdf5cd4a2f3209e5341762a34c80acedbaca0896ccb904ecdd707"},
-    {file = "grpcio-1.49.0.tar.gz", hash = "sha256:90ec337f36db26fbc70a4c032bcbabb2008f950f4194e99385118a12688fdf92"},
+    {file = "grpcio-1.49.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:fd86040232e805b8e6378b2348c928490ee595b058ce9aaa27ed8e4b0f172b20"},
+    {file = "grpcio-1.49.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6fd0c9cede9552bf00f8c5791d257d5bf3790d7057b26c59df08be5e7a1e021d"},
+    {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d0d402e158d4e84e49c158cb5204119d55e1baf363ee98d6cb5dce321c3a065d"},
+    {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ceec743d42a627e64ea266059a62d214c5a3cdfcd0d7fe2b7a8e4e82527c7"},
+    {file = "grpcio-1.49.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2106d9c16527f0a85e2eea6e6b91a74fc99579c60dd810d8690843ea02bc0f5f"},
+    {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:52dd02b7e7868233c571b49bc38ebd347c3bb1ff8907bb0cb74cb5f00c790afc"},
+    {file = "grpcio-1.49.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:120fecba2ec5d14b5a15d11063b39783fda8dc8d24addd83196acb6582cabd9b"},
+    {file = "grpcio-1.49.1-cp310-cp310-win32.whl", hash = "sha256:f1a3b88e3c53c1a6e6bed635ec1bbb92201bb6a1f2db186179f7f3f244829788"},
+    {file = "grpcio-1.49.1-cp310-cp310-win_amd64.whl", hash = "sha256:a7d0017b92d3850abea87c1bdec6ea41104e71c77bca44c3e17f175c6700af62"},
+    {file = "grpcio-1.49.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9fb17ff8c0d56099ac6ebfa84f670c5a62228d6b5c695cf21c02160c2ac1446b"},
+    {file = "grpcio-1.49.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:075f2d06e3db6b48a2157a1bcd52d6cbdca980dd18988fe6afdb41795d51625f"},
+    {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d93a1b4572b461a227f1db6b8d35a88952db1c47e5fadcf8b8a2f0e1dd9201"},
+    {file = "grpcio-1.49.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc79b2b37d779ac42341ddef40ad5bf0966a64af412c89fc2b062e3ddabb093f"},
+    {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5f8b3a971c7820ea9878f3fd70086240a36aeee15d1b7e9ecbc2743b0e785568"},
+    {file = "grpcio-1.49.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49b301740cf5bc8fed4fee4c877570189ae3951432d79fa8e524b09353659811"},
+    {file = "grpcio-1.49.1-cp311-cp311-win32.whl", hash = "sha256:1c66a25afc6c71d357867b341da594a5587db5849b48f4b7d5908d236bb62ede"},
+    {file = "grpcio-1.49.1-cp311-cp311-win_amd64.whl", hash = "sha256:6b6c3a95d27846f4145d6967899b3ab25fffc6ae99544415e1adcacef84842d2"},
+    {file = "grpcio-1.49.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:1cc400c8a2173d1c042997d98a9563e12d9bb3fb6ad36b7f355bc77c7663b8af"},
+    {file = "grpcio-1.49.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:34f736bd4d0deae90015c0e383885b431444fe6b6c591dea288173df20603146"},
+    {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:196082b9c89ebf0961dcd77cb114bed8171964c8e3063b9da2fb33536a6938ed"},
+    {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c9f89c42749890618cd3c2464e1fbf88446e3d2f67f1e334c8e5db2f3272bbd"},
+    {file = "grpcio-1.49.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64419cb8a5b612cdb1550c2fd4acbb7d4fb263556cf4625f25522337e461509e"},
+    {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8a5272061826e6164f96e3255405ef6f73b88fd3e8bef464c7d061af8585ac62"},
+    {file = "grpcio-1.49.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ea9d0172445241ad7cb49577314e39d0af2c5267395b3561d7ced5d70458a9f3"},
+    {file = "grpcio-1.49.1-cp37-cp37m-win32.whl", hash = "sha256:2070e87d95991473244c72d96d13596c751cb35558e11f5df5414981e7ed2492"},
+    {file = "grpcio-1.49.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fcedcab49baaa9db4a2d240ac81f2d57eb0052b1c6a9501b46b8ae912720fbf"},
+    {file = "grpcio-1.49.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:afbb3475cf7f4f7d380c2ca37ee826e51974f3e2665613996a91d6a58583a534"},
+    {file = "grpcio-1.49.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a4f9ba141380abde6c3adc1727f21529137a2552002243fa87c41a07e528245c"},
+    {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:cf0a1fb18a7204b9c44623dfbd1465b363236ce70c7a4ed30402f9f60d8b743b"},
+    {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17bb6fe72784b630728c6cff9c9d10ccc3b6d04e85da6e0a7b27fb1d135fac62"},
+    {file = "grpcio-1.49.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18305d5a082d1593b005a895c10041f833b16788e88b02bb81061f5ebcc465df"},
+    {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b6a1b39e59ac5a3067794a0e498911cf2e37e4b19ee9e9977dc5e7051714f13f"},
+    {file = "grpcio-1.49.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e20d59aafc086b1cc68400463bddda6e41d3e5ed30851d1e2e0f6a2e7e342d3"},
+    {file = "grpcio-1.49.1-cp38-cp38-win32.whl", hash = "sha256:e1e83233d4680863a421f3ee4a7a9b80d33cd27ee9ed7593bc93f6128302d3f2"},
+    {file = "grpcio-1.49.1-cp38-cp38-win_amd64.whl", hash = "sha256:221d42c654d2a41fa31323216279c73ed17d92f533bc140a3390cc1bd78bf63c"},
+    {file = "grpcio-1.49.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:fa9e6e61391e99708ac87fc3436f6b7b9c6b845dc4639b406e5e61901e1aacde"},
+    {file = "grpcio-1.49.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9b449e966ef518ce9c860d21f8afe0b0f055220d95bc710301752ac1db96dd6a"},
+    {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:aa34d2ad9f24e47fa9a3172801c676e4037d862247e39030165fe83821a7aafd"},
+    {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5207f4eed1b775d264fcfe379d8541e1c43b878f2b63c0698f8f5c56c40f3d68"},
+    {file = "grpcio-1.49.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b24a74651438d45619ac67004638856f76cc13d78b7478f2457754cbcb1c8ad"},
+    {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fe763781669790dc8b9618e7e677c839c87eae6cf28b655ee1fa69ae04eea03f"},
+    {file = "grpcio-1.49.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2f2ff7ba0f8f431f32d4b4bc3a3713426949d3533b08466c4ff1b2b475932ca8"},
+    {file = "grpcio-1.49.1-cp39-cp39-win32.whl", hash = "sha256:08ff74aec8ff457a89b97152d36cb811dcc1d17cd5a92a65933524e363327394"},
+    {file = "grpcio-1.49.1-cp39-cp39-win_amd64.whl", hash = "sha256:274ffbb39717918c514b35176510ae9be06e1d93121e84d50b350861dcb9a705"},
+    {file = "grpcio-1.49.1.tar.gz", hash = "sha256:d4725fc9ec8e8822906ae26bb26f5546891aa7fbc3443de970cc556d43a5c99f"},
 ]
 huggingface-hub = [
-    {file = "huggingface_hub-0.9.1-py3-none-any.whl", hash = "sha256:7a588046bdeb84e7bc99b3da58bbb4312a56d94ba51ebc60dfe610c18b3d0b9f"},
-    {file = "huggingface_hub-0.9.1.tar.gz", hash = "sha256:6395f26aaf44bbb4a73d3e14aca228fa39534696f651c6c82a6347f8c9f5950b"},
+    {file = "huggingface_hub-0.10.0-py3-none-any.whl", hash = "sha256:71c6ca3014cbf1245cafbc0b426a27e0ff0465853b4f85aefcd2863f61f1dc2d"},
+    {file = "huggingface_hub-0.10.0.tar.gz", hash = "sha256:1c7ec31b837db090abdf60c3e008e8d8569d47d976671699adb880aacf60d491"},
 ]
 hydra-core = [
     {file = "hydra-core-1.2.0.tar.gz", hash = "sha256:4990721ce4ac69abafaffee566d6b63a54faa6501ecce65b338d3251446ff634"},
@@ -1734,8 +1808,12 @@ idna = [
     {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
-    {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
+    {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"},
+    {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"},
+]
+importlib-resources = [
+    {file = "importlib_resources-5.10.0-py3-none-any.whl", hash = "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437"},
+    {file = "importlib_resources-5.10.0.tar.gz", hash = "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668"},
 ]
 iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
@@ -1758,11 +1836,11 @@ joblib = [
 kyoto-reader = [
     {file = "kyoto-reader-2.5.2.zip", hash = "sha256:98ced4f5045ca3959e043cb5dbe59aeed6ecdf7e01b1a5d7b3814ca4b9b0de77"},
 ]
-markdown = [
+Markdown = [
     {file = "Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"},
     {file = "Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"},
 ]
-markupsafe = [
+MarkupSafe = [
     {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
     {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
     {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
@@ -1804,6 +1882,60 @@ markupsafe = [
     {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
     {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
 ]
+msgpack = [
+    {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"},
+    {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"},
+    {file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"},
+    {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"},
+    {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"},
+    {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"},
+    {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"},
+    {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"},
+    {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"},
+    {file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"},
+    {file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"},
+    {file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"},
+    {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"},
+    {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"},
+    {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"},
+    {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"},
+    {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"},
+    {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"},
+    {file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"},
+    {file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"},
+    {file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"},
+    {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"},
+    {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"},
+    {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"},
+    {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"},
+    {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"},
+    {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"},
+    {file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"},
+    {file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"},
+    {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"},
+    {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"},
+    {file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"},
+    {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"},
+    {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"},
+    {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"},
+    {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"},
+    {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"},
+    {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"},
+    {file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"},
+    {file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"},
+    {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"},
+    {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"},
+    {file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"},
+    {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"},
+    {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"},
+    {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"},
+    {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"},
+    {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"},
+    {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"},
+    {file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"},
+    {file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"},
+    {file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"},
+]
 multidict = [
     {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
     {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
@@ -1866,29 +1998,30 @@ multidict = [
     {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
 ]
 mypy = [
-    {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"},
-    {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"},
-    {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"},
-    {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"},
-    {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"},
-    {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"},
-    {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"},
-    {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"},
-    {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"},
-    {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"},
-    {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"},
-    {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"},
-    {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"},
-    {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"},
-    {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"},
-    {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"},
-    {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"},
-    {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"},
-    {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"},
-    {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"},
-    {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"},
-    {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"},
-    {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"},
+    {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"},
+    {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"},
+    {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"},
+    {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"},
+    {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"},
+    {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"},
+    {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"},
+    {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"},
+    {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"},
+    {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"},
+    {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"},
+    {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"},
+    {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"},
+    {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"},
+    {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"},
+    {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"},
+    {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"},
+    {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"},
+    {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"},
+    {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"},
+    {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"},
+    {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"},
+    {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"},
+    {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"},
 ]
 mypy-extensions = [
     {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
@@ -1981,27 +2114,33 @@ packaging = [
     {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
 ]
 pandas = [
-    {file = "pandas-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:799e6a25932df7e6b1f8dabf63de064e2205dc309abb75956126a0453fd88e97"},
-    {file = "pandas-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd1d69a387f7d5e1a5a06a87574d9ef2433847c0e78113ab51c84d3a8bcaeaa"},
-    {file = "pandas-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:94f2ed1fd51e545ebf71da1e942fe1822ee01e10d3dd2a7276d01351333b7c6b"},
-    {file = "pandas-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4591cadd06fbbbd16fafc2de6e840c1aaefeae3d5864b688004777ef1bbdede3"},
-    {file = "pandas-1.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0022fe6a313df1c4869b5edc012d734c6519a6fffa3cf70930f32e6a1078e49"},
-    {file = "pandas-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:785e878a6e6d8ddcdb8c181e600855402750052497d7fc6d6b508894f6b8830b"},
-    {file = "pandas-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c4bb8b0ab9f94207d07e401d24baebfc63057246b1a5e0cd9ee50df85a656871"},
-    {file = "pandas-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51c424ca134fdaeac9a4acd719d1ab48046afc60943a489028f0413fdbe9ef1c"},
-    {file = "pandas-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ce35f947202b0b99c660221d82beb91d2e6d553d55a40b30128204e3e2c63848"},
-    {file = "pandas-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee6f1848148ed3204235967613b0a32be2d77f214e9623f554511047705c1e04"},
-    {file = "pandas-1.4.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cc960959be28d064faefc0cb2aef854d46b827c004ebea7e79b5497ed83e7d"},
-    {file = "pandas-1.4.4-cp38-cp38-win32.whl", hash = "sha256:9d805bce209714b1c1fa29bfb1e42ad87e4c0a825e4b390c56a3e71593b7e8d8"},
-    {file = "pandas-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:afbddad78a98ec4d2ce08b384b81730de1ccc975b99eb663e6dac43703f36d98"},
-    {file = "pandas-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a08ceb59db499864c58a9bf85ab6219d527d91f14c0240cc25fa2c261032b2a7"},
-    {file = "pandas-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0959c41004e3d2d16f39c828d6da66ebee329836a7ecee49fb777ac9ad8a7501"},
-    {file = "pandas-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87b4194f344dcd14c0f885cecb22005329b38bda10f1aaf7b9596a00ec8a4768"},
-    {file = "pandas-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d2a7a3c1fea668d56bd91edbd5f2732e0af8feb9d2bf8d9bfacb2dea5fa9536"},
-    {file = "pandas-1.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a981cfabf51c318a562deb4ae7deec594c07aee7cf18b4594a92c23718ec8275"},
-    {file = "pandas-1.4.4-cp39-cp39-win32.whl", hash = "sha256:050aada67a5ec6699a7879e769825b510018a95fb9ac462bb1867483d0974a97"},
-    {file = "pandas-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:8d4d2fe2863ecddb0ba1979bdda26c8bc2ea138f5a979abe3ba80c0fa4015c91"},
-    {file = "pandas-1.4.4.tar.gz", hash = "sha256:ab6c0d738617b675183e5f28db32b5148b694ad9bba0a40c3ea26d96b431db67"},
+    {file = "pandas-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0d8d7433d19bfa33f11c92ad9997f15a902bda4f5ad3a4814a21d2e910894484"},
+    {file = "pandas-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cc47f2ebaa20ef96ae72ee082f9e101b3dfbf74f0e62c7a12c0b075a683f03c"},
+    {file = "pandas-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e8e5edf97d8793f51d258c07c629bd49d271d536ce15d66ac00ceda5c150eb3"},
+    {file = "pandas-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41aec9f87455306496d4486df07c1b98c15569c714be2dd552a6124cd9fda88f"},
+    {file = "pandas-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c76f1d104844c5360c21d2ef0e1a8b2ccf8b8ebb40788475e255b9462e32b2be"},
+    {file = "pandas-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:1642fc6138b4e45d57a12c1b464a01a6d868c0148996af23f72dde8d12486bbc"},
+    {file = "pandas-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:171cef540bfcec52257077816a4dbbac152acdb8236ba11d3196ae02bf0959d8"},
+    {file = "pandas-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a68a9b9754efff364b0c5ee5b0f18e15ca640c01afe605d12ba8b239ca304d6b"},
+    {file = "pandas-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:86d87279ebc5bc20848b4ceb619073490037323f80f515e0ec891c80abad958a"},
+    {file = "pandas-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:207d63ac851e60ec57458814613ef4b3b6a5e9f0b33c57623ba2bf8126c311f8"},
+    {file = "pandas-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e252a9e49b233ff96e2815c67c29702ac3a062098d80a170c506dff3470fd060"},
+    {file = "pandas-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:de34636e2dc04e8ac2136a8d3c2051fd56ebe9fd6cd185581259330649e73ca9"},
+    {file = "pandas-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1d34b1f43d9e3f4aea056ba251f6e9b143055ebe101ed04c847b41bb0bb4a989"},
+    {file = "pandas-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b82ccc7b093e0a93f8dffd97a542646a3e026817140e2c01266aaef5fdde11b"},
+    {file = "pandas-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e30a31039574d96f3d683df34ccb50bb435426ad65793e42a613786901f6761"},
+    {file = "pandas-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62e61003411382e20d7c2aec1ee8d7c86c8b9cf46290993dd8a0a3be44daeb38"},
+    {file = "pandas-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc987f7717e53d372f586323fff441263204128a1ead053c1b98d7288f836ac9"},
+    {file = "pandas-1.5.0-cp38-cp38-win32.whl", hash = "sha256:e178ce2d7e3b934cf8d01dc2d48d04d67cb0abfaffdcc8aa6271fd5a436f39c8"},
+    {file = "pandas-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:33a9d9e21ab2d91e2ab6e83598419ea6a664efd4c639606b299aae8097c1c94f"},
+    {file = "pandas-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:73844e247a7b7dac2daa9df7339ecf1fcf1dfb8cbfd11e3ffe9819ae6c31c515"},
+    {file = "pandas-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9c5049333c5bebf993033f4bf807d163e30e8fada06e1da7fa9db86e2392009"},
+    {file = "pandas-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85a516a7f6723ca1528f03f7851fa8d0360d1d6121cf15128b290cf79b8a7f6a"},
+    {file = "pandas-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:947ed9f896ee61adbe61829a7ae1ade493c5a28c66366ec1de85c0642009faac"},
+    {file = "pandas-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7f38d91f21937fe2bec9449570d7bf36ad7136227ef43b321194ec249e2149d"},
+    {file = "pandas-1.5.0-cp39-cp39-win32.whl", hash = "sha256:2504c032f221ef9e4a289f5e46a42b76f5e087ecb67d62e342ccbba95a32a488"},
+    {file = "pandas-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a4fc04838615bf0a8d3a03ed68197f358054f0df61f390bcc64fbe39e3d71ec"},
+    {file = "pandas-1.5.0.tar.gz", hash = "sha256:3ee61b881d2f64dd90c356eb4a4a4de75376586cd3c9341c6c0fcaae18d52977"},
 ]
 pathtools = [
     {file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"},
@@ -2014,30 +2153,31 @@ promise = [
     {file = "promise-2.3.tar.gz", hash = "sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0"},
 ]
 protobuf = [
-    {file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"},
-    {file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"},
-    {file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"},
-    {file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"},
-    {file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"},
-    {file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"},
-    {file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"},
-    {file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"},
-    {file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"},
-    {file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"},
-    {file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"},
-    {file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"},
-    {file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"},
-    {file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"},
-    {file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"},
-    {file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"},
-    {file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"},
-    {file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"},
-    {file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"},
-    {file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"},
-    {file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"},
-    {file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"},
-    {file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"},
-    {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"},
+    {file = "protobuf-3.19.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:010be24d5a44be7b0613750ab40bc8b8cedc796db468eae6c779b395f50d1fa1"},
+    {file = "protobuf-3.19.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11478547958c2dfea921920617eb457bc26867b0d1aa065ab05f35080c5d9eb6"},
+    {file = "protobuf-3.19.6-cp310-cp310-win32.whl", hash = "sha256:559670e006e3173308c9254d63facb2c03865818f22204037ab76f7a0ff70b5f"},
+    {file = "protobuf-3.19.6-cp310-cp310-win_amd64.whl", hash = "sha256:347b393d4dd06fb93a77620781e11c058b3b0a5289262f094379ada2920a3730"},
+    {file = "protobuf-3.19.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a8ce5ae0de28b51dff886fb922012dad885e66176663950cb2344c0439ecb473"},
+    {file = "protobuf-3.19.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b0d02163c4e67279ddb6dc25e063db0130fc299aefabb5d481053509fae5c8"},
+    {file = "protobuf-3.19.6-cp36-cp36m-win32.whl", hash = "sha256:30f5370d50295b246eaa0296533403961f7e64b03ea12265d6dfce3a391d8992"},
+    {file = "protobuf-3.19.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0c0714b025ec057b5a7600cb66ce7c693815f897cfda6d6efb58201c472e3437"},
+    {file = "protobuf-3.19.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5057c64052a1f1dd7d4450e9aac25af6bf36cfbfb3a1cd89d16393a036c49157"},
+    {file = "protobuf-3.19.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bb6776bd18f01ffe9920e78e03a8676530a5d6c5911934c6a1ac6eb78973ecb6"},
+    {file = "protobuf-3.19.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84a04134866861b11556a82dd91ea6daf1f4925746b992f277b84013a7cc1229"},
+    {file = "protobuf-3.19.6-cp37-cp37m-win32.whl", hash = "sha256:4bc98de3cdccfb5cd769620d5785b92c662b6bfad03a202b83799b6ed3fa1fa7"},
+    {file = "protobuf-3.19.6-cp37-cp37m-win_amd64.whl", hash = "sha256:aa3b82ca1f24ab5326dcf4ea00fcbda703e986b22f3d27541654f749564d778b"},
+    {file = "protobuf-3.19.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2b2d2913bcda0e0ec9a784d194bc490f5dc3d9d71d322d070b11a0ade32ff6ba"},
+    {file = "protobuf-3.19.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d0b635cefebd7a8a0f92020562dead912f81f401af7e71f16bf9506ff3bdbb38"},
+    {file = "protobuf-3.19.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a552af4dc34793803f4e735aabe97ffc45962dfd3a237bdde242bff5a3de684"},
+    {file = "protobuf-3.19.6-cp38-cp38-win32.whl", hash = "sha256:0469bc66160180165e4e29de7f445e57a34ab68f49357392c5b2f54c656ab25e"},
+    {file = "protobuf-3.19.6-cp38-cp38-win_amd64.whl", hash = "sha256:91d5f1e139ff92c37e0ff07f391101df77e55ebb97f46bbc1535298d72019462"},
+    {file = "protobuf-3.19.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0ccd3f940fe7f3b35a261b1dd1b4fc850c8fde9f74207015431f174be5976b3"},
+    {file = "protobuf-3.19.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:30a15015d86b9c3b8d6bf78d5b8c7749f2512c29f168ca259c9d7727604d0e39"},
+    {file = "protobuf-3.19.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:878b4cd080a21ddda6ac6d1e163403ec6eea2e206cf225982ae04567d39be7b0"},
+    {file = "protobuf-3.19.6-cp39-cp39-win32.whl", hash = "sha256:5a0d7539a1b1fb7e76bf5faa0b44b30f812758e989e59c40f77a7dab320e79b9"},
+    {file = "protobuf-3.19.6-cp39-cp39-win_amd64.whl", hash = "sha256:bbf5cea5048272e1c60d235c7bd12ce1b14b8a16e76917f371c718bd3005f045"},
+    {file = "protobuf-3.19.6-py2.py3-none-any.whl", hash = "sha256:14082457dc02be946f60b15aad35e9f5c69e738f80ebbc0900a19bc83734a5a4"},
+    {file = "protobuf-3.19.6.tar.gz", hash = "sha256:5f5540d57a43042389e87661c6eaa50f47c19c6176e8cf1c4f287aeefeccb5c4"},
 ]
 psutil = [
     {file = "psutil-5.9.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:8f024fbb26c8daf5d70287bb3edfafa22283c255287cf523c5d81721e8e5d82c"},
@@ -2085,11 +2225,11 @@ pyasn1-modules = [
     {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
     {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
 ]
-pydeprecate = [
+pyDeprecate = [
     {file = "pyDeprecate-0.3.2-py3-none-any.whl", hash = "sha256:ed86b68ed837e6465245904a3de2f59bf9eef78ac7a2502ee280533d04802457"},
     {file = "pyDeprecate-0.3.2.tar.gz", hash = "sha256:d481116cc5d7f6c473e7c4be820efdd9b90a16b594b350276e9e66a6cb5bdd29"},
 ]
-pygments = [
+Pygments = [
     {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"},
     {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"},
 ]
@@ -2106,29 +2246,33 @@ pytest = [
     {file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"},
 ]
 pytest-cov = [
-    {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
-    {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
+    {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"},
+    {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"},
+]
+pytest-profiling = [
+    {file = "pytest-profiling-1.7.0.tar.gz", hash = "sha256:93938f147662225d2b8bd5af89587b979652426a8a6ffd7e73ec4a23e24b7f29"},
+    {file = "pytest_profiling-1.7.0-py2.py3-none-any.whl", hash = "sha256:999cc9ac94f2e528e3f5d43465da277429984a1c237ae9818f8cfd0b06acb019"},
 ]
 python-dateutil = [
     {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
     {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
 ]
 python-dotenv = [
-    {file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"},
-    {file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"},
+    {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
+    {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
 ]
-python-levenshtein = [
+python-Levenshtein = [
     {file = "python-Levenshtein-0.12.2.tar.gz", hash = "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6"},
 ]
 pytorch-lightning = [
-    {file = "pytorch-lightning-1.6.5.tar.gz", hash = "sha256:8d521f2619b9db2ada5bbaf9713330d01460e75a11e4bc0bc2ca25fd37c47c57"},
-    {file = "pytorch_lightning-1.6.5-py3-none-any.whl", hash = "sha256:00c9205d26aa354defdd4dd592b2dded33916d6e0c180ccffbb06cf34dc67ccf"},
+    {file = "pytorch-lightning-1.7.7.tar.gz", hash = "sha256:27c2dd01a18db2415168e3fa3775ccb5a1fa1e2961a50439ad9365507fe9d4ae"},
+    {file = "pytorch_lightning-1.7.7-py3-none-any.whl", hash = "sha256:4438b8284d7f7fdb06cf3566a7b5b6f102ac8971cf7bb6d3f1b1de64628241f3"},
 ]
 pytz = [
-    {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"},
-    {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"},
+    {file = "pytz-2022.4-py2.py3-none-any.whl", hash = "sha256:2c0784747071402c6e99f0bafdb7da0fa22645f06554c7ae06bf6358897e9c91"},
+    {file = "pytz-2022.4.tar.gz", hash = "sha256:48ce799d83b6f8aab2020e369b627446696619e79645419610b9facd909b3174"},
 ]
-pyyaml = [
+PyYAML = [
     {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
     {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
     {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
@@ -2269,12 +2413,12 @@ requests-oauthlib = [
     {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
 ]
 rhoknp = [
-    {file = "rhoknp-0.4.0-py3-none-any.whl", hash = "sha256:733c7059f1ea4b4b8c810a8e01867d82be885b33912e641c3e5f9e4f55bd4272"},
-    {file = "rhoknp-0.4.0.tar.gz", hash = "sha256:549440dbd36b03e41b3bfa7adbc89ff9de3b805dcfb3654f01288638595f6d97"},
+    {file = "rhoknp-0.4.4-py3-none-any.whl", hash = "sha256:3b856a7c036fe45c6e01347559e3137083ec68652a5b674b2bcf989939e9af6e"},
+    {file = "rhoknp-0.4.4.tar.gz", hash = "sha256:457b11535eabbf65d847e0313dee6ef9c05299212b3e88f4228e90a462826778"},
 ]
 rich = [
-    {file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"},
-    {file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"},
+    {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"},
+    {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"},
 ]
 rsa = [
     {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
@@ -2301,29 +2445,27 @@ scikit-learn = [
     {file = "scikit_learn-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:407e9a1cb9e6ba458a539986a9bd25546a757088095b3aab91d465b79a760d37"},
 ]
 scipy = [
-    {file = "scipy-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c61b4a91a702e8e04aeb0bfc40460e1f17a640977c04dda8757efb0199c75332"},
-    {file = "scipy-1.9.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d79da472015d0120ba9b357b28a99146cd6c17b9609403164b1a8ed149b4dfc8"},
-    {file = "scipy-1.9.1-cp310-cp310-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:825951b88f56765aeb6e5e38ac9d7d47407cfaaeb008d40aa1b45a2d7ea2731e"},
-    {file = "scipy-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f950a04b33e17b38ff561d5a0951caf3f5b47caa841edd772ffb7959f20a6af0"},
-    {file = "scipy-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc81ac25659fec73599ccc52c989670e5ccd8974cf34bacd7b54a8d809aff1a"},
-    {file = "scipy-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:8d3faa40ac16c6357aaf7ea50394ea6f1e8e99d75e927a51102b1943b311b4d9"},
-    {file = "scipy-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a412c476a91b080e456229e413792bbb5d6202865dae963d1e6e28c2bb58691"},
-    {file = "scipy-1.9.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:eb954f5aca4d26f468bbebcdc5448348eb287f7bea536c6306f62ea062f63d9a"},
-    {file = "scipy-1.9.1-cp38-cp38-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:3c6f5d1d4b9a5e4fe5e14f26ffc9444fc59473bbf8d45dc4a9a15283b7063a72"},
-    {file = "scipy-1.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bc4e2c77d4cd015d739e75e74ebbafed59ba8497a7ed0fd400231ed7683497c4"},
-    {file = "scipy-1.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0419485dbcd0ed78c0d5bf234c5dd63e86065b39b4d669e45810d42199d49521"},
-    {file = "scipy-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34441dfbee5b002f9e15285014fd56e5e3372493c3e64ae297bae2c4b9659f5a"},
-    {file = "scipy-1.9.1-cp38-cp38-win32.whl", hash = "sha256:b97b479f39c7e4aaf807efd0424dec74bbb379108f7d22cf09323086afcd312c"},
-    {file = "scipy-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8fe305d9d67a81255e06203454729405706907dccbdfcc330b7b3482a6c371d"},
-    {file = "scipy-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:39ab9240cd215a9349c85ab908dda6d732f7d3b4b192fa05780812495536acc4"},
-    {file = "scipy-1.9.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:71487c503e036740635f18324f62a11f283a632ace9d35933b2b0a04fd898c98"},
-    {file = "scipy-1.9.1-cp39-cp39-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:3bc1ab68b9a096f368ba06c3a5e1d1d50957a86665fc929c4332d21355e7e8f4"},
-    {file = "scipy-1.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f7c39f7dbb57cce00c108d06d731f3b0e2a4d3a95c66d96bce697684876ce4d4"},
-    {file = "scipy-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47d1a95bd9d37302afcfe1b84c8011377c4f81e33649c5a5785db9ab827a6ade"},
-    {file = "scipy-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d7cf7b25c9f23c59a766385f6370dab0659741699ecc7a451f9b94604938ce"},
-    {file = "scipy-1.9.1-cp39-cp39-win32.whl", hash = "sha256:09412eb7fb60b8f00b328037fd814d25d261066ebc43a1e339cdce4f7502877e"},
-    {file = "scipy-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:90c805f30c46cf60f1e76e947574f02954d25e3bb1e97aa8a07bc53aa31cf7d1"},
-    {file = "scipy-1.9.1.tar.gz", hash = "sha256:26d28c468900e6d5fdb37d2812ab46db0ccd22c63baa095057871faa3a498bc9"},
+    {file = "scipy-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ee4ceed204f269da19f67f0115a85d3a2cd8547185037ad99a4025f9c61d02e9"},
+    {file = "scipy-1.9.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:17be1a7c68ec4c49d8cd4eb1655d55d14a54ab63012296bdd5921c92dc485acd"},
+    {file = "scipy-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72297eb9702576bd8f626bb488fd32bb35349d3120fc4a5e733db137f06c9a6"},
+    {file = "scipy-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa270cc6080c987929335c4cb94e8054fee9a6058cecff22276fa5dbab9856fc"},
+    {file = "scipy-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:22380e076a162e81b659d53d75b02e9c75ad14ea2d53d9c645a12543414e2150"},
+    {file = "scipy-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbed414fc25d64bd6d1613dc0286fbf91902219b8be63ad254525162235b67e9"},
+    {file = "scipy-1.9.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:885b7ac56d7460544b2ef89ab9feafa30f4264c9825d975ef690608d07e6cc55"},
+    {file = "scipy-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5994a8232cc6510a8e85899661df2d11198bf362f0ffe6fbd5c0aca17ab46ce3"},
+    {file = "scipy-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e9c83dccac06f3b9aa02df69577f239758d5d0d0c069673fb0b47ecb971983d"},
+    {file = "scipy-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:92c5e627a0635ca02e6494bbbdb74f98d93ac8730416209d61de3b70c8a821be"},
+    {file = "scipy-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6194da32e0ce9200b2eda4eb4edb89c5cb8b83d6deaf7c35f8ad3d5d7627d5c"},
+    {file = "scipy-1.9.2-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:148cb6f53d9d10dafde848e9aeb1226bf2809d16dc3221b2fa568130b6f2e586"},
+    {file = "scipy-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:658fd31c6ad4eb9fa3fd460fcac779f70a6bc7480288a211b7658a25891cf01d"},
+    {file = "scipy-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4012dbe540732311b8f4388b7e1482eb43a7cc0435bbf2b9916b3d6c38fb8d01"},
+    {file = "scipy-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:d6cb1f92ded3fc48f7dbe94d20d7b9887e13b874e79043907de541c841563b4c"},
+    {file = "scipy-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1e3b23a82867018cd26255dc951789a7c567921622073e1113755866f1eae928"},
+    {file = "scipy-1.9.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:82e8bfb352aa9dce9a0ffe81f4c369a2c87c85533519441686f59f21d8c09697"},
+    {file = "scipy-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b95283529712101bfb7c87faf94cb86ed9e64de079509edfe107e5cfa55733"},
+    {file = "scipy-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c8c29703202c39d699b0d6b164bde5501c212005f20abf46ae322b9307c8a41"},
+    {file = "scipy-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:7b2608b3141c257d01ae772e23b3de9e04d27344e6b68a890883795229cb7191"},
+    {file = "scipy-1.9.2.tar.gz", hash = "sha256:99e7720caefb8bca6ebf05c7d96078ed202881f61e0c68bd9e0f3e8097d6f794"},
 ]
 sentencepiece = [
     {file = "sentencepiece-0.1.97-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6f249c8f1852893be86eae66b19d522c5fb30bbad4fe2d1b07f06fdc86e1907e"},
@@ -2365,8 +2507,8 @@ sentencepiece = [
     {file = "sentencepiece-0.1.97.tar.gz", hash = "sha256:c901305e0a710bbcd296f66d79e96f744e6e175b29812bd5178318437d4e1f6c"},
 ]
 sentry-sdk = [
-    {file = "sentry-sdk-1.9.8.tar.gz", hash = "sha256:ef4b4d57631662ff81e15c22bf007f7ce07c47321648c8e601fbd22950ed1a79"},
-    {file = "sentry_sdk-1.9.8-py2.py3-none-any.whl", hash = "sha256:7378c08d81da78a4860da7b4900ac51fef38be841d01bc5b7cb249ac51e070c6"},
+    {file = "sentry-sdk-1.9.10.tar.gz", hash = "sha256:4fbace9a763285b608c06f01a807b51acb35f6059da6a01236654e08b0ee81ff"},
+    {file = "sentry_sdk-1.9.10-py2.py3-none-any.whl", hash = "sha256:2469240f6190aaebcb453033519eae69cfe8cc602065b4667e18ee14fc1e35dc"},
 ]
 seqeval = [
     {file = "seqeval-1.2.2.tar.gz", hash = "sha256:f28e97c3ab96d6fcd32b648f6438ff2e09cfba87f05939da9b3970713ec56e6f"},
@@ -2434,8 +2576,8 @@ setproctitle = [
     {file = "setproctitle-1.3.2.tar.gz", hash = "sha256:b9fb97907c830d260fa0658ed58afd48a86b2b88aac521135c352ff7fd3477fd"},
 ]
 setuptools = [
-    {file = "setuptools-65.3.0-py3-none-any.whl", hash = "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82"},
-    {file = "setuptools-65.3.0.tar.gz", hash = "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"},
+    {file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
+    {file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
 ]
 shortuuid = [
     {file = "shortuuid-1.0.9-py3-none-any.whl", hash = "sha256:b2bb9eb7773170e253bb7ba25971023acb473517a8b76803d9618668cb1dd46f"},
@@ -2450,7 +2592,7 @@ smmap = [
     {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
 ]
 tensorboard = [
-    {file = "tensorboard-2.9.0-py3-none-any.whl", hash = "sha256:bd78211076dca5efa27260afacfaa96cd05c7db12a6c09cc76a1d6b2987ca621"},
+    {file = "tensorboard-2.10.1-py3-none-any.whl", hash = "sha256:fb9222c1750e2fa35ef170d998a1e229f626eeced3004494a8849c88c15d8c1c"},
 ]
 tensorboard-data-server = [
     {file = "tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7"},
@@ -2545,18 +2687,18 @@ typer = [
     {file = "typer-0.6.1.tar.gz", hash = "sha256:2d5720a5e63f73eaf31edaa15f6ab87f35f0690f8ca233017d7d23d743a91d73"},
 ]
 typing-extensions = [
-    {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
-    {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
+    {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
+    {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
 ]
 urllib3 = [
     {file = "urllib3-1.26.12-py2.py3-none-any.whl", hash = "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"},
     {file = "urllib3-1.26.12.tar.gz", hash = "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e"},
 ]
 wandb = [
-    {file = "wandb-0.13.3-py2.py3-none-any.whl", hash = "sha256:a246ed531ae405f2c95d299adb7311f2cf333c6c73ce36e3fcaaae60f0817c6c"},
-    {file = "wandb-0.13.3.tar.gz", hash = "sha256:8f91a8b67949313769c686e4f5139e4c247de8ce140e72fdcdd7896da2ea1c87"},
+    {file = "wandb-0.13.4-py2.py3-none-any.whl", hash = "sha256:ce64648325a22c3166f23e00aaf373732d0450196242a0a93efd774f7e79f9c0"},
+    {file = "wandb-0.13.4.tar.gz", hash = "sha256:51e2672e12cca94680f6bcff0af80822d562e6b7846036050fc5bdd00240ea75"},
 ]
-werkzeug = [
+Werkzeug = [
     {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"},
     {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"},
 ]
@@ -2626,6 +2768,6 @@ yarl = [
     {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"},
 ]
 zipp = [
-    {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"},
-    {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"},
+    {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"},
+    {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"},
 ]
diff --git a/pyproject.toml b/pyproject.toml
index 79b5c209..211342ba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,44 +41,47 @@ include = [
 ]
 
 [tool.poetry.dependencies]
-python = ">=3.9,<3.11"
+python = ">=3.8,<3.11"
 torch = "~1.11.0"
-pytorch-lightning = "^1.6.5"
+torchmetrics = "^0.9.3"
+pytorch-lightning = "^1.7"
 omegaconf = "^2.1"
 hydra-core = "^1.2"
 tokenizers = "^0.12.1"
 transformers = "~4.19.2"
 typer = "^0.6.1"
 sentencepiece = "^0.1.96"
-protobuf = "^3.20"
+protobuf = "^3.19"
 seqeval = "^1.2.2"
-wandb = "^0.13.3"
 rich = "^12.4.4"
-rhoknp = "^0.4.0"
+rhoknp = "^0.4.3"
 python-Levenshtein = "^0.12.2"
 ipadic = "^1.0.0"
-python-dotenv = "^0.20.0"
-pandas = "^1.4.2"
+python-dotenv = "^0.21.0"
+pandas = "^1.4"
 dartsclone = "^0.10.2"
-kyoto-reader = "^2.5.2"
 jinf = "^1.0.4"
 tinydb = "^4.7.0"
 BetterJSONStorage = "^1.3.0"
-
-[tool.poetry.dev-dependencies]
-pytest = "^7.1"
-pytest-cov = "^3.0.0"
+importlib-resources = "^5.10.0"
 
 [tool.poetry.scripts]
 kwja = "kwja.cli.cli:app"
 
+[tool.poetry.group.dev.dependencies]
+wandb = "^0.13.3"
+kyoto-reader = "^2.5"
+pytest-profiling = "^1.7"
+pytest = "^7.1"
+pytest-cov = "^4.0"
+
 [build-system]
 requires = ["poetry-core"]
 build-backend = "poetry.core.masonry.api"
 
 [tool.black]
 line-length = 120
-target-version = ["py39", "py310"]
+target-version = ["py38", "py39", "py310"]
 
 [tool.isort]
 line_length = 120
@@ -87,4 +90,4 @@ profile = "black"
 src_paths = ["src"]
 
 [tool.mypy]
-python_version = 3.9
+python_version = 3.8
diff --git a/scripts/add_features_to_raw_corpus.py b/scripts/add_features_to_raw_corpus.py
index 77358c59..af6f0db0 100644
--- a/scripts/add_features_to_raw_corpus.py
+++ b/scripts/add_features_to_raw_corpus.py
@@ -5,7 +5,7 @@
 from itertools import product
 from pathlib import Path
 from subprocess import PIPE, Popen
-from typing import Any, Optional, Union
+from typing import Any, Dict, List, Optional, Tuple, Union
 
 from kyoto_reader import KyotoReader
 from rhoknp import KNP, Document, Jumanpp, Morpheme, Sentence
@@ -69,14 +69,14 @@ def _postprocess_sentence(
     ) -> None:
         for original_morpheme, augmented_morpheme in zip(original_sentence.morphemes, augmented_sentence.morphemes):
             # Jumanpp may override reading
-            augmented_morpheme._attributes.reading = original_morpheme.reading
+            augmented_morpheme.attributes.reading = original_morpheme.reading
             if update_original and not original_sentence.need_knp:
                 # add Semantics
                 for k, v in augmented_morpheme.semantics.items():
                     original_morpheme.semantics[k] = v
 
 
-def align(morphemes1: list[Morpheme], morphemes2: list[Morpheme]) -> Union[dict[str, list[Morpheme]], None]:
+def align(morphemes1: List[Morpheme], morphemes2: List[Morpheme]) -> Union[Dict[str, List[Morpheme]], None]:
     alignment = {}
     idx1, idx2 = 0, 0
     for _ in range(max(len(morphemes1), len(morphemes2))):
@@ -102,7 +102,7 @@ def align(morphemes1: list[Morpheme], morphemes2: list[Morpheme]) -> Union[dict[
     return alignment
 
 
-def extract_named_entities(tagged_sentence: Sentence) -> list[tuple[str, list[Morpheme]]]:
+def extract_named_entities(tagged_sentence: Sentence) -> List[Tuple[str, List[Morpheme]]]:
     named_entities = []
     category, morphemes_buff = "", []
     for morpheme in tagged_sentence.morphemes:
@@ -120,7 +120,7 @@ def extract_named_entities(tagged_sentence: Sentence) -> list[tuple[str, list[Mo
     return named_entities
 
 
-def set_named_entities(document: Document, sid2tagged_sentence: dict[str, Sentence]) -> None:
+def set_named_entities(document: Document, sid2tagged_sentence: Dict[str, Sentence]) -> None:
     for sentence in document.sentences:
         # 既にneタグが付与されている文は対象としない
         if sentence.sid in sid2tagged_sentence and len(sentence.named_entities) == 0:
@@ -196,10 +196,10 @@ def refresh(document: Document) -> None:
 
 
 def add_features(
-    doc_ids: list[str],
+    doc_ids: List[str],
     reader: KyotoReader,
     output_dir: Path,
-    sid2tagged_sentence: Optional[dict[str, Sentence]] = None,
+    sid2tagged_sentence: Optional[Dict[str, Sentence]] = None,
 ) -> None:
     jumanpp_augmenter = JumanppAugmenter()
     knp = KNP(options=["-tab", "-dpnd-fast", "-read-feature"])
diff --git a/scripts/analyze.py b/scripts/analyze.py
index f9a581a9..4ddb7038 100644
--- a/scripts/analyze.py
+++ b/scripts/analyze.py
@@ -1,4 +1,5 @@
 import sys
+from typing import List
 
 import hydra
 import pytorch_lightning as pl
@@ -23,7 +24,7 @@ def main(cfg: DictConfig):
         except ValueError:
             cfg.devices = None
 
-    callbacks: list[Callback] = [hydra.utils.instantiate(cfg.callbacks.prediction_writer, use_stdout=True)]
+    callbacks: List[Callback] = [hydra.utils.instantiate(cfg.callbacks.prediction_writer, use_stdout=True)]
 
     trainer: pl.Trainer = hydra.utils.instantiate(
         cfg.trainer,
diff --git a/scripts/predict.py b/scripts/predict.py
index 646fc745..bca19600 100644
--- a/scripts/predict.py
+++ b/scripts/predict.py
@@ -1,3 +1,5 @@
+from typing import List
+
 import hydra
 import pytorch_lightning as pl
 import transformers.utils.logging as hf_logging
@@ -21,7 +23,7 @@ def main(cfg: DictConfig):
         except ValueError:
             cfg.devices = None
 
-    callbacks: list[Callback] = []
+    callbacks: List[Callback] = []
     for k, v in cfg.get("callbacks", {}).items():
         if k == "prediction_writer":
             callbacks.append(hydra.utils.instantiate(v))
diff --git a/scripts/stats.py b/scripts/stats.py
index b4c0b2f0..73d64fda 100644
--- a/scripts/stats.py
+++ b/scripts/stats.py
@@ -1,5 +1,5 @@
 import logging
-from typing import Union
+from typing import Dict, Union
 
 import hydra
 import transformers.utils.logging as hf_logging
@@ -17,8 +17,8 @@
 OmegaConf.register_new_resolver("concat", lambda x, y: x + y)
 
 
-def get_word_dataset_stats(dataset: WordDataset) -> dict[str, Union[int, float]]:
-    stats: dict[str, Union[int, float]] = {"num_examples": len(dataset)}
+def get_word_dataset_stats(dataset: WordDataset) -> Dict[str, Union[int, float]]:
+    stats: Dict[str, Union[int, float]] = {"num_examples": len(dataset)}
 
     # Reading predictions
     num_reading_labels = 0
diff --git a/scripts/train.py b/scripts/train.py
index 494591de..eed77ac6 100644
--- a/scripts/train.py
+++ b/scripts/train.py
@@ -1,6 +1,6 @@
 import logging
 import math
-from typing import Optional
+from typing import List, Optional
 
 import hydra
 import pytorch_lightning as pl
@@ -30,7 +30,7 @@ def main(cfg: DictConfig):
 
     is_debug: bool = True if "fast_dev_run" in cfg.trainer else False
     logger: Optional[LightningLoggerBase] = hydra.utils.instantiate(cfg.logger) if not is_debug else None
-    callbacks: list[Callback] = list(map(hydra.utils.instantiate, cfg.get("callbacks", {}).values()))
+    callbacks: List[Callback] = list(map(hydra.utils.instantiate, cfg.get("callbacks", {}).values()))
 
     # Calculate gradient_accumulation_steps assuming DDP
     num_devices: int = len(cfg.devices) if isinstance(cfg.devices, (list, ListConfig)) else cfg.devices
diff --git a/scripts/view_typo_results.py b/scripts/view_typo_results.py
index 74c7140f..ae8c0c61 100644
--- a/scripts/view_typo_results.py
+++ b/scripts/view_typo_results.py
@@ -1,11 +1,12 @@
 import json
 from argparse import ArgumentParser
 from pathlib import Path
+from typing import Dict, List, Tuple
 
 from Levenshtein import opcodes
 
 
-def apply_ops(pre_text: str, kdrs: list[str], inss: list[str]) -> str:
+def apply_ops(pre_text: str, kdrs: List[str], inss: List[str]) -> str:
     post_text = ""
     for i, c in enumerate(pre_text):
         if inss[i] != "_":
@@ -23,12 +24,12 @@ def apply_ops(pre_text: str, kdrs: list[str], inss: list[str]) -> str:
     return post_text
 
 
-def get_diffs(before: str, after: str) -> list[tuple[str, str]]:
-    before_split: list[str] = list(before)
-    after_split: list[str] = list(after)
+def get_diffs(before: str, after: str) -> List[Tuple[str, str]]:
+    before_split: List[str] = list(before)
+    after_split: List[str] = list(after)
 
-    char2id: dict[str, str] = dict()
-    id2char: dict[str, str] = dict()
+    char2id: Dict[str, str] = dict()
+    id2char: Dict[str, str] = dict()
     id_ = 0
     for char in before_split + after_split:
         if char not in char2id:
@@ -39,7 +40,7 @@ def get_diffs(before: str, after: str) -> list[tuple[str, str]]:
     before_id_string: str = "".join([char2id[i] for i in before_split])
     after_id_string: str = "".join([char2id[i] for i in after_split])
 
-    diff_list: list[tuple[str, str]] = []
+    diff_list: List[Tuple[str, str]] = []
     for operation, bb, be, ab, ae in opcodes(before_id_string, after_id_string):
         if operation == "insert":
             for char_id in after_id_string[ab:ae]:
@@ -55,8 +56,8 @@ def get_diffs(before: str, after: str) -> list[tuple[str, str]]:
 
 
 def calc_tp_fp_fn(pre_text: str, ref_post_text: str, pred_post_text: str):
-    ref_diffs: list[tuple[str, str]] = get_diffs(pre_text, ref_post_text)
-    pred_diffs: list[tuple[str, str]] = get_diffs(pre_text, pred_post_text)
+    ref_diffs: List[Tuple[str, str]] = get_diffs(pre_text, ref_post_text)
+    pred_diffs: List[Tuple[str, str]] = get_diffs(pre_text, pred_post_text)
 
     ref_num: int = len(ref_diffs)
     tp: int = 0
diff --git a/src/kwja/callbacks/char_module_writer.py b/src/kwja/callbacks/char_module_writer.py
index 8062b4cd..72e237a1 100644
--- a/src/kwja/callbacks/char_module_writer.py
+++ b/src/kwja/callbacks/char_module_writer.py
@@ -2,7 +2,7 @@
 import sys
 from io import TextIOBase
 from pathlib import Path
-from typing import Any, Optional, Sequence, TextIO, Union
+from typing import Any, List, Optional, Sequence, TextIO, Union
 
 import pytorch_lightning as pl
 import torch
@@ -43,7 +43,7 @@ def write_on_epoch_end(
         predictions: Sequence[Any],
         batch_indices: Optional[Sequence[Any]] = None,
     ) -> None:
-        sentences: list[Sentence] = []
+        sentences: List[Sentence] = []
         dataloaders = trainer.predict_dataloaders
         for prediction in predictions:
             for batch_pred in prediction:
@@ -79,9 +79,9 @@ def write_on_epoch_end(
                     document = dataset.doc_id2document[example.doc_id]
                     for sentence in document.sentences:
                         Morpheme.count = 0
-                        morphemes: list[Morpheme] = []
+                        morphemes: List[Morpheme] = []
                         word_surf: str = ""
-                        word_norm_ops: list[str] = []
+                        word_norm_ops: List[str] = []
                         for char in sentence.text:
                             if word_segmenter_types[char_idx] == "B" and word_surf:
                                 word_norm = get_normalized(word_surf, word_norm_ops, strict=False)
diff --git a/src/kwja/callbacks/typo_module_writer.py b/src/kwja/callbacks/typo_module_writer.py
index b8f92e53..7d62f0a3 100644
--- a/src/kwja/callbacks/typo_module_writer.py
+++ b/src/kwja/callbacks/typo_module_writer.py
@@ -2,7 +2,7 @@
 import sys
 from io import TextIOBase
 from pathlib import Path
-from typing import Any, Optional, Sequence, TextIO, Union
+from typing import Any, Dict, List, Optional, Sequence, TextIO, Tuple, Union
 
 import pytorch_lightning as pl
 import torch
@@ -44,9 +44,9 @@ def __init__(
 
         self.opn2id, self.id2opn = self.get_opn_dict(path=Path(extended_vocab_path))
 
-    def get_opn_dict(self, path: Path) -> tuple[dict[str, int], dict[int, str]]:
-        opn2id: dict[str, int] = self.tokenizer.get_vocab()
-        id2opn: dict[int, str] = {idx: opn for opn, idx in opn2id.items()}
+    def get_opn_dict(self, path: Path) -> Tuple[Dict[str, int], Dict[int, str]]:
+        opn2id: Dict[str, int] = self.tokenizer.get_vocab()
+        id2opn: Dict[int, str] = {idx: opn for opn, idx in opn2id.items()}
         with path.open(mode="r") as f:
             for line in f:
                 opn = str(line.strip())
@@ -54,10 +54,10 @@ def get_opn_dict(self, path: Path) -> tuple[dict[str, int], dict[int, str]]:
                 id2opn[len(id2opn)] = opn
         return opn2id, id2opn
 
-    def convert_id2opn(self, opn_ids_list: list[list[int]], opn_prefix: str) -> list[list[str]]:
-        opns_list: list[list[str]] = []
+    def convert_id2opn(self, opn_ids_list: List[List[int]], opn_prefix: str) -> List[List[str]]:
+        opns_list: List[List[str]] = []
         for opn_ids in opn_ids_list:
-            opns: list[str] = []
+            opns: List[str] = []
             for opn_id in opn_ids:
                 opn: str = self.id2opn[opn_id]
                 opns.append(TOKEN2TYPO_OPN.get(opn, f"{opn_prefix}:{opn}"))
@@ -65,33 +65,33 @@ def convert_id2opn(self, opn_ids_list: list[list[int]], opn_prefix: str) -> list
         return opns_list
 
     @staticmethod
-    def apply_opn(pre_text: str, kdrs: list[str], inss: list[str]) -> str:
+    def apply_opn(pre_text: str, kdrs: List[str], inss: List[str]) -> str:
         post_text = ""
         assert len(pre_text) + 1 == len(kdrs) + 1 == len(inss)
         for char_idx, char in enumerate(pre_text):
             # insert
             if inss[char_idx] != "_":
-                post_text += inss[char_idx].removeprefix("I:")
+                post_text += inss[char_idx][2:]  # remove prefix "I:"
             # keep, delete, replace
             if kdrs[char_idx] == "K":
                 post_text += char
             elif kdrs[char_idx] == "D":
                 pass
             elif kdrs[char_idx].startswith("R:"):
-                post_text += kdrs[char_idx].removeprefix("R:")
+                post_text += kdrs[char_idx][2:]  # remove prefix "R:"
             else:
                 raise ValueError("unsupported operation!")
         if inss[-1] != "_":
-            post_text += inss[-1].removeprefix("I:")
+            post_text += inss[-1][2:]  # remove prefix "I:"
         return post_text
 
     def get_opn_ids_list(
         self, batch_values: torch.Tensor, batch_indices: torch.Tensor, opn_prefix: str
-    ) -> list[list[int]]:
+    ) -> List[List[int]]:
         # Do not edit if the operation probability (replace, delete, and insert) is less than "confidence_threshold"
-        opn_ids_list: list[list[int]] = []
+        opn_ids_list: List[List[int]] = []
         for values, indices in zip(batch_values.tolist(), batch_indices.tolist()):
-            opn_ids: list[int] = []
+            opn_ids: List[int] = []
             for value, index in zip(values, indices):
                 if opn_prefix == "R" and value < self.confidence_threshold:
                     opn_ids.append(self.opn2id["<k>"])
@@ -112,7 +112,7 @@ def write_on_epoch_end(
         results = []
         for prediction in predictions:
             for batch_pred in prediction:
-                kdr_preds: list[list[str]] = self.convert_id2opn(
+                kdr_preds: List[List[str]] = self.convert_id2opn(
                     opn_ids_list=self.get_opn_ids_list(
                         batch_values=batch_pred["kdr_values"],
                         batch_indices=batch_pred["kdr_indices"],
@@ -120,7 +120,7 @@ def write_on_epoch_end(
                     ),
                     opn_prefix="R",
                 )
-                ins_preds: list[list[str]] = self.convert_id2opn(
+                ins_preds: List[List[str]] = self.convert_id2opn(
                     opn_ids_list=self.get_opn_ids_list(
                         batch_values=batch_pred["ins_values"],
                         batch_indices=batch_pred["ins_indices"],
diff --git a/src/kwja/callbacks/word_module_discourse_writer.py b/src/kwja/callbacks/word_module_discourse_writer.py
index f83cf771..eaa3b2f9 100644
--- a/src/kwja/callbacks/word_module_discourse_writer.py
+++ b/src/kwja/callbacks/word_module_discourse_writer.py
@@ -3,7 +3,7 @@
 import sys
 from io import TextIOBase
 from pathlib import Path
-from typing import Any, Optional, Sequence, TextIO, Union
+from typing import Any, List, Optional, Sequence, TextIO, Union
 
 import pytorch_lightning as pl
 import torch
@@ -44,7 +44,7 @@ def write_on_epoch_end(
         predictions: Sequence[Any],
         batch_indices: Optional[Sequence[Any]] = None,
     ) -> None:
-        sentences: list[Sentence] = []
+        sentences: List[Sentence] = []
         dataloaders = trainer.predict_dataloaders
         for prediction in predictions:
             for batch_pred in prediction:
@@ -73,7 +73,7 @@ def write_on_epoch_end(
             self.destination.write(output_string)
 
     @staticmethod
-    def _add_discourse(document: Document, discourse_preds: list[list[int]]) -> None:
+    def _add_discourse(document: Document, discourse_preds: List[List[int]]) -> None:
         if document.need_clause_tag:
             logger.warning("failed to output clause boundaries")
             return
diff --git a/src/kwja/callbacks/word_module_writer.py b/src/kwja/callbacks/word_module_writer.py
index f0e6eba1..1bb4690a 100644
--- a/src/kwja/callbacks/word_module_writer.py
+++ b/src/kwja/callbacks/word_module_writer.py
@@ -5,7 +5,7 @@
 import sys
 from io import TextIOBase
 from pathlib import Path
-from typing import Any, Optional, Sequence, TextIO, Union
+from typing import Any, Dict, List, Optional, Sequence, Set, TextIO, Tuple, Union
 
 import pytorch_lightning as pl
 import torch
@@ -52,7 +52,7 @@ def __init__(
         output_dir: str,
         reading_resource_path: str,
         jumandic_path: str,
-        ambig_surf_specs: list[dict[str, str]],
+        ambig_surf_specs: List[Dict[str, str]],
         pred_filename: str = "predict",
         use_stdout: bool = False,
     ) -> None:
@@ -68,6 +68,7 @@ def __init__(
             ensure_ascii=False,
             access_mode="r",
             storage=CachingMiddleware(BetterJSONStorage),
+            cache_size=0,
         )
         self.ambig_surf_specs = ambig_surf_specs
 
@@ -87,7 +88,7 @@ def write_on_epoch_end(
         predictions: Sequence[Any],
         batch_indices: Optional[Sequence[Any]] = None,
     ) -> None:
-        sentences: list[Sentence] = []
+        sentences: List[Sentence] = []
         dataloaders = trainer.predict_dataloaders
         for prediction in predictions:
             for batch_pred in prediction:
@@ -192,14 +193,14 @@ def write_on_epoch_end(
 
     def _create_morphemes(
         self,
-        words: list[str],
-        norms: list[str],
-        reading_preds: list[str],
-        pos_preds: list[int],
-        subpos_preds: list[int],
-        conjtype_preds: list[int],
-        conjform_preds: list[int],
-    ) -> list[Morpheme]:
+        words: List[str],
+        norms: List[str],
+        reading_preds: List[str],
+        pos_preds: List[int],
+        subpos_preds: List[int],
+        conjtype_preds: List[int],
+        conjform_preds: List[int],
+    ) -> List[Morpheme]:
         morphemes = []
         for word, norm, reading, pos_index, subpos_index, conjtype_index, conjform_index in zip(
             words, norms, reading_preds, pos_preds, subpos_preds, conjtype_preds, conjform_preds
@@ -212,8 +213,8 @@ def _create_morphemes(
             conjtype_id = conjtype_index
             conjform = INDEX2CONJFORM_TYPE[conjform_index]
             conjform_id = CONJTYPE_CONJFORM_TYPE2ID[conjtype][conjform]
-            semantics: dict[str, Union[str, bool]] = {}
-            homograph_ops: list[dict[str, Any]] = []
+            semantics: Dict[str, Union[str, bool]] = {}
+            homograph_ops: List[Dict[str, Any]] = []
 
             # create lemma using norm, conjtype and conjform
             if conjtype == "*":
@@ -230,10 +231,10 @@ def _create_morphemes(
                         (q.pos == pos) & (q.subpos == subpos) & (q.conjtype == conjtype) & (q.surf == norm)
                     )
                     if len(matches) > 0:
-                        lemma_set: set[str] = set()
+                        lemma_set: Set[str] = set()
                         for entry in matches:
                             lemma_set.add(entry["lemma"])
-                        lemmas: list[str] = list(lemma_set)
+                        lemmas: List[str] = list(lemma_set)
                         lemma = lemmas[0]
                         if len(lemmas) > 1:
                             homograph_ops.append({"type": "lemma", "values": lemmas[1:]})
@@ -263,7 +264,7 @@ def _create_morphemes(
                     # homograph
                     semantics_list = []
                     for entry2 in matches[1:]:
-                        semantics2: dict[str, Union[str, bool]] = {}
+                        semantics2: Dict[str, Union[str, bool]] = {}
                         semantics2.update(SemanticsDict.from_sstring('"' + entry2["semantics"] + '"'))
                         semantics_list.append(semantics2)
                     homograph_ops.append(
@@ -315,14 +316,14 @@ def _get_mrph_type_preds(
         subpos_logits: torch.Tensor,
         conjtype_logits: torch.Tensor,
         conjform_logits: torch.Tensor,
-    ) -> tuple[list[list[int]], list[list[int]], list[list[int]], list[list[int]]]:
-        batch_pos_preds: list[list[int]] = torch.argmax(pos_logits, dim=-1).tolist()
-        batch_subpos_logits: list[list[list[float]]] = subpos_logits.tolist()
-        refined_batch_subpos_preds: list[list[int]] = []
+    ) -> Tuple[List[List[int]], List[List[int]], List[List[int]], List[List[int]]]:
+        batch_pos_preds: List[List[int]] = torch.argmax(pos_logits, dim=-1).tolist()
+        batch_subpos_logits: List[List[List[float]]] = subpos_logits.tolist()
+        refined_batch_subpos_preds: List[List[int]] = []
         for batch_idx, batch_pos_pred in enumerate(batch_pos_preds):
-            refined_subpos_preds: list[int] = []
+            refined_subpos_preds: List[int] = []
             for pos_idx, pos in enumerate(batch_pos_pred):
-                possible_subpos_ids: set[int] = {
+                possible_subpos_ids: Set[int] = {
                     SUBPOS_TYPES.index(x) for x in POS_SUBPOS_TYPE2ID[INDEX2POS_TYPE[pos]].keys()
                 }
                 refined_subpos_pred: int = 0
@@ -334,13 +335,13 @@ def _get_mrph_type_preds(
                 refined_subpos_preds.append(refined_subpos_pred)
             refined_batch_subpos_preds.append(refined_subpos_preds)
 
-        batch_conjtype_preds: list[list[int]] = torch.argmax(conjtype_logits, dim=-1).tolist()
-        batch_conjform_logits: list[list[list[float]]] = conjform_logits.tolist()
-        refined_batch_conjform_preds: list[list[int]] = []
+        batch_conjtype_preds: List[List[int]] = torch.argmax(conjtype_logits, dim=-1).tolist()
+        batch_conjform_logits: List[List[List[float]]] = conjform_logits.tolist()
+        refined_batch_conjform_preds: List[List[int]] = []
         for batch_idx, batch_conjtype_pred in enumerate(batch_conjtype_preds):
-            refined_conjform_preds: list[int] = []
+            refined_conjform_preds: List[int] = []
             for conjtype_idx, conjtype in enumerate(batch_conjtype_pred):
-                possible_conjform_ids: set[int] = {
+                possible_conjform_ids: Set[int] = {
                     CONJFORM_TYPES.index(x) for x in CONJTYPE_CONJFORM_TYPE2ID[INDEX2CONJTYPE_TYPE[conjtype]].keys()
                 }
                 refined_conjform_pred: int = 0
@@ -356,7 +357,7 @@ def _get_mrph_type_preds(
 
     @staticmethod
     def _chunk_morphemes(
-        document: Document, morphemes: list[Morpheme], word_feature_preds: list[list[float]]
+        document: Document, morphemes: List[Morpheme], word_feature_preds: List[List[float]]
     ) -> Document:
         assert len(morphemes) <= len(word_feature_preds)
         sentences = []
@@ -411,9 +412,9 @@ def _chunk_morphemes(
         return Document.from_sentences(sentences)
 
     @staticmethod
-    def _add_base_phrase_features(document: Document, base_phrase_feature_preds: list[list[float]]) -> None:
+    def _add_base_phrase_features(document: Document, base_phrase_feature_preds: List[List[float]]) -> None:
         base_phrases = document.base_phrases
-        clause_start_index_set: set[int] = {0}
+        clause_start_index_set: Set[int] = {0}
         for base_phrase in base_phrases:
             for feature, pred in zip(BASE_PHRASE_FEATURES, base_phrase_feature_preds[base_phrase.head.global_index]):
                 if feature != "節-主辞" and pred >= 0.5:
@@ -423,7 +424,7 @@ def _add_base_phrase_features(document: Document, base_phrase_feature_preds: lis
                         clause_start_index_set.add(base_phrase.global_index + 1)
         if base_phrases[-1].features.get("節-区切", False) is False:
             clause_start_index_set.add(len(base_phrases))
-        clause_start_indices: list[int] = sorted(clause_start_index_set)
+        clause_start_indices: List[int] = sorted(clause_start_index_set)
 
         for span in zip(clause_start_indices[:-1], clause_start_indices[1:]):
             clause = base_phrases[slice(*span)]
@@ -435,7 +436,7 @@ def _add_base_phrase_features(document: Document, base_phrase_feature_preds: lis
             clause_head.features["節-主辞"] = True
 
     @staticmethod
-    def _add_named_entities(document: Document, ne_tag_preds: list[int]) -> None:
+    def _add_named_entities(document: Document, ne_tag_preds: List[int]) -> None:
         for sentence in document.sentences:
             morphemes = sentence.morphemes
             category = ""
@@ -484,9 +485,9 @@ def _resolve_dependency(base_phrase: BasePhrase, dependency_manager: DependencyM
     def _add_dependency(
         self,
         document: Document,
-        dependency_preds: list[list[int]],
-        dependency_type_preds: list[list[int]],
-        special_to_index: dict[str, int],
+        dependency_preds: List[List[int]],
+        dependency_type_preds: List[List[int]],
+        special_to_index: Dict[str, int],
     ) -> None:
         for sentence in extract_target_sentences(document):
             base_phrases = sentence.base_phrases
@@ -529,11 +530,11 @@ def _add_dependency(
     def _add_cohesion(
         self,
         document: Document,
-        cohesion_preds: list[list[int]],
-        task_to_rel_types: dict[CohesionTask, list[str]],
-        exophora_referents: list[ExophoraReferent],
-        index_to_special: dict[int, str],
-        task_to_extractors: dict[CohesionTask, Extractor],
+        cohesion_preds: List[List[int]],
+        task_to_rel_types: Dict[CohesionTask, List[str]],
+        exophora_referents: List[ExophoraReferent],
+        index_to_special: Dict[int, str],
+        task_to_extractors: Dict[CohesionTask, Extractor],
     ) -> None:
         all_rel_types = [t for ts in task_to_rel_types.values() for t in ts]
         for base_phrase in document.base_phrases:
@@ -551,7 +552,7 @@ def _add_cohesion(
                     base_phrase.rels += [rel for rel in rel_tags if rel.type in rel_types]
 
     @staticmethod
-    def _add_discourse(document: Document, discourse_preds: list[list[int]]) -> None:
+    def _add_discourse(document: Document, discourse_preds: List[List[int]]) -> None:
         if document.need_clause_tag:
             logger.warning("failed to output clause boundaries")
             return
@@ -570,11 +571,11 @@ def _add_discourse(document: Document, discourse_preds: list[list[int]]) -> None
 
     @staticmethod
     def _to_rels(
-        prediction: list[int],  # (rel)
-        morphemes: list[Morpheme],
-        rel_types: list[str],
-        exophora_referents: list[ExophoraReferent],
-        index_to_special: dict[int, str],
+        prediction: List[int],  # (rel)
+        morphemes: List[Morpheme],
+        rel_types: List[str],
+        exophora_referents: List[ExophoraReferent],
+        index_to_special: Dict[int, str],
     ) -> RelTagList:
         rel_tags = RelTagList()
         assert len(rel_types) == len(prediction)
diff --git a/src/kwja/cli/cli.py b/src/kwja/cli/cli.py
index 9ca43fad..6c037be6 100644
--- a/src/kwja/cli/cli.py
+++ b/src/kwja/cli/cli.py
@@ -1,9 +1,9 @@
-from importlib import resources
 from pathlib import Path
 from tempfile import TemporaryDirectory
-from typing import Optional
+from typing import List, Optional
 
 import hydra
+import importlib_resources
 import pytorch_lightning as pl
 import torch
 import typer
@@ -13,8 +13,7 @@
 
 import kwja
 from kwja.callbacks.word_module_discourse_writer import WordModuleDiscourseWriter
-from kwja.callbacks.word_module_writer import WordModuleWriter
-from kwja.cli.utils import download_checkpoint_from_url, suppress_debug_info
+from kwja.cli.utils import download_checkpoint_from_url, prepare_device, suppress_debug_info
 from kwja.datamodule.datamodule import DataModule
 from kwja.models.char_module import CharModule
 from kwja.models.typo_module import TypoModule
@@ -30,170 +29,276 @@
 OmegaConf.register_new_resolver("concat", lambda x, y: x + y)
 
 app = typer.Typer(pretty_exceptions_show_locals=False)
+resource_path = importlib_resources.files(kwja) / "resource"
 
 
-def version_callback(value: bool) -> None:
-    if value is True:
-        typer.echo(f"KWJA {kwja.__version__}")
-        raise typer.Exit()
+class CLIProcessor:
+    def __init__(self, specified_device: str) -> None:
+        self.device_name, self.device = prepare_device(specified_device)
 
+        self.tmp_dir: TemporaryDirectory = TemporaryDirectory()
+        self.typo_path: Path = self.tmp_dir.name / Path("predict_typo.txt")
+        self.char_path: Path = self.tmp_dir.name / Path("predict_char.juman")
+        self.word_path: Path = self.tmp_dir.name / Path("predict_word.knp")
+        self.word_discourse_path: Path = self.tmp_dir.name / Path("predict_word_discourse.knp")
 
-@app.command()
-def main(
-    text: Optional[str] = typer.Option(None, help="Text to be analyzed."),
-    filename: Optional[Path] = typer.Option(None, help="File to be analyzed."),
-    discourse: Optional[bool] = typer.Option(True, help="Whether to perform discourse relation analysis."),
-    version: Optional[bool] = typer.Option(None, "--version", callback=version_callback, is_eager=True),
-) -> None:
-    assert version is None
-    if text is not None and filename is not None:
-        typer.echo("ERROR: Please provide text or filename, not both", err=True)
-        raise typer.Exit()
-    elif text is not None:
-        input_texts: list[str] = text.splitlines()
-    elif filename is not None:
-        with Path(filename).open() as f:
-            input_texts = [line.strip() for line in f]
-    else:
-        typer.echo("ERROR: Please provide text or filename", err=True)
-        raise typer.Exit
-
-    tmp_dir: TemporaryDirectory = TemporaryDirectory()
-    typo_path: Path = tmp_dir.name / Path("predict_typo.txt")
-    char_path: Path = tmp_dir.name / Path("predict_char.juman")
-    word_path: Path = tmp_dir.name / Path("predict_word.knp")
-    word_discourse_path: Path = tmp_dir.name / Path("predict_word_discourse.knp")
-
-    # typo module
-    typo_checkpoint_path: Path = download_checkpoint_from_url(TYPO_CHECKPOINT_URL)
-    typo_model: TypoModule = TypoModule.load_from_checkpoint(str(typo_checkpoint_path))
-    typo_cfg = typo_model.hparams
-    extended_vocab_path = resources.files("kwja") / "resource/typo_correction/multi_char_vocab.txt"
-    typo_cfg.datamodule.predict.extended_vocab_path = str(extended_vocab_path)
-    typo_cfg.dataset.extended_vocab_path = str(extended_vocab_path)
-    typo_cfg.callbacks.prediction_writer.extended_vocab_path = str(extended_vocab_path)
-    typo_trainer: pl.Trainer = pl.Trainer(
-        logger=False,
-        enable_progress_bar=False,
-        callbacks=[
-            hydra.utils.instantiate(
-                typo_cfg.callbacks.prediction_writer,
-                output_dir=str(tmp_dir.name),
-                pred_filename=typo_path.stem,
-            )
-        ],
-        devices=1,
-    )
-    typo_cfg.datamodule.predict.texts = input_texts
-    typo_datamodule = DataModule(cfg=typo_cfg.datamodule)
-    typo_datamodule.setup(stage=TrainerFn.PREDICTING)
-    typo_trainer.predict(model=typo_model, dataloaders=typo_datamodule.predict_dataloader())
-    del typo_model
-
-    # char module
-    char_checkpoint_path: Path = download_checkpoint_from_url(CHAR_CHECKPOINT_URL)
-    char_model: CharModule = CharModule.load_from_checkpoint(str(char_checkpoint_path))
-    char_cfg = char_model.hparams
-    char_trainer: pl.Trainer = pl.Trainer(
-        logger=False,
-        enable_progress_bar=False,
-        callbacks=[
-            hydra.utils.instantiate(
-                char_cfg.callbacks.prediction_writer,
-                output_dir=str(tmp_dir.name),
-                pred_filename=char_path.stem,
-            )
-        ],
-        devices=1,
-    )
-    char_cfg.datamodule.predict.texts = typo_path.read_text().splitlines()
-    char_datamodule = DataModule(cfg=char_cfg.datamodule)
-    char_datamodule.setup(stage=TrainerFn.PREDICTING)
-    char_trainer.predict(model=char_model, dataloaders=char_datamodule.predict_dataloader())
-    del char_model
-
-    # word module
-    word_checkpoint_path: Path = download_checkpoint_from_url(WORD_CHECKPOINT_URL)
-    word_checkpoint = torch.load(str(word_checkpoint_path), map_location=lambda storage, loc: storage)
-    hparams = word_checkpoint["hyper_parameters"]["hparams"]
-    reading_resource_path = resources.files("kwja") / "resource/reading_prediction"
-    hparams.datamodule.predict.reading_resource_path = reading_resource_path
-    hparams.dataset.reading_resource_path = reading_resource_path
-    hparams.callbacks.prediction_writer.reading_resource_path = reading_resource_path
-    hparams.callbacks.prediction_writer.jumandic_path = resources.files("kwja") / "resource/jumandic"
-    word_model: WordModule = WordModule.load_from_checkpoint(str(word_checkpoint_path), hparams=hparams)
-    word_cfg = word_model.hparams
-    word_cfg.datamodule.predict.reading_resource_path = reading_resource_path
-    word_cfg.dataset.reading_resource_path = reading_resource_path
-    word_cfg.callbacks.prediction_writer.reading_resource_path = reading_resource_path
-    word_cfg.callbacks.prediction_writer.jumandic_path = resources.files("kwja") / "resource/jumandic"
-    word_trainer: pl.Trainer = pl.Trainer(
-        logger=False,
-        enable_progress_bar=False,
-        callbacks=[
-            hydra.utils.instantiate(
-                word_cfg.callbacks.prediction_writer,
-                output_dir=str(tmp_dir.name),
-                pred_filename=word_path.stem,
-            )
-        ],
-        devices=1,
-    )
-    word_cfg.datamodule.predict.juman_file = char_path
-    word_datamodule = DataModule(cfg=word_cfg.datamodule)
-    word_datamodule.setup(stage=TrainerFn.PREDICTING)
-    word_trainer.predict(model=word_model, dataloaders=word_datamodule.predict_dataloader())
-    word_module_writer: WordModuleWriter = word_trainer.callbacks[0]
-    # word module (discourse) cannot be initialized unless this is written because multiple tinyDBs cannot be opened.
-    word_module_writer.jumandic.close()
-    del word_model
-    document: Document = Document.from_knp(word_path.read_text())
-    # Remove the result of discourse relation analysis by the jointly learned model.
-    for base_phrase in document.base_phrases:
-        if "談話関係" in base_phrase.features:
-            del base_phrase.features["談話関係"]
-    if not discourse:
-        print(document.to_knp(), end="")
-    else:
-        # word module (discourse)
+        self.typo_model: Optional[TypoModule] = None
+        self.typo_trainer: Optional[pl.Trainer] = None
+        self.char_model: Optional[CharModule] = None
+        self.char_trainer: Optional[pl.Trainer] = None
+        self.word_model: Optional[WordModule] = None
+        self.word_trainer: Optional[pl.Trainer] = None
+        self.word_discourse_model: Optional[WordModule] = None
+        self.word_discourse_trainer: Optional[pl.Trainer] = None
+
+    def load_typo(self) -> None:
+        typo_checkpoint_path: Path = download_checkpoint_from_url(TYPO_CHECKPOINT_URL)
+        self.typo_model = TypoModule.load_from_checkpoint(
+            str(typo_checkpoint_path),
+            map_location=self.device,
+        )
+        extended_vocab_path = resource_path / "typo_correction/multi_char_vocab.txt"
+        if self.typo_model is None:
+            raise ValueError("typo model does not exist")
+        self.typo_model.hparams.datamodule.predict.extended_vocab_path = str(extended_vocab_path)
+        self.typo_model.hparams.dataset.extended_vocab_path = str(extended_vocab_path)
+        self.typo_model.hparams.callbacks.prediction_writer.extended_vocab_path = str(extended_vocab_path)
+        self.typo_trainer = pl.Trainer(
+            logger=False,
+            enable_progress_bar=False,
+            callbacks=[
+                hydra.utils.instantiate(
+                    self.typo_model.hparams.callbacks.prediction_writer,
+                    output_dir=str(self.tmp_dir.name),
+                    pred_filename=self.typo_path.stem,
+                )
+            ],
+            accelerator=self.device_name,
+            devices=1,
+        )
+
+    def apply_typo(self, input_texts: List[str]) -> None:
+        if self.typo_model is None:
+            raise ValueError("typo model does not exist")
+        self.typo_model.hparams.datamodule.predict.texts = input_texts
+        typo_datamodule = DataModule(cfg=self.typo_model.hparams.datamodule)
+        typo_datamodule.setup(stage=TrainerFn.PREDICTING)
+        if self.typo_trainer is None:
+            raise ValueError("typo trainer does not exist")
+        self.typo_trainer.predict(model=self.typo_model, dataloaders=typo_datamodule.predict_dataloader())
+
+    def del_typo(self) -> None:
+        del self.typo_model, self.typo_trainer
+
+    def load_char(self) -> None:
+        char_checkpoint_path: Path = download_checkpoint_from_url(CHAR_CHECKPOINT_URL)
+        self.char_model = CharModule.load_from_checkpoint(
+            str(char_checkpoint_path),
+            map_location=self.device,
+        )
+        if self.char_model is None:
+            raise ValueError("char model does not exist")
+        self.char_trainer = pl.Trainer(
+            logger=False,
+            enable_progress_bar=False,
+            callbacks=[
+                hydra.utils.instantiate(
+                    self.char_model.hparams.callbacks.prediction_writer,
+                    output_dir=str(self.tmp_dir.name),
+                    pred_filename=self.char_path.stem,
+                )
+            ],
+            accelerator=self.device_name,
+            devices=1,
+        )
+
+    def apply_char(self) -> None:
+        if self.char_model is None:
+            raise ValueError("char model does not exist")
+        self.char_model.hparams.datamodule.predict.texts = self.typo_path.read_text().splitlines()
+        char_datamodule = DataModule(cfg=self.char_model.hparams.datamodule)
+        char_datamodule.setup(stage=TrainerFn.PREDICTING)
+        if self.char_trainer is None:
+            raise ValueError("char trainer does not exist")
+        self.char_trainer.predict(model=self.char_model, dataloaders=char_datamodule.predict_dataloader())
+
+    def del_char(self) -> None:
+        del self.char_model, self.char_trainer
+
+    def load_word(self) -> None:
+        word_checkpoint_path: Path = download_checkpoint_from_url(WORD_CHECKPOINT_URL)
+        word_checkpoint = torch.load(str(word_checkpoint_path), map_location=lambda storage, loc: storage)
+        hparams = word_checkpoint["hyper_parameters"]["hparams"]
+        reading_resource_path = resource_path / "reading_prediction"
+        jumandic_path = resource_path / "jumandic"
+        hparams.datamodule.predict.reading_resource_path = reading_resource_path
+        hparams.dataset.reading_resource_path = reading_resource_path
+        hparams.callbacks.prediction_writer.reading_resource_path = reading_resource_path
+        hparams.callbacks.prediction_writer.jumandic_path = jumandic_path
+        self.word_model = WordModule.load_from_checkpoint(
+            str(word_checkpoint_path),
+            hparams=hparams,
+            map_location=self.device,
+        )
+        if self.word_model is None:
+            raise ValueError("word model does not exist")
+        self.word_model.hparams.datamodule.predict.reading_resource_path = reading_resource_path
+        self.word_model.hparams.dataset.reading_resource_path = reading_resource_path
+        self.word_model.hparams.callbacks.prediction_writer.reading_resource_path = reading_resource_path
+        self.word_model.hparams.callbacks.prediction_writer.jumandic_path = jumandic_path
+        self.word_trainer = pl.Trainer(
+            logger=False,
+            enable_progress_bar=False,
+            callbacks=[
+                hydra.utils.instantiate(
+                    self.word_model.hparams.callbacks.prediction_writer,
+                    output_dir=str(self.tmp_dir.name),
+                    pred_filename=self.word_path.stem,
+                )
+            ],
+            accelerator=self.device_name,
+            devices=1,
+        )
+
+    def apply_word(self) -> None:
+        if self.word_model is None:
+            raise ValueError("word model does not exist")
+        self.word_model.hparams.datamodule.predict.juman_file = self.char_path
+        word_datamodule = DataModule(cfg=self.word_model.hparams.datamodule)
+        word_datamodule.setup(stage=TrainerFn.PREDICTING)
+        if self.word_trainer is None:
+            raise ValueError("word trainer does not exist")
+        self.word_trainer.predict(model=self.word_model, dataloaders=word_datamodule.predict_dataloader())
+
+    def del_word(self) -> None:
+        del self.word_model, self.word_trainer
+
+    def load_word_discourse(self) -> None:
         word_discourse_checkpoint_path: Path = download_checkpoint_from_url(WORD_DISCOURSE_CHECKPOINT_URL)
         word_discourse_checkpoint = torch.load(
             str(word_discourse_checkpoint_path), map_location=lambda storage, loc: storage
         )
         hparams = word_discourse_checkpoint["hyper_parameters"]["hparams"]
-        reading_resource_path = resources.files("kwja") / "resource/reading_prediction"
+        reading_resource_path = resource_path / "reading_prediction"
+        jumandic_path = resource_path / "jumandic"
         hparams.datamodule.predict.reading_resource_path = reading_resource_path
         hparams.dataset.reading_resource_path = reading_resource_path
         hparams.callbacks.prediction_writer.reading_resource_path = reading_resource_path
-        hparams.callbacks.prediction_writer.jumandic_path = resources.files("kwja") / "resource/jumandic"
-        word_discourse_model: WordModule = WordModule.load_from_checkpoint(
-            str(word_discourse_checkpoint_path), hparams=hparams
+        hparams.callbacks.prediction_writer.jumandic_path = jumandic_path
+        self.word_discourse_model = WordModule.load_from_checkpoint(
+            str(word_discourse_checkpoint_path),
+            hparams=hparams,
+            map_location=self.device,
         )
-        word_discourse_cfg = word_discourse_model.hparams
-        word_discourse_cfg.datamodule.predict.reading_resource_path = reading_resource_path
-        word_discourse_cfg.dataset.reading_resource_path = reading_resource_path
-
-        word_discourse_trainer: pl.Trainer = pl.Trainer(
+        if self.word_discourse_model is None:
+            raise ValueError("word discourse model does not exist")
+        self.word_discourse_model.hparams.datamodule.predict.reading_resource_path = reading_resource_path
+        self.word_discourse_model.hparams.dataset.reading_resource_path = reading_resource_path
+        self.word_discourse_trainer = pl.Trainer(
             logger=False,
             enable_progress_bar=False,
             callbacks=[
                 WordModuleDiscourseWriter(
-                    output_dir=str(tmp_dir.name),
-                    pred_filename=word_discourse_path.stem,
+                    output_dir=str(self.tmp_dir.name),
+                    pred_filename=self.word_discourse_path.stem,
                 )
             ],
+            accelerator=self.device_name,
             devices=1,
         )
-        word_discourse_cfg.datamodule.predict.knp_file = word_path
-        word_discourse_datamodule = DataModule(cfg=word_discourse_cfg.datamodule)
+
+    def apply_word_discourse(self) -> None:
+        if self.word_discourse_model is None:
+            raise ValueError("word discourse model does not exist")
+        self.word_discourse_model.hparams.datamodule.predict.knp_file = self.word_path
+        word_discourse_datamodule = DataModule(cfg=self.word_discourse_model.hparams.datamodule)
         word_discourse_datamodule.setup(stage=TrainerFn.PREDICTING)
-        word_discourse_trainer.predict(
-            model=word_discourse_model,
+        if self.word_discourse_trainer is None:
+            raise ValueError("word discourse trainer does not exist")
+        self.word_discourse_trainer.predict(
+            model=self.word_discourse_model,
             dataloaders=word_discourse_datamodule.predict_dataloader(),
         )
-        discourse_document: Document = Document.from_knp(word_discourse_path.read_text())
+
+    def output_word_result(self) -> None:
+        document: Document = Document.from_knp(self.word_path.read_text())
+        # Remove the result of discourse relation analysis by the jointly learned model.
+        for base_phrase in document.base_phrases:
+            if "談話関係" in base_phrase.features:
+                del base_phrase.features["談話関係"]
+        print(document.to_knp(), end="")
+
+    def output_word_discourse_result(self) -> None:
+        discourse_document: Document = Document.from_knp(self.word_discourse_path.read_text())
         print(discourse_document.to_knp(), end="")
-    tmp_dir.cleanup()
+
+
+def version_callback(value: bool) -> None:
+    if value is True:
+        typer.echo(f"KWJA {kwja.__version__}")
+        raise typer.Exit()
+
+
+@app.command()
+def main(
+    text: Optional[str] = typer.Option(None, help="Text to be analyzed."),
+    filename: Optional[Path] = typer.Option(None, help="File to be analyzed."),
+    device: str = typer.Option("cpu", help="Device to be used. Please specify 'cpu' or 'gpu'."),
+    discourse: Optional[bool] = typer.Option(True, help="Whether to perform discourse relation analysis."),
+    _: Optional[bool] = typer.Option(None, "--version", callback=version_callback, is_eager=True),
+) -> None:
+    input_text = ""
+    if text is not None and filename is not None:
+        typer.echo("ERROR: Please provide text or filename, not both", err=True)
+        raise typer.Exit()
+    elif text is not None:
+        input_text = text
+    elif filename is not None:
+        with Path(filename).open() as f:
+            input_text = f.read()
+
+    processor = CLIProcessor(specified_device=device)
+    if input_text:
+        processor.load_typo()
+        processor.apply_typo([input_text])
+        processor.del_typo()
+
+        processor.load_char()
+        processor.apply_char()
+        processor.del_char()
+
+        processor.load_word()
+        processor.apply_word()
+        processor.del_word()
+
+        if not discourse:
+            processor.output_word_result()
+        else:
+            processor.load_word_discourse()
+            processor.apply_word_discourse()
+            processor.output_word_discourse_result()
+    else:
+        typer.echo('Please end your input with a new line and type "EOD"')
+        processor.load_typo()
+        processor.load_char()
+        processor.load_word()
+        if discourse:
+            processor.load_word_discourse()
+
+        while True:
+            inp = input()
+            if inp == "EOD":
+                processor.apply_typo([input_text])
+                processor.apply_char()
+                processor.apply_word()
+                if not discourse:
+                    processor.output_word_result()
+                else:
+                    processor.apply_word_discourse()
+                    processor.output_word_discourse_result()
+                input_text = ""
+            else:
+                input_text += inp + "\n"
 
 
 if __name__ == "__main__":
diff --git a/src/kwja/cli/utils.py b/src/kwja/cli/utils.py
index 939f229c..749b11ae 100644
--- a/src/kwja/cli/utils.py
+++ b/src/kwja/cli/utils.py
@@ -3,9 +3,10 @@
 import sys
 import warnings
 from pathlib import Path
-from typing import Optional, Union
+from typing import Optional, Tuple, Union
 from urllib.parse import urlparse
 
+import torch
 import transformers.utils.logging as hf_logging
 from torch.hub import download_url_to_file
 
@@ -59,3 +60,12 @@ def _get_kwja_cache_dir() -> Path:
     if path := os.getenv(ENV_XDG_CACHE_HOME):
         cache_dir = Path(path)
     return cache_dir / "kwja"
+
+
+def prepare_device(device_name: str) -> Tuple[str, torch.device]:
+    n_gpu = torch.cuda.device_count()
+    if device_name == "gpu" and n_gpu == 0:
+        print("Warning: There's no GPU available on this machine. Using CPU instead.")
+        return "cpu", torch.device("cpu")
+    else:
+        return device_name, torch.device("cuda:0" if device_name == "gpu" else "cpu")
diff --git a/src/kwja/datamodule/datamodule.py b/src/kwja/datamodule/datamodule.py
index e8ca0ec7..02e0f39a 100644
--- a/src/kwja/datamodule/datamodule.py
+++ b/src/kwja/datamodule/datamodule.py
@@ -1,4 +1,4 @@
-from typing import Optional, Union
+from typing import Dict, List, Optional, Union
 
 import hydra
 import pytorch_lightning as pl
@@ -24,8 +24,8 @@ def __init__(self, cfg: DictConfig) -> None:
         self.num_workers: int = cfg.num_workers
 
         self.train_dataset: Optional[ConcatDataset] = None
-        self.valid_datasets: dict[str, Union[TypoDataset, CharDataset, WordDataset]] = {}
-        self.test_datasets: dict[str, Union[TypoDataset, CharDataset, WordDataset]] = {}
+        self.valid_datasets: Dict[str, Union[TypoDataset, CharDataset, WordDataset]] = {}
+        self.test_datasets: Dict[str, Union[TypoDataset, CharDataset, WordDataset]] = {}
         self.predict_dataset: Optional[
             Union[
                 TypoDataset, CharDataset, WordDataset, TypoInferenceDataset, CharInferenceDataset, WordInferenceDataset
@@ -48,10 +48,10 @@ def setup(self, stage: Optional[str] = None) -> None:
     def train_dataloader(self) -> DataLoader:
         return self._get_dataloader(dataset=self.train_dataset, shuffle=True)
 
-    def val_dataloader(self) -> list[DataLoader]:
+    def val_dataloader(self) -> List[DataLoader]:
         return [self._get_dataloader(dataset, shuffle=False) for dataset in self.valid_datasets.values()]
 
-    def test_dataloader(self) -> list[DataLoader]:
+    def test_dataloader(self) -> List[DataLoader]:
         return [self._get_dataloader(dataset, shuffle=False) for dataset in self.test_datasets.values()]
 
     def predict_dataloader(self) -> DataLoader:
diff --git a/src/kwja/datamodule/datasets/base_dataset.py b/src/kwja/datamodule/datasets/base_dataset.py
index 40df7206..887e26a2 100644
--- a/src/kwja/datamodule/datasets/base_dataset.py
+++ b/src/kwja/datamodule/datasets/base_dataset.py
@@ -1,7 +1,7 @@
 import logging
 from functools import cached_property
 from pathlib import Path
-from typing import Union
+from typing import Dict, List, Union
 
 from rhoknp import Document, Sentence
 from torch.utils.data import Dataset
@@ -15,7 +15,7 @@
 class BaseDataset(Dataset):
     def __init__(
         self,
-        source: Union[Path, str, list[Document]],
+        source: Union[Path, str, List[Document]],
         document_split_stride: int,
         model_name_or_path: str,
         max_seq_length: int,
@@ -27,14 +27,14 @@ def __init__(
             **tokenizer_kwargs,
         )
         self.max_seq_length = max_seq_length
-        self.orig_documents: list[Document]
+        self.orig_documents: List[Document]
         if isinstance(source, (Path, str)):
             source = Path(source)
             assert source.is_dir()
             self.orig_documents = self._load_documents(source, ext)
         else:
             self.orig_documents = source
-        self.doc_id2document: dict[str, Document] = {}
+        self.doc_id2document: Dict[str, Document] = {}
         for orig_document in self.orig_documents:
             self.doc_id2document.update(
                 {
@@ -48,11 +48,11 @@ def __init__(
             )
 
     @cached_property
-    def documents(self) -> list[Document]:
+    def documents(self) -> List[Document]:
         return list(self.doc_id2document.values())
 
     @staticmethod
-    def _load_documents(document_dir: Path, ext: str = "knp") -> list[Document]:
+    def _load_documents(document_dir: Path, ext: str = "knp") -> List[Document]:
         documents = []
         for path in sorted(document_dir.glob(f"*.{ext}")):
             # TODO: fix document files that raise exception
@@ -62,7 +62,7 @@ def _load_documents(document_dir: Path, ext: str = "knp") -> list[Document]:
                 logger.warning(f"{path} is not a valid knp file.")
         return documents
 
-    def _split_document(self, document: Document, max_token_length: int, stride: int) -> list[Document]:
+    def _split_document(self, document: Document, max_token_length: int, stride: int) -> List[Document]:
         cum_lens = [0]
         for sentence in document.sentences:
             num_tokens = self._get_tokenized_len(sentence)
@@ -75,7 +75,7 @@ def _split_document(self, document: Document, max_token_length: int, stride: int
         while end < len(document.sentences) and cum_lens[end + 1] - cum_lens[0] <= max_token_length:
             end += 1
 
-        sub_documents: list[Document] = []
+        sub_documents: List[Document] = []
         sub_idx = 0
         while end <= len(document.sentences):
             start = 0
@@ -88,9 +88,11 @@ def _split_document(self, document: Document, max_token_length: int, stride: int
             # TODO: fix rhoknp to keep comments in sentence
             comments = [s.comment for s in document.sentences[start:end]]
             sub_document = Document.from_sentences(document.sentences[start:end])
+            sub_doc_id = to_sub_doc_id(document.doc_id, sub_idx, stride=stride)
             for comment, sentence in zip(comments, sub_document.sentences):
                 sentence.comment = comment
-            sub_document.doc_id = to_sub_doc_id(document.doc_id, sub_idx, stride=stride)
+                sentence.doc_id = sub_doc_id
+            sub_document.doc_id = sub_doc_id
             sub_documents.append(sub_document)
             sub_idx += 1
             end += stride
diff --git a/src/kwja/datamodule/datasets/char_dataset.py b/src/kwja/datamodule/datasets/char_dataset.py
index 160ca37a..ba64cbe3 100644
--- a/src/kwja/datamodule/datasets/char_dataset.py
+++ b/src/kwja/datamodule/datasets/char_dataset.py
@@ -1,7 +1,7 @@
 import logging
 from dataclasses import dataclass
 from pathlib import Path
-from typing import Union
+from typing import Dict, List, Union
 
 import torch
 from rhoknp import Document, Sentence
@@ -46,15 +46,15 @@ def __init__(
         )
         self.denormalizer: SentenceDenormalizer = SentenceDenormalizer()
         self.denormalize_prob: float = denormalize_prob
-        self.examples: list[CharExampleSet] = self._load_examples(self.documents)
+        self.examples: List[CharExampleSet] = self._load_examples(self.documents)
 
     def __len__(self) -> int:
         return len(self.examples)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.examples[index])
 
-    def _load_examples(self, documents: list[Document]) -> list[CharExampleSet]:
+    def _load_examples(self, documents: List[Document]) -> List[CharExampleSet]:
         examples = []
         idx = 0
         for document in tqdm(documents, dynamic_ncols=True):
@@ -90,13 +90,13 @@ def _load_examples(self, documents: list[Document]) -> list[CharExampleSet]:
             )
         return examples
 
-    def encode(self, example: CharExampleSet) -> dict[str, torch.Tensor]:
+    def encode(self, example: CharExampleSet) -> Dict[str, torch.Tensor]:
         char_feature_example = example.char_feature_example
-        seg_types: list[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
+        seg_types: List[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
         for i, seg_label in char_feature_example.seg_types.items():
             # 先頭のCLSトークンをIGNORE_INDEXにするため+1
             seg_types[i + 1] = seg_label
-        norm_types: list[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
+        norm_types: List[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
         for i, norm_label in char_feature_example.norm_types.items():
             # 先頭のCLSトークンをIGNORE_INDEXにするため+1
             norm_types[i + 1] = norm_label
diff --git a/src/kwja/datamodule/datasets/char_inference_dataset.py b/src/kwja/datamodule/datasets/char_inference_dataset.py
index 557a42cc..00b68006 100644
--- a/src/kwja/datamodule/datasets/char_inference_dataset.py
+++ b/src/kwja/datamodule/datasets/char_inference_dataset.py
@@ -1,7 +1,7 @@
 import logging
 from dataclasses import dataclass
 from datetime import datetime
-from typing import Optional, Union
+from typing import Dict, List, Optional, Union
 
 import torch
 from omegaconf import ListConfig
@@ -35,15 +35,15 @@ def __init__(
     ) -> None:
         documents = self._create_documents_from_texts(list(texts), doc_id_prefix)
         super().__init__(documents, document_split_stride, model_name_or_path, max_seq_length, tokenizer_kwargs or {})
-        self.examples: list[CharInferenceExample] = self._load_examples(self.documents)
+        self.examples: List[CharInferenceExample] = self._load_examples(self.documents)
 
     def __len__(self) -> int:
         return len(self.examples)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.examples[index])
 
-    def _load_examples(self, documents: list[Document]) -> list[CharInferenceExample]:
+    def _load_examples(self, documents: List[Document]) -> List[CharInferenceExample]:
         examples = []
         idx = 0
         for document in documents:
@@ -72,7 +72,7 @@ def _load_examples(self, documents: list[Document]) -> list[CharInferenceExample
         return examples
 
     @staticmethod
-    def encode(example: CharInferenceExample) -> dict[str, torch.Tensor]:
+    def encode(example: CharInferenceExample) -> Dict[str, torch.Tensor]:
         input_ids = example.encoding["input_ids"]
         attention_mask = example.encoding["attention_mask"]
         return {
@@ -82,10 +82,10 @@ def encode(example: CharInferenceExample) -> dict[str, torch.Tensor]:
         }
 
     @staticmethod
-    def _create_documents_from_texts(texts: list[str], doc_id_prefix: Optional[str]) -> list[Document]:
+    def _create_documents_from_texts(texts: List[str], doc_id_prefix: Optional[str]) -> List[Document]:
         senter = RegexSenter()
         # split text into sentences
-        documents: list[Document] = [senter.apply_to_document(text) for text in texts]
+        documents: List[Document] = [senter.apply_to_document(text) for text in texts]
         if doc_id_prefix is None:
             doc_id_prefix = datetime.now().strftime("%Y%m%d%H%M")
         doc_id_width = len(str(len(documents)))
diff --git a/src/kwja/datamodule/datasets/typo_dataset.py b/src/kwja/datamodule/datasets/typo_dataset.py
index 40310463..ead66ea6 100644
--- a/src/kwja/datamodule/datasets/typo_dataset.py
+++ b/src/kwja/datamodule/datasets/typo_dataset.py
@@ -1,6 +1,6 @@
 import json
 from pathlib import Path
-from typing import Union
+from typing import Dict, List, Union
 
 import torch
 from torch.utils.data import Dataset
@@ -35,31 +35,31 @@ def __init__(
         self.unk_token_id: int = self.tokenizer.unk_token_id
         self.max_seq_length: int = max_seq_length
 
-        self.opn2id: dict[str, int] = self.get_opn2id(path=Path(extended_vocab_path))
+        self.opn2id: Dict[str, int] = self.get_opn2id(path=Path(extended_vocab_path))
 
     def __len__(self) -> int:
         return len(self.documents)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.documents[index])
 
     @staticmethod
-    def load_documents(path: Path) -> list[dict[str, Union[str, list[str]]]]:
-        documents: list[dict[str, Union[str, list[str]]]] = []
+    def load_documents(path: Path) -> List[Dict[str, Union[str, List[str]]]]:
+        documents: List[Dict[str, Union[str, List[str]]]] = []
         for file_path in sorted(path.glob("**/*.jsonl")):
             with file_path.open(mode="r", encoding="utf-8") as f:
                 for line in f:
                     documents.append(json.loads(line))
         return documents
 
-    def get_opn2id(self, path: Path) -> dict[str, int]:
+    def get_opn2id(self, path: Path) -> Dict[str, int]:
         opn2id = self.tokenizer.get_vocab()
         with path.open(mode="r") as f:
             for line in f:
                 opn2id[str(line.strip())] = len(opn2id)
         return opn2id
 
-    def encode(self, document: dict[str, Union[str, list[str]]]) -> dict[str, torch.Tensor]:
+    def encode(self, document: Dict[str, Union[str, List[str]]]) -> Dict[str, torch.Tensor]:
         if isinstance(document["pre_text"], list):
             raise ValueError('document["pre_text"] must be string')
         encoding: BatchEncoding = self.tokenizer(
@@ -71,23 +71,23 @@ def encode(self, document: dict[str, Union[str, list[str]]]) -> dict[str, torch.
         input_ids = encoding["input_ids"]
         attention_mask = encoding["attention_mask"]
 
-        kdr_labels: list[int] = []
+        kdr_labels: List[int] = []
         for opn in document["kdrs"][:-1]:
             if opn in TYPO_OPN2TOKEN:
                 kdr_label = self.opn2id[TYPO_OPN2TOKEN[opn]]
             else:
-                kdr_label = self.opn2id.get(opn.removeprefix("R:"), self.unk_token_id)
+                kdr_label = self.opn2id.get(opn[2:], self.unk_token_id)  # remove prefix "R:" from opn
             kdr_labels.append(kdr_label)
         kdr_labels.append(self.pad_token_id)
         kdr_labels = [self.pad_token_id] + kdr_labels[: self.max_seq_length - 2] + [self.pad_token_id]
         kdr_labels = kdr_labels + [self.pad_token_id] * (self.max_seq_length - len(kdr_labels))
 
-        ins_labels: list[int] = []
+        ins_labels: List[int] = []
         for opn in document["inss"]:
             if opn in TYPO_OPN2TOKEN:
                 ins_label = self.opn2id[TYPO_OPN2TOKEN[opn]]
             else:
-                ins_label = self.opn2id.get(opn.removeprefix("I:"), self.unk_token_id)
+                ins_label = self.opn2id.get(opn[2:], self.unk_token_id)  # remove prefix "I:" from opn
             ins_labels.append(ins_label)
         ins_labels = [self.pad_token_id] + ins_labels[: self.max_seq_length - 2] + [self.pad_token_id]
         ins_labels = ins_labels + [self.pad_token_id] * (self.max_seq_length - len(ins_labels))
diff --git a/src/kwja/datamodule/datasets/typo_inference_dataset.py b/src/kwja/datamodule/datasets/typo_inference_dataset.py
index 482efd51..5352e9dc 100644
--- a/src/kwja/datamodule/datasets/typo_inference_dataset.py
+++ b/src/kwja/datamodule/datasets/typo_inference_dataset.py
@@ -1,3 +1,5 @@
+from typing import Dict, List
+
 import torch
 from torch.utils.data import Dataset
 from transformers import AutoTokenizer, BatchEncoding, PreTrainedTokenizerBase
@@ -9,7 +11,7 @@
 class TypoInferenceDataset(Dataset):
     def __init__(
         self,
-        texts: list[str],
+        texts: List[str],
         model_name_or_path: str = "nlp-waseda/roberta-base-japanese",
         max_seq_length: int = 512,
         tokenizer_kwargs: dict = None,
@@ -25,10 +27,10 @@ def __init__(
     def __len__(self) -> int:
         return len(self.texts)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.texts[index])
 
-    def encode(self, text: str) -> dict[str, torch.Tensor]:
+    def encode(self, text: str) -> Dict[str, torch.Tensor]:
         encoding: BatchEncoding = self.tokenizer(
             text + TYPO_DUMMY_TOKEN,
             truncation=True,
diff --git a/src/kwja/datamodule/datasets/word_dataset.py b/src/kwja/datamodule/datasets/word_dataset.py
index 563470a6..a473ba06 100644
--- a/src/kwja/datamodule/datasets/word_dataset.py
+++ b/src/kwja/datamodule/datasets/word_dataset.py
@@ -1,7 +1,7 @@
 import logging
 from dataclasses import dataclass
 from pathlib import Path
-from typing import Union
+from typing import Dict, List, Tuple, Union
 
 import torch
 from omegaconf import ListConfig
@@ -80,11 +80,11 @@ def __init__(
             max_seq_length,
             tokenizer_kwargs or {},
         )
-        self.special_tokens: list[str] = list(special_tokens)
+        self.special_tokens: List[str] = list(special_tokens)
         self.exophora_referents = [ExophoraReferent(s) for s in exophora_referents]
-        self.cohesion_tasks: list[CohesionTask] = [CohesionTask(t) for t in cohesion_tasks]
-        self.pas_cases: list[str] = list(pas_cases)
-        self.bar_rels: list[str] = list(bar_rels)
+        self.cohesion_tasks: List[CohesionTask] = [CohesionTask(t) for t in cohesion_tasks]
+        self.pas_cases: List[str] = list(pas_cases)
+        self.bar_rels: List[str] = list(bar_rels)
         self.cohesion_task_to_rel_types = {
             CohesionTask.PAS_ANALYSIS: self.pas_cases,
             CohesionTask.BRIDGING: self.bar_rels,
@@ -96,14 +96,14 @@ def __init__(
             CohesionTask.COREFERENCE: CoreferenceExtractor(self.exophora_referents, restrict_cohesion_target),
             CohesionTask.BRIDGING: BridgingExtractor(self.bar_rels, self.exophora_referents, restrict_cohesion_target),
         }
-        self.special_to_index: dict[str, int] = {
+        self.special_to_index: Dict[str, int] = {
             token: self.max_seq_length - len(self.special_tokens) + i for i, token in enumerate(self.special_tokens)
         }
-        self.index_to_special: dict[int, str] = {v: k for k, v in self.special_to_index.items()}
+        self.index_to_special: Dict[int, str] = {v: k for k, v in self.special_to_index.items()}
         self.reading_resource_path = Path(reading_resource_path)
         self.reading2id = get_reading2id(str(self.reading_resource_path / "vocab.txt"))
         self.reading_aligner = ReadingAligner(self.tokenizer, KanjiDic(str(self.reading_resource_path / "kanjidic")))
-        self.examples: list[WordExampleSet] = self._load_examples(self.documents)
+        self.examples: List[WordExampleSet] = self._load_examples(self.documents)
         self.special_encoding: Encoding = self.tokenizer(
             self.special_tokens,
             is_split_into_words=True,
@@ -113,11 +113,11 @@ def __init__(
         ).encodings[0]
 
     @property
-    def special_indices(self) -> list[int]:
+    def special_indices(self) -> List[int]:
         return list(self.special_to_index.values())
 
     @property
-    def cohesion_special_indices(self) -> list[int]:
+    def cohesion_special_indices(self) -> List[int]:
         return [index for special, index in self.special_to_index.items() if special != "[ROOT]"]
 
     @property
@@ -125,10 +125,10 @@ def num_special_tokens(self) -> int:
         return len(self.special_tokens)
 
     @property
-    def cohesion_rel_types(self) -> list[str]:
+    def cohesion_rel_types(self) -> List[str]:
         return [t for task in self.cohesion_tasks for t in self.cohesion_task_to_rel_types[task]]
 
-    def _load_examples(self, documents: list[Document]) -> list[WordExampleSet]:
+    def _load_examples(self, documents: List[Document]) -> List[WordExampleSet]:
         examples = []
         idx = 0
         for document in tqdm(documents, dynamic_ncols=True):
@@ -195,10 +195,10 @@ def _load_examples(self, documents: list[Document]) -> list[WordExampleSet]:
     def __len__(self) -> int:
         return len(self.examples)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.examples[index])
 
-    def encode(self, example: WordExampleSet) -> dict[str, torch.Tensor]:
+    def encode(self, example: WordExampleSet) -> Dict[str, torch.Tensor]:
         merged_encoding: Encoding = Encoding.merge([example.encoding, self.special_encoding])
 
         document = self.doc_id2document[example.doc_id]
@@ -235,7 +235,7 @@ def encode(self, example: WordExampleSet) -> dict[str, torch.Tensor]:
                 if mrph_type in all_types:
                     morpheme_types[morpheme_index][i] = all_types.index(mrph_type)
 
-        ne_tags: list[int] = [
+        ne_tags: List[int] = [
             (NE_TAGS.index("O") if morpheme_index in word_feature_example.features.keys() else IGNORE_INDEX)
             for morpheme_index in range(self.max_seq_length)
         ]
@@ -261,7 +261,7 @@ def encode(self, example: WordExampleSet) -> dict[str, torch.Tensor]:
 
         # dependency parsing
         dependency_example = example.dependency_example
-        dependencies: list[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
+        dependencies: List[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
         for morpheme_index, dependency in dependency_example.dependencies.items():
             dependencies[morpheme_index] = dependency if dependency != -1 else self.special_to_index["[ROOT]"]
 
@@ -271,14 +271,14 @@ def encode(self, example: WordExampleSet) -> dict[str, torch.Tensor]:
             for candidate_index in candidates + [self.special_to_index["[ROOT]"]]:
                 dependency_mask[morpheme_index][candidate_index] = True
 
-        dependency_types: list[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
+        dependency_types: List[int] = [IGNORE_INDEX for _ in range(self.max_seq_length)]
         for morpheme_index, dependency_type in dependency_example.dependency_types.items():
             dependency_types[morpheme_index] = DEPENDENCY_TYPE2INDEX[dependency_type]
 
         # PAS analysis & coreference resolution
         cohesion_example = example.cohesion_example
-        cohesion_target: list[list[list[int]]] = []  # (task, src, tgt)
-        candidates_set: list[list[list[int]]] = []  # (task, src, tgt)
+        cohesion_target: List[List[List[int]]] = []  # (task, src, tgt)
+        candidates_set: List[List[List[int]]] = []  # (task, src, tgt)
         if CohesionTask.PAS_ANALYSIS in self.cohesion_tasks:
             task = CohesionTask.PAS_ANALYSIS
             annotation = cohesion_example.annotations[task]
@@ -334,11 +334,11 @@ def encode(self, example: WordExampleSet) -> dict[str, torch.Tensor]:
 
     def dump_prediction(
         self,
-        result: list[list[list[float]]],  # word level
+        result: List[List[List[float]]],  # word level
         example: CohesionExample,
-    ) -> list[list[list[float]]]:  # (phrase, rel, 0 or phrase+special)
+    ) -> List[List[List[float]]]:  # (phrase, rel, 0 or phrase+special)
         """1 example 中に存在する基本句それぞれに対してシステム予測のリストを返す．"""
-        ret: list[list[list[float]]] = [[] for _ in next(iter(example.phrases.values()))]
+        ret: List[List[List[float]]] = [[] for _ in next(iter(example.phrases.values()))]
         task_idx = 0
         if CohesionTask.PAS_ANALYSIS in self.cohesion_tasks:
             for _ in self.pas_cases:
@@ -359,13 +359,13 @@ def dump_prediction(
 
     def _word2bp_level(
         self,
-        word_level_output: list[list[float]],
-        phrases: list[Phrase],
-    ) -> list[list[float]]:  # (bp, 0 or bp+special)
-        ret: list[list[float]] = [[] for _ in phrases]
+        word_level_output: List[List[float]],
+        phrases: List[Phrase],
+    ) -> List[List[float]]:  # (bp, 0 or bp+special)
+        ret: List[List[float]] = [[] for _ in phrases]
         for anaphor in filter(lambda p: p.is_target, phrases):
-            word_level_scores: list[float] = word_level_output[anaphor.dmid]
-            phrase_level_scores: list[float] = []
+            word_level_scores: List[float] = word_level_output[anaphor.dmid]
+            phrase_level_scores: List[float] = []
             for tgt_bp in phrases:
                 if tgt_bp.dtid not in anaphor.candidates:
                     phrase_level_scores.append(-1)  # pad -1 for non-candidate phrases
@@ -376,7 +376,7 @@ def _word2bp_level(
             ret[anaphor.dtid] = softmax(phrase_level_scores).tolist()
         return ret
 
-    def _gen_subword_map(self, encoding: Encoding, include_additional_words: bool = True) -> list[list[bool]]:
+    def _gen_subword_map(self, encoding: Encoding, include_additional_words: bool = True) -> List[List[bool]]:
         subword_map = [[False] * self.max_seq_length for _ in range(self.max_seq_length)]
         for token_id, word_id in enumerate(encoding.word_ids):
             if word_id is None or token_id in self.special_indices:
@@ -389,16 +389,16 @@ def _gen_subword_map(self, encoding: Encoding, include_additional_words: bool =
 
     def _convert_annotation_to_feature(
         self,
-        annotation: list[list[str]],  # phrase level
-        phrases: list[Phrase],
-    ) -> tuple[list[list[int]], list[list[int]]]:
-        scores_set: list[list[int]] = [[0] * self.max_seq_length for _ in range(self.max_seq_length)]
-        candidates_set: list[list[int]] = [[] for _ in range(self.max_seq_length)]
+        annotation: List[List[str]],  # phrase level
+        phrases: List[Phrase],
+    ) -> Tuple[List[List[int]], List[List[int]]]:
+        scores_set: List[List[int]] = [[0] * self.max_seq_length for _ in range(self.max_seq_length)]
+        candidates_set: List[List[int]] = [[] for _ in range(self.max_seq_length)]
         for phrase in phrases:
-            arguments: list[str] = annotation[phrase.dtid]
-            candidates: list[int] = phrase.candidates  # phrase level
+            arguments: List[str] = annotation[phrase.dtid]
+            candidates: List[int] = phrase.candidates  # phrase level
             for mrph in phrase.children:
-                scores: list[int] = [0] * self.max_seq_length
+                scores: List[int] = [0] * self.max_seq_length
                 if mrph.is_target:
                     for arg_string in arguments:
                         # arg_string: 著者, 8%C, 15%O, 2, [NULL], ...
@@ -412,7 +412,7 @@ def _convert_annotation_to_feature(
                             word_index = phrases[int(arg_string)].dmid
                         scores[word_index] = 1
 
-                word_level_candidates: list[int] = []
+                word_level_candidates: List[int] = []
                 for candidate in candidates:
                     word_level_candidates.append(phrases[candidate].dmid)
                 word_level_candidates += self.cohesion_special_indices
diff --git a/src/kwja/datamodule/datasets/word_inference_dataset.py b/src/kwja/datamodule/datasets/word_inference_dataset.py
index c3f146ee..6d527c78 100644
--- a/src/kwja/datamodule/datasets/word_inference_dataset.py
+++ b/src/kwja/datamodule/datasets/word_inference_dataset.py
@@ -3,7 +3,7 @@
 from dataclasses import dataclass
 from datetime import datetime
 from pathlib import Path
-from typing import Optional, Union
+from typing import Dict, List, Optional, Union
 
 import torch
 from omegaconf import ListConfig
@@ -57,14 +57,14 @@ def __init__(
         super().__init__(documents, document_split_stride, model_name_or_path, max_seq_length, tokenizer_kwargs or {})
 
         self.exophora_referents = [ExophoraReferent(s) for s in exophora_referents]
-        self.special_tokens: list[str] = list(special_tokens)
-        self.special_to_index: dict[str, int] = {
+        self.special_tokens: List[str] = list(special_tokens)
+        self.special_to_index: Dict[str, int] = {
             token: self.max_seq_length - len(self.special_tokens) + i for i, token in enumerate(self.special_tokens)
         }
-        self.index_to_special: dict[int, str] = {v: k for k, v in self.special_to_index.items()}
+        self.index_to_special: Dict[int, str] = {v: k for k, v in self.special_to_index.items()}
         self.cohesion_tasks = [CohesionTask(t) for t in cohesion_tasks]
-        self.pas_cases: list[str] = list(pas_cases)
-        self.bar_rels: list[str] = list(bar_rels)
+        self.pas_cases: List[str] = list(pas_cases)
+        self.bar_rels: List[str] = list(bar_rels)
         self.cohesion_task_to_rel_types = {
             CohesionTask.PAS_ANALYSIS: self.pas_cases,
             CohesionTask.BRIDGING: self.bar_rels,
@@ -75,7 +75,7 @@ def __init__(
             CohesionTask.COREFERENCE: CoreferenceExtractor(self.exophora_referents, restrict_cohesion_target),
             CohesionTask.BRIDGING: BridgingExtractor(self.bar_rels, self.exophora_referents, restrict_cohesion_target),
         }
-        self.examples: list[WordInferenceExample] = self._load_examples(self.documents)
+        self.examples: List[WordInferenceExample] = self._load_examples(self.documents)
         self.special_encoding: Encoding = self.tokenizer(
             self.special_tokens,
             is_split_into_words=True,
@@ -85,11 +85,11 @@ def __init__(
         ).encodings[0]
 
     @property
-    def special_indices(self) -> list[int]:
+    def special_indices(self) -> List[int]:
         return list(self.special_to_index.values())
 
     @property
-    def cohesion_special_indices(self) -> list[int]:
+    def cohesion_special_indices(self) -> List[int]:
         return [index for special, index in self.special_to_index.items() if special != "[ROOT]"]
 
     @property
@@ -97,11 +97,11 @@ def num_special_tokens(self) -> int:
         return len(self.special_tokens)
 
     @property
-    def cohesion_rel_types(self) -> list[str]:
+    def cohesion_rel_types(self) -> List[str]:
         return [t for ts in self.cohesion_task_to_rel_types.values() for t in ts]
 
     @staticmethod
-    def _create_documents_from_texts(texts: list[str], doc_id_prefix: Optional[str]) -> list[Document]:
+    def _create_documents_from_texts(texts: List[str], doc_id_prefix: Optional[str]) -> List[Document]:
         did2sentences = defaultdict(list)
         if doc_id_prefix is None:
             doc_id_prefix = datetime.now().strftime("%Y%m%d%H%M")
@@ -136,10 +136,10 @@ def _create_documents_from_texts(texts: list[str], doc_id_prefix: Optional[str])
     def __len__(self) -> int:
         return len(self.examples)
 
-    def __getitem__(self, index: int) -> dict[str, torch.Tensor]:
+    def __getitem__(self, index: int) -> Dict[str, torch.Tensor]:
         return self.encode(self.examples[index])
 
-    def _load_examples(self, documents: list[Document]) -> list[WordInferenceExample]:
+    def _load_examples(self, documents: List[Document]) -> List[WordInferenceExample]:
         examples = []
         idx = 0
         for document in documents:
@@ -170,7 +170,7 @@ def _load_examples(self, documents: list[Document]) -> list[WordInferenceExample
             logger.error("No examples to process. Make sure any texts are given and they are not too long.")
         return examples
 
-    def encode(self, example: WordInferenceExample) -> dict[str, torch.Tensor]:
+    def encode(self, example: WordInferenceExample) -> Dict[str, torch.Tensor]:
         document = self.doc_id2document[example.doc_id]
 
         target_mask = [False for _ in range(self.max_seq_length)]
@@ -215,7 +215,7 @@ def encode(self, example: WordInferenceExample) -> dict[str, torch.Tensor]:
             "tokens": " ".join(self.tokenizer.decode(id_) for id_ in merged_encoding.ids),
         }
 
-    def _gen_subword_map(self, encoding: Encoding, include_additional_words: bool = True) -> list[list[bool]]:
+    def _gen_subword_map(self, encoding: Encoding, include_additional_words: bool = True) -> List[List[bool]]:
         subword_map = [[False] * self.max_seq_length for _ in range(self.max_seq_length)]
         for token_id, word_id in enumerate(encoding.word_ids):
             if word_id is None or token_id in self.special_indices:
diff --git a/src/kwja/datamodule/examples/base_phrase_feature.py b/src/kwja/datamodule/examples/base_phrase_feature.py
index ae7cb0f6..c8542bb3 100644
--- a/src/kwja/datamodule/examples/base_phrase_feature.py
+++ b/src/kwja/datamodule/examples/base_phrase_feature.py
@@ -1,3 +1,5 @@
+from typing import List, Set
+
 from rhoknp import Document
 
 from kwja.utils.constants import BASE_PHRASE_FEATURES, IGNORE_VALUE_FEATURE_PAT
@@ -9,8 +11,8 @@ class BasePhraseFeatureExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.heads: list[int] = []  # 基本句単位主辞形態素インデックス
-        self.features: list[set[str]] = []  # 基本句単位素性リスト
+        self.heads: List[int] = []  # 基本句単位主辞形態素インデックス
+        self.features: List[Set[str]] = []  # 基本句単位素性リスト
 
     def load(self, document: Document) -> None:
         self.doc_id = document.doc_id
diff --git a/src/kwja/datamodule/examples/char_feature.py b/src/kwja/datamodule/examples/char_feature.py
index 008565d8..72fa0595 100644
--- a/src/kwja/datamodule/examples/char_feature.py
+++ b/src/kwja/datamodule/examples/char_feature.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 from rhoknp import Document
 
 from kwja.utils.constants import IGNORE_INDEX, IGNORE_WORD_NORM_TYPE, SEG_TYPES, WORD_NORM_TYPES
@@ -10,8 +12,8 @@ class CharFeatureExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.seg_types: dict[int, int] = {}  # 文字 index -> 単語分割用のBIタグ
-        self.norm_types: dict[int, int] = {}  # 文字 index -> 正規化タイプ
+        self.seg_types: Dict[int, int] = {}  # 文字 index -> 単語分割用のBIタグ
+        self.norm_types: Dict[int, int] = {}  # 文字 index -> 正規化タイプ
 
         self.normalizer: MorphemeNormalizer = MorphemeNormalizer()
 
diff --git a/src/kwja/datamodule/examples/cohesion.py b/src/kwja/datamodule/examples/cohesion.py
index e22d90c8..2b55c955 100644
--- a/src/kwja/datamodule/examples/cohesion.py
+++ b/src/kwja/datamodule/examples/cohesion.py
@@ -1,5 +1,6 @@
 import logging
 from enum import Enum
+from typing import Dict, List
 
 from rhoknp import Document
 
@@ -27,18 +28,18 @@ class CohesionExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.annotations: dict[CohesionTask, Annotation] = {}
-        self.phrases: dict[CohesionTask, list[Phrase]] = {}
+        self.annotations: Dict[CohesionTask, Annotation] = {}
+        self.phrases: Dict[CohesionTask, List[Phrase]] = {}
 
     @property
-    def mrphs(self) -> dict[CohesionTask, list[Mrph]]:
+    def mrphs(self) -> Dict[CohesionTask, List[Mrph]]:
         return {task: sum((p.children for p in phrases), []) for task, phrases in self.phrases.items()}
 
     def load(
         self,
         document: Document,
-        tasks: list[CohesionTask],
-        extractors: dict[CohesionTask, Extractor],
+        tasks: List[CohesionTask],
+        extractors: Dict[CohesionTask, Extractor],
     ):
         self.doc_id = document.doc_id
         for task in tasks:
@@ -48,7 +49,7 @@ def load(
             self.annotations[task] = extractor.extract(document, phrases)  # extract gold
 
     @staticmethod
-    def _construct_phrases(document: Document) -> list[Phrase]:
+    def _construct_phrases(document: Document) -> List[Phrase]:
         # construct phrases and mrphs
         phrases = []
         for sentence in document.sentences:
diff --git a/src/kwja/datamodule/examples/dependency.py b/src/kwja/datamodule/examples/dependency.py
index f1311eab..85c0d39a 100644
--- a/src/kwja/datamodule/examples/dependency.py
+++ b/src/kwja/datamodule/examples/dependency.py
@@ -1,3 +1,5 @@
+from typing import Dict, List
+
 from rhoknp import Document
 from rhoknp.props import DepType
 
@@ -9,9 +11,9 @@ class DependencyExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.dependencies: dict[int, int] = {}  # 形態素単位係り受け
-        self.candidates: dict[int, list[int]] = {}  # 形態素単位係り先選択候補
-        self.dependency_types: dict[int, DepType] = {}  # 形態素単位係り受けラベル
+        self.dependencies: Dict[int, int] = {}  # 形態素単位係り受け
+        self.candidates: Dict[int, List[int]] = {}  # 形態素単位係り先選択候補
+        self.dependency_types: Dict[int, DepType] = {}  # 形態素単位係り受けラベル
 
     def load(self, document: Document) -> None:
         self.doc_id = document.doc_id
diff --git a/src/kwja/datamodule/examples/discourse.py b/src/kwja/datamodule/examples/discourse.py
index 2097bb47..fc6b7d0b 100644
--- a/src/kwja/datamodule/examples/discourse.py
+++ b/src/kwja/datamodule/examples/discourse.py
@@ -1,3 +1,5 @@
+from typing import List
+
 from rhoknp import Document
 
 from kwja.utils.constants import DISCOURSE_RELATION_MAP
@@ -8,7 +10,7 @@ class DiscourseExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.discourse_relations: list[list[str]] = []
+        self.discourse_relations: List[List[str]] = []
 
     def load(self, document: Document, has_annotation: bool = True) -> None:
         self.doc_id = document.doc_id
diff --git a/src/kwja/datamodule/examples/reading.py b/src/kwja/datamodule/examples/reading.py
index 489cb904..27f5862e 100644
--- a/src/kwja/datamodule/examples/reading.py
+++ b/src/kwja/datamodule/examples/reading.py
@@ -1,5 +1,5 @@
 import logging
-from typing import Union
+from typing import List, Union
 
 from rhoknp import Document
 
@@ -13,7 +13,7 @@ class ReadingExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.readings: Union[list[str], None] = []
+        self.readings: Union[List[str], None] = []
 
     def load(self, document: Document, aligner: ReadingAligner) -> None:
         self.doc_id = document.doc_id
diff --git a/src/kwja/datamodule/examples/word_feature.py b/src/kwja/datamodule/examples/word_feature.py
index 0798e90b..046b64aa 100644
--- a/src/kwja/datamodule/examples/word_feature.py
+++ b/src/kwja/datamodule/examples/word_feature.py
@@ -1,3 +1,5 @@
+from typing import Dict, List, Set, Tuple
+
 from rhoknp import Document
 from rhoknp.props import NamedEntity
 
@@ -10,9 +12,9 @@ class WordFeatureExample:
 
     def __init__(self) -> None:
         self.doc_id: str = ""
-        self.types: dict[int, tuple[str, str, str, str]] = {}  # 形態素 index -> 形態素タイプ (品詞, 品詞細分類, 活用型, 活用形)
-        self.named_entities: list[NamedEntity] = []  # 固有表現
-        self.features: dict[int, set[str]] = {}  # 形態素 index -> 形態素素性集合
+        self.types: Dict[int, Tuple[str, str, str, str]] = {}  # 形態素 index -> 形態素タイプ (品詞, 品詞細分類, 活用型, 活用形)
+        self.named_entities: List[NamedEntity] = []  # 固有表現
+        self.features: Dict[int, Set[str]] = {}  # 形態素 index -> 形態素素性集合
 
     def load(self, document: Document) -> None:
         self.doc_id = document.doc_id
diff --git a/src/kwja/datamodule/extractors/base.py b/src/kwja/datamodule/extractors/base.py
index bda44282..3b9c6f74 100644
--- a/src/kwja/datamodule/extractors/base.py
+++ b/src/kwja/datamodule/extractors/base.py
@@ -1,4 +1,5 @@
 from dataclasses import dataclass
+from typing import List
 
 from rhoknp import BasePhrase, Document
 from rhoknp.cohesion import ExophoraReferent, ExophoraReferentType
@@ -19,17 +20,17 @@ def is_target(self):
 class Phrase:
     dtid: int
     surf: str
-    dmids: list[int]
+    dmids: List[int]
     dmid: int
-    children: list[Mrph]
+    children: List[Mrph]
     is_target: bool
-    candidates: list[int]  # candidates list is always empty if is_target is False
+    candidates: List[int]  # candidates list is always empty if is_target is False
 
 
 class Extractor:
     def __init__(
         self,
-        exophora_referents: list[ExophoraReferent],
+        exophora_referents: List[ExophoraReferent],
         restrict_target: bool = False,
     ) -> None:
         self.exophora_referents = exophora_referents
@@ -47,7 +48,7 @@ def _relax_exophora_referent(
             exophora_referent.index = None
         return exophora_referent
 
-    def extract(self, document: Document, phrases: list[Phrase]):
+    def extract(self, document: Document, phrases: List[Phrase]):
         raise NotImplementedError
 
     def is_target(self, bp: BasePhrase) -> bool:
diff --git a/src/kwja/datamodule/extractors/bridging.py b/src/kwja/datamodule/extractors/bridging.py
index 9939140e..3a2ec2df 100644
--- a/src/kwja/datamodule/extractors/bridging.py
+++ b/src/kwja/datamodule/extractors/bridging.py
@@ -1,5 +1,6 @@
 import logging
 from dataclasses import dataclass
+from typing import List
 
 from rhoknp import BasePhrase, Document
 from rhoknp.cohesion import Argument, EndophoraArgument, ExophoraArgument, ExophoraReferent
@@ -12,14 +13,14 @@
 
 @dataclass(frozen=True)
 class BridgingAnnotation:
-    arguments_set: list[list[str]]
+    arguments_set: List[List[str]]
 
 
 class BridgingExtractor(Extractor):
     def __init__(
         self,
-        bar_rels: list[str],
-        exophors: list[ExophoraReferent],
+        bar_rels: List[str],
+        exophors: List[ExophoraReferent],
         restrict_target: bool,
     ) -> None:
         super().__init__(exophors, restrict_target=restrict_target)
@@ -28,17 +29,17 @@ def __init__(
     def extract(
         self,
         document: Document,
-        phrases: list[Phrase],
+        phrases: List[Phrase],
     ) -> BridgingAnnotation:
         bp_list = document.base_phrases
-        arguments_set: list[list[str]] = [[] for _ in bp_list]
+        arguments_set: List[List[str]] = [[] for _ in bp_list]
         for anaphor in [bp for sent in extract_target_sentences(document) for bp in sent.base_phrases]:
             if self.is_target(anaphor) is False:
                 continue
             phrases[anaphor.global_index].is_target = True
-            candidates: list[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, anaphor) is True]
+            candidates: List[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, anaphor) is True]
             phrases[anaphor.global_index].candidates = candidates
-            arguments: list[Argument] = []
+            arguments: List[Argument] = []
             assert anaphor.pas is not None, "pas has not been set"
             for rel in self.rels:
                 arguments += anaphor.pas.get_arguments(rel, relax=False)
@@ -48,9 +49,9 @@ def extract(
 
     def _get_args(
         self,
-        orig_args: list[Argument],
-        candidates: list[int],
-    ) -> list[str]:
+        orig_args: List[Argument],
+        candidates: List[int],
+    ) -> List[str]:
         """Get string representations of orig_args.
         If the return value is an empty list, do not calculate loss for this argument.
         overt: {dmid}%C
@@ -60,7 +61,7 @@ def _get_args(
         no arg: [NULL]
         """
         # filter out non-target exophors
-        args: list[Argument] = []
+        args: List[Argument] = []
         for arg in orig_args:
             if isinstance(arg, ExophoraArgument):
                 arg.exophora_referent = self._relax_exophora_referent(arg.exophora_referent)
@@ -72,7 +73,7 @@ def _get_args(
                 args.append(arg)
         if not args:
             return ["[NULL]"]
-        arg_strings: list[str] = []
+        arg_strings: List[str] = []
         for arg in args:
             if isinstance(arg, EndophoraArgument):
                 if arg.base_phrase.global_index not in candidates:
diff --git a/src/kwja/datamodule/extractors/coreference.py b/src/kwja/datamodule/extractors/coreference.py
index f4550956..3a55a63f 100644
--- a/src/kwja/datamodule/extractors/coreference.py
+++ b/src/kwja/datamodule/extractors/coreference.py
@@ -1,5 +1,6 @@
 import logging
 from dataclasses import dataclass
+from typing import List
 
 from rhoknp import BasePhrase, Document
 from rhoknp.cohesion import ExophoraReferent
@@ -12,13 +13,13 @@
 
 @dataclass(frozen=True)
 class CoreferenceAnnotation:
-    arguments_set: list[list[str]]
+    arguments_set: List[List[str]]
 
 
 class CoreferenceExtractor(Extractor):
     def __init__(
         self,
-        exophors: list[ExophoraReferent],
+        exophors: List[ExophoraReferent],
         restrict_target: bool,
     ) -> None:
         super().__init__(exophors, restrict_target=restrict_target)
@@ -26,15 +27,15 @@ def __init__(
     def extract(
         self,
         document: Document,
-        phrases: list[Phrase],
+        phrases: List[Phrase],
     ) -> CoreferenceAnnotation:
         bp_list = document.base_phrases
-        mentions_set: list[list[str]] = [[] for _ in bp_list]
+        mentions_set: List[List[str]] = [[] for _ in bp_list]
         for mention in [bp for sent in extract_target_sentences(document) for bp in sent.base_phrases]:
             if self.is_target(mention) is False:
                 continue
             phrases[mention.global_index].is_target = True
-            candidates: list[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, mention) is True]
+            candidates: List[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, mention) is True]
             phrases[mention.global_index].candidates = candidates
             mentions_set[mention.global_index] = self._get_mentions(mention, candidates)
 
@@ -43,12 +44,12 @@ def extract(
     def _get_mentions(
         self,
         src_mention: BasePhrase,
-        candidates: list[int],
-    ) -> list[str]:
+        candidates: List[int],
+    ) -> List[str]:
         if not src_mention.entities:
             return ["[NA]"]
 
-        ment_strings: list[str] = []
+        ment_strings: List[str] = []
         tgt_mentions = src_mention.get_coreferents(include_nonidentical=False)
         exophora_referents = [e.exophora_referent for e in src_mention.entities if e.exophora_referent is not None]
         for mention in tgt_mentions:
diff --git a/src/kwja/datamodule/extractors/pas.py b/src/kwja/datamodule/extractors/pas.py
index 2fa524b0..8ae14fe3 100644
--- a/src/kwja/datamodule/extractors/pas.py
+++ b/src/kwja/datamodule/extractors/pas.py
@@ -1,6 +1,7 @@
 import logging
 from collections import defaultdict
 from dataclasses import dataclass
+from typing import Dict, List
 
 from rhoknp import BasePhrase, Document
 from rhoknp.cohesion import Argument, ArgumentType, EndophoraArgument, ExophoraArgument, ExophoraReferent
@@ -13,14 +14,14 @@
 
 @dataclass(frozen=True)
 class PasAnnotation:
-    arguments_set: list[dict[str, list[str]]]
+    arguments_set: List[Dict[str, List[str]]]
 
 
 class PasExtractor(Extractor):
     def __init__(
         self,
-        cases: list[str],
-        exophors: list[ExophoraReferent],
+        cases: List[str],
+        exophors: List[ExophoraReferent],
         restrict_target: bool,
     ) -> None:
         self.pas_targets = ["pred", "noun"]
@@ -30,15 +31,15 @@ def __init__(
     def extract(
         self,
         document: Document,
-        phrases: list[Phrase],
+        phrases: List[Phrase],
     ) -> PasAnnotation:
         bp_list = document.base_phrases
-        arguments_set: list[dict[str, list[str]]] = [defaultdict(list) for _ in bp_list]
+        arguments_set: List[Dict[str, List[str]]] = [defaultdict(list) for _ in bp_list]
         for anaphor in [bp for sent in extract_target_sentences(document) for bp in sent.base_phrases]:
             if self.is_target(anaphor) is False:
                 continue
             phrases[anaphor.global_index].is_target = True
-            candidates: list[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, anaphor) is True]
+            candidates: List[int] = [bp.global_index for bp in bp_list if self.is_candidate(bp, anaphor) is True]
             phrases[anaphor.global_index].candidates = candidates
             assert anaphor.pas is not None, "pas has not been set"
             for case in self.cases:
@@ -49,9 +50,9 @@ def extract(
 
     def _get_args(
         self,
-        orig_args: list[Argument],
-        candidates: list[int],
-    ) -> list[str]:
+        orig_args: List[Argument],
+        candidates: List[int],
+    ) -> List[str]:
         """Get string representations of orig_args.
         If the return value is an empty list, do not calculate loss for this argument.
         overt: {dmid}%C
@@ -61,7 +62,7 @@ def _get_args(
         no arg: [NULL]
         """
         # filter out non-target exophors
-        args: list[Argument] = []
+        args: List[Argument] = []
         for arg in orig_args:
             if isinstance(arg, ExophoraArgument):
                 arg.exophora_referent = self._relax_exophora_referent(arg.exophora_referent)
@@ -73,7 +74,7 @@ def _get_args(
                 args.append(arg)
         if not args:
             return ["[NULL]"]
-        arg_strings: list[str] = []
+        arg_strings: List[str] = []
         for arg in args:
             if isinstance(arg, EndophoraArgument):
                 if arg.base_phrase.global_index not in candidates:
diff --git a/src/kwja/evaluators/cohesion_analysis_metric.py b/src/kwja/evaluators/cohesion_analysis_metric.py
index 3433e6f6..f18de291 100644
--- a/src/kwja/evaluators/cohesion_analysis_metric.py
+++ b/src/kwja/evaluators/cohesion_analysis_metric.py
@@ -1,4 +1,5 @@
 from statistics import mean
+from typing import Dict, List, Tuple
 
 import numpy as np
 import torch
@@ -15,12 +16,12 @@ class CohesionAnalysisMetric(Metric):
 
     def __init__(self):
         super().__init__()
-        # Metric state variables can either be torch.Tensor or an empty list which can be used to store torch.Tensors`.
-        # i.e. Expected metric state to be either a Tensor or a list of Tensor
+        # Metric state variables can either be `torch.Tensor` or an empty list which can be used to store
+        # `torch.Tensors`. i.e. Expected metric state to be either a Tensor or a list of Tensor
         self.add_state("example_ids", default=list())
         self.add_state("predictions", default=list())
-        self.example_ids: list[torch.Tensor]  # [()]
-        self.predictions: list[torch.Tensor]  # [(rel, phrase)]
+        self.example_ids: List[torch.Tensor]  # [()]
+        self.predictions: List[torch.Tensor]  # [(rel, phrase)]
 
     def update(
         self,
@@ -33,7 +34,7 @@ def update(
             self.example_ids.append(eid)
             gold_example: CohesionExample = dataset.examples[eid.item()].cohesion_example
             # (rel, phrase, 0 or phrase+special)
-            preds: list[list[list[float]]] = dataset.dump_prediction(out.tolist(), gold_example)
+            preds: List[List[List[float]]] = dataset.dump_prediction(out.tolist(), gold_example)
             self.predictions.append(
                 torch.as_tensor(
                     [[(np.argmax(ps).item() if ps else -1) for ps in pred] for pred in preds],
@@ -42,10 +43,10 @@ def update(
                 )
             )
 
-    def compute(self, dataset: WordDataset) -> tuple[ScoreResult, dict[str, float]]:
+    def compute(self, dataset: WordDataset) -> Tuple[ScoreResult, Dict[str, float]]:
         knp_writer = CohesionKNPWriter(dataset)
         assert len(self.example_ids) == len(self.predictions), f"{len(self.example_ids)} vs {len(self.predictions)}"
-        predictions: dict[int, list[list[int]]] = {
+        predictions: Dict[int, List[List[int]]] = {
             eid.item(): prediction.tolist() for eid, prediction in zip(self.example_ids, self.predictions)
         }
         documents_pred = knp_writer.write(predictions, destination=None)
diff --git a/src/kwja/evaluators/cohesion_scorer.py b/src/kwja/evaluators/cohesion_scorer.py
index cd9a8a0d..fceb5478 100644
--- a/src/kwja/evaluators/cohesion_scorer.py
+++ b/src/kwja/evaluators/cohesion_scorer.py
@@ -7,7 +7,7 @@
 from functools import reduce
 from operator import add
 from pathlib import Path
-from typing import Any, Optional, TextIO, Union
+from typing import Any, Dict, List, Optional, Set, TextIO, Union
 
 import pandas as pd
 from rhoknp import BasePhrase, Document
@@ -23,13 +23,13 @@ class Scorer:
     """A class to evaluate system output.
 
     To evaluate system output with this class, you have to prepare gold data and system prediction data as instances of
-    :class:`kyoto_reader.Document`
+    :class:`rhoknp.Document`
 
     Args:
         documents_pred (list[Document]): システム予測文書集合
         documents_gold (list[Document]): 正解文書集合
-        target_cases (list[str]): 評価の対象とする格 (kyoto_reader.ALL_CASES を参照)
-        exophora_referents (list[ExophoraReferent]): 評価の対象とする外界照応の照応先 (kyoto_reader.ALL_EXOPHORS を参照)
+        target_cases (list[str]): 評価の対象とする格 (rhoknp.cohesion.rel.CASE_TYPES を参照)
+        exophora_referents (list[ExophoraReferent]): 評価の対象とする外界照応の照応先 (rhoknp.cohesion.ExophoraReferentType を参照)
         bridging (bool): 橋渡し照応の評価を行うかどうか (default: False)
         coreference (bool): 共参照の評価を行うかどうか (default: False)
         pas_target (str): 述語項構造解析において述語として扱う対象 ('pred': 用言, 'noun': 体言, 'all': 両方, '': 述語なし (default: pred))
@@ -58,27 +58,27 @@ class Scorer:
 
     def __init__(
         self,
-        documents_pred: list[Document],
-        documents_gold: list[Document],
-        target_cases: list[str],
-        exophora_referents: list[ExophoraReferent],
+        documents_pred: List[Document],
+        documents_gold: List[Document],
+        target_cases: List[str],
+        exophora_referents: List[ExophoraReferent],
         bridging: bool = False,
         coreference: bool = False,
         pas_target: str = "pred",
     ) -> None:
         # long document may have been ignored
         assert set(doc.doc_id for doc in documents_pred) <= set(doc.doc_id for doc in documents_gold)
-        self.cases: list[str] = target_cases if pas_target != "" else []
-        self.doc_ids: list[str] = [doc.doc_id for doc in documents_pred]
-        self.did2document_pred: dict[str, Document] = {doc.doc_id: doc for doc in documents_pred}
-        self.did2document_gold: dict[str, Document] = {doc.doc_id: doc for doc in documents_gold}
+        self.cases: List[str] = target_cases if pas_target != "" else []
+        self.doc_ids: List[str] = [doc.doc_id for doc in documents_pred]
+        self.did2document_pred: Dict[str, Document] = {doc.doc_id: doc for doc in documents_pred}
+        self.did2document_gold: Dict[str, Document] = {doc.doc_id: doc for doc in documents_gold}
         self.bridging: bool = bridging
         self.coreference: bool = coreference
         self.pas_target: str = pas_target
 
-        self.comp_result: dict[tuple, str] = {}
-        self.sub_scorers: list[SubScorer] = []
-        self.exophora_referents: list[ExophoraReferent] = exophora_referents
+        self.comp_result: Dict[tuple, str] = {}
+        self.sub_scorers: List[SubScorer] = []
+        self.exophora_referents: List[ExophoraReferent] = exophora_referents
 
     def run(self) -> "ScoreResult":
         """読み込んだ正解文書集合とシステム予測文書集合に対して評価を行う
@@ -139,26 +139,26 @@ def __init__(
         self,
         document_pred: Document,
         document_gold: Document,
-        cases: list[str],
+        cases: List[str],
         bridging: bool,
         coreference: bool,
-        exophora_referents: list[ExophoraReferent],
+        exophora_referents: List[ExophoraReferent],
         pas_target: str,
     ):
         assert document_pred.doc_id == document_gold.doc_id
         self.doc_id: str = document_gold.doc_id
         self.document_pred: Document = document_pred
         self.document_gold: Document = document_gold
-        self.cases: list[str] = cases
+        self.cases: List[str] = cases
         self.pas: bool = pas_target != ""
         self.bridging: bool = bridging
         self.coreference: bool = coreference
-        self.comp_result: dict[tuple, str] = {}
-        self.exophora_referents: list[ExophoraReferent] = exophora_referents
+        self.comp_result: Dict[tuple, str] = {}
+        self.exophora_referents: List[ExophoraReferent] = exophora_referents
 
-        self.predicates_pred: list[Predicate] = []
-        self.bridgings_pred: list[Predicate] = []
-        self.mentions_pred: list[BasePhrase] = []
+        self.predicates_pred: List[Predicate] = []
+        self.bridgings_pred: List[Predicate] = []
+        self.mentions_pred: List[BasePhrase] = []
         for bp in document_pred.base_phrases:
             assert bp.pas is not None, "pas has not been set"
             if PasExtractor.is_pas_target(
@@ -171,9 +171,9 @@ def __init__(
                 self.bridgings_pred.append(bp.pas.predicate)
             if self.coreference and CoreferenceExtractor.is_coreference_target(bp):
                 self.mentions_pred.append(bp)
-        self.predicates_gold: list[Predicate] = []
-        self.bridgings_gold: list[Predicate] = []
-        self.mentions_gold: list[BasePhrase] = []
+        self.predicates_gold: List[Predicate] = []
+        self.bridgings_gold: List[Predicate] = []
+        self.mentions_gold: List[BasePhrase] = []
         for bp in document_gold.base_phrases:
             assert bp.pas is not None, "pas has not been set"
             if PasExtractor.is_pas_target(
@@ -204,12 +204,12 @@ def _evaluate_pas(self) -> pd.DataFrame:
         measures = pd.DataFrame(
             [[Measure() for _ in Scorer.DEPTYPE2ANALYSIS.values()] for _ in self.cases],
             index=self.cases,
-            columns=Scorer.DEPTYPE2ANALYSIS.values(),
+            columns=list(Scorer.DEPTYPE2ANALYSIS.values()),
         )
-        global_index2predicate_pred: dict[int, Predicate] = {
+        global_index2predicate_pred: Dict[int, Predicate] = {
             pred.base_phrase.global_index: pred for pred in self.predicates_pred
         }
-        global_index2predicate_gold: dict[int, Predicate] = {
+        global_index2predicate_gold: Dict[int, Predicate] = {
             pred.base_phrase.global_index: pred for pred in self.predicates_gold
         }
 
@@ -271,7 +271,7 @@ def _evaluate_pas(self) -> pd.DataFrame:
                     measures.at[case, analysis].denom_gold += 1
         return measures
 
-    def _filter_args(self, args: list[Argument], predicate: Predicate) -> list[Argument]:
+    def _filter_args(self, args: List[Argument], predicate: Predicate) -> List[Argument]:
         filtered_args = []
         for arg in args:
             if isinstance(arg, ExophoraArgument):
@@ -293,18 +293,18 @@ def _filter_args(self, args: list[Argument], predicate: Predicate) -> list[Argum
 
     def _evaluate_bridging(self) -> pd.Series:
         """calculate bridging anaphora resolution scores"""
-        measures: dict[str, Measure] = OrderedDict((anal, Measure()) for anal in Scorer.DEPTYPE2ANALYSIS.values())
-        global_index2anaphor_pred: dict[int, Predicate] = {
+        measures: Dict[str, Measure] = OrderedDict((anal, Measure()) for anal in Scorer.DEPTYPE2ANALYSIS.values())
+        global_index2anaphor_pred: Dict[int, Predicate] = {
             pred.base_phrase.global_index: pred for pred in self.bridgings_pred
         }
-        global_index2anaphor_gold: dict[int, Predicate] = {
+        global_index2anaphor_gold: Dict[int, Predicate] = {
             pred.base_phrase.global_index: pred for pred in self.bridgings_gold
         }
 
         for global_index in range(len(self.document_pred.base_phrases)):
             if global_index in global_index2anaphor_pred:
                 anaphor_pred = global_index2anaphor_pred[global_index]
-                antecedents_pred: list[Argument] = self._filter_args(
+                antecedents_pred: List[Argument] = self._filter_args(
                     anaphor_pred.pas.get_arguments("ノ", relax=False), anaphor_pred
                 )
             else:
@@ -314,10 +314,10 @@ def _evaluate_bridging(self) -> pd.Series:
 
             if global_index in global_index2anaphor_gold:
                 anaphor_gold: Predicate = global_index2anaphor_gold[global_index]
-                antecedents_gold: list[Argument] = self._filter_args(
+                antecedents_gold: List[Argument] = self._filter_args(
                     anaphor_gold.pas.get_arguments("ノ", relax=False), anaphor_gold
                 )
-                antecedents_gold_relaxed: list[Argument] = anaphor_gold.pas.get_arguments("ノ", relax=True)
+                antecedents_gold_relaxed: List[Argument] = anaphor_gold.pas.get_arguments("ノ", relax=True)
                 antecedents_gold_relaxed += anaphor_gold.pas.get_arguments("ノ？", relax=True)
                 antecedents_gold_relaxed = self._filter_args(antecedents_gold_relaxed, anaphor_gold)
             else:
@@ -413,7 +413,7 @@ def _evaluate_coref(self) -> pd.Series:
         return pd.Series([measure], index=["all"])
 
     @staticmethod
-    def filter_mentions(tgt_mentions: set[BasePhrase], src_mention: BasePhrase) -> set[BasePhrase]:
+    def filter_mentions(tgt_mentions: Set[BasePhrase], src_mention: BasePhrase) -> Set[BasePhrase]:
         """filter out cataphors"""
         return {tgt_mention for tgt_mention in tgt_mentions if tgt_mention.global_index < src_mention.global_index}
 
@@ -426,7 +426,7 @@ class ScoreResult:
     measures_bridging: Optional[pd.Series]
     measure_coref: Optional[pd.Series]
 
-    def to_dict(self) -> dict[str, dict[str, "Measure"]]:
+    def to_dict(self) -> Dict[str, Dict[str, "Measure"]]:
         """convert data to dictionary"""
         df_all = pd.DataFrame(index=["all_case"])
         if self.pas:
diff --git a/src/kwja/evaluators/conll18_ud_eval.py b/src/kwja/evaluators/conll18_ud_eval.py
index 16f74f5d..6ee3ceb3 100644
--- a/src/kwja/evaluators/conll18_ud_eval.py
+++ b/src/kwja/evaluators/conll18_ud_eval.py
@@ -97,6 +97,7 @@
 import unicodedata
 import unittest
 from dataclasses import dataclass  # FIX
+from typing import List
 
 # CoNLL-U column names
 ID, FORM, LEMMA, UPOS, XPOS, FEATS, HEAD, DEPREL, DEPS, MISC = range(10)
@@ -181,7 +182,7 @@ def _encode(text):
 
 # Load given CoNLL-U file into internal representation
 # def load_conllu(file):
-def load_conllu(lines: list[str]):  # FIX
+def load_conllu(lines: List[str]):  # FIX
     # Internal representation classes
     class UDRepresentation:
         def __init__(self):
@@ -577,7 +578,7 @@ def align_words(gold_words, system_words):
 # def load_conllu_file(path):
 #     _file = open(path, mode="r", **({"encoding": "utf-8"} if sys.version_info >= (3, 0) else {}))
 #     return load_conllu(_file)
-def load_conllu_lines(lines: list[str]):  # FIX
+def load_conllu_lines(lines: List[str]):  # FIX
     return load_conllu(lines)
 
 
@@ -594,8 +595,8 @@ def evaluate_wrapper(args):  # FIX
 
 @dataclass  # FIX
 class Args:
-    gold_lines: list[str]
-    system_lines: list[str]
+    gold_lines: List[str]
+    system_lines: List[str]
     verbose: bool
     counts: bool
 
diff --git a/src/kwja/evaluators/dependency_parsing_metric.py b/src/kwja/evaluators/dependency_parsing_metric.py
index f8536cfd..bc4af9d5 100644
--- a/src/kwja/evaluators/dependency_parsing_metric.py
+++ b/src/kwja/evaluators/dependency_parsing_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, List, Tuple, Union
 
 import torch
 from rhoknp import BasePhrase, Document, Morpheme
@@ -28,7 +28,7 @@ def update(
         self.dependency_predictions.append(dependency_predictions)
         self.dependency_type_predictions.append(dependency_type_predictions)
 
-    def compute(self, documents: list[Document]) -> dict[str, Union[torch.Tensor, float]]:
+    def compute(self, documents: List[Document]) -> Dict[str, Union[torch.Tensor, float]]:
         sorted_indices = self.unique(self.example_ids)
         (example_ids, dependency_predictions, dependency_type_predictions) = map(
             lambda x: x[sorted_indices].tolist(),
@@ -59,10 +59,10 @@ def unique(x: torch.Tensor, dim: int = None):
 
     def _to_base_phrase_based_conll(
         self,
-        documents: list[Document],
+        documents: List[Document],
         dependency_predictions: torch.Tensor,
         dependency_type_predictions: torch.Tensor,
-    ) -> tuple[list[str], list[str]]:
+    ) -> Tuple[List[str], List[str]]:
         gold_lines, system_lines = [], []
         for document, dependency_prediction, dependency_type_prediction in zip(
             documents, dependency_predictions, dependency_type_predictions
@@ -100,10 +100,10 @@ def _to_base_phrase_based_conll(
 
     def _to_morpheme_based_conll(
         self,
-        documents: list[Document],
+        documents: List[Document],
         dependency_predictions: torch.Tensor,
         dependency_type_predictions: torch.Tensor,
-    ) -> tuple[list[str], list[str]]:
+    ) -> Tuple[List[str], List[str]]:
         gold_lines, system_lines = [], []
         for document, dependency_prediction, dependency_type_prediction in zip(
             documents, dependency_predictions, dependency_type_predictions
@@ -175,9 +175,9 @@ def get_system_predictions(
         unit: Union[BasePhrase, Morpheme],
         topk_heads: torch.Tensor,  # (k, )
         topk_dependency_types: torch.Tensor,  # (k, )
-        morpheme_global_index2unit_index: dict[int, int],
+        morpheme_global_index2unit_index: Dict[int, int],
         dependency_manager: DependencyManager,
-    ) -> tuple[int, DepType]:
+    ) -> Tuple[int, DepType]:
         src = unit.index + 1
         for head, dependency_type in zip(topk_heads, topk_dependency_types):
             system_head = morpheme_global_index2unit_index[head]
@@ -197,7 +197,7 @@ def get_system_predictions(
 
     def _resolve_dependency(
         self, unit: Union[BasePhrase, Morpheme], dependency_manager: DependencyManager
-    ) -> tuple[int, DepType]:
+    ) -> Tuple[int, DepType]:
         src = unit.index + 1
         num_units = self.get_number_of_units(unit)
         # 日本語の係り受けは基本的にleft-to-rightなので、まず右方向に係れるか調べる
diff --git a/src/kwja/evaluators/discourse_parsing_metric.py b/src/kwja/evaluators/discourse_parsing_metric.py
index 13ff8797..86f9bb4d 100644
--- a/src/kwja/evaluators/discourse_parsing_metric.py
+++ b/src/kwja/evaluators/discourse_parsing_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, Union
 
 import torch
 from torchmetrics import Metric
@@ -12,8 +12,8 @@ class DiscourseParsingMetric(Metric):
 
     def __init__(self):
         super().__init__()
-        # Metric state variables can either be torch.Tensor or an empty list which can be used to store torch.Tensors`.
-        # i.e. Expected metric state to be either a Tensor or a list of Tensor
+        # Metric state variables can either be `torch.Tensor` or an empty list which can be used to store
+        # `torch.Tensors`. i.e. Expected metric state to be either a Tensor or a list of Tensor
         self.add_state("discourse_parsing_predictions", default=list(), dist_reduce_fx="cat")  # list[torch.Tensor]
         self.add_state("discourse_parsing_labels", default=list(), dist_reduce_fx="cat")  # list[torch.Tensor]
 
@@ -25,7 +25,7 @@ def update(
         self.discourse_parsing_predictions.append(discourse_parsing_predictions)
         self.discourse_parsing_labels.append(discourse_parsing_labels)
 
-    def compute(self) -> dict[str, Union[torch.Tensor, float]]:
+    def compute(self) -> Dict[str, Union[torch.Tensor, float]]:
         predictions = self.discourse_parsing_predictions.view(-1)
         labels = self.discourse_parsing_labels.view(-1)
 
diff --git a/src/kwja/evaluators/ner_metric.py b/src/kwja/evaluators/ner_metric.py
index ba601843..afaffcbd 100644
--- a/src/kwja/evaluators/ner_metric.py
+++ b/src/kwja/evaluators/ner_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, List, Tuple, Union
 
 import numpy as np
 import torch
@@ -26,7 +26,7 @@ def update(
         self.ne_tag_predictions.append(ne_tag_predictions)
         self.ne_tags.append(ne_tags)
 
-    def compute(self) -> dict[str, Union[torch.Tensor, float]]:
+    def compute(self) -> Dict[str, Union[torch.Tensor, float]]:
         sorted_indices = self._unique(self.example_ids)
         ne_tag_predictions, ne_tags = map(
             lambda x: x[sorted_indices],
@@ -47,10 +47,10 @@ def _unique(x: torch.Tensor, dim: int = None):
         return inverse.new_empty(unique.size(0)).scatter_(0, inverse, perm)
 
     @staticmethod
-    def _align_predictions(prediction: np.ndarray, label: np.ndarray) -> tuple[list[list[str]], list[list[str]]]:
+    def _align_predictions(prediction: np.ndarray, label: np.ndarray) -> Tuple[List[List[str]], List[List[str]]]:
         batch_size, sequence_len = label.shape
-        aligned_prediction: list[list[str]] = [[] for _ in range(batch_size)]
-        aligned_label: list[list[str]] = [[] for _ in range(batch_size)]
+        aligned_prediction: List[List[str]] = [[] for _ in range(batch_size)]
+        aligned_label: List[List[str]] = [[] for _ in range(batch_size)]
         for i in range(batch_size):
             for j in range(sequence_len):
                 # 評価対象外の label / prediction は含めない
@@ -63,7 +63,7 @@ def _compute_ner_metrics(
         self,
         ne_tag_predictions: torch.Tensor,
         ne_tags: torch.Tensor,
-    ) -> dict[str, float]:
+    ) -> Dict[str, float]:
         ner_metric = {}
         aligned_predictions, aligned_labels = self._align_predictions(
             prediction=ne_tag_predictions.detach().cpu().numpy(),
diff --git a/src/kwja/evaluators/phrase_analysis_metric.py b/src/kwja/evaluators/phrase_analysis_metric.py
index 53e9d83a..b2d2095a 100644
--- a/src/kwja/evaluators/phrase_analysis_metric.py
+++ b/src/kwja/evaluators/phrase_analysis_metric.py
@@ -1,5 +1,5 @@
 from itertools import chain
-from typing import Literal
+from typing import Dict, List, Literal, Tuple
 
 import numpy as np
 import torch
@@ -33,7 +33,7 @@ def update(
         self.base_phrase_feature_predictions.append(base_phrase_feature_predictions)
         self.base_phrase_features.append(base_phrase_features)
 
-    def compute(self) -> dict[str, float]:
+    def compute(self) -> Dict[str, float]:
         sorted_indices = self.unique(self.example_ids)
         # (num_base_phrase_features, b, seq)
         (word_feature_predictions, word_features, base_phrase_feature_predictions, base_phrase_features,) = map(
@@ -60,10 +60,10 @@ def unique(x: torch.Tensor, dim: int = None):
     @staticmethod
     def align_predictions(
         prediction: np.ndarray, label: np.ndarray, io_tag: Literal["I", "O"]
-    ) -> tuple[list[list[str]], list[list[str]]]:
+    ) -> Tuple[List[List[str]], List[List[str]]]:
         b, seq_len = label.shape
-        aligned_prediction: list[list[str]] = [[] for _ in range(b)]
-        aligned_label: list[list[str]] = [[] for _ in range(b)]
+        aligned_prediction: List[List[str]] = [[] for _ in range(b)]
+        aligned_label: List[List[str]] = [[] for _ in range(b)]
         for i in range(b):
             for j in range(seq_len):
                 # 評価対象外の label / prediction は含めない
@@ -76,7 +76,7 @@ def compute_word_feature_metrics(
         self,
         word_feature_predictions: torch.Tensor,
         word_features: torch.Tensor,
-    ) -> dict[str, float]:
+    ) -> Dict[str, float]:
         word_feature_metrics = {}
         aligned_predictions, aligned_labels = [], []
         for i, (word_feature_prediction, word_feature_label) in enumerate(zip(word_feature_predictions, word_features)):
@@ -116,7 +116,7 @@ def compute_base_phrase_feature_metrics(
         self,
         base_phrase_feature_predictions: torch.Tensor,
         base_phrase_features: torch.Tensor,
-    ) -> dict[str, float]:
+    ) -> Dict[str, float]:
         base_phrase_feature_metrics = {}
         aligned_predictions, aligned_labels = [], []
         for i, (base_phrase_feature_prediction, base_phrase_feature_label) in enumerate(
diff --git a/src/kwja/evaluators/reading_predictor_metric.py b/src/kwja/evaluators/reading_predictor_metric.py
index ed7bf87d..f2199afc 100644
--- a/src/kwja/evaluators/reading_predictor_metric.py
+++ b/src/kwja/evaluators/reading_predictor_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, Union
 
 import torch
 from torchmetrics import Metric
@@ -17,7 +17,7 @@ def update(self, predictions: torch.Tensor, labels: torch.Tensor) -> None:
         self.predictions.append(predictions)
         self.labels.append(labels)
 
-    def compute(self) -> dict[str, Union[torch.Tensor, float]]:
+    def compute(self) -> Dict[str, Union[torch.Tensor, float]]:
         predictions = self.predictions.view(-1)
         labels = self.labels.view(-1)
         ignore_indexes = labels == IGNORE_INDEX
diff --git a/src/kwja/evaluators/typo_correction_metric.py b/src/kwja/evaluators/typo_correction_metric.py
index 235b7d5d..cc80e8f2 100644
--- a/src/kwja/evaluators/typo_correction_metric.py
+++ b/src/kwja/evaluators/typo_correction_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, Union
 
 import torch
 from torchmetrics.functional import accuracy
@@ -10,11 +10,11 @@ def __init__(self) -> None:
 
     @staticmethod
     def compute_metrics(
-        outputs: dict[str, torch.Tensor],
-        batch: dict[str, torch.Tensor],
-    ) -> dict[str, Union[torch.Tensor, float]]:
+        outputs: Dict[str, torch.Tensor],
+        batch: Dict[str, torch.Tensor],
+    ) -> Dict[str, Union[torch.Tensor, float]]:
 
-        metrics: dict[str, Union[torch.Tensor, float]] = dict(loss=outputs["loss"])
+        metrics: Dict[str, Union[torch.Tensor, float]] = dict(loss=outputs["loss"])
         for key in outputs.keys():
             if "_loss" in key:
                 metrics[key] = outputs[key].detach()
diff --git a/src/kwja/evaluators/word_analysis_metric.py b/src/kwja/evaluators/word_analysis_metric.py
index a2e74c61..07d6ed98 100644
--- a/src/kwja/evaluators/word_analysis_metric.py
+++ b/src/kwja/evaluators/word_analysis_metric.py
@@ -1,3 +1,5 @@
+from typing import Dict, List
+
 import torch
 from torchmetrics import Metric
 from torchmetrics.functional import f1_score
@@ -44,8 +46,8 @@ def _convert(preds: torch.Tensor, labels: torch.Tensor):
         converted_labels = torch.masked_select(labels, ignore_index_pos)
         return converted_preds, converted_labels
 
-    def compute(self) -> dict[str, float]:
-        metrics: dict[str, float] = {}
+    def compute(self) -> Dict[str, float]:
+        metrics: Dict[str, float] = {}
         pos_preds, pos_labels = self._convert(preds=self.pos_preds, labels=self.pos_labels)
         metrics["pos_f1"] = f1_score(
             preds=pos_preds,
@@ -66,7 +68,7 @@ def compute(self) -> dict[str, float]:
             preds=conjform_preds,
             target=conjform_labels,
         ).item()
-        f1s: list[float] = [
+        f1s: List[float] = [
             metrics["pos_f1"],
             metrics["subpos_f1"],
             metrics["conjtype_f1"],
diff --git a/src/kwja/evaluators/word_normalization_metric.py b/src/kwja/evaluators/word_normalization_metric.py
index b9ca5849..e623ca96 100644
--- a/src/kwja/evaluators/word_normalization_metric.py
+++ b/src/kwja/evaluators/word_normalization_metric.py
@@ -1,4 +1,4 @@
-from typing import Union
+from typing import Dict, Union
 
 import torch
 from sklearn.metrics import accuracy_score
@@ -19,7 +19,7 @@ def update(self, norm_preds: torch.Tensor, norm_types: torch.Tensor) -> None:
         self.norm_types.append(norm_types[~ignore_indexes])
 
     def compute(self):
-        metrics: dict[str, Union[torch.Tensor, float]] = dict()
+        metrics: Dict[str, Union[torch.Tensor, float]] = dict()
         preds = self.norm_preds.cpu()
         labels = self.norm_types.cpu()
 
diff --git a/src/kwja/evaluators/word_segmentation_metric.py b/src/kwja/evaluators/word_segmentation_metric.py
index 759a7299..a17c9608 100644
--- a/src/kwja/evaluators/word_segmentation_metric.py
+++ b/src/kwja/evaluators/word_segmentation_metric.py
@@ -1,4 +1,4 @@
-from typing import Tuple, Union
+from typing import Dict, List, Tuple, Union
 
 import numpy as np
 import torch
@@ -18,7 +18,7 @@ def __init__(self) -> None:
         self.add_state("seg_types", default=[], dist_reduce_fx="cat")
 
     @staticmethod
-    def filter_predictions(preds: list[str], labels: list[str]) -> Tuple[list[str], list[str]]:
+    def filter_predictions(preds: List[str], labels: List[str]) -> Tuple[List[str], List[str]]:
         filtered_preds = []
         filtered_labels = []
         for pred, label in zip(preds, labels):
@@ -29,10 +29,10 @@ def filter_predictions(preds: list[str], labels: list[str]) -> Tuple[list[str],
         return filtered_preds, filtered_labels
 
     @staticmethod
-    def _align_predictions(prediction: np.ndarray, label: np.ndarray) -> tuple[list[list[str]], list[list[str]]]:
+    def _align_predictions(prediction: np.ndarray, label: np.ndarray) -> Tuple[List[List[str]], List[List[str]]]:
         batch_size, sequence_len = label.shape
-        aligned_prediction: list[list[str]] = [[] for _ in range(batch_size)]
-        aligned_label: list[list[str]] = [[] for _ in range(batch_size)]
+        aligned_prediction: List[List[str]] = [[] for _ in range(batch_size)]
+        aligned_label: List[List[str]] = [[] for _ in range(batch_size)]
         for i in range(batch_size):
             for j in range(sequence_len):
                 # 評価対象外の label / prediction は含めない
@@ -46,7 +46,7 @@ def update(self, seg_preds: torch.Tensor, seg_types: torch.Tensor) -> None:
         self.seg_types.append(seg_types)
 
     def compute(self):
-        metrics: dict[str, Union[torch.Tensor, float]] = dict()
+        metrics: Dict[str, Union[torch.Tensor, float]] = dict()
         aligned_predictions, aligned_labels = self._align_predictions(
             prediction=self.seg_preds.detach().cpu().numpy(),
             label=self.seg_types.detach().cpu().numpy(),
diff --git a/src/kwja/models/char_module.py b/src/kwja/models/char_module.py
index 62e6461f..4630c118 100644
--- a/src/kwja/models/char_module.py
+++ b/src/kwja/models/char_module.py
@@ -1,4 +1,5 @@
-from typing import Any, Optional
+import copy
+from typing import Any, Dict, Optional
 
 import hydra
 import torch
@@ -29,22 +30,22 @@ def __init__(self, hparams: DictConfig) -> None:
         pretrained_model_config: PretrainedConfig = self.char_encoder.pretrained_model.config
 
         self.word_segmenter = WordSegmenter(hparams, pretrained_model_config)
-        self.valid_word_segmenter_metrics: dict[str, WordSegmentationMetric] = {
+        self.valid_word_segmenter_metrics: Dict[str, WordSegmentationMetric] = {
             corpus: WordSegmentationMetric() for corpus in self.valid_corpora
         }
-        self.test_word_segmenter_metrics: dict[str, WordSegmentationMetric] = {
+        self.test_word_segmenter_metrics: Dict[str, WordSegmentationMetric] = {
             corpus: WordSegmentationMetric() for corpus in self.test_corpora
         }
 
         self.word_normalizer = WordNormalizer(hparams, pretrained_model_config)
-        self.valid_word_normalizer_metrics: dict[str, WordNormalizationMetric] = {
+        self.valid_word_normalizer_metrics: Dict[str, WordNormalizationMetric] = {
             corpus: WordNormalizationMetric() for corpus in self.valid_corpora
         }
-        self.test_word_normalizer_metrics: dict[str, WordNormalizationMetric] = {
+        self.test_word_normalizer_metrics: Dict[str, WordNormalizationMetric] = {
             corpus: WordNormalizationMetric() for corpus in self.test_corpora
         }
 
-    def forward(self, **kwargs) -> dict[str, dict[str, torch.Tensor]]:
+    def forward(self, **kwargs) -> Dict[str, Dict[str, torch.Tensor]]:
         encoder_output = self.char_encoder(kwargs)  # (b, seq_len, h)
         word_segmenter_outputs = self.word_segmenter(encoder_output, kwargs)
         word_normalizer_outputs = self.word_normalizer(encoder_output, kwargs)
@@ -54,7 +55,7 @@ def forward(self, **kwargs) -> dict[str, dict[str, torch.Tensor]]:
         }
 
     def training_step(self, batch: Any, batch_idx: int) -> STEP_OUTPUT:
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         word_segmenter_loss = outputs["word_segmenter_outputs"]["loss"]
         self.log("train/word_segmenter_loss", word_segmenter_loss)
         word_normalizer_loss = outputs["word_normalizer_outputs"]["loss"]
@@ -62,7 +63,7 @@ def training_step(self, batch: Any, batch_idx: int) -> STEP_OUTPUT:
         return word_segmenter_loss + word_normalizer_loss
 
     def validation_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any:
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         corpus = self.valid_corpora[dataloader_idx or 0]
         word_segmenter_args = {
             "seg_preds": torch.argmax(outputs["word_segmenter_outputs"]["logits"], dim=-1),
@@ -101,7 +102,7 @@ def validation_epoch_end(self, validation_step_outputs) -> None:
         self.log("valid/f1", (word_segmenter_f1 + word_normalizer_f1) / 2)
 
     def test_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any:
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         corpus = self.test_corpora[dataloader_idx or 0]
         word_segmenter_args = {
             "seg_preds": torch.argmax(outputs["word_segmenter_outputs"]["logits"], dim=-1),
@@ -139,7 +140,7 @@ def test_epoch_end(self, test_step_outputs) -> None:
         self.log("valid/word_normalizer_f1", word_normalizer_f1)
 
     def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any:
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         return {
             "example_ids": batch["example_ids"],
             "dataloader_idx": dataloader_idx or 0,
@@ -184,8 +185,8 @@ def configure_optimizers(self):
             "lr_scheduler": {"scheduler": lr_scheduler, "interval": "step", "frequency": 1},
         }
 
-    def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None:
-        hparams = checkpoint["hyper_parameters"]["hparams"]
+    def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None:
+        hparams: DictConfig = copy.deepcopy(checkpoint["hyper_parameters"])
         OmegaConf.set_struct(hparams, False)
         hparams = filter_dict_items(hparams, self.hparams.hparams_to_ignore_on_save)
         checkpoint["hyper_parameters"] = {"hparams": hparams}
diff --git a/src/kwja/models/models/char_encoder.py b/src/kwja/models/models/char_encoder.py
index 3a2f8906..53bbb37f 100644
--- a/src/kwja/models/models/char_encoder.py
+++ b/src/kwja/models/models/char_encoder.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import torch
 import torch.nn as nn
 from omegaconf import DictConfig
@@ -18,7 +20,7 @@ def __init__(self, hparams: DictConfig) -> None:
             )
         self.word_embed = self.pretrained_model.embeddings.word_embeddings
 
-    def forward(self, inputs: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
+    def forward(self, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
         outputs = self.pretrained_model(
             input_ids=inputs["input_ids"],
             attention_mask=inputs["attention_mask"],
diff --git a/src/kwja/models/models/cohesion_analyzer.py b/src/kwja/models/models/cohesion_analyzer.py
index abc1a04a..598d383c 100644
--- a/src/kwja/models/models/cohesion_analyzer.py
+++ b/src/kwja/models/models/cohesion_analyzer.py
@@ -1,3 +1,5 @@
+from typing import Tuple
+
 import torch
 import torch.nn as nn
 from transformers import PretrainedConfig
@@ -19,7 +21,7 @@ def __init__(self, pretrained_model_config: PretrainedConfig, num_rels: int) ->
     def forward(
         self,
         pooled_outputs: torch.Tensor,  # (b, seq, hid)
-    ) -> tuple[torch.Tensor, torch.Tensor]:  # (), (b, seq, seq)
+    ) -> Tuple[torch.Tensor, torch.Tensor]:  # (), (b, seq, seq)
         batch_size, sequence_len, _ = pooled_outputs.size()
 
         h_src = self.l_src(self.dropout(pooled_outputs))  # (b, seq, rel*hid)
diff --git a/src/kwja/models/models/phrase_analyzer.py b/src/kwja/models/models/phrase_analyzer.py
index a74011a3..7422d966 100644
--- a/src/kwja/models/models/phrase_analyzer.py
+++ b/src/kwja/models/models/phrase_analyzer.py
@@ -1,6 +1,6 @@
 from collections import OrderedDict
 from math import sqrt
-from typing import Optional
+from typing import Dict, List, Optional, Tuple
 
 import torch
 import torch.nn as nn
@@ -59,8 +59,8 @@ def __init__(self, pretrained_model_config: PretrainedConfig) -> None:
             )
         )
 
-    def forward(self, pooled_outputs: torch.Tensor, batch: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        outputs: dict[str, torch.Tensor] = {}
+    def forward(self, pooled_outputs: torch.Tensor, batch: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        outputs: Dict[str, torch.Tensor] = {}
         ne_logits, emissions = self.compute_ne_logits(pooled_outputs, batch)
         outputs["ne_logits"] = ne_logits
         word_feature_logits = self.word_feature_head(pooled_outputs)  # (batch_size, seq_length, num_word_features)
@@ -90,8 +90,8 @@ def forward(self, pooled_outputs: torch.Tensor, batch: dict[str, torch.Tensor])
         return outputs
 
     def compute_ne_logits(
-        self, pooled_outputs: torch.Tensor, batch: dict[str, torch.Tensor]
-    ) -> tuple[torch.Tensor, torch.Tensor]:
+        self, pooled_outputs: torch.Tensor, batch: Dict[str, torch.Tensor]
+    ) -> Tuple[torch.Tensor, torch.Tensor]:
         ne_logits = self.ne_head(pooled_outputs)  # (batch_size, seq_length, num_tags)
         device, shape = map(lambda x: getattr(ne_logits, x), ["device", "shape"])
         non_target = torch.full_like(ne_logits, -1024.0)
@@ -143,7 +143,7 @@ class CRF(nn.Module):
     .. _Viterbi algorithm: https://en.wikipedia.org/wiki/Viterbi_algorithm
     """
 
-    def __init__(self, tags: tuple[str, ...], batch_first: bool = True) -> None:
+    def __init__(self, tags: Tuple[str, ...], batch_first: bool = True) -> None:
         num_tags = len(tags)
         assert num_tags > 0, f"invalid number of tags: {num_tags}"
         super().__init__()
@@ -156,7 +156,7 @@ def __init__(self, tags: tuple[str, ...], batch_first: bool = True) -> None:
 
         self.reset_parameters(tags)
 
-    def reset_parameters(self, tags: tuple[str, ...]) -> None:
+    def reset_parameters(self, tags: Tuple[str, ...]) -> None:
         bound = sqrt(6 / self.num_tags)
         nn.init.uniform_(self.start_transitions, -bound, bound)
         nn.init.uniform_(self.end_transitions, -bound, bound)
@@ -228,7 +228,7 @@ def forward(
         assert reduction == "token_mean"
         return llh.sum() / mask.type_as(emissions).sum()
 
-    def decode(self, emissions: torch.FloatTensor, mask: Optional[torch.ByteTensor] = None) -> list[list[int]]:
+    def decode(self, emissions: torch.FloatTensor, mask: Optional[torch.ByteTensor] = None) -> List[List[int]]:
         """Find the most likely tag sequence using Viterbi algorithm.
 
         Args:
@@ -353,7 +353,7 @@ def _compute_normalizer(self, emissions: torch.FloatTensor, mask: torch.ByteTens
         # Sum (log-sum-exp) over all possible tags
         return torch.logsumexp(score, dim=1)  # (batch_size,)
 
-    def _viterbi_decode(self, emissions: torch.FloatTensor, mask: torch.ByteTensor) -> list[list[int]]:
+    def _viterbi_decode(self, emissions: torch.FloatTensor, mask: torch.ByteTensor) -> List[List[int]]:
         # emissions: (seq_length, batch_size, num_tags)
         # mask: (seq_length, batch_size)
         assert emissions.dim() == 3 and mask.dim() == 2
diff --git a/src/kwja/models/models/reading_predictor.py b/src/kwja/models/models/reading_predictor.py
index 31feba5c..daca56fe 100644
--- a/src/kwja/models/models/reading_predictor.py
+++ b/src/kwja/models/models/reading_predictor.py
@@ -1,5 +1,6 @@
 from collections import OrderedDict
 from pathlib import Path
+from typing import Dict
 
 import torch
 import torch.nn as nn
@@ -30,8 +31,8 @@ def __init__(self, reading_resource_path: str, pretrained_model_config: Pretrain
             )
         )
 
-    def forward(self, pooled_outputs: torch.Tensor, batch: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        outputs: dict[str, torch.Tensor] = {}
+    def forward(self, pooled_outputs: torch.Tensor, batch: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        outputs: Dict[str, torch.Tensor] = {}
         reading_predictor_logits = self.reading_predictor_head(pooled_outputs)  # (b, seq, num_labels)
         outputs["logits"] = reading_predictor_logits
         if "reading_ids" in batch:
diff --git a/src/kwja/models/models/relation_analyzer.py b/src/kwja/models/models/relation_analyzer.py
index fd487163..2e6a1a9b 100644
--- a/src/kwja/models/models/relation_analyzer.py
+++ b/src/kwja/models/models/relation_analyzer.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import torch
 import torch.nn as nn
 import torch.nn.functional as F
@@ -30,9 +32,9 @@ def __init__(self, hparams: DictConfig, pretrained_model_config: PretrainedConfi
     def forward(
         self,
         pooled_outputs: torch.Tensor,
-        batch: dict[str, torch.Tensor],
-    ) -> dict[str, torch.Tensor]:
-        output: dict[str, torch.Tensor] = dict()
+        batch: Dict[str, torch.Tensor],
+    ) -> Dict[str, torch.Tensor]:
+        output: Dict[str, torch.Tensor] = dict()
         # (b, seq, seq)
         dependency_logits, dependency_type_logits = self.dependency_parser(
             pooled_outputs,
diff --git a/src/kwja/models/models/typo_corrector.py b/src/kwja/models/models/typo_corrector.py
index c3145b84..6e55ad89 100644
--- a/src/kwja/models/models/typo_corrector.py
+++ b/src/kwja/models/models/typo_corrector.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import hydra
 import torch
 import torch.nn as nn
@@ -38,8 +40,8 @@ def __init__(
         assert self.tokenizer.pad_token_id is not None
         self.pad_token_id: int = self.tokenizer.pad_token_id
 
-    def forward(self, encoder_output: torch.Tensor, inputs: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        output: dict[str, torch.Tensor] = dict()
+    def forward(self, encoder_output: torch.Tensor, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        output: Dict[str, torch.Tensor] = dict()
         kdr_logits = self.kdr_cls(encoder_output)  # (b, seq_len, kdr_label_num)
         output["kdr_logits"] = kdr_logits
         ins_logits = self.ins_cls(encoder_output)  # (b, seq_len, ins_label_num)
diff --git a/src/kwja/models/models/word_analyzer.py b/src/kwja/models/models/word_analyzer.py
index dabe4c11..afd72fe5 100644
--- a/src/kwja/models/models/word_analyzer.py
+++ b/src/kwja/models/models/word_analyzer.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import torch
 import torch.nn as nn
 import torch.nn.functional as F
@@ -24,8 +26,8 @@ def __init__(self, pretrained_model_config: PretrainedConfig) -> None:
         self.conjtype_cls = nn.Linear(hidden_size, self.num_conjtype_labels)
         self.conjform_cls = nn.Linear(hidden_size, self.num_conjform_labels)
 
-    def forward(self, encoder_output: torch.Tensor, inputs: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        output: dict[str, torch.Tensor] = dict()
+    def forward(self, encoder_output: torch.Tensor, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        output: Dict[str, torch.Tensor] = dict()
         base_output = self.base_cls(encoder_output)  # (b, seq_len, h)
         pos_logits = self.pos_cls(base_output)  # (b, seq_len, pos_label_num)
         subpos_logits = self.subpos_cls(base_output)  # (b, seq_len, subpos_label_num)
diff --git a/src/kwja/models/models/word_encoder.py b/src/kwja/models/models/word_encoder.py
index 4a679fd2..172d8dde 100644
--- a/src/kwja/models/models/word_encoder.py
+++ b/src/kwja/models/models/word_encoder.py
@@ -1,3 +1,5 @@
+from typing import Dict, Tuple
+
 import torch
 import torch.nn as nn
 from omegaconf import DictConfig
@@ -19,8 +21,8 @@ def __init__(self, hparams: DictConfig) -> None:
             )
 
     def forward(
-        self, batch: dict[str, torch.Tensor], pooling_strategy: PoolingStrategy
-    ) -> tuple[torch.Tensor, torch.Tensor]:
+        self, batch: Dict[str, torch.Tensor], pooling_strategy: PoolingStrategy
+    ) -> Tuple[torch.Tensor, torch.Tensor]:
         outputs = self.pretrained_model(
             input_ids=batch["input_ids"],
             attention_mask=batch["attention_mask"],
diff --git a/src/kwja/models/models/word_normalizer.py b/src/kwja/models/models/word_normalizer.py
index 316c93db..e59475e6 100644
--- a/src/kwja/models/models/word_normalizer.py
+++ b/src/kwja/models/models/word_normalizer.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import torch
 import torch.nn as nn
 import torch.nn.functional as F
@@ -21,8 +23,8 @@ def __init__(self, hparams: DictConfig, pretrained_model_config: PretrainedConfi
             nn.Linear(self.hidden_size, self.num_labels),
         )
 
-    def forward(self, encoder_output: torch.Tensor, inputs: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        output: dict[str, torch.Tensor] = dict()
+    def forward(self, encoder_output: torch.Tensor, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        output: Dict[str, torch.Tensor] = dict()
         logits = self.cls(encoder_output)  # (b, seq_len, word_norm_label_num)
         output["logits"] = logits
         if "norm_types" in inputs:
diff --git a/src/kwja/models/models/word_segmenter.py b/src/kwja/models/models/word_segmenter.py
index f0815d92..b0b7814d 100644
--- a/src/kwja/models/models/word_segmenter.py
+++ b/src/kwja/models/models/word_segmenter.py
@@ -1,3 +1,5 @@
+from typing import Dict
+
 import torch
 import torch.nn as nn
 import torch.nn.functional as F
@@ -21,8 +23,8 @@ def __init__(self, hparams: DictConfig, pretrained_model_config: PretrainedConfi
             nn.Linear(self.hidden_size, self.num_labels),
         )
 
-    def forward(self, encoder_output: torch.Tensor, inputs: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
-        output: dict[str, torch.Tensor] = dict()
+    def forward(self, encoder_output: torch.Tensor, inputs: Dict[str, torch.Tensor]) -> Dict[str, torch.Tensor]:
+        output: Dict[str, torch.Tensor] = dict()
         logits = self.cls(encoder_output)  # (b, seq_len, seg_label_num)
         output["logits"] = logits
         if "seg_types" in inputs:
diff --git a/src/kwja/models/typo_module.py b/src/kwja/models/typo_module.py
index 17bffdbb..c76d2668 100644
--- a/src/kwja/models/typo_module.py
+++ b/src/kwja/models/typo_module.py
@@ -1,4 +1,5 @@
-from typing import Any, Optional, Union
+import copy
+from typing import Any, Dict, Optional, Union
 
 import hydra
 import torch
@@ -32,29 +33,29 @@ def forward(self, **kwargs):
         model_output = self.model(encoder_output, kwargs)
         return model_output
 
-    def training_step(self, batch: Any, batch_idx: int) -> dict[str, Any]:
-        outputs: dict[str, torch.Tensor] = self(**batch)
-        result: dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
+    def training_step(self, batch: Any, batch_idx: int) -> Dict[str, Any]:
+        outputs: Dict[str, torch.Tensor] = self(**batch)
+        result: Dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
         for name, value in result.items():
             self.log(f"train/{name}", value)
         return result
 
-    def validation_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> dict[str, Any]:
-        outputs: dict[str, torch.Tensor] = self(**batch)
-        result: dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
+    def validation_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Dict[str, Any]:
+        outputs: Dict[str, torch.Tensor] = self(**batch)
+        result: Dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
         for name, value in result.items():
             self.log(f"valid/{name}", value)
         return result
 
-    def test_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> dict[str, Any]:
-        outputs: dict[str, torch.Tensor] = self(**batch)
-        result: dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
+    def test_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Dict[str, Any]:
+        outputs: Dict[str, torch.Tensor] = self(**batch)
+        result: Dict[str, Union[torch.Tensor, float]] = self.metrics.compute_metrics(outputs, batch)
         for name, value in result.items():
             self.log(f"test/{name}", value)
         return result
 
     def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any:
-        outputs: dict[str, torch.Tensor] = self(**batch)
+        outputs: Dict[str, torch.Tensor] = self(**batch)
         # the prediction of the first token (= [CLS]) is excluded.
         kdr_probs = torch.softmax(outputs["kdr_logits"][:, 1:, :], dim=-1)  # (b, seq_len - 1, kdr_label_num)
         kdr_values, kdr_indices = torch.max(kdr_probs, dim=-1)
@@ -108,8 +109,8 @@ def configure_optimizers(self):
             },
         }
 
-    def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None:
-        hparams = checkpoint["hyper_parameters"]["hparams"]
+    def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None:
+        hparams: DictConfig = copy.deepcopy(checkpoint["hyper_parameters"])
         OmegaConf.set_struct(hparams, False)
         hparams = filter_dict_items(hparams, self.hparams.hparams_to_ignore_on_save)
         checkpoint["hyper_parameters"] = {"hparams": hparams}
diff --git a/src/kwja/models/word_module.py b/src/kwja/models/word_module.py
index e7d5d1f6..27e72e7b 100644
--- a/src/kwja/models/word_module.py
+++ b/src/kwja/models/word_module.py
@@ -1,7 +1,8 @@
+import copy
 from enum import Enum
 from pathlib import Path
 from statistics import mean
-from typing import Any, Optional
+from typing import Any, Dict, Optional
 
 import hydra
 import torch
@@ -54,54 +55,54 @@ def __init__(self, hparams: DictConfig) -> None:
         self.reading_predictor: ReadingPredictor = ReadingPredictor(
             hparams.dataset.reading_resource_path, pretrained_model_config
         )
-        self.valid_reading_predictor_metrics: dict[str, ReadingPredictorMetric] = {
+        self.valid_reading_predictor_metrics: Dict[str, ReadingPredictorMetric] = {
             corpus: ReadingPredictorMetric() for corpus in self.valid_corpora
         }
-        self.test_reading_predictor_metrics: dict[str, ReadingPredictorMetric] = {
+        self.test_reading_predictor_metrics: Dict[str, ReadingPredictorMetric] = {
             corpus: ReadingPredictorMetric() for corpus in self.test_corpora
         }
 
         self.word_analyzer: WordAnalyzer = WordAnalyzer(pretrained_model_config)
-        self.valid_word_analysis_metrics: dict[str, WordAnalysisMetric] = {
+        self.valid_word_analysis_metrics: Dict[str, WordAnalysisMetric] = {
             corpus: WordAnalysisMetric() for corpus in self.valid_corpora
         }
-        self.test_word_analysis_metrics: dict[str, WordAnalysisMetric] = {
+        self.test_word_analysis_metrics: Dict[str, WordAnalysisMetric] = {
             corpus: WordAnalysisMetric() for corpus in self.test_corpora
         }
-        self.valid_ner_metrics: dict[str, NERMetric] = {corpus: NERMetric() for corpus in self.valid_corpora}
-        self.test_ner_metrics: dict[str, NERMetric] = {corpus: NERMetric() for corpus in self.test_corpora}
+        self.valid_ner_metrics: Dict[str, NERMetric] = {corpus: NERMetric() for corpus in self.valid_corpora}
+        self.test_ner_metrics: Dict[str, NERMetric] = {corpus: NERMetric() for corpus in self.test_corpora}
 
         self.phrase_analyzer: PhraseAnalyzer = PhraseAnalyzer(pretrained_model_config)
-        self.valid_phrase_analysis_metrics: dict[str, PhraseAnalysisMetric] = {
+        self.valid_phrase_analysis_metrics: Dict[str, PhraseAnalysisMetric] = {
             corpus: PhraseAnalysisMetric() for corpus in self.valid_corpora
         }
-        self.test_phrase_analysis_metrics: dict[str, PhraseAnalysisMetric] = {
+        self.test_phrase_analysis_metrics: Dict[str, PhraseAnalysisMetric] = {
             corpus: PhraseAnalysisMetric() for corpus in self.test_corpora
         }
 
         self.relation_analyzer: RelationAnalyzer = RelationAnalyzer(hparams, pretrained_model_config)
-        self.valid_dependency_parsing_metrics: dict[str, DependencyParsingMetric] = {
+        self.valid_dependency_parsing_metrics: Dict[str, DependencyParsingMetric] = {
             corpus: DependencyParsingMetric() for corpus in self.valid_corpora
         }
-        self.test_dependency_parsing_metrics: dict[str, DependencyParsingMetric] = {
+        self.test_dependency_parsing_metrics: Dict[str, DependencyParsingMetric] = {
             corpus: DependencyParsingMetric() for corpus in self.test_corpora
         }
-        self.valid_cohesion_analysis_metrics: dict[str, CohesionAnalysisMetric] = {
+        self.valid_cohesion_analysis_metrics: Dict[str, CohesionAnalysisMetric] = {
             corpus: CohesionAnalysisMetric() for corpus in self.valid_corpora
         }
-        self.test_cohesion_analysis_metrics: dict[str, CohesionAnalysisMetric] = {
+        self.test_cohesion_analysis_metrics: Dict[str, CohesionAnalysisMetric] = {
             corpus: CohesionAnalysisMetric() for corpus in self.test_corpora
         }
-        self.valid_discourse_parsing_metrics: dict[str, DiscourseParsingMetric] = {
+        self.valid_discourse_parsing_metrics: Dict[str, DiscourseParsingMetric] = {
             corpus: DiscourseParsingMetric() for corpus in self.valid_corpora
         }
-        self.test_discourse_parsing_metrics: dict[str, DiscourseParsingMetric] = {
+        self.test_discourse_parsing_metrics: Dict[str, DiscourseParsingMetric] = {
             corpus: DiscourseParsingMetric() for corpus in self.test_corpora
         }
 
         self.discourse_parsing_threshold: float = self.hparams.discourse_parsing_threshold
 
-    def forward(self, **batch) -> dict[str, dict[str, torch.Tensor]]:
+    def forward(self, **batch) -> Dict[str, Dict[str, torch.Tensor]]:
         # (batch_size, seq_len, hidden_size)
         hast_hidden_states, pooled_outputs = self.word_encoder(batch, PoolingStrategy.FIRST)
         reading_predictor_outputs = self.reading_predictor(hast_hidden_states, batch)
@@ -117,7 +118,7 @@ def forward(self, **batch) -> dict[str, dict[str, torch.Tensor]]:
 
     def training_step(self, batch: Any, batch_idx: int) -> torch.Tensor:
         batch["training"] = True
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         loss = torch.tensor(0.0, device=self.device)
         if WordTask.READING_PREDICTION in self.training_tasks:
             reading_predictor_loss = outputs["reading_predictor_outputs"]["loss"]
@@ -158,7 +159,7 @@ def training_step(self, batch: Any, batch_idx: int) -> torch.Tensor:
 
     def validation_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> None:
         batch["training"] = False
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         corpus = self.valid_corpora[dataloader_idx or 0]
 
         reading_predictor_metric_args = {
@@ -238,7 +239,7 @@ def validation_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[i
         self.log("valid/discourse_parsing_loss", outputs["relation_analyzer_outputs"]["discourse_parsing_loss"])
 
     def validation_epoch_end(self, validation_step_outputs) -> None:
-        log_metrics: dict[str, dict[str, float]] = {corpus: {} for corpus in self.valid_corpora}
+        log_metrics: Dict[str, Dict[str, float]] = {corpus: {} for corpus in self.valid_corpora}
 
         for corpus, metric in self.valid_reading_predictor_metrics.items():
             if WordTask.READING_PREDICTION in self.training_tasks:
@@ -302,7 +303,7 @@ def validation_epoch_end(self, validation_step_outputs) -> None:
 
     def test_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> None:
         batch["training"] = False
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         corpus = self.test_corpora[dataloader_idx or 0]
 
         reading_predictor_metric_args = {
@@ -382,7 +383,7 @@ def test_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] =
         self.log("test/discourse_parsing_loss", outputs["relation_analyzer_outputs"]["discourse_parsing_loss"])
 
     def test_epoch_end(self, test_step_outputs) -> None:
-        log_metrics: dict[str, dict[str, float]] = {corpus: {} for corpus in self.test_corpora}
+        log_metrics: Dict[str, Dict[str, float]] = {corpus: {} for corpus in self.test_corpora}
 
         for corpus, metric in self.test_reading_predictor_metrics.items():
             if WordTask.READING_PREDICTION in self.training_tasks:
@@ -449,7 +450,7 @@ def test_epoch_end(self, test_step_outputs) -> None:
 
     def predict_step(self, batch: Any, batch_idx: int, dataloader_idx: Optional[int] = None) -> Any:
         batch["training"] = False
-        outputs: dict[str, dict[str, torch.Tensor]] = self(**batch)
+        outputs: Dict[str, Dict[str, torch.Tensor]] = self(**batch)
         return {
             "tokens": batch["tokens"],
             "example_ids": batch["example_ids"],
@@ -505,8 +506,8 @@ def configure_optimizers(self):
             "lr_scheduler": {"scheduler": lr_scheduler, "interval": "step", "frequency": 1},
         }
 
-    def on_save_checkpoint(self, checkpoint: dict[str, Any]) -> None:
-        hparams = checkpoint["hyper_parameters"]["hparams"]
+    def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None:
+        hparams: DictConfig = copy.deepcopy(checkpoint["hyper_parameters"])
         OmegaConf.set_struct(hparams, False)
         hparams = filter_dict_items(hparams, self.hparams.hparams_to_ignore_on_save)
         checkpoint["hyper_parameters"] = {"hparams": hparams}
diff --git a/src/kwja/preprocessors/preprocess_typo.py b/src/kwja/preprocessors/preprocess_typo.py
index cc1da257..b6ed3005 100644
--- a/src/kwja/preprocessors/preprocess_typo.py
+++ b/src/kwja/preprocessors/preprocess_typo.py
@@ -7,7 +7,7 @@
 from dataclasses import dataclass
 from enum import Enum
 from pathlib import Path
-from typing import Optional, Tuple
+from typing import List, Optional, Tuple
 
 from Levenshtein import opcodes
 
@@ -39,7 +39,7 @@ def __init__(self):
         pass
 
     @staticmethod
-    def decompose(pre_text: str, post_text: str, typo_diffs: list[TypoDiff]) -> Optional[list[Block]]:
+    def decompose(pre_text: str, post_text: str, typo_diffs: List[TypoDiff]) -> Optional[List[Block]]:
         # decompose pre_text into blocks
         # return None if alignment fails
         assert len(typo_diffs) > 0
@@ -47,7 +47,7 @@ def decompose(pre_text: str, post_text: str, typo_diffs: list[TypoDiff]) -> Opti
         s_idx: int = 0
         remaining_pre_text: str = pre_text
         remaining_post_text: str = post_text
-        blocks: list[Block] = []
+        blocks: List[Block] = []
         for diff in typo_diffs:
             if len(diff.pre_str) > 0 and len(diff.post_str) > 0:
                 # both pre_str and post_str are non-null: check if post_text contains post_str at the position s_idx
@@ -122,11 +122,11 @@ def decompose(pre_text: str, post_text: str, typo_diffs: list[TypoDiff]) -> Opti
         return blocks
 
     @staticmethod
-    def generate_opn(pre_text: str, blocks: list[Block]) -> Tuple[list[str], list[str]]:
+    def generate_opn(pre_text: str, blocks: List[Block]) -> Tuple[List[str], List[str]]:
         # Keep ("K"), Delete ("D"), Replace ("R:x")
-        kdr_opns: list[str] = ["K"] * (len(pre_text) + 1)
+        kdr_opns: List[str] = ["K"] * (len(pre_text) + 1)
         # Insert ("I:x"), Nothing ("_")
-        ins_opns: list[str] = ["_"] * (len(pre_text) + 1)
+        ins_opns: List[str] = ["_"] * (len(pre_text) + 1)
 
         s_idx = 0
         for block in blocks:
@@ -163,7 +163,7 @@ def generate_opn(pre_text: str, blocks: list[Block]) -> Tuple[list[str], list[st
 
     @staticmethod
     def make_multi_char_vocab(train_path: Path, output_dir: Path) -> None:
-        multi_char_vocabs: list[str] = []
+        multi_char_vocabs: List[str] = []
         with train_path.open(mode="r") as fr:
             for line in fr:
                 train_example: dict = json.loads(line)
@@ -211,13 +211,13 @@ def main():
 
     preprocessor: TypoPreprocessor = TypoPreprocessor()
     for filename in ["test", "train"]:
-        category2obj = defaultdict(list[str])
-        other_objs: list[str] = []
+        category2obj = defaultdict(List[str])
+        other_objs: List[str] = []
         with Path(f"{args.input_dir}/{filename}.jsonl").open("r") as f:
             for line in f:
                 example: dict = json.loads(line)
                 diffs = [diff for diff in example["diffs"] if diff["category"] != "not-typo"]
-                blocks: Optional[list[Block]] = preprocessor.decompose(
+                blocks: Optional[List[Block]] = preprocessor.decompose(
                     pre_text=example["pre_text"],
                     post_text=example["post_text"],
                     typo_diffs=[TypoDiff(pre_str=diff["pre_str"], post_str=diff["post_str"]) for diff in diffs],
@@ -242,8 +242,8 @@ def main():
             with test_dir.joinpath(f"{filename}.jsonl").open("w") as f:
                 f.write("\n".join(other_objs) + "\n")
         else:
-            valid_save_objs: list[str] = []
-            train_save_objs: list[str] = other_objs
+            valid_save_objs: List[str] = []
+            train_save_objs: List[str] = other_objs
             for category, objs in category2obj.items():
                 random_objs = random.sample(objs, len(objs))
                 valid_save_objs.extend(random_objs[: args.num_valid_samples_per_category])
diff --git a/src/kwja/preprocessors/wiki_ene_dic.py b/src/kwja/preprocessors/wiki_ene_dic.py
index 3895d97f..fe4637fa 100644
--- a/src/kwja/preprocessors/wiki_ene_dic.py
+++ b/src/kwja/preprocessors/wiki_ene_dic.py
@@ -4,12 +4,12 @@
 import sys
 from argparse import ArgumentParser
 from pathlib import Path
-from typing import Union
+from typing import Dict, List, Set, Union
 
 import dartsclone
 from tqdm import tqdm
 
-BAD_ENE_PATTERNS: list[str] = [
+BAD_ENE_PATTERNS: List[str] = [
     r"^1\.7\.19\.",  # 芸術作品名
     r"^1\.7\.20\.0$",  # 出版物名＿その他
     r"^1\.7\.20\.2$",  # 雑誌名
@@ -34,13 +34,13 @@ def __init__(self, input_json_path: str, output_dir: str):
         )  # numbers, two alphabets, two hiragana/katakanas
         self.bad_ene_pattern = re.compile("|".join(BAD_ENE_PATTERNS))
         self.ene_pattern = re.compile(r"^(0$|\d+\.\d+)")
-        self.stop_titles: set[str] = {"ぼく", "ぼくら", "先生"}
+        self.stop_titles: Set[str] = {"ぼく", "ぼくら", "先生"}
 
-        self.entries: list[dict] = self._load_json(path=input_json_path)
+        self.entries: List[dict] = self._load_json(path=input_json_path)
 
     @staticmethod
-    def _load_json(path: str) -> list[dict]:
-        entries: list[dict] = []
+    def _load_json(path: str) -> List[dict]:
+        entries: List[dict] = []
         with Path(path).open() as f:
             for line in tqdm(f, desc="load original json file..."):
                 entries.append(json.loads(line))
@@ -60,7 +60,7 @@ def _is_bad_entry(self, entry: dict) -> bool:
                 return True
         return False
 
-    def _normalize_enes(self, entry: dict) -> list[str]:
+    def _normalize_enes(self, entry: dict) -> List[str]:
         enes = set()
         for cand in list(entry["ENEs"].values())[0]:
             m = self.ene_pattern.match(cand["ENE_id"])
@@ -71,8 +71,8 @@ def _normalize_enes(self, entry: dict) -> list[str]:
                 enes.add(m[1])
         return list(enes)
 
-    def get_filtered_entries(self, save_filtered_results: bool = False) -> list[dict]:
-        converted_entries: dict[str, set] = {}
+    def get_filtered_entries(self, save_filtered_results: bool = False) -> List[dict]:
+        converted_entries: Dict[str, set] = {}
         for entry in tqdm(self.entries, desc="process filtering..."):
             m = self.title_pattern.match(entry["title"])
             entry["title_normalized"] = entry["title"] if m is None else m[1]
@@ -85,7 +85,7 @@ def get_filtered_entries(self, save_filtered_results: bool = False) -> list[dict
             else:
                 converted_entries[entry["title_normalized"]] = set(entry["ENEs_normalized"])
 
-        outputs: list[dict[str, Union[str, list[str]]]] = [
+        outputs: List[Dict[str, Union[str, List[str]]]] = [
             {"title": k, "classes": list(v)} for k, v in converted_entries.items()
         ]
         if save_filtered_results:
@@ -94,10 +94,10 @@ def get_filtered_entries(self, save_filtered_results: bool = False) -> list[dict
                     f.write(json.dumps(output, ensure_ascii=False) + "\n")
         return outputs
 
-    def build_db(self, entries: list[dict]) -> None:
+    def build_db(self, entries: List[dict]) -> None:
         ene_set_all: set = set()
-        title2class: dict[bytes, int] = dict()
-        classes_all: dict[str, int] = dict()
+        title2class: Dict[bytes, int] = dict()
+        classes_all: Dict[str, int] = dict()
         for entry in entries:
             title_byte: bytes = entry["title"].encode()
             ene_set_all |= set(entry["classes"])
@@ -106,8 +106,8 @@ def build_db(self, entries: list[dict]) -> None:
                 classes_all[class_str] = len(classes_all)
             title2class[title_byte] = classes_all[class_str]
 
-        sorted_titles: list[bytes] = sorted(title2class.keys())
-        sorted_classes: list[int] = [title2class[t] for t in sorted_titles]
+        sorted_titles: List[bytes] = sorted(title2class.keys())
+        sorted_classes: List[int] = [title2class[t] for t in sorted_titles]
 
         # build index of darts-clone
         darts = dartsclone.DoubleArray()
@@ -115,7 +115,7 @@ def build_db(self, entries: list[dict]) -> None:
         darts.save(f"{self.output_dir}/wiki.da")
 
         # save classes with darts-clone's index
-        values: list[list[str]] = [[""]] * len(classes_all)
+        values: List[List[str]] = [[""]] * len(classes_all)
         for k, v in classes_all.items():
             values[v] = k.split("\t")
         with Path(f"{self.output_dir}/wiki_values.pkl").open("wb") as f:
diff --git a/src/kwja/utils/cohesion.py b/src/kwja/utils/cohesion.py
index b4a1ef18..14e9b133 100644
--- a/src/kwja/utils/cohesion.py
+++ b/src/kwja/utils/cohesion.py
@@ -2,7 +2,7 @@
 import logging
 from collections import defaultdict
 from pathlib import Path
-from typing import TextIO, Union
+from typing import Dict, List, TextIO, Union
 
 from rhoknp import BasePhrase, Document, Sentence
 from rhoknp.cohesion import Argument, EndophoraArgument, ExophoraArgument, ExophoraReferent, Pas, RelTag, RelTagList
@@ -22,29 +22,31 @@ class CohesionKNPWriter:
     """
 
     def __init__(self, dataset: WordDataset) -> None:
-        self.cases: list[str] = dataset.pas_cases
-        self.tasks: list[CohesionTask] = dataset.cohesion_tasks
-        self.rel_types: list[str] = dataset.cohesion_rel_types
-        self.exophora_referents: list[ExophoraReferent] = dataset.exophora_referents
-        self.specials: list[str] = dataset.special_tokens
-        self.documents: list[Document] = [self.reconstruct(doc) for doc in dataset.documents]
-        self.examples: list[CohesionExample] = [e.cohesion_example for e in dataset.examples]
+        self.cases: List[str] = dataset.pas_cases
+        self.tasks: List[CohesionTask] = dataset.cohesion_tasks
+        self.rel_types: List[str] = dataset.cohesion_rel_types
+        self.exophora_referents: List[ExophoraReferent] = dataset.exophora_referents
+        self.specials: List[str] = dataset.special_tokens
+        self.documents: List[Document] = [self._reconstruct(doc) for doc in dataset.documents]
+        self.examples: List[CohesionExample] = [e.cohesion_example for e in dataset.examples]
         self.kc: bool = False
 
     @staticmethod
-    def reconstruct(doc):
+    def _reconstruct(doc: Document) -> Document:
         doc_id = doc.doc_id
-        reconstructed = Document.from_knp(doc.to_knp())
+        reconstructed = doc.reparse()
         reconstructed.doc_id = doc_id
+        for sentence in reconstructed.sentences:
+            sentence.doc_id = doc_id
         return reconstructed
 
     def write(
         self,
-        predictions: dict[int, list[list[int]]],
+        predictions: Dict[int, List[List[int]]],
         destination: Union[Path, TextIO, None] = None,
         skip_untagged: bool = True,
         add_pas_tag: bool = True,
-    ) -> list[Document]:
+    ) -> List[Document]:
         """Write final predictions to files.
 
         Args:
@@ -62,8 +64,8 @@ def write(
         elif not (destination is None or isinstance(destination, io.TextIOBase)):
             logger.warning("invalid output destination")
 
-        did2prediction: dict[str, list] = {self.examples[eid].doc_id: pred for eid, pred in predictions.items()}
-        orig_did_to_sentences: dict[str, list[Sentence]] = defaultdict(list)
+        did2prediction: Dict[str, list] = {self.examples[eid].doc_id: pred for eid, pred in predictions.items()}
+        orig_did_to_sentences: Dict[str, List[Sentence]] = defaultdict(list)
         for document in self.documents:
             if prediction := did2prediction.get(document.doc_id):  # (phrase, rel)
                 for base_phrase, pred in zip(document.base_phrases, prediction):
@@ -73,7 +75,11 @@ def write(
                     continue
                 for base_phrase in document.base_phrases:
                     base_phrase.rels = []
-            orig_did_to_sentences[to_orig_doc_id(document.doc_id)] += extract_target_sentences(document)
+            orig_doc_id = to_orig_doc_id(document.doc_id)
+            target_sentences = extract_target_sentences(document)
+            for sentence in target_sentences:
+                sentence.doc_id = orig_doc_id
+            orig_did_to_sentences[orig_doc_id] += target_sentences
 
         documents = []
         for sentences in orig_did_to_sentences.values():
@@ -94,8 +100,8 @@ def write(
 
     def _to_rels(
         self,
-        prediction: list[int],  # (rel)
-        base_phrases: list[BasePhrase],
+        prediction: List[int],  # (rel)
+        base_phrases: List[BasePhrase],
     ) -> RelTagList:
         rels = RelTagList()
         assert len(self.rel_types) == len(prediction)
@@ -134,10 +140,10 @@ def _to_rels(
 
     def _add_pas_tag(
         self,
-        knp_lines: list[str],
+        knp_lines: List[str],
         document: Document,
-    ) -> list[str]:
-        dtid2pas = {pas.predicate.base_phrase.global_index: pas for pas in document.pas_list()}
+    ) -> List[str]:
+        dtid2pas = {pas.predicate.base_phrase.global_index: pas for pas in document.pas_list}
         dtid = 0
         output_knp_lines = []
         for line in knp_lines:
@@ -160,7 +166,7 @@ def _pas_string(
         cfid: str,
         document: Document,
     ) -> str:
-        sid2index: dict[str, int] = {sent.sid: i for i, sent in enumerate(document.sentences)}
+        sid2index: Dict[str, int] = {sent.sid: i for i, sent in enumerate(document.sentences)}
         case_elements = []
         for case in self.cases + ["ノ"] * (CohesionTask.BRIDGING in self.tasks):
             items = ["-"] * 6
@@ -168,7 +174,7 @@ def _pas_string(
             args = pas.get_arguments(case, relax=False)
             if args:
                 arg: Argument = args[0]
-                items[1] = arg.type.value  # フラグ (C/N/O/D/E/U)
+                items[1] = str(arg.type.value)  # フラグ (C/N/O/D/E/U)
                 items[2] = str(arg)  # 見出し
                 if isinstance(arg, EndophoraArgument):
                     items[3] = str(sid2index[pas.sid] - sid2index[arg.base_phrase.sentence.sid])  # N文前
diff --git a/src/kwja/utils/constants.py b/src/kwja/utils/constants.py
index 6977aad0..67b173bc 100644
--- a/src/kwja/utils/constants.py
+++ b/src/kwja/utils/constants.py
@@ -1,5 +1,6 @@
 import itertools
 import re
+from typing import Dict, Tuple
 
 from rhoknp.props import DepType, NamedEntityCategory
 
@@ -10,14 +11,14 @@
     "D": "<d>",
     "_": "<_>",
 }
-TOKEN2TYPO_OPN: dict[str, str] = {v: k for k, v in TYPO_OPN2TOKEN.items()}
+TOKEN2TYPO_OPN: Dict[str, str] = {v: k for k, v in TYPO_OPN2TOKEN.items()}
 TYPO_DUMMY_TOKEN = "<dummy>"
 
 SEG_TYPES = (
     "B",
     "I",
 )
-INDEX2SEG_TYPE: dict[int, str] = {index: seg_type for index, seg_type in enumerate(SEG_TYPES)}
+INDEX2SEG_TYPE: Dict[int, str] = {index: seg_type for index, seg_type in enumerate(SEG_TYPES)}
 
 WORD_NORM_TYPES = ("K", "D", "V", "S", "P", "E")
 INDEX2WORD_NORM_TYPE = {index: word_norm_type for index, word_norm_type in enumerate(WORD_NORM_TYPES)}
@@ -42,9 +43,9 @@
 }
 POS_TYPES = tuple(POS_TYPE2ID.keys())
 
-INDEX2POS_TYPE: dict[int, str] = {index: pos_type for index, pos_type in enumerate(POS_TYPES)}
+INDEX2POS_TYPE: Dict[int, str] = {index: pos_type for index, pos_type in enumerate(POS_TYPES)}
 # 品詞細分類
-POS_SUBPOS_TYPE2ID: dict[str, dict[str, int]] = {
+POS_SUBPOS_TYPE2ID: Dict[str, Dict[str, int]] = {
     "特殊": {
         "句点": 1,
         "読点": 2,
@@ -106,7 +107,7 @@
     for subpos_type in subpos_type2ids.keys()
     if subpos_type != "*"
 )
-INDEX2SUBPOS_TYPE: dict[int, str] = {index: subpos_type for index, subpos_type in enumerate(SUBPOS_TYPES)}
+INDEX2SUBPOS_TYPE: Dict[int, str] = {index: subpos_type for index, subpos_type in enumerate(SUBPOS_TYPES)}
 # 活用型
 CONJTYPE_TYPES = (
     "*",
@@ -143,9 +144,9 @@
     "動詞性接尾辞ます型",
     "動詞性接尾辞うる型",
 )
-INDEX2CONJTYPE_TYPE: dict[int, str] = {index: conjtype_type for index, conjtype_type in enumerate(CONJTYPE_TYPES)}
+INDEX2CONJTYPE_TYPE: Dict[int, str] = {index: conjtype_type for index, conjtype_type in enumerate(CONJTYPE_TYPES)}
 # 活用形
-CONJTYPE_CONJFORM_TYPE2ID: dict[str, dict[str, int]] = {
+CONJTYPE_CONJFORM_TYPE2ID: Dict[str, Dict[str, int]] = {
     "*": {"*": 0},
     "母音動詞": {
         "*": 0,
@@ -827,9 +828,9 @@
     )
 )
 CONJFORM_TYPES = tuple(sorted(set(CONJFORM_TYPES_WITH_OVERLAP), key=CONJFORM_TYPES_WITH_OVERLAP.index))
-INDEX2CONJFORM_TYPE: dict[int, str] = {index: conjform_type for index, conjform_type in enumerate(CONJFORM_TYPES)}
+INDEX2CONJFORM_TYPE: Dict[int, str] = {index: conjform_type for index, conjform_type in enumerate(CONJFORM_TYPES)}
 
-NE_TAGS: tuple[str, ...] = sum(
+NE_TAGS: Tuple[str, ...] = sum(
     [(f"B-{cat.value}", f"I-{cat.value}") for cat in NamedEntityCategory if cat != NamedEntityCategory.OPTIONAL], ("O",)
 )
 
@@ -837,7 +838,7 @@
     "用言表記先頭",
     "用言表記末尾",
 )
-WORD_FEATURES: tuple[str, ...] = ("基本句-主辞", "基本句-区切", "文節-区切", *SUB_WORD_FEATURES)
+WORD_FEATURES: Tuple[str, ...] = ("基本句-主辞", "基本句-区切", "文節-区切", *SUB_WORD_FEATURES)
 
 SUB_BASE_PHRASE_FEATURES = (
     # cf. https://github.com/ku-nlp/knp/blob/master/doc/knp_feature.pdf
@@ -916,16 +917,14 @@
     *SUB_BASE_PHRASE_FEATURES,
 )
 IGNORE_VALUE_FEATURE_PAT = re.compile(r"節-(前向き)?機能疑?")
-IGNORE_INDEX = -100
-
 
-DEPENDENCY_TYPE2INDEX: dict[DepType, int] = {
+DEPENDENCY_TYPE2INDEX: Dict[DepType, int] = {
     DepType.DEPENDENCY: 0,
     DepType.PARALLEL: 1,
     DepType.APPOSITION: 2,
     DepType.IMPERFECT_PARALLEL: 3,
 }
-INDEX2DEPENDENCY_TYPE: dict[int, DepType] = {
+INDEX2DEPENDENCY_TYPE: Dict[int, DepType] = {
     index: dependency_type for dependency_type, index in DEPENDENCY_TYPE2INDEX.items()
 }
 
@@ -965,6 +964,6 @@
     "対比",
     "逆接",
 )
-INDEX2DISCOURSE_RELATION: dict[int, str] = {
+INDEX2DISCOURSE_RELATION: Dict[int, str] = {
     index: discourse_relation for index, discourse_relation in enumerate(DISCOURSE_RELATIONS)
 }
diff --git a/src/kwja/utils/dependency_parsing.py b/src/kwja/utils/dependency_parsing.py
index ed392ea9..23e468ef 100644
--- a/src/kwja/utils/dependency_parsing.py
+++ b/src/kwja/utils/dependency_parsing.py
@@ -1,9 +1,10 @@
 from collections import defaultdict
+from typing import Dict, Set
 
 
 class DependencyManager:
     def __init__(self) -> None:
-        self.directed_graph: dict[int, set] = defaultdict(set)
+        self.directed_graph: Dict[int, set] = defaultdict(set)
         self.root = False
 
     def add_edge(self, src: int, dst: int) -> None:
@@ -12,7 +13,7 @@ def add_edge(self, src: int, dst: int) -> None:
     def remove_edge(self, src: int, dst: int) -> None:
         self.directed_graph[src].remove(dst)
 
-    def is_cyclic(self, src: int, visited: set[int], cache: dict[int, bool]) -> bool:
+    def is_cyclic(self, src: int, visited: Set[int], cache: Dict[int, bool]) -> bool:
         if src in cache:
             return cache[src]
 
@@ -31,8 +32,8 @@ def is_cyclic(self, src: int, visited: set[int], cache: dict[int, bool]) -> bool
         return ret
 
     def has_cycle(self) -> bool:
-        visited: set[int] = set()
-        cache: dict[int, bool] = {}
+        visited: Set[int] = set()
+        cache: Dict[int, bool] = {}
         for src in list(self.directed_graph.keys()):
             if self.is_cyclic(src, visited, cache):
                 return True
diff --git a/src/kwja/utils/reading.py b/src/kwja/utils/reading.py
index 4ac5e8e7..b9c1603e 100644
--- a/src/kwja/utils/reading.py
+++ b/src/kwja/utils/reading.py
@@ -3,7 +3,7 @@
 import re
 import unicodedata
 from dataclasses import dataclass
-from typing import Dict, Final, Optional, Union
+from typing import Dict, Final, List, Optional, Tuple, Union
 
 import jaconv
 import numpy as np
@@ -157,7 +157,7 @@
 ID_ID: Final = 1
 
 
-def get_reading2id(path: str) -> dict[str, int]:
+def get_reading2id(path: str) -> Dict[str, int]:
     reading2id = {UNK: UNK_ID, ID: ID_ID}
     with open(path, "r") as f:
         for line in f:
@@ -175,7 +175,7 @@ def __init__(self, tokenizer: PreTrainedTokenizerBase, kanji_dic: KanjiDic) -> N
         self.tokenizer = tokenizer
         self.kanji_dic = kanji_dic
 
-    def align(self, sequence: Union[Sentence, Document]) -> list[tuple[str, str]]:
+    def align(self, sequence: Union[Sentence, Document]) -> List[Tuple[str, str]]:
         inp = " ".join([morpheme.surf for morpheme in sequence.morphemes])
         reading_list = []
 
@@ -197,7 +197,7 @@ def align(self, sequence: Union[Sentence, Document]) -> list[tuple[str, str]]:
         assert len(subword_list) == len(reading_list)
         return list(zip(subword_list, reading_list))
 
-    def _align_morpheme(self, morpheme: Morpheme, subwords: list[str]) -> list[str]:
+    def _align_morpheme(self, morpheme: Morpheme, subwords: List[str]) -> List[str]:
         # trivial
         if len(subwords) == 1:
             return [morpheme.reading]
@@ -230,8 +230,8 @@ class Node:
 
         # build lattice
         # no node can cross boundaries
-        td_lattice: list[list[list[Node]]] = []
-        td_holder: list[list[tuple[Optional[Node], Optional[Node], int]]] = []
+        td_lattice: List[List[List[Node]]] = []
+        td_holder: List[List[Tuple[Optional[Node], Optional[Node], int]]] = []
         node: Optional[Node] = None
         node_prev: Optional[Node] = None
         for i in range(len(surf)):
@@ -382,7 +382,8 @@ class Node:
                     break
         return subreading_list
 
-    def _extend_kanji_reading_list(self, kanji_reading_list_orig: list[str]) -> list[str]:
+    @staticmethod
+    def _extend_kanji_reading_list(kanji_reading_list_orig: List[str]) -> List[str]:
         kanji_reading_list = []
         for kanji_reading in kanji_reading_list_orig:
             kanji_reading = re.sub("-", "", kanji_reading)
@@ -399,7 +400,7 @@ def _extend_kanji_reading_list(self, kanji_reading_list_orig: list[str]) -> list
         return kanji_reading_list
 
 
-def get_word_level_readings(readings: list[str], tokens: list[str], subword_map: list[list[bool]]) -> list[str]:
+def get_word_level_readings(readings: List[str], tokens: List[str], subword_map: List[List[bool]]) -> List[str]:
     """サブワードレベルの読みを単語レベルの読みに変換．
 
     Args:
@@ -407,7 +408,7 @@ def get_word_level_readings(readings: list[str], tokens: list[str], subword_map:
         tokens: list[str] サブワードレベルの表層．
         subword_map: list[list[bool]] subword_map[i][j] = True ならば i 番目の単語は j 番目のトークンを含む．
     """
-    ret: list[str] = []
+    ret: List[str] = []
     for flags in subword_map:
         item = ""
         for token, reading, flag in zip(tokens, readings, flags):
@@ -449,7 +450,7 @@ def get_word_level_readings(readings: list[str], tokens: list[str], subword_map:
                     # print(aligner.align(document))
                     subreading_counter[subreading] += 1
             except ValueError:
-                logging.warn("skip {document.doc_id} for an error")
+                logging.warning(f"skip {document.doc_id} for an error")
     for subreading, count in sorted(
         sorted(subreading_counter.items(), key=lambda pair: pair[0]), key=lambda pair: pair[1], reverse=True
     ):
diff --git a/src/kwja/utils/sub_document.py b/src/kwja/utils/sub_document.py
index 22830b54..afd9af65 100644
--- a/src/kwja/utils/sub_document.py
+++ b/src/kwja/utils/sub_document.py
@@ -1,8 +1,9 @@
 import re
+from typing import List
 
 from rhoknp import Document, Sentence
 
-SUB_DOC_PAT: re.Pattern[str] = re.compile(r"^(?P<did>[a-zA-Z\d\-_]+?)-split(?P<stride>[1-9])(?P<idx>\d{2})$")
+SUB_DOC_PAT: re.Pattern = re.compile(r"^(?P<did>[a-zA-Z\d\-_]+?)-split(?P<stride>[1-9])(?P<idx>\d{2})$")
 
 
 def is_target_sentence(sentence: Sentence) -> bool:
@@ -19,7 +20,7 @@ def is_target_sentence(sentence: Sentence) -> bool:
         return sentence in sentence.document.sentences[-stride:]
 
 
-def extract_target_sentences(document: Document) -> list[Sentence]:
+def extract_target_sentences(document: Document) -> List[Sentence]:
     return [sentence for sentence in document.sentences if is_target_sentence(sentence)]
 
 
diff --git a/src/kwja/utils/word_normalize.py b/src/kwja/utils/word_normalize.py
index 895abcd7..54a45d83 100644
--- a/src/kwja/utils/word_normalize.py
+++ b/src/kwja/utils/word_normalize.py
@@ -1,4 +1,5 @@
 import logging
+from typing import List
 
 import numpy as np
 from jinf import Jinf
@@ -151,7 +152,7 @@ class MorphemeNormalizer:
     def __init__(self) -> None:
         self.jinf = Jinf()
 
-    def get_normalization_opns(self, morpheme: Morpheme) -> list[str]:
+    def get_normalization_opns(self, morpheme: Morpheme) -> List[str]:
         try:
             if morpheme.conjtype == "*":
                 normalized = morpheme.surf
@@ -163,13 +164,13 @@ def get_normalization_opns(self, morpheme: Morpheme) -> list[str]:
             return [IGNORE_WORD_NORM_TYPE] * len(morpheme.surf)
 
 
-def get_normalization_opns(surf: str, normalized: str) -> list[str]:
+def get_normalization_opns(surf: str, normalized: str) -> List[str]:
     surf_len = len(surf) + 1
     normalized_len = len(normalized) + 1
     if surf_len < normalized_len:
         raise ValueError(f"failed to construct normalization labels to convert {surf} to {normalized}")
     d = np.inf * np.ones((surf_len, normalized_len), dtype=np.int32)
-    dops: list[list[str]] = []
+    dops: List[List[str]] = []
     for i in range(surf_len):
         dops.append([])
         for j in range(normalized_len):
@@ -231,7 +232,7 @@ def get_normalization_opns(surf: str, normalized: str) -> list[str]:
     return ops
 
 
-def get_normalized(surf: str, ops: list[str], strict: bool = True) -> str:
+def get_normalized(surf: str, ops: List[str], strict: bool = True) -> str:
     assert len(surf) == len(ops)
     normalized = ""
     for i, (c, op) in enumerate(zip(surf, ops)):
@@ -257,7 +258,7 @@ def get_normalized(surf: str, ops: list[str], strict: bool = True) -> str:
                     raise ValueError(f"not a small kana {c} in {surf}")
                 normalized += c
         elif op == "P":
-            # NOTE: in canonical ops, P and E must follow K
+            # NOTE: in canonical ops, P and E must follow K,
             # but we do not check this constraint here
             if len(normalized) <= 0:
                 if strict:
@@ -320,7 +321,7 @@ def denormalize(self, sentence: Sentence, p=0.5) -> None:
             surf2 = self.md.denormalize(morpheme)
             if surf2 != morpheme.surf:
                 if np.random.rand() < prob:
-                    morpheme._attributes.surf = surf2
+                    morpheme.attributes.surf = surf2
                     prob *= 0.1
             else:
                 prob = min(prob * 1.5, p)
@@ -329,7 +330,7 @@ def _is_normal_morpheme(self, morpheme):
         opn = self.mn.get_normalization_opns(morpheme)
         if opn[0] == IGNORE_WORD_NORM_TYPE:
             return False
-        return all(map(lambda x: x in ("K"), opn))
+        return all(map(lambda x: x in ("K",), opn))
 
 
 class MorphemeDenormalizer:
@@ -445,7 +446,8 @@ def _denormalize(self, surf: str) -> str:
         idx = np.random.choice(len(probs), p=probs)
         return cands[idx][0]
 
-    def _denormalize_deterministic(self, surf, var_prolonged=False) -> str:
+    @staticmethod
+    def _denormalize_deterministic(surf, var_prolonged=False) -> str:
         # S
         if surf[-1] in UPPER2LOWER:
             surf2 = surf[:-1] + UPPER2LOWER[surf[-1]]


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b30d04ee..528f3639 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,37 +1,44 @@
 name: Test
 
-on: [push, pull_request]
+on: [ push, pull_request ]
 
 jobs:
   test:
     name: Run tests with pytest
     runs-on: ubuntu-latest
+    strategy:
+      max-parallel: 3
+      fail-fast: false
+      matrix:
+        python-version: [ "3.8", "3.9", "3.10" ]
     steps:
-    - name: Checkout repository
-      uses: actions/checkout@v3
-    - name: Set up Python 3.9
-      uses: actions/setup-python@v3
-      with:
-        python-version: 3.9
-    - name: Install Poetry
-      run: |
-        python3 -m pip install --user pipx
-        python3 -m pipx ensurepath
-        python3 -m pipx install poetry
-    - name: Add path for Python packages
-      run: echo "$HOME/.local/bin" >> $GITHUB_PATH
-    - name: Install dependencies
-      run: |
-        poetry config virtualenvs.create false
-        poetry install --no-interaction
-    - name: Run tests
-      run: |
-        poetry run pytest --cov=./ --cov-report=xml
-    - name: Upload coverage to Codecov
-      uses: codecov/codecov-action@v3
-      with:
-        token: ${{ secrets.CODECOV_TOKEN }}
-        files: ./coverage.xml
-        root_dir: true
-        name: codecov-umbrella
-        verbose: true
+      - name: Checkout repository
+        uses: actions/checkout@v3
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v3
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Install Poetry
+        run: |
+          python3 -m pip install --user pipx
+          python3 -m pipx ensurepath
+          python3 -m pipx install poetry
+      - name: Add path for Python packages
+        run: echo "$HOME/.local/bin" >> $GITHUB_PATH
+      - name: Install dependencies
+        run: |
+          poetry config virtualenvs.create false
+          poetry install --no-interaction
+      - name: Run tests
+        run: |
+          poetry run pytest --cov=./ --cov-report=xml
+        env:
+          XDG_CACHE_HOME: ${{ github.workspace }}/.cache
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@v3
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          files: ./coverage.xml
+          root_dir: true
+          name: codecov-umbrella
+          verbose: true
diff --git a/scripts/test.py b/scripts/test.py
index 17e2b37b..b7828dad 100644
--- a/scripts/test.py
+++ b/scripts/test.py
@@ -1,6 +1,6 @@
 import logging
 import math
-from typing import Optional
+from typing import List, Optional
 
 import hydra
 import pytorch_lightning as pl
@@ -29,7 +29,7 @@ def main(cfg: DictConfig):
 
     is_debug: bool = True if "fast_dev_run" in cfg.trainer else False
     logger: Optional[LightningLoggerBase] = hydra.utils.instantiate(cfg.logger) if not is_debug else None
-    callbacks: list[Callback] = list(map(hydra.utils.instantiate, cfg.get("callbacks", {}).values()))
+    callbacks: List[Callback] = list(map(hydra.utils.instantiate, cfg.get("callbacks", {}).values()))
 
     # Calculate gradient_accumulation_steps assuming DDP
     num_devices: int = len(cfg.devices) if isinstance(cfg.devices, (list, ListConfig)) else cfg.devices
diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py
index 9e5d2bf3..fc66f42e 100644
--- a/tests/cli/test_cli.py
+++ b/tests/cli/test_cli.py
@@ -1,15 +1,13 @@
-# TODO: uncomment here after renaming project
-
-# from typer.testing import CliRunner
-#
-# from kwja.cli.cli import app
-#
-# runner = CliRunner()
-#
-#
-# def test_app_with_text_input():
-#     _ = runner.invoke(app, args=["--text", "おはよう"])
-#
-#
-# def test_app_with_file_input():
-#     _ = runner.invoke(app, args=["--filename", "./sample.txt"])
+from typer.testing import CliRunner
+
+from kwja.cli.cli import app
+
+runner = CliRunner()
+
+
+def test_app_with_text_input():
+    _ = runner.invoke(app, args=["--text", "おはよう"])
+
+
+def test_app_with_file_input():
+    _ = runner.invoke(app, args=["--filename", "./sample.txt"])
diff --git a/tests/datamodule/datasets/test_base_dataset.py b/tests/datamodule/datasets/test_base_dataset.py
index 1952e6f9..a58da0c9 100644
--- a/tests/datamodule/datasets/test_base_dataset.py
+++ b/tests/datamodule/datasets/test_base_dataset.py
@@ -31,7 +31,7 @@ def test_load_documents():
 
 
 def test_split_document():
-    dataset = BaseDataset(path, **(base_dataset_kwargs | {"max_seq_length": 13}))
+    dataset = BaseDataset(path, **{**base_dataset_kwargs, "max_seq_length": 13})
     assert len(dataset.orig_documents) == 2
     assert [doc.doc_id for doc in dataset.orig_documents] == ["000", "1"]
     assert len(dataset.documents) == 3
@@ -39,7 +39,7 @@ def test_split_document():
 
 
 def test_split_document_overflow():
-    dataset = BaseDataset(path, **(base_dataset_kwargs | {"max_seq_length": 3}))
+    dataset = BaseDataset(path, **{**base_dataset_kwargs, "max_seq_length": 3})
     assert len(dataset.orig_documents) == 2
     assert [doc.doc_id for doc in dataset.orig_documents] == ["000", "1"]
     assert len(dataset.documents) == 3
diff --git a/tests/datamodule/datasets/test_char_dataset.py b/tests/datamodule/datasets/test_char_dataset.py
index 21a9f501..4f37f52d 100644
--- a/tests/datamodule/datasets/test_char_dataset.py
+++ b/tests/datamodule/datasets/test_char_dataset.py
@@ -40,7 +40,7 @@ def test_getitem():
 
 def test_encode():
     max_seq_length = 20
-    dataset = CharDataset(str(path), **(char_dataset_kwargs | {"max_seq_length": max_seq_length}))
+    dataset = CharDataset(str(path), **{**char_dataset_kwargs, "max_seq_length": max_seq_length})
     document = Document.from_knp(
         textwrap.dedent(
             """\
diff --git a/tests/datamodule/datasets/test_word_dataset.py b/tests/datamodule/datasets/test_word_dataset.py
index 25ec897b..94c5fbd4 100644
--- a/tests/datamodule/datasets/test_word_dataset.py
+++ b/tests/datamodule/datasets/test_word_dataset.py
@@ -1,6 +1,7 @@
 import json
 import textwrap
 from pathlib import Path
+from typing import Dict, List
 
 from omegaconf import ListConfig
 from rhoknp import Document
@@ -351,7 +352,7 @@ def test_pas():
 
     assert len(mrphs) == len(mrphs_exp)
     for phrase in phrases:
-        arguments: dict[str, list[str]] = annotation.arguments_set[phrase.dtid]
+        arguments: Dict[str, List[str]] = annotation.arguments_set[phrase.dtid]
         for case in dataset.pas_cases:
             arg_strings = [arg[:-2] if arg[-2:] in ("%C", "%N", "%O") else arg for arg in arguments[case]]
             arg_strings = [(s if s in dataset.special_to_index else str(phrases[int(s)].dmid)) for s in arg_strings]
diff --git a/tests/evaluators/test_word_segmentation_metric.py b/tests/evaluators/test_word_segmentation_metric.py
index a96319c8..2eaf8ce6 100644
--- a/tests/evaluators/test_word_segmentation_metric.py
+++ b/tests/evaluators/test_word_segmentation_metric.py
@@ -1,3 +1,5 @@
+from typing import List
+
 import pytest
 
 from kwja.evaluators.word_segmentation_metric import WordSegmentationMetric
@@ -37,10 +39,10 @@
     ],
 )
 def test_filter_predictions(
-    preds: list[str],
-    labels: list[str],
-    expected_filtered_preds: list[str],
-    expected_filtered_labels: list[str],
+    preds: List[str],
+    labels: List[str],
+    expected_filtered_preds: List[str],
+    expected_filtered_labels: List[str],
 ):
     filtered_preds, filtered_labels = WordSegmentationMetric.filter_predictions(preds, labels)
     assert filtered_preds == expected_filtered_preds