From 99d605369985b1402748f8f28b835c83721d1140 Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Fri, 7 Feb 2025 12:01:10 +0200
Subject: [PATCH 1/9] Add API Budget

---
 .../source_klaviyo/manifest.yaml              | 73 ++++++++++++++++++-
 1 file changed, 71 insertions(+), 2 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
index 0116d4cb99f9d..fdcd1a1577cb7 100644
--- a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
@@ -253,7 +253,7 @@ definitions:
       type: InlineSchemaLoader
       schema: "#/definitions/lists_detailed_schema"
     retriever:
-      $ref: "#/definitions/semi_incremental_retriever"
+      $ref: "#/definitions/base_retriever"
       requester:
         $ref: "#/definitions/requester"
         request_parameters:
@@ -1061,6 +1061,75 @@ metadata:
       primaryKeysArePresent: true
       primaryKeysAreUnique: true
 
+api_budget:
+  type: HTTPAPIBudget
+  # Each policy here uses a MovingWindowCallRatePolicy with two rates:
+  # one for burst (per-second) and one for steady (per-minute).
+  policies:
+    # Profiles (and global_exclusions share the same endpoint)
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 10          # burst: 10 calls per second
+          interval: PT1S
+        - limit: 150         # steady: 150 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/profiles($|/)"  # matches '/profiles' (exact or with trailing slash/extra)
+    # Events (and events_detailed share the same endpoint)
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 350         # burst: 350 calls per second
+          interval: PT1S
+        - limit: 3500        # steady: 3500 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/events($|/)"  # matches '/events' and '/events_detailed' if using same endpoint
+    # Email Templates
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 10          # burst: 10 calls per second
+          interval: PT1S
+        - limit: 150         # steady: 150 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/templates($|/)"  # matches '/templates'
+    # Metrics
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 10          # burst: 10 calls per second
+          interval: PT1S
+        - limit: 150         # steady: 150 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/metrics($|/)"
+    # Lists (the parent endpoint for lists streams)
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 75          # burst: 75 calls per second
+          interval: PT1S
+        - limit: 700         # steady: 700 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/lists$"   # exactly '/lists'
+    # Lists Detailed (uses a different URL path – note the extra segment)
+    - type: MovingWindowCallRatePolicy
+      rates:
+        - limit: 1           # burst: 1 call per second
+          interval: PT1S
+        - limit: 15          # steady: 15 calls per minute
+          interval: PT1M
+      matchers:
+        - method: GET
+          url_path_pattern: "^/api/lists/"   # matches any path beginning with '/lists/' (e.g. '/lists/123')
+  # Other API budget settings:
+  status_codes_for_ratelimit_hit: [429]
+  maximum_attempts_to_acquire: 100000
+
 # Klaviyo's rate limiting is different by endpoints:
 # - XS: 1/s burst; 15/m steady
 # - S: 3/s burst; 60/m steady
@@ -1085,5 +1154,5 @@ metadata:
 # Based on the above, the only threads that allow for slicing and hence might perform more concurrent HTTP requests are `events` and `events_detailed`. There are no slicing for the others and hence the concurrency is limited by the number of streams querying the same endpoint. Given that the event endpoint is XL, we will set a default concurrency to 10.
 concurrency_level:
   type: ConcurrencyLevel
-  default_concurrency: "{{ config.get('num_workers', 10) }}"
+  default_concurrency: "{{ config.get('num_workers', 50) }}"
   max_concurrency: 50

From 0e27b80d19b99693c1395299b26320814b73320d Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Mon, 10 Feb 2025 12:44:08 +0200
Subject: [PATCH 2/9] Update poetry.lock

---
 .../connectors/source-klaviyo/poetry.lock     | 445 ++++++++++++++----
 .../connectors/source-klaviyo/pyproject.toml  |   3 +-
 2 files changed, 346 insertions(+), 102 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/poetry.lock b/airbyte-integrations/connectors/source-klaviyo/poetry.lock
index 567c0c4e18210..e5f67f41a5e56 100644
--- a/airbyte-integrations/connectors/source-klaviyo/poetry.lock
+++ b/airbyte-integrations/connectors/source-klaviyo/poetry.lock
@@ -1,14 +1,16 @@
-# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
 
 [[package]]
 name = "airbyte-cdk"
-version = "6.27.0"
+version = "6.33.1.dev1"
 description = "A framework for writing Airbyte Connectors."
 optional = false
 python-versions = "<3.13,>=3.10"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
-    {file = "airbyte_cdk-6.27.0-py3-none-any.whl", hash = "sha256:ee2229bdb7e2d8b4477ff6d23051f30e87b975ade6c1ed62641da9a318ff2277"},
-    {file = "airbyte_cdk-6.27.0.tar.gz", hash = "sha256:0ae79346fb34eba956a6fadef6dae0eaac6c6ee0018408a6f3ed464368fee1e7"},
+    {file = "airbyte_cdk-6.33.1.dev1-py3-none-any.whl", hash = "sha256:d5601d9325564af59c7f6f7d788ed1bcb8956ad11555015410079410ba3098a1"},
+    {file = "airbyte_cdk-6.33.1.dev1.tar.gz", hash = "sha256:a176117408aa663c1734db2bae899ad4293c6af3310a02ae34758b2762c61acd"},
 ]
 
 [package.dependencies]
@@ -28,12 +30,11 @@ nltk = "3.9.1"
 numpy = "<2"
 orjson = ">=3.10.7,<4.0.0"
 pandas = "2.2.2"
-pendulum = "<3.0.0"
 psutil = "6.1.0"
 pydantic = ">=2.7,<3.0"
 pyjwt = ">=2.8.0,<3.0.0"
 pyrate-limiter = ">=3.1.0,<3.2.0"
-python-dateutil = "*"
+python-dateutil = ">=2.9.0,<3.0.0"
 python-ulid = ">=3.0.0,<4.0.0"
 pytz = "2024.2"
 PyYAML = ">=6.0.1,<7.0.0"
@@ -43,6 +44,7 @@ requests_cache = "*"
 serpyco-rs = ">=1.10.2,<2.0.0"
 Unidecode = ">=1.3,<2.0"
 wcmatch = "10.0"
+whenever = ">=0.6.16,<0.7.0"
 xmltodict = ">=0.13,<0.15"
 
 [package.extras]
@@ -56,6 +58,8 @@ version = "0.14.2"
 description = "Declares the Airbyte Protocol using Python Dataclasses. Dataclasses in Python have less performance overhead compared to Pydantic models, making them a more efficient choice for scenarios where speed and memory usage are critical"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "airbyte_protocol_models_dataclasses-0.14.2-py3-none-any.whl", hash = "sha256:ae06a406df031afa42f1156bacc587958197e5c7d9bbaf11893480903d4ded8b"},
     {file = "airbyte_protocol_models_dataclasses-0.14.2.tar.gz", hash = "sha256:9279237156b722cdd54e7b9ec8f97d264bd96e3f3008bc5fc47c215288a2212a"},
@@ -67,6 +71,8 @@ version = "0.7.0"
 description = "Reusable constraint types to use with typing.Annotated"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
     {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -78,6 +84,8 @@ version = "4.8.0"
 description = "High level compatibility layer for multiple asynchronous event loop implementations"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"},
     {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"},
@@ -100,6 +108,8 @@ version = "1.4.1"
 description = "Atomic file writes."
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["dev"]
+markers = "python_version <= \"3.11\" and sys_platform == \"win32\""
 files = [
     {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"},
 ]
@@ -110,6 +120,8 @@ version = "0.4.0"
 description = "PEP 224 implementation"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "attributes-doc-0.4.0.tar.gz", hash = "sha256:b1576c94a714e9fc2c65c47cf10d0c8e1a5f7c4f5ae7f69006be108d95cbfbfb"},
     {file = "attributes_doc-0.4.0-py2.py3-none-any.whl", hash = "sha256:4c3007d9e58f3a6cb4b9c614c4d4ce2d92161581f28e594ddd8241cc3a113bdd"},
@@ -121,6 +133,8 @@ version = "25.1.0"
 description = "Classes Without Boilerplate"
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"},
     {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"},
@@ -140,6 +154,8 @@ version = "2.2.1"
 description = "Function decoration for backoff and retry"
 optional = false
 python-versions = ">=3.7,<4.0"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"},
     {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"},
@@ -151,6 +167,8 @@ version = "2.5.post1"
 description = "Bash style brace expander."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "bracex-2.5.post1-py3-none-any.whl", hash = "sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6"},
     {file = "bracex-2.5.post1.tar.gz", hash = "sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6"},
@@ -162,6 +180,8 @@ version = "5.5.1"
 description = "Extensible memoizing collections and decorators"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"},
     {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"},
@@ -173,6 +193,8 @@ version = "24.1.2"
 description = "Composable complex class support for attrs and dataclasses."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "cattrs-24.1.2-py3-none-any.whl", hash = "sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0"},
     {file = "cattrs-24.1.2.tar.gz", hash = "sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85"},
@@ -195,13 +217,15 @@ ujson = ["ujson (>=5.7.0)"]
 
 [[package]]
 name = "certifi"
-version = "2024.12.14"
+version = "2025.1.31"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
-    {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
-    {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
+    {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
+    {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
 ]
 
 [[package]]
@@ -210,6 +234,8 @@ version = "1.17.1"
 description = "Foreign Function Interface for Python calling C code."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\" and platform_python_implementation != \"PyPy\""
 files = [
     {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
     {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -289,6 +315,8 @@ version = "3.4.1"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
     {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
@@ -390,6 +418,8 @@ version = "8.1.8"
 description = "Composable command line interface toolkit"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
     {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
@@ -404,10 +434,12 @@ version = "0.4.6"
 description = "Cross-platform colored terminal text."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+groups = ["main", "dev"]
 files = [
     {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
     {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
 ]
+markers = {main = "python_version <= \"3.11\" and platform_system == \"Windows\"", dev = "python_version <= \"3.11\" and sys_platform == \"win32\""}
 
 [[package]]
 name = "cryptography"
@@ -415,6 +447,8 @@ version = "43.0.3"
 description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"},
     {file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"},
@@ -464,6 +498,8 @@ version = "2.2.0"
 description = "Filesystem-like pathing and searching for dictionaries"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "dpath-2.2.0-py3-none-any.whl", hash = "sha256:b330a375ded0a0d2ed404440f6c6a715deae5313af40bbb01c8a41d891900576"},
     {file = "dpath-2.2.0.tar.gz", hash = "sha256:34f7e630dc55ea3f219e555726f5da4b4b25f2200319c8e6902c394258dd6a3e"},
@@ -475,6 +511,8 @@ version = "1.23.0"
 description = "Dynamic version generation"
 optional = false
 python-versions = ">=3.5"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "dunamai-1.23.0-py3-none-any.whl", hash = "sha256:a0906d876e92441793c6a423e16a4802752e723e9c9a5aabdc5535df02dbe041"},
     {file = "dunamai-1.23.0.tar.gz", hash = "sha256:a163746de7ea5acb6dacdab3a6ad621ebc612ed1e528aaa8beedb8887fccd2c4"},
@@ -489,6 +527,8 @@ version = "1.2.2"
 description = "Backport of PEP 654 (exception groups)"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version < \"3.11\""
 files = [
     {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
     {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
@@ -503,6 +543,8 @@ version = "1.5.1"
 description = "Let your Python tests travel through time"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"},
     {file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"},
@@ -517,6 +559,8 @@ version = "1.3.0"
 description = "GenSON is a powerful, user-friendly JSON Schema generator."
 optional = false
 python-versions = "*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"},
     {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"},
@@ -528,6 +572,8 @@ version = "0.14.0"
 description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
     {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -539,6 +585,8 @@ version = "1.0.7"
 description = "A minimal low-level HTTP client."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"},
     {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"},
@@ -560,6 +608,8 @@ version = "0.28.1"
 description = "The next generation HTTP client."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
     {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
@@ -584,6 +634,8 @@ version = "3.10"
 description = "Internationalized Domain Names in Applications (IDNA)"
 optional = false
 python-versions = ">=3.6"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
     {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
@@ -598,6 +650,8 @@ version = "2.0.0"
 description = "brain-dead simple config-ini parsing"
 optional = false
 python-versions = ">=3.7"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
     {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -609,6 +663,8 @@ version = "0.6.1"
 description = "An ISO 8601 date/time/duration parser and formatter"
 optional = false
 python-versions = "*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"},
     {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"},
@@ -623,6 +679,8 @@ version = "3.1.5"
 description = "A very fast and expressive template engine."
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
     {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
@@ -640,6 +698,8 @@ version = "1.4.2"
 description = "Lightweight pipelining with Python functions"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"},
     {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"},
@@ -651,6 +711,8 @@ version = "1.33"
 description = "Apply JSON-Patches (RFC 6902)"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"},
     {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"},
@@ -665,6 +727,8 @@ version = "3.0.0"
 description = "Identify specific nodes in a JSON document (RFC 6901)"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"},
     {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"},
@@ -676,6 +740,8 @@ version = "0.2"
 description = "An implementation of JSON Reference for Python"
 optional = false
 python-versions = "*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "jsonref-0.2-py3-none-any.whl", hash = "sha256:b1e82fa0b62e2c2796a13e5401fe51790b248f6d9bf9d7212a3e31a3501b291f"},
     {file = "jsonref-0.2.tar.gz", hash = "sha256:f3c45b121cf6257eafabdc3a8008763aed1cd7da06dbabc59a9e4d2a5e4e6697"},
@@ -687,6 +753,8 @@ version = "4.17.3"
 description = "An implementation of JSON Schema validation for Python"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
     {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
@@ -706,6 +774,8 @@ version = "0.1.42"
 description = "Building applications with LLMs through composability"
 optional = false
 python-versions = "<4.0,>=3.8.1"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "langchain_core-0.1.42-py3-none-any.whl", hash = "sha256:c5653ffa08a44f740295c157a24c0def4a753333f6a2c41f76bf431cd00be8b5"},
     {file = "langchain_core-0.1.42.tar.gz", hash = "sha256:40751bf60ea5d8e2b2efe65290db434717ee3834870c002e40e2811f09d814e6"},
@@ -728,6 +798,8 @@ version = "0.1.147"
 description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
 optional = false
 python-versions = "<4.0,>=3.8.1"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"},
     {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"},
@@ -749,6 +821,8 @@ version = "3.0.2"
 description = "Safely add untrusted strings to HTML/XML markup."
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
     {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
@@ -819,6 +893,8 @@ version = "3.9.1"
 description = "Natural Language Toolkit"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1"},
     {file = "nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868"},
@@ -844,6 +920,8 @@ version = "1.26.4"
 description = "Fundamental package for array computing in Python"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
     {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
@@ -889,6 +967,8 @@ version = "3.10.15"
 description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04"},
     {file = "orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8"},
@@ -977,6 +1057,8 @@ version = "23.2"
 description = "Core utilities for Python packages"
 optional = false
 python-versions = ">=3.7"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
     {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
@@ -988,6 +1070,8 @@ version = "2.2.2"
 description = "Powerful data structures for data analysis, time series, and statistics"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
     {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
@@ -1060,6 +1144,8 @@ version = "2.1.2"
 description = "Python datetimes made easy"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pendulum-2.1.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:b6c352f4bd32dff1ea7066bd31ad0f71f8d8100b9ff709fb343f3b86cee43efe"},
     {file = "pendulum-2.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:318f72f62e8e23cd6660dbafe1e346950281a9aed144b5c596b2ddabc1d19739"},
@@ -1094,6 +1180,8 @@ version = "4.3.6"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
     {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
@@ -1110,6 +1198,8 @@ version = "1.5.0"
 description = "plugin and hook calling mechanisms for python"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
     {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@@ -1125,6 +1215,8 @@ version = "6.1.0"
 description = "Cross-platform lib for process and system monitoring in Python."
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"},
     {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"},
@@ -1155,6 +1247,8 @@ version = "1.11.0"
 description = "library with cross-python path, ini-parsing, io, code, log facilities"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
     {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
@@ -1166,6 +1260,8 @@ version = "2.22"
 description = "C parser in Python"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\" and platform_python_implementation != \"PyPy\""
 files = [
     {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
     {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -1177,6 +1273,8 @@ version = "2.10.6"
 description = "Data validation using Python type hints"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
     {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
@@ -1197,6 +1295,8 @@ version = "2.27.2"
 description = "Core functionality for Pydantic validation and serialization"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
     {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
@@ -1309,6 +1409,8 @@ version = "2.10.1"
 description = "JSON Web Token implementation in Python"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"},
     {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"},
@@ -1326,6 +1428,8 @@ version = "3.1.1"
 description = "Python Rate-Limiter using Leaky-Bucket Algorithm"
 optional = false
 python-versions = ">=3.8,<4.0"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pyrate_limiter-3.1.1-py3-none-any.whl", hash = "sha256:c51906f1d51d56dc992ff6c26e8300e32151bc6cfa3e6559792e31971dfd4e2b"},
     {file = "pyrate_limiter-3.1.1.tar.gz", hash = "sha256:2f57eda712687e6eccddf6afe8f8a15b409b97ed675fe64a626058f12863b7b7"},
@@ -1341,6 +1445,8 @@ version = "0.20.0"
 description = "Persistent/Functional/Immutable data structures"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"},
     {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"},
@@ -1382,6 +1488,8 @@ version = "6.2.5"
 description = "pytest: simple powerful testing with Python"
 optional = false
 python-versions = ">=3.6"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
     {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
@@ -1406,6 +1514,8 @@ version = "3.14.0"
 description = "Thin-wrapper around the mock package for easier use with pytest"
 optional = false
 python-versions = ">=3.8"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"},
     {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"},
@@ -1423,6 +1533,8 @@ version = "2.9.0.post0"
 description = "Extensions to the standard Python datetime module"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
     {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -1437,6 +1549,8 @@ version = "3.0.0"
 description = "Universally unique lexicographically sortable identifier"
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "python_ulid-3.0.0-py3-none-any.whl", hash = "sha256:e4c4942ff50dbd79167ad01ac725ec58f924b4018025ce22c858bfcff99a5e31"},
     {file = "python_ulid-3.0.0.tar.gz", hash = "sha256:e50296a47dc8209d28629a22fc81ca26c00982c78934bd7766377ba37ea49a9f"},
@@ -1451,6 +1565,8 @@ version = "2024.2"
 description = "World timezone definitions, modern and historical"
 optional = false
 python-versions = "*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"},
     {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"},
@@ -1462,6 +1578,8 @@ version = "2020.1"
 description = "The Olson timezone database for Python."
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "pytzdata-2020.1-py2.py3-none-any.whl", hash = "sha256:e1e14750bcf95016381e4d472bad004eef710f2d6417240904070b3d6654485f"},
     {file = "pytzdata-2020.1.tar.gz", hash = "sha256:3efa13b335a00a8de1d345ae41ec78dd11c9f8807f522d39850f2dd828681540"},
@@ -1473,6 +1591,8 @@ version = "6.0.2"
 description = "YAML parser and emitter for Python"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
     {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
@@ -1531,99 +1651,101 @@ files = [
 
 [[package]]
 name = "rapidfuzz"
-version = "3.11.0"
+version = "3.12.1"
 description = "rapid fuzzy string matching"
 optional = false
 python-versions = ">=3.9"
-files = [
-    {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb8a54543d16ab1b69e2c5ed96cabbff16db044a50eddfc028000138ca9ddf33"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:231c8b2efbd7f8d2ecd1ae900363ba168b8870644bb8f2b5aa96e4a7573bde19"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54e7f442fb9cca81e9df32333fb075ef729052bcabe05b0afc0441f462299114"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:906f1f2a1b91c06599b3dd1be207449c5d4fc7bd1e1fa2f6aef161ea6223f165"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed59044aea9eb6c663112170f2399b040d5d7b162828b141f2673e822093fa8"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cb1965a28b0fa64abdee130c788a0bc0bb3cf9ef7e3a70bf055c086c14a3d7e"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b488b244931d0291412917e6e46ee9f6a14376625e150056fe7c4426ef28225"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ba13557fec9d5ffc0a22826754a7457cc77f1b25145be10b7bb1d143ce84c6"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3871fa7dfcef00bad3c7e8ae8d8fd58089bad6fb21f608d2bf42832267ca9663"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b2669eafee38c5884a6e7cc9769d25c19428549dcdf57de8541cf9e82822e7db"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ffa1bb0e26297b0f22881b219ffc82a33a3c84ce6174a9d69406239b14575bd5"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:45b15b8a118856ac9caac6877f70f38b8a0d310475d50bc814698659eabc1cdb"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-win32.whl", hash = "sha256:22033677982b9c4c49676f215b794b0404073f8974f98739cb7234e4a9ade9ad"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:be15496e7244361ff0efcd86e52559bacda9cd975eccf19426a0025f9547c792"},
-    {file = "rapidfuzz-3.11.0-cp310-cp310-win_arm64.whl", hash = "sha256:714a7ba31ba46b64d30fccfe95f8013ea41a2e6237ba11a805a27cdd3bce2573"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8724a978f8af7059c5323d523870bf272a097478e1471295511cf58b2642ff83"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b63cb1f2eb371ef20fb155e95efd96e060147bdd4ab9fc400c97325dfee9fe1"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82497f244aac10b20710448645f347d862364cc4f7d8b9ba14bd66b5ce4dec18"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:339607394941801e6e3f6c1ecd413a36e18454e7136ed1161388de674f47f9d9"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84819390a36d6166cec706b9d8f0941f115f700b7faecab5a7e22fc367408bc3"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eea8d9e20632d68f653455265b18c35f90965e26f30d4d92f831899d6682149b"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b659e1e2ea2784a9a397075a7fc395bfa4fe66424042161c4bcaf6e4f637b38"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1315cd2a351144572e31fe3df68340d4b83ddec0af8b2e207cd32930c6acd037"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a7743cca45b4684c54407e8638f6d07b910d8d811347b9d42ff21262c7c23245"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5bb636b0150daa6d3331b738f7c0f8b25eadc47f04a40e5c23c4bfb4c4e20ae3"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:42f4dd264ada7a9aa0805ea0da776dc063533917773cf2df5217f14eb4429eae"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:51f24cb39e64256221e6952f22545b8ce21cacd59c0d3e367225da8fc4b868d8"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-win32.whl", hash = "sha256:aaf391fb6715866bc14681c76dc0308f46877f7c06f61d62cc993b79fc3c4a2a"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:ebadd5b8624d8ad503e505a99b8eb26fe3ea9f8e9c2234e805a27b269e585842"},
-    {file = "rapidfuzz-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:d895998fec712544c13cfe833890e0226585cf0391dd3948412441d5d68a2b8c"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f382fec4a7891d66fb7163c90754454030bb9200a13f82ee7860b6359f3f2fa8"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dfaefe08af2a928e72344c800dcbaf6508e86a4ed481e28355e8d4b6a6a5230e"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92ebb7c12f682b5906ed98429f48a3dd80dd0f9721de30c97a01473d1a346576"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a1b3ebc62d4bcdfdeba110944a25ab40916d5383c5e57e7c4a8dc0b6c17211a"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c6d7fea39cb33e71de86397d38bf7ff1a6273e40367f31d05761662ffda49e4"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99aebef8268f2bc0b445b5640fd3312e080bd17efd3fbae4486b20ac00466308"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4469307f464ae3089acf3210b8fc279110d26d10f79e576f385a98f4429f7d97"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb97c53112b593f89a90b4f6218635a9d1eea1d7f9521a3b7d24864228bbc0aa"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef8937dae823b889c0273dfa0f0f6c46a3658ac0d851349c464d1b00e7ff4252"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d95f9e9f3777b96241d8a00d6377cc9c716981d828b5091082d0fe3a2924b43e"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b1d67d67f89e4e013a5295e7523bc34a7a96f2dba5dd812c7c8cb65d113cbf28"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d994cf27e2f874069884d9bddf0864f9b90ad201fcc9cb2f5b82bacc17c8d5f2"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-win32.whl", hash = "sha256:ba26d87fe7fcb56c4a53b549a9e0e9143f6b0df56d35fe6ad800c902447acd5b"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1f7efdd7b7adb32102c2fa481ad6f11923e2deb191f651274be559d56fc913b"},
-    {file = "rapidfuzz-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:ed78c8e94f57b44292c1a0350f580e18d3a3c5c0800e253f1583580c1b417ad2"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e60814edd0c9b511b5f377d48b9782b88cfe8be07a98f99973669299c8bb318a"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f28952da055dbfe75828891cd3c9abf0984edc8640573c18b48c14c68ca5e06"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e8f93bc736020351a6f8e71666e1f486bb8bd5ce8112c443a30c77bfde0eb68"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76a4a11ba8f678c9e5876a7d465ab86def047a4fcc043617578368755d63a1bc"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc0e0d41ad8a056a9886bac91ff9d9978e54a244deb61c2972cc76b66752de9c"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8ea35f2419c7d56b3e75fbde2698766daedb374f20eea28ac9b1f668ef4f74"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd340bbd025302276b5aa221dccfe43040c7babfc32f107c36ad783f2ffd8775"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:494eef2c68305ab75139034ea25328a04a548d297712d9cf887bf27c158c388b"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a167344c1d6db06915fb0225592afdc24d8bafaaf02de07d4788ddd37f4bc2f"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:8c7af25bda96ac799378ac8aba54a8ece732835c7b74cfc201b688a87ed11152"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d2a0f7e17f33e7890257367a1662b05fecaf56625f7dbb6446227aaa2b86448b"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4d0d26c7172bdb64f86ee0765c5b26ea1dc45c52389175888ec073b9b28f4305"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-win32.whl", hash = "sha256:6ad02bab756751c90fa27f3069d7b12146613061341459abf55f8190d899649f"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:b1472986fd9c5d318399a01a0881f4a0bf4950264131bb8e2deba9df6d8c362b"},
-    {file = "rapidfuzz-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c408f09649cbff8da76f8d3ad878b64ba7f7abdad1471efb293d2c075e80c822"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1bac4873f6186f5233b0084b266bfb459e997f4c21fc9f029918f44a9eccd304"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f9f12c2d0aa52b86206d2059916153876a9b1cf9dfb3cf2f344913167f1c3d4"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd501de6f7a8f83557d20613b58734d1cb5f0be78d794cde64fe43cfc63f5f2"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4416ca69af933d4a8ad30910149d3db6d084781d5c5fdedb713205389f535385"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f0821b9bdf18c5b7d51722b906b233a39b17f602501a966cfbd9b285f8ab83cd"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0edecc3f90c2653298d380f6ea73b536944b767520c2179ec5d40b9145e47aa"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4513dd01cee11e354c31b75f652d4d466c9440b6859f84e600bdebfccb17735a"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d9727b85511b912571a76ce53c7640ba2c44c364e71cef6d7359b5412739c570"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ab9eab33ee3213f7751dc07a1a61b8d9a3d748ca4458fffddd9defa6f0493c16"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6b01c1ddbb054283797967ddc5433d5c108d680e8fa2684cf368be05407b07e4"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:3857e335f97058c4b46fa39ca831290b70de554a5c5af0323d2f163b19c5f2a6"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d98a46cf07c0c875d27e8a7ed50f304d83063e49b9ab63f21c19c154b4c0d08d"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-win32.whl", hash = "sha256:c36539ed2c0173b053dafb221458812e178cfa3224ade0960599bec194637048"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:ec8d7d8567e14af34a7911c98f5ac74a3d4a743cd848643341fc92b12b3784ff"},
-    {file = "rapidfuzz-3.11.0-cp39-cp39-win_arm64.whl", hash = "sha256:62171b270ecc4071be1c1f99960317db261d4c8c83c169e7f8ad119211fe7397"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f06e3c4c0a8badfc4910b9fd15beb1ad8f3b8fafa8ea82c023e5e607b66a78e4"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fe7aaf5a54821d340d21412f7f6e6272a9b17a0cbafc1d68f77f2fc11009dcd5"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25398d9ac7294e99876a3027ffc52c6bebeb2d702b1895af6ae9c541ee676702"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a52eea839e4bdc72c5e60a444d26004da00bb5bc6301e99b3dde18212e41465"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c87319b0ab9d269ab84f6453601fd49b35d9e4a601bbaef43743f26fabf496c"},
-    {file = "rapidfuzz-3.11.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3048c6ed29d693fba7d2a7caf165f5e0bb2b9743a0989012a98a47b975355cca"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b04f29735bad9f06bb731c214f27253bd8bedb248ef9b8a1b4c5bde65b838454"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7864e80a0d4e23eb6194254a81ee1216abdc53f9dc85b7f4d56668eced022eb8"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3794df87313dfb56fafd679b962e0613c88a293fd9bd5dd5c2793d66bf06a101"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d71da0012face6f45432a11bc59af19e62fac5a41f8ce489e80c0add8153c3d1"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff38378346b7018f42cbc1f6d1d3778e36e16d8595f79a312b31e7c25c50bd08"},
-    {file = "rapidfuzz-3.11.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6668321f90aa02a5a789d4e16058f2e4f2692c5230252425c3532a8a62bc3424"},
-    {file = "rapidfuzz-3.11.0.tar.gz", hash = "sha256:a53ca4d3f52f00b393fab9b5913c5bafb9afc27d030c8a1db1283da6917a860f"},
+groups = ["main"]
+markers = "python_version <= \"3.11\""
+files = [
+    {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbb7ea2fd786e6d66f225ef6eef1728832314f47e82fee877cb2a793ebda9579"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ae41361de05762c1eaa3955e5355de7c4c6f30d1ef1ea23d29bf738a35809ab"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc3c39e0317e7f68ba01bac056e210dd13c7a0abf823e7b6a5fe7e451ddfc496"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69f2520296f1ae1165b724a3aad28c56fd0ac7dd2e4cff101a5d986e840f02d4"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34dcbf5a7daecebc242f72e2500665f0bde9dd11b779246c6d64d106a7d57c99"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:773ab37fccf6e0513891f8eb4393961ddd1053c6eb7e62eaa876e94668fc6d31"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ecf0e6de84c0bc2c0f48bc03ba23cef2c5f1245db7b26bc860c11c6fd7a097c"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4dc2ebad4adb29d84a661f6a42494df48ad2b72993ff43fad2b9794804f91e45"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8389d98b9f54cb4f8a95f1fa34bf0ceee639e919807bb931ca479c7a5f2930bf"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:165bcdecbfed9978962da1d3ec9c191b2ff9f1ccc2668fbaf0613a975b9aa326"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:129d536740ab0048c1a06ccff73c683f282a2347c68069affae8dbc423a37c50"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b67e390261ffe98ec86c771b89425a78b60ccb610c3b5874660216fcdbded4b"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-win32.whl", hash = "sha256:a66520180d3426b9dc2f8d312f38e19bc1fc5601f374bae5c916f53fa3534a7d"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:82260b20bc7a76556cecb0c063c87dad19246a570425d38f8107b8404ca3ac97"},
+    {file = "rapidfuzz-3.12.1-cp310-cp310-win_arm64.whl", hash = "sha256:3a860d103bbb25c69c2e995fdf4fac8cb9f77fb69ec0a00469d7fd87ff148f46"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d9afad7b16d01c9e8929b6a205a18163c7e61b6cd9bcf9c81be77d5afc1067a"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb424ae7240f2d2f7d8dda66a61ebf603f74d92f109452c63b0dbf400204a437"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42149e6d13bd6d06437d2a954dae2184dadbbdec0fdb82dafe92860d99f80519"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:760ac95d788f2964b73da01e0bdffbe1bf2ad8273d0437565ce9092ae6ad1fbc"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2cf27e8e4bf7bf9d92ef04f3d2b769e91c3f30ba99208c29f5b41e77271a2614"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00ceb8ff3c44ab0d6014106c71709c85dee9feedd6890eff77c814aa3798952b"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b61c558574fbc093d85940c3264c08c2b857b8916f8e8f222e7b86b0bb7d12"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:346a2d8f17224e99f9ef988606c83d809d5917d17ad00207237e0965e54f9730"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d60d1db1b7e470e71ae096b6456e20ec56b52bde6198e2dbbc5e6769fa6797dc"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2477da227e266f9c712f11393182c69a99d3c8007ea27f68c5afc3faf401cc43"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8499c7d963ddea8adb6cffac2861ee39a1053e22ca8a5ee9de1197f8dc0275a5"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:12802e5c4d8ae104fb6efeeb436098325ce0dca33b461c46e8df015c84fbef26"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-win32.whl", hash = "sha256:e1061311d07e7cdcffa92c9b50c2ab4192907e70ca01b2e8e1c0b6b4495faa37"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6e4ed63e204daa863a802eec09feea5448617981ba5d150f843ad8e3ae071a4"},
+    {file = "rapidfuzz-3.12.1-cp311-cp311-win_arm64.whl", hash = "sha256:920733a28c3af47870835d59ca9879579f66238f10de91d2b4b3f809d1ebfc5b"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f6235b57ae3faa3f85cb3f90c9fee49b21bd671b76e90fc99e8ca2bdf0b5e4a3"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af4585e5812632c357fee5ab781c29f00cd06bea58f8882ff244cc4906ba6c9e"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5942dc4460e5030c5f9e1d4c9383de2f3564a2503fe25e13e89021bcbfea2f44"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b31ab59e1a0df5afc21f3109b6cfd77b34040dbf54f1bad3989f885cfae1e60"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97c885a7a480b21164f57a706418c9bbc9a496ec6da087e554424358cadde445"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d844c0587d969ce36fbf4b7cbf0860380ffeafc9ac5e17a7cbe8abf528d07bb"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93c95dce8917bf428064c64024de43ffd34ec5949dd4425780c72bd41f9d969"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:834f6113d538af358f39296604a1953e55f8eeffc20cb4caf82250edbb8bf679"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a940aa71a7f37d7f0daac186066bf6668d4d3b7e7ef464cb50bc7ba89eae1f51"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ec9eaf73501c9a7de2c6938cb3050392e2ee0c5ca3921482acf01476b85a7226"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3c5ec360694ac14bfaeb6aea95737cf1a6cf805b5fe8ea7fd28814706c7fa838"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b5e176524653ac46f1802bdd273a4b44a5f8d0054ed5013a8e8a4b72f254599"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-win32.whl", hash = "sha256:6f463c6f1c42ec90e45d12a6379e18eddd5cdf74138804d8215619b6f4d31cea"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:b894fa2b30cd6498a29e5c470cb01c6ea898540b7e048a0342775a5000531334"},
+    {file = "rapidfuzz-3.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:43bb17056c5d1332f517b888c4e57846c4b5f936ed304917eeb5c9ac85d940d4"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:97f824c15bc6933a31d6e3cbfa90188ba0e5043cf2b6dd342c2b90ee8b3fd47c"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a973b3f5cabf931029a3ae4a0f72e3222e53d412ea85fc37ddc49e1774f00fbf"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df7880e012228722dec1be02b9ef3898ed023388b8a24d6fa8213d7581932510"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c78582f50e75e6c2bc38c791ed291cb89cf26a3148c47860c1a04d6e5379c8e"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d7d9e6a04d8344b0198c96394c28874086888d0a2b2f605f30d1b27b9377b7d"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5620001fd4d6644a2f56880388179cc8f3767670f0670160fcb97c3b46c828af"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0666ab4c52e500af7ba5cc17389f5d15c0cdad06412c80312088519fdc25686d"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:27b4d440fa50b50c515a91a01ee17e8ede719dca06eef4c0cccf1a111a4cfad3"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83dccfd5a754f2a0e8555b23dde31f0f7920601bfa807aa76829391ea81e7c67"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b572b634740e047c53743ed27a1bb3b4f93cf4abbac258cd7af377b2c4a9ba5b"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7fa7b81fb52902d5f78dac42b3d6c835a6633b01ddf9b202a3ca8443be4b2d6a"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1d4fbff980cb6baef4ee675963c081f7b5d6580a105d6a4962b20f1f880e1fb"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-win32.whl", hash = "sha256:3fe8da12ea77271097b303fa7624cfaf5afd90261002314e3b0047d36f4afd8d"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:6f7e92fc7d2a7f02e1e01fe4f539324dfab80f27cb70a30dd63a95445566946b"},
+    {file = "rapidfuzz-3.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:e31be53d7f4905a6a038296d8b773a79da9ee9f0cd19af9490c5c5a22e37d2e5"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bef5c91d5db776523530073cda5b2a276283258d2f86764be4a008c83caf7acd"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:841e0c2a5fbe8fc8b9b1a56e924c871899932c0ece7fbd970aa1c32bfd12d4bf"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:046fc67f3885d94693a2151dd913aaf08b10931639cbb953dfeef3151cb1027c"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4d2d39b2e76c17f92edd6d384dc21fa020871c73251cdfa017149358937a41d"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5857dda85165b986c26a474b22907db6b93932c99397c818bcdec96340a76d5"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c26cd1b9969ea70dbf0dbda3d2b54ab4b2e683d0fd0f17282169a19563efeb1"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf56ea4edd69005786e6c80a9049d95003aeb5798803e7a2906194e7a3cb6472"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbe7580b5fb2db8ebd53819171ff671124237a55ada3f64d20fc9a149d133960"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:018506a53c3b20dcbda8c93d4484b9eb1764c93d5ea16be103cf6b0d8b11d860"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:325c9c71b737fcd32e2a4e634c430c07dd3d374cfe134eded3fe46e4c6f9bf5d"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:930756639643e3aa02d3136b6fec74e5b9370a24f8796e1065cd8a857a6a6c50"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0acbd27543b158cb915fde03877383816a9e83257832818f1e803bac9b394900"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-win32.whl", hash = "sha256:80ff9283c54d7d29b2d954181e137deee89bec62f4a54675d8b6dbb6b15d3e03"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:fd37e53f0ed239d0cec27b250cec958982a8ba252ce64aa5e6052de3a82fa8db"},
+    {file = "rapidfuzz-3.12.1-cp39-cp39-win_arm64.whl", hash = "sha256:4a4422e4f73a579755ab60abccb3ff148b5c224b3c7454a13ca217dfbad54da6"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b7cba636c32a6fc3a402d1cb2c70c6c9f8e6319380aaf15559db09d868a23e56"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b79286738a43e8df8420c4b30a92712dec6247430b130f8e015c3a78b6d61ac2"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dc1937198e7ff67e217e60bfa339f05da268d91bb15fec710452d11fe2fdf60"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b85817a57cf8db32dd5d2d66ccfba656d299b09eaf86234295f89f91be1a0db2"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04283c6f3e79f13a784f844cd5b1df4f518ad0f70c789aea733d106c26e1b4fb"},
+    {file = "rapidfuzz-3.12.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a718f740553aad5f4daef790191511da9c6eae893ee1fc2677627e4b624ae2db"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cbdf145c7e4ebf2e81c794ed7a582c4acad19e886d5ad6676086369bd6760753"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0d03ad14a26a477be221fddc002954ae68a9e2402b9d85433f2d0a6af01aa2bb"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1187aeae9c89e838d2a0a2b954b4052e4897e5f62e5794ef42527bf039d469e"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd47dfb1bca9673a48b923b3d988b7668ee8efd0562027f58b0f2b7abf27144c"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187cdb402e223264eebed2fe671e367e636a499a7a9c82090b8d4b75aa416c2a"},
+    {file = "rapidfuzz-3.12.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d6899b41bf6c30282179f77096c1939f1454836440a8ab05b48ebf7026a3b590"},
+    {file = "rapidfuzz-3.12.1.tar.gz", hash = "sha256:6a98bbca18b4a37adddf2d8201856441c26e9c981d8895491b5bc857b5f780eb"},
 ]
 
 [package.extras]
@@ -1635,6 +1757,8 @@ version = "2024.11.6"
 description = "Alternative regular expression module, to replace re."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"},
     {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"},
@@ -1738,6 +1862,8 @@ version = "2.32.3"
 description = "Python HTTP for Humans."
 optional = false
 python-versions = ">=3.8"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
     {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
@@ -1759,6 +1885,8 @@ version = "1.2.1"
 description = "A persistent cache for python requests"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "requests_cache-1.2.1-py3-none-any.whl", hash = "sha256:1285151cddf5331067baa82598afe2d47c7495a1334bfe7a7d329b43e9fd3603"},
     {file = "requests_cache-1.2.1.tar.gz", hash = "sha256:68abc986fdc5b8d0911318fbb5f7c80eebcd4d01bfacc6685ecf8876052511d1"},
@@ -1789,6 +1917,8 @@ version = "1.12.1"
 description = "Mock out responses from the requests package"
 optional = false
 python-versions = ">=3.5"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"},
     {file = "requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563"},
@@ -1806,6 +1936,8 @@ version = "1.0.0"
 description = "A utility belt for advanced users of python-requests"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"},
     {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"},
@@ -1820,6 +1952,8 @@ version = "1.13.0"
 description = ""
 optional = false
 python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "serpyco_rs-1.13.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e722b3053e627d8a304e462bce20cae1670a2c4b0ef875b84d0de0081bec4029"},
     {file = "serpyco_rs-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f10e89c752ff78d720a42e026b0a9ada70717ad6306a9356f794280167d62bf"},
@@ -1874,6 +2008,8 @@ version = "1.17.0"
 description = "Python 2 and 3 compatibility utilities"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
     {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -1885,6 +2021,8 @@ version = "1.3.1"
 description = "Sniff out which async library your code is running under"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
     {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
@@ -1896,6 +2034,8 @@ version = "8.5.0"
 description = "Retry code until it succeeds"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"},
     {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"},
@@ -1911,6 +2051,8 @@ version = "0.10.2"
 description = "Python Library for Tom's Obvious, Minimal Language"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+groups = ["dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@@ -1922,6 +2064,8 @@ version = "4.67.1"
 description = "Fast, Extensible Progress Meter"
 optional = false
 python-versions = ">=3.7"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
     {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
@@ -1943,6 +2087,8 @@ version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
     {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
@@ -1954,6 +2100,8 @@ version = "2025.1"
 description = "Provider of IANA time zone data"
 optional = false
 python-versions = ">=2"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"},
     {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"},
@@ -1965,6 +2113,8 @@ version = "1.3.8"
 description = "ASCII transliterations of Unicode text"
 optional = false
 python-versions = ">=3.5"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39"},
     {file = "Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4"},
@@ -1976,6 +2126,8 @@ version = "1.4.3"
 description = "URL normalization for Python"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "url-normalize-1.4.3.tar.gz", hash = "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2"},
     {file = "url_normalize-1.4.3-py2.py3-none-any.whl", hash = "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed"},
@@ -1990,6 +2142,8 @@ version = "2.3.0"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 optional = false
 python-versions = ">=3.9"
+groups = ["main", "dev"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
     {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
@@ -2007,6 +2161,8 @@ version = "10.0"
 description = "Wildcard/glob file name matcher."
 optional = false
 python-versions = ">=3.8"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "wcmatch-10.0-py3-none-any.whl", hash = "sha256:0dd927072d03c0a6527a20d2e6ad5ba8d0380e60870c383bc533b71744df7b7a"},
     {file = "wcmatch-10.0.tar.gz", hash = "sha256:e72f0de09bba6a04e0de70937b0cf06e55f36f37b3deb422dfaf854b867b840a"},
@@ -2015,18 +2171,105 @@ files = [
 [package.dependencies]
 bracex = ">=2.1.1"
 
+[[package]]
+name = "whenever"
+version = "0.6.17"
+description = "Modern datetime library for Python"
+optional = false
+python-versions = ">=3.9"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
+files = [
+    {file = "whenever-0.6.17-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8e9e905fd19b0679e5ab1a0d0110a1974b89bf4cbd1ff22c9e352db381e4ae4f"},
+    {file = "whenever-0.6.17-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cd615e60f992fb9ae9d73fc3581ac63de981e51013b0fffbf8e2bd748c71e3df"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd717faa660771bf6f2fda4f75f2693cd79f2a7e975029123284ea3859fb329c"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2ea744d9666be8880062da0d6dee690e8f70a2bc2a42b96ee17e10e36b0b5266"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6b32593b44332660402c7e4c681cce6d7859b15a609d66ac3a28a6ad6357c2f"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a01e4daaac24e0be48a6cb0bb03fa000a40126b1e9cb8d721ee116b2f44c1bb1"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53e88fe9fccb868ee88bb2ee8bfcbc55937d0b40747069f595f10b4832ff1545"},
+    {file = "whenever-0.6.17-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2dce7b9faf23325b38ca713b2c7a150a8befc832995213a8ec46fe15af6a03e7"},
+    {file = "whenever-0.6.17-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0925f7bf3448ef4f8c9b93de2d1270b82450a81b5d025a89f486ea61aa94319"},
+    {file = "whenever-0.6.17-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:82203a572049070d685499dd695ff1914fee62f32aefa9e9952a60762217aa9e"},
+    {file = "whenever-0.6.17-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c30e5b5b82783bc85169c8208ab3acf58648092515017b2a185a598160503dbb"},
+    {file = "whenever-0.6.17-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:763e59062adc9adfbde45c3ad8b5f472b337cc5cebc70760627d004a4c286d33"},
+    {file = "whenever-0.6.17-cp310-cp310-win32.whl", hash = "sha256:f71387bbe95cd98fc78653b942c6e02ff4245b6add012b3f11796220272984ce"},
+    {file = "whenever-0.6.17-cp310-cp310-win_amd64.whl", hash = "sha256:996ab1f6f09bc9e0c699fa58937b5adc25e39e979ebbebfd77bae09221350f3d"},
+    {file = "whenever-0.6.17-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:87e28378945182e822e211fcea9e89c7428749fd440b616d6d81365202cbed09"},
+    {file = "whenever-0.6.17-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cf4ee3e8d5a55d788e8a79aeff29482dd4facc38241901f18087c3e662d16ba"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97ffc43cd278f6f58732cd9d83c822faff3b1987c3b7b448b59b208cf6b6293"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ce99533865fd63029fa64aef1cfbd42be1d2ced33da38c82f8c763986583982"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68b88e023d64e8ccfabe04028738d8041eccd5a078843cd9b506e51df3375e84"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9159bae31f2edaf5e70e4437d871e52f51e7e90f1b9faaac19a8c2bccba5170a"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f9c4ee1f1e85f857507d146d56973db28d148f50883babf1da3d24a40bbcf60"},
+    {file = "whenever-0.6.17-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0acd8b3238aa28a20d1f93c74fd84c9b59e2662e553a55650a0e663a81d2908d"},
+    {file = "whenever-0.6.17-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ae238cd46567b5741806517d307a81cca45fd49902312a9bdde27db5226e8825"},
+    {file = "whenever-0.6.17-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:99f72853e8292284c2a89a06ab826892216c04540a0ca84b3d3eaa9317dbe026"},
+    {file = "whenever-0.6.17-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ccb6c77b497d651a283ef0f40ada326602b313ee71d22015f53d5496124dfc10"},
+    {file = "whenever-0.6.17-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a1918c9836dc331cd9a39175806668b57b93d538d288469ad8bedb144ec11b"},
+    {file = "whenever-0.6.17-cp311-cp311-win32.whl", hash = "sha256:72492f130a8c5b8abb2d7b16cec33b6d6ed9e294bb63c56ab1030623de4ae343"},
+    {file = "whenever-0.6.17-cp311-cp311-win_amd64.whl", hash = "sha256:88dc4961f8f6cd16d9b70db022fd6c86193fad429f98daeb82c8e9ba0ca27e5c"},
+    {file = "whenever-0.6.17-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:d72c2413e32e3f382f6def337961ea7f20e66d0452ebc02e2fa215e1c45df73e"},
+    {file = "whenever-0.6.17-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d12b891d780d9c98585b507e9f85097085337552b75f160ce6930af96509faa1"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:503aaf2acfd5a7926ca5c6dc6ec09fc6c2891f536ab9cbd26a072c94bda3927f"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6de09bcddfeb61c822019e88d8abed9ccc1d4f9d1a3a5d62d28d94d2fb6daff5"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfe430df7f336d8793b6b844f0d2552e1589e39e72b7414ba67139b9b402bed"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99776635ac174a3df4a372bfae7420b3de965044d69f2bee08a7486cabba0aaa"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdbb6d8dae94b492370949c8d8bf818f9ee0b4a08f304dadf9d6d892b7513676"},
+    {file = "whenever-0.6.17-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:45d66e68cdca52ca3e6e4990515d32f6bc4eb6a24ff8cbcbe4df16401dd2d3c7"},
+    {file = "whenever-0.6.17-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73947bd633bc658f8a8e2ff2bff34ee7caabd6edd9951bb2d778e6071c772df4"},
+    {file = "whenever-0.6.17-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9f9d5b108f9abf39471e3d5ef22ff2fed09cc51a0cfa63c833c393b21b8bdb81"},
+    {file = "whenever-0.6.17-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a42231e7623b50a60747a752a97499f6ad03e03ce128bf97ded84e12b0f4a77e"},
+    {file = "whenever-0.6.17-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a6d9458d544006131e1210343bf660019abfa11d46f5be8ad2d7616dc82340f4"},
+    {file = "whenever-0.6.17-cp312-cp312-win32.whl", hash = "sha256:ca1eda94ca2ef7ad1a1249ea80949be252e78a0f10463e12c81ad126ec6b99e5"},
+    {file = "whenever-0.6.17-cp312-cp312-win_amd64.whl", hash = "sha256:fd7de20d6bbb74c6bad528c0346ef679957db21ce8a53f118e53b5f60f76495b"},
+    {file = "whenever-0.6.17-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca9ee5b2b04c5a65112f55ff4a4efcba185f45b95766b669723e8b9a28bdb50b"},
+    {file = "whenever-0.6.17-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bef0cf1cd4282044d98e4af9969239dc139e5b192896d4110d0d3f4139bdb30"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04ac4e1fc1bc0bfb35f2c6a05d52de9fec297ea84ee60c655dec258cca1e6eb7"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c792f96d021ba2883e6f4b70cc58b5d970f026eb156ff93866686e27a7cce93"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a7f938b5533e751702de95a615b7903457a7618b94aef72c062fa871ad691b"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:47d2dbb85c512e28c14eede36a148afbb90baa340e113b39b2b9f0e9a3b192dd"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea2b49a91853c133e8954dffbf180adca539b3719fd269565bf085ba97b47f5f"},
+    {file = "whenever-0.6.17-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:91fcb2f42381a8ad763fc7ee2259375b1ace1306a02266c195af27bd3696e0da"},
+    {file = "whenever-0.6.17-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e4d5e3429015a5082cd171ceea633c6ea565d90491005cdcef49a7d6a17c99"},
+    {file = "whenever-0.6.17-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f05731f530e4af29582a70cf02f8441027a4534e67b7c484efdf210fc09d0421"},
+    {file = "whenever-0.6.17-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0d417b7de29aea2cfa7ea47f344848491d44291f28c038df869017ae66a50b48"},
+    {file = "whenever-0.6.17-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8208333ece7f2e0c232feeecbd21bde3888c6782d3b08372ae8b5269938645b3"},
+    {file = "whenever-0.6.17-cp313-cp313-win32.whl", hash = "sha256:c4912104731fd2be89cd031d8d34227225f1fae5181f931b91f217e69ded48ff"},
+    {file = "whenever-0.6.17-cp313-cp313-win_amd64.whl", hash = "sha256:4f46ad87fab336d7643e0c2248dcd27a0f4ae42ac2c5e864a9d06a8f5538efd0"},
+    {file = "whenever-0.6.17-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:53f03ae8c54aa60f5f22c790eb63ad644e97f8fba4b22337572a4e16bc4abb73"},
+    {file = "whenever-0.6.17-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42fce832892578455d46870dc074521e627ba9272b839a8297784059170030f5"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ac0786d6cb479275ea627d84536f38b6a408348961856e2e807d82d4dc768ed"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e2f490b5e90b314cf7615435e24effe2356b57fa907fedb98fe58d49c6109c5"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c1f25ab893cfa724b319a838ef60b918bd35be8f3f6ded73e6fd6e508b5237e"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac5f644d0d3228e806b5129cebfb824a5e26553a0d47d89fc9e962cffa1b99ed"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e185309314b1abcc14c18597dd0dfe7fd8b39670f63a7d9357544994cba0e251"},
+    {file = "whenever-0.6.17-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cc78b8a73a71241bf356743dd76133ccf796616823d8bbe170701a51d10b9fd3"},
+    {file = "whenever-0.6.17-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0ea05123a0b3673c7cf3ea1fe3d8aa9362571db59f8ea15d7a8fb05d885fd756"},
+    {file = "whenever-0.6.17-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9f0c874dbb49c3a733ce4dde86ffa243f166b9d1db4195e05127ec352b49d617"},
+    {file = "whenever-0.6.17-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:86cfbd724b11e8a419056211381bde4c1d35ead4bea8d498c85bee3812cf4e7c"},
+    {file = "whenever-0.6.17-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e1514f4a3094f11e1ad63b9defadf375d953709c7806cc1d2396634a7b00a009"},
+    {file = "whenever-0.6.17-cp39-cp39-win32.whl", hash = "sha256:715ed172e929327c1b68e107f0dc9520237d92e11c26db95fd05869724f3e9d9"},
+    {file = "whenever-0.6.17-cp39-cp39-win_amd64.whl", hash = "sha256:5fed15042b2b0ea44cafb8b7426e99170d3f4cd64dbeb966c77f14985e724d82"},
+    {file = "whenever-0.6.17.tar.gz", hash = "sha256:9c4bfe755c8f06726c4031dbbecd0a7710e2058bc2f3b4e4e331755af015f55f"},
+]
+
+[package.dependencies]
+tzdata = {version = ">=2020.1", markers = "sys_platform == \"win32\""}
+
 [[package]]
 name = "xmltodict"
 version = "0.14.2"
 description = "Makes working with XML feel like you are working with JSON"
 optional = false
 python-versions = ">=3.6"
+groups = ["main"]
+markers = "python_version <= \"3.11\""
 files = [
     {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"},
     {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"},
 ]
 
 [metadata]
-lock-version = "2.0"
+lock-version = "2.1"
 python-versions = "^3.10,<3.12"
-content-hash = "3cfa2b29c444ab0c35f30ed1cc0edcf31549057ef36997fefb59774817c40e90"
+content-hash = "0f7a990c524720e78dca8adaa825ca1fd63cbdeeda3aebe5b1b6a1f6176153cd"
diff --git a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
index fbbdbfa8bdc28..45aa197660b4e 100644
--- a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
+++ b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
@@ -17,7 +17,8 @@ include = "source_klaviyo"
 
 [tool.poetry.dependencies]
 python = "^3.10,<3.12"
-airbyte_cdk = "^6"
+airbyte_cdk = "v6.33.1.dev1"
+pendulum = "<3.0.0"
 
 [tool.poetry.scripts]
 source-klaviyo = "source_klaviyo.run:run"

From ee24f8e91af0c6342a99c7cff37f8395000674e3 Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Mon, 10 Feb 2025 12:58:47 +0200
Subject: [PATCH 3/9] Add params to request matcher

---
 .../connectors/source-klaviyo/source_klaviyo/manifest.yaml   | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
index fdcd1a1577cb7..64eab0fc3c08a 100644
--- a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
@@ -1126,7 +1126,8 @@ api_budget:
       matchers:
         - method: GET
           url_path_pattern: "^/api/lists/"   # matches any path beginning with '/lists/' (e.g. '/lists/123')
-  # Other API budget settings:
+          params:
+            "additional-fields[list]": "profile_count"  # Other API budget settings:
   status_codes_for_ratelimit_hit: [429]
   maximum_attempts_to_acquire: 100000
 
@@ -1154,5 +1155,5 @@ api_budget:
 # Based on the above, the only threads that allow for slicing and hence might perform more concurrent HTTP requests are `events` and `events_detailed`. There are no slicing for the others and hence the concurrency is limited by the number of streams querying the same endpoint. Given that the event endpoint is XL, we will set a default concurrency to 10.
 concurrency_level:
   type: ConcurrencyLevel
-  default_concurrency: "{{ config.get('num_workers', 50) }}"
+  default_concurrency: "{{ config.get('num_workers', 25) }}"
   max_concurrency: 50

From f4db8a38a28ce97c1cc2ea1b07237f2d816d9692 Mon Sep 17 00:00:00 2001
From: Octavia Squidington III
 <octavia-squidington-iii@users.noreply.github.com>
Date: Mon, 10 Feb 2025 13:26:39 +0000
Subject: [PATCH 4/9] chore: auto-fix lint and format issues

---
 .../source_klaviyo/manifest.yaml              | 36 +++++++++----------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
index 64eab0fc3c08a..fc2760f7eb950 100644
--- a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
@@ -1069,39 +1069,39 @@ api_budget:
     # Profiles (and global_exclusions share the same endpoint)
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 10          # burst: 10 calls per second
+        - limit: 10 # burst: 10 calls per second
           interval: PT1S
-        - limit: 150         # steady: 150 calls per minute
+        - limit: 150 # steady: 150 calls per minute
           interval: PT1M
       matchers:
         - method: GET
-          url_path_pattern: "^/api/profiles($|/)"  # matches '/profiles' (exact or with trailing slash/extra)
+          url_path_pattern: "^/api/profiles($|/)" # matches '/profiles' (exact or with trailing slash/extra)
     # Events (and events_detailed share the same endpoint)
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 350         # burst: 350 calls per second
+        - limit: 350 # burst: 350 calls per second
           interval: PT1S
-        - limit: 3500        # steady: 3500 calls per minute
+        - limit: 3500 # steady: 3500 calls per minute
           interval: PT1M
       matchers:
         - method: GET
-          url_path_pattern: "^/api/events($|/)"  # matches '/events' and '/events_detailed' if using same endpoint
+          url_path_pattern: "^/api/events($|/)" # matches '/events' and '/events_detailed' if using same endpoint
     # Email Templates
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 10          # burst: 10 calls per second
+        - limit: 10 # burst: 10 calls per second
           interval: PT1S
-        - limit: 150         # steady: 150 calls per minute
+        - limit: 150 # steady: 150 calls per minute
           interval: PT1M
       matchers:
         - method: GET
-          url_path_pattern: "^/api/templates($|/)"  # matches '/templates'
+          url_path_pattern: "^/api/templates($|/)" # matches '/templates'
     # Metrics
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 10          # burst: 10 calls per second
+        - limit: 10 # burst: 10 calls per second
           interval: PT1S
-        - limit: 150         # steady: 150 calls per minute
+        - limit: 150 # steady: 150 calls per minute
           interval: PT1M
       matchers:
         - method: GET
@@ -1109,25 +1109,25 @@ api_budget:
     # Lists (the parent endpoint for lists streams)
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 75          # burst: 75 calls per second
+        - limit: 75 # burst: 75 calls per second
           interval: PT1S
-        - limit: 700         # steady: 700 calls per minute
+        - limit: 700 # steady: 700 calls per minute
           interval: PT1M
       matchers:
         - method: GET
-          url_path_pattern: "^/api/lists$"   # exactly '/lists'
+          url_path_pattern: "^/api/lists$" # exactly '/lists'
     # Lists Detailed (uses a different URL path – note the extra segment)
     - type: MovingWindowCallRatePolicy
       rates:
-        - limit: 1           # burst: 1 call per second
+        - limit: 1 # burst: 1 call per second
           interval: PT1S
-        - limit: 15          # steady: 15 calls per minute
+        - limit: 15 # steady: 15 calls per minute
           interval: PT1M
       matchers:
         - method: GET
-          url_path_pattern: "^/api/lists/"   # matches any path beginning with '/lists/' (e.g. '/lists/123')
+          url_path_pattern: "^/api/lists/" # matches any path beginning with '/lists/' (e.g. '/lists/123')
           params:
-            "additional-fields[list]": "profile_count"  # Other API budget settings:
+            "additional-fields[list]": "profile_count" # Other API budget settings:
   status_codes_for_ratelimit_hit: [429]
   maximum_attempts_to_acquire: 100000
 

From bbe1dd29dd2c078c7d7d77248fb544b071e94736 Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Tue, 11 Feb 2025 17:26:21 +0200
Subject: [PATCH 5/9] Add docs

---
 .../rate-limit-api-budget.md                  | 292 ++++++++++++++++++
 1 file changed, 292 insertions(+)
 create mode 100644 docs/connector-development/config-based/understanding-the-yaml-file/rate-limit-api-budget.md

diff --git a/docs/connector-development/config-based/understanding-the-yaml-file/rate-limit-api-budget.md b/docs/connector-development/config-based/understanding-the-yaml-file/rate-limit-api-budget.md
new file mode 100644
index 0000000000000..dee3bcdbd4683
--- /dev/null
+++ b/docs/connector-development/config-based/understanding-the-yaml-file/rate-limit-api-budget.md
@@ -0,0 +1,292 @@
+# Rate limiting (API Budget)
+
+In order to prevent sending too many requests to the API in a short period of time, you can configure an API Budget. This budget determines the maximum number of calls that can be made within a specified time interval (or intervals). This mechanism is particularly useful for respecting third-party API rate limits and avoiding potential throttling or denial of service.
+
+When using an **HTTPAPIBudget**, rate limit updates can be automatically extracted from HTTP response headers such as _remaining calls_ or _time-to-reset_ values.
+
+## Schema
+
+```yaml
+HTTPAPIBudget:
+  type: object
+  title: HTTP API Budget
+  description: >
+    An HTTP-specific API budget that extends APIBudget by updating rate limiting information based
+    on HTTP response headers. It extracts available calls and the next reset timestamp from the HTTP responses.
+  required:
+    - type
+    - policies
+  properties:
+    type:
+      type: string
+      enum: [HTTPAPIBudget]
+    policies:
+      type: array
+      description: List of call rate policies that define how many calls are allowed.
+      items:
+        anyOf:
+          - "$ref": "#/definitions/FixedWindowCallRatePolicy"
+          - "$ref": "#/definitions/MovingWindowCallRatePolicy"
+          - "$ref": "#/definitions/UnlimitedCallRatePolicy"
+    ratelimit_reset_header:
+      type: string
+      default: "ratelimit-reset"
+      description: The HTTP response header name that indicates when the rate limit resets.
+    ratelimit_remaining_header:
+      type: string
+      default: "ratelimit-remaining"
+      description: The HTTP response header name that indicates the number of remaining allowed calls.
+    status_codes_for_ratelimit_hit:
+      type: array
+      default: [429]
+      items:
+        type: integer
+      description: List of HTTP status codes that indicate a rate limit has been hit.
+  additionalProperties: true
+```
+
+An `HTTPAPIBudget` may contain one or more rate policies. These policies define how rate limits should be enforced.
+
+## Example usage
+```yaml
+api_budget:
+  type: "HTTPAPIBudget"
+  ratelimit_reset_header: "X-RateLimit-Reset"
+  ratelimit_remaining_header: "X-RateLimit-Remaining"
+  status_codes_for_ratelimit_hit: [ 429 ]
+  policies:
+    - type: "UnlimitedCallRatePolicy"
+      matchers: []
+    - type: "FixedWindowCallRatePolicy"
+      period: "PT1H"
+      call_limit: 1000
+      matchers:
+        - method: "GET"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/users"
+    - type: "MovingWindowCallRatePolicy"
+      rates:
+        - limit: 100
+          interval: "PT1M"
+      matchers:
+        - method: "POST"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/users"
+```
+Above, we define:
+
+1. **UnlimitedCallRatePolicy**: A policy with no limit on requests.
+2. **FixedWindowCallRatePolicy**: Allows a set number of calls within a fixed time window (in the example, 1000 calls per 1 hour).
+3. **MovingWindowCallRatePolicy**: Uses a moving time window to track how many calls were made in the last interval. In the example, up to 100 calls per 1 minute for `POST /users`.
+
+
+## Rate Policies
+### Unlimited call rate policy
+Use this policy if you want to allow unlimited calls for a subset of requests.
+For instance, the policy below will not limit requests that match its `matchers`:
+
+```yaml
+UnlimitedCallRatePolicy:
+  type: object
+  title: Unlimited Call Rate Policy
+  description: A policy that allows unlimited calls for specific requests.
+  required:
+    - type
+    - matchers
+  properties:
+    type:
+      type: string
+      enum: [UnlimitedCallRatePolicy]
+    matchers:
+      type: array
+      items:
+        "$ref": "#/definitions/HttpRequestRegexMatcher"
+```
+
+#### Example
+```yaml
+api_budget:
+  type: "HTTPAPIBudget"
+  policies:
+    - type: "UnlimitedCallRatePolicy"
+      # For any GET request on https://api.example.com/sandbox
+      matchers:
+        - method: "GET"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/sandbox"
+```
+Here, any request matching the above matcher is not rate-limited.
+
+### Fixed Window Call Rate Policy
+This policy allows **n** calls per specified interval (for example, 1000 calls per hour). After the time window ends (the “fixed window”), it resets, and you can make new calls.
+
+```yaml
+FixedWindowCallRatePolicy:
+  type: object
+  title: Fixed Window Call Rate Policy
+  description: A policy that allows a fixed number of calls within a specific time window.
+  required:
+    - type
+    - period
+    - call_limit
+    - matchers
+  properties:
+    type:
+      type: string
+      enum: [FixedWindowCallRatePolicy]
+    period:
+      type: string
+      format: duration
+    call_limit:
+      type: integer
+    matchers:
+      type: array
+      items:
+        "$ref": "#/definitions/HttpRequestRegexMatcher"
+    additionalProperties: true
+```
+- **period**: In ISO 8601 duration format (e.g. `PT1H` for 1 hour, `PT15M` for 15 minutes).
+- **call_limit**: Maximum allowed calls within that period.
+- **matchers**: A list of request matchers (by HTTP method, URL path, etc.) that this policy applies to.
+
+#### Example
+```yaml
+api_budget:
+ type: "HTTPAPIBudget"
+ policies:
+   - type: "FixedWindowCallRatePolicy"
+     period: "PT1H"
+     call_limit: 1000
+     matchers:
+       - method: "GET"
+         url_base: "https://api.example.com"
+         url_path_pattern: "^/users"
+```
+
+### Moving Window Call Rate Policy
+This policy allows a certain number of calls in a “sliding” or “moving” window, using timestamps for each call. For example, 100 requests allowed within the last 60 seconds.
+
+```yaml
+MovingWindowCallRatePolicy:
+  type: object
+  title: Moving Window Call Rate Policy
+  description: A policy that allows a fixed number of calls within a moving time window.
+  required:
+    - type
+    - rates
+    - matchers
+  properties:
+    type:
+      type: string
+      enum: [MovingWindowCallRatePolicy]
+    rates:
+      type: array
+      items:
+        "$ref": "#/definitions/Rate"
+    matchers:
+      type: array
+      items:
+        "$ref": "#/definitions/HttpRequestRegexMatcher"
+    additionalProperties: true
+```
+
+- **rates**: A list of `Rate` objects, each specifying a `limit` and `interval`.
+- **interval**: An ISO 8601 duration (e.g., `"PT1M"` is 1 minute).
+- **limit**: Number of calls allowed within that interval.
+
+#### Example
+```yaml
+api_budget:
+  type: "HTTPAPIBudget"
+  policies:
+    - type: "MovingWindowCallRatePolicy"
+      rates:
+        - limit: 100
+          interval: "PT1M"
+      matchers:
+        - method: "GET"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/orders"
+```
+In this example, at most 100 requests to `GET /orders` can be made in any rolling 1-minute period.
+
+## Matching requests with matchers
+Each policy has a `matchers` array of objects defining which requests it applies to. The schema for each matcher:
+
+```yaml
+HttpRequestRegexMatcher:
+  type: object
+  properties:
+    method:
+      type: string
+      description: The HTTP method (e.g. GET, POST).
+    url_base:
+      type: string
+      description: The base URL to match (e.g. "https://api.example.com" without trailing slash).
+    url_path_pattern:
+      type: string
+      description: A regular expression to match the path portion.
+    params:
+      type: object
+      additionalProperties: true
+    headers:
+      type: object
+      additionalProperties: true
+  additionalProperties: true
+```
+- **method**: Matches if the request method equals the one in the matcher (case-insensitive).
+- **url_base**: Matches the scheme + host portion (no trailing slash).
+- **url_path_pattern**: Regex is tested against the request path.
+- **params**: The query parameters must match.
+- **headers**: The headers must match.
+A request is rate-limited by the first policy whose matchers pass. If no policy matches, then the request will be allowed if you have not defined a default/other policy that catches everything else.
+
+## Putting it all together
+You may define multiple policies for different endpoints. For example:
+
+```yaml
+api_budget:
+  type: "HTTPAPIBudget"
+  # Use standard rate limit headers from your API
+  ratelimit_reset_header: "X-RateLimit-Reset"
+  ratelimit_remaining_header: "X-RateLimit-Remaining"
+  status_codes_for_ratelimit_hit: [429, 420]
+
+  policies:
+    # Policy 1: Unlimited
+    - type: "UnlimitedCallRatePolicy"
+      matchers:
+        - url_base: "https://api.example.com"
+          method: "GET"
+          url_path_pattern: "^/sandbox"
+
+    # Policy 2: 1000 calls per hour
+    - type: "FixedWindowCallRatePolicy"
+      period: "PT1H"
+      call_limit: 1000
+      matchers:
+        - method: "GET"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/users"
+
+    # Policy 3: 500 calls per hour
+    - type: "FixedWindowCallRatePolicy"
+      period: "PT1H"
+      call_limit: 500
+      matchers:
+        - method: "POST"
+          url_base: "https://api.example.com"
+          url_path_pattern: "^/orders"
+
+    # Policy 4: 20 calls every 5 minutes (moving window).
+    - type: "MovingWindowCallRatePolicy"
+      rates:
+        - limit: 20
+          interval: "PT5M"
+      matchers:
+        - url_base: "https://api.example.com"
+          url_path_pattern: "^/internal"
+```
+1. The request attempts to match the first policy (unlimited on `GET /sandbox`). If it matches, it’s unlimited. 
+2. Otherwise, it checks the second policy (1000/hour for `GET /users`), etc. 
+3. If still no match, it is not rate-limited by these defined policies (unless you add a “catch-all” policy).

From fb0e144c5176137cf88659a37caa81c7fe76c6e0 Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Tue, 11 Feb 2025 20:03:34 +0200
Subject: [PATCH 6/9] Update version

---
 .../connectors/source-klaviyo/metadata.yaml      |  2 +-
 .../connectors/source-klaviyo/pyproject.toml     |  2 +-
 .../source-klaviyo/source_klaviyo/manifest.yaml  | 16 ++--------------
 docs/integrations/sources/klaviyo.md             |  1 +
 4 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/metadata.yaml b/airbyte-integrations/connectors/source-klaviyo/metadata.yaml
index 5f513f033e9b2..07f7b0605e7fa 100644
--- a/airbyte-integrations/connectors/source-klaviyo/metadata.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/metadata.yaml
@@ -8,7 +8,7 @@ data:
   definitionId: 95e8cffd-b8c4-4039-968e-d32fb4a69bde
   connectorBuildOptions:
     baseImage: docker.io/airbyte/python-connector-base:3.0.0@sha256:1a0845ff2b30eafa793c6eee4e8f4283c2e52e1bbd44eed6cb9e9abd5d34d844
-  dockerImageTag: 2.11.11
+  dockerImageTag: 2.12.0
   dockerRepository: airbyte/source-klaviyo
   githubIssueLabel: source-klaviyo
   icon: klaviyo.svg
diff --git a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
index 45aa197660b4e..7c1d706793b3f 100644
--- a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
+++ b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
@@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
 build-backend = "poetry.core.masonry.api"
 
 [tool.poetry]
-version = "2.11.11"
+version = "2.12.0"
 name = "source-klaviyo"
 description = "Source implementation for Klaviyo."
 authors = [ "Airbyte <contact@airbyte.io>",]
diff --git a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
index 64eab0fc3c08a..b7e8ba19df3a6 100644
--- a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
@@ -61,19 +61,6 @@ definitions:
     requester: "#/definitions/requester"
     paginator: "#/definitions/paginator"
 
-  semi_incremental_retriever:
-    $ref: "#/definitions/base_retriever"
-    record_selector:
-      $ref: "#/definitions/selector"
-      record_filter:
-        # Removing the record filter will migrate `lists_detailed` to concurrency,
-        # which will cause it to fail due to rate limits.
-        # Rate limiting for this stream should be implemented first.
-        type: RecordFilter
-        condition: |
-          {% set starting_point = stream_state.get('updated', config.get('start_date')) %}
-          {{ starting_point and record.get('attributes', {}).get('updated') > starting_point or not starting_point }}
-
   profiles_retriever:
     $ref: "#/definitions/base_retriever"
     paginator:
@@ -106,12 +93,13 @@ definitions:
 
   base_semi_incremental_stream:
     $ref: "#/definitions/base_stream"
-    retriever: "#/definitions/semi_incremental_retriever"
+    retriever: "#/definitions/base_retriever"
     incremental_sync:
       type: DatetimeBasedCursor
       cursor_field: "updated"
       datetime_format: "%Y-%m-%dT%H:%M:%S%z"
       start_datetime: "{{ config.get('start_date', '2012-01-01T00:00:00Z') }}"
+      is_client_side_incremental: true
 
   # Incremental streams
   profiles_stream:
diff --git a/docs/integrations/sources/klaviyo.md b/docs/integrations/sources/klaviyo.md
index a283d733cff0a..9d1ed8cddc42a 100644
--- a/docs/integrations/sources/klaviyo.md
+++ b/docs/integrations/sources/klaviyo.md
@@ -95,6 +95,7 @@ contain the `predictive_analytics` field and workflows depending on this field w
 
 | Version | Date       | Pull Request                                               | Subject                                                                                                                                                                |
 |:--------|:-----------|:-----------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 2.12.0 | 2025-02-11 | [53223](https://github.com/airbytehq/airbyte/pull/53223) | Add API Budget |
 | 2.11.11 | 2025-01-27 | [52563](https://github.com/airbytehq/airbyte/pull/52563) | Fix `lists_detailed` incremental sync |
 | 2.11.10 | 2025-01-25 | [52285](https://github.com/airbytehq/airbyte/pull/52285) | Update dependencies |
 | 2.11.9 | 2025-01-11 | [51198](https://github.com/airbytehq/airbyte/pull/51198) | Update dependencies |

From 8089e3d9e574d6fe8d04d3a8f41b6a3cb5f30bce Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Thu, 13 Feb 2025 12:13:58 +0200
Subject: [PATCH 7/9] Update CDK version

---
 .../connectors/source-klaviyo/poetry.lock                 | 8 ++++----
 .../connectors/source-klaviyo/pyproject.toml              | 2 +-
 .../source-klaviyo/source_klaviyo/manifest.yaml           | 1 -
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/poetry.lock b/airbyte-integrations/connectors/source-klaviyo/poetry.lock
index e5f67f41a5e56..16431ebadcad7 100644
--- a/airbyte-integrations/connectors/source-klaviyo/poetry.lock
+++ b/airbyte-integrations/connectors/source-klaviyo/poetry.lock
@@ -2,15 +2,15 @@
 
 [[package]]
 name = "airbyte-cdk"
-version = "6.33.1.dev1"
+version = "6.33.4"
 description = "A framework for writing Airbyte Connectors."
 optional = false
 python-versions = "<3.13,>=3.10"
 groups = ["main"]
 markers = "python_version <= \"3.11\""
 files = [
-    {file = "airbyte_cdk-6.33.1.dev1-py3-none-any.whl", hash = "sha256:d5601d9325564af59c7f6f7d788ed1bcb8956ad11555015410079410ba3098a1"},
-    {file = "airbyte_cdk-6.33.1.dev1.tar.gz", hash = "sha256:a176117408aa663c1734db2bae899ad4293c6af3310a02ae34758b2762c61acd"},
+    {file = "airbyte_cdk-6.33.4-py3-none-any.whl", hash = "sha256:1df057563078744220832922e7ae0b465b85f1de43aa955caa45377c66ffe072"},
+    {file = "airbyte_cdk-6.33.4.tar.gz", hash = "sha256:8e93400b72e7492c45ad1962533d5f65df66ed0d89f627e1cb0bad86ee354562"},
 ]
 
 [package.dependencies]
@@ -2272,4 +2272,4 @@ files = [
 [metadata]
 lock-version = "2.1"
 python-versions = "^3.10,<3.12"
-content-hash = "0f7a990c524720e78dca8adaa825ca1fd63cbdeeda3aebe5b1b6a1f6176153cd"
+content-hash = "2982e20fc923aa1516f081137731b723f75086fc9991d93b369e214abda2c4f3"
diff --git a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
index 7c1d706793b3f..b0efdcc0bfc7d 100644
--- a/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
+++ b/airbyte-integrations/connectors/source-klaviyo/pyproject.toml
@@ -17,7 +17,7 @@ include = "source_klaviyo"
 
 [tool.poetry.dependencies]
 python = "^3.10,<3.12"
-airbyte_cdk = "v6.33.1.dev1"
+airbyte_cdk = "^6"
 pendulum = "<3.0.0"
 
 [tool.poetry.scripts]
diff --git a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
index 8a06c4a7c8a58..ed0b459080d76 100644
--- a/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
+++ b/airbyte-integrations/connectors/source-klaviyo/source_klaviyo/manifest.yaml
@@ -1117,7 +1117,6 @@ api_budget:
           params:
             "additional-fields[list]": "profile_count" # Other API budget settings:
   status_codes_for_ratelimit_hit: [429]
-  maximum_attempts_to_acquire: 100000
 
 # Klaviyo's rate limiting is different by endpoints:
 # - XS: 1/s burst; 15/m steady

From 894921ab84cf7e5d3c4b0000d438b5f015077950 Mon Sep 17 00:00:00 2001
From: Anatolii Yatsuk <tolikyatsuk@gmail.com>
Date: Thu, 13 Feb 2025 12:39:07 +0200
Subject: [PATCH 8/9] Fix unit tests

---
 .../source-klaviyo/unit_tests/test_streams.py | 35 ++++++++++++++-----
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
index f501f5aeaa68f..087c6f7f6e1d8 100644
--- a/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
+++ b/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
@@ -8,6 +8,7 @@
 from datetime import datetime, timedelta
 from typing import Any, List, Mapping, Optional
 from unittest import mock
+from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read
 from unittest.mock import patch
 
 import freezegun
@@ -24,7 +25,7 @@
 from airbyte_cdk import AirbyteTracedException
 from airbyte_cdk.models import SyncMode
 from airbyte_cdk.sources.streams import Stream
-from airbyte_cdk.test.catalog_builder import CatalogBuilder
+from airbyte_cdk.test.catalog_builder import CatalogBuilder, ConfiguredAirbyteStreamBuilder
 from airbyte_cdk.test.state_builder import StateBuilder
 
 
@@ -32,6 +33,7 @@
 API_KEY = "some_key"
 START_DATE = pendulum.datetime(2020, 10, 10)
 CONFIG = {"api_key": API_KEY, "start_date": START_DATE}
+CONFIG_NO_DATE = {"api_key": API_KEY}
 
 EVENTS_STREAM_DEFAULT_START_DATE = "2012-01-01T00:00:00+00:00"
 EVENTS_STREAM_CONFIG_START_DATE = "2021-11-08T00:00:00+00:00"
@@ -48,8 +50,24 @@ def get_step_diff(provided_date: str) -> int:
     return (freeze_date - provided_date).days // 7
 
 
-def get_stream_by_name(stream_name: str, config: Mapping[str, Any]) -> Stream:
-    source = SourceKlaviyo(CatalogBuilder().build(), KlaviyoConfigBuilder().build(), StateBuilder().build())
+def read_records(stream_name: str, config: Mapping[str, Any], states: Mapping[str, Any] = dict()) -> List[Mapping[str, Any]]:
+    state = StateBuilder()
+    for stream_name_key in states:
+        state.with_stream_state(stream_name_key, states[stream_name_key])
+    source = SourceKlaviyo(CatalogBuilder().build(), config, state.build())
+    output = read(source,
+        config,
+        CatalogBuilder()
+        .with_stream(ConfiguredAirbyteStreamBuilder().with_name(stream_name))
+        .build(),)
+    return [r.record.data for r in output.records]
+
+
+def get_stream_by_name(stream_name: str, config: Mapping[str, Any], states: Mapping[str, Any] = dict()) -> Stream:
+    state = StateBuilder()
+    for stream_name_key in states:
+        state.with_stream_state(stream_name_key, states[stream_name_key])
+    source = SourceKlaviyo(CatalogBuilder().build(), KlaviyoConfigBuilder().build(), state.build())
     matches_by_name = [stream_config for stream_config in source.streams(config) if stream_config.name == stream_name]
     if not matches_by_name:
         raise ValueError("Please provide a valid stream name.")
@@ -336,7 +354,7 @@ class TestSemiIncrementalKlaviyoStream:
         ("start_date", "stream_state", "input_records", "expected_records"),
         (
             (
-                "2021-11-08T00:00:00+00:00",
+                "2021-11-08T00:00:00Z",
                 "2022-11-07T00:00:00+00:00",
                 [
                     {"attributes": {"updated": "2022-11-08T00:00:00+00:00"}},
@@ -349,7 +367,7 @@ class TestSemiIncrementalKlaviyoStream:
                 ],
             ),
             (
-                "2021-11-08T00:00:00+00:00",
+                "2021-11-09T00:00:00Z",
                 None,
                 [
                     {"attributes": {"updated": "2022-11-08T00:00:00+00:00"}},
@@ -361,14 +379,13 @@ class TestSemiIncrementalKlaviyoStream:
                     {"attributes": {"updated": "2023-11-08T00:00:00+00:00"}, "updated": "2023-11-08T00:00:00+00:00"},
                 ],
             ),
-            ("2021-11-08T00:00:00+00:00", "2022-11-07T00:00:00+00:00", [], []),
+            ("2021-11-08T00:00:00Z", "2022-11-07T00:00:00+00:00", [], []),
         ),
     )
     def test_read_records(self, start_date, stream_state, input_records, expected_records, requests_mock):
-        stream = get_stream_by_name("metrics", CONFIG | {"start_date": start_date})
+        state = {"metrics": {"updated": stream_state}} if stream_state else {}
         requests_mock.register_uri("GET", f"https://a.klaviyo.com/api/metrics", status_code=200, json={"data": input_records})
-        stream.stream_state = {stream.cursor_field: stream_state if stream_state else start_date}
-        records = get_records(stream=stream, sync_mode=SyncMode.incremental)
+        records = read_records("metrics", CONFIG_NO_DATE | {"start_date": start_date}, state)
         assert records == expected_records
 
 

From 2fec089fc46fabbb3987b67207501b2b1b671cc8 Mon Sep 17 00:00:00 2001
From: Octavia Squidington III
 <octavia-squidington-iii@users.noreply.github.com>
Date: Thu, 13 Feb 2025 10:49:21 +0000
Subject: [PATCH 9/9] chore: auto-fix lint and format issues

---
 .../source-klaviyo/unit_tests/test_streams.py          | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py b/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
index 087c6f7f6e1d8..0dffe4456f9ca 100644
--- a/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
+++ b/airbyte-integrations/connectors/source-klaviyo/unit_tests/test_streams.py
@@ -8,7 +8,6 @@
 from datetime import datetime, timedelta
 from typing import Any, List, Mapping, Optional
 from unittest import mock
-from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read
 from unittest.mock import patch
 
 import freezegun
@@ -26,6 +25,7 @@
 from airbyte_cdk.models import SyncMode
 from airbyte_cdk.sources.streams import Stream
 from airbyte_cdk.test.catalog_builder import CatalogBuilder, ConfiguredAirbyteStreamBuilder
+from airbyte_cdk.test.entrypoint_wrapper import EntrypointOutput, read
 from airbyte_cdk.test.state_builder import StateBuilder
 
 
@@ -55,11 +55,11 @@ def read_records(stream_name: str, config: Mapping[str, Any], states: Mapping[st
     for stream_name_key in states:
         state.with_stream_state(stream_name_key, states[stream_name_key])
     source = SourceKlaviyo(CatalogBuilder().build(), config, state.build())
-    output = read(source,
+    output = read(
+        source,
         config,
-        CatalogBuilder()
-        .with_stream(ConfiguredAirbyteStreamBuilder().with_name(stream_name))
-        .build(),)
+        CatalogBuilder().with_stream(ConfiguredAirbyteStreamBuilder().with_name(stream_name)).build(),
+    )
     return [r.record.data for r in output.records]